3.2 代码实现
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch. // 注册APP,这里的字符串就是Wechat URL Scheme里面对应的ID 也是申请回来的ID,必须一致 [WXApi registerApp:@"这里填写申请回来的ID"]; return YES;}
- 2) 请求服务器的参数,拉起微信支付App(超级关键,注意听)
#pragma mark - 微信支付- (void)wechatPay { // 把生成的订单信息组装起来传给服务器,如何组装就和服务器约定好 [[TWTShoppingCartLogic sharedData] goToWechatEasyPay:self.orderStr way:@"2" complete:^(NSError *error, id data) { NSMutableString *stamp = [data objectForKey:@"timestamp"]; // 调起微信支付 PayReq *req = [[PayReq alloc] init]; req.partnerId = [data objectForKey:@"partnerid"]; req.prepayId = [data objectForKey:@"prepayid"]; req.nonceStr = [data objectForKey:@"noncestr"]; req.timeStamp = stamp.intValue; req.package = [data objectForKey:@"package"]; req.sign = [data objectForKey:@"sign"]; [WXApi sendReq:req]; }];}
- 这里请求的方法和步骤就不写了,无非就是post信息给服务器,咱们看看需要的数据格式(假数据)
{ "appid" : "wxb4b",微信开放平台审核通过的AppID "noncestr" : "171127dd056d05e423c8b9e",随机字符串 "package" : "Sign=WXPay", 固定值 "partnerid" : "130", 微信支付分配的商户ID "prepayid" : "wx201609291601", 预支付交易会话ID "sign" : "684371081C049B6017641", 签名,除了sign,剩下6个组合的再次签名字符串 "timestamp" : 147513 当前时间}
- 第一种:老司机后台类型
- 其实当你把订单传给后台的时候,后台事先会把订单通过微信的生成预支付订单生成prepayID,点击打开链接,那么对于老司机来说,怎么可能把这种返回的数据返回给你?
- 他们会把接受的prepayID根据上面的结构组装起来,那么预支付订单生成的时候也会返回sign字段,老司机不会直接用,后台会把这个字段,也就是剩下6个字段再次md5签名生成签名算法新的sign字段组装完毕返回给你,这种情况下直接在App上配置模型,拉起微信支付,非常舒畅,一气呵成!!!
- 第二种:无法理解类型后台(让你自己签名)
- 当你把订单传给他的时候,同样他会生成个预订单prepayID,那么这种司机开车特别猛,直接把返回的参数根据格式组装后弹回给你,sign字段也是预订单生成后的,没有经过二次md5签名,他也没有告诉你,那么你也特别猛,没问他,直接用他的字段,组装完毕,拉起微信,我擦,你会直接懵逼了,那么你将会只会看到这个。自己写个本地的md5玩玩(假的千万别用,网上找来的分享下)
// 创建package签名- (NSString *) createMd5Sign:(NSMutableDictionary *)dict { NSMutableString *contentString =[NSMutableString string]; NSArray *keys = [dict allKeys]; // 按字母顺序排序 NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1 compare:obj2 options:NSNumericSearch]; }]; // 拼接字符串 for (NSString *categoryId in sortedArray) { if ( ![[dict objectForKey:categoryId] isEqualToString:@""] && ![categoryId isEqualToString:@"sign"] && ![categoryId isEqualToString:@"key"] ) { [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]]; } } // 添加key字段 [contentString appendFormat:@"key=%@", self.spKey]; // 得到MD5 sign签名 NSString *md5Sign =[contentString MD5]; return md5Sign;}- (NSMutableDictionary *)payWithprePayid:(NSString *)prePayid { if(prePayid == nil) { NSLog(@"prePayid 为空"); return nil; } // 获取到prepayid后进行第二次签名 NSString *package, *time_stamp, *nonce_str; // 设置支付参数 time_t now; time(&now); time_stamp = [NSString stringWithFormat:@"%ld", now]; nonce_str = [time_stamp MD5]; // 重新按提交格式组包,微信客户端暂只支持package = Sign = WXPay格式,须考虑升级后支持携带package具体参数的情况 // package = [NSString stringWithFormat:@"Sign = %@",package]; package = @"Sign = WXPay"; // 第二次签名参数列表 NSMutableDictionary *signParams = [NSMutableDictionary dictionary]; NSLog(@"%@", signParams); [signParams setObject: self.appId forKey:@"appid"]; [signParams setObject: self.mchId forKey:@"partnerid"]; [signParams setObject: nonce_str forKey:@"noncestr"]; [signParams setObject: package forKey:@"package"]; [signParams setObject: time_stamp forKey:@"timestamp"]; [signParams setObject: prePayid forKey:@"prepayid"]; // 生成签名 NSString *sign = [self createMd5Sign:signParams]; // 添加签名 [signParams setObject: sign forKey:@"sign"]; //返回参数列表 return signParams;}
- 如果真的要在App端二次签名的话,那加密的时候还要加入申请的密钥,但是真的不好
- 这样做
- 其一:服务器已经做过一次签名了,第二次做了返回给你就好了,没必要再给App。
- 其二:不安全,全放在App上,这种东西一定要放到服务器
- 小技巧:其实出现上面那种情况有几种可能
- 1.sign没有二次签名
- 2.noncerStr是服务器返回的,不要自己生成
- 3.package是写死的,不要写错了
- 4.timeStamp是10位数
- 5.自己签名的sign一定要全部大写
- 6.为了避免上面的情况,交给服务器管理,我们负责组装拉起微信支付就好了
- 3) 处理回调信息
Appdelegate- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { // 跳转到URL scheme中配置的地址 // NSLog(@"跳转到URL scheme中配置的地址-->%@",url); return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];}// 支付成功时调用,回到第三方应用中- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { // 微信调用结束 if ([url.scheme isEqualToString:WECHAT_APPKEY]) { return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]]; }}
- 这里的处理是根据微信官网提供的方法,代理到专门处理的单利当中去统一处理WXApiManager
- 注意点:有些人用NSNotificationCenter来通知到发出请求的界面去,然后在发起的界面处理回调的逻辑,但是这里, 你要考虑一种非人类的交互,TMD有人在拉起微信支付的时候把自己的App给推出了或者App自己挂了,那么当回调生效的时候,原先拉起微信支付App的界面已经消失了,你发的通知他收不到了,这种情况我是存到本地的
[[NSUserDefaultsstandardUserDefaults] setValue:self.orderStrforKey:@"WECHAT_PAY_ORDER_TRADEID"];[[NSUserDefaultsstandardUserDefaults] synchronize];
- 处理回调的时候直接从本地读取
- 最终处理逻辑的地方(这里不能直接用他的返回接过,要二次确认)
// 微信回调,有支付结果的时候会回调这个方法- (void)onResp:(BaseResp *)resp { if([resp isKindOfClass:[PayResp class]]) { // 支付返回结果,实际支付结果需要去微信服务器端查询 NSString *strMsg,*strTitle = [NSString stringWithFormat:@"支付结果"]; switch (resp.errCode) { case WXSuccess: strMsg = @"支付结果:成功!"; NSLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode); // 这里别用返回的状态来确定是否正真支付成功了,这样是不对的,我们必须拿着存到本地的traderID去服务器再次check,这样和服务器收到的异步回调结果匹配之后才能确认是否真的已经支付成功了 [[TWTShoppingCartLogic sharedData] gotoCheckWeChatOrder:tradeID compelete:^(NSError *error, id data) { // 二次确认 }]; break; default: strMsg = [NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode,resp.errStr]; NSLog(@"错误,retcode = %d, retstr = %@", resp.errCode,resp.errStr); break; } }}