近期笔者开发的项目中,需要用到支付宝支付和微信支付。大概一个月前,支付宝就已经集成完毕并可以正常使用。但在集成坑爹的微信支付SDK时,遇到了诸多问题,搞了将近三个星期。期间不断的跟后台同事核对代码(签名、下单),支付流程,其中的血泪艰辛,不言而喻。现笔者把集成过程中遇到的一些问题记录下来,供自己和大家参考。如果有什么不对的地方,也请大家多多指正:
吐槽完了,下面出正文。
补充说明:第一准备阶段不需要开发者负责操作,如果你是iOS开发人员,只想找到调用微信支付的代码,可直接跳过 第一准备阶段。
一、准备阶段
(1)笔者公司用户是微信开放平台下开通的微信支付功能,网址:https://open.weixin.qq.com/?qq-pf-to=pcqq.c2c。网上大部分资料讲的都是微信公众平台下的微信支付集成,其实原理和代码实现上大同小异。
(2)到微信开放平台的管理中心创建移动应用,待移动应用审核通过以后,申请获得微信支付能,这个流程走下来,大概需要 2 - 3 周。笔者建议提前到开放平台审核为好。网址:https://open.weixin.qq.com/cgi-bin/applist?t=manage/list&lang=zh_CN&token=478d1d01575659fb7b53ee40d1f37c5cfc8689e5
移动应用审核通过后,获取APP_ID和AppSecret,附上截图:
(3)等到移动应用审核通过,获得微信支付能力之后,进入微信商户商户平台。商户平台的账号和密码,在公司账号开通微信支付功能以后,财付通就会下发。微信商户平台网址:https://pay.weixin.qq.com/index.php/home/login?return_url=/
进入微信商户平台后,获取商户号和商户API密钥。附上截图:
二、获取微信支付IOS头文件和库下载及APP支付示例和解读
(1)进入网址https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=11_1,下载微信支付APP支付示例。在APP支付示例中,已经包含了头文件和库,所以不需要在下载头文件和库。
(2)解压文件,会得到文件名为:wechat_sdk_sample_ios_v3_pay的文件夹。从名字可以知道,这是一份v3版本的代码,目前v2版本的微信支付几乎已经不用,新添加的微信支付SDK几乎都是v3版本的。集成的时候,要明白这点,很重要。
(3)文件目录及作用
readme.txt:首次集成微信支付SDK时,读读没坏处。
设置appid.jpg:直接运行demo是无法调用微信客户端的,需要按钮图片的内容,设置 URL Schemes 和 APP_ID 才能运行。
wechat_sdk_sample_ios_payV3:
~ lib:配置文件,配置了微信支付所要设置的相关内容
~ SDKExport:微信支付头文件和库文件
~ SDKSample:项目功能实现代码
(4)代码准备工作
Xcode打开工程代码,点击AppDelegate.m文件,可以查看到微信支付的实现代码。
~[WXApi registerApp:APP_ID withDescription:@"demo 2.0"];向微信注册,必写代码。
~ -(void) onReq:(BaseReq*)req:发送信息给微信客户端,请查看微信支付demo具体代码实现
~ -(void) onResp:(BaseResp*)resp:收到微信客户端信息的代理方法,请查看微信支付demo具体代码实现
(5)调起微信支付
重点来了!!!通过调试,我们可以知道,"微信支付测试签名"、“微信支付demo”两个按钮,分别对应的是
- (void)sendPay 和 - (void)sendPay_demo 两个方法,那么这两个方法都做了什么呢?什么是签名呢?
在解释上面的问题前,有必要先解释一下微信支付的签名流程。
微信支付demo和微信支付开发文档多处提到以下几点:
~ 后台服务器做签名、下单请求,从微信服务器获取到 预支付ID 和 sign 签名等多个参数;
~ APP通过获取后台返回的参数(openID、partnerID商户号、prepayID预支付ID、nonceStr随机字符串、timeStamp时间戳、package包、sign签名),调用微信支付接口,就可以调用手机微信客户端进行支付
~ 调用微信支付的代码如下(核心代码哦,我们所有的操作都是为了这段核心的参数做准备):
//调起微信支付 PayReq* req = [[[PayReq alloc] init]autorelease]; req.openID = [dict objectForKey:@"appid"]; req.partnerId = [dict objectForKey:@"partnerid"]; req.prepayId = [dict objectForKey:@"prepayid"]; req.nonceStr = [dict objectForKey:@"noncestr"]; req.timeStamp = stamp.intValue; req.package = [dict objectForKey:@"package"]; req.sign = [dict objectForKey:@"sign"]; [WXApi sendReq:req];
根据上面的说明,童鞋们不知道明白了没。其实所有的代码,都是为了上面的核心作准备的,目的地就是为了获取上面核心代码的参数,已调用微信支付的接口。我们回到最初点:
~ - (void)sendPay:模拟签名的过程。为了更好的安全性,关于微信支付的下单和签名的过程,一般都是在后台完成,后台再把这些参数传回到APP,APP再调用。这个方法却是在APP端完成下单和签名的操作,这其实只是一个演示的过程“//本实例只是演示签名过程, 请将该过程在商户服务器上实现”
~ - (void)sendPay_demo:使用微信后台提供的参数,做支付演示。调用这个方法,控制台会打印出这样一串东西:url:http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios&order_no=1444376336&product_name=Ios%B7%FE%CE%F1%C6%F7%B6%CB%C7%A9%C3%FB%D6%A7%B8%B6%20%B2%E2%CA%D4&order_price=0.01。这串字符串中,包含了调用微信支付接口所需的所有的参数。核心代码通过使用这些参数,就可以成功调用微信客户端完成支付。
微信支付提供的官方文档:
统一下单,获取预支付ID:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1
调起支付接口,调起手机微信客户端:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2
通过上面的描述,童鞋们明白了签名、下单、调用微信支付调用的原理和代码实现了没?
三、代码实现微信支付
通过上面的描述,我们可以得知一下几点:
(0)在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 函数中添加以下代码://向微信注册 [WXApi registerApp:APP_ID withDescription:@"demo 2.0"];
(1)调用微信支付前,需要下单、签名等操作,以便获取微信支付所必要的参数。为了提高安全性,下单、签名操作一般是在后台完成
参数包括:appid、partid(商户号)、prepayid(预支付订单ID)、noncestr(参与签名的随机字符串)、timestamp(参与签名的时间戳)、sign(签名字符串)
(2)APP端获取后台的参数数据,调用一下的核心代码,调起微信支付
//调起微信支付 PayReq* req = [[[PayReq alloc] init]autorelease]; req.openID = [dict objectForKey:@"appid"]; req.partnerId = [dict objectForKey:@"partnerid"]; req.prepayId = [dict objectForKey:@"prepayid"]; req.nonceStr = [dict objectForKey:@"noncestr"]; req.timeStamp = stamp.intValue; req.package = [dict objectForKey:@"package"]; req.sign = [dict objectForKey:@"sign"]; [WXApi sendReq:req];
(3)笔者提供一段亲测成功的核心代码,演示如何后台获取参数,调起微信支付:
//请求网络数据 [[TDNetworkingHelper sharedInstane] postRequestWithPath:[NSString stringWithFormat:@"%@%@", BASE_URL, orders_pay_wxpay_sign] parameter:paramDic whenSuccessed:^(id json) { if ([[json objectForKey:@"code"] integerValue]==1) //数据获取成功 { //调用微信支付 [self sendWeChatReqWithOrderData:[json objectForKey:@"data"]]; } else //获取失败 { ALERT([json objectForKey:@"msg"]); } [self hideHub]; self.view.userInteractionEnabled = true; } whenFailed:^(NSError *error) { [self hideHub]; self.view.userInteractionEnabled = true; //判断网络状态 [self judgeNetworkStaus]; }]; -(void)sendWeChatReqWithOrderData:(NSDictionary*)orderData { NSLog(@"提交的参数:%@", orderData); //调起微信支付 PayReq* req = [[PayReq alloc] init]; req.openID = [orderData objectForKey:@"appid"]; req.partnerId = [orderData objectForKey:@"partnerid"]; req.prepayId = [orderData objectForKey:@"prepayid"]; req.nonceStr = [orderData objectForKey:@"noncestr"]; req.timeStamp = (UInt32)[[orderData objectForKey:@"timestamp"] integerValue]; req.package = [orderData objectForKey:@"package"]; req.sign = [orderData objectForKey:@"sign"]; [WXApi sendReq:req]; }
五、无法完成微信支付的各种异常状态分与处理
笔者在开发过程中,也遇到过不少的异常状态,现记录如下:
(1)无法调起手机微信客户端、无法调起微信客户端页面,一闪而过
原因分析:第一种如果您的APP集成了友盟或者shareSDK的第三方分享SDK,会导致WechaSDK包的重复冲突;
处理方法:删除友盟或者shareSDK里面的WechaSDK包;在调用微信支付接口的文件里,后缀改为 .mm
(2)中间只有一个确定按钮,点击按钮,返回APP,提示 “支付结果:失败!retcode=-2,retstr=nil ”
原因分析:到这里,说明代码没有问题,是传递的参数有问题,任何一个参数出错都可能导致这样的问题。
处理方法:与后台完成签名、下单的同事沟通,检查后台的代码。
备注:我就是卡在这里两个多星期,后来检查发现是后台同事在签名的时候出现了问题。微信支付要求有两次签名,第二次需要timestamp(时间戳)参与签名。我们只有一次签名,一直没法调起支付界面
希望这篇可以帮到你,让你少走弯路。
对以上内容有任何疑问的童鞋,请留言指教!