Implementing In-App Purchases in Monogame

I’m looking for help with regards to implementing in-app purchases. I am a bit of a noob when it comes to app development, so please humor me. I’m currently working with a Monogame shared project, hoping to target both iOS and Android platforms, and have had trouble getting any code for in-app purchases to work.

What I’ve tried:

I’ve tried reading through the below and using the example code segments, but I don’t have the using directives they use (and so the code is not recognized by my project):


I’ve also looked through here:

As well as the example code linked to in that document:

And I have the same issues (can’t really get anything to work)

Anyone know if there any guides for Monogame specifically? Or alternatively, any suggestions as to where I’m going wrong or what different approaches I can take?

Any help would be much appreciated.

I think this is independent from the MG framework. It depends on the target OS.
Others here may be more helpful

Maybe the Xamarin InAppBilling plugin could help you implement in app purchases on all platforms! :slight_smile:

1 Like

This is excellent - great documentation too! Thanks very much!

1 Like

So it’s supposed to be pretty easy to implement the code. Yet, despite all the documentation, after several days of trying things, I still can’t get the sample code provided for the InAppBilling plugin to work. I’m really lost, so would greatly appreciate any help from anyone who’s successfully used the plugin (or would be kind enough to try installing the plugin and giving the code a try!).

Here’s the code I have so far, adapted from the sample code provided, and cleaned to be easier to read. NotificationMessageBox is a custom function that displays a message box with the desired text. I am using a shared project, and am just building the iOS side for now:

    public async void MakePurchase()
    {
        if (!CrossInAppBilling.IsSupported)
        {
            NotificationMessageBox("CrossInAppBilling not supported");
            return;
        }

        var billing = CrossInAppBilling.Current;
        try
        {
            var connected = await billing.ConnectAsync();
            if (!connected)
                NotificationMessageBox("Not Connected");
            else
            {
                var purchase = await billing.PurchaseAsync("productID", ItemType.InAppPurchase, "payload");
                //where productID is the com.website.product format for the item in the app store
              
                if (purchase == null)
                {
                    NotificationMessageBox("Not Purchased");
                }
                else
                {
                    NotificationMessageBox("Purchased");
                }
            }
        }
        catch (InAppBillingPurchaseException purchaseEx)
        {
            switch (purchaseEx.PurchaseError)
            {
                //Error handling code removed for sake of simplicity/readability
            }
        }
        finally
        {
            await billing.DisconnectAsync();
        }

        CrossInAppBilling.Dispose();
    }

All code compiles, and the project builds. Everything works fine until I get to the line:

var purchase = await billing.PurchaseAsync(“productID”, ItemType.InAppPurchase, “payload”);

With a break point on this line, as soon as I take one step, I get the prompt to sign in to the app store. After I sign in, the code then immediately jumps to the catch, skipping over the entire if (purchase == null) statement (it doesn’t even check the if statement).

At this point, the variable “purchase” is still null.

The purchaseEx error that I get is of the type GeneralError, of which unfortunately the only description provided in the documentation is “other error”. Even looking at the source code, it refers to an unknown error received.

Finally, at the end of the code block above, the app crashes.

Interestingly, even when I change the productID to a nonsense string, I get exactly the same behaviour and same error - it appears to fail before it even makes the call to the app store to check the product?

I read somewhere that you can’t test in-app purchases on an emulator, so I tried deploying to a physical device, but got the same behaviour and error messages as well. I’ve tried with a real and a sandbox Apple ID and got the same results (though maybe I incorrectly used the sandbox account?)

Any suggestions as to where I’m going wrong would be greatly appreciated.

Have you managed to get this working?
It seems that plugin is not officially supported by Xamarin.
I am currently working on in app purchases for both iOS and Android but currently I am trying platform specific apis.

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

It’s mainly maintained by James Montemagno who is the principal program manager at Xamarin. So, not truly official but that’s just a matter of words. :slight_smile:

1 Like

Hi,
This works fine, but when i close the purchase pop up and tried to purchase again, every time purchase variable is null. Do you have any idea?

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

Thanks