總結一下開發蘋果內購IAP和防丟單的一些措施

apple.jpg

因為之前做的項目都是電商的,交易的也都是實體的,所以都會采用微信,支付寶等第三方支付,現在這個公司的項目是個付費小說的軟件,要搞支付只能選擇蘋果內購了。
具體怎么在ituens connect開通就不在介紹了,網上也有很多資料,這篇文章主要分享一下app接入內購的代碼,已經防丟單的處理策略。

下面先介紹一下 內購的主要流程

  1. 首先拿到productid去請求蘋果服務器,請求產品信息
  2. 得到蘋果服務器的返回信息,如果產品存在且為可售狀態,則生成內購訂單,加入到蘋果的交易隊列
  3. 蘋果檢查到支付成功或者失敗后,會通過代理,回調支付的結果
  4. 根據結果,如果成功則取出交易憑證,一般是交給自己的服務器去驗證
  5. 驗證完成之后的一些處理
// 去蘋果服務器請求產品信息
- (void)requestProductData:(NSString *)productId {
    NSArray *productArr = [[NSArray alloc]initWithObjects:productId, nil];
    NSSet *productSet = [NSSet setWithArray:productArr];
    SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
    request.delegate = self;
    [request start];
    
#pragma mark - SKProductsRequestDelegate //這個是向蘋果服務器請求產品的代理

- (void)requestDidFinish:(SKRequest *)request
{
    _timeIntervalStart = [[NSDate date] timeIntervalSince1970];
    [KR_RECHARGELOG appendingString:@"-> SKProductReqFinish"];
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
    [KR_PROGRESS toast:STRING_LOCALIZE(@"recharge_pay_failed")];
    [KR_RECHARGELOG appendingString:@"-> SKProductReqFail"];
}

/**
 收到產品返回信息
 */
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSArray *productArr = response.products;
    if ([productArr count] == 0)
    {
        return;
    }
    
    SKProduct *product = nil;
    for (SKProduct *pro in productArr) {
        if ([pro.productIdentifier isEqualToString:_orderModel.data[@"product_id"]]) {
            product = pro;
            break;
        }
    }
    if (product)
    {
        //此處為創建內購訂單,加入到蘋果內購iap的交易隊列
        SKMutablePayment * payment = [SKMutablePayment paymentWithProduct:product];
        //此處是把訂單號 存到 payment的applicationUsername字段上
        payment.applicationUsername = _orderModel.orderNum;
        //發送內購請求
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    }
    else
    {
        NSLog(@"沒有此商品");
    }
}
#pragma mark - SKPaymentTransactionObserver
// 監聽購買結果
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
{
    for (SKPaymentTransaction *transaction in transactions)
    {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased://交易完
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed://交易失敗
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored://已經購買過該商品
                [self restoreTransaction:transaction];
                break;
            case SKPaymentTransactionStatePurchasing://商品添加進列表
                break;
            case SKPaymentTransactionStateDeferred://狀態未確定
            default:
                break;
        }
    }
}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {
    //交易失敗
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

- (void)restoreTransaction:(SKPaymentTransaction *)transaction
{
    [KR_PROGRESS toast:STRING_LOCALIZE(@"recharge_reinstate")];
    // 對于已購商品,處理恢復購買的邏輯
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

// 驗證購買
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    // 驗證憑據,獲取到蘋果返回的交易憑據
    // appStoreReceiptURL iOS7.0增加的,購買交易完成后,會將憑據存放在該地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];
   //這個recptData就是交易的憑證,你可以選擇是客戶端自己去向蘋果驗證,但更多情況是 把憑證傳給服務器,讓服務器去驗證
}

在開發測試的時候可以先客戶端驗證,先驗證能不能完整走完內購流程

//沙盒測試環境驗證
#define SANDBOX @"https://sandbox.itunes.apple.com/verifyReceipt"
//正式環境驗證
#define AppStore @"https://buy.itunes.apple.com/verifyReceipt"
// 驗證購買
- (void)verifyPurchaseWithPaymentTrasaction {

    // 驗證憑據,獲取到蘋果返回的交易憑據
    // appStoreReceiptURL iOS7.0增加的,購買交易完成后,會將憑據存放在該地址
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    // 從沙盒中獲取到購買憑據
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptURL];


    // 發送網絡POST請求,對購買憑據進行驗證
    //測試驗證地址:https://sandbox.itunes.apple.com/verifyReceipt
    //正式驗證地址:https://buy.itunes.apple.com/verifyReceipt
    NSURL *url = [NSURL URLWithString:SANDBOX];
    NSMutableURLRequest *urlRequest =
    [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
    urlRequest.HTTPMethod = @"POST";
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    urlRequest.HTTPBody = payloadData;
    // 提交驗證請求,并獲得官方的驗證JSON結果 iOS9后更改了另外的一個方法
    NSData *result = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse:nil error:nil];
    // 官方驗證結果為空
    if (result == nil) {
        NSLog(@"驗證失敗");
        return;
    }
    NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:result options:NSJSONReadingAllowFragments error:nil];
    if (dict != nil) {
        // 比對字典中以下信息基本上可以保證數據安全
        // bundle_id , application_version , product_id , transaction_id
        //        NSLog(@"驗證成功!購買的商品是:%@", @"_productName");

        NSLog(@"驗證成功%@",dict);
    }
}

以上基本上就是內購的流程了,但是有可能由于個種原因,導致蘋果內購的- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions回調不及時,或者當次根本就沒回調成功,等等照成訂單丟失的現象。
下面我結合我們項目的實際情況來說一下對app內購丟單的處理,我們的app邏輯是在下單的同時,后臺會給你一個訂單號,等app支付完成后,在蘋果回調時,把憑證和訂單號一塊傳給后臺,如果后臺驗證成功,則會根據訂單號增加對應的用戶id的看點。剛上線的時候,每天都收到很多看點不到賬的問題反饋,原因是因為蘋果回調的不及時,等蘋果回調了,訂單號已經不存在了,所以造成,扣款成功,服務器卻無法知道成功狀態,導致訂單丟失。
后來我們進行了優化處理,比如在上述- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response方法中,將訂單號保存到對應的SKMutablePayment,然后等到蘋果內購回調的時候,取出訂單號,然后將交易憑證和訂單號一塊發給服務器處理。
還有就是在蘋果回調的時候,將訂單號以及憑證本地化存儲,我做項目的時候采用的是,每有一個交易就存儲到本地的plist文件數組中,在很多地方去主動檢查有沒有存儲的訂單號,如果有就依次取出去拿到服務器認證,認證完成之后在本地刪除對應的憑證及訂單號。
經過優化之后投訴量基本上在幾天一個,對內購丟單有疑問的同學可以互相交流一下

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容