Slide 1

Slide 1 text

iOS: In-App Purchases Ali Rantakari Mobile Fortnightly • 20.12.2013

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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!)

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Purchasing • Register payment queue observer • Always, right away when the app launches • When user chooses to buy something,
 Initiate payment • -[SKPayment paymentWithProduct:] • -[SKPaymentQueue addPayment:]

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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*)

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

Receipt Validation: On the client • Only possible with iOS 7 Grand Unified Receipts • OpenSSL, ASN.1 … nontrivial code required

Slide 14

Slide 14 text

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.

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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…

Slide 20

Slide 20 text

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”

Slide 21

Slide 21 text

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)