In-App Purchases - James Montemagno https://staging.montemagno.com/tag/in-app-purchases-2/ https://staging.montemagno.com/content/images/2017/08/MonogramIcon_Orange_NoBuffer.png In-App Purchases - James Montemagno https://staging.montemagno.com/tag/in-app-purchases-2/ Hugo -- gohugo.io Tue, 13 Oct 2026 23:21:57 GMT 60 Enabling Users to Manage Subscriptions on iOS/Android https://staging.montemagno.com/enabling-users-to-manage-subscriptions-on-ios-android/ 632f2485ae7288003d7930aa Sat, 24 Sep 2022 16:01:17 GMT

You know I love talking about in app purchases and subscriptions. Due to my InAppBilling library I get all the "fun" updates when Apple or Google force developers to make changes to stay compliant. Upcoming is a nice large change from Google to push Android developers to use Billing v4 (which my library does!) and another is that we must now give users a way to easily manage and cancel their subscriptions. You may have recently seen this message from google:

We’re updating our Subscriptions policy to state that users must be able to easily cancel their subscriptions from within the app. {#we%E2%80%99re-updating-our-subscriptions-policy-to-state-that-users-must-be-able-to-easily-cancel-their-subscriptions-from-within-the-app}

What does this mean? What do I need to do Google?!?!! Well if you dig through the documentation you will see that you have to add a button to your purchase screen and also create a deep link to launch the store management screen. Let's see what this looks like in code for Xamarin and .NET MAUI apps:

public async Task ManageSubscription(string sku)
{
    var url = string.Empty;
    if (DeviceInfo.Platform == DevicePlatform.iOS)
    {
        url = "https://support.apple.com/HT202039";
    }
    else if (DeviceInfo.Platform == DevicePlatform.Android)
    {
        if (string.IsNullOrWhiteSpace(sku))
        {
            url = "https://play.google.com/store/account/subscriptions";
        }
        else
        {
            url = $"https://play.google.com/store/account/subscriptions?sku={sku}&package={AppInfo.PackageName}";
        }
    }

    if (string.IsNullOrWhiteSpace(url))
        return;

    await Browser.OpenAsync(url);
}

For iOS there isn't really a requirement or even an API to give users an easy way to manage subscriptions, but I figure why not.

For Android, there are two cases here:

  1. They don't have any non-expired subscriptions and you just launch the generic page.
  2. Your user has a non-expired subscriptions for your app, where expiryTime is in the future or autoRenewing is set to true. In this case you would want to open the subscription page to the very specific subscription for them to manage.

And, that is about it... Well actually you could simplify this entire process by creating a Manage App Subscriptions page on your website and linking to that. Then your code becomes:

await Browser.OpenAsync("https://refractored.com/manage-app-subscriptions");

This should also adhere to the app store guidelines and also you can add additional information. Cheers!

Checkout my videos on YouTube

]]>
Cross-Platform In-App Purchases for Xamarin.Mac Apps! https://staging.montemagno.com/cross-platform-in-app-purchases-for-xamarin-mac-apps/ 6022c9a56c48dd0039592ea7 Tue, 09 Feb 2021 18:04:20 GMT

This week, one of my best friends Frank Krueger released his brilliant app iCircuit 3D for macOS (he also wrote an awesome blog on how it was made). While I know that catalyst support for Xamarin (and .NET MAUI) is coming soon, I still think there are great opportunities to monetize macOS apps written in C# today (see iCircuit 3D!). I released my In-App Billing Plugin a few years ago for iOS, Android, and UWP apps and it has been awesome to hear how it has helped so many developers monetize their apps. The library has expanded over the years to support subscriptions and updated to the latest Android billing libraries. I have used this library personally to monetize my apps including Island Tracker and My Cadence. If you haven't seen the API it is absolutely lovely! Check this out:

Get Product Info

Everything is basically three lines of code... Connect, Call, Disconnect:

var connected = await CrossInAppBilling.Current.ConnectAsync();
if(!connected)
    return;

var items = await CrossInAppBilling.Current.GetProductInfoAsync(ItemType.InAppPurchase, "myitem");

await CrossInAppBilling.Current.DisconnectAsync();

Purchase an Item

Rinse and repeat!

var connected = await CrossInAppBilling.Current.ConnectAsync();
if(!connected)
    return;

var purchase = await CrossInAppBilling.Current.PurchaseAsync(productId, ItemType.InAppPurchase);
if(purchase != null & purcxhase.State == PurchaseState.Purchased)
{
	//item bought
}

await CrossInAppBilling.Current.DisconnectAsync();

Now for Xamarin.Mac!

I recently had a few user of My Stream Timer as me how they can donate and support the development of the app. I never really thought about monetizing the app as I made it sort of for myself to show a countdown before my streams. However, this app has grown to nearly 20,000 downloads from all sorts of streamers including yoga instructors, teachers, coders, and gamers! So I figure I would extend support for my plugin to macOS. This weekend I sat down and 3 hours later it was done! Luckily for me the API is extremely similar to iOS with StoreKit. There are a few differences around making the actual purchase where you can't specify a string id, but instead must get the actual product from StoreKit first. Luckily this method already existed in my code and after a bit of hacking I got it working. Oh and there is no TransactionReceipt, which is weird but there are newer APIs that developers are supposed to query. Beyond that the API works as expected! Check it out!

In app purchase in My Stream Timer

My favorite part has to be testing IAPs on macOS because it is so simple. All you need to do is log out of the app store and then open your app in debug and make the purchase! It just works! I love it!

If you are making macOS apps I hope that you will checkout the plugin and try to add some monetization to your app!

]]>