詳解 Auto-Renewable Subscription / Detailed Auto-Renewable Subscription

070d33664df274636dad3da21c9b21fc?s=47 rockname
September 06, 2019

詳解 Auto-Renewable Subscription / Detailed Auto-Renewable Subscription

070d33664df274636dad3da21c9b21fc?s=128

rockname

September 06, 2019
Tweet

Transcript

  1. 2.

    ࣗݾ঺հ • גࣜձࣾϛΫγΟ • Ո଒ΞϧόϜΈͯͶ / ΞϓϦ։ൃάϧʔϓ
 iOS(Swift, obj-c) >

    Android(Kotlin, Java) == Rails(ruby) • VTuber͕޷͖ • WWDC 2019 ࢀՃ͠·ͨ͠ ✈ ϩΫωϜ@rockname
  2. 3.

    ࠓ೔࿩͢͜ͱ 1. Auto-Renewable Subscriptionsͱ͸ʁ 2. ΈͯͶʹ͓͚ΔಋೖͷܦҢ 3. جຊతͳ࣮૷ํ๏ 4. αʔόʔؒ௨஌ʹ͍ͭͯ

    5. Status Pollingʹ͍ͭͯ 6. Sandbox؀ڥʹ͓͚Δಈ࡞֬ೝ 7. ϦδΣΫτʹ͍ͭͯؾΛ͚͍ͭͨϙΠϯτ 8. ·ͱΊ
  3. 13.
  4. 14.
  5. 15.
  6. 18.

    લ४උ • App಺՝ۚΛ࡞੒ • Reference Name / Product ID •

    Price / Duration • Subscription Display Name • Subscription GroupΛ࡞੒ • Reference Name • Subscription Group Display Name ʮ"QQ4UPSF$POOFDUϔϧϓʯΑΓ IUUQTIFMQBQQMFDPNBQQTUPSFDPOOFDUEFWGDF App Store Connect্ͰϓϩμΫτΛ࡞੒
  7. 20.
  8. 21.
  9. 22.
  10. 23.
  11. 24.
  12. 25.
  13. 26.
  14. 27.
  15. 28.
  16. 30.
  17. 32.

    class PaymentTransactionObserver: NSObject, SKPaymentTransactionObserver { static let shared = PaymentTransactionObserver()

    private let queue = SKPaymentQueue.default() private override init() { super.init() NotificationCenter.default.addObserver( self, selector: #selector(self.didFinishLaunchingNotification), name: UIApplication.didFinishLaunchingNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(self.willTerminate), name: UIApplication.willTerminateNotification, object: nil ) } deinit { NotificationCenter.default.removeObserver(self) } @objc private func didFinishLaunchingNotification() { queue.add(self) } @objc private func willTerminate() { queue.remove(self) } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { // Handle transaction states here. } }
  18. 33.

    class PaymentTransactionObserver: NSObject, SKPaymentTransactionObserver { static let shared = PaymentTransactionObserver()

    private let queue = SKPaymentQueue.default() private override init() { super.init() NotificationCenter.default.addObserver( self, selector: #selector(self.didFinishLaunchingNotification), name: UIApplication.didFinishLaunchingNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(self.willTerminate), name: UIApplication.willTerminateNotification, object: nil ) } deinit { NotificationCenter.default.removeObserver(self) } @objc private func didFinishLaunchingNotification() { queue.add(self) } @objc private func willTerminate() { queue.remove(self) } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { // Handle transaction states here. } } Observer͸γϯάϧτϯͰ࡞Δ
  19. 34.

    class PaymentTransactionObserver: NSObject, SKPaymentTransactionObserver { static let shared = PaymentTransactionObserver()

    private let queue = SKPaymentQueue.default() private override init() { super.init() NotificationCenter.default.addObserver( self, selector: #selector(self.didFinishLaunchingNotification), name: UIApplication.didFinishLaunchingNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(self.willTerminate), name: UIApplication.willTerminateNotification, object: nil ) } deinit { NotificationCenter.default.removeObserver(self) } @objc private func didFinishLaunchingNotification() { queue.add(self) } @objc private func willTerminate() { queue.remove(self) } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { // Handle transaction states here. } } ΞϓϦىಈ࣌ʹpayment queueʹ௥Ճ
  20. 35.

    class PaymentTransactionObserver: NSObject, SKPaymentTransactionObserver { static let shared = PaymentTransactionObserver()

    private let queue = SKPaymentQueue.default() private override init() { super.init() NotificationCenter.default.addObserver( self, selector: #selector(self.didFinishLaunchingNotification), name: UIApplication.didFinishLaunchingNotification, object: nil ) NotificationCenter.default.addObserver( self, selector: #selector(self.willTerminate), name: UIApplication.willTerminateNotification, object: nil ) } deinit { NotificationCenter.default.removeObserver(self) } @objc private func didFinishLaunchingNotification() { queue.add(self) } @objc private func willTerminate() { queue.remove(self) } func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { // Handle transaction states here. } } ΞϓϦऴྃ࣌ʹpayment queue͔Β࡟আ
  21. 36.
  22. 37.

    App Store͔ΒProduct InformationΛऔಘ • SKPaymentQueue.canMakePayments() ͰϢʔβʔ͕ߪೖͰ͖Δ ͔֬ೝ • App Store

    ConnectͰొ࿥ͨ͠product identifierΛ࢖༻ͯ͠App Store͔ΒϓϩμΫτ৘ใΛऔಘ͢Δ • product identifierͷ؅ཧʹ͸2ύλʔϯ͋Δ 1. BundleʹؚΊΔ 2. αʔόʔͰ؅ཧ͢Δ
  23. 38.

    App Store͔ΒProduct InformationΛऔಘ • SKPaymentQueue.canMakePayments() ͰϢʔβʔ͕ߪೖͰ͖Δ ͔֬ೝ • App Store

    ConnectͰొ࿥ͨ͠product identifierΛ࢖༻ͯ͠App Store͔ΒϓϩμΫτ৘ใΛऔಘ͢Δ • product identifierͷ؅ཧʹ͸2ύλʔϯ͋Δ 1. BundleʹؚΊΔ 2. αʔόʔͰ؅ཧ͢Δ ΈͯͶͰ͸ ʮ2. αʔόʔͰ؅ཧ͢ΔʯͰ࣮૷
  24. 39.

    // Keep a strong reference to the product request. var

    request: SKProductsRequest! func requestProductInformation(with productIdentifiers: [String]) { let productIdentifiers = Set(productIdentifiers) request = SKProductsRequest(productIdentifiers: productIdentifiers) request.delegate = self request.start() }
  25. 40.

    // Keep a strong reference to the product request. var

    request: SKProductsRequest! func requestProductInformation(with productIdentifiers: [String]) { let productIdentifiers = Set(productIdentifiers) request = SKProductsRequest(productIdentifiers: productIdentifiers) request.delegate = self request.start() } ϦΫΤετ͸ڧࢀরͰอ࣋͢Δඞཁ͕͋Δ
  26. 41.

    // Keep a strong reference to the product request. var

    request: SKProductsRequest! func requestProductInformation(with productIdentifiers: [String]) { let productIdentifiers = Set(productIdentifiers) request = SKProductsRequest(productIdentifiers: productIdentifiers) request.delegate = self request.start() } delegateΛηοτͯ͠ϦΫΤετΛ։࢝
  27. 42.

    // SKProductsRequestDelegate protocol method. func productsRequest(_ request: SKProductsRequest, didReceive response:

    SKProductsResponse) { if !response.products.isEmpty { let products = response.products // Custom method. displayStore(products) } for invalidIdentifier in response.invalidProductIdentifiers { // Handle any invalid product identifiers as appropriate. } }
  28. 43.

    // SKProductsRequestDelegate protocol method. func productsRequest(_ request: SKProductsRequest, didReceive response:

    SKProductsResponse) { if !response.products.isEmpty { let products = response.products // Custom method. displayStore(products) } for invalidIdentifier in response.invalidProductIdentifiers { // Handle any invalid product identifiers as appropriate. } } delegateϝιουͰϓϩμΫτͷ഑ྻ Λड͚औͬͯUIʹදࣔ
  29. 44.

    // SKProductsRequestDelegate protocol method. func productsRequest(_ request: SKProductsRequest, didReceive response:

    SKProductsResponse) { if !response.products.isEmpty { let products = response.products // Custom method. displayStore(products) } for invalidIdentifier in response.invalidProductIdentifiers { // Handle any invalid product identifiers as appropriate. } } ΈͯͶͰ͸invalidIdentifierΛ೦ͷҝ Crashlyticsʹඈ͹͍ͯ͠Δ
  30. 45.
  31. 47.
  32. 50.
  33. 51.

    TransactionΛॲཧ͢Δ • SKPaymentTransactionObserver#paymentQueue(_:updatedTransactio ns:) ʹtransaction͕ྲྀΕͯ͘Δ • transactionͷ SKPaymentTransactionState ʹԠͯ͡ద੾ʹॲཧ •

    transaction͸ finishTransaction() ͠ͳ͍͔͗Γqueueʹ࢒Γଓ͚Δ • ߪೖ్தʹΞϓϦΛऴྃͤͯ͞͠·ͬͨϢʔβʔ͸࣍ճىಈ࣌ʹ෮ ؼͰ͖Δ • updatedTransactions Λ͍ͭͰ΋ॲཧͯ݁͠ՌΛϢʔβʔʹ൓өͤ͞ Δ͜ͱ͕Ͱ͖Δঢ়ଶΛߏங͢Δඞཁ͕͋Δ
  34. 52.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO
  35. 53.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO
  36. 54.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO
  37. 55.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO
  38. 56.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO
  39. 57.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO
  40. 58.

    4,1BZNFOU
 5SBOTBDUJPO4UBUF QVSDIBTJOH EFGFSSFE GBJMFE QVSDIBTFE SFTUPSFE ঢ়ଶ ߪೖத ʮ"TLUP#VZʯʹΑ

    ͬͯࢠڙ͕ߪೖΛ੍ ݶ͞Ε͍ͯΔঢ়ଶ ߪೖʹࣦഊ ߪೖʹ੒ޭ લճͷߪೖͷ෮ݩʹ ੒ޭ 6*΁ͷ൓ө ߪೖதͷϓϩάϨε Λදࣔ͢Δ 6*ʹ΋൓ө FSSPSʹΑͬͯϝοη ʔδΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ ϨγʔτͷWBMJEBUJPO ݁ՌΛදࣔ USBOTBDUJPOͷॲཧ   pOJTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO WBMJEBUJPOͷ੒ޭ͋Δ ͍͸Ϩγʔτ͕ظݶ ੾Εͷ৔߹͸ pOTI5SBOTBDUJPO ΈͯͶͰ͸ ʮUI΁ͷ൓өʯͱʮtransactionͷॲཧʯ͸ ׬શʹಠཱ࣮ͯ͠ߦ͍ͯ͠Δ
  41. 59.
  42. 61.

    // Get receipt if available if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,

    FileManager.default.fileExists(atPath: appStoreReceiptURL.path) { do { let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) let receiptString = receiptData.base64EncodedString(options: []) // Send ReceiptData to your server with user information } catch { print("Couldn't read receipt data with error: " + error.localizedDescription) } }
  43. 62.

    // Get receipt if available if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,

    FileManager.default.fileExists(atPath: appStoreReceiptURL.path) { do { let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) let receiptString = receiptData.base64EncodedString(options: []) // Send ReceiptData to your server with user information } catch { print("Couldn't read receipt data with error: " + error.localizedDescription) } } Ϩγʔτ͕ଘࡏ͍ͯ͠Ε͹औಘ
  44. 63.

    // Get receipt if available if let appStoreReceiptURL = Bundle.main.appStoreReceiptURL,

    FileManager.default.fileExists(atPath: appStoreReceiptURL.path) { do { let receiptData = try Data(contentsOf: appStoreReceiptURL, options: .alwaysMapped) let receiptString = receiptData.base64EncodedString(options: []) // Send ReceiptData to your server with user information } catch { print("Couldn't read receipt data with error: " + error.localizedDescription) } } base64encodingͨ͠ϨγʔτΛ Ϣʔβʔ৘ใͱڞʹServer΁POST
  45. 65.

    /verifyReceipt ͷϨεϙϯε • environment: Sandbox͔PROD͕஋ͱͯ͠ೖ͍ͬͯΔɻ • status: ਖ਼ৗͳΒ0ɺͦ͏Ͱͳ͚Ε͹20000୆ͷΤϥʔίʔυ͕ೖ͍ͬͯΔɻ • latest_receipt:

    Base64Τϯίʔυ͞Εͨ࠷৽Ϩγʔτσʔλ • latest_receipt_info: ࠷৽Ϩγʔτ৘ใ͕ೖ͍ͬͯΔɻجຊͪ͜Βͷ৘ใΛ΋ͱʹdbΛߋ৽ ͍ͯ͘͠ɻ cancellation_at ϑΟʔϧυ͕ଘࡏ͢Δ͔Ͳ͏͔ΛݟͯApple ΧελϚʔαϙʔτ ʹΑͬͯΩϟϯηϧ͞Ε͔ͨͲ͏͔Λه࿥͓ͯ͘͠ͱྑ͍ɻ • pending_renewal_info: ࣗಈߋ৽͕༧ఆ͞Ε͍ͯΔɺ͋Δ͍͸աڈʹࣗಈߋ৽ʹࣦഊͨ͠Ϩ γʔτ͕ೖ͍ͬͯΔɻ is_in_billing_retry_period ϑΟʔϧυΛݟͯϦτϥΠத͔Ͳ͏͔Λه ࿥ expiration_intent ϑΟʔϧυΛݟͯظݶ੾Εͷཧ༝Λه࿥͓ͯ͘͠ͱྑ͍ɻ • is-retryable: Apple಺෦ͷΤϥʔʹΑͬͯϨγʔτͷvalidationʹࣦഊͨ͠৔߹ʹtrueͱͳ Δɺͦͷ৔߹retry͢Δͱྑ͍ɻ
  46. 66.
  47. 69.

    ൪֎ฤ: ΤϥʔϋϯυϦϯά • ൃੜ͠͏ΔΤϥʔͷछྨ 1. transactionState ͕ .failed ͷঢ়ଶ 2.

    transactionState ͸ .purchased ͕ͩϨ γʔτͷόϦσʔγϣϯʹࣦഊ
  48. 70.

    1. transactionState͕ .failed ͷঢ়ଶ • AppStoreଆͷΤϥʔͳͷͰɺ localizedDescription ΛAlertʹ දࣔͯ͋͛͠Δ •

    ͨͩ͠ɺߪೖ్தʹΩϟϯηϧΛԡͯ͠΋transaction ͸ .failed ѻ͍ͱͳΔͷͰguardͯ͋͛͠Δඞཁ͕͋Δ
  49. 71.

    1. transactionState͕ .failed ͷঢ়ଶ • AppStoreଆͷΤϥʔͳͷͰɺ localizedDescription ΛAlertʹ දࣔͯ͋͛͠Δ •

    ͨͩ͠ɺߪೖ్தʹΩϟϯηϧΛԡͯ͠΋transaction ͸ .failed ѻ͍ͱͳΔͷͰguardͯ͋͛͠Δඞཁ͕͋Δ if let error = error as? SKError, error.code != .paymentCancelled { DispatchQueue.main.async { self.delegate?.presentAlert(error.localizedDescription) } }
  50. 72.

    2. transactionState͸ .purchased ͕ͩϨγʔ τͷόϦσʔγϣϯʹࣦഊ • ΈͯͶͷ৔߹2ύλʔϯͷΤϥʔΛϋϯυϦϯά 1. αʔόʔଆͰͷ༧ଌෆೳͳΤϥʔ 2.

    ड͚औͬͨϨγʔτ͕͢ͰʹϢʔβʔ(Ո଒)ͱ
 ඥ͍͍ͮͯΔ৔߹ͷΤϥʔ • restoreʹΑͬͯαϒεΫϦϓγϣϯΛ෮ݩ͢Δࡍ ʹൃੜ͠͏Δ (restoreʹ͍ͭͯ͸ޙ΄Ͳ࿩͠·͢)
  51. 77.

    αʔόʔؒ௨஌Λड͚औΔ • POST͞ΕΔJSON͔Βجຊతʹ࢖͏ϑΟʔϧυ͸ҎԼͷ௨Γ • environment: Sandbox͔PROD͕஋ͱͯ͠ೖ͍ͬͯΔ • notification_type: αʔόʔؒ௨஌ͷtype(ৄ͘͠͸ޙड़͠·͢) •

    password: /verifyReceiptͷࡍʹ΋࢖༻ͨ͠shared secret • cancellation_date: AppleͷαϙʔτΛ௨ͯ͠ฦۚॲཧ͕ͳ͞ΕͯΩϟϯηϧ͞Εͨ೔࣌ • latest_receipt: Base64Τϯίʔυ͞Εͨ࠷৽Ϩγʔτσʔλ • latest_receipt_info: latest_receiptΛJSONͰදݱͨ͠΋ͷ • latest_expired_receipt: Base64Τϯίʔυ͞Εͨظݶͷ੾Ε͍ͯΔϨγʔτɺ • latest_expired_receipt_info: latest_expired_receiptΛJSONͰදݱͨ͠΋ͷ • auto_renew_status: ݱࡏࣗಈߋ৽ઃఆத͔Ͳ͏͔ͷϑϥά • auto_renew_status_change_date: ࣗಈߋ৽ͷঢ়ଶΛߋ৽ͨ͠೔࣌
  52. 78.

    αʔόʔؒ௨஌Λड͚औΔ • POST͞ΕΔJSON͔Βجຊతʹ࢖͏ϑΟʔϧυ͸ҎԼͷ௨Γ • environment: Sandbox͔PROD͕஋ͱͯ͠ೖ͍ͬͯΔ • notification_type: αʔόʔؒ௨஌ͷtype(ৄ͘͠͸ޙड़͠·͢) •

    password: /verifyReceiptͷࡍʹ΋࢖༻ͨ͠shared secret • cancellation_date: AppleͷαϙʔτΛ௨ͯ͠ฦۚॲཧ͕ͳ͞ΕͯΩϟϯηϧ͞Εͨ೔࣌ • latest_receipt: Base64Τϯίʔυ͞Εͨ࠷৽Ϩγʔτσʔλ • latest_receipt_info: latest_receiptΛJSONͰදݱͨ͠΋ͷ • latest_expired_receipt: Base64Τϯίʔυ͞Εͨظݶͷ੾Ε͍ͯΔϨγʔτɺ • latest_expired_receipt_info: latest_expired_receiptΛJSONͰදݱͨ͠΋ͷ • auto_renew_status: ݱࡏࣗಈߋ৽ઃఆத͔Ͳ͏͔ͷϑϥά • auto_renew_status_change_date: ࣗಈߋ৽ͷঢ়ଶΛߋ৽ͨ͠೔࣌ ΈͯͶͰ͸ latest_receipt ͷσʔλΛ
 /verifyReceiptʹ౤͛Δϑϩʔʹ৐ͤΔ
 Α͏ʹ࣮૷
  53. 79.

    ͦΕͧΕͷ௨஌λΠϓͷछྨ */*5*"-@#6: αϒεΫϦϓγϣϯΛ࠷ॳʹߪೖͨ͠ͱ͖ʹड͚औΔɻ ͍ͭͰ΋WFSJGZͯ͠࠷৽ϨγʔτΛऔͬͯདྷΕΔΑ͏ʹɺϨγʔτσʔλΛอଘ͠ ͓ͯ͘ɻ $"/$&- "QQMFͷαϙʔτܦ༝ɺ͋Δ͍͸"QQ4UPSFͰαϒεΫϦϓγϣϯΛΞοϓάϨʔ υͨ͠ࡍʹड͚औΔɻ TVCTDSJQUJPO@EBUFϑΟʔϧυ͕͍ͭΩϟϯηϧ͞Ε͔ͨΛ࣋ͭɻ 3&/&8"-

    Ұ౓ࣗಈߋ৽ʹࣦഊͨ͠ظݶ੾ΕͷαϒεΫϦϓγϣϯ͕ͦͷޙແࣄʹߋ৽Ͱ͖ ͨͱ͖ʹड͚औΔɻ FYQJSFT@EBUFΛݟͯ࣍ͷߋ৽࣌ࠁΛ֬ఆͤ͞Δɻ */5&3"$5*7& @3&/&8"- ΞϓϦܦ༝ɺ͋Δ͍͸"QQ4UPSFͷΞΧ΢ϯτઃఆ͔ΒαϒεΫϦϓγϣϯΛߋ৽ ͞Εͨࡍʹड͚औΔɻ αʔϏεΛ͙͢ʹར༻Ͱ͖Δঢ়ଶʹͯ͋͛͠Δඞཁ͕͋Δɻ %*%@$)"/(& @3&/&8"-@13&' ϓϥϯͷάϨʔυΛมߋ͞Εͨࡍʹड͚औΔɻ ݱߦͷϓϥϯʹ͸ಛʹӨڹ͸ͳ͍ɻ %*%@$)"/(& @3&/&8"-@45"564 ࣗಈߋ৽ͷઃఆʹมߋ͕͋ͬͨࡍʹड͚औΔɻ BVUP@SFOFX@TUBUVT@DIBOHF@EBUFͱBVUP@SFOFX@TUBUVTΛݟ͍ͯͭมߋ͕͋ͬ ͨͷ͔ɺݱࡏͷࣗಈߋ৽ͷঢ়ଶΛ֬ೝ͢Δɻ
  54. 80.

    ࠓळ͔Β௥Ճ͞ΕΔ৽͍͠௨஌λΠϓ • WWDC19 ʮIn-App Purchases and Using Server-to-Server Notificationsʯʹͯൃද •

    ҎԼͷ3͕ͭ৽͘͠ՃΘΔ • DID_FAIL_TO_RENEW • DID_RECOVER • PRICE_INCREASE_CONSENT • DID_RECOVER͸طଘͷRENEWAL௨஌λΠϓͱஔ͖׵ΘΔ New
  55. 107.

    ࠓळ͔Β৽͘͠௥Ճ͞ΕΔunified_receipt • WWDC19ʮIn-App Purchases and Using Server-to-Server Notificationsʯ ʹͯൃද •

    unified_receipt ͱ͍͏৽͍͠ϑΟʔϧυ͕௥Ճ͞ΕΔ • /verifyReceipt Λୟ͔ͳ͍ͱಘΒΕͳ͔ͬͨϑΟʔϧυΛऔಘͰ͖Δ • ΋͏ latest_receipt ͱ latest_receipt_info Λࢀর͢Δඞཁ͕ͳ͘ɺ / verifyReceiptΛୟ͘ඞཁ΋ͳ͍ • (receiptِ͕૷͞ΕΔՄೳੑΛߟ͑Δͱ/verifyReceiptΛୟ͍͓͖͍ͯͨ ؾ࣋ͪʹͳΔ͕ɺՌͨͯ͠…) New
  56. 108.

    ࠓळ͔Β৽͘͠௥Ճ͞ΕΔGrace Period • WWDC19 ʮIn-App Purchases and Using Server-to-Server Notificationsʯʹͯൃද

    • Billing ErrorʹΑΔղ໿ϢʔβʔΛݮΒ͢΂͘ɺ༗ޮظݶʹରͯ࣋ͨͤ͠Δ༛༧ظؒ • ࣮͸WWDC18ͷηογϣϯʮEngineering SubscriptionsʯͰ͸ࣗલͰͷ࣮૷Λ ਪ঑͞Ε͍ͯͨ • ΈͯͶͰ͸ࣗલͰ࣮૷ࡁΈʂ • Grace Periodظؒத΋αʔϏεΛఏڙͯ͋͛͠Δ͜ͱͰɺղ໿཰ΛݮΒͭͭ͠ɺͦ ͷظؒ෼ͷ௥Ճใु΋ಘΔ͜ͱ͕Ͱ͖Δ • Appleͷ౷ܭͱͯ͠͸ 16೔ؒ ͕ߋ৽ʹࣦഊͨ͠Ϣʔβʔͷ80%ΛऔΓ໭ͤΔظؒͷ Α͏Ͱɺͦ͜ΛGrace Periodͱͯ͠ఆΊ͍ͯΔ New
  57. 109.

    Grace Periodͷ࣮૷ํ๏ • WWDCͷηογϣϯͰड़΂ΒΕ͍ͯͨͷ͸ҎԼͷ3Step ͚ͩͰ͋Δ 1. App Store ConnectͰGrace PeriodΛߏ੒

    2. /verifyReceipt ͷ৽͍͠ϑΟʔϧυ grace_period_expires_date ΛϋϯυϦϯά 3. Graceظؒ·ͰαʔϏεΛఏڙ͠ଓ͚ΔΑ͏ʹ࣮૷
  58. 111.

    Status Pollingͱ͸ʁ • ظݶ੾ΕલޙͷϨγʔτΛ /verifyReceipt ʹରͯ͠tokenͷΑ͏ʹ౤͛Δ͜ ͱͰ࠷৽ϨγʔτΛऔಘͯ͠dbͷঢ়ଶΛߋ৽͢Δ͜ͱΛࢦ͢ • Ϩγʔτ͸ΞϓϦͷىಈ(͋Δ͍͸αʔόʔؒ௨஌)ʹΑͬͯͷΈߋ৽͞ΕΔ ͷͰɺ΋͠WebͰར༻Ͱ͖ΔαʔϏεΛఏڙ͍ͯ͠Δ৔߹ɺStatus

    Polling Ͱ࠷৽ͷঢ়ଶΛҡ࣋͢Δඞཁ͕͋Δ • ΈͯͶϓϨϛΞϜͰ͸Web্ͷػೳΛఏڙ͍ͯͨͨ͠Ί
 ࣮૷͓ͯ͘͠ඞཁ͕͋ͬͨ • ͪͳΈʹ͜ͷख๏͸WWDC18 ʮEngineering SubscriptionsʯʹΑͬͯޠΒ ΕΔͷΈͰυΩϡϝϯτԽ͞Ε͍ͯͳ͍
  59. 114.

    Sandbox؀ڥ • App Store ConnectͰςελʔΞΧ΢ϯτΛ࡞੒͢ Δ͜ͱͰςετ؀ڥͰಈ࡞֬ೝ͕Ͱ͖Δ • ʮUsers and Accessʯͷϖʔδ͔Β؆୯ʹ࡞੒Մೳ

    • ֤SandboxϢʔβʔʹରͯ͠طଘͷApple IDʹඥͮ ͚ΒΕ͍ͯͳ͍ϝʔϧΞυϨεΛઃఆ • νʔϜͰڞ༗͓ͯ͘͠ͱΑ͍
  60. 117.

    Sandbox؀ڥʁຊ൪؀ڥʁʁ • αʔόʔؒ௨஌ • ઃఆͰ͖ΔURL͸̍ͭͳͷͰɺຊ൪αʔόʔʹͷΈ௨஌͸POST͞ΕΔ • POST͞Εͨ௨஌ͷenvironmentΛݟͯ `Sandbox` Ͱ͋Ε͹։ൃαʔόʔ΁Ϧμ ΠϨΫτ͢Δඞཁ͕͋Δ

    • /verifyReceipt • ຊ൪: https://buy.itunes.apple.com/verifyReceipt
 Sandbox: https://sandbox.itunes.apple.com/verifyReceipt
 • Appleͷ৹ࠪ͸Sandbox؀ڥͰߦΘΕΔ •ʮΈͯͶ: ຊ൪ + Receipt: Sandboxʯ ͷঢ়گ͕ੜ·ΕΔ • Ұ౓ϨγʔτΛຊ൪ʹPOSTͯ͠20007ͷΤϥʔίʔυ͕ฦ͖ͬͯͨΒSandbox ʹ౤͛௚͢ඞཁ͕͋Δ
  61. 123.

    2. Refresh Receipt • Apple͔Β࠷৽ͷϨγʔτΛཁٻͯ͠ϩʔΧϧʹอଘ͢Δ • ৽͘͠transactionΛൃߦ͸͠ͳ͍ // Keep a

    strong reference to the product request. var request: SKProductsRequest! func requestProductInformation(with productIdentifiers: [String]) { let productIdentifiers = Set(productIdentifiers) request = SKReceiptRefreshRequest( receiptProperties: [SKReceiptPropertyIsExpired: false] ) request.delegate = self request.start() }
  62. 124.

    2. Refresh Receipt • Apple͔Β࠷৽ͷϨγʔτΛཁٻͯ͠ϩʔΧϧʹอଘ͢Δ • ৽͘͠transactionΛൃߦ͸͠ͳ͍ // Keep a

    strong reference to the product request. var request: SKProductsRequest! func requestProductInformation(with productIdentifiers: [String]) { let productIdentifiers = Set(productIdentifiers) request = SKReceiptRefreshRequest( receiptProperties: [SKReceiptPropertyIsExpired: false] ) request.delegate = self request.start() } validationʹ੒ޭ͠ͳ͍ݶΓfinishTransaction() Λݺͼग़͢͜ͱ͸ͳ͍ͷͰɺΈͯͶͰ͸Refresh ReceiptʹΑͬͯRestoreΛ࣮૷͍ͯ͠Δ
  63. 130.

    ߪೖγʔϯʹදࣔ͢΂͖৘ใΛ࿙Εͳ͘ ྻڍ͢Δ • αϒεΫϦϓγϣϯͷ໊લɺظؒɺαϒεΫϦϓγϣϯͷ֤ظؒʹఏڙ͞ΕΔίϯςϯπ΍α ʔϏε • Ϣʔβʔ΁ͷ੥ٻํ๏ɺϢʔβʔ͕αϒεΫϦϓγϣϯΛ؅ཧ͢Δํ๏ • ߪೖ֬ఆ࣌ʹɺࢧ෷͍͸ϢʔβʔͷApple IDΞΧ΢ϯτʹ՝ۚ͞ΕΔ͜ͱ

    • ݱࡏͷظ͕ؒऴྃ͢Δগͳ͘ͱ΋24࣌ؒલʹϢʔβʔ͕Ωϟϯηϧ͠ͳ͍ݶΓɺαϒεΫ Ϧϓγϣϯ͸ࣗಈతʹߋ৽͞ΕΔ͜ͱ • ݱࡏͷظ͕ؒऴྃ͢Δલͷ24࣌ؒҎ಺ʹɺΞΧ΢ϯτʹߋ৽ֹ͕ۚ՝ۚ͞ΕΔ͜ͱ
  64. 131.

    ߪೖγʔϯʹදࣔ͢΂͖৘ใΛ࿙Εͳ͘ ྻڍ͢Δ • αϒεΫϦϓγϣϯͷ໊લɺظؒɺαϒεΫϦϓγϣϯͷ֤ظؒʹఏڙ͞ΕΔίϯςϯπ΍α ʔϏε • Ϣʔβʔ΁ͷ੥ٻํ๏ɺϢʔβʔ͕αϒεΫϦϓγϣϯΛ؅ཧ͢Δํ๏ • ߪೖ֬ఆ࣌ʹɺࢧ෷͍͸ϢʔβʔͷApple IDΞΧ΢ϯτʹ՝ۚ͞ΕΔ͜ͱ

    • ݱࡏͷظ͕ؒऴྃ͢Δগͳ͘ͱ΋24࣌ؒલʹϢʔβʔ͕Ωϟϯηϧ͠ͳ͍ݶΓɺαϒεΫ Ϧϓγϣϯ͸ࣗಈతʹߋ৽͞ΕΔ͜ͱ • ݱࡏͷظ͕ؒऴྃ͢Δલͷ24࣌ؒҎ಺ʹɺΞΧ΢ϯτʹߋ৽ֹ͕ۚ՝ۚ͞ΕΔ͜ͱ • Ϣʔβʔ͸App StoreͷΞΧ΢ϯτઃఆͰαϒεΫϦϓγϣϯΛ؅ཧ͓ΑͼΩϟϯηϧͰ ͖Δ͜ͱ
  65. 132.

    ߪೖγʔϯʹදࣔ͢΂͖৘ใΛ࿙Εͳ͘ ྻڍ͢Δ • αϒεΫϦϓγϣϯͷ໊લɺظؒɺαϒεΫϦϓγϣϯͷ֤ظؒʹఏڙ͞ΕΔίϯςϯπ΍α ʔϏε • Ϣʔβʔ΁ͷ੥ٻํ๏ɺϢʔβʔ͕αϒεΫϦϓγϣϯΛ؅ཧ͢Δํ๏ • ߪೖ֬ఆ࣌ʹɺࢧ෷͍͸ϢʔβʔͷApple IDΞΧ΢ϯτʹ՝ۚ͞ΕΔ͜ͱ

    • ݱࡏͷظ͕ؒऴྃ͢Δগͳ͘ͱ΋24࣌ؒલʹϢʔβʔ͕Ωϟϯηϧ͠ͳ͍ݶΓɺαϒεΫ Ϧϓγϣϯ͸ࣗಈతʹߋ৽͞ΕΔ͜ͱ • ݱࡏͷظ͕ؒऴྃ͢Δલͷ24࣌ؒҎ಺ʹɺΞΧ΢ϯτʹߋ৽ֹ͕ۚ՝ۚ͞ΕΔ͜ͱ • Ϣʔβʔ͸App StoreͷΞΧ΢ϯτઃఆͰαϒεΫϦϓγϣϯΛ؅ཧ͓ΑͼΩϟϯηϧͰ ͖Δ͜ͱ • Appͷར༻ن໿΁ͷϦϯΫ
  66. 133.

    ߪೖγʔϯʹදࣔ͢΂͖৘ใΛ࿙Εͳ͘ ྻڍ͢Δ • αϒεΫϦϓγϣϯͷ໊લɺظؒɺαϒεΫϦϓγϣϯͷ֤ظؒʹఏڙ͞ΕΔίϯςϯπ΍α ʔϏε • Ϣʔβʔ΁ͷ੥ٻํ๏ɺϢʔβʔ͕αϒεΫϦϓγϣϯΛ؅ཧ͢Δํ๏ • ߪೖ֬ఆ࣌ʹɺࢧ෷͍͸ϢʔβʔͷApple IDΞΧ΢ϯτʹ՝ۚ͞ΕΔ͜ͱ

    • ݱࡏͷظ͕ؒऴྃ͢Δগͳ͘ͱ΋24࣌ؒલʹϢʔβʔ͕Ωϟϯηϧ͠ͳ͍ݶΓɺαϒεΫ Ϧϓγϣϯ͸ࣗಈతʹߋ৽͞ΕΔ͜ͱ • ݱࡏͷظ͕ؒऴྃ͢Δલͷ24࣌ؒҎ಺ʹɺΞΧ΢ϯτʹߋ৽ֹ͕ۚ՝ۚ͞ΕΔ͜ͱ • Ϣʔβʔ͸App StoreͷΞΧ΢ϯτઃఆͰαϒεΫϦϓγϣϯΛ؅ཧ͓ΑͼΩϟϯηϧͰ ͖Δ͜ͱ • Appͷར༻ن໿΁ͷϦϯΫ • ݱଘͷར༻ऀ͕αΠϯΠϯͨ͠ΓߪೖΛ෮ݩͨ͠Γ͢Δํ๏
  67. 134.

    ߪೖγʔϯʹදࣔ͢΂͖৘ใΛ࿙Εͳ͘ ྻڍ͢Δ • αϒεΫϦϓγϣϯͷ໊લɺظؒɺαϒεΫϦϓγϣϯͷ֤ظؒʹఏڙ͞ΕΔίϯςϯπ΍α ʔϏε • Ϣʔβʔ΁ͷ੥ٻํ๏ɺϢʔβʔ͕αϒεΫϦϓγϣϯΛ؅ཧ͢Δํ๏ • ߪೖ֬ఆ࣌ʹɺࢧ෷͍͸ϢʔβʔͷApple IDΞΧ΢ϯτʹ՝ۚ͞ΕΔ͜ͱ

    • ݱࡏͷظ͕ؒऴྃ͢Δগͳ͘ͱ΋24࣌ؒલʹϢʔβʔ͕Ωϟϯηϧ͠ͳ͍ݶΓɺαϒεΫ Ϧϓγϣϯ͸ࣗಈతʹߋ৽͞ΕΔ͜ͱ • ݱࡏͷظ͕ؒऴྃ͢Δલͷ24࣌ؒҎ಺ʹɺΞΧ΢ϯτʹߋ৽ֹ͕ۚ՝ۚ͞ΕΔ͜ͱ • Ϣʔβʔ͸App StoreͷΞΧ΢ϯτઃఆͰαϒεΫϦϓγϣϯΛ؅ཧ͓ΑͼΩϟϯηϧͰ ͖Δ͜ͱ • Appͷར༻ن໿΁ͷϦϯΫ • ݱଘͷར༻ऀ͕αΠϯΠϯͨ͠ΓߪೖΛ෮ݩͨ͠Γ͢Δํ๏ • ϓϥΠόγʔϙϦγʔ΁ͷϦϯΫ
  68. 137.