前言
StoreKit2对比之前方便了很多,但是API需要iOS15以上才能支持,开发的话xcode13
- 后台能查看订单信息
- 后台能查看退单信息
- 后台能收到退订的消息
- APP能直接拉取苹果未完成订单信息
- APP能申请退款
代码
直接上代码吧,比较简单,参考苹果的官方demo做的
//
// iosStoreKit2.swift
// ios_storekit
//
// Created by iOS on 2023/5/23.
//
import Foundation
import StoreKit
typealias Transaction = StoreKit.Transaction
typealias RenewalInfo = StoreKit.Product.SubscriptionInfo.RenewalInfo
typealias RenewalState = StoreKit.Product.SubscriptionInfo.RenewalState
public enum StoreError: Error { // 错误回调枚举
case failedVerification
case noProduct
}
public enum StoreState: Int64 { // 支付状态
case start // 开始
case pay // 进行苹果支付
case verifiedServer // 服务器校验
case userCancelled // 用户取消
case pending // 等待(家庭用户才有的状态)
case unowned
}
class Store: ObservableObject {
typealias KStateBlock = (_ state :StoreState,_ param:Dictionary<String,Any>?) ->()
var stateBlock: KStateBlock! // 状态回调
var updateListenerTask: Task<Void, Error>? = nil // 支付事件监听
var transactionMap :[String:Transaction] // 用于完成Id的缓存map
var name: String = "iosStore" // 单例的写法
static let shared = {
let instance = Store()
return instance
}()
private init() { // 单例需要保证private的私有性质
transactionMap = [:] // 初始化
Task {
updateListenerTask = listenForTransactions()
}
}
// 退订
func refunRequest(for transactionId: UInt64, scene: UIWindowScene) async{
do {
try await Transaction.beginRefundRequest(for: transactionId, in: scene)
}catch{
print("iap error")
}
}
// 购买某个产品
func requestBuyProduct(productId:String) async throws -> Transaction?{
if(stateBlock != nil ){
stateBlock(StoreState.start,nil)
}
do {
let list:[String] = [productId]
let storeProducts = try await Product.products(for: Set.init(list))
if storeProducts.count > 0 {
return try await purchase(storeProducts[0])
}else {
print("iap: no found product")
throw StoreError.noProduct // 没有该产品
}
} catch {
print("Failed product request from the App Store server: \(error)")
throw StoreError.noProduct // 没有该产品
}
}
// 购买
private func purchase(_ product: Product) async throws -> Transaction? {
if(stateBlock != nil ){
stateBlock(StoreState.pay,nil)
}
let result = try await product.purchase()
switch result {
case .success(let verification): // 用户购买完成
let transaction = try await verifiedAndFinish(verification)
return transaction
case .userCancelled: // 用户取消
if(stateBlock != nil ){
stateBlock(StoreState.userCancelled,nil)
}
return nil
case .pending: // 此次购买被挂起
if(stateBlock != nil ){
stateBlock(StoreState.pending,nil)
}
return nil
default:
if(stateBlock != nil ){
stateBlock(StoreState.unowned,nil)
}
return nil
}
}
// 校验
func checkVerified<T>(_ result: VerificationResult<T>) throws -> T {
//Check whether the JWS passes StoreKit verification.
switch result {
case .unverified:
//StoreKit parses the JWS, but it fails verification.
throw StoreError.failedVerification
case .verified(let safe):
//The result is verified. Return the unwrapped value.
print("iap: verified success")
return safe
}
}
// 校验&完成后传给服务器
func verifiedAndFinish(_ verification:VerificationResult<Transaction>) async throws -> Transaction?{
//Check whether the transaction is verified. If it isn't,
//this function rethrows the verification error.
let transaction = try checkVerified(verification)
// 这里将订单提交给服务器进行验证 ~~~
let transactionId = try verification.payloadValue.id
// 添加进入待完成map
let key = String(transactionId)
transactionMap[key] = transaction
await uploadServer(for: transactionId)
// 这里不触发完成,等服务器验证再触发完成逻辑
// await transaction.finish()
print("iap: finish")
return transaction
}
// 事件完成处理
func transactionFinish(transaction:String) async{
if(transactionMap[transaction] != nil){
await transactionMap[transaction]!.finish()
print("transactionFinish end")
}else {
print("transaction不存在,参数不正确,Id=\(transaction)")
}
}
@MainActor
func uploadServer(for transactionId:UInt64) async {
let dic :Dictionary<String,Any> = ["transactionId":transactionId]
if(stateBlock != nil ){
stateBlock(StoreState.verifiedServer,dic)
}
}
// 支付监听事件
func listenForTransactions() -> Task<Void, Error> {
return Task.detached {
//Iterate through any transactions that don't come from a direct call to `purchase()`.
// 修改update 为 unfinished?
for await result in Transaction.updates { //会导致二次校验?
do {
print("iap: updates")
print("result:\(result)")
let transaction = try await self.verifiedAndFinish(result)
} catch {
//StoreKit has a transaction that fails verification. Don't deliver content to the user.
print("Transaction failed verification")
}
}
}
}
// 销毁调用
deinit {
updateListenerTask?.cancel()
}
}
使用方法差不多就下面那样,根据自己的修改吧
tips:我做的是flutter的插件
import Flutter
import UIKit
import ObjectMapper
public class SwiftIosStorekitPlugin: NSObject, FlutterPlugin {
static var channel:FlutterMethodChannel! // 通信信道
public static func register(with registrar: FlutterPluginRegistrar) {
channel = FlutterMethodChannel(name: "ios_storekit", binaryMessenger: registrar.messenger())
let instance = SwiftIosStorekitPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
if call.method == "getPlatformVersion" {
storeKitPay()
}else if call.method == "storeKitLaunch" {
storeKitLaunch()
}else if call.method == "storeKitPay" {
storeKitPay()
}else if call.method == "storeKitRefun" {
let context = Context()
let model = Mapper<StoreKitTransactionIdModel>(context: context).map(JSONObject: call.arguments)
storeKitRefun(Id: model?.transactionId ?? "")
}else if call.method == "storeKitFinish" {
let context = Context()
let model = Mapper<StoreKitTransactionIdModel>(context: context).map(JSONObject: call.arguments)
storeKitFinish(Id: model?.transactionId ?? "")
}else {
result(nil)
}
}
// 开始进行内购
private func storeKitPay(){
let store = Store.shared
Task {
do {
if try await store.requestBuyProduct(productId:"ZB60M1M") != nil {
print("完成了")
}
} catch StoreError.failedVerification,StoreError.noProduct {
print("error")
}catch {
print("Failed fuel purchase: \(error)")
}
}
}
// 请求退款
private func storeKitRefun(Id:String){
let store = Store.shared
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
// if let keyWindow = windowScene.windows.first(where: \.isKeyWindow) {
Task{
let transId = UInt64(Id)
await store.refunRequest(for: transId ?? 0, scene: windowScene)
}
// }
}
}
// 启动自动监听事件
private func storeKitLaunch(){
print("storeKitLaunch")
let store = Store.shared
store.stateBlock = {(state:StoreState,param:Dictionary<String,Any>?)->Void in
print("state block:\(state)")
if state == StoreState.verifiedServer {
SwiftIosStorekitPlugin.channel.invokeMethod("verifiedServer", arguments: param)
}
}
}
// 完成事件
private func storeKitFinish(Id:String) {
let store = Store.shared
Task{
await store.transactionFinish(transaction: Id)
}
}
func requestServer() async{
let dic :Dictionary<String,Any> = ["transactionId":"123456"]
SwiftIosStorekitPlugin.channel.invokeMethod("verifiedServer", arguments: dic)
}
}