ios StoreKit内购接入流程(非订阅)

1,960 阅读3分钟

创建观察者处理对支付队列

class StoreObserver: NSObject, SKPaymentTransactionObserver {
    //Initialize the store observer.
    override init() {
        super.init()
        //Other initialization here.
    }

    //Observe transaction updates.
    func paymentQueue(_ queue: SKPaymentQueue,updatedTransactions transactions: [SKPaymentTransaction]) {
        //Handle transaction states here.
    }
}

AppDelegate处理

import StoreKit

let iapObserver = StoreObserver()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
//[[SKPaymentQueue defaultQueue] addTransactionObserver:self<img src="];" alt="" width="50%" />
    SKPaymentQueue.default().add(iapObserver)
    return true
}
func applicationWillTerminate(_ application: UIApplication) {
    SKPaymentQueue.default().remove(iapObserver)
}

遵循SKProductsRequestDelegate,SKPaymentTransactionObserver协议

获取产品信息

//确认授权
var isAuthorizedForPayments: Bool {
    return SKPaymentQueue.canMakePayments()
}
//请求 继承<SKProductsRequestDelegate>协议
fileprivate func fetchProducts(matchingIdentifiers identifiers: [String]) {
    // Create a set for the product identifiers.
    let productIdentifiers = Set(identifiers)
    
    // Initialize the product request with the above identifiers.
    productRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
    productRequest.delegate = self
    
    // Send the request to the App Store.
    productRequest.start()
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    NSArray *pro = response.products;
    if (pro.count > 0) {
        SKProduct *product;
        for (SKProduct *pid in pro) {
            NSLog(@"描述信息-> %@",pid.description);
            NSLog(@"产品标题-> %@; 产品描述-> %@",pid.localizedTitle,pid.localizedDescription);
            NSLog(@"价格-> %@",pid.price);
            product = pid;
        }
        [[SKPaymentQueue defaultQueue] addPayment:[SKPayment paymentWithProduct:(SKProduct *)product]];
    }else {
        NSLog(@"无法获得商品");
    }
}

- (void)requestDidFinish:(SKRequest *)request {
    NSLog(@"处理交易请求完成");
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    NSLog(@"处理交易请求失败");
}

发起购买

//通过上一步从apple获取的商品信息发起
SKProduct *product = <# 上面回调方法里pro中要购买的商品 #>;
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
payment.quantity = 1;//商品数量
[[SKPaymentQueue defaultQueue] addPayment:payment]

//自行设置发起(确认商品ID真实有效)
SKMutablePayment *payment = [[SKMutablePayment alloc] init];
payment.applicationUsername = _orderId; //透传参数。可以传你自己的订单号(不稳定)
payment.productIdentifier = _productId; //商品ID payment.quantity = _count; //商品数量,一般默认都传1 
[[SKPaymentQueue defaultQueue] addPayment:payment];

购买结果回调

- (void)paymentQueue:(SKPaymentQueue *)queue
 updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchased:
                //交易完成
                [self paymentTransactionPurchased];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStatePurchasing:
                //交易中
                [self paymentTransactionPurchasing];
                break;
            case SKPaymentTransactionStateRestored:
                //已经购买过
                [self paymentTransactionRestored];
                [[SKPaymentQueue defaultQueue] finishTransaction:tran];
                break;
            case SKPaymentTransactionStateFailed:
                //交易失败
                [self paymentTransactionFailed:tran];
                break;
            default:
                break;
        }
    }
}

购买凭证检验

- (void)paymentTransactionPurchased {
    NSURL *receiptUrl = NSBundle.mainBundle.appStoreReceiptURL;
    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];
    if (!receiptData) {
        return;
    }
    NSString *receiptStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSLog(@"订单信息-> %@",receiptStr);
    //验证购买
    //收据(自行验证Or后台验证)
    [self verify_IapCode:receiptStr];
}

- (void)paymentTransactionPurchasing {
    NSLog(@"交易中");
}

- (void)paymentTransactionRestored {
    NSLog(@"已经过买过");
}

- (void)paymentTransactionFailed:(SKPaymentTransaction *)transaction {
    NSError *error = transaction.error;
    if (error) {
        if (error.code != SKErrorPaymentCancelled) {
            NSLog(@"交易失败");
        }else {
            NSLog(@"交易取消");
        }
    }else {
        [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
    }
}

交易结束

在适当位置

[[SKPaymentQueue defaultQueue] finishTransaction:transaction];

storeKit

storeKitV1:

storeKitV2:

  • In-App Purchase: 一个基于 Swift 的 API,以 JSON Web Signature (JWS) 格式提供 Apple 签名交易验证,从 iOS 15、macOS 12、tvOS 15 和 watchOS 8 开始提供

Link Binary with Libraries 直接添加

内购退款

退款通知--orderId--查对应transaction 交易信息 通过用户的任一个 originalTransactionId 可以查到这个用户的所有退款记录订单

当用户申请退款时,苹果通知(CONSUMPTION_REQUEST)开发者服务器,开发者可在12小时内,提供用户的信息(比如游戏金币是否已消费、用户充值过多少钱、退款过多少钱等),最后苹果收到这些信息,协助“退款决策系统” 来决定是否允许用户退款!详细见文档:[Send Consumption Information(developer.apple.com/documentati…)

#防掉单# 防hook

建议通过KeyChain,发起支付时缓存订单号,校验凭证成功后清理 掉单时:

  • SKPaymentQueue 检测是否有未完成的交易----通过订单号请求服务器查询支付结果
  • KeyChain 未清理单号----通过订单号请求服务器查询支付结果

参考文档