Implementing iOS Promotional Offers with IOS Native
Stanislav Osipov
Stan’s Assets - Dedicated team | Top Asset Store publisher | Games, AR/VR, 3D Art, Unity plugins/SDKs
The "promotional offers" is a relatively new cool iOS feature available from iOS 12.2. Recently I finished a feature request from one of the IOS Native plugin users and would like to share some good articles about it and also a few code snippets on how to implement it.
First of all, here is a couple of awesome articles I think you should read before jumping to implementing this feature in your game.
But for now, let's just go to the short description of how to do it.
1. Pull offer info.
Pull the product data but it's id if you haven't got all the product info yet (normally you pull all the product info as a store initialization process.)
var productsRequest = new ISN_SKProductsRequest(new List<string>{"my.product.id"});
Once a request is sent, we can print available discounts for our product.
productsRequest.Start(response => { if (response.IsSucceeded) { // We only interested in 1st product // since we requesting only 1 is in this sample: var product = response.Products[0]; foreach (var discount in product.Discounts) { Debug.Log($"Product has available Discount: " + $"{discount.LocalizedPrice}"); } } });
The available discounts are presented by ISN_SKProductDiscount model.
2. Determine Eligibility
In order for an offer to be applied, a user either needs to have an active or lapsed subscription. + any additional eligibility check logic you would like to add based on your app business logic.
3. Sign
This is probably the trickiest part. We need to convert our ISN_SKProductDiscount into an ISN_SKPaymentDiscount. I will post here part of Jacob Eiting guide from Configuring iOS Subscription Offers article.
The constructor for ISN_SKPaymentDiscount provides 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.
First, generate your subscription key, you can do this from the “Users and Access” section of App Store Connect.
Configuring your key will trigger a one-time download of a p8 file of your private key. Don’t lose it! You will need this key every time you want any user to redeem an offer. Apple provided a signing guide for doing so but it’s light on specifics. But don’t worry! We don’t have to learn the Elliptic Curve Digital Signature Algorithm, we can use Python.
First, install the ecdsa package for Python:
pip install ecdsa
Next we need to convert your p8 key file into a DER file readable by the ecdsa library. Luckily the OpenSSL command line tool can do this.
openssl pkcs8 -nocrypt -in SubscriptionKey_XWSXTGQVX2.p8 -out cert.der -outform der
With the converted key, the following will generate the needed signature:
import json import uuid import time import hashlib import base64 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, key_id, product, offer, application_username, str(nonce), # Should be lower case str(timestamp)]) # 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'), hashfunc=hashlib.sha256, sigencode=sigencode_der) encoded_signature = base64.b64encode(signature) print(str(encoded_signature, 'utf-8'), str(nonce), str(timestamp), key_id)
This is just a proof of concept. You will want this on your server and perhaps have some logic to determine, for a given user, the requested offer is appropriate. This forces the developer to use off device control for granting offers.
Once you’ve generated the signature, nonce and timestamp send these along with the key_id back to your app where we can create an ISN_SKPaymentDiscount.
4. Display an offer
The following code snippet shows how to display an offer using our ISN_SKPaymentDiscount object.
var payment = new ISN_SKMutablePayment(product); payment.ApplicationUsername = appUserID; payment.PaymentDiscount = discount; ISN_SKPaymentQueue.AddPayment(payment);
And that's it, this is how you do it :)