苹果内购(IAP)流程设计

2,594 阅读3分钟

名词简称

  • S-->我方服务端
  • C-->客户端
  • ipa-->苹果端,苹果支付

前置储备

  • iap商品

一个商品只能产生一个订单,再次购买同一个(未完成)商品,会返回上次的订单.也就是说,客户端,针对于一个商品,只能有一个在进行的交易.

  • iap"订单"

receiptData,一串data,iap解析后,是一个数组形式的商品信息.

  • iap完成交易

苹果只负责收钱,收钱之后,会返回给客户端,收过钱的商品的商品信息.服务端解析后(解析成功),然后下发商品.再由客户端,完成这笔交易.

  • C与iap交互流程
graph TD
A[点击购买] -->B[开启iap支付]-->C[支付成功]-->D[ipa返回receiptData]-->E[C将receiptData回传给S]-->F[S下发商品,并告知C]-->G[C告知iap完成交易]
  • receiptData解析

receiptData是一个数组,S解析完成后,里面包含未完成的商品.S会针对于里面的商品给用户下发商品.

下发商品失败的情况.iap告知S,receiptData不合法,或者S请求iap失败.

  • 商品订单

针对于S而言,用户每次点击购买,都是生成一个新的订单(新的orderId)

  • S与iap交互流程,下发商品流程
graph TD
A[接收orderId与receiptData]-->B[请求iap]-->C[iap解析receiptData]-->D[返回给S receiptData解析结果]-->E[S检查orderId与receiptData中的商品]-->F[下发商品]
  • C--S--iap交互逻辑
graph TD
A[S生成orderId交给C]-->C
B[iap生成receiptData交给C]-->C
C[C将orderId,receiptData交给S]-->E[S将receiptData传给iap]-->F[S收到iap解析receiptData]-->G[S下发商品]-->H[C收到下发商品通知]-->I[C完成iap订单]
  • 商品补偿逻辑

当下发商品失败后(S请求iap失败),S会过段时间再次请求

核心代码解析

SwiftyStoreKit(开源库)

预先封装好了StoreKit官方苹果的api,使代码更简洁.

核心功能:查询商品,购买商品,接收订单结果队列,验证订单(本地验证),完成订单

AppleRechargeLocalModel

var orderNo : String = ""//服务端订单
var productId : String = ""//商品id
var receiptData : String = ""//ipa的receiptData
var identf: String = ""//生成的唯一标识

MLDAppleReceiptValidator(inherit ReceiptValidator)

//1.本地开启存根AppleRechargeLocalModel
//2.给服务端发送验证信息
validate(receiptData:completion:)

MLDSwiftyStore(encap SwiftyStoreKit)

/// 初始化SwiftyStoreKit,实际就是开屏接收未完成订单
/// SwiftyStoreKit.completeTransactions(atomically:completion:)
/// 生命周期中只会开屏的时候调用一次
initSwiftStore()

/// 获取内购商品信息
/// - Parameter idArray: id集合
/// - Parameter success: 商品描述,价格
/// - Parameter fail: 无效原因
/// 通过用户选中的商品ID,在iap中查询,这个商品的信息,如果不存在,则商品无效/未上架
/// retrieveProductsInfo(productIds:completion:)
getStoreList(idArray:success:)

/// 内购
/// - Parameter productID: 商品ID
/// - Parameter success: 购买成功,返回详情
/// - Parameter fail: 购买失败,返回失败原因
/// purchaseProduct(productId:quantity:atomically:applicationUsername: simulatesAskToBuyInSandbox:completion:)
purchase(productID:applicationUsername:success:fail:)

/// 本地验证(SwiftyStoreKit 已经写好的类) AppleReceiptValidator
/// - Parameter service: .production 苹果验证  .sandbox 本地验证
/// - Parameter success: 成功
/// - Parameter fail: 失败
/// 内部方法调用MLDAppleReceiptValidator.validate(receiptData:completion:)
verifyReceipt(service:success:fail:)

/// 完成订单
/// SwiftyStoreKit.finishTransaction(transaction)
finish(transaction:)

AppleRechargeDataBase(extension MLDSwiftyStore)

var orderNo : String = ""
var productId : String = ""
var receiptData : String = ""
var identf: String = ""//生成的唯一标识

总流程图

graph TD
A[点击购买] -->B[核对apple stone商品] 
B-->C[创建server订单] -->D[本地保存订单商品以及server订单__商品是用于iap]
D-->E{向苹果发起支付请求}
E-->a[成功]
E-->b[失败]-->P
a-->F[取消支付]-->G[删除本地订单]
a-->H{用户向苹果支付} 
H-->I[支付成功] -->J{苹果返回ReciptData} -->L(本地保存ReciptData) -->K[向服务端发送ReciptData] -->M[轮询订单状态] -->N[下发商品成功] -->G
O[本地检查是否有未下发商品订单] -->P[与服务端核对订单状态]  --> T[商品未下发] -->U[展示恢复Tips]
U-->Q[有ReciptData] --> K
U-->R[没有ReciptData] -->E
P-->S[商品已经下发]-->G

注意

SwiftyStoreKit.completeTransactions(atomically: completion:)

接受订单结果队列,查看此appleID中未完成的订单

接受订单结果队列,只有在app打开的时候会返回一次,且不能主动调用.只有再次支付的时候才会再次返回果.

当用户没有绑定支付宝的时候,苹果会直接返回失败.

app-->未绑定支付宝-->引导绑定支付宝-->绑定成功-->扣钱

                           ↓
             苹果直接返回支付失败失败