1. Hello Guest! It's with a heavy heart that we must announce the removal of the Legacy GMC Archive. If you wish to save anything from it, now's the time! Please see this topic for more information.
    Dismiss Notice

iOS IAP not working

Discussion in 'GameMaker Studio 2 Community Tech Support' started by yakmoon, Nov 14, 2019.

Tags:
  1. yakmoon

    yakmoon Member

    Joined:
    Sep 20, 2016
    Posts:
    33
    Hi, after following the IAP documentation and carefully examining the IOS IAP extenstion I couldn't find why the extension is not working. I searched on this forum to see if others are having the same issue. and yes there is another topic with IAP problem. but nobody even cared to answer the question to why it's not working.
    this is the issue I have:
    create event
    Code:
    global.IAP_Enabled = ios_iap_IsAuthorisedForPayment();
            IAP_PID0="Product1";
            IAP_PID1="Product2";
            global.IAP_PurchaseID[0, 0] = IAP_PID0;
            global.IAP_PurchaseID[1, 0] = IAP_PID1;
            if global.IAP_Enabled
            {
                ios_iap_AddProduct(IAP_PID0);
                ios_iap_AddProduct(IAP_PID1);
                ios_iap_QueryProducts();
            }
    the problem is that ios_iap_QueryProducts is not working properly. because in the social event I'm receiving an empty list of products. I wonder why?!
    and when executing ios_iap_PurchaseProduct(); nothing happens, you know why? because the list doesn't exist! so is there any solution to this, or I should forget about ios?
    note: I've submitted the game to app store and the testers there rejected my game because the IAP is not working.
    this is their response:
    We found that your in-app purchase products exhibited one or more bugs when reviewed on iPad running iOS 13.2 on Wi-Fi.

    Specifically, the “BUY” button was not responsive.

    Next Steps

    When validating receipts on your server, your server needs to be able to handle a production-signed app getting its receipts from Apple’s test environment. The recommended approach is for your production server to always validate receipts against the production App Store first. If validation fails with the error code "Sandbox receipt used in production," you should validate against the test environment instead.

    Resources

    You can learn more about testing in-app purchase products in your development sandbox environment in App Store Connect Developer Help.

    For more information on receipt validation, please see What url should I use to verify my receipt? in the In-App Purchase FAQ.

    Learn how to generate a receipt validation code in App Store Connect Developer Help.



    Please see attached screenshot for details.

    I'm really excited to read the replies on this topic.
     
  2. chirpy

    chirpy Member

    Joined:
    Oct 6, 2016
    Posts:
    171
    You didn’t mention Apple sandbox account so I’m not sure whether you had it configured correctly. If you cannot get a list that’s probably one of the first things you need to check. Other than that, you might want to check more details in Apple’s IAP documentation.

    I have gotten my IAP working and approved recently after 2.2.4 update so I’m sure it is at least partly working (I only added one nonconsumable product for ads removal). Had to handle network status and connection retries, ui to reflect parental control (not authorized for payment case) etc. Good luck.
     
  3. spinout

    spinout Member

    Joined:
    Jun 21, 2016
    Posts:
    85
    I've got IAP working for subscriptions and non consumables such as ad removals, but I'm having trouble getting consumables approved by the Apple team. They said they are unresponsive, although they work when I test them on sandbox.
     
  4. chirpy

    chirpy Member

    Joined:
    Oct 6, 2016
    Posts:
    171
    I had confronted similar issues when submitting first non-consumable (got sandbox working).
    It seems to me that Apple is being pretty strict on parental control, so they randomly pick apps to test IAPs on devices where IAPs aren't allowed.
    I had to change my IAP buttons to reflect the status (I ended up hiding all disallowed IAP buttons).

    Disclaimer:
    Since the app review board never answered me directly whether it was all about parental control until I got approved, it was just my assumption.
    I had rewritten my network retry logic, and checked other possibilities mentioned in Apple developer forum before assuming this was the issue.

    Ref:
    https://developer.apple.com/app-store/review/guidelines/
    Screen time / content & privacy restrictions:
    https://support.apple.com/bg-bg/HT201304
     
  5. spinout

    spinout Member

    Joined:
    Jun 21, 2016
    Posts:
    85
    I've got it working and approved by the Apple review team. I changed from validating the receipts locally to verifying then on Apple's servers. By their recommended procedure they suggest to verify on production server, if that fails fall back and try to verify it on the sandbox server. Worked!
     
    yakmoon and chirpy like this.
  6. chirpy

    chirpy Member

    Joined:
    Oct 6, 2016
    Posts:
    171
  7. yakmoon

    yakmoon Member

    Joined:
    Sep 20, 2016
    Posts:
    33
    can you enlighten us on how did you do that?
     
    chirpy likes this.
  8. spinout

    spinout Member

    Joined:
    Jun 21, 2016
    Posts:
    85
    ////make the pychase
    if global.IAPEnabled {
    ios_iap_PurchaseProduct(global.ProductID[2,0]);
    iaploading = true;
    iaptimer = 700;
    } else {

    }


    ////in-app response
    var _json = async_load[? "response_json"];
    if _json != "" {
    var _map = json_decode(_json);
    var _plist = _map[? "purchases"];
    var _sz = ds_list_size(_plist);
    for (var i=0;i<_sz;++i;) {
    var _pmap = _plist[| i];
    if _pmap[? "purchaseState"] != ios_purchase_failed {
    var _receipt = ios_iap_GetReceipt();
    if _receipt != "" {
    switch _pmap[? "productId"] {
    case "subway.sub":
    currentIAP = 0;
    break;
    case "subway.adfree":
    currentIAP = 1;
    break;
    case "subway.gems1":
    currentIAP = 2;
    break;
    case "subway.gems2":
    currentIAP = 3;
    break;
    case "subway.gems3":
    currentIAP = 4;
    break;
    case "subway.gems4":
    currentIAP = 5;
    break;
    case "subway.gems5":
    currentIAP = 6;
    break;
    case "subway.gems6":
    currentIAP = 7;
    break;
    }
    receiptID= Script_IAPs_validation_server(1);
    } else {
    if global.receiptFails < 1 {
    ios_iap_RefreshReceipt();
    global.receiptFails++;
    } else {
    global.IAPEnabled = false;
    }
    }
    } else {
    iaploading = false;
    iaptimer = 0;
    }
    var _ptoken = _pmap[? "purchaseToken"];
    ios_iap_FinishTransaction(_ptoken);
    ds_map_destroy(_pmap);
    }
    ds_map_destroy(_map);
    }
    break;

    ////Script_IAPs_validation_server()
    var map = ds_map_create();
    ds_map_add(map,"receipt-data",ios_iap_GetReceipt());//iap_receipt_full_read_base64())
    ds_map_add(map,"password","########################");/// only used if you have subscriptions
    var json = json_encode(map);
    ds_map_destroy(map);

    if argument0 {
    return http_post_string("https://buy.itunes.apple.com/verifyReceipt",json);
    } else {
    return http_post_string("https://sandbox.itunes.apple.com/verifyReceipt",json);
    }

    ////http response
    if (not isMap(async_load)) {
    exit;
    } else {
    if (async_load[?"id"] == receiptID) {

    if(async_load[?"status"] != 0 or async_load[?"http_status"] != 200) {
    iaploading = false;
    iaptimer = 0;
    exit;
    }

    var storeReceipt= async_load[?"result"];

    var map = json_decode(storeReceipt);

    if (map[?"status"] != 0) {
    if map[? "status"] == "21007" {
    receiptID= Script_IAPs_validation_server(0);
    } else {
    iaploading = false;
    iaptimer = 0;
    }
    ds_map_destroy(map);
    exit;
    }
    var _amount = 0;
    var _sub = false;
    switch currentIAP {
    case -1: exit;
    case 0:
    _sub = true;
    break;
    case 1:
    if !adfree {
    sAdfreeBought(1);
    }
    break;
    case 2:
    _amount = gemsValue[0];
    break;
    case 3:
    _amount = gemsValue[1];
    break;
    case 4:
    _amount = gemsValue[2];
    break;
    case 5:
    _amount = gemsValue[3];
    break;
    case 6:
    _amount = gemsValue[4];
    break;
    case 7:
    _amount = gemsValue[5];
    break;
    }
    currentIAP = -1;
    if _amount > 0 {
    ////give gems
    }
    else if _sub {
    //do sub stuff
    iaploading = false;
    iaptimer = 0;
    ds_map_destroy(map);
    }
    }
     
    chirpy likes this.
  9. MrBazooka

    MrBazooka Member

    Joined:
    Jul 6, 2018
    Posts:
    13
    Hi there

    I have a few questions about where you implemented everything. Is the purchase section on a button and the rest of the code in the async event on a persistent controller object?
     
  10. MrBazooka

    MrBazooka Member

    Joined:
    Jul 6, 2018
    Posts:
    13
    In the Apple IAP Manual included with the extension it reads:
    "IMPORTANT! In order for the function ios_iap_ValidateReceipt() to return a true response, users must download the Apple Inc. Root Certificate and include it with their project in the Included Files (using included files covers iOS/tvOS and macOS)"

    Can someone explain how I include this certificate with my project? I tried Spinout's method, no errors but it doesn't work. Just an unresponsive button
     
  11. BaBiA Game Studio

    BaBiA Game Studio Member

    Joined:
    Jun 20, 2016
    Posts:
    873
    chirpy likes this.
  12. chirpy

    chirpy Member

    Joined:
    Oct 6, 2016
    Posts:
    171
  13. MrBazooka

    MrBazooka Member

    Joined:
    Jul 6, 2018
    Posts:
    13
    Thanks guys. I hope this works. Apple contacted me about featuring my game, but one of the prerequisites is to implement an IAP for extra "lives". I've been struggling to get the one consumable IAP to work.
     
    chirpy likes this.
  14. spinout

    spinout Member

    Joined:
    Jun 21, 2016
    Posts:
    85
    Wow! That must be frustrating! Did you get it to work? Sorry, I can go days without checking these forums sometimes.
     
  15. MrBazooka

    MrBazooka Member

    Joined:
    Jul 6, 2018
    Posts:
    13
    Apple said that if I can't get my IAP to work that they'll just feature as is. I really want to try and implement one IAP that just gives extra lives. I'm posting my code below. Maybe someone can spot something. I followed the YYG tutorial on IAPs.

    In main room>
    IAP controller object (persistent)

    Create event:
    Code:
    global.IAP_Enabled = ios_iap_IsAuthorisedForPayment();
    global.ProductID[0, 0] = "ios_continues_15_more";
    
    if global.IAP_Enabled
        {
        ios_iap_AddProduct(global.ProductID[0, 0]);
        ios_iap_QueryProducts();
        }
    else
    {
        obj_btn_iap.sprite_index = spr_btn_cantbuy;
    }
    
    Button object

    Left released:
    Code:
    if global.IAP_Enabled
        {
        ios_iap_PurchaseProduct(global.ProductID[0, 0]);
        }
    
    // According to documentation should trigger async event ios_payment_queue_update

    Same IAP controller object as above

    Async IAP event:
    Code:
    var _eventId = async_load[? "id"];
    
    eventID = _eventId;
    
    switch (_eventId)
        {
     
        case ios_payment_queue_update:
            // 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];
                    // Check purchases
                    var _ptoken = _pmap[? "purchaseToken"];
                    if _pmap[? "purchaseState"] != ios_purchase_failed
                        {
                        var _receipt = ios_iap_GetReceipt();
                        // CALL SERVER CHECK WITH RECEIPT HERE
                        // or validate, finalise and award the product
                        if ios_iap_ValidateReceipt(_receipt) == true
                            {
                            obj_iap.continues_left = 15;
                            sprite_index = spr_btn_blank;
                            scr_iap();
                            obj_iap.continues_left -= 1;
                         
                            ios_iap_FinishTransaction(_ptoken);
                            }
                        else
                            {
                            // Validation failed, so deal with it here
                            ios_iap_RefreshReceipt();
                            }
                        }
                    else
                        {
                        // Purchase failed, so finalise it.
                        ios_iap_FinishTransaction(_ptoken);
                        }
                    ds_map_destroy(_pmap);
                    }
                }
            break;
        }
    
    Please any help would be greatly appreciated
     
    Last edited: Nov 26, 2019
  16. MrBazooka

    MrBazooka Member

    Joined:
    Jul 6, 2018
    Posts:
    13
    Any ideas anyone?
     
  17. chirpy

    chirpy Member

    Joined:
    Oct 6, 2016
    Posts:
    171
    @MrBazooka
    I only vaguely remember that ios_iap_ValidateReceipt does not actually take a _receipt argument by looking at the code you posted.
    Perhaps you'd want to create a new thread and provide more details about your question and where you got stuck, what error messages?
     
  18. MrBazooka

    MrBazooka Member

    Joined:
    Jul 6, 2018
    Posts:
    13
    chirpy likes this.

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice