Add transactions handler to init method according ios documentation it's important to add handler on application:didFinishLaunchingWithOptions: (https://developer.apple.com/documentation/storekit/in-app_purchase/setting_up_the_transaction_observer_for_the_payment_queue?language=objc), move observable commands to separate queue so it can be processed separately and after listener setup, add IAP_ProcessPendingTransactions function to resolve pending transactions on iOS.

This commit is contained in:
Denis Dyukorev 2020-03-04 17:26:52 +03:00
parent d6cc6f55f9
commit cbc1f659c1
3 changed files with 46 additions and 23 deletions

View File

@ -50,6 +50,12 @@ struct IAP
static IAP g_IAP; static IAP g_IAP;
static int IAP_ProcessPendingTransactions(lua_State* L)
{
//todo handle pending transactions if there is such thing on Android
return 0;
}
static int IAP_List(lua_State* L) static int IAP_List(lua_State* L)
{ {
int top = lua_gettop(L); int top = lua_gettop(L);
@ -187,6 +193,7 @@ static const luaL_reg IAP_methods[] =
{"restore", IAP_Restore}, {"restore", IAP_Restore},
{"set_listener", IAP_SetListener}, {"set_listener", IAP_SetListener},
{"get_provider_id", IAP_GetProviderId}, {"get_provider_id", IAP_GetProviderId},
{"process_pending_transactions", IAP_ProcessPendingTransactions},
{0, 0} {0, 0}
}; };

View File

@ -77,6 +77,11 @@ static void IAPList_Callback(void* luacallback, const char* result_json)
dmScript::TeardownCallback(callback); dmScript::TeardownCallback(callback);
} }
static int IAP_ProcessPendingTransactions(lua_State* L)
{
return 0;
}
static int IAP_List(lua_State* L) static int IAP_List(lua_State* L)
{ {
DM_LUA_STACK_CHECK(L, 0); DM_LUA_STACK_CHECK(L, 0);
@ -210,6 +215,7 @@ static const luaL_reg IAP_methods[] =
{"restore", IAP_Restore}, {"restore", IAP_Restore},
{"set_listener", IAP_SetListener}, {"set_listener", IAP_SetListener},
{"get_provider_id", IAP_GetProviderId}, {"get_provider_id", IAP_GetProviderId},
{"process_pending_transactions", IAP_ProcessPendingTransactions},
{0, 0} {0, 0}
}; };

View File

@ -29,6 +29,7 @@ struct IAP
NSMutableDictionary* m_PendingTransactions; NSMutableDictionary* m_PendingTransactions;
dmScript::LuaCallbackInfo* m_Listener; dmScript::LuaCallbackInfo* m_Listener;
IAPCommandQueue m_CommandQueue; IAPCommandQueue m_CommandQueue;
IAPCommandQueue m_ObservableQueue;
SKPaymentTransactionObserver* m_Observer; SKPaymentTransactionObserver* m_Observer;
}; };
@ -324,28 +325,25 @@ static void HandlePurchaseResult(IAPCommand* cmd)
assert(top == lua_gettop(L)); assert(top == lua_gettop(L));
} }
@implementation SKPaymentTransactionObserver static void processTransactions(IAP* m_IAP, NSArray* transactions) {
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction* transaction in transactions) { for (SKPaymentTransaction* transaction in transactions) {
if ((!m_IAP->m_AutoFinishTransactions) && (transaction.transactionState == SKPaymentTransactionStatePurchased)) {
if ((!self.m_IAP->m_AutoFinishTransactions) && (transaction.transactionState == SKPaymentTransactionStatePurchased)) {
NSData *data = [transaction.transactionIdentifier dataUsingEncoding:NSUTF8StringEncoding]; NSData *data = [transaction.transactionIdentifier dataUsingEncoding:NSUTF8StringEncoding];
uint64_t trans_id_hash = dmHashBuffer64((const char*) [data bytes], [data length]); uint64_t trans_id_hash = dmHashBuffer64((const char*) [data bytes], [data length]);
[self.m_IAP->m_PendingTransactions setObject:transaction forKey:[NSNumber numberWithInteger:trans_id_hash] ]; [m_IAP->m_PendingTransactions setObject:transaction forKey:[NSNumber numberWithInteger:trans_id_hash] ];
} }
if (!self.m_IAP->m_Listener) if (!m_IAP->m_Listener)
continue; continue;
IAPTransaction* iap_transaction = new IAPTransaction; IAPTransaction* iap_transaction = new IAPTransaction;
CopyTransaction(transaction, iap_transaction); CopyTransaction(transaction, iap_transaction);
IAPCommand cmd; IAPCommand cmd;
cmd.m_Callback = self.m_IAP->m_Listener; cmd.m_Callback = m_IAP->m_Listener;
cmd.m_Command = IAP_PURCHASE_RESULT; cmd.m_Command = IAP_PURCHASE_RESULT;
cmd.m_Data = iap_transaction; cmd.m_Data = iap_transaction;
IAP_Queue_Push(&self.m_IAP->m_CommandQueue, &cmd); IAP_Queue_Push(&m_IAP->m_ObservableQueue, &cmd);
switch (transaction.transactionState) switch (transaction.transactionState)
{ {
@ -364,6 +362,18 @@ static void HandlePurchaseResult(IAPCommand* cmd)
break; break;
} }
} }
}
static int IAP_ProcessPendingTransactions(lua_State* L)
{
processTransactions(&g_IAP, [SKPaymentQueue defaultQueue].transactions);
return 0;
}
@implementation SKPaymentTransactionObserver
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
processTransactions(self.m_IAP, transactions);
} }
@end @end
@ -477,18 +487,6 @@ static int IAP_SetListener(lua_State* L)
dmScript::DestroyCallback(iap->m_Listener); dmScript::DestroyCallback(iap->m_Listener);
iap->m_Listener = dmScript::CreateCallback(L, 1); iap->m_Listener = dmScript::CreateCallback(L, 1);
if (g_IAP.m_Observer == 0) {
SKPaymentTransactionObserver* observer = [[SKPaymentTransactionObserver alloc] init];
observer.m_IAP = &g_IAP;
// NOTE: We add the listener *after* a lua listener is set
// The payment queue is persistent and "old" transaction might be processed
// from previous session. We call "finishTransaction" when appropriate
// for all transaction and we must ensure that the result is delivered to lua.
[[SKPaymentQueue defaultQueue] addTransactionObserver: observer];
g_IAP.m_Observer = observer;
}
return 0; return 0;
} }
@ -506,6 +504,7 @@ static const luaL_reg IAP_methods[] =
{"restore", IAP_Restore}, {"restore", IAP_Restore},
{"set_listener", IAP_SetListener}, {"set_listener", IAP_SetListener},
{"get_provider_id", IAP_GetProviderId}, {"get_provider_id", IAP_GetProviderId},
{"process_pending_transactions", IAP_ProcessPendingTransactions},
{0, 0} {0, 0}
}; };
@ -520,6 +519,7 @@ static dmExtension::Result InitializeIAP(dmExtension::Params* params)
g_IAP.m_InitCount++; g_IAP.m_InitCount++;
IAP_Queue_Create(&g_IAP.m_CommandQueue); IAP_Queue_Create(&g_IAP.m_CommandQueue);
IAP_Queue_Create(&g_IAP.m_ObservableQueue);
lua_State*L = params->m_L; lua_State*L = params->m_L;
int top = lua_gettop(L); int top = lua_gettop(L);
@ -535,6 +535,12 @@ static dmExtension::Result InitializeIAP(dmExtension::Params* params)
lua_pop(L, 1); lua_pop(L, 1);
assert(top == lua_gettop(L)); assert(top == lua_gettop(L));
SKPaymentTransactionObserver* observer = [[SKPaymentTransactionObserver alloc] init];
observer.m_IAP = &g_IAP;
[[SKPaymentQueue defaultQueue] addTransactionObserver: observer];
g_IAP.m_Observer = observer;
return dmExtension::RESULT_OK; return dmExtension::RESULT_OK;
} }
@ -557,6 +563,9 @@ static void IAP_OnCommand(IAPCommand* cmd, void*)
static dmExtension::Result UpdateIAP(dmExtension::Params* params) static dmExtension::Result UpdateIAP(dmExtension::Params* params)
{ {
IAP_Queue_Flush(&g_IAP.m_CommandQueue, IAP_OnCommand, 0); IAP_Queue_Flush(&g_IAP.m_CommandQueue, IAP_OnCommand, 0);
if (g_IAP.m_Observer) {
IAP_Queue_Flush(&g_IAP.m_ObservableQueue, IAP_OnCommand, 0);
}
return dmExtension::RESULT_OK; return dmExtension::RESULT_OK;
} }
@ -585,6 +594,7 @@ static dmExtension::Result FinalizeIAP(dmExtension::Params* params)
} }
IAP_Queue_Destroy(&g_IAP.m_CommandQueue); IAP_Queue_Destroy(&g_IAP.m_CommandQueue);
IAP_Queue_Destroy(&g_IAP.m_ObservableQueue);
return dmExtension::RESULT_OK; return dmExtension::RESULT_OK;
} }