This post is by Team SaaStr from SaaStr
Click here to view on the original site: Original Post
It’s taken a looooong time, but SaaS mobile subscriptions are finally becoming mainstream. SaaS on mobile is nothing new, but having the primary payment and subscription mechanics be on mobile is new. And still early. This is a fun trend to watch so I’m
In the beta for iOS 12.2, Apple announced a new feature for subscription developers called “Subscription Offers.” Subscription offers allow developers to apply custom pricing and trials to existing and lapsed subscriptions. With this guide I’m going to explain what offers are, explore some potential use cases, and then, as usual, share my signature juicy hot take on the state of iOS in-app subscriptions.
Subscription OffersThere are two parts to a subscription offer:
- A configuration in App Store Connect
- A collection StoreKit classes for applying the offer
Configuring OffersIn the App Store Connect interface, Offers (called promotional offers in ASC), can be configured as part of the pricing of an in-app purchase product. There are two fields that you need to specify: the “Reference Name” which is just used in the ASC UI and the “Promotional Offer Product Code” which is what you will actually use to reference and activate an offer in your app. Just like introductory offers, there are three types of subscription offers:
- Pay-up-front — The customer pays once for a period of time, e.g. $0.99 for 3 months. Allowed durations are 1, 2, 3, 6 and 12 months.
- Pay-as-you-go — The customer pays a reduced rate, each period, for a number of periods, e.g. $0.99 per month for 3 months. Allowed durations are 1-12 months. Can only be specified in months.
- Free — This is analogous to a free trial, the user receives 1 of a specified period free. Just to keep things interesting the allowed durations are 3 days, 1 week, 2 weeks, 1 month, 2 months, 3 months, 6 months, and 1 year.
Using a Subscription OfferFor how easy it is to configure offers, actually using them is a different story. In order to prevent fraud, Apple has required that an offer first be cryptographically signed with the newly introduced subscription keys before it’s consumed. I’ll explain step-by-step how to fetch, sign, generate, and apply an offer.
1. SKProductDiscountOur journey begins with an SKProductDiscount. These will represent an impotent version of the promotional offer you set up in ASC. To access the available offers, you need to check the newly added discounts array on
SKProductDiscountclass is the same class used for introductory offers and includes information like the price, the payment mode, number of periods, period duration, and the identifier. Using the
identifier, iterate through the discounts array to find the product discount you want to apply. Then we get it ready for application.
2. Determine EligibilityIn order for an offer to be applied, a user either needs to have an active or lapsed subscription. To determine this you need to verify and check the StoreKit receipt on the device or rely on some other record of the users subscription history. Once you know a user is eligible you can use the
SKProductDiscountto display to the user the appropriate UI, price, etc for giving them the offer.
3. Generate the SignatureThis is where it really gets fun. Before we can apply the offer we need to convert our
SKProductDiscountinto an SKPaymentDiscount. The init methodfor
SKPaymentDiscountprovides some clue to what we’ll need to achieve that:
- identifier — The identifier of the subscription offer
- keyIdentifier — The identifier of the subscription key used to sign the offer
- nonce — A throwaway value generated along with the signature
- signature — The signature itself
- timestamp — The timestamp when the signature was generated.
ecdsapackage for Python:
|pip install ecdsa|
ecdsalibrary. Luckily the OpenSSL command line tool can do this.
|openssl pkcs8 -nocrypt -in SubscriptionKey_XWSXTGQVX2.p8 -out cert.der -outform der|
|from ecdsa import SigningKey|
|from ecdsa.util import sigencode_der|
|bundle_id = ‘com.myapp’|
|key_id = ‘XWSXTGQVX2’|
|product = ‘com.myapp.product.a’|
|offer = ‘REFERENCE_CODE’ # This is the code set in ASC|
|application_username = ‘user_name’ # Should be the same you use when|
|# making purchases|
|nonce = uuid.uuid4()|
|timestamp = int(round(time.time() * 1000))|
|payload = ‘\u2063’.join([bundle_id,|
|str(nonce), # Should be lower case|
|# Read the key file|
|with open(‘cert.der’, ‘rb’) as myfile:|
|der = myfile.read()|
|signing_key = SigningKey.from_der(der)|
|signature = signing_key.sign(payload.encode(‘utf-8’),|
|encoded_signature = base64.b64encode(signature)|
|print(str(encoded_signature, ‘utf-8’), str(nonce), str(timestamp), key_id)|
key_idback to your app where we can create an
4. SKPaymentDiscountUsing the values generated on our server, we can now initialize an
SKPaymentDiscount. This is probably the simplest step, just call the initmethod with the values generated by your server. Ensure that the values you receive back from the server are cast to the correct Cocoa types before passing on to the init method, otherwise it may fail silently (this cost me a day). No matter the validity of the signature, the init will produce an
5. Applying the OfferTo apply an offer, you need to initiate a purchase with StoreKit just like a normal purchase, but you also apply your offer by using an
SKMutablePaymentand setting the
paymentDiscountproperty with the
SKPaymentDiscountgenerated from your signature.
|SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];|
|payment.applicationUsername = self.appUserID;|
|payment.paymentDiscount = discount;|
|[self purchaseProduct:product withPayment:payment completion:completion];|
What can you do with offers?Unfortunately, not that much. Apple’s official line on what they are to be used for is: “Retaining and winning back subscribers.” This is because they are limited to existing or lapsed subscribers. Placing this restriction severely limits their utility, but here are some simple ideas:
Winning Back Churned SubscribersThis seems like a rich vein that is enabled by offers. Say you have a user who has churned, the next time they open the app, you can generate an offer to reduce their price or give them another free trial. Here’s the rub though, if someone quit paying for your app, it’s probably because they just don’t use your app anymore. Most iOS app subscriptions are less than ten dollars a month. You’d have to find enough subscribers for which the decision to stay subscribed is down to say, a five dollar difference in price. In my experience, a churned user is churned for lack of usage, not price sensitivity. The other big reason why this isn’t that exciting is that you can do this already by setting up discounted products and subscription groups. If your app controls which products are shown to a user, it’s not really possible to exploit without jailbreaking.
Retaining Existing Users and IncentivesSince offers can be applied to existing subscriptions, you can temporarily discount a users subscription. This doesn’t seem that interesting as a blanket offer, but it gets really interesting if you think about offers an an incentive. One growth tactic I often hear developers wanting to employ is giving paying users a free month as an incentive for taking some action, usually referring a friend. In the past, this was never possible because it wasn’t possible to programmatically alter a user’s subscription without having them cancel it. With offers, that would be possible.
The ProblemsOffers are cool. It’s neat to have some ability to modify an existing subscription. But, it’s added even more complexity to an already complexsystem for marginal benefit.
Doesn’t Help Price TestingOne of the most common cases for changing the price of a product is the ability to convert new users. Price optimization has always been a great way to maximize your LTV at the top of the funnel. Building a business on mobile subscriptions means high churn, typically north of 20% monthly. At those churn rates, you really have to rely on filling the top of funnel and maximizing LTV through pricing. It’s doubtful that offers will reduce churn enough to make much of a difference.
The Added Crypto is OverkillPaying 👏 customers 👏 aren’t 👏 going 👏 to 👏 hack 👏 you. All of this overhead in requiring an offer to be signed is quite silly. Ostensibly, it is there to prevent a bad actor from hacking your app, adding a
paymentDiscountto a product they’ve already purchased. There are way easier ways to steal content from mobile apps.