苹果内购代码组件的重构。
在iOS众多系统框架中,StoreKit算是被诟病得够多的了。StoreKit的设计,要求App来确保交易的最后交付——从苹果的生态来讲,这是可以理解的;但是交易流程中的错误,却很少返回有意义的错误码,“无法连接到iTunes Connect”成了无法明确的错误描述。
本文主要是组件代码重构的一个说明。
前置知识
内购的交互/交易流程如下图所示:
IAP相关的其他背景知识,不做具体介绍,
- App内购买项目配置流程,请参考官网;
- 订单凭据校验,请参考官网;
- App内购买测试,请参考官网;
- 苹果的官方示例;
- 有疑问请参考FAQ;
需要说明的几点是:
Transaction
必须关闭,无论成功或失败。Transaction
关闭后,App必须负责确保完成业务端的交易流程;
- 需要考虑订单持久化问题;
- 业务端的订单生成与校验逻辑;
任务
方案的设定,我们把每一次发起的交易作为一个任务,加入到任务队列,生命周期完成后移出队列。这样设计,一是可以保证各个任务(的处理逻辑)是独立的、生命周期是完整的,二是任务队列可以协调任务的并发情况。
任务的模型设计如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @interface HLTPaymentTask : NSOperation<HLTOrderConfiguration> { @protected HLTOrderModel *_order; HLTPaymentCompletion _completion; NSTimeInterval _startTime; }
@property (nonatomic,assign) HLTTaskStatus taskStatus;;
@property (nonatomic,weak) id<HLTPaymentTaskDelegate> delegate;
@property (nonatomic,copy,readonly) NSString *productId;
@property (nonatomic,strong,readonly,nullable) HLTOrderModel *order;
@property (nonatomic,assign,readonly) NSTimeInterval startTime;
@property (nonatomic,strong,readonly,nullable) SKProduct *skProduct;
@property (nonatomic,weak) id<HLTOrderGenerator> orderGenerator;
@property (nonatomic,weak) id<HLTOrderVerifier> orderVerifier;
@property (nonatomic,strong) NSDictionary *userInfo; @property (nonatomic,strong) NSObject *userInfoObject;
- (instancetype)initWithProductId:(NSString *)productId completion:(HLTPaymentCompletion)completion;
@end
|
可以看到,Task
维护了自身的taskStatus
,它本身可以做状态流转的校验。
Task
持有的订单模型HLTOrderModel
,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| @interface HLTOrderModel : NSObject <NSSecureCoding, HLTOrderConfiguration>
@property (nonatomic,copy) NSString *productId;
@property (nonatomic,copy) NSString *orderId;
@property (nonatomic,copy) NSString *userIdentifier;
@property (nonatomic,assign) HLTOrderSource orderSource;
@property (nonatomic,assign) HLTOrderStatus orderStatus;
@property (nonatomic,assign) NSTimeInterval createdTime;
@property (nonatomic,assign) NSTimeInterval iapBeginTime;
@property (nonatomic,assign) NSTimeInterval iapFinishTime;
@property (nonatomic,assign) NSInteger receiptVerifyCount;
@property (nonatomic,copy) NSString *hint;
@property (nonatomic,strong,readonly) HLTOrderTransaction *transaction;
@property (nonatomic,strong,readonly) SKPaymentTransaction *skTransaction;
@property (nonatomic,assign,readonly) NSTimeInterval updateTime;
@property (nonatomic,strong) NSDictionary *userInfo; @property (nonatomic,strong) NSObject *userInfoObject;
- (instancetype)initWithProductId:(NSString *)productId; - (BOOL)isEqualToOrder:(HLTOrderModel *)other; - (void)updateWithTransaction:(HLTOrderTransaction *)transaction; - (BOOL)isOrderIdValid;
@end
|
Order
作为贯穿整个交易流程的状态集合,
① 是需要持久化到本地的;
② 可以携带业务定义数据,这个是必然的;
这两点都实现了。
业务逻辑注入
IAP内部的交易流程,业务方其实是不太关注的。业务方关注的是,它用这个组件的时候,如何根据业务定制“订单生成/验证”的逻辑,如何获取交易流程的状态回调。
① 状态回调,其实在Task
的定义中可以看到,是提供了事件代理的。
② 业务定制,Task
提供了orderGenerator
与orderVerifier
作为业务逻辑注入的入口,实现接口就可以了;
外部入口
业务层看到的入口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| @interface HLTStoreKit : NSObject
@property (nonatomic,strong) id<HLTOrderGenerator> orderGenerator;
@property (nonatomic,strong) id<HLTOrderVerifier> orderVerifier;
@property (nonatomic,strong) id<HLTOrderPersistence> orderPersistence;
+ (instancetype)defaultStore; - (NSTimeInterval)launchTime;
#pragma mark - Configuration
+ (void)setLogger:(void (^)(NSDictionary *params, NSString *format, ...))logger;
#pragma mark - Transaction
- (void)startObservingTransaction; - (void)stopObservingTransaction;
- (void)purchase:(NSString *)productId configuration:(HLTOrderConfigurationBlock __nullable)configuration completion:(HLTPaymentCompletion __nullable)completion;
- (void)tryRetrievalOrder:(HLTOrderModel *)order;
@end
|
继续完善。
Comments