(This article is a follow-up to the Shopify application we built in Shopify Billing 101. You can get a free copy of that guide by clicking right here and entering your email address.)
If you've ever downloaded a free app or game from your smartphone's app store, there's a pretty good chance that you've run into something called the "freemium" business model. Put simply, the freemium model lets app developers offer their app for "free", but then they include premium "in-app purchases" that will unlock a feature of the app or progress more easily through a game.
In the mobile app world, this is seen as a legitimate business model. It's a model that can make the developer thousands of dollars more than if they had offered the app for a single one-time purchase of $1.99. In-app purchases are quickly becoming the top business model throughout the iOS, Android and Windows app stores.
When we went over the Shopify billing API and built your sample Shopify application, we really only covered using recurring "subscription" charges as your app's payment model. There's another way to get payments from your users, though, without setting them up for a recurring charge.
It's called the "application charge", but we can think of them as "in-app purchases" for Shopify apps.
Let's say you've got some sort of extra add-on that your users can purchase. To use an example from my own Shopify app, imagine that your app offers text message alerts for each user. Unlike email messages, though, text messages aren't free — they cost a modest chunk of change that you have to pay for out of your own pocket.
To recoup your losses on sending text messages, we can use Shopify's application charge API to create an in-app purchase. To make that happen, let's add several different text message bundles that your users can purchase. Each bundle includes a set amount of text messages, and they'll get a discount for buying the bigger bundles.
|Super Pro||10 texts||$35.00|
(These prices are made up! They aren't anywhere close to reflecting the actual price of text messages. If you're interested in building text messaging into your app, I use a service called Twilio.)
To get started, we're going to need a way to track how many text messages a user can use If you're running the application that we built in Shopify Billing 101, open up the "IdentityModels.cs" file under the "Models" folder in your project. Add the following integer property to your
Let's quickly write the HTML for selecting a bundle. You probably don't want your user purchasing a bundle until they've created an account, connected their Shopify store and then accepted your recurring subscription charge. That means that the Dashboard page (the one that we protected with the
[RequireSubscription] attribute) will be the perfect place to show the bundle selection.
On top of that, I'm going to add a small form to the same page that will let the user "send" a text message.
Open your dashboard view file at "Views/Dashboard/Index.cshtml" and add the following HTML:
There's a few things going on here that should be pointed out.
First, the very first line of our view now states that the view's model is of type
int. That model is the number of text messages that the user can send before they must purchase another bundle. We'll be passing it to the view from the
DashboardController in just a moment.
Second, we're only showing one form at a time. In a real application, these forms would probably be in completely separate views, but I'm keeping things very simple for this tutorial. If the user has one or more text messages available, they'll see the "Send Message" form. If they don't have any messages available, they'll see the "Purchase Messages" form.
Finally, the two forms are posting to two different actions —
DashboardController.PurchaseBundle for purchasing, and
DashboardController.SendTextMessage for sending. Before we get to those two actions, let's make sure the controller is passing the proper model to our view.
Open up the "Controllers/DashboardController.cs" file in your project and find the
Index action (it's probably the only one if you're using the app built in Shopify Billing 101). You'll need to mark that action as async, make it pull in the user model via the
UserManager, and then pass in the user's new
TextMessagesAllowed property to the view.
I'm also going to add some code that will check the controller's
TempData object for an "Error" string. We'll be setting that later, but in short, this error will be set by the
ShopifyController when it's trying to activate the charge for a bundle. If the error exists, it will be displayed inside of the form thanks to
And with that, the controller is now passing in the correct model to the view. Now, let's wire up those two
PurchaseBundle action will receive a string parameter named
bundleId, which we'll use to determine which bundle the user is trying to purchase. If you look back at the HTML we just added to the dashboard view, you'll see that there are three options: "basic", "pro" and "super pro". We're going to need to determine bundles from that string in two different places though, so let's create a model for doing that.
Under the "Models" folder, create a new class named
TextMessageBundle with the following properties:
The model also has a method named
CreateFromId which accepts a string parameter named
bundleId. This method is where we'll determine the price and name of a text message bundle without having to write the same code more than once.
There we go! That should save us a small bit of time since we would be using this code in two (or more) different places in the app. Let's head back to the
DashboardController and have the new
TextMessageBundle.CreateFromId to determine which bundle the user is trying to purchase, and then pull in the user model from your database.
Application charges (a.k.a. in-app purchase charges) are almost identical to recurring subscription charges. You'll need to use ShopifySharp to create the charge and then redirect the user to the charge's
ConfirmationUrl. You'll also need to set the charge's
ReturnUrl so that Shopify will know where to send the user after they accept or decline the purchase.
I'm a big fan of keeping my redirect URLs separate and focused on a single purpse, so I'm going to use "http://localhost:62211/shopify/BundlePurchaseResult" as the charge's return URL. Don't forget to change "localhost:62211" to whatever your app's localhost or domain URL is! Also, remember that Shopify's redirect URLs are case-sensitive — "/BundlePurchaseResult" is not the same as "/bundlepurchaseresult".
(Once you've figured out which redirect URL you want to use, head over to your app's Shopify settings in your partner dashboard and add the new redirect URL to the "Redirection URLs" setting. Shopify will deny your charge request if you try to use a redirect URL that isn't in your app's settings.)
We'll want to pass the text message bundle's id to our redirect handler, so make sure you attach it to the charge's
(Important! ShopifySharp was recently updated with the application charge feature. If you're not using ShopifySharp, you can install it by running
install-package ShopifySharp in Visual Studio's Package Manager Console. If you are using ShopifySharp, you'll need to be on at least version 1.6.0. You can make sure it's updated to the latest version by running
If you haven't had a chance to read through the Shopify billing guide yet — or you just need a quick refresher — here's what you need to know about charges (both the recurring subscription and in-app purchase flavors):
- After creating a charge with
service.CreateAsync, the charge's
ConfirmationUrlproperty will be filled in by Shopify. You must redirect the user to that URL where they can either confirm or decline the charge.
- After creating a charge, the charge's
Statuswill be set to
Pending. When a user accepts the charge, it will be set to
Accepted. If they decline it, it will be set to
- A charge still has to be activated after a user has accepted it, and you can only activate a charge if the status is
Accepted. When you activate an application charge (in-app purchase), the user will be billed immediately. When you activate a recurring (subscription) charge, the charge's free trial will start and then they'll be billed when it ends.
- Subscription charges can be deleted (canceled) only if they've been activated. In-app purchase charges cannot be deleted at all. However, both types will be deleted by Shopify, automatically, if their status is still
Pendingafter 48 hours.
With that knowledge permeating your brain waves, let's move on to create the
DashboardController.SendTextMessage action. This is a very simple one that's just going to decrement the user's
TextMessagesAllowed property. Of course, if your app really offered sending text messages, this would be the place to do that.
Handling the purchase result
With those two actions now written and ready to go, let's finish it up with the last bit of code you'll need to write for handling in-app purchases in your Shopify application. We just set up a new redirect URL for our bundle purchases, and they should be pointing to "/shopify/BundlePurchaseResult", so let's head over to the
ShopifyController and add the
Just like the controller's
ChargeResult action, it's going to be async and accept a string parameter named
shop, a long parameter named
charge_id, and the string parameter
bundleId that we set when creating the charge. Unlike the
ChargeResult action, this one is going to be marked with your
[RequireSubscription] attribute, which will ensure that any user hitting the new action has already logged in, connected their shop and accepted your subscription charge.
Like a lot of our actions, it's going to immediately pull in the user too. Then, it will use the
bundleId parameter to figure out which bundle they were trying to purchase.
Next up, we'll use ShopifySharp's
ShopifyChargeService to pull in the charge itself using the
charge_id parameter. Now here's the tricky part: if the user has waited more than 48 hours (for some reason) to accept or decline the charge, Shopify will delete it automatically. With that in mind, if ShopifySharp throws a "Not found" exception when trying to pull in the charge, we'll know that it has been deleted and the user will have to accept a new one.
Fortunately, we already know which bundle the user was trying to buy, so we could automatically recreate it and send the user back to accept it. However, your user would have know idea what just happened and probably couldn't be blamed for assuming that you're just trying to charge them twice for the same purchase.
So instead of recreating the charge and immediately sending them back, we'll use the
TempData class to add an error message and redirect the user back to the dashboard. Remember, we set up the
DashboardController.Index action to check for a
TempData error and then display it in the form, so this is an effective way to tell the user that something went wrong and they need to purchase the bundle again.
If the charge hasn't been deleted, we'll next check that its status is
Accepted. Remember, a charge cannot be activated if its status isn't
Accepted. Assuming that the user has accepted the charge, we'll activate it, increase their
TextMessagesAllowed property and send them back to the dashboard where they can send text messages to their heart's content.
Testing your in-app purchases
Because we set the charge's
Test property to true, we can now easily test the process of purchasing a bundle without charging your own credit card. After firing up your application on debug mode, creating a new account, connecting a Shopify store and then accepting the subscription charge, you should see the "Purchase Messages" screen at /Dashboard.
Let's purchase the single message bundle. You should be redirected to Shopify to accept or decline the charge.
Declining the charge should send you back to /shopify/BundlePurchaseResult, which will then redirect you back to the dashboard with an error that says you need to accept the charge.
Let's do as the error says and purchase the single message bundle again, this time accepting the charge. You'll be sent back to /shopify/BundlePurchaseResult where your app will activate the charge (immediately adding the charge to the shop's invoice) and then send you to the dashboard.
This time, you should see the "Send Message" form.
And finally, sending a message should decrement your
TotalMessagesAllowed down to zero, prompting you to buy another bundle.
That's it! Now you can create in-app purchases in your Shopify app and start hunting those elusive whales — or just provide a better experience for your customers. You know, whichever.
Other uses for application charges
While this tutorial has focused on using application charges for in-app purchases, there is one other way that you could use an application charge in your app. Rather than charging your users each month for their use of your app, you can instead use the application charge API to charge them a flat, one-time fee of e.g. $19.95 in exchange for lifetime access to your app.
To do that, you'd do something similar to the following:
- Add some kind of boolean property to your user model such as
- Modify the
[RequireSubscription]attribute to check for that flag.
- Replace the recurring charge in
RegisterController.Connectpost action with a single application charge.
ShopifyController.ChargeResultto activate a single charge rather than a subscription charge, and have it set the user's
HasPaidflag to true.
Learn how to build rock solid Shopify apps with C# and ASP.NET!
Did you enjoy this article? I wrote a premium course for C# and ASP.NET developers, and it's all about building rock-solid Shopify apps from day one.
Enter your email here and I'll send you a free sample from The Shopify Development Handbook. It'll help you get started with integrating your users' Shopify stores and charging them with the Shopify billing API.