Implementing In-App Purchases in Monogame

Alright so after weeks of digging around, reading, getting extremely frustrated, almost giving up, then realizing I missed something really simple and stupid, and then repeating the cycle, it seems I finally have it working. I will still need to test it in actual production, but at least I’m getting success messages from the sandbox environment (in iOS - I will have to figure out Android later…).

Notes below for reference. I recognize the below will probably be obvious for many of you, but I’ll leave it here as a record for myself and maybe this can help someone who’s just starting out somewhere :slight_smile:

Key things from the iOS side: sandbox testers can apparently be corrupted if you log in to any production environment. I suspect I may have done this accidentally somehow during my tests. No verification was needed for my sandbox account. Using subaddresses definitely help here because if the account gets corrupted, you need to make a new sandbox account, and nobody wants to create a new email account each time. Also, you need an actual device to test (where you sign out of your real account before starting the app); simulator doesn’t work. Oh and for some reason, the first time I tried it (choosing “use an existing account” when prompted), I had to enter my account details twice. It failed (could not connect to iTunes). Then, I tried again, (so this time the device recognized my testing username, and I only had to enter the password) and it worked.

For the code itself: I needed to do some reading about async programming in C# to get some idea of how it works. In essence, as one guy on stackoverflow put it, it’s “asyncs all the way up”, with the only exception being the very first call, which will be an async void. All other functions return a task which is awaited in the other async function calls.

So the code looks something like this:

Your trigger in the main code (somewhere in the update code, wherever you want the purchase to happen, would in this example be a call to the function CheckPurchase() (I picked these function names arbitrarily):

    async void CheckPurchase()
    {
		
	bool purchaseIsSuccessful = await TestMakePurchase("com.website.appName.purchaseName", "");
	// Where you would substitute the string above with what corresponds with what you have as "Product ID" in the app store.

        if (purchaseIsSuccessful)
        {
            //Success! Do what you want here

        }
        else
        {
            //:(
        }
    }

    public async Task<bool> TestMakePurchase(string productId, string payload)
    {
        var billing = CrossInAppBilling.Current;
        try
        {
            var connected = await billing.ConnectAsync();
            if (!connected)
            {
                //we are offline or can't connect, don't try to purchase
                return false;
            }

            //check purchases
            var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, payload);

            //possibility that a null came through.
            if (purchase == null)
            {
                //did not purchase
                return false;
            }
            else
            {
                //purchased!
                return true;
            }
        }
        catch (InAppBillingPurchaseException purchaseEx)
        {
            
                //Error handling code removed for sake of simplicity/readability

        }
		
        finally
        {
            await billing.DisconnectAsync();
        }
    }

I’m cautiously optimistic this means I should be able to get this working now. Thanks all for the help - I’m sure I’ll hit another roadblock somewhere along the way but at least I’m making progress :slight_smile:

3 Likes