iOS內(nèi)購編程指南

一般來說,開發(fā)人員剛接觸內(nèi)購旬昭,都會(huì)遇到流程不清楚篙螟、千頭萬緒。
如何一次性搞定內(nèi)購問題问拘?

一遍略、掌握內(nèi)購流程:

1、完成前期準(zhǔn)備工作

1)骤坐、接手內(nèi)購绪杏,一定要閱讀蘋果的《APP內(nèi)購買項(xiàng)目》文檔
2)、iTunesconnect后臺(tái)配置內(nèi)購項(xiàng)目

a或油、未配置過內(nèi)購的新項(xiàng)目寞忿,簽署 Paid Applications agreement(《付費(fèi)應(yīng)用程序協(xié)議》)


424da0e1-d0e4-4c92-9525-e853a243fa77.png

b、配置內(nèi)購項(xiàng)目顶岸,“App -->功能-->APP內(nèi)購買項(xiàng)目”
參考:創(chuàng)建及發(fā)布說明

3)腔彰、Xcode工程配置
da468add-ce28-4140-86bd-6aa27c3d40be.png

開啟此選項(xiàng)App Store中APP的介紹界面顯示內(nèi)購的相關(guān)項(xiàng)目叫编,關(guān)閉則不顯示

4)、開發(fā)實(shí)現(xiàn)流程:
296873ed-be51-4435-8ddc-d8391fd9b56f.jpg

2霹抛、iTunesconnect創(chuàng)建產(chǎn)品

蘋果的內(nèi)購分以下四類商品:
1搓逾、消耗型項(xiàng)目
只可使用一次的產(chǎn)品,使用之后即失效杯拐,必須再次購買霞篡。
示例:釣魚 App 中的魚食。
2端逼、非消耗型項(xiàng)目
只需購買一次朗兵,不會(huì)過期或隨著使用而減少的產(chǎn)品。
示例:游戲 App 的賽道顶滩。
3余掖、自動(dòng)續(xù)期訂閱
允許用戶在固定時(shí)間段內(nèi)購買動(dòng)態(tài)內(nèi)容的產(chǎn)品。除非用戶選擇取消礁鲁,否則此類訂閱會(huì)自動(dòng)續(xù)期盐欺。
示例:每月訂閱提供流媒體服務(wù)的 App。
4仅醇、非續(xù)期訂閱
允許用戶購買有時(shí)限性服務(wù)的產(chǎn)品冗美。此 App 內(nèi)購買項(xiàng)目的內(nèi)容可以是靜態(tài)的。此類訂閱不會(huì)自動(dòng)續(xù)期析二。
示例:為期一年的已歸檔文章目錄訂閱粉洼。

首先,在“我的APP”——“功能”——“App內(nèi)購買項(xiàng)目”添加適合自己的商品:


3CAFC87E-7FFA-43C3-8370-0E3A28329B14.png

二甲抖、開發(fā)實(shí)現(xiàn):

每個(gè)開發(fā)人員帳戶可在該帳戶的所有 App 中創(chuàng)建最多 10,000 個(gè) App 內(nèi)購買項(xiàng)目產(chǎn)品漆改。App 內(nèi)購買項(xiàng)目共有四種類型:消耗型、非消耗型准谚、自動(dòng)續(xù)期訂閱和非續(xù)期訂閱挫剑。

開源庫(XYIAPKit),使用此開源庫直接可省去“開發(fā)實(shí)現(xiàn)”這一步驟柱衔。

實(shí)現(xiàn)步驟主要包括三步:

1樊破、首先在項(xiàng)目工程中加入StoreKit.framework
2、加入頭文件#import <StoreKit/StoreKit.h>
3唆铐、遵守代理SKPaymentTransactionObserver,SKProductsRequestDelegate

步驟一:App Store請求內(nèi)購項(xiàng)

注意:此步驟建議在開始創(chuàng)建購買訂單前完成哲戚,這樣可以減少購買時(shí)查詢訂單的時(shí)間
1)、判斷用戶是否具備支付權(quán)限

if ([SKPaymentQueue canMakePayments]) {
        //允許應(yīng)用內(nèi)付費(fèi)購買
    }else {
        //用戶禁止應(yīng)用內(nèi)付費(fèi)購買.
    }

Indicates whether the user is allowed to make payments.
An iPhone can be restricted from accessing the Apple App Store. For example, parents can restrict their children’s ability to purchase additional content. Your application should confirm that the user is allowed to authorize payments before adding a payment to the queue. Your application may also want to alter its behavior or appearance when the user is not allowed to authorize payments.

2艾岂、創(chuàng)建一個(gè)商品查詢的請求顺少,productIdentifiers指需要查詢的“產(chǎn)品ID”的數(shù)組

NSSet * set = [NSSet setWithArray:productIdentifiers];
SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
request.delegate = self;
[request start];

查詢的結(jié)果將通過SKProductsRequestDelegate得到查詢的結(jié)果
獲取商品的查詢結(jié)果

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response NS_AVAILABLE_IOS(3_0);

在此回調(diào)中我們可以拿到所查詢產(chǎn)品的信息,保存所查詢的信息,以供構(gòu)建購買時(shí)使用脆炎。

SKProductsResponse

// Array of SKProduct instances.
@property(nonatomic, readonly) NSArray<SKProduct *> *products NS_AVAILABLE_IOS(3_0);

// Array of invalid product identifiers.
@property(nonatomic, readonly) NSArray<NSString *> *invalidProductIdentifiers NS_AVAILABLE_IOS(3_0);

獲取請求完成和失敗的結(jié)果

- (void)requestDidFinish:(SKRequest *)request NS_AVAILABLE_IOS(3_0);
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error NS_AVAILABLE_IOS(3_0);

步驟二:開始構(gòu)建購買請求

1)梅猿、判斷請求的產(chǎn)品ID是否已獲取到它的產(chǎn)品信息SKProduct,且是否可用秒裕。

若商品可用創(chuàng)建支付袱蚓;
若商品不可用提示用戶;
若商品尚未獲取它的產(chǎn)品信息几蜻,進(jìn)行“步驟一”操作

2)喇潘、創(chuàng)建支付
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
3)、添加支付交易的Observer

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

注意在適當(dāng)?shù)臅r(shí)候移除Observer
[[SKPaymentQueue defaultQueue] removeTransactionObserver:self];

可以通過遵循SKPaymentTransactionObserver協(xié)議來監(jiān)聽整個(gè)交易的過程

交易狀態(tài)發(fā)生改變時(shí)梭稚,包括狀態(tài)的改變颖低,交易的結(jié)束

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions NS_AVAILABLE_IOS(3_0);

switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased://交易完成
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed://交易失敗
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored://已經(jīng)購買過該商品
                [self restoreTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchasing:      //商品添加進(jìn)列表
                NSLog(@"商品添加進(jìn)列表");
                break;
            default:
                break;
        }
4)、校驗(yàn)票據(jù)

票據(jù)的校驗(yàn)是保證內(nèi)購安全完成的非常關(guān)鍵的一步弧烤,一般有三種方式:
1枫甲、服務(wù)器驗(yàn)證,獲取票據(jù)信息后上傳至信任的服務(wù)器扼褪,由服務(wù)器完成與App Store的驗(yàn)證(提倡使用此方法,比較安全)
2粱栖、本地票據(jù)校驗(yàn)
3话浇、本地App Store請求驗(yàn)證

a)、本地票據(jù)校驗(yàn)

本地票據(jù)校驗(yàn)的一般步驟
要驗(yàn)證收據(jù)闹究,請按順序執(zhí)行以下測試:
1.找到收據(jù)幔崖。
如果沒有收據(jù),則驗(yàn)證失敗渣淤。
2.驗(yàn)證收據(jù)是否由Apple 正確簽署
如果收據(jù)不是由 Apple 簽署赏寇,則驗(yàn)證失敗。
3.驗(yàn)證收據(jù)中的Bundle Identifier(數(shù)據(jù)包標(biāo)識(shí)符)與在Info.plist文件中含有您要的CFBundleIdentifier值的硬編碼常量相匹配价认。
如果兩者不匹配嗅定,則驗(yàn)證失敗。
4.驗(yàn)證收據(jù)中的版本標(biāo)識(shí)符字符串與在Info.plist文件中含有您要的CFBundleShortVersionString值(macOS)或
CFBundleVersion 值(iOS)的硬編碼常量相匹配用踩。
如果兩者不匹配渠退,則驗(yàn)證失敗。
5.按照“計(jì)算 GUID 的哈希(Hash)(第 8 頁)”所述計(jì)算GUID 的哈希(Hash)脐彩。
如果結(jié)果與收據(jù)中的哈希(Hash)不匹配碎乃,則驗(yàn)證失敗。
如果通過所有測試惠奸,則驗(yàn)證成功梅誓。

注意: Bundle Identifier(數(shù)據(jù)包標(biāo)識(shí)符)和版本標(biāo)識(shí)符字符串是UTF-8 字符串,而不僅僅是一系列字節(jié)。確保您相應(yīng)地編寫比較邏輯代碼梗掰。

如果您的 App 支持“批量購買計(jì)劃”嵌言,請檢查收據(jù)的有效日期。

b)愧怜、App Store驗(yàn)證:

1)呀页、獲取票據(jù)

- (NSString *)iapReceipt
{
    NSString *receiptString = nil;
    NSURL *rereceiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    if ([[NSFileManager defaultManager] fileExistsAtPath:[rereceiptURL path]]) {
        NSData *receiptData = [NSData dataWithContentsOfURL:rereceiptURL];
        receiptString = [receiptData base64EncodedStringWithOptions:0];
    }
    
    return receiptString;
}

如果此時(shí)未獲取到票據(jù)的信息,使用SKReceiptRefreshRequest來刷新票據(jù)結(jié)果拥坛。

SKReceiptRefreshRequest *refreshReceiptRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:@{}];
refreshReceiptRequest.delegate = self;
[refreshReceiptRequest start];
- (void)requestDidFinish:(SKRequest *)request {
}

2)蓬蝶、請求驗(yàn)證
獲取到票據(jù)以后我們通過App Store來驗(yàn)證票據(jù)是否真實(shí)
沙盒狀態(tài)下使用:https://sandbox.itunes.apple.com/verifyReceipt來驗(yàn)證
生產(chǎn)環(huán)境下使用:https://buy.itunes.apple.com/verifyReceipt
常見的驗(yàn)證狀態(tài)代碼:

6861C64F-2A45-4EF5-8D42-6A07C6B174A8.png
由于審核時(shí),審核人員為沙盒狀態(tài)猜惋,注意狀態(tài)碼“21007”
- (void)verifyRequestData:(NSString *)base64Data
                      url:(NSString *)url
              transaction:(SKPaymentTransaction *)transaction
                  success:(void (^)(void))successBlock
                  failure:(void (^)(NSError *error))failureBlock
{
    NSMutableDictionary *params = [NSMutableDictionary dictionary];
    [params setValue:base64Data forKey:@"receipt-data"];
    [params setValue:self.sharedSecretKey forKey:@"password"];
    
    NSError *jsonError;
    NSData *josonData = [NSJSONSerialization dataWithJSONObject:params
                                                        options:NSJSONWritingPrettyPrinted
                                                          error:&jsonError];
    if (jsonError) {
        NSLog(@"verifyRequestData failed: error = %@", jsonError);
    }
    
    
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url]];
    request.HTTPBody = josonData;
    static NSString *requestMethod = @"POST";
    request.HTTPMethod = requestMethod;
    
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSError *error;
        NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:&error];
        dispatch_async(dispatch_get_main_queue(), ^{
            
            if (!data) {
                NSError *wrapperError = [weakSelf unableVerifyReceiptError:error];
                if (failureBlock != nil) failureBlock(wrapperError);
                return;
            }
            
            NSError *jsonError;
            NSDictionary *responseJSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
            if (!responseJSON) {
                NSLog(@"Failed To Parse Server Response");
                if (failureBlock != nil) failureBlock(jsonError);
            }
            
            static NSString *statusKey = @"status";
            NSInteger statusCode = [responseJSON[statusKey] integerValue];
            
            static NSInteger successCode = 0;
            static NSInteger sandboxCode = 21007;
            if (statusCode == successCode) {
                [weakSelf saveVerifiedReceipts:transaction response:responseJSON];
                if (successBlock != nil) successBlock();
            } else if (statusCode == sandboxCode) {
                [weakSelf sandboxVerify:base64Data
                            transaction:transaction
                                success:successBlock
                                failure:failureBlock];
            } else {
                NSLog(@"Verification Failed With Code %ld", (long)statusCode);
                NSError *serverError = [NSError errorWithDomain:XYStoreErrorDomain code:statusCode userInfo:nil];
                if (failureBlock != nil) failureBlock(serverError);
            }
        });
    });
}

3)丸氛、核實(shí)票據(jù)信息
a、非訂閱產(chǎn)品著摔,若返回結(jié)果正確缓窜,驗(yàn)證通過
b、訂閱產(chǎn)品需要驗(yàn)證訂閱是否過期

- (BOOL)checkIsSubscribed:(XYiTunesResponse *)iTunesResponse productId:(NSString *)productId
{
    NSDate *expires_date;
    for (XYInAppReceipt *appReceipt in iTunesResponse.latest_receipt_info) {
        
        if ([appReceipt.product_id isEqualToString:productId] == NO) {
            continue;
        }
        
        if (expires_date) {
            expires_date = [expires_date laterDate:appReceipt.expires_date];
        }else {
            expires_date = appReceipt.expires_date;
        }
    }
    
    // 不包含過期信息表示無自動(dòng)續(xù)期交易
    if (!expires_date) {
        return NO;
    }
    
    if (([expires_date timeIntervalSinceDate:iTunesResponse.receipt.request_date] > 0) && ([expires_date timeIntervalSinceDate:[NSDate date]] > 0)) {
        // 1谍咆、對比請求時(shí)間
        // 針對SKPaymentTransactionObserver的監(jiān)聽禾锤,當(dāng)交易信息發(fā)生更新時(shí),蘋果會(huì)自動(dòng)推送當(dāng)前的交易狀態(tài)摹察,
        // 緩存票據(jù)更新時(shí)的請求時(shí)間恩掷,通過與過期時(shí)間對比來確定用戶的訂閱是否過期
        // 此方式可以避免用戶修改系統(tǒng)時(shí)間造成的問題,也能保證及時(shí)的更新用戶訂閱狀況
        // 也可使用獲取當(dāng)前外部服務(wù)器的時(shí)間供嚎,當(dāng)然需要異步操作黄娘,時(shí)間成本比較高
        
        // 2、對比系統(tǒng)時(shí)間
        // 防止訂閱后克滴,用戶強(qiáng)制斷網(wǎng)
        
        return YES;
    }
    
    return NO;
}

@interface XYInAppReceipt : NSObject

/**
 The default value is 1, the minimum value is 1, and the maximum value is 10.
 */
@property (nonatomic, assign) NSInteger quantity;

@property (nonatomic, copy) NSString *product_id;

@property (nonatomic, copy) NSString *transaction_id;

@property (nonatomic, copy) NSString *original_transaction_id;

@property (nonatomic, strong) NSDate *purchase_date;

@property (nonatomic, strong) NSDate *original_purchase_date;

/**
 僅用于逼争,自動(dòng)續(xù)費(fèi)訂閱
 true,表示處于 免費(fèi)試用 時(shí)期
 如果已有票據(jù)中含有is_trial_period或者is_in_intro_offer_period為true劝赔,用戶不再具備有此項(xiàng)資格
 
 For a subscription, whether or not it is in the free trial period.
 This key is only present for auto-renewable subscription receipts. The value for this key is "true" if the customer’s subscription is currently in the free trial period, or "false" if not.
 
 Note: If a previous subscription period in the receipt has the value “true” for either the is_trial_period or the is_in_intro_offer_period key, the user is not eligible for a free trial or introductory price within that subscription group.
 */
@property (nonatomic, assign) BOOL is_trial_period;

//**********************以上為四種內(nèi)購類型公共字段誓焦,下面字段為自動(dòng)續(xù)期訂閱獨(dú)有字段*********************

/**
 This key is only present for auto-renewable subscription receipts.
 Use this value to identify the date when the subscription will renew or expire, to determine if a customer should have access to content or service.
 After validating the latest receipt, if the subscription expiration date for the latest renewal transaction is a past date, it is safe to assume that the subscription has expired.
 
 */
@property (nonatomic, strong) NSDate *expires_date;

/**
 “1” - Customer canceled their subscription.
 “2” - Billing error; for example customer’s payment information was no longer valid.
 “3” - Customer did not agree to a recent price increase.
 “4” - Product was not available for purchase at the time of renewal.
 “5” - Unknown error
 */
@property (nonatomic, assign) NSInteger expiration_intent;

/**
 對于訂閱過期的自動(dòng)續(xù)費(fèi)產(chǎn)品,蘋果是否會(huì)嘗試自動(dòng)續(xù)費(fèi)
 For an expired subscription, whether or not Apple is still attempting to automatically renew the subscription.
 “1” - App Store is still attempting to renew the subscription.
 “0” - App Store has stopped attempting to renew the subscription.
 
 This key is only present for auto-renewable subscription receipts. If the customer’s subscription failed to renew because the App Store was unable to complete the transaction, this value will reflect whether or not the App Store is still trying to renew the subscription.
 */
@property (nonatomic, assign) BOOL is_in_billing_retry_period;

/**
 僅用于着帽,自動(dòng)續(xù)費(fèi)訂閱
 true罩阵,表示處于 引導(dǎo)價(jià)格 時(shí)期
 如果已有票據(jù)中含有is_trial_period或者is_in_intro_offer_period為true,用戶不再具備有此項(xiàng)資格
 
 For an auto-renewable subscription, whether or not it is in the introductory price period.
 This key is only present for auto-renewable subscription receipts. The value for this key is "true" if the customer’s subscription is currently in an introductory price period, or "false" if not.
 
 Note: If a previous subscription period in the receipt has the value “true” for either the is_trial_period or the is_in_intro_offer_period key, the user is not eligible for a free trial or introductory price within that subscription group.
 */
@property (nonatomic, assign) BOOL is_in_intro_offer_period;

/**
 退款操作時(shí)間
 For a transaction that was canceled by Apple customer support, the time and date of the cancellation. For an auto-renewable subscription plan that was upgraded, the time and date of the upgrade transaction.
 Treat a canceled receipt the same as if no purchase had ever been made.
 A canceled in-app purchase remains in the receipt indefinitely. Only applicable if the refund was for a non-consumable product, an auto-renewable subscription, a non-renewing subscription, or for a free subscription.
 */
@property (nonatomic, strong) NSDate *cancellation_date;


/**
 內(nèi)購取消的原因
 “1” - Customer canceled their transaction due to an actual or perceived issue within your app.
 
 “0” - Transaction was canceled for another reason, for example, if the customer made the purchase accidentally.
 
 Use this value along with the cancellation date to identify possible issues in your app that may lead customers to contact Apple customer support.
 */
@property (nonatomic, copy) NSString *cancellation_reason;

/**
 APP唯一標(biāo)識(shí)符
 */
@property (nonatomic, copy) NSString *app_item_id;

/**
 This key is not present for receipts created in the test environment. Use this value to identify the version of the app that the customer bought
 */
@property (nonatomic, copy) NSString *version_external_identifier;

/**
 This value is a unique ID that identifies purchase events across devices, including subscription renewal purchase events.
 */
@property (nonatomic, copy) NSString *web_order_line_item_id;

/**
 The current renewal status for the auto-renewable subscription.
 “1” - Subscription will renew at the end of the current subscription period.
 
 “0” - Customer has turned off automatic renewal for their subscription.
 
 This key is only present for auto-renewable subscription receipts, for active or expired subscriptions. The value for this key should not be interpreted as the customer’s subscription status. You can use this value to display an alternative subscription product in your app, for example, a lower level subscription plan that the customer can downgrade to from their current plan.
 
 */
@property (nonatomic, assign) NSInteger auto_renew_status;

/**
 The current renewal preference for the auto-renewable subscription.
 This key is only present for auto-renewable subscription receipts. The value for this key corresponds to the productIdentifier property of the product that the customer’s subscription renews. You can use this value to present an alternative service level to the customer before the current subscription period ends.
 */
@property (nonatomic, copy) NSString *auto_renew_product_id;

/**
 The current price consent status for a subscription price increase.
 “1” - Customer has agreed to the price increase. Subscription will renew at the higher price.
 
 “0” - Customer has not taken action regarding the increased price. Subscription expires if the customer takes no action before the renewal date.
 
 This key is only present for auto-renewable subscription receipts if the subscription price was increased without keeping the existing price for active subscribers. You can use this value to track customer adoption of the new price and take appropriate action.
 */
@property (nonatomic, assign) NSInteger price_consent_status;

*自動(dòng)續(xù)期訂閱

1)启摄、自動(dòng)續(xù)費(fèi)
購買流程上稿壁,自動(dòng)續(xù)費(fèi)訂閱與普通購買沒有區(qū)別;
主要的區(qū)別在于:除了第一次購買行為是用戶主動(dòng)觸發(fā)的歉备。后續(xù)續(xù)費(fèi)都是Apple自動(dòng)完成的傅是,一般在要過期的前24小時(shí)開始,蘋果會(huì)嘗試扣費(fèi),扣費(fèi)成功的話 會(huì)在APP下次啟動(dòng)的時(shí)候主動(dòng)推送給APP喧笔。
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
添加上面的監(jiān)聽就是非常必要的了帽驯。

如何處理首次訂閱以及續(xù)費(fèi)訂閱?

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased://交易完成
 
                 // 訂閱特殊處理
                 if(transaction.originalTransaction){
                      // 如果是自動(dòng)續(xù)費(fèi)的訂單originalTransaction會(huì)有內(nèi)容 
                 }else{
                      // 普通購買书闸,以及 第一次購買 自動(dòng)訂閱
                 }
                break;
            case SKPaymentTransactionStateFailed: //交易失敗

                break;
            case SKPaymentTransactionStateRestored: //已經(jīng)購買過該商品

                break;
            case SKPaymentTransactionStatePurchasing:  //商品添加進(jìn)列表

                break;
            default:
                break;
        }
    }
}

關(guān)鍵點(diǎn): 上面代碼可以看到尼变,通過transaction.originalTransaction來判斷是否為續(xù)費(fèi)訂單,通過這個(gè)知識(shí)浆劲,對之后的支付統(tǒng)計(jì)及付費(fèi)跟蹤非常關(guān)鍵嫌术。

2)、推介促銷價(jià)(目前比較流行的套路)

f415317a-a6f4-4cc4-8c27-7031fdb78e86.png

主要分:隨用隨付牌借、隨用隨付度气、免費(fèi)試用
a、隨用隨付

如果您選擇“隨用隨付”膨报,則顧客將為選定時(shí)限的每個(gè)結(jié)算周期支付推介促銷價(jià)(例如磷籍,訂閱的標(biāo)準(zhǔn)價(jià)格為 9.99 美元,推介促銷價(jià)為前 3 個(gè)月每月 1.99 美元)现柠≡毫欤可設(shè)定以下時(shí)限:

1 周訂閱,1 至 12 周

1 個(gè)月訂閱够吩,1 至 12 個(gè)月

2 個(gè)月訂閱栅盲,2、4废恋、6、8扒寄、10 和 12 個(gè)月

3 個(gè)月訂閱鱼鼓,3、6该编、9 和 12 個(gè)月

6 個(gè)月訂閱迄本,6 和 12 個(gè)月

1 年訂閱,1 年

b课竣、提前支付

如果您選擇“提前支付”嘉赎,顧客將一次性支付選定時(shí)限的推介促銷價(jià)(例如,訂閱的標(biāo)準(zhǔn)價(jià)格為 9.99 美元于樟,推介促銷價(jià)為前 2 個(gè)月 1.99 美元)公条。可設(shè)定以下時(shí)限:1 個(gè)月迂曲、2 個(gè)月靶橱、3 個(gè)月、6 個(gè)月或 1 年。

c关霸、免費(fèi)試用

如果您選擇“免費(fèi)試用”传黄,則顧客在選定的時(shí)限內(nèi)免費(fèi)訪問訂閱。時(shí)限可以是 3 天队寇、1 周膘掰、2 周、1 個(gè)月佳遣、2 個(gè)月识埋、3 個(gè)月、6 個(gè)月或 1 年苍日。

推介促銷價(jià)是為回饋用戶的一個(gè)促銷方式惭聂,Apple推出后很受APP開發(fā)的歡迎。
當(dāng)然也是非常流行的套路相恃,我們了解一下這個(gè)地方套路的核心問題:
拿免費(fèi)試用來說辜纲,用戶一旦點(diǎn)擊了免費(fèi)試用后,免費(fèi)試用結(jié)束后拦耐,蘋果將自動(dòng)進(jìn)行扣款耕腾;
那么如果試用后不想購買而想退款的話,找到取消的地方就需要靠智商了杀糯,取消免費(fèi)試用的路徑隱藏的非常深扫俺。


1
5322eec9-ff3e-4962-a908-51e56fce0a42.png
0b6e907e-a101-4290-9d5a-430883d75601.png
0b6e907e-a101-4290-9d5a-430883d75601.png
39605e26-7ca0-471d-9d4e-85524a638996.png
cc407dec-efdd-4bde-878e-795e3eceabd3.png

四、內(nèi)購的測試問題

1)固翰、沙盒測試:非正式環(huán)境下測試需要添加沙盒測試員

您可以使用沙箱技術(shù)測試您的 App 和 App 內(nèi)購買項(xiàng)目狼纬,而無需創(chuàng)建財(cái)務(wù)交易。沙箱技術(shù)是一個(gè)使用 App Store 基礎(chǔ)架構(gòu)但不處理實(shí)際付款的測試環(huán)境骂际。它會(huì)返回交易疗琉,付款被視為已成功處理。
添加方式:


64e162fc-cb43-4c7c-84e5-32bef7c7b489.png

自動(dòng)續(xù)費(fèi)測試時(shí)需注意:

11436ef0-9cfe-40c4-bd2c-fe4aa2d1e5fd.png

意思就是歉铝,沙箱環(huán)境 自動(dòng)續(xù)費(fèi)時(shí)間縮短了盈简,一周 對應(yīng) 三分鐘,一月 對應(yīng) 五分鐘太示。柠贤。。
購買完一個(gè)一周 類型訂閱类缤,就不要在APP不退出的情況等待了臼勉,必須3分鐘 或是 10分鐘后重新登錄,Apple才會(huì)主動(dòng)告知你結(jié)果餐弱,也就是第一點(diǎn)提到的坚俗。

沙箱環(huán)境自動(dòng)續(xù)費(fèi)是一定會(huì)自動(dòng)續(xù)費(fèi)的嗎镜盯?
不一定的,有時(shí)候會(huì)猖败,有時(shí)候不會(huì)速缆。所以要多測測,多建幾個(gè)測試賬號(hào)恩闻。

2)艺糜、若發(fā)現(xiàn)測試的商品無法進(jìn)行購買,或者無法調(diào)起付費(fèi)幢尚,可以檢驗(yàn)商品是否存在破停,或者是否正確等問題。

五尉剩、審核問題

添加自動(dòng)訂閱真慢,必須在訂閱路徑內(nèi)需包含以下信息:
1)、隱私協(xié)議及關(guān)于App的內(nèi)容
2)理茎、暴露自動(dòng)續(xù)費(fèi)的信息

 – Information about the auto-renewable nature of the subscription
 – Links to the privacy policy and terms of use
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末黑界,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子皂林,更是在濱河造成了極大的恐慌朗鸠,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件础倍,死亡現(xiàn)場離奇詭異烛占,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沟启,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門忆家,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人德迹,你說我怎么就攤上這事芽卿。” “怎么了浦辨?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沼沈。 經(jīng)常有香客問我,道長列另,這世上最難降的妖魔是什么芽腾? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮页衙,結(jié)果婚禮上摊滔,老公的妹妹穿的比我還像新娘阴绢。我一直安慰自己,他們只是感情好艰躺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布呻袭。 她就那樣靜靜地躺著,像睡著了一般腺兴。 火紅的嫁衣襯著肌膚如雪左电。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天页响,我揣著相機(jī)與錄音篓足,去河邊找鬼。 笑死闰蚕,一個(gè)胖子當(dāng)著我的面吹牛栈拖,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播没陡,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼涩哟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诗鸭?” 一聲冷哼從身側(cè)響起染簇,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎强岸,沒想到半個(gè)月后锻弓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝌箍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年青灼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片妓盲。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡杂拨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出悯衬,到底是詐尸還是另有隱情弹沽,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布筋粗,位于F島的核電站策橘,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏娜亿。R本人自食惡果不足惜丽已,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望买决。 院中可真熱鬧沛婴,春花似錦吼畏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至旁仿,卻和暖如春藕夫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枯冈。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工毅贮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尘奏。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓足陨,卻偏偏與公主長得像侵佃,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一.總說內(nèi)購的內(nèi)容 協(xié)議劫恒、稅務(wù)和銀行業(yè)務(wù) 信息填寫 內(nèi)購商品的添加 添加沙盒測試賬號(hào) 內(nèi)購代碼的具體實(shí)現(xiàn) 內(nèi)購的注...
    默默_David閱讀 3,657評論 0 6
  • 一.總說內(nèi)購的內(nèi)容 協(xié)議寝优、稅務(wù)和銀行業(yè)務(wù) 信息填寫 內(nèi)購商品的添加 添加沙盒測試賬號(hào) 內(nèi)購代碼的具體實(shí)現(xiàn) 內(nèi)購的注...
    九洲仙人閱讀 2,964評論 2 3
  • iOS應(yīng)用內(nèi)付費(fèi)(IAP)開發(fā)步驟 1.蘋果iTunes Connect內(nèi)購產(chǎn)品信息錄入匙奴。 1)創(chuàng)建app內(nèi)購買項(xiàng)...
    MillerWang閱讀 11,014評論 0 7
  • 1.支付類和協(xié)議 Store Kit框架下提供用來處理應(yīng)用內(nèi)購大致的類可以分為兩部分:1.請求商品浮声,2.購買商品。...
    蕭子然閱讀 7,140評論 10 10
  • 我是一名來自蕪湖薈萃中學(xué)初二七班的學(xué)生,前幾天在老師和同學(xué)的推舉下我迎來了人生的第一次論壇導(dǎo)演機(jī)會(huì) 其實(shí)說真的革骨,剛...
    樂如魚閱讀 1,326評論 7 7