iOS实现微信支付流程详解
背景
自微信支付、支付宝支付入世以来,移动端的支付日渐火热。虚拟货币有取代实体货币的趋向(这句纯属扯淡,不用管),支付在app开发中是一项基本的功能,有必要去掌握。从难易程度上讲,不管是微信支付还是支付宝支付都是非常简单的,因为第三方的支付文档非常详细,而且他们内部的安全性也非常高。作为使用这些支付策略的我们,只需要掌握流程,能够实现正常支付的功能即可。为什么要写下这篇博文,原因有二。其一,微信支付流程中有坑,其二,以后忘记了可以拿出来看看。
配置
1.微信支付需要两个账号,财付通和微信开发者,注册完成后需要开通支付功能,这些流程就不用多说了。在所有申请成功后,我们要取出我们支付功能需要的key:
appID、app密钥(微信发给你的邮件中有如何生成密钥的链接)、商户号。
2.在Xcode中配置appID,需要设置下白名单,在url中配置appID,不清楚的童鞋可以百度一下。
支付
从此处开始,进入本次的主题,开始支付。支付的过程且分为四个步骤:
第一步 获取订单号
获取订单号的途径可以是客户端自己生成,也可以去服务器生成,不过一般都是服务器生成。假设拿到了订单号设为order_no。
第二步 统一下单
这个过程是非常关键的一步,也是坑常驻的一步。统一下单有的人做法是在服务器操作,有的在客户端,不管在哪下单都是有必要搞懂的。接下来看看统一下单必须要的参数列表:
/*应用ID微信开放平台审核通过的应用appID*/ @property(nonatomic,copy)NSString*appid; /*商户号微信支付分配的商户号*/ @property(nonatomic,copy)NSString*mch_id; /*随机字符串随机字符串,不长于32位*/ @property(nonatomic,copy)NSString*nonce_str; /*签名*/ @property(nonatomic,copy)NSString*sign; /*商品描述天天爱消除-游戏充值。*/ @property(nonatomic,copy)NSString*body; /*商户订单号*/ @property(nonatomic,copy)NSString*out_trade_no; /*总金额订单总金额,单位为分*/ @property(nonatomic,copy)NSString*total_fee; /*终端IP*/ @property(nonatomic,copy)NSString*spbill_create_ip; /*通知地址*/ @property(nonatomic,copy)NSString*notify_url; /*交易类型*/ @property(nonatomic,copy)NSString*trade_type;
其中,sign是其他所有参数按照key1=value1&key2=value2...的方式拼接,然后进行加密得到。参数拼接按照字母排序,举个例子,参数为appid=@"id",mch_id=@"mch"得到的字符串应该是:@"appid=id&mch_id=mch",然后对该字符串进行加密,如下述代码
-(instancetype)initWithDicInfo:(NSDictionary*)infoDic{
if(self=[superinit]){
self.appid=WECHAT_SHARE_APPID;
self.mch_id=WECHAT_MCH_ID;
self.nonce_str=[AppMethodgetRandomString];
self.body=@"test";
self.out_trade_no=[infoDicformateObjectForKey:@"order_no"];
self.total_fee=[NSStringstringWithFormat:@"%@",[infoDicformateObjectForKey:@"amount"]];
self.spbill_create_ip=[AppMethoddeviceIPAdress];
self.notify_url=[NSStringstringWithFormat:@"%@%@",BASE_URL,WECHAT_NOTI_URL];
self.trade_type=@"APP";
self.payDic=[NSMutableDictionarydictionary];
[self.payDicsetValue:self.appidforKey:@"appid"];
[self.payDicsetValue:self.mch_idforKey:@"mch_id"];
[self.payDicsetValue:self.nonce_strforKey:@"nonce_str"];
[self.payDicsetValue:self.bodyforKey:@"body"];
[self.payDicsetValue:self.out_trade_noforKey:@"out_trade_no"];
[self.payDicsetValue:self.total_feeforKey:@"total_fee"];
[self.payDicsetValue:self.spbill_create_ipforKey:@"spbill_create_ip"];
[self.payDicsetValue:self.notify_urlforKey:@"notify_url"];
[self.payDicsetValue:self.trade_typeforKey:@"trade_type"];
self.sign=[selfpartnerSignOrder:self.payDic];
[self.payDicsetValue:self.signforKey:@"sign"];
}
returnself;
}
-(NSString*)partnerSignOrder:(NSDictionary*)paramDic{
NSArray*keyArray=[paramDicallKeys];
NSMutableArray*sortedKeyArray=[NSMutableArrayarrayWithArray:keyArray];
[sortedKeyArraysortUsingComparator:^NSComparisonResult(NSString*key1,NSString*key2){
return[key1compare:key2];
}];
NSMutableString*paramString=[NSMutableStringstringWithString:@""];
//拼接成A=B&X=Y
for(NSString*keyinsortedKeyArray){
if([paramDic[key]length]!=0){
[paramStringappendFormat:@"&%@=%@",key,paramDic[key]];
}
}
if([paramStringlength]>1){
[paramStringdeleteCharactersInRange:NSMakeRange(0,1)];//removefirst'&'
}
[paramStringappendFormat:@"&key=%@",WeChatPARTNER_ID];//app密钥
return[[AppMethodsignString:paramString]uppercaseString];
}
AppMethod.m
+(NSString*)signString:(NSString*)origString{
constchar*original_str=[origStringUTF8String];
unsignedcharresult[32];
CC_MD5(original_str,(CC_LONG)strlen(original_str),result);//调用md5
NSMutableString*hash=[NSMutableStringstring];
for(inti=0;i<16;i++){
[hashappendFormat:@"%02X",result[i]];
}
returnhash;
}
这样,统一下单的参数已经准备好了,下面开始请求微信的下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder,现在坑又来了。按照微信的下单说明,传给微信服务器的参数必须是XML格式的数据。如果你传过这种类型的自然好办,不过我猜大多数童鞋没有传过这种类型的数据,好在AF有提供方法,不然就等着哭吧。继续看代码
+(void)postWechatPayWithUrl:(NSString*)url
params:(id)params
andSuccess:(requestSuccessResult)successBlock
andFailure:(requestFailureResult)failureBlock{
NSString*string=[paramsXMLString];//这里需要导入XMLDictionary文件,里面有该方法
AFHTTPSessionManager*session=[AFHTTPSessionManagermanager];
//这里传入的XML字符串只是形似XML,但不是正确是XML格式,需要使用AF方法进行转义
session.responseSerializer=[[AFHTTPResponseSerializeralloc]init];
[session.requestSerializersetValue:@"text/xml;charset=utf-8"forHTTPHeaderField:@"Content-Type"];
[session.requestSerializersetValue:urlforHTTPHeaderField:@"SOAPAction"];
[session.requestSerializersetQueryStringSerializationWithBlock:^NSString*(NSURLRequest*request,NSDictionary*parameters,NSError*__autoreleasing*error){
returnstring;
}];
[sessionPOST:urlparameters:paramsprogress:^(NSProgress*_NonnulluploadProgress){
}success:^(NSURLSessionDataTask*_Nonnulltask,id_NullableresponseObject){
successBlock(responseObject);
}failure:^(NSURLSessionDataTask*_Nullabletask,NSError*_Nonnullerror){
HPError*hpError=[HPErrorerrorWithCode:error.codedesc:error.description];
failureBlock(hpError);
}];
}
按照上述的步骤,到这里就可以正常下单了,如果微信服务器返回给你的数据中有"result_code"=SUCCESS;"return_code"=SUCCESS,说明下单成功,这个步骤也到此结束。
第三步 调起微信客户端,并完成支付
如果第二步正常下单,那么微信会返回给你预支付ID,这个ID在最后的支付中至关重要,下面的代码是比较统一的,大家都这么写。
HPWechatProduct*product=[[HPWechatProductalloc]initWithDic:result];
PayReq*req=[[PayReqalloc]init];
req.partnerId=product.partnerid;
req.prepayId=product.prepayid;
req.nonceStr=product.noncestr;
req.timeStamp=[product.timestampintValue];
req.package=product.package;
req.sign=product.sign;
BOOLflag=[WXApisendReq:req];
HPWechatProduct.m
-(instancetype)initWithDic:(NSDictionary*)dic{
if(self=[superinit]){
self.appid=WECHAT_SHARE_APPID;
self.partnerid=mah_id;
self.prepayid=[dicformateObjectForKey:@"prepay_id"];
self.package=@"Sign=WXPay";
self.noncestr=[AppMethodgetRandomString];
self.timestamp=[selfgetTime];
NSMutableDictionary*dic=[NSMutableDictionarydictionary];
[dicsetValue:self.appidforKey:@"appid"];
[dicsetValue:self.partneridforKey:@"partnerid"];
[dicsetValue:self.prepayidforKey:@"prepayid"];
[dicsetValue:self.packageforKey:@"package"];
[dicsetValue:self.noncestrforKey:@"noncestr"];
[dicsetValue:self.timestampforKey:@"timestamp"];
self.sign=[selfpartnerSignOrder:dic];
}
returnself;
}
-(NSString*)partnerSignOrder:(NSDictionary*)paramDic{
NSArray*keyArray=[paramDicallKeys];
NSMutableArray*sortedKeyArray=[NSMutableArrayarrayWithArray:keyArray];
[sortedKeyArraysortUsingComparator:^NSComparisonResult(NSString*key1,NSString*key2){
return[key1compare:key2];
}];
NSMutableString*paramString=[NSMutableStringstringWithString:@""];
//拼接成A=B&X=Y
for(NSString*keyinsortedKeyArray){
if([paramDic[key]length]!=0){
[paramStringappendFormat:@"&%@=%@",key,paramDic[key]];
}
}
if([paramStringlength]>1){
[paramStringdeleteCharactersInRange:NSMakeRange(0,1)];//removefirst'&'
}
[paramStringappendFormat:@"&key=%@",WeChatPARTNER_ID];
return[[AppMethodsignString:paramString]uppercaseString];
}
-(NSString*)getTime{
NSTimeIntervalinterval=[[NSDatedate]timeIntervalSince1970];
return[NSStringstringWithFormat:@"%ld",(long)interval];
}
AppMethod.m
+(NSString*)getRandomString
{
NSString*str=[NSStringstringWithFormat:@"%s",genRandomString(32)];
returnstr;
}
如果到了这一步,而且跑到了微信并完成了支付,那么微信会有一个回调,告诉你支付成功了。然而真的成功了嘛,请继续看第四步。
第四步 去服务器查询是否支付成功
即使微信告诉你支付成功了,你也不能相信,只有钱真正打到你们的账号里面了,才算支付成功。任何时候都不能以微信的回调的值判断支付是否成功(这是微信文档说的)。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。