Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOS In-App Purchases

iOS In-App Purchases

Presented at the Futurice Mobile Fortnightly in December 2013.

Ali Rantakari

December 20, 2013
Tweet

More Decks by Ali Rantakari

Other Decks in Programming

Transcript

  1. Types of Products • Consumable • e.g. a bag of

    100 gold coins • Can be purchased multiple times • Can (obviously) not be restored • Non-consumable • e.g. Red shorts for your David Hasselhoff character in Baywatch: The Game • Can be purchased only once • Can be restored (e.g. onto another device) • Subscription • Auto-renewable or not
  2. Configuring Products • IAP Product info entered into Apple’s system

    through iTunes Connect • Each product has: • Unique identifier (you choose this) • Price tier (just like with apps) • Title and description (localized!)
  3. Presenting Products • Determine product IDs • Hardcode into app

    • Or download from your server • Get SKProduct objects using product IDs • SKProductsRequest • SKProduct objects contain all the info you configured in iTunes Connect
  4. Product Presentation • IAP purchase dialogs are localized based on

    the logged-in user’s App Store region — not based on the device language like everything else! • If your Apple ID is using the Finnish App Store, your IAP dialogs will be in Finnish even if your device language is English
  5. Product Presentation • If you display product prices in your

    own UI, remember to use the price locale from the SKProduct! • SKProduct.price (NSDecimalNumber*) • SKProduct.priceLocale (NSLocale*) @implementation SKProduct (MYExtensions) - (NSString *) myprefix_formattedPrice { NSNumberFormatter *nf = [[NSNumberFormatter alloc] init]; [nf setFormatterBehavior:NSNumberFormatterBehavior10_4]; [nf setNumberStyle:NSNumberFormatterCurrencyStyle]; [nf setLocale:self.priceLocale]; return [nf stringFromNumber:self.price]; } @end
  6. Purchasing • Register payment queue observer • Always, right away

    when the app launches • When user chooses to buy something,
 Initiate payment • -[SKPayment paymentWithProduct:] • -[SKPaymentQueue addPayment:]
  7. Transaction Queue • The queue is per-Apple ID and per-device

    • Persists even if app is deleted and re-installed! • Transactions in the queue can be in different states: • Purchasing (Apple is processing it) • Purchased (Apple is done with it — now you finish it) • Failed • Your app must have a single payment transaction observer registered at all times • SKPaymentTransactionObserver protocol • Only one global observer (the transaction queue is conceptually global so the observer should be, too) • Transaction state transitions can occur at any time
  8. Transaction Queue • Once a transaction enters the “purchased” state,

    Apple has processed it and now you are responsible for “finishing” it (and marking it as such) • Finishing a transaction means your app giving the user what they paid for • Purchased transactions remain in the queue forever until you mark them as finished • -[SKPaymentQueue finishTransaction:] • Once you’ve marked them as “finished”, purchase transactions will disappear forever from the queue
  9. Receipt • Uniquely identifies a purchase transaction • Contains info

    on what product was purchased (and when) • Contains a cryptographic signature • An opaque blob of binary data (NSData*)
  10. Receipts: iOS 6 vs 7 • In iOS 6, each

    transaction had its own receipt •SKPaymentTransaction.transactionReceipt • In iOS 7, an app has one receipt • “Grand Unified Receipt” • Info about the purchase of the app itself + IAPs •NSBundle.mainBundle.appStoreReceiptURL • Do not use -respondsToSelector: to check for this API (exists as a private method on iOS 6)! • Receipt may not exist on disk yet → must use SKReceiptRefreshRequest • e.g. if app has just been installed
  11. Receipt Validation • Either on the client, or on the

    server • Q: Where do you store the thing the user purchased? • If you assign purchased content to a user account on your server, it makes sense to validate receipts there • If you assign purchased content to a user only on the device itself, it makes sense to validate receipts there
  12. Receipt Validation: On the client • Only possible with iOS

    7 Grand Unified Receipts • OpenSSL, ASN.1 … nontrivial code required
  13. Receipt Validation: On the Server • Send base64-encoded receipt to

    Apple’s validation server HTTP API — get back JSON • Never talk to Apple’s validation server from your client — this is insecure. Client talks to your server, which in turn talks to Apple’s validation server.
  14. Receipt Validation: On the Server $ curl -Ssk -H "Content-Type:

    application/json" --data @old_receipt.json https:// sandbox.itunes.apple.com/verifyReceipt | jsonfmt { "receipt": { "original_purchase_date_pst": "2013-11-11 01:29:54 America/Los_Angeles", "purchase_date_ms": "1384162194000", "unique_identifier": "c6f98bb7e61985130158719f0f14b8dc13bdced1", "original_transaction_id": "1000000092888512", "bvrs": "3.4", "transaction_id": "1000000092888512", "quantity": "1", "unique_vendor_identifier": "04FEAF19-EDE1-4AA0-89E6-AF15D141809C", "item_id": "714735785", "product_id": "david_hasselhoffs_red_panties", "purchase_date": "2013-11-11 09:29:54 Etc/GMT", "original_purchase_date": "2013-11-11 09:29:54 Etc/GMT", "purchase_date_pst": "2013-11-11 01:29:54 America/Los_Angeles", "bid": "com.futurice.someapp", "original_purchase_date_ms": "1384162194000" }, "status": 0 } iOS 6 receipt
  15. Receipt Validation: On the Server $ curl -Ssk -H "Content-Type:

    application/json" --data @new_receipt.json https://sandbox.itunes.apple.com/ verifyReceipt | jsonfmt { "status": 0, "environment": "Sandbox", "receipt": { "receipt_type": "ProductionSandbox", "adam_id": 0, "bundle_id": "com.futurice.someapp", "application_version": "3.4", "download_id": 0, "request_date": "2013-12-09 14:55:10 Etc/GMT", "request_date_ms": "1386600110975", "request_date_pst": "2013-12-09 06:55:10 America/Los_Angeles", "in_app": [ { "quantity": "1", "product_id": "david_hasselhoffs_red_panties", "transaction_id": "1000300095673519", "original_transaction_id": "1000004095673519", "purchase_date": "2013-12-09 14:35:04 Etc/GMT", "purchase_date_ms": "1386549704000", "purchase_date_pst": "2013-12-09 06:35:04 America/Los_Angeles", "original_purchase_date": "2013-12-05 12:59:19 Etc/GMT", "original_purchase_date_ms": "1386238359000", "original_purchase_date_pst": "2013-12-05 04:59:19 America/Los_Angeles", "is_trial_period": "false" } ] } iOS 7 receipt
  16. Receipt Validation: On the Server • Apple has two validation

    servers: production and “sandbox” (testing) • Always send receipts to the production server first • If the production server says “this is a sandbox receipt”, then re-send it to the sandbox server • Also do this on your production server — Apple’s reviewers use test environment (“sandbox”) receipts to test your IAPs
  17. Testing • Must configure test Apple IDs into iTunes Connect

    • Dev-signed (or ad-hoc signed) apps use “sandbox” environment for IAPs — must use test Apple IDs; no actual credit card required • Only apps signed by Apple (i.e. released in the App Store) can perform actual IAP payments
  18. Some App Store Rules • No external payment methods allowed

    • “Apps utilizing a system other than the In-App Purchase API (IAP) to purchase content, functionality, or services in an App will be rejected” • Heavily enforced! • The only way around this is to simply avoid any mention of the external payment mechanism, and ensure that the app contains zero links to any web pages from where you could access the external payment system • Cases in point: Spotify, Kindle…
  19. Some App Store Rules • No physical products allowed •

    “Apps using IAP to purchase physical goods or goods and services used outside of the application will be rejected” • Sharing purchased content between platforms: Inconclusive but might be a problem • “Apps that use IAP to purchase credits or other currencies must consume those credits within the App”
  20. Not Covered • Restoring non-consumable purchases • “I bought this

    game character on my iPhone; now I want it on my iPad as well” • Subscriptions • Product downloads hosted by Apple • Newsstand (should probably avoid anyway)