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...的方式拼接,然后进行加密得到。参数拼接按照字母排序,举个例子,参数为[email protected]"id",[email protected]"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] != 0){
            [paramString appendFormat:@"&%@=%@", key, paramDic[key]];
        }
    }
    if ([paramString length] > 1){
        [paramString deleteCharactersInRange:NSMakeRange(0, 1)];    // 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[32];
    CC_MD5(original_str, (CC_LONG)strlen(original_str), result);//调用md5
    NSMutableString *hash = [NSMutableString string];
    for (int i = 0; i < 16; 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] != 0){
            [paramString appendFormat:@"&%@=%@", key, paramDic[key]];
        }
    }
    if ([paramString length] > 1){
        [paramString deleteCharactersInRange:NSMakeRange(0, 1)];    // 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(32)];
    return str;
}

如果到了这一步,而且跑到了微信并完成了支付,那么微信会有一个回调,告诉你支付成功了。然而真的成功了嘛,请继续看第四步。

第四步  去服务器查询是否支付成功

即使微信告诉你支付成功了,你也不能相信,只有钱真正打到你们的账号里面了,才算支付成功。任何时候都不能以微信的回调的值判断支付是否成功(这是微信文档说的)。

时间: 2024-10-09 22:17:36

iOS 微信支付流程详解的相关文章

iOS中 最新微信支付/最全的微信支付教程详解 韩俊强的博客

每日更新关注:http://weibo.com/hanjunqiang  新浪微博! 亲们, 首先让我们来看一下微信支付的流程吧. 1. 注册微信开放平台,创建应用获取appid,appSecret,申请支付功能,申请成功之后会返回一些参数. 2. 下载微信支付sdk 3. 客户端请求订单,后台与微信后台交互,返回给客户端支付参数 4. 调用微信客户端,由微信客户端和微信服务器打交道: 5. 客户端和服务端都会收到支付结果:(前台消息不可靠,我们需要去后台验证,如果后台没有收到支付通知,后台去微

基于H5的微信支付开发详解

这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可供参考,有的朋友直接看文档就可以自己实现此支付接口的开发了. 一.前言 为何我还写一篇微信支付接口的博文呢?第一,我们必须知道,所谓的工作经验很多都是靠总结出来的,你只有总结了更多知识,积累了更多经验,你才能在该行业中脱颖而出,我个人觉得如今的招聘,很多都需要工作经验(1年.3年.5年....),其

****基于H5的微信支付开发详解[转]

这次总结一下用户在微信内打开网页时,可以调用微信支付完成下单功能的模块开发,也就是在微信内的H5页面通过jsApi接口实现支付功能.当然了,微信官网上的微信支付开发文档也讲解的很详细,并且有实现代码可供参考,有的朋友直接看文档就可以自己实现此支付接口的开发了. 一.前言 为何我还写一篇微信支付接口的博文呢?第一,我们必须知道,所谓的工作经验很多都是靠总结出来的,你只有总结了更多知识,积累了更多经验,你才能在该行业中脱颖而出,我个人觉得如今的招聘,很多都需要工作经验(1年.3年.5年....),其

微信支付最详解教程

最近要用微信支付功能,在此总结一下! 需要下面第三方支持 备注:JSONKit框架是基于MRC的,如果工程开发环境是ARC的话,请在编译时设定 编译参数 -fno-objc-arc 1.首先到微信开放平台上,申请app及与T进行签约.认证 https://open.weixin.qq.com/ 获取到: /** *  微信开放平台申请得到的 appid, 需要同时添加在info.plist文件中URL schema,用于完成时,回调到app */ #define WXAppId @"wxd930

iOS应用发布流程详解

这篇博客将会以一个完整的过程来实现一个iOS App提交审核的过程.在这个过程中,我们会涉及到证书.代码签名.授权文件.真机调试.开发者账号等一些概念,我也会有所解释.推荐大家先去看<iOS应用分发与内测(一)><iOS应用分发与内测(二)><iOS开发Provionsioning Profile解析>这几篇博客,看完以后会对发布App的流程非常熟悉. 我先对一些概念做一个讲解: [证书 Certification] 证书是对电脑开发资格的认证,每个开发者账号有一套.一

微信支付流程(IOS)

微信支付流程(IOS) 1.注册微信开发账号,开通支付权限(注册谁都会了) 2.把三个文件拉进去 libWeChatSDK.a WXApi.h WXApiObject.h 3.添加URL Types 4.AppDelegate.m中添加微信账号 [WXApi registerApp:@"wxalsdfjalsfals" withDescription:@"tencent"]; 回调 - (BOOL)application:(UIApplication *)appli

ios新特征 ARC详解

IOS ARC 分类: IOS ARC2013-01-17 09:16 2069人阅读 评论(0) 收藏 举报 目录(?)[+] 关闭工程的ARC(Automatic Reference Counting) 顺带附上ARC教程 本文部分实例取自iOS 5 Toturail一书中关于ARC的教程和公开内容,仅用于技术交流和讨论.请不要将本文的部分或全部内容用于商用,谢谢合作. 欢迎转载本文,但是转载请注明本文出处:http://www.onevcat.com/2012/06/arc-hand-by

ssl协议工作流程详解

SSL 协议 (HTTPS) 握手.工作流程详解 (双向 HTTPS 流程 )SSL 协议的工作流程:服务器认证阶段: 1)客户端向服务器发送一个开始信息"Hello"以便开始一个新的会话连接; 2)服务器根据客户的信息确定是否需要生成新的主密钥,如需要则服务器在响应客户的"Hello"信息时将包含生成主密钥所需的信息; 3)客户根据收到的服务器响应信息,产生一个主密钥,并用服务器的公开密钥加密后传给服务器; 4)服务器恢复该主密钥,并返回给客户一个用主密钥认证的信

[转]iOS学习之UINavigationController详解与使用(一)添加UIBarButtonItem

转载地址:http://blog.csdn.net/totogo2010/article/details/7681879 1.UINavigationController导航控制器如何使用 UINavigationController可以翻译为导航控制器,在iOS里经常用到. 我们看看它的如何使用: 下面的图显示了导航控制器的流程.最左侧是根视图,当用户点击其中的General项时 ,General视图会滑入屏幕:当用户继续点击Auto-Lock项时,Auto-Lock视图将滑入屏幕.相应地,在