第三方支付一键集成调用
说到支付,现在阿里爸爸打造的无现金城市计划真是TMD的爽啊,哈哈,当然作为开发者,面对越来越多的APP需要集成支付功能,我们每次都要在各个开发者中心来回奔命,而且一不小心就会踩雷,对于已经集成了N的N次方的我来说,还是总会不可避免的在各个平台之间云里雾里,所以就有了今天的主题:一次性集成多个主流支付平台,而且还提供了平台内部支付的扩展哦,用起来贼揦子爽!
PS.关于第三方支付的相关账户申请和应用创建请自己去开发者平台设置哦,这里就直接进入正题
我将支付工具分为四个模块:网络请求模块,支付处理模块,数据解析模块和异常处理模块.
1,整体结构
ECPay.png Snip20170803_2.png其中,payManager就是统一的支付入口,Frameworks文件夹主要用来放第三方引入的一些文件和依赖,包括第三方的相关配置也在这里;
Http文件夹只包含一个类,主要是用来请求我们的服务器,因为不管是自己的钱包支付还是调用第三方支付都需要先请求服务器生成订单,再去请求第三方的支付接口(自有钱包支付可以省略第二步,具体情况按项目需求来定,最好和第三方保持一致,前后台处理逻辑就是同一套);
重点在PayChannels文件夹:
首先是支付协议,使用不同的支付方式但是都是遵循同一套支付协议Even_IPay:
然后我们给每一种支付方式去遵循协议,同时.m文件实现协议方法:
Snip20170803_4.pngException是支付调用的异常处理,在整个sdk中非常重要,这也是我们测试回调的主要依据;
Parsing是数据解析模块,在我们请求支付和支付回调都会用到,因为每个支付方式的数据模型都不一样,所以Model是专门接收数据转模型的.
好啦,主要框架就是这样接下来看看支付流程
2,支付流程(包括网络请求,数据解析和错误处理)
- (void)pay {
NSInteger payChannel = PayChannel_walletPay;
NSString* fee = [NSString stringWithFormat:@"%.2f",[self.feeTextField.text doubleValue]];
if ([self.payTypeButton.titleLabel.text isEqualToString:@"支付宝支付"]) {
payChannel = PayChannel_aliPay;
} else if ([self.payTypeButton.titleLabel.text isEqualToString:@"本地钱包"]) {
payChannel = PayChannel_walletPay;
} else if ([self.payTypeButton.titleLabel.text isEqualToString:@"微信支付"]) {
payChannel = PayChannel_weixinPay;
}
WEAKSELF
[[Even_PayManager sharePayManager]payWithChannel:payChannel andAmount:[fee doubleValue]*100 andPayType:PayType_Wallet_TipFee andPayId:self.userID andDescription:@"小费" andController:self CompleteBlock:^(NSString *result, Even_PayError *error) {
if (error) {
[MBProgressHUD showError:result];
} else {
//支付成功
weakSelf.payFeeBlock(fee);
[weakSelf back];
}
}];
}
调用就这么简单,只需要支付方式和费用,剩下的两个参数是根据项目情况自己设置的.
那么我们看看方法执行顺序:
-(void)payWithChannel:(PayChannel)payChannel andAmount:(long)amount andPayType:(PayType)payType andPayId:(NSString*)rid andDescription:(NSString*)description andController:(UIViewController*)controller CompleteBlock:(Even_PayComplation)completeBlock{
//请求支付
NSDictionary* paramsDic = @{@"paid":[NSNumber numberWithInteger:payChannel],@"rid":rid?rid:@"",@"tag":description?description:@"",@"cent":[NSNumber numberWithLong:amount],@"uid":[UserViewModel currenUser].uid,@"type":[NSNumber numberWithInteger:payType]};
//这里是自己的钱包支付直接省略了第一步
if (payChannel == PayChannel_walletPay) {
[[Even_PayEngine sharedEngine] payWithCharge:[paramsDic mj_JSONString] controller:controller scheme:@"Even_PaySDK" withComplation:completeBlock];
return;
}
//请求第三方支付
[HttpUtils postWithParams:paramsDic callback:^(NSString *result) {
//在这里拿到服务器返回的订单数据并解析
NSData *jsonData = [result dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* resultDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
NSString* msg = resultDic[@"msg"];
if ([msg isEqualToString:@"success"]) {
//调用第三方支付
[[Even_PayEngine sharedEngine] payWithCharge:result controller:controller scheme:@"Even_PaySDK" withComplation:completeBlock];
} else {
//返回错误类型
completeBlock(nil,[Even_PayErrorUtils create:Even_PayErrorServerVerifyError]);
}
}];
}
调起第三方支付后就进入了我们的支付模块:
//处理支付
-(void)payWithCharge:(NSString*)charge controller:(UIViewController*)controller scheme:(NSString*)scheme withComplation:(Even_PayComplation)complation{
//验证Controller是否为空
if (controller == nil) {
complation(nil,[Even_PayErrorUtils create:Even_PayErrorViewControllerIsNil]);
return;
}
//数据解析
Even_ParsingContext* context = [[Even_ParsingContext alloc] initWithParsing:[[Even_JsonParsing alloc] init]];
Even_Charge* chargeObj = [context parsing:charge];
_channel = chargeObj.channel;
//验证付款对象(支付对象)
if (![Even_PayErrorUtils invalidCharge:chargeObj withComplation:complation]) {
return;
}
//唤起支付
id<Even_IPay> pay = [_channelDic objectForKey:chargeObj.channel];
if (pay == nil) {
complation(nil,[Even_PayErrorUtils create:Even_PayErrorInvalidChannel]);
return;
}
//这里会进入之前实现支付协议的类中,并进行回调处理
[pay payWithCharge:chargeObj controller:controller scheme:scheme withComplation:complation];
}
整个支付和处理的流程清晰简明,下面我们看看错误处理的逻辑:
这里我们需要有一个错误管理的类,并且只在sdk内部使用:
Snip20170803_9.png
然后有一个异常类:
//错误类型
typedef NS_ENUM(NSUInteger,Even_PayErrorOption){
//支付服务器验证错误
Even_PayErrorInvalidChannel,
//取消支付
Even_PayErrorCancelled,
//Controller不能为空
Even_PayErrorViewControllerIsNil,
//网络连接异常
Even_PayErrorConnectionError,
//请求超时
Even_PayErrorRequestTimeOut,
//没有安装微信APP
Even_PayErrorWxNoInstalled,
//调起支付失败
Even_PayErrorActivation,
//验证支付凭证失败
Even_PayErrorInvalidCredential,
//订单缺失
Even_PayErrorNullOrder,
//服务器身份验证失败--token缺失 code--500
Even_PayErrorServerVerifyError,
//未知异常
Even_PayErrorUnknownError,
//钱包余额不足
Even_PayErrorDidNotHaveEnoughMoney,
//支付密码错误
Even_PayErrorSendErrorPwd,
//重复支付
Even_PayErrorRePay,
//支付密码连续错误三次,账号锁定
Even_PayErrorSendErrorPwdForThreeTimes
};
并且对于每个异常有相应的描述和获取方式:
- (instancetype)init{
self = [super init];
if (self) {
//初始化异常信息
//注册异常信息(配置文件)
_errorDic = [[NSMutableDictionary alloc] init];
[_errorDic setValue:@"取消支付" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorCancelled]];
[_errorDic setValue:@"没有这个支付渠道" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorInvalidChannel]];
[_errorDic setValue:@"ViewController不能为空" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorViewControllerIsNil]];
[_errorDic setValue:@"链接异常" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorConnectionError]];
[_errorDic setValue:@"未知异常" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorUnknownError]];
[_errorDic setValue:@"请求超时" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorRequestTimeOut]];
//新增异常
[_errorDic setValue:@"请求安装微信APP" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorWxNoInstalled]];
[_errorDic setValue:@"调起支付控件失败" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorActivation]];
[_errorDic setValue:@"验证支付凭证失败" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorInvalidCredential]];
[_errorDic setValue:@"服务器身份验证失败" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorServerVerifyError]];
[_errorDic setValue:@"订单缺失" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorNullOrder]];
[_errorDic setValue:@"余额不足" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorDidNotHaveEnoughMoney]];
[_errorDic setValue:@"密码输入错误" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorSendErrorPwd]];
[_errorDic setValue:@"订单已支付" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorRePay]];
[_errorDic setValue:@"密码输入错误三次,账号被冻结24小时" forKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)Even_PayErrorSendErrorPwdForThreeTimes]];
}
return self;
}
-(NSString*)getMsg{
return [_errorDic objectForKey:[[NSString alloc] initWithFormat:@"%lu",(unsigned long)_errorOption]];
}
因为sdk整体调用依赖于block回调,所以从调用开始到回调结束会一直接受来自异常处理类的回调,在回调中我们就能清楚知道每次问题的所在.
最后这个sdk已经打包上传到我的github:https://github.com/even-cheng/Even_PaySDK,如有需求可以直接下载使用,欢迎点评指正.