日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

IAP详解

發布時間:2023/12/16 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 IAP详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

In-App Purchase Walk Through

1. 適用情況

想使用In-App Purchase(以下簡稱IAP)完成App內付費前,先確定需求是不是能用這個方案來滿足。
除了IAP以外,還有支付寶SDK、信用卡等其他方式完成軟件內付費。

在蘋果制定的游戲規則中,所有在App內提供的服務需要付費時,都應當使用IAP,比如軟件功能、游戲道具;所有在App外提供的服務需要付費時,都應使用其他支付方式,比如Uber的信用卡,淘寶、快的打車的支付寶SDK等。

在IAP里,可以出售:

  • 數字內容:比如雜志、圖片、游戲關卡解鎖、相機付費濾鏡等;
  • 軟件功能:如各種擴展features;
  • 一次性服務:比如一次語音通話等。注意是「一次性」服務,不是一次「性服務」。
  • 在IAP里,不能出售:

  • 現實世界的商品或服務:比如剛才提到的一次「性服務」。嚴格遵守此方案有個好處:IAP 如果被破解,用戶無法得到大量實物,開發商也不會有很大經濟損失。非要做的話想繞過也是可以的:用 IAP 購買代幣,審核通過后用代幣購買實物。
  • 其他the App Review GuideLines中規定的不允許的內容:比如剛才提到的一次「性服務」。
  • 順便說下,有次大網易的同事分享時提到:使用兌換碼兌換App內服務是一條高壓線。像Uber和Amazon里允許有碼,是因為他們的碼是用在現實世界的產品或服務上的。

    如果你確定內購需求符合IAP的使用要求,可以繼續往下讀了。

    2. 購買及發放虛擬產品流程

    官方給出的流程圖是這樣的:

  • 獲取內購列表(從App內讀取或從自己服務器讀取)
  • App Store請求可用的內購列表
  • 向用戶展示內購列表
  • 用戶選擇了內購列表,再發個購買請求
  • 收到購買完成的回掉
  • 發放虛擬產品
  • 3.虛擬產品

    虛擬產品的分類

    虛擬產品分為以下幾種類型:

  • 消耗品(Consumable products):比如游戲內金幣等。
  • 不可消耗品(Non-consumable products):簡單來說就是一次購買,終身可用(用戶可隨時從App Store restore)。
  • 自動更新訂閱品(Auto-renewable subscriptions):和不可消耗品的不同點是有失效時間。比如一整年的付費周刊。在這種模式下,開發者定期投遞內容,用戶在訂閱期內隨時可以訪問這些內容。訂閱快要過期時,系統將自動更新訂閱(如果用戶同意)。
  • 非自動更新訂閱品(Non-renewable subscriptions):一般使用場景是從用戶從IAP購買后,購買信息存放在自己的開發者服務器上。失效日期/可用是由開發者服務器自行控制的,而非由App Store控制,這一點與自動更新訂閱品有差異。
  • 免費訂閱品(Free subscriptions):在Newsstand中放置免費訂閱的一種方式。免費訂閱永不過期。只能用于Newsstand-enabled apps。
  • 類型2、3、5都是以Apple ID為粒度的。比如小張有三個iPad,有一個Apple ID購買了不可消耗品,則三個iPad上都可以使用。
    類型1、4一般來說則是現買現用。如果開發者自己想做更多控制,一般選4。

    幾種產品的區別如下(表格懶得翻譯了):

    Table 1-1 Comparison of product types

    Product typeNon-consumable?Consumable
    Users can buy?Once?Multiple times
    Appears in the receiptAlwaysOnce
    Synced across devicesBy the systemNot synced
    RestoredBy the systemNot restored

    Table 1-2 Comparison of subscription types

    Subscription typeAuto-renewableNon-renewingFree
    Users can buyMultiple timesMultiple timesOnce
    Appears in the receiptAlwaysOnceAlways
    Synced across devicesBy the systemBy your appBy the system
    RestoredBy the systemBy your appBy the system

    關于自動更新訂閱品更新周期組(Auto-Renewable Subscription Duration Families):

    每種訂閱品的每種更新周期可以在iTunes Connect中設置一個單獨的價格。圖中給出了一種訂閱品的不同長度的更新周期的價格截圖:

    你可以把每種訂閱品的每個長度的更新周期看成一個單獨的產品,每個產品有自己獨有的時長、價格、市場促銷屬性。
    為了讓用戶可以從中挑選一個偏好的更新周期,上圖中我們為此種訂閱的每個長度的更新周期分別設值了一個單獨的價格,有一周的、一個月的、兩個月的、季度的、半年的和一年的。
    上圖中這種訂閱品的全部六種更新周期合起來稱為一個自動更新訂閱品更新周期組(Auto-Renewable Subscription Duration Families)。

    4. 人肉和iTunes Connect交互

    填寫銀行卡與納稅信息

  • 登錄iTunes Connect
  • 點擊Agreements, Tax, and Banking,填寫Contact Info, Bank Info, Tax Info。如果填過就不用再填了。剛剛填寫完成后,各種信息正在審核,如下圖:

    即使信息正在審核,沙箱環境下也是可以訪問IAP服務的,并不需要等審核完成才能測試。
  • 新建虛擬產品

  • 登錄iTunes Connect
  • 點擊My Apps
  • 進入想使用IAP的App詳情
  • 選擇In-App Purchases
  • 點擊Create New
  • 選擇IAP虛擬產品類型。注意虛擬產品一旦新建,類型無法修改。
  • 填入Internal Name。只能在iTunes Connect中看到這個名字。不會出現在App Store中。最長255字節。
  • 填入Product ID。每件產品有一個單獨的Product IDProduct ID用于從App Store獲取價格信息,以及付費時標識是哪種產品被購買了。例如com.163.neteasemusic.skin.dog。這個新建之后也是不能修改的。
  • 然后是設置價格。Cleared For Sale選為YES是虛擬產品被審核通過自動上架。NO是手動上架。Price Tier則是價格。
  • 多語言描述,這個是給用戶看的。
  • 是否要在iTunes托管可購買內容,這個后面再說
  • 填入review notes和review用的截圖。
  • 保存。
  • 保存后除了類型和Product ID都可以修改
  • 新建完,不用等待蘋果審核就可以在沙箱環境使用了。

    新建測試帳號

  • 登錄iTunes Connect
  • 點擊Users and Roles
  • 點擊Sandbox Testers
  • 添加Tester。添加后在測試機上用tester帳號登錄app store
  • 附:在蘋果托管不可消耗品(Non-consumable products)的內容需知

    托管內容僅限于針對不可消耗品。
    首次創建不可消耗品時可以選擇把內容托管到蘋果服務器,當然也可以隨時將自己服務器上的內容遷移到蘋果服務器由蘋果托管。
    需要使用托管功能的話,首先在iTunes Connect中提交不可消耗品讓蘋果審核。然后在Xcode中選取In-App Purchase Content template創建虛擬產品, 放入需要托管的內容, 然后使用Archive功能上傳。或者使用Xcode為每一種虛擬產品創建一個.pkg文件,然后使用Application Loader一次性上傳。
    具體細節請參考Using Application Loader中和In-App Purchase有關的章節。

    關于和iTunes Connect的交互,更多細節請參考In-App Purchase Configuration Guide for iTunes Connect。

    5. 代碼里該做的事情

    獲取產品列表

  • 首先讀取出App中內嵌的或是服務端中的Product IDs。
  • 使用SKProductRequest向蘋果服務器驗證哪些Product IDs是可用的。注意不要在[Class load]里發送請求,一定要等到App didFinishLauching之后再發,不然無法接到請求返回。核心代碼如下:
  • 1 2 3 4 5 6 7 #import <StoreKit/StoreKit.h> #define kInAppPurchaseProUpgradeProductId @"com.163.neteasemusic.skin.dog" ... NSSet *productIDs = [NSSet setWithObject:kInAppPurchaseProUpgradeProductId]; SKProductsRequest *request= [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs]; request.delegate = self; [request start];

    接收結果

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response { NSArray *myProducts = response.products; for (SKProduct *product in myProducts) { //product } } - (void)request:(SKRequest *)request didFailWithError:(NSError *)error { //處理錯誤 }

    向自己的服務器生成訂單

    如果需要經過自己的服務器做二次驗證,建議在調用蘋果支付接口前做這一步。
    訂單中必須要保存的是訂單ID和用戶想要購買的商品ID。這個記錄是為了在二次驗證時服務端做檢查,防止 A 商品的 receipt 被用戶拿來做 B 商品的購買結果校驗。

    發送購買請求

    1 2 3 4 5 6 #import <StoreKit/StoreKit.h> ... SKProduct *product = <# products request中返回的SKProduct #>; SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product]; payment.quantity = 2; [[SKPaymentQueue defaultQueue] addPayment:payment];

    或者

    1 2 3 4 5 #import <StoreKit/StoreKit.h> ... SKProduct *product = <# products request中返回的SKProduct #>; SKPayment *payment = [SKPayment paymentWithProduct:product]; [[SKPaymentQueue defaultQueue] addPayment:payment];

    觀察購買狀態

    首先在程序啟動時注冊觀察者

    1 2 3 #import <StoreKit/StoreKit.h> ... [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];

    并且實現回調,處理相應的購買返回。

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { for (SKPaymentTransaction *transaction in transactions) { switch (transaction.transactionState) { // Call the appropriate custom method for the transaction state. case SKPaymentTransactionStatePurchasing: [self showTransactionAsInProgress:transaction deferred:NO]; break; case SKPaymentTransactionStateDeferred: [self showTransactionAsInProgress:transaction deferred:YES]; break; case SKPaymentTransactionStateFailed: [self failedTransaction:transaction]; break; case SKPaymentTransactionStatePurchased: [self completeTransaction:transaction]; break; case SKPaymentTransactionStateRestored: [self restoreTransaction:transaction]; break; default: // For debugging NSLog(@"Unexpected transaction state %@", @(transaction.transactionState)); break; } } }

    需要監聽SKPaymentQueue的更多狀態變更,請實現SKPaymentTransactionObserver協議中提供的更多方法。

    完成購買

    在收到Purchased或Restored回調后,持久化購買記錄以及receipt data。iOS6.X 或之前的版本中,持久化 receipt data 必須萬無一失,因為一旦丟失,將沒有任何途徑再次拿到此 receipt,造成用戶購買記錄丟失。獲取 receipt data 需要注意的點將在后面的二次驗證中詳細說。
    然后通知PaymentQueue,購買已經完成了。對finishTransaction則會觸發系統IAP的UI刷新:

    1 2 SKPaymentTransaction *transaction = <# The current payment #>; [[SKPaymentQueue defaultQueue] finishTransaction:transaction];

    另外在發放功能或道具之前,最好在自己服務端做一次二次校驗,防止越獄插件或者Wifi的HTTP代理偽造購買記錄。

    二次驗證防止破解

    越獄插件或者HTTP代理均可讓用戶做到偽造購買記錄。當我們收到購買完成的回調后,最好經過自己服務器驗證購買是否合法。

    經過 App Store 驗證

    以下代碼用Cocoa實現了二次驗證的過程。但是這個過程最好通過自己的后臺服務器來做,不然非常容易在客戶端被偽造返回結果。
    這里使用Cocoa實現只是為了闡述請求與返回值的格式。 發送二次驗證請求:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #define SANDBOX_VERIFY_RECEIPT_URL [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"] #define APP_STORE_VERIFY_RECEIPT_URL [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"] #ifdef DEBUG #define VERIFY_RECEIPT_URL SANDBOX_VERIFY_RECEIPT_URL #else #define VERIFY_RECEIPT_URL APP_STORE_VERIFY_RECEIPT_URL #endif ... - (void)verifyTransaction:(SKPaymentTransaction *)transaction { NSData *transactionReceipt = [[self class]] receiptDataFromTransaction:transaction]; NSString *base64String = [OTBase64Helper base64forData:transactionReceipt]; NSDictionary *receiptDictionary = @{@"receipt-data":base64String}; NSData *data = [receiptDictionary JSONData]; if (_receiptRequest) { [_receiptRequest cancel]; _receiptRequest = nil; } _receiptRequest = [[ASIFormDataRequest alloc] initWithURL:VERIFY_RECEIPT_URL]; _receiptRequest.userInfo = @{@"ProductIdentifier" : transaction.payment.productIdentifier}; _receiptRequest.delegate = self; [_receiptRequest appendPostData:data]; [_receiptRequest startAsynchronous]; } + (NSData *)receiptDataFromTransaction:(SKPaymentTransaction *)transaction { NSData *receiptData = [self receiptDataInReceiptURL]; if (!receiptData) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated" if ([transaction respondsToSelector:@selector(transactionReceipt)]) { //Works in iOS3 - iOS8, deprected since iOS7, actual deprecated (returns nil) since iOS9 receiptData = transaction.transactionReceipt; } #pragma clang diagnostic pop } return receiptData; } + (NSData *)receiptDataInReceiptURL { if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0") && [[NSBundle mainBundle] respondsToSelector:@selector(appStoreReceiptURL)]) { //Works since iOS7, implemented but calls selector not found directly in iOS6 //so must decide by if system version >= 7.0, DO NOT use respondsToSelector:@selector(appStoreReceiptURL) NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL]; if ([[NSFileManager defaultManager] fileExistsAtPath:[receiptUrl path]]) { NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl]; return receiptData; } } return nil; }

    需要注意的是,如果 App 不需要支持 iOS6.x 及之前的版本,建議僅使用 appStoreReceiptURL 獲取 receipt 數據。appStoreReceiptURL 從 iOS7 開始啟用,會返回用戶在此 app 上購買過的全部 receipt 的 data(粒度猜測應該是本機,本 App Store 帳號,本 App 內的購買,具體沒測試)。

    接收二次驗證結果:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 - (void)requestFinished:(ASIHTTPRequest *)request { NSString *responseString = [request responseString]; NSDictionary *dictionary = [responseString objectFromJSONString]; NSString *productId = dictionary[@"receipt"][@"product_id"]; NSNumber *status = dictionary[@"status"]; if (status.intValue == 0) { //校驗成功,發放內容 //status code 0為成功 } else { //校驗失敗,不做處理或相應懲罰 //21000 App Store不能讀取你提供的JSON對象 //21002 receipt-data域的數據有問題 //21003 receipt無法通過驗證 //21004 提供的shared secret不匹配你賬號中的shared secret //21005 receipt服務器當前不可用 //21006 receipt合法,但是訂閱已過期。服務器接收到這個狀態碼時,receipt數據仍然會解碼并一起發送 //21007 receipt是Sandbox receipt,但卻發送至生產系統的驗證服務 //21008 receipt是生產receipt,但卻發送至Sandbox環境的驗證服務 } } - (void)requestFailed:(ASIHTTPRequest *)request { //出錯處理 }

    蘋果的返回值如下:

    使用transaction.transactionReceipt取得的小票經二次驗證后返回值:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 { "receipt": { "original_purchase_date_pst":"2015-06-03 04:00:37 America/Los_Angeles", "purchase_date_ms":"1433329237329", "unique_identifier":"secret9f135e2cd8f7dda951a15c01cd2220c60b", "original_transaction_id":"1000000157783770", "bvrs":"2.6.0", "transaction_id":"1000000157783770", "quantity":"1", "unique_vendor_identifier":"SECRETCD-89AD-45C4-8937-359CCA9E8F36", "item_id":"SECRET509", "product_id":"com.your.iap.product.id", "purchase_date":"2015-06-03 11:00:37 Etc/GMT", "original_purchase_date":"2015-06-03 11:00:37 Etc/GMT", "purchase_date_pst":"2015-06-03 04:00:37 America/Los_Angeles", "bid":"com.your.app.bundle.id", "original_purchase_date_ms":"1433329237329" }, "status": 0 }

    使用[[NSBundle mainBundle] appStoreReceiptURL]取得的小票經二次驗證后返回值:

    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 { "status":0, "environment":"Sandbox", "receipt": { //in_app 中全部支付共有的信息(本App的IAP信息) "receipt_type":"ProductionSandbox", "adam_id":0, "app_item_id":0, "bundle_id":"com.netease.neteasemusic", "application_version":"2.9.0", "download_id":0, "version_external_identifier":0, "request_date":"2015-07-29 03:37:17 Etc/GMT", "request_date_ms":"1438141037628", "request_date_pst":"2015-07-28 20:37:17 America/Los_Angeles", "original_purchase_date":"2013-08-01 07:00:00 Etc/GMT", "original_purchase_date_ms":"1375340400000", "original_purchase_date_pst":"2013-08-01 00:00:00 America/Los_Angeles", "original_application_version":"1.0", "in_app": [ //每筆交易分別的信息 { "quantity":"1", "product_id":"com.netease.neteasemusictest.pack.clouddrive.2t12", "transaction_id":"1000000164784521", "original_transaction_id":"1000000164784521", "purchase_date":"2015-07-23 15:03:04 Etc/GMT", "purchase_date_ms":"1437663784000", "purchase_date_pst":"2015-07-23 08:03:04 America/Los_Angeles", "original_purchase_date":"2015-07-23 15:03:04 Etc/GMT", "original_purchase_date_ms":"1437663784000", "original_purchase_date_pst":"2015-07-23 08:03:04 America/Los_Angeles", "is_trial_period":"false" }, { "quantity":"1", "product_id":"com.netease.neteasemusictest.pack.clouddrive.1t12", "transaction_id":"1000000165136224", "original_transaction_id":"1000000165136224", "purchase_date":"2015-07-27 06:40:40 Etc/GMT", "purchase_date_ms":"1437979240000", "purchase_date_pst":"2015-07-26 23:40:40 America/Los_Angeles", "original_purchase_date":"2015-07-27 06:40:40 Etc/GMT", "original_purchase_date_ms":"1437979240000", "original_purchase_date_pst":"2015-07-26 23:40:40 America/Los_Angeles", "is_trial_period":"false" } ] } }

    純本地驗證

    除了網絡驗證以外,蘋果提供了純粹的本地驗證方式:Validating Receipts Locally.
    Receipt data 經過 App Store 證書簽名,所以第三方無法憑空生成能夠通過此法驗證的 receipt data。只要做好證書校驗,無需擔心用戶會偽造 receipt data。
    在客戶端使用這種方式可以做到防止被通用破解方式破解,但并不能防止針對特定 App 的破解。
    實際上,這種驗證方式是蘋果為服務端設計的。Receipt data 的格式遵守ASN.1格式,服務端安裝asn1c就可以解析 receipt data,并不需要純手寫一份解析代碼。只要服務端代碼和 asn1c 不出 bug,在服務端使用這種方式驗證就是安全的。

    第三方網站驗證

    有些第三方網站提供了經服務端的驗證服務。比如urbanairship. 但是我并沒有用過,所以不知道具體效果如何。畢竟第三方服務無法做到在用戶發起購買之前生成訂單記錄,與購買后驗證結果比對,所以我還是比較擔心第三方驗證服務的安全性的。而且雞國網絡連國外驗證服務器,你懂的。。

    總之想要萬無一失,建議開發自己的驗證接口。

    更多驗證相關問題,請參考Receipt Validation Programming Guide

    大多數產品在驗證成功后,才是真正的發放內容、道具等。特別是充值后立即消費的虛擬貨幣基本都是這么處理的。
    但是從接口來看, IAP 的設計者是想讓開發者在購買完成時發放內容、道具,在二次驗證失敗時以刪除內容、道具等方式來進行處罰。

    6.服務端二次驗證后再發放數據中的安全問題

    由于是和錢關系最緊密的功能,IAP安全性顯得無比重要。

    客戶端數據安全

    客戶端根據使用不同的獲取 receipt 的接口,需要做的事情也不同:

    1. 客戶端使用transaction.transactionReceipt(或使用transaction.transactionReceipt + appStoreReceiptURL兩者):

    如果要支持 iOS6,那么不得不使用transaction.transactionReceipt在 iOS6上讀取receipt,客戶端需要保證持久化邏輯:
    在transaction完成后,和服務端的二次驗證完成前,要對receipt data做持久化; 若存在未上傳成功的 receipt ,需要開定時器重試上傳; 如果要做刪除持久化數據,刪除的時機應當是收到從服務端發回的二次驗證請求的響應時,確認服務端已和蘋果完成通信之后(服務端返回和蘋果連接失敗則不應刪除已保存的receiptData),若 IAP 交易不頻繁,可考慮不刪除持久化的 receipt data。

    2. 客戶端僅使用appStoreReceiptURL獲取receipt

    客戶端每次交易完成從 appStoreReceiptURL 讀取出 data 上傳給服務器。同時有上傳請求正在進行的話,持久化一個標記,表示有未完成的上傳,在全部上傳完成后將此標記置為上傳結束。如果標記存在,需要開定時器重試上傳。

    服務端數據安全

    服務端安全包含兩部分:防止已扣款卻購買無效;防止作弊。目前想到最圓的實踐是:
    1. 在從客戶端上傳的 receipt data 中解析出的數據(包括本地解析或經蘋果服務器解析)中,從全部交易記錄中找到所有未被標記為已使用的交易記錄,作為集合A。
    2. 枚舉當前用戶的全部待支付訂單,匹配集合A中的交易記錄中product id相同的項,并將此交易記錄的 transaction id 記錄下,標記為已使用;并將此訂單標記為已支付,并發放道具。
    3. 線上環境僅在審核期間允許使用sandbox環境做二次驗證,防止上線后內部同事使用sandbox test user免費充值道具到線上環境。需注意審核期間必須開啟 sandbox 環境驗證,不然會被 rejected。

    其他安全問題

  • 盡量使用純 HTTPS 接口上傳 receipt,并嚴格校驗 SSL 證書,防止中間人攻擊。
  • 越獄后有木馬會盜取用戶的支付,如果必要,需要提醒用戶越獄風險。
  • 7.切換線上/測試環境

    需要在代碼里顯示聲明的環境,就只有二次驗證地址:

    1 2 #define SANDBOX_VERIFY_RECEIPT_URL [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"] #define APP_STORE_VERIFY_RECEIPT_URL [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"]

    而在Cocoa中調用蘋果接口時,開發證書、Beta測試證書、提交證書編譯出的App連接的都是Sandbox環境;只有上線后的App連接的是線上環境。

    另外再次強調,除非少量必要的自己線上環境的測試需要連接蘋果的 Sandbox 驗證服務之外,自己服務端的二次驗證 API 應該嚴格做到自己的環境是線上環境,則連接蘋果的線上環境二次驗證接口。防止監守自盜的情況出現。

    8.提交審核

    如果是初次提交審核,IAP 商品要和第一個支持 IAP 的版本一起提交。審核期間要允許 sandbox 環境二次驗證。
    后續新增的 IAP 商品則沒有此限制,可以隨時提交審核。

    Over

    總結

    以上是生活随笔為你收集整理的IAP详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。