借助第三方库:​​SwiftyStoreKit​​ AppStore内购流程:

1. 获取商品列表(可以从自己服务器获取,但是商品需要绑定AppStore创建商品的id)
2. 用户选择并购买商品的时候,发起Storekit支付并等待返回结果
3. 如果支付成功,则获取Receipt
4. 获取Receipt之后,则进行AppStore支付结果验证。验证成功之后才可以发放商品。

关于验证分两种:一种是本地验证,一种是服务端验证。
如果本地验证在在获取到了Receipt之后可以直接verifyPurchase,根据结果进行产品发放。
如果服务端验证在需要将Receipt发送到服务端,并由服务端发放商品,并且结果告知客户端。

以上是正常流程。但是很有可能在支付过程中发生崩溃,所以需要做容错处理。如果用户购买了商品并且没有对AppStore结果进行验证的话,那么当App再次启动的时候我们会收到transaction事件,此时我们可以再次处理后面的验证工作。

下面是部分代码:
在App启动的时候监听transaction事件,并进行验证处理。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
for purchase in purchases {
switch purchase.transaction.transactionState {
case .purchased:
//这里只保存了用户最后一笔商品的支付情况
let payData = CfgMgr.share.getPayData()
if payData != nil{
//info[""] = userId
//info["productId"] = productId
//info["applePid"] = applePid
//info["money"] = money

//校验用户的最后一笔订单
let userId = payData!["userId"] as! String
let applePid = payData!["applePid"] as! String
if userId == CfgMgr.share.getUserId() && purchase.productId == applePid{
let productId = payData!["productId"] as! Int
let money = payData!["money"] as! String
//注意在这里获取Receipt的时候不能刷新Recepit否则会失败
self.doFetchReceipt(userId: userId, productId: productId, applePid: applePid, money: money)
//这里获取Receipt并且上传检验产品结果
break
}
}

if purchase.needsFinishTransaction {
// Deliver content from server, then:没有确认的订单
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
// Unlock content
case .restored:
if purchase.needsFinishTransaction {
// Deliver content from server, then:没有确认的订单
SwiftyStoreKit.finishTransaction(purchase.transaction)
}
case .failed, .purchasing, .deferred:
break // do nothing
}
}
}
return true
}

func doFetchReceipt(userId: String, productId: Int, applePid: String, money: String){
//不能刷新,刷新会导致失败
SwiftyStoreKit.fetchReceipt(forceRefresh: false) { [unowned self]result in
switch result {
case .success(let receiptData):
let encryptedReceipt = receiptData.base64EncodedString(options: [])
NetMgr.share.postIosOrder(userId: userId, productId: productId, applePid: applePid, money: money, payload: encryptedReceipt, completion: onRespPostIosOrder)
print("Fetch receipt success:\n\(encryptedReceipt)")
case .error(let error):
print("Fetch receipt failed: \(error)")
}
}
}

购买:

func onCharge(){
_productItem = ProductMgr.share.findItem(productId: ProductMgr.share.selProductId)

if _productItem == nil{
showTipView(title: nil, msg: "产品id不存在,请刷新后重试", okText: "确定", okClick: {}, cancelText: nil, cancelClick: {})
return
}

showLoadingView(title: "AppStore支付中...")
//"fmspeed_ceshi_2"
SwiftyStoreKit.purchaseProduct(_productItem!.applePid, quantity: 1, atomically: false) { [unowned self] result in
//self.hideLoadingView()
switch result {
case .success(let purchase):
var price = purchase.product.localizedPrice
price = price?.replacingOccurrences(of: "¥", with: "")
price = price?.replacingOccurrences(of: " ", with: "")
let downloads = purchase.transaction.downloads
if !downloads.isEmpty {
SwiftyStoreKit.start(downloads)
}

if purchase.needsFinishTransaction {
//注意这里千万不要finishTransaction,否则下面验证AppStore购买结果将会查询不到该笔商品。
//SwiftyStoreKit.finishTransaction(purchase.transaction)
print("needsFinishTransaction")
}
CfgMgr.share.setPayData(userId: _userId, productId: _productItem!.id, applePid: _productItem!.applePid, money: price == nil ? "0.00":price!)
doFetchReceipt(applePid:purchase.productId,money:price == nil ? "0.00":price!)

case .error(let error):
var errMsg = ""
switch error.code {
case .unknown: errMsg = "Unknown error. Please contact support"
case .clientInvalid: errMsg = "Not allowed to make the payment"
case .paymentCancelled: errMsg = "用户取消支付"
case .paymentInvalid: errMsg = "该产品不存在,请联系客服确认"
case .paymentNotAllowed: errMsg = "The device is not allowed to make the payment"
case .storeProductNotAvailable: errMsg = "The product is not available in the current storefront"
case .cloudServicePermissionDenied: errMsg = "Access to cloud service information is not allowed"
case .cloudServiceNetworkConnectionFailed: errMsg = "Could not connect to the network"
case .cloudServiceRevoked: errMsg = "User has revoked permission to use this cloud service"
default: errMsg = (error as NSError).localizedDescription
}
print(errMsg)
hideLoadingView()
showTipView(title: "支付失败", msg: errMsg, okText: "确定", okClick: {}, cancelText: nil, cancelClick: {})
}
}

}

func doFetchReceipt(applePid:String,money:String){
showLoadingView(title: "正在获取支付结果...")
SwiftyStoreKit.fetchReceipt(forceRefresh: true) { [unowned self]result in
switch result {
case .success(let receiptData):
let encryptedReceipt = receiptData.base64EncodedString(options: [])
/*let appleValidator = AppleReceiptValidator(service: .sandbox, sharedSecret: "a92f7a4a41e24082a1094da6f330efb9")
SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
switch result {
case .success(let receipt):
print(receipt)
// Verify the purchase of Consumable or NonConsumable
let purchaseResult = SwiftyStoreKit.verifyPurchase(
productId: applePid,
inReceipt: receipt)

switch purchaseResult {
case .purchased(let receiptItem):
print("\(applePid) is purchased: \(receiptItem)")
case .notPurchased:
print("The user has never purchased \(applePid)")
}
case .error(let error):
print("Receipt verification failed: \(error)")
}
}*/
showLoadingView(title: "验证支付结果...")
//这里是发送到服务端验证的,如果不想服务端验证,则可以直接在本地SwiftyStoreKit.verifyPurchase验证
NetMgr.share.postIosOrder(userId: _userId, productId: _productItem!.id, applePid: applePid, money: money, payload: encryptedReceipt, completion: onRespPostIosOrder)
print("Fetch receipt success:\n\(encryptedReceipt)")
case .error(let error):
print("Fetch receipt failed: \(error)")
hideLoadingView()
showTipView(title: nil, msg: "获取支付结果异常,请重试或者联系人工客服?", okText: "重试", okClick: {
doFetchReceipt(applePid: applePid,money: money)
}, cancelText:"取消", cancelClick: {})
}
}
}

Note:验证结果之前千万不要finishTransaction,否则后面验证将会查询不到该笔商品。