iOSDC 2023 で登壇した資料です。 https://fortee.jp/iosdc-japan-2023/speaker/proposal/form?r=proposal&id=38b684f3-86a6-43be-8d27-48d9710626bf
StoreKit2ΛͬͨΞϓϦ՝ۚͷϑϧϦχϡʔΞϧ
View Slide
ࣗݾհ● גࣜձࣾCAM த్Ͱೖࣾ● iOS ΤϯδχΞ 5● CAMͰɺLDHɺΞΠυϧɺ͍ɺ৽R25ͳͲɺΤϯλϝࣄۀʹैࣄ● λοϓϧʹδϣΠϯͯ͠1ҏ౻ ༔ฏ
ओͳ༰● λοϓϧʹ͍ͭͯ● λοϓϧͷ՝ۚʹ͍ͭͯ● ϑϧϦχϡʔΞϧͷഎܠ● ϑϧϦχϡʔΞϧ·Ͱͷظϓϥϯ● StoreKit2● όοΫΤϯυଆͷରԠ● ϦϦʔε࣌ʹରԠͯ͠Α͔ͬͨ͜ͱ● ϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...
ฐࣾͷԣ࣠৫ʹ͍ͭͯגࣜձࣾCAM → גࣜձࣾλοϓϧ
ԣ࣠৫ʹ͍ͭͯαΠόʔΤʔδΣϯτάϧʔϓʹάϧʔϓ
λοϓϧʹ͍ͭͯ
λοϓϧʹ͍ͭͯ● ϚονϯάΞϓϦͷʮλοϓϧʯɺ20145݄ʹαʔϏεΛ։࢝ɻ● ࣗͷझຯߦͬͯΈ͍ͨσʔτεϙοτΛ͖͔͚ͬʹɺ࿀ͷ૬ख͕ݟ͚ͭΒΕΔࠃ࠷େنͷϚονϯάΞϓϦɻ
9/1ʹɺϚονϯάΞϓϦͷTVCM͕ղې͞Ε·ͨ͠TVCMͷ์ө͕݅ݫ͍͠Β͍͠...
λοϓϧͷ՝ۚʹ͍ͭͯ
λοϓϧͷ՝ۚʹ͍ͭͯ - ௨ৗʻফܕͷϙΠϯτʼ ʻαϒεΫͷγϯϓϧɺελϯμʔυϓϥϯʼ
λοϓϧͷ՝ۚʹ͍ͭͯ - ηʔϧࢪࡦ
ϑϧϦχϡʔΞϧͷഎܠ
ϑϧϦχϡʔΞϧͷഎܠ● ՝ۚͷϚΠΫϩαʔϏεԽʢෳࡶͩͬͨγεςϜΛγϯϓϧʹ͍ͨ͠ʣ● ՝ۚج൫ͷΓ͑ʢαΠόʔΤʔδΣϯτͷ՝ۚج൫Λ͍͍ͨʣ● App Store Server Notifications ͷಋೖʢVersion 2ʣΞϓϦ՝ۚʹ͓͍ͯߪೖɺࣗಈߋ৽ɺαϒεΫఀࢭͳͲͷΠϕϯτΛαʔόʔʹϦΞϧλΠϜͰ௨Ͱ͖ΔػೳɻόοΫΤϯυଆ
ϑϧϦχϡʔΞϧͷഎܠ● StoreKit2 ͷಋೖ● KMMɺBFFΛͬͨ՝ۚؔ࿈ͷڞ௨ԽʢAndroidͱ՝ۚϩδοΫΛڞ௨Խ͍ͨ͠ʣ● ͓ࢼ͠ΦϑΝʔɺϓϩϞʔγϣϯΦϑΝʔͷಋೖiOSଆ
ϑϧϦχϡʔΞϧͷظϓϥϯ
ϑϧϦχϡʔΞϧͷظϓϥϯʢ4ϲ݄ؒʣ՝ۚͷϚΠΫϩαʔϏεԽKMMɺBFFԽ※ StoreKit1 ͷঢ়ଶStep1 Step2 Step3StoreKit2ͷಋೖ ͓ࢼ͠ΦϑΝʔϓϩϞʔγϣϯΦϑΝʔͷಋೖେ͖ͳࢪࡦɺ͔͠՝ۚͷͨΊɺ3ͭʹ͚ͨ
StoreKit2γϯϓϧͳίʔυ
StoreKit2 - γϯϓϧͳίʔυϓϩμΫτใͷऔಘ - Product.products()● Կ४උඞཁͳ͍ɻͨͬͨɺ1ߦͷίʔυɻ● ͜ͷ Productʢ= SKProductʣʹ͋ΒΏΔใ͕٧·͍ͬͯΔɻ
StoreKit2 - γϯϓϧͳίʔυϓϩμΫτͷߪೖ - Product.purchase()● Կ४උඞཁͳ͍ɻͨͬͨɺ1ߦͷίʔυͰߪೖ։࢝ɻ● ߪೖͷݕূΞϓϦଆͰɻ● τϥϯβΫγϣϯಉؔ͡ͰྃͰ͖Δɻ● try catch ͰΤϥʔଈ࠲ʹऔಘͰ͖Δɻ● Delegate ͳ͠ɻ● ίʔϧόοΫؔͳ͘ͳΔɻ
StoreKit2 - γϯϓϧͳίʔυ2ϲॴͷτϥϯβΫγϣϯ● ΞϓϦىಈ࣌ͷτϥϯβΫγϣϯ● ߪೖͷ෮ݩ࣌ͷτϥϯβΫγϣϯͪ͜Β؆୯ʹ࣮Ͱ͖Δɻ
StoreKit2 - γϯϓϧͳίʔυΞϓϦىಈ࣌ͷτϥϯβΫγϣϯᶃ - Transaction.updates● ࣗಈߋ৽ͷαϒεΫͳͲͰൃੜͨ͠ɺະॲཧͷτϥϯβΫγϣϯΛऔಘͰ͖Δɻ● λοϓϧͰ Root ͷViewController Ͱ listen ͍ͯ͠Δɻʢৗʹࢹʣ
StoreKit2 - γϯϓϧͳίʔυߪೖͷ෮ݩ࣌ͷτϥϯβΫγϣϯᶄ - Transaction.currentEntitilements● ෮ݩରͷτϥϯβΫγϣϯΛऔಘͰ͖Δɻ● λοϓϧͰઃఆը໘ʹ͋Δʮߪೖ༰ͷ෮ݩʯϝχϡʔʹ࣮͍ͯ͠Δɻ
StoreKit2 - γϯϓϧͳίʔυߪೖͷ෮ݩ࣌ͷτϥϯβΫγϣϯᶄ - Transaction.unfinished● ߪೖ࣌ʹApp Storeͷܾࡁ͕ྃͨ͠ɺαʔόʔଆͷܾࡁޙॲཧʹࣦഊͨ͠έʔεͳͲͰɺະॲཧͷτϥϯβΫγϣϯΛऔಘͰ͖Δɻ● ͪ͜Βઃఆը໘ʹ͋Δʮߪೖ༰ͷ෮ݩʯϝχϡʔʹ࣮͍ͯ͠Δɻ
StoreKit2 - γϯϓϧͳίʔυτϥϯβΫγϣϯॲཧͷҙʂᶃλοϓϧͰɺ෮ݩ࣌ʹҎԼͷ2छྨͷτϥϯβΫγϣϯΛ֬ೝͯ͠Δɻᶃ Transaction.currentEntitlementsᶄ Transaction.unfinishedԿΒ͔ͷཧ༝ͰߪೖͷτϥϯβΫγϣϯΛྃͰ͖ͳ͔ͬͨͱ͖ɺ֘ͷτϥϯβΫγϣϯ Transaction.currentEntitlements Ͱͳ͘ɺTransaction.unfinished ʹଘࡏ͍ͯͨ͠ɻ։ൃॳظஈ֊Ͱ Transaction.currentEntitlements ʹར༻Մೳ͕ͯ͢ଘࡏ͢Δͱࢥ͕ͬͨɺςετΛ௨ͯ͡ɺະྃͷτϥϯβΫγϣϯ Transaction.currentEntitlements ʹؚ·Ε͍ͯͳ͍͜ͱ͕Θ͔ͬͨɻ
StoreKit2 - γϯϓϧͳίʔυτϥϯβΫγϣϯॲཧͷҙʂᶄͦͷͨΊɺαʔόʔଆͰAPIΛϩοΫͯ͠ɺαʔόʔଆͷ࿈ଓܾͨ͠ࡁޙॲཧ͕ൃੜ͠ͳ͍Α͏ʹରࡦͨ͠ɻΛߪೖͨ͠ͱ͖ɺαʔόʔଆͰܾࡁޙॲཧΛ͢Δ͕ɺॏෳͯ͠৽نߪೖͷॲཧͯ͠͠·͍ͬͯͨɻAppleͷެࣜαϯϓϧίʔυʹɺߪೖʹΑΔτϥϯβΫγϣϯ listen͠ͳ͍ͱॻ͍͕ͯ͋ͬͨ...// Iterate through any transactions that don't come from a direct call to `purchase()`.https://developer.apple.com/documentation/storekit/in-app_purchase/implementing_a_store_in_your_app_using_the_storekit_apiA. ߪೖޙB. ΞϓϦىಈ࣌ʹ listen ͨ͠ Transactions.updates
StoreKit2 - γϯϓϧͳίʔυϨγʔτϨγʔτ͕ෆཁʹͳͬͨʂͦͷΘΓʹԿ͕ඞཁʁ
StoreKit2 - γϯϓϧͳίʔυϨγʔτ͕ෆཁʹͳͬͨʂ- ফܕ● ϨγʔτͷΘΓʹɺjwsRepresentation ΛαʔόʔʹૹΔɻ● base64ʹΤϯίʔυ͞Ε͍ͯΔͷͰɺσίʔυ͢ΕtransactionId ͳͲΛऔಘͰ͖Δɻ● ߪೖޭޙͷฦΓͰऔಘͰ͖Δɻ
StoreKit2 - γϯϓϧͳίʔυϨγʔτ͕ෆཁʹͳͬͨʂ- αϒεΫϦϓγϣϯ● ϨγʔτͷΘΓʹɺoriginalTransactionId ΛαʔόʔʹૹΔɻ● λοϓϧͷαʔόʔଆͰɺoriginalTransactionId ΛجʹApp Store Server API ΛͬͯɺτϥϯβΫγϣϯͷৄࡉใΛऔಘ͢Δɻ● ͪ͜ΒɺߪೖޭޙͷฦΓͰऔಘͰ͖Δɻ
StoreKit2 - γϯϓϧͳίʔυStoreKit2Ͱγϯϓϧͳίʔυʹͳ͓͔ͬͨ͛ͰίʔυΛ͍͘͢ͳΓσόοάָ͕ʹͳͬͨ՝ۚཧղͷϋʔυϧ͕Լ͕ͬͨ
StoreKit2ΦϑΝʔ
StoreKit2 - ΦϑΝʔ͓ࢼ͠ແྉηʔϧࢪࡦͷͨΊʹɺΦϑΝʔΛՃͨ͠ɻ● ͓ࢼ͠ΦϑΝʔ● ϓϩϞʔγϣϯΦϑΝʔ
StoreKit2 - ΦϑΝʔ● αϒεΫάϧʔϓͰҰ͚ͩఏڙՄೳɻ● ίʔυଆͰΦϑΝʔͷద༻༗ແΛࢦఆͰ͖ͳ͍ɻʢAppleଆͰஅʣ● αϒεΫάϧʔϓͰɺར༻தͷαϒεΫ͕͋Δͱద༻Ͱ͖ͳ͍ɻ※ΞοϓάϨʔυɺμϯάϨʔυͰͷఏڙͰ͖ͳ͍ɻ● αϒεΫͷߪೖܦݧ͕͋ͬͯɺ͓ࢼ͠ܦݧ͕ͳ͚Εɺ͓ࢼ͠ΦϑΝʔΛఏڙͰ͖Δɻ͓ࢼ͠ΦϑΝʔ
StoreKit2 - ΦϑΝʔ͓ࢼ͠ΦϑΝʔ● ͓ࢼ͠ΦϑΝʔͷใProduct.subscription?.introductoryOffer● ͓ࢼ͠ΦϑΝʔΛఏڙՄೳ͔ʁProduct.SubscriptionInfo.isEligibleForIntroOffer
StoreKit2 - ΦϑΝʔ͓ࢼ͠ΦϑΝʔ - ҙʂisEligibleForIntroOffer == true Ͱɺಉ͡αϒεΫάϧʔϓͰɺར༻தͷαϒεΫ͕͋Δͱ͓ࢼ͠ΦϑΝʔΛద༻Ͱ͖ͳ͍ɻͦͷͨΊλοϓϧͰɺProduct.SubscriptionInfo.status Λऔಘͯ͠ɺݱࡏར༻தͷαϒεΫ͕ଘࡏ͢Δ͔νΣοΫ͍ͯ͠Δɻtrue ͚ͩͲɺ͓ࢼ͠ΛఏڙͰ͖ͳ͍ͷͰҙʂ
StoreKit2 - ΦϑΝʔϨγʔτͷதΛൈਮʢΕͳ͍Α͏ʹϝϞʣ● ແྉ͓ࢼ͠ΦϑΝʔ"is_trial_period": "true","is_in_intro_offer_period": "false",● ༗ྉ͓ࢼ͠ΦϑΝʔ"is_trial_period": "false","is_in_intro_offer_period": "true",͓ࢼ͠ΦϑΝʔ - Ϩγʔτͷத
StoreKit2 - ΦϑΝʔ● ԿͰఏڙՄೳɻ● ίʔυଆͰߪೖ࣌ʹOfferIdΛࢦఆ͢ΔɻOfferIdΛࢦఆ͠ͳ͍ͱ௨ৗՁ֨ѻ͍ɻ● ΞϓϦͰҰͰαϒεΫߪೖܦݧ͕ͳ͍ͱఏڙͰ͖ͳ͍ɻ※ߪೖܦݧ͕ͳ͍ͷʹϓϩϞʔγϣϯΦϑΝʔͰߪೖ͠Α͏ͱ͢ΔͱΤϥʔʹͳΔɻϓϩϞʔγϣϯΦϑΝʔ
StoreKit2 - ΦϑΝʔ● ϓϩϞʔγϣϯΦϑΝʔใͷऔಘʢ࠷େ10݅ͷొ͕ՄೳʣProduct.subscription?.promotionalOffers.first(where: { $0.id == offerId })● ϓϩϞʔγϣϯΦϑΝʔΛఏڙՄೳ͔ʁProduct.SubscriptionInfo.status(for: subscriptionGroupId)?.isEmptyλοϓϧͰɺಉ͡αϒεΫάϧʔϓͷߪೖܦݧͰஅͨ͠ɻ※ϕετϓϥΫςΟε͕͋Εڭ͍͖͍͑ͯͨͩͨͰ͢ɻϓϩϞʔγϣϯΦϑΝʔ
StoreKit2 - ΦϑΝʔϨγʔτͷதΛൈਮʢΕ͕ͪͳͷͰϝϞʣ"promotional_offer_id": "**************************"ϓϩϞʔγϣϯΦϑΝʔ - Ϩγʔτͷத
ʮappBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + appAccountToken +'\u2063' + nonce + '\u2063' + timestampʯʮappBundleId + '\u2063' + keyIdentifier + '\u2063' + productIdentifier + '\u2063' + offerIdentifier + '\u2063' + '' + '\u2063' + nonce +'\u2063' + timestampʯStoreKit2 - ΦϑΝʔϓϩϞʔγϣϯΦϑΝʔΛߪೖ͢Δͱ͖ɺαʔόʔଆͰ signature Λੜͯ͠ɺiOSଆͰ Product.purchase ࣌ʹ signature Λ༩͢Δඞཁ͕͋Δɻ͜ͷͱ͖ɺΦϓγϣφϧͷ appAccountToken ͕ෆཁͳ߹ɺۭจࣈͱͯ͠ઃఆ͢Δɻ※ۭจࣈແ͍ঢ়ଶͰ signature Λ࡞ͨ͠ΒߪೖͰ͖ͣΤϥʔʹͳΔɻϓϩϞʔγϣϯΦϑΝʔͷॺ໊࡞Ͱ·ͬͨ͜ͱʢsignatureʣ[Link]https://developer.apple.com/documentation/storekit/in-app_purchase/original_api_for_in-app_purchase/subscriptions_and_offers/generating_a_signature_for_promotional_offers
όοΫΤϯυଆͷରԠ
όοΫΤϯυଆͷରԠApp Store Server API● αϒεΫใͷऔಘαϒεΫͷܾࡁޙॲཧͷͱ͖ɺoriginalTransactionId ͔ΒৄࡉใΛऔಘ͢ΔͨΊʹɺԼه API Λར༻ͨ͠ɻhttps://api.storekit.itunes.apple.com/inApps/v1/subscriptions/{transactionId}https://developer.apple.com/documentation/appstoreserverapi/get_all_subscription_statuses● τϥϯβΫγϣϯཤྺαϒεΫͷΞοϓάϨʔυ/μϯάϨʔυͷࡍʹɺաڈͷߪೖใ͕ඞཁͩͬͨͨΊɺԼه API Λར༻ͨ͠ɻhttps://api.storekit.itunes.apple.com/inApps/v1/history/{transactionId}https://developer.apple.com/documentation/appstoreserverapi/get_transaction_history
ϦϦʔεޙʹରԠͯ͠Α͔ͬͨ͜ͱ
ϦϦʔε࣌ʹରԠͯ͠Α͔ͬͨ͜ͱஈ֊తϦϦʔε● App Store Connect ͔ΒΞϓϦΛϦϦʔε͢Δͱ͖ɺʮஈ֊తϦϦʔεʯΛར༻ͨ͠ɻ● λοϓϧͰ௨ৗ࣌ஈ֊తϦϦʔεΛ࣮ࢪ͍ͯ͠Δ͕ɺ՝ۚؔ࿈ͷϦϦʔε࣌ɺΧελϚʔαϙʔτͷ͍߹Θͤঢ়گͳͲݟͳ͕Βɺ͍ͭΑΓ༨༟Λ࣋ͬͯ100%։์ʹͨ͠ɻ
ϦϦʔε࣌ʹରԠͯ͠Α͔ͬͨ͜ͱ৽چίʔυͷΓସ͑● Firebase ͷ Remote Config ΛͬͯɺԿ͔େ͖ͳ͕ൃੜͨ͠ͱ͖ͷͨΊʹɺ৽چͷίʔυΛΓସ͑ΒΕΔΑ͏ʹͨ͠ɻ● ։์Λ0%ʹ͢ΔͱɺچίʔυͰ՝ۚॲཧ͕࣮ߦ͞ΕΔɻ
ϦϦʔε࣌ʹରԠͯ͠Α͔ͬͨ͜ͱΤϥʔϩάͷग़ྗ - Datadog● Datadog Λར༻ͯ͠ɺ՝ۚؔ࿈ͷΤϥʔΛग़ྗͨ͠ɻϢʔβʔ͔Βͷ͍߹Θͤ࣌ͷௐࠪʹʹཱͬͨɻ● εϜʔζͳௐࠪͷͨΊʹɺͪΐͬͱΓ͗͢ʁ͘Β͍ৄࡉͳΤϥʔ༰ΛૹΔ͜ͱΛ͓͢͢Ίɻ● Τϥʔ͕ٸܹʹ૿͑ͨࡍɺSlack௨͕ಧ͘Α͏ʹ Monitors Λར༻ͨ͠ɻ
ϦϦʔε࣌ʹରԠͯ͠Α͔ͬͨ͜ͱΤϥʔϝοηʔδʹΤϥʔ ID● ֤छ՝ۚؔ࿈ͷΤϥʔΛ·ͱΊͯཧͯ͠ɺϢʔβʔʹදࣔ͢ΔΤϥʔϝοηʔδʹIDΛ༩ͨ͠ɻ● Ϣʔβʔ͔Βͷ͍߹Θͤ࣌ʹɺΤϥʔϝοηʔδͷεΫγϣ͓ئ͍͍ͯ͠ΔͨΊɺID͕ෆ۩߹ௐࠪʹཱͬͨɻ
ϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...
ϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...StoreKit1 ͱ 2 Λࠞࡏͤͯ͞͠·ͬͨ...
ϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...αʔόʔଆͷ߹ʹΑΓɺஈ֊Λ౿ΜͰϦχϡʔΞϧΛܭըͨ͠ɻStep1: ·ͣɺiOSଆͷΈΛ StoreKit2 ʹҠߦStep2: ࣍ʹɺαʔόʔଆΛ StoreKit2 ʹରԠͨ͠APIʹҠߦ
ͦͷͨΊɺ࠷ॳͷஈ֊Ͱچདྷܕͷ receipt σʔλ(StoreKit1) ΛܾͬͨࡁޙॲཧΛͨ͠ɻͭ·ΓɺiOSଆͷߪೖStoreKit2ɺαʔόʔଆͷܾࡁޙॲཧStoreKit1ϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...
͜ͷͱ͖ɺαʔόʔଆͰ receipt ΛͬͯϨγʔτͷதΛ֬ೝͨ͠Βɺ࠷৽ͷߪೖใؚ͕·Ε͓ͯΒͣɺਖ਼ৗʹߪೖΛ༩Ͱ͖ͳ͔ͬͨɻϨγʔτͷөʹλΠϜϥά͕͋ͬͨɻϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...
Apple ͷςΫχΧϧαϙʔτʹ͍߹ΘͤͨΒɺStoreKit1ͱ2 Λࠞࡏͤͯͩ͞Ίɻαʔόʔଆ StoreKit2 Λͬͨਪͷํ๏ͰɺܾࡁޙॲཧΛ͢ΔΑ͏ʹͱࢦఠΛड͚ͨɻσόοά࣌ΤϥʔʹͳΒͳ͔͔ͬͨΒɺ͍͚Δͱࢥͬͨ...ϦϦʔεޙʹ໌Β͔ʹͳࣦͬͨഊ...
ͥͻɺStoreKit2ʹ͠·͠ΐ͏ʂ
Ҏ্ʹͳΓ·͢͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠