• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!
  • Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

iOS Cancelling IAP returns purchases:[]

Josepho

Member
Hi im having issues trying to make an IAP on my game, the process of making the IAP works perfect the problem comes when the user cancels the iap without completing it, it happens that the json returned is purchases:[] so the code provided in the examples dont catch this cause the bucle for its not started

This is a bug right? cause its an issue i didnt have in previous updates and the code havent changed

I dont know if it helps im using mac version 2.3.1.542 and the device has ios 14,4

GML:
        case ios_payment_queue_update: ///ios_iap_RestorePurchases()
            // Decode the returned JSON
          
            var _json = async_load[? "response_json"];
            if _json != ""
                {
                  
                var _map = json_decode(_json);
                var _plist = _map[? "purchases"];
                var _sz = ds_list_size(_plist);
                // loop through purchases
                for (var i = 0; i < _sz; ++i;)
                    {
                    var _pmap = _plist[| i];
                    var _ptoken = _pmap[? "purchaseToken"];

                    if _pmap[? "purchaseState"] != ios_purchase_failed
                        {
  
                        var _receipt = ios_iap_GetReceipt();
                        // CALL SERVER CHECK WITH RECEIPT HERE
                        // or validate, award the product, and finalise
                        if ios_iap_ValidateReceipt() == true
                            {
                              
                          
                            purchaseExecution(_pmap[? "productId"],false);
                          

                            }
                        else
                            {
                            // Validation failed, so deal with it here
                            // We will still need to finalise the transaction
                            global.purchaseState = "nopurchase";
                            }
                        }
                    else
                        {
                        // Purchase failed, so deal with it here
                        // You will still need to finalise the transaction
                        global.purchaseState = "nopurchase";
                        }
                    ios_iap_FinishTransaction(_ptoken);
                    //ds_map_destroy(_pmap);
                    }
                ds_map_destroy(_map);
                }
            break;
 
Last edited:

Josepho

Member
Im going to bump this, cause almost a year has passed and no solutions where provided, very sad.
 

VlaZel

Member
Hi im having issues trying to make an IAP on my game, the process of making the IAP works perfect the problem comes when the user cancels the iap without completing it, it happens that the json returned is purchases:[] so the code provided in the examples dont catch this cause the bucle for its not started

This is a bug right? cause its an issue i didnt have in previous updates and the code havent changed

I dont know if it helps im using mac version 2.3.1.542 and the device has ios 14,4

GML:
        case ios_payment_queue_update: ///ios_iap_RestorePurchases()
            // Decode the returned JSON
         
            var _json = async_load[? "response_json"];
            if _json != ""
                {
                 
                var _map = json_decode(_json);
                var _plist = _map[? "purchases"];
                var _sz = ds_list_size(_plist);
                // loop through purchases
                for (var i = 0; i < _sz; ++i;)
                    {
                    var _pmap = _plist[| i];
                    var _ptoken = _pmap[? "purchaseToken"];

                    if _pmap[? "purchaseState"] != ios_purchase_failed
                        {
 
                        var _receipt = ios_iap_GetReceipt();
                        // CALL SERVER CHECK WITH RECEIPT HERE
                        // or validate, award the product, and finalise
                        if ios_iap_ValidateReceipt() == true
                            {
                             
                         
                            purchaseExecution(_pmap[? "productId"],false);
                         

                            }
                        else
                            {
                            // Validation failed, so deal with it here
                            // We will still need to finalise the transaction
                            global.purchaseState = "nopurchase";
                            }
                        }
                    else
                        {
                        // Purchase failed, so deal with it here
                        // You will still need to finalise the transaction
                        global.purchaseState = "nopurchase";
                        }
                    ios_iap_FinishTransaction(_ptoken);
                    //ds_map_destroy(_pmap);
                    }
                ds_map_destroy(_map);
                }
            break;
Hello, this is the normal behavior of the extension that you built into the project, because it is implemented in such a way that, regardless of the response of the apple store, it will send json with purchases even if it is empty
 

Attachments

Josepho

Member
But I would expect a response with a cancel or finished event, so i can catch the event and act. What im trying to do is a loading circle when the game is "thinking" and the user has tap on a purchase. If i can track teh cancel the circle remains. Im using some crapp solutions right now i would like to have a pro solution and its what i would expect as the extension is a yoyogames extension..
 

clee2005

Member
But I would expect a response with a cancel or finished event, so i can catch the event and act. What im trying to do is a loading circle when the game is "thinking" and the user has tap on a purchase. If i can track teh cancel the circle remains. Im using some crapp solutions right now i would like to have a pro solution and its what i would expect as the extension is a yoyogames extension..
I've just noticed the same thing in our games. It'll require a modification to the Extension. Then pass back a different response which you can handle in the IAP event. It would be nice if this was already handled, because even if we make changes to this extension, any new updates from YYG may not include our changes as has happened to me many times and I lose customizations over time. I'll have a look and post back here if it's easily modified.
 

clee2005

Member
I've just noticed the same thing in our games. It'll require a modification to the Extension. Then pass back a different response which you can handle in the IAP event. It would be nice if this was already handled, because even if we make changes to this extension, any new updates from YYG may not include our changes as has happened to me many times and I lose customizations over time. I'll have a look and post back here if it's easily modified.
Ok so modify the iOSTransactionListener.mm file by replacing this function :

GML:
/// Handles failed purchase transactions.
-(NSString*)handleFailedTransaction:(SKPaymentTransaction*)transaction
{
    if(transaction!=nil && transaction.transactionIdentifier!=nil)
    {
        [m_activeTransactions setValue:transaction forKey:transaction.transactionIdentifier];
       
        const char* product = [transaction.payment.productIdentifier cStringUsingEncoding:NSUTF8StringEncoding];
        const char* receipt = [[self getReceiptData:transaction] cStringUsingEncoding:NSUTF8StringEncoding];
        const char* token = [transaction.transactionIdentifier cStringUsingEncoding:NSUTF8StringEncoding];
       
        // Receipt data could be quite large so don't risk overrunning a fixed sized buffer
        char* json = (char*)alloca(strlen(product) + strlen(receipt) + strlen(token) + 256);
        sprintf(json, "{ \"productId\":\"%s\", \"purchaseState\":%d, \"responseCode\":%d, \"purchaseToken\":\"%s\", \"receipt\":\"%s\" }", product, (int)purchase_failed, (int)transaction.transactionState, token, receipt);
       
        return [[[NSString alloc] initWithCString:json encoding:NSASCIIStringEncoding] autorelease];
    }
    else {

        // Return so Cancelled transaction can be handled
        char jId[3];
        sprintf(jId, "id");
        char jResponse[20];
        sprintf(jResponse, "response_json");
        int dsMapIndex = dsMapCreate();
        dsMapAddInt(dsMapIndex, jId, payment_queue_update);
        dsMapAddString(dsMapIndex, jResponse, const_cast<char*>([@"{\"cancelled\":\"true\"}" UTF8String]));
        CreateAsyncEventOfTypeWithDSMap(dsMapIndex, EVENT_OTHER_WEB_IAP);
   
        return nil;
       
    }

}
Then in your in-app purchase event :

Code:
    case ios_payment_queue_update:
        // Decode the returned JSON
        var _json = async_load[? "response_json"];
        if (_json != "") {
            var _map = json_decode(_json);
            if (_map[? "cancelled"] <> undefined and _map[? "cancelled"] == "true") {
                buying = false;
            }
            else {    
                var _plist = _map[? "purchases"];

                  ... do the rest of your IAP handling ....
It may not be ideal, but this is how I got it to work.

Cheers,
Chris
 
Last edited:

xDGameStudios

GameMaker Staff
GameMaker Dev.
The right place to request features is the helpdesk :)
if you want a new event to be triggered, then this is not a bug is a feature request.
Feel free to add that request we will look into the possibility.

Thank you,
Francisco (xD)
 

clee2005

Member
The right place to request features is the helpdesk :)
if you want a new event to be triggered, then this is not a bug is a feature request.
Feel free to add that request we will look into the possibility.

Thank you,
Francisco (xD)
I've made the feature request. Thanks for the push to do so.
 
Top