ios-iap应用内购接入


title: ios-iap应用内购接入
categories: Ios
tags: [ios, iap, 内购, 苹果支付]
date: 2021-03-26 16:51:37
comments: false
mathjax: true
toc: true

ios-iap应用内购接入


前篇

  • 官方
    • App 内购买项目信息 - https://help.apple.com/app-store-connect/#/dev84b80958f
  • 接入Apple Pay流程 - https://juejin.cn/post/6844904001897512967
  • iOS内购(IAP)模块总结 - https://www.jianshu.com/p/bf7e42a3c73b
  • iOS的应用内支付In-app purchase的开发 - http://blog.hudongdong.com/ios/868.html
  • 2019最新iOS内购流程基本配置篇 - https://www.jianshu.com/p/94fa6793b197
  • apple苹果IOS内购申请教程协议、税务和银行业务配置

前置条件

  1. 协议是 active 状态, https://appstoreconnect.apple.com/agreements/#/

  2. 产品 id 是 非 元数据丢失 状态


流程

  1. 先创建个沙盒测试人员, 测试时不会真实扣钱. https://appstoreconnect.apple.com/access/testers

  2. 内购代码

    #import "IapHelper.h"
    #import "NetTool.h"
    #import "NSString+MyExt.h"
    
    #import <Foundation/Foundation.h>
    #import <StoreKit/StoreKit.h>
    #import <YYModel/YYModel.h>
    
    @interface IapHelper()<SKProductsRequestDelegate, SKPaymentTransactionObserver>
    @property (strong, nonatomic) CodeMsgFn _Nullable cb;
    @end
    
    @implementation IapHelper
    
    static IapHelper *_sharedIns = nil;
    +(instancetype) shareInstance {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            _sharedIns = [[self alloc] init] ;
            [[SKPaymentQueue defaultQueue] addTransactionObserver:_sharedIns];
        }) ;
        return _sharedIns ;
    }
    
    // 内扣接口
    - (void)getById:(NSString *)prodId cb:(CodeMsgFn)cb {
        if (![SKPaymentQueue canMakePayments]) {
            cb(ECodeSupportError, @"--- cant pay");
            return;
        }
    
        self.cb = cb;
        NSSet *set = [NSSet setWithObjects:prodId, nil];
        SKProductsRequest * request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
        request.delegate = self;
        [request start];
    }
    
    // 客户端校验 receipt, 测试使用
    -(void)verify:(NSString*)url cb:(CodeMsgFn)cb {
        NSURL *recepitURL = [[NSBundle mainBundle] appStoreReceiptURL];
        NSData *receipt = [NSData dataWithContentsOfURL:recepitURL];
        NSDictionary *requestContents = @{@"receipt-data": [receipt base64EncodedStringWithOptions:0]};
        [NetTool httpPostAsync:url json:[requestContents yy_modelToJSONString] callback:^(CHttpJsonRsp * _Nonnull rsp) {
            cb(ECodeOk, [rsp yy_modelToJSONString]);
        }];
    }
    
    -(void)doCallback:(ECode)code msg:(NSString*)msg{
        if (self.cb) {
            self.cb(code, msg);
            self.cb = nil;
        }
    }
    
    // ------------------------------- 实现 SKProductsRequestDelegate 的接口
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
        if (response.products.count == 0) {
            [self doCallback:ECodeTaskError msg:@"--- products count is 0"];
            return;
        }
        
        SKProduct* product = response.products[0];
        SKMutablePayment *payMent = [SKMutablePayment paymentWithProduct:product];
        payMent.quantity = 1;
        payMent.applicationUsername = @"orderId_001"; // 透传参数
        [[SKPaymentQueue defaultQueue] addPayment:payMent];
    }
    
    // ------------------------------- 实现 SKPaymentTransactionObserver 的接口
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
        if (transactions.count == 0) {
            [self doCallback:ECodeLoginError msg:@"--- transactions count is 0"];
            return;
        }
        
        SKPaymentTransaction* transaction = transactions[0];
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                [self doCallback:ECodeOk msg:@"--- pay success"];
                break;
            case SKPaymentTransactionStateFailed:
                [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
                [self doCallback:ECodeCancel msg:@"--- SKPaymentTransactionStateFailed"];
                break;
            default:
                break;
        }
    }
    
    @end
    

验证 receipt

  • 参考: https://developer.apple.com/cn/documentation/storekit/in-app_purchase/validating_receipts_with_the_app_store/
  • 生产环境 url: https://buy.itunes.apple.com/verifyReceipt
  • 沙盒环境 url: https://sandbox.itunes.apple.com/verifyReceipt

验证流程: 先使用生产环境 url 去验证, 如果返回的 status 为 0, 表示是生成环境产生的正式购买; 如果返回的 status 为 21007, 表示是 测试人员产生的测试购买, 弹窗会显示环境是 sandbox.

  • 返回值

    {
          
          
        "receipt":{
          
          
            "receipt_type":"ProductionSandbox",
            "adam_id":0,
            "app_item_id":0,
            "bundle_id":"aaa.bbb.com",
            "application_version":"1",
            "download_id":0,
            "version_external_identifier":0,
            "receipt_creation_date":"2021-03-29 09:46:49 Etc/GMT",
            "receipt_creation_date_ms":"1617011209000",
            "receipt_creation_date_pst":"2021-03-29 02:46:49 America/Los_Angeles",
            "request_date":"2021-03-29 09:46:54 Etc/GMT",
            "request_date_ms":"1617011214550",
            "request_date_pst":"2021-03-29 02:46:54 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":"chips90",
                    "transaction_id":"1000000123123123",
                    "original_transaction_id":"1000000123123123",
                    "purchase_date":"2021-03-29 09:46:48 Etc/GMT",
                    "purchase_date_ms":"1617011208000",
                    "purchase_date_pst":"2021-03-29 02:46:48 America/Los_Angeles",
                    "original_purchase_date":"2021-03-29 09:46:48 Etc/GMT",
                    "original_purchase_date_ms":"1617011208000",
                    "original_purchase_date_pst":"2021-03-29 02:46:48 America/Los_Angeles",
                    "is_trial_period":"false"
                }
            ]
        },
        "environment":"Sandbox",
        "status":0 // 0 就是成功的标记
    }
    

踩坑

产品状态 元数据丢失

需要填写 审核信息, 包裹 截图 和 备注, 状态就会修改为 准备提交


拉取 产品 id 为空

可以以下几个原因

  1. 产品状态是 元数据丢失, 不是 准备提交

  2. 付费 app 协议没有同意, 需要同意后才行 (需要账户持有人才能同意, 协作伙伴不行), 状态是 有效 状态才行.

    https://appstoreconnect.apple.com/agreements/#/


付费 app 协议 地址错误

付费 app 协议 同意前可能遇到 地址错误要修正, 报错: The address entered appears to be invalid.

报这样的错只要提交三次就行了, 这是什么鬼操作.

第一填写比如: (第一行加个 # 号, 第三行不用填)

Address
# FLAT 501,MAPPLE CELESTIA,PLOT 49,
JAY ABHERI ENCLAVE,GACHIBOWLI,HYDERABAD,

参考: https://stackoverflow.com/questions/43922778/the-address-entered-appears-to-be-invalid-please-correct-your-address-and-resu


猜你喜欢

转载自blog.csdn.net/yangxuan0261/article/details/116118350