In-App Purchase:应用内购买服务,简称IAP。是苹果为App内的虚拟商品和服务的交易定制的系统。git
所谓应用内购买,是指在操做虚拟商品交易的时候,不容许使用相似微信/支付宝/Apple Pay第三方支付手段,若是经过另类方式避开的话,会被下架。github
请参考前一篇文章 《iOS打包上架最新(含内购)2019年》swift
消耗型:使用一次并耗尽,能够重复购买。相似于宝箱,武器皮肤安全
非消耗型:只需购买一次,不会过时。相似于地图,电子书ruby
自动续期订阅:购买一次后服务或内容会自动续订,直到用户决定取消。相似于Apple musicbash
非续期订阅:购买一次后,不会自动续订,而且能够再次购买,等同于购买一段时间的服务能够叠加。相似于会员,必定数量钻石服务器
经过StoreKit框架使应用程序链接到App Store,以提示并安全地处理付款。微信
先遵循SKProductsRequestDelegate,SKPaymentTransactionObserver协议。app
//添加支付监听
SKPaymentQueue.default().add(self)
//移除监听
SKPaymentQueue.default().remove(self)复制代码
点击购买
框架
//是否容许付款(经过权限限制购买的人群)
if SKPaymentQueue.canMakePayments() {
pid:这里指的是选择套餐对应的id,从苹果后台的套餐配置生成,App服务端获取
let set = NSSet(array: [pid])
//检索本地化商品信息
let request = SKProductsRequest(productIdentifiers: set as! Set<String>)
request.delegate = self
request.start()
}else {
print("禁止购买")
}复制代码
恢复购买,这里的做用是持久化咱们的订单信息,下次从新监听支付队列的时候会从新调用
恢复以前的购买
SKPaymentQueue.default().restoreCompletedTransactions()//非续订订阅型和消耗型没法恢复
购买历史记录的事务,添加回队列出错回调
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("错误恢复: \(error)")
}
恢复购买,成功添加队列回调
func paymentQueueRestoreCompletedTransactionsFinished(_ queue:SKPaymentQueue){
}复制代码
产品要求代理<SKProductsRequestDelegate>
收到产品信息的响应
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
}
请求失败
func request(_ request: SKRequest, didFailWithError error: Error) {
}
请求完成
func requestDidFinish(_ request: SKRequest) {
}复制代码
付款交易观察代理<SKPaymentTransactionObserver>
监听交易状态
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
for tran in transactions {
switch tran.transactionState {
case .purchased:
//交易完成,调起验证购买
//1.从沙盒获取持久化订单信息
//2.将收据上传到App服务器,成功后结束交易
//3.删除保存的订单信息
case .purchasing:
//交易中
case .restored:
//已经购买过
case .failed:
//交易失败
break
default:
break
}
}
}
交易结束
private func payFinish(transaction:SKPaymentTransaction) {
}复制代码
import UIKit
import StoreKit
// MARK: - swift title: IAP应用内购买
// MARK: - 点击购买
class UserIAPBuy: NSObject,SKProductsRequestDelegate,SKPaymentTransactionObserver {
func startBuyInnerProduct(_ type: String) -> () {
SKPaymentQueue.default().add(self as SKPaymentTransactionObserver)
localRequest(type)
}
func localRequest(_ type: String) -> () {
if SKPaymentQueue.canMakePayments() {
let set = NSSet(array: [type])
//检索本地化商品信息
let request = SKProductsRequest(productIdentifiers: set as! Set<String>)
request.delegate = self
request.start()
}else {
print("禁止购买")
}
}
}
// MARK: - 恢复购买
extension UserIAPBuy {
func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) {
print("错误恢复 code: (error)")
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
//恢复购买获取成功回调
var array = String
print("received restored transactions: (queue.transactions.count)")
for pay:SKPaymentTransaction in queue.transactions {
let productID = pay.payment.productIdentifier
array.append(productID)
print("message: (array)")
}
}
//恢复以前购买
@objc func replyToBuy() {
SKPaymentQueue.default().restoreCompletedTransactions()
}
}
// MARK: - 代理:商品 SKProductsRequestDelegate
extension UserIAPBuy {
func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
// 收到App Store商品信息的响应
let pro = response.products
if pro.count > 0 {
var p: SKProduct?
for pid in pro {
print("描述信息: (pid.description)")
print("产品标题: (pid.localizedTitle), 产品描述信息: (pid.localizedDescription)")
print("价格: (pid.price)")
p = pid
}
guard let pro = p else {
return
}
SKPaymentQueue.default().add(SKPayment(product: p))
}else {
// 处理没法得到商品信息的操做
print("没法取得商品信息")
}
}
func requestDidFinish(_ request: SKRequest) {
// 处理请求完成
}
func request(_ request: SKRequest, didFailWithError error: Error) {
// 处理请求失败
}
}
// MARK: - 代理:付款交易观察 SKPaymentTransactionObserver
extension UserIAPBuy {
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
//监听交易状态
for tran in transactions {
switch tran.transactionState {
case .purchased:
//交易完成,调起验证购买
//1.从沙盒获取持久化订单信息
//2.将收据上传到App服务器,成功后结束交易
//3.删除保存的订单信息
paymentTransactionPurchased()
SKPaymentQueue.default().finishTransaction(tran)
case .purchasing:
//交易中
paymentTransactionPurchasing()
case .restored:
//已经购买过
paymentTransactionRestored()
SKPaymentQueue.default().finishTransaction(tran)
case .failed:
//交易失败
paymentTransactionFailed(tran)
default:
break
}
}
}
func paymentTransactionPurchased() {
// 验证购买,避免越狱软件模拟苹果请求达到非法购买问题
guard let receiptUrl = Bundle.main.appStoreReceiptURL,let receiptData = try? Data(contentsOf: receiptUrl) else {
// 从沙盒中获取交易凭证而且b拼接成请求提数据0
return
}
let receiptStr = receiptData.base64EncodedString(options: Data.Base64EncodingOptions.endLineWithLineFeed)
//处理订单信息
print("订单信息: (receiptStr)")
//删除保存的订单
}
func paymentTransactionPurchasing() {
print("交易中")
}
func paymentTransactionRestored() {
print("已经购买过")
}
func paymentTransactionFailed(_ transaction: SKPaymentTransaction) {
guard let err = transaction.error else {
SKPaymentQueue.default().finishTransaction(transaction)
return
}
if (err as NSError).code != SKError.paymentCancelled.rawValue {
print("交易失败")
}else{
print("交易取消")
}
}
}复制代码
沙箱帐号是测试用途的Apple ID,因为内购涉及实际金钱交易,在测试阶段咱们能够经过注册苹果的沙箱帐号来完成内购付款。
电子邮箱能够自定义。
App Store地区的选择和结算价格有关。
技术总结:
1.在App Store后台配置内购买项目,须要先完成协议,税务银行项目。这部分可参考上一篇
2.配置具有In-App Purchase功能的App ID,并开启IAP权限。
3.经过StoreKit框架,在对应的地方实现并调整你的内购代码。
须要注意的地方:
1.有关于服务端验证收据,服务端也是有沙盒和正式两个环境,具体实现也有不少成熟的开源框架可用,这里也须要把应用套餐的信息给服务端配置。
2.苹果自带的内购买体验确实不是很好,因为自己服务器在美区,链接会比较慢,添加沙盒App ID的时候,建议不要在App内绑定,能够选择从设置-iTunesStore来绑定AppleID测试帐号,绑定成功,底部会新增一个沙盒帐号。
3.应用套餐也是须要审核的,把改填的地方填写清楚,问题不大,另外应用内套餐审核是伴随主App一块儿审核的,若是主App没有过,那么原先套餐审核经过的状态就会改变,在下一次从新提交主App的时候须要重置套餐的状态,不然,没法查找对应套餐信息。
验证购买(二次验证):
1.App将订单收据传给服务器
2.服务器收到收据后发送给App Store进行验证,并返回验证结果
这一步尽可能放在服务端操做,将全部的验证交易数据放在客户端不只不安全,并且不稳定。
服务端ruby集成的内购框架(Monza)
OC代码Demo下载:github.com/marst123/In…
纯属我的分享,但愿对iOS社区能有更多贡献。