iOS 微信支付流程详解
背景
自微信支付、支付宝支付入世以来,移动端的支付日渐火热。虚拟货币有取代实体货币的趋向(这句纯属扯淡,不用管),支付在app开发中是一项基本的功能,有必要去掌握。从难易程度上讲,不管是微信支付还是支付宝支付都是非常简单的,因为第三方的支付文档非常详细,而且他们内部的安全性也非常高。作为使用这些支付策略的我们,只需要掌握流程,能够实现正常支付的功能即可。为什么要写下这篇博文,原因有二。其一,微信支付流程中有坑,其二,以后忘记了可以拿出来看看。
配置
1.微信支付需要两个账号,财付通和微信开发者,注册完成后需要开通支付功能,这些流程就不用多说了。在所有申请成功后,我们要取出我们支付功能需要的key:
appID、app密钥(微信发给你的邮件中有如何生成密钥的链接)、商户号。
2.在Xcode中配置app ID,需要设置下白名单,在url中配置app ID,不清楚的童鞋可以百度一下。
支付
从此处开始,进入本次的主题,开始支付。支付的过程且分为四个步骤:
第一步 获取订单号
获取订单号的途径可以是客户端自己生成,也可以去服务器生成,不过一般都是服务器生成。假设拿到了订单号设为order_no。
第二步 统一下单
这个过程是非常关键的一步,也是坑常驻的一步。统一下单有的人做法是在服务器操作,有的在客户端,不管在哪下单都是有必要搞懂的。接下来看看统一下单必须要的参数列表:
/*应用ID 微信开放平台审核通过的应用app ID*/
@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 = [super init]) {
self.appid = WECHAT_SHARE_APPID;
self.mch_id = WECHAT_MCH_ID;
self.nonce_str = [AppMethod getRandomString];
self.body = @"test";
self.out_trade_no = [infoDic formateObjectForKey:@"order_no"];
self.total_fee = [NSString stringWithFormat:@"%@", [infoDic formateObjectForKey:@"amount"]];
self.spbill_create_ip = [AppMethod deviceIPAdress];
self.notify_url = [NSString stringWithFormat:@"%@%@", BASE_URL, WECHAT_NOTI_URL];
self.trade_type = @"APP";
self.payDic = [NSMutableDictionary dictionary];
[self.payDic setValue:self.appid forKey:@"appid"];
[self.payDic setValue:self.mch_id forKey:@"mch_id"];
[self.payDic setValue:self.nonce_str forKey:@"nonce_str"];
[self.payDic setValue:self.body forKey:@"body"];
[self.payDic setValue:self.out_trade_no forKey:@"out_trade_no"];
[self.payDic setValue:self.total_fee forKey:@"total_fee"];
[self.payDic setValue:self.spbill_create_ip forKey:@"spbill_create_ip"];
[self.payDic setValue:self.notify_url forKey:@"notify_url"];
[self.payDic setValue:self.trade_type forKey:@"trade_type"];
self.sign = [self partnerSignOrder:self.payDic];
[self.payDic setValue:self.sign forKey:@"sign"];
}
return self;
}
- (NSString *)partnerSignOrder:(NSDictionary*)paramDic{
NSArray *keyArray = [paramDic allKeys];
NSMutableArray *sortedKeyArray = [NSMutableArray arrayWithArray:keyArray];
[sortedKeyArray sortUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) {
return [key1 compare:key2];
}];
NSMutableString *paramString = [NSMutableString stringWithString:@""];
// 拼接成 A=B&X=Y
for (NSString *key in sortedKeyArray){
if ([paramDic[key] length] != ){
[paramString appendFormat:@"&%@=%@", key, paramDic[key]];
}
}
if ([paramString length] > ){
[paramString deleteCharactersInRange:NSMakeRange(, )]; // remove first '&'
}
[paramString appendFormat:@"&key=%@", WeChatPARTNER_ID];//app密钥
return [[AppMethod signString:paramString] uppercaseString];
}
AppMethod.m
+ (NSString *)signString:(NSString*)origString{
const char *original_str = [origString UTF8String];
unsigned char result[];
CC_MD5(original_str, (CC_LONG)strlen(original_str), result);//调用md5
NSMutableString *hash = [NSMutableString string];
for (int i = ; i < ; i++){
[hash appendFormat:@"%02X", result[i]];
}
return hash;
}
这样,统一下单的参数已经准备好了,下面开始请求微信的下单接口: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 = [params XMLString];//这里需要导入XMLDictionary文件,里面有该方法
AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
// 这里传入的XML字符串只是形似XML,但不是正确是XML格式,需要使用AF方法进行转义
session.responseSerializer = [[AFHTTPResponseSerializer alloc] init];
[session.requestSerializer setValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
[session.requestSerializer setValue:url forHTTPHeaderField:@"SOAPAction"];
[session.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) {
return string;
}]; [session POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) { } success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
successBlock(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
HPError *hpError = [HPError errorWithCode:error.code desc:error.description];
failureBlock(hpError);
}];
}
按照上述的步骤,到这里就可以正常下单了,如果微信服务器返回给你的数据中有"result_code" = SUCCESS; "return_code" = SUCCESS,说明下单成功,这个步骤也到此结束。
第三步 调起微信客户端,并完成支付
如果第二步正常下单,那么微信会返回给你预支付ID,这个ID在最后的支付中至关重要,下面的代码是比较统一的,大家都这么写。
HPWechatProduct *product = [[HPWechatProduct alloc] initWithDic:result];
PayReq *req = [[PayReq alloc] init];
req.partnerId = product.partnerid;
req.prepayId = product.prepayid;
req.nonceStr = product.noncestr;
req.timeStamp = [product.timestamp intValue];
req.package = product.package;
req.sign = product.sign;
BOOL flag = [WXApi sendReq:req]; HPWechatProduct.m
- (instancetype)initWithDic:(NSDictionary *)dic{
if (self = [super init]) {
self.appid = WECHAT_SHARE_APPID;
self.partnerid = mah_id;
self.prepayid = [dic formateObjectForKey:@"prepay_id"];
self.package = @"Sign=WXPay";
self.noncestr = [AppMethod getRandomString];
self.timestamp = [self getTime];
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:self.appid forKey:@"appid"];
[dic setValue:self.partnerid forKey:@"partnerid"];
[dic setValue:self.prepayid forKey:@"prepayid"];
[dic setValue:self.package forKey:@"package"];
[dic setValue:self.noncestr forKey:@"noncestr"];
[dic setValue:self.timestamp forKey:@"timestamp"];
self.sign = [self partnerSignOrder:dic];
}
return self;
}
- (NSString *)partnerSignOrder:(NSDictionary*)paramDic{
NSArray *keyArray = [paramDic allKeys];
NSMutableArray *sortedKeyArray = [NSMutableArray arrayWithArray:keyArray];
[sortedKeyArray sortUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) {
return [key1 compare:key2];
}];
NSMutableString *paramString = [NSMutableString stringWithString:@""];
// 拼接成 A=B&X=Y
for (NSString *key in sortedKeyArray){
if ([paramDic[key] length] != ){
[paramString appendFormat:@"&%@=%@", key, paramDic[key]];
}
}
if ([paramString length] > ){
[paramString deleteCharactersInRange:NSMakeRange(, )]; // remove first '&'
}
[paramString appendFormat:@"&key=%@", WeChatPARTNER_ID];
return [[AppMethod signString:paramString] uppercaseString];
} - (NSString *)getTime{
NSTimeInterval interval = [[NSDate date] timeIntervalSince1970];
return [NSString stringWithFormat:@"%ld", (long)interval];
} AppMethod.m
+ (NSString *)getRandomString
{
NSString *str = [NSString stringWithFormat:@"%s",genRandomString()];
return str;
}
如果到了这一步,而且跑到了微信并完成了支付,那么微信会有一个回调,告诉你支付成功了。然而真的成功了嘛,请继续看第四步。
第四步 去服务器查询是否支付成功
即使微信告诉你支付成功了,你也不能相信,只有钱真正打到你们的账号里面了,才算支付成功。任何时候都不能以微信的回调的值判断支付是否成功(这是微信文档说的)。
iOS 微信支付流程详解的更多相关文章
- iOS 组件化流程详解(git创建流程)
[链接]组件化流程详解(一)https://www.jianshu.com/p/2deca619ff7e
- iOS中 最新微信支付/最全的微信支付教程详解 韩俊强的博客
每日更新关注:http://weibo.com/hanjunqiang 新浪微博! 亲们, 首先让我们来看一下微信支付的流程吧. 1. 注册微信开放平台,创建应用获取appid,appSecret, ...
- 基于H5的微信支付开发详解
这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
- ****基于H5的微信支付开发详解[转]
这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
- ***PHP基于H5的微信支付开发详解(CI框架)
这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可 ...
- Android app 第三方微信支付接入详解
微信支付做了好几遍了,都没有出现什么棘手的问题,下面一一为大家分享一下,欢迎吐槽. 还是老样子,接入微信的支付要第一步添加微信支付官方的包libammsdk.jar 首先就处理略坑的一个问题,app应 ...
- vue-cli中进行微信支付代码详解
最近做微信支付,颇经历一番波折,这里总结一下,便于以后少走弯路: 在进行微信支付,除了需要公众号之外,你还需要一个微信商户.根据商户规则进行商户申请 这是公众号的基本开发配置,这里在微信授权的时候就已 ...
- 微信小程序中的微信支付js代码和流程详解
微信支付流程 步骤 (一)获取用户的信息 (二)统一下单(返回的prepay_id用于第(三)步的请求参数) (三)发起支付请求 操作(这边假设你已经获得了用户的openId) (一).获取用户ope ...
- iOS学习之UINavigationController详解与使用(一)添加UIBarButtonItem
http://blog.csdn.net/totogo2010/article/details/7681879 1.UINavigationController导航控制器如何使用 UINavigati ...
随机推荐
- 配置LAMP实现WordPress
环境说明: 在同一台主机上实现LAMP(Linux + Apache + MariaDB + PHP) CentOS 7.3.Apache 2.4.6.MariaDB 5.5.52.PHP 5.4.1 ...
- HDOJ2002-计算球体面积
Problem Description 根据输入的半径值,计算球的体积. Input 输入数据有多组,每组占一行,每行包括一个实数,表示球的半径. Output 输出对应的球的体积,对于每组输 ...
- DDD理论学习系列(12)-- 仓储
DDD理论学习系列--案例及目录 1. 引言 DDD中Repository这个单词,主要有两种翻译:资源库和仓储,本文取仓储之译. 说到仓储,我们肯定就想到了仓库,仓库一般用来存放货物,而仓库一般由仓 ...
- IBM Websphere 集群会话共享问题解决办法
遇到一应用部署环境如下图: 两台HTTP SERVER(以下简称IHS)负责转发数据包,其中F5采用粘性模式,即一个用户在会话周期内的数据包一定会被转发到IHS中的一台, 但IHS 到Web Serv ...
- java笔录---反射机制(1)
引言 为了方便记忆java的反射机制,在这里仔细的总结了一下.主要是怕以后忘记了,这样也方便回忆.因为最近利用空余时间深入的了解spring和Mybatis框架, 像spring中核心模块IO ...
- Redis-rdb持久化
Redis实现快照的过程 redis调用fork,现在有了子进程和父进程 父进程继续处理client请求,子进程负责将内存内容写入到临时文. 由于os的写时复制机制(copy on write)父子进 ...
- 有关cxf与安卓客户端或者C客户端的中文乱码问题
前段时间在与C的客户端和安卓的客户端进行联调,首先我的方法接收的是C客户端所传递的数据,但是传递到方法内的中文就变成了乱码(我的方法的编码是utf8),最后通过String a = new Strin ...
- 设置双击直接打开.ipynb文件
本文环境:win10(64)+anaconda3(64) anaconda3安装好后,可在开始菜单下查看 背景:jupyter notebook打开后无法更改路径,只能在默认路径下upload文件(如 ...
- JavaScript获取客户端IP地址
1. 第三方接口 1) 这里提供一个搜狐接口的地址:http://pv.sohu.com/cityjson?ie=utf-8 ,将这个js引入到页面即可得到returnCitySN. 2) api.i ...
- AngularJS--购物车全选/取消全选功能实现
刚学习angularJS,于是练习写了一个类似于购物车的全选/取消全选的功能,主要实现的功能有: 1.勾选全选checkbox,列表数据全部被勾选,取消同理,用ng-model实现双向绑定: 2.选中 ...