iOS收藏固予SDK

总结一下开发苹果内购IAP和防丢单的一些措施

2017-10-11  本文已影响1252人  蛋壳儿
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文件数组中,在很多地方去主动检查有没有存储的订单号,如果有就依次取出去拿到服务器认证,认证完成之后在本地删除对应的凭证及订单号。
经过优化之后投诉量基本上在几天一个,对内购丢单有疑问的同学可以互相交流一下

上一篇 下一篇

猜你喜欢

热点阅读