In app purchases - my working solution using PayPal

Many have asked for a way to support in app purchases. While everyone waits for the AG platform to offer a solution, I have been attempting to meet this need using PayPal. It’s a little clunky, but it seems to work. The beauty of this is it will allow your users to pay using PayPal, Venmo, or a credit card.

For those that might find this workaround useful, I’ll try to give a detailed description of how to build this into your app.

Concept Overview

  • Your app will keep track if it’s the free version or paid version using a “paidApp” app variable
  • paidApp variable will be saved (and loaded on app load) so this setting persists
  • Premium features of your app will only function if paidApp variable is true
  • When a premium feature is attempted when paidApp is false, the limitation is described and the user is presented with the option to pay to upgrade the app
  • If user chooses to upgrade, a WebView is opened that presents PayPal button(s)
  • The user clicks on the button and PayPal code takes over (for login, source of funds, etc.)
  • When the payment completes successfully, an appgyver flow is triggered, which sets paidApp to true and saves this setting
  • Now the app’s premium features will function without limitation

While this method could be duplicated to offer a number of upgrades for various premium features, for simplicity I’m going to describe a single purchase example for enabling all premium features with one purchase.

Prerequisites

This solution assumes you have the following:

  • A PayPal account (I don’t think this needs to be a Business account, but not sure since that’s what I tested with)
  • A place to host a couple of HTML files (your company web site)
  • An AppGyver app with premium features that you want to cripple (or limit) until the user pays to upgrade

paidApp Variable

First we need to create the paidApp variable and manage the loading and saving of it. This is probably common knowledge for many of you, but in the interest of completeness, I’ll cover it here.

  1. Create an app variable called paidApp of type true/false (default is false)
  2. In Data tab, create a new Client-side storage resource called “settings”
  3. In addition to the default ‘id’ property, add another property called paidApp of type true/false
  4. In the Global canvas, create a flow on app launch to load the saved setting (or create it on the first launch)

This flow attempts to load the single record from the ‘settings’ resource (ID is appSettings or anything you like). If the initial attempt to get it fails (because it doesn’t yet exist), the 2nd block creates the record (custom object with ID=appSettings and Paid=false. Then we try to load the settings record again (as it should always succeed now). Then the last block sets the state of the paidApp app variable to the output of the previous block: Final Get Settings / Record: paidApp

  1. Also in the Global canvas, add another flow to handle saving the updated record if the paidApp variable is ever changed.

SettingsSave

For the Update Record block, use the same ID as above (appSettings) and in the custom object, set paidApp to the current value of the app variable of the same name.

PayPal Developer Sandbox

To make it easier to test this functionality, create sandbox accounts (one for the user and one for you the merchant) using the PayPal Developer Dashboard. See docs about this. This will allow you to role-play paying with various payments sources, currencies, etc. to make sure everything works properly. Note the email address and password for the personal account, as this will be the credentials you use when testing how the user would pay for the app upgrade.

Next, visit My apps & credentials and create an app (in sandbox, not live), attaching it to the email address of the business sandbox account created above. Copy the long Client ID that is assigned to this app (you will need it later).

Hosted HTML Pages

You will need two pages to make this work. Working a bit backwards:

  1. thanks.html – download here thanks.html (366 Bytes)
    The content of this page isn’t really important as the user will never see this (if everything works).
    Upload this file to the site where this will be hosted, and note the full URL to the file (as this will be needed later).

  2. upgrade.html – download sample here upgrade-sample.html (1.3 KB)
    You will need to modify this file to suit your needs. At the very least, you need to modify the following:

  • Customize & style the readable text: Upgrade app to Pro for only…
  • Replace the YOUR_CLIENT_ID_HERE with the long string you copied from the sandbox app you created in your PayPal Developer Dashboard
  • Edit the description “Upgrade MyApp to Pro” to better suit your app
  • Edit {“currency_code”:“USD”,“value”:2.99} for the desired payment you wish
  • Edit the URL http://yoursite.com/myapp/thanks.html to match the URL where you put the thanks.html file.
    Rename this file (to remove -sample) and upload it to the site where this will be hosted, and note the full URL to it (as this will be needed later when you create the AppGyver upgrade page).

The style and various PayPal options embedded in this file may not suit your taste. Go to PayPal’s Button Factory to make your choices and have it spit out the code that you can place into this file.

Upgrade Page

Now that you have the HTML files that tie into PayPal’s button, you are ready to create the AppGyver upgrade page. This new page will only be visited when the user decides to pay for the app.

  1. Add a WebView component (from the component market) to this page. Feel free to style the page and the WebView size to taste and to match the rest of your app.
  2. Set the WebView’s URL to the full URL where your upgrade.html (you created above) is hosted
    Note that I tried to use the HTML Content field rather than an externally hosted HTML file, but I couldn’t make this work for some reason. Too big??

Now add the following flow for the WebView.

This event is triggered whenever the WebView’s location changes. The first block checks to make sure that we’re at the thanks.html page (not the initial upgrade.html). It’s formula is: CONTAINS(outputs[“Receive event”].event.url, “thanks.html”)

The next block verifies that the transaction was actually completed. It does this by doing a cheesy split to get the status that follows the st= query in the redirect URL. This should really be doing query parsing, but this is okay since your upgrade.html file specifies the format of this (one and only) query. The formula is: SPLIT(outputs[“Receive event”].event.url,"=")[1] == "COMPLETED"

Once it makes it through these first 2 checks, you’re home free. The funds have been transferred and the flow now sets the paidApp variable to true (which automatically saves it in the global canvas). Then it navigates back to the page from which this upgrade page was entered.

Premium Features

Next, add the logic to cripple or limit one or more of your app’s premium features. The specifics of this will depend greatly on your app. A very simple example is a button that performs an action that is only supported when the user has paid for the upgrade.

Extremely simple example: a feature that can only be accessed if the app purchase has previously been made. The following flow controls access to the feature and will redirect to the upgrade process, if necessary.

A more complicated solution might involve allowing a user to take some action a limited number of times unless the app purchase has been previously made.

In this example, a counter allows something to occur up to 5 times, but after that the user is redirected to the upgrade path. To prevent being able to circumvent this by closing and reopening the app, this counter should probably be added to the settings schema and created/loaded/saved along with paidApp.

To make debugging of these premium feature enables easier, you might want to temporarily add a checkbox field somewhere in your app and bind it to the paidApp app variable. By directly controlling the paidApp variable, you can easily test your app’s behavior in the free and paid states. Don’t forget to remove this checkbox before distributing your final app :wink:

For extra finesse, you can style buttons based on the state of paidApp variable (for example, to show features ghosted out when paidApp == false).

Test!

Test, test, test! Take advantage of the sandbox and try paying for the upgrade in a number of ways. Using the PayPal developer portal, you can even simulate failed payments. Make sure everything responds properly. I accept no liability if this fails to collect money for you :stuck_out_tongue:

Security

I’m unaware of a way for the app user to direct the WebView to a location of their choice. If there is a way, however, it would be pretty easy for them to spoof the logic described here and get an upgraded app without paying. There are ways to solve that, if so, but I didn’t complicate things with that on the assumption that the WebView is locked down. (Correct me if I’m wrong.)

Go Live

Now you’re ready to switch from the sandbox environment to the live environment.

  1. In the PayPal Developer Dashboard, create another app, this time selecting Live environment. This should be attached to your actual paypal account, rather than a sandbox account.
  2. Modify the Client ID value in your upgrade.html file to use the client ID for this live app (replacing the Client ID from the sandbox app)
  3. Upload the modified upgrade.html to the same place as before (overwriting the sandbox version)
    No additional changes are needed to the AppGyver app to go live.

Maybe you will find this solution workable, at least until we have something better to use.

10 Likes

Very nice! Thanks for that!!! :clap: :clap: :clap:

Great Tutorial. Thank you for this! :+1:

Note that the sample html file I posted in the tutorial renders pretty horribly on a mobile device, at least on a phone. By adding a media query to adapt things for a smaller screen (plus a background image and some other CSS styling), I was able to make this a MUCH better user experience. This is all pretty common knowledge that you can easily find with a google search.

@Marty_Flickinger

Hi Marty! Thank you for your post. I need to implement IAP for my app to be viable - does this method utilise Apple / Google IAP - and if not won’t your app be rejected by the app store as per guidelines??

Thank you again for your post this could potentially be an absolute life saver!

Anuj

@Anuj_Nayyar

Correct, this method does not use Google or Apple IAP system, so it is only useful if you distribute the app yourself, not via the those stores. The exception is for South Korea, which was granted an exception recently. At least for Google. Stripe and other payment methods have this same problem. We’re all grasping for interim solutions until AppGyver supports this natively.

I should probably have added this disclaimer at the top of the post.

1 Like

Ahh cool thanks for the clarification and useful post anyway mate!

Hi community,

Do you think it would be legal to sell a webapp upgraded with this PayPal in-app purchase solution, and at the same time, sell the same webapp but without this upgrade on the Google and Apple stores? At the same price, of course :wink:

I dont see any reason why couldn’t you, except from the ethical part…
also im not sure how you are planning to use a web app in google and apple store

1 Like

Thank for your reply @Dimos_Vamvourellis, I plan to use a web app accessible through my website with a web app build, and a native app build for the stores. In this way, I won’t have to pay any commission to the stores for the users I drive to my website by myself, and I will have the exposure from the stores by giving them the commission.

@Marty_Flickinger I have a question for you, how will it work if I don’t have authentification through my web app. Will the user have to pay for each device he will use? Because when I use my phone, the session is different that the session I opened through my computer, for example… No?

If you have no authentication, how do you intend separating paid users from non-paid one?

So it means the user who has paid through a device will be able to use the app whenever he wants on that device after payment. No?

Hi community,

Just to say that I succeeded in implementing @Marty_Flickinger solution, but by using Stripe instead of PayPal. I was able to create a customized payment page requiring card number, exp month and year, and able to process a payment through Stripe Payments platform. I used info from this post and from Stripe implement instruction.

It this with a webapp or have you also had it approved onto the Playstore and app store?

It is a webapp I install on my website with this kind of IAP, and I will also publish the same app without IAP and with a native build in the stores.

One thing is missing in this guide. At the step “paidApp Variable”, you also have to add a Data variable from settings resource / Single data record / ID: appSettings

Hi! Any success with publishing this workaround on playstore/appstore? Thanks :wink: