IOS客户端接入微信支付
实际上,从代码的角度,调起支付APP就是把一些关键的参数通过一定方式打包成为一个订单,然后发送到支付平台的服务器。所以,只要搞清楚了参数设置,搞清楚了每个支付平台的SDK里面一些关键API的使用,基本上就可以很简单的支持支付。
今天记录一下客户端里面,如何支持微信支付。首先。我们要仔细阅读一下微信SDK的开发文档,了解一下整个支付的大概流程。
然后根据提示,把相应的SDK下载下来,所谓的SDK,也就是一个链接库和两个头文件,很简单。
下载完毕,需要把SDK导入到工程里面,并且配置一下工程。因为开发者文档已经有详细描述,这里就不再复述。
从文档看到,调起微信支付其实最核心的是一下这么一段
<codeclass="hljs"vbscript="">PayReq*request=[[[PayReqalloc]init]autorelease]; request.partnerId=@10000100; request.prepayId=@1101000000140415649af9fc314aa427; request.package=@Sign=WXPay; request.nonceStr=@a462b76e7436e98e0ed6e13c64b4fd1c; request.timeStamp=@1397527777; request.sign=@582282D72DD2B03AD892830965F428CB16E7A256; [WXApisendReq:request];</code>
这里的范例是一段hardcode,真正使用的时候,参数都需要自行传入。
为了搞清楚如何使用API,我们可以下载Sample代码。不过,这个sample代码应该是微信的实习生写的,而且应该是一个对于C++比较熟悉,对于ObjectC比较陌生的实习生。。。代码风格可以看出很多东西哈。。所以这个sample读起来总觉得有点奇怪。当然,写出这个demo也是需要不错的水平,因为这个sample不仅仅是一些API的调用,还包括了一些算法的实现,MD5之类的。
看懂了sample之后,一般可以自己重构一下,成为自己APP里面的一个Manager类。
我是在2015523下载的微信Sampel代码,里面包括有:
ApiXml.h
ApiXml.m
WXUtil.h
WXUtil.m
payRequestHandler.h
payRequestHandler.m
如果比较看重命名规范的OC程序猿,就会觉得这个payRequestHandler类非常别扭,不符合camel命名规则,而且handler这个词更偏向于c++风格。我就以这个类为原型,重构了一下,并改装成一个传参的方法,供自己的APP调用。APP里面卖商品,一般就是商品名字,价格两个关键参数。所以这个重构的方法也只是提供这两个参数的接口。
ApiXml.h&&ApiXml.m&&WXUtil.h&&WXUtil.m不变
<codeclass="hljs"objectivec="">// //WechatPayManager.h // //CreatedbyHuangCharlieon5/24/15. // // #import<foundationfoundation.h=""> #importWXUtil.h #importApiXml.h #importWXApi.h //账号帐户资料 //更改商户把相关参数后可测试 #defineAPP_ID@wx@@@@@@@@@@@@@@@@//APPID #defineAPP_SECRET@//appsecret,看起来好像没用 //商户号,填写商户对应参数 #defineMCH_ID@@@@@@@@@@@ //商户API密钥,填写相应参数 #definePARTNER_ID@12345678901234567890123456789012 //支付结果回调页面 #defineNOTIFY_URL@http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php //获取服务器端支付数据地址(商户自定义)(在小吉这里,签名算法直接放在APP端,故不需要自定义) #defineSP_URL@http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php @interfaceWechatPayManager:NSObject { } //预支付网关url地址 @property(nonatomic,strong)NSString*payUrl; //debug信息 @property(nonatomic,strong)NSMutableString*debugInfo; @property(nonatomic,assign)NSIntegerlastErrCode;//返回的错误码 //商户关键信息 @property(nonatomic,strong)NSString*appId,*mchId,*spKey; //初始化函数 -(id)initWithAppID:(NSString*)appID mchID:(NSString*)mchID spKey:(NSString*)key; //获取当前的debug信息 -(NSString*)getDebugInfo; //获取预支付订单信息(核心是一个prepayID) -(NSMutableDictionary*)getPrepayWithOrderName:(NSString*)name price:(NSString*)price device:(NSString*)device; @end </foundation></code> <codeclass="hljs"objectivec="">// //WechatPayManager.m // //CreatedbyHuangCharlieon5/24/15. // // #importWechatPayManager.h @implementationWechatPayManager //初始化函数 -(id)initWithAppID:(NSString*)appIDmchID:(NSString*)mchIDspKey:(NSString*)key { self=[superinit]; if(self) { //初始化私有参数,主要是一些和商户有关的参数 self.payUrl=@https://api.mch.weixin.qq.com/pay/unifiedorder; if(self.debugInfo==nil){ self.debugInfo=[NSMutableStringstring]; } [self.debugInfosetString:@]; self.appId=appID;//微信分配给商户的appID self.mchId=mchID;// self.spKey=key;//商户的密钥 } returnself; } //获取debug信息 -(NSString*)getDebugInfo { NSString*res=[NSStringstringWithString:self.debugInfo]; [self.debugInfosetString:@]; returnres; } //创建package签名 -(NSString*)createMd5Sign:(NSMutableDictionary*)dict { NSMutableString*contentString=[NSMutableStringstring]; NSArray*keys=[dictallKeys]; //按字母顺序排序 NSArray*sortedArray=[keyssortedArrayUsingComparator:^NSComparisonResult(idobj1,idobj2){ return[obj1compare:obj2options:NSNumericSearch]; }]; //拼接字符串 for(NSString*categoryIdinsortedArray){ if(![[dictobjectForKey:categoryId]isEqualToString:@] &&![categoryIdisEqualToString:@sign] &&![categoryIdisEqualToString:@key] ) { [contentStringappendFormat:@%@=%@&,categoryId,[dictobjectForKey:categoryId]]; } } //添加key字段 [contentStringappendFormat:@key=%@,self.spKey]; //得到MD5sign签名 NSString*md5Sign=[WXUtilmd5:contentString]; //输出DebugInfo [self.debugInfoappendFormat:@MD5签名字符串: %@ ,contentString]; returnmd5Sign; } //获取package带参数的签名包 -(NSString*)genPackage:(NSMutableDictionary*)packageParams { NSString*sign; NSMutableString*reqPars=[NSMutableStringstring]; //生成签名 sign=[selfcreateMd5Sign:packageParams]; //生成xml的package NSArray*keys=[packageParamsallKeys]; [reqParsappendString:@<xml> ]; for(NSString*categoryIdinkeys){ [reqParsappendFormat:@<%@>%@<!--%@--> ,categoryId,[packageParamsobjectForKey:categoryId],categoryId]; } [reqParsappendFormat:@<sign>%@</sign> </xml>,sign]; return[NSStringstringWithString:reqPars]; } //提交预支付 -(NSString*)sendPrepay:(NSMutableDictionary*)prePayParams { NSString*prepayid=nil; //获取提交支付 NSString*send=[selfgenPackage:prePayParams]; //输出DebugInfo [self.debugInfoappendFormat:@API链接:%@ ,self.payUrl]; [self.debugInfoappendFormat:@发送的xml:%@ ,send]; //发送请求postxml数据 NSData*res=[WXUtilhttpSend:self.payUrlmethod:@POSTdata:send]; //输出DebugInfo [self.debugInfoappendFormat:@服务器返回: %@ ,[[NSStringalloc]initWithData:resencoding:NSUTF8StringEncoding]]; XMLHelper*xml=[[XMLHelperalloc]autorelease]; //开始解析 [xmlstartParse:res]; NSMutableDictionary*resParams=[xmlgetDict]; //判断返回 NSString*return_code=[resParamsobjectForKey:@return_code]; NSString*result_code=[resParamsobjectForKey:@result_code]; if([return_codeisEqualToString:@SUCCESS]) { //生成返回数据的签名 NSString*sign=[selfcreateMd5Sign:resParams]; NSString*send_sign=[resParamsobjectForKey:@sign]; //验证签名正确性 if([signisEqualToString:send_sign]){ if([result_codeisEqualToString:@SUCCESS]){ //验证业务处理状态 prepayid=[resParamsobjectForKey:@prepay_id]; return_code=0; [self.debugInfoappendFormat:@获取预支付交易标示成功! ]; } }else{ self.lastErrCode=1; [self.debugInfoappendFormat:@gen_sign=%@ _sign=%@ ,sign,send_sign]; [self.debugInfoappendFormat:@服务器返回签名验证错误!!! ]; } }else{ self.lastErrCode=2; [self.debugInfoappendFormat:@接口返回错误!!! ]; } returnprepayid; } -(NSMutableDictionary*)getPrepayWithOrderName:(NSString*)name price:(NSString*)price device:(NSString*)device { //订单标题,展示给用户 NSString*orderName=name; //订单金额,单位(分) NSString*orderPrice=price;//以分为单位的整数 //支付设备号或门店号 NSString*orderDevice=device; //支付类型,固定为APP NSString*orderType=@APP; //发器支付的机器ip,暂时没有发现其作用 NSString*orderIP=@196.168.1.1; //随机数串 srand((unsigned)time(0)); NSString*noncestr=[NSStringstringWithFormat:@%d,rand()]; NSString*orderNO=[NSStringstringWithFormat:@%ld,time(0)]; //================================ //预付单参数订单设置 //================================ NSMutableDictionary*packageParams=[NSMutableDictionarydictionary]; [packageParamssetObject:self.appIdforKey:@appid];//开放平台appid [packageParamssetObject:self.mchIdforKey:@mch_id];//商户号 [packageParamssetObject:orderDeviceforKey:@device_info];//支付设备号或门店号 [packageParamssetObject:noncestrforKey:@nonce_str];//随机串 [packageParamssetObject:orderTypeforKey:@trade_type];//支付类型,固定为APP [packageParamssetObject:orderNameforKey:@body];//订单描述,展示给用户 [packageParamssetObject:NOTIFY_URLforKey:@notify_url];//支付结果异步通知 [packageParamssetObject:orderNOforKey:@out_trade_no];//商户订单号 [packageParamssetObject:orderIPforKey:@spbill_create_ip];//发器支付的机器ip [packageParamssetObject:orderPriceforKey:@total_fee];//订单金额,单位为分 //获取prepayId(预支付交易会话标识) NSString*prePayid; prePayid=[selfsendPrepay:packageParams]; if(prePayid==nil) { [self.debugInfoappendFormat:@获取prepayid失败! ]; returnnil; } //获取到prepayid后进行第二次签名 NSString*package,*time_stamp,*nonce_str; //设置支付参数 time_tnow; time(&now); time_stamp=[NSStringstringWithFormat:@%ld,now]; nonce_str=[WXUtilmd5:time_stamp]; //重新按提交格式组包,微信客户端暂只支持package=Sign=WXPay格式,须考虑升级后支持携带package具体参数的情况 //package=[NSStringstringWithFormat:@Sign=%@,package]; package=@Sign=WXPay; //第二次签名参数列表 NSMutableDictionary*signParams=[NSMutableDictionarydictionary]; [signParamssetObject:self.appIdforKey:@appid]; [signParamssetObject:self.mchIdforKey:@partnerid]; [signParamssetObject:nonce_strforKey:@noncestr]; [signParamssetObject:packageforKey:@package]; [signParamssetObject:time_stampforKey:@timestamp]; [signParamssetObject:prePayidforKey:@prepayid]; //生成签名 NSString*sign=[selfcreateMd5Sign:signParams]; //添加签名 [signParamssetObject:signforKey:@sign]; [self.debugInfoappendFormat:@第二步签名成功,sign=%@ ,sign]; //返回参数列表 returnsignParams; } @end</code>
然后,在需要调用微信支付的Controller里面,新建一个方法。在合适的地方调用。这个方法里面利用WechatPayManager这个类进行了初始化和参数封装,然后把上述的核心代码(PayReq那一段)
<codeclass="hljs"objectivec="">-(void)wxPayWithOrderName:(NSString*)nameprice:(NSString*)price { //创建支付签名对象&&初始化支付签名对象 WechatPayManager*wxpayManager=[[[WechatPayManageralloc]initWithAppID:APP_IDmchID:MCH_IDspKey:PARTNER_ID]autorelease]; //获取到实际调起微信支付的参数后,在app端调起支付 //生成预支付订单,实际上就是把关键参数进行第一次加密。 NSString*device=[[UserManagerdefaultManager]userId]; NSMutableDictionary*dict=[wxpayManagergetPrepayWithOrderName:name price:price device:device]; if(dict==nil){ //错误提示 NSString*debug=[wxpayManagergetDebugInfo]; return; } NSMutableString*stamp=[dictobjectForKey:@timestamp]; //调起微信支付 PayReq*req=[[[PayReqalloc]init]autorelease]; req.openID=[dictobjectForKey:@appid]; req.partnerId=[dictobjectForKey:@partnerid]; req.prepayId=[dictobjectForKey:@prepayid]; req.nonceStr=[dictobjectForKey:@noncestr]; req.timeStamp=stamp.intValue; req.package=[dictobjectForKey:@package]; req.sign=[dictobjectForKey:@sign]; //BOOLflag=[WXApisendReq:req]; BOOLflag=[WXApisafeSendReq:req]; }</code>
再者,支付完成了需要调用一个delegate,这个delegate方便个性化显示支付结果。一般直接把这两个delegate放在AppDelegate就好了。因为有一些其他内容也是需要在AppDelegate里面实现,省的分开找不到。
<codeclass="hljs"objectivec="">-(void)onResp:(BaseResp*)resp { //启动微信支付的response NSString*strMsg=[NSStringstringWithFormat:@errcode:%d,resp.errCode]; if([respisKindOfClass:[PayRespclass]]){ //支付返回结果,实际支付结果需要去微信服务器端查询 switch(resp.errCode){ case0: strMsg=@支付结果:成功!; break; case-1: strMsg=@支付结果:失败!; break; case-2: strMsg=@用户已经退出支付!; break; default: strMsg=[NSStringstringWithFormat:@支付结果:失败!retcode=%d,retstr=%@,resp.errCode,resp.errStr]; break; } } }</code>
注意事项:
1)如果APP里面已经使用了ShareSDK,就有一些地方要注意。不要再重复导入微信的SDK,因为shareSDK里面的extend已经包括了微信的SDK。
2)微信本身是鼓励客户APP把签名算法放到服务器上面,这样信息就不容易被破解。但是如果客户APP本身没有服务器端,或者认为不需要放到服务器端,也可以直接把签名(加密)的部分直接放在APP端。Sample代码的注释有点乱,多次提到服务器云云,但是其实可以不这么做。
3)微信的price单位是分。注意下即可。
4)暂时想不到,以后想到了再记录。。
以上是本文给大家叙述的IOS客户端接入微信支付的全部内容,希望大家喜欢。