iOS开发- 微信支付
来源:互联网 发布:阿里云邮箱个人版注册 编辑:程序博客网 时间:2024/05/21 09:57
因为近期项目中需要接受微信支付功能,自己也爬了很多的坑,所以做了一下这边文章供大家学习参考,远离爬坑,文章主要讲到以下五部分:
一,填写商户平台所需资料
二,具体演示代码@Github下载地址
本文为本人学习记录笔记,如需转载,请注明出处@iOS_lyon
填写商户平台所需资料
一,填写经营信息
@查看截图指引
下图选择不同的类目,所需要上传的资料也是有所不同的,下图拿其它为例子
二,填写商户信息(确认信息操作,此处省略)
三,填写对公帐号信息
以下是微信官方给出的交互步骤
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,注意:package的值格式为Sign = WXPay
步骤4:商店APP调起微信支付.api参见本章节【app端开发步骤说明】
步骤6:商户后台接收支付通知.api参见【支付结果通知API】
步骤6 :商户后台查询支付结果。,api参见【查询订单API】
具体项目
一,创建一个项目
二,下载微信终端SDK文件
SDK文件包括libWeChatSDK.a,WXApi.h,WXApiObject.h三个。
请前往“ 资源下载页 ”下载最新SDK包
1.将下载好的SDK导入项目,如下图
2.添加依赖库
3.准备所需配置的数据
// 开放平台登录https://open.weixin.qq.com的开发者中心获取APPID#define WX_APPID @"wxd21d890***2db4ca"// 开放平台登录https://open.weixin.qq.com的开发者中心获取AppSecret。#define WX_APPSecret @"fc32dfae9****eb5f77dddd4ea5"// 微信支付商户号#define MCH_ID @"13536**702"// 安全校验码(MD5)密钥,商户平台登录账户和密码登录http://pay.weixin.qq.com 平台设置的“API密钥”,为了安全,请设置为以数字和字母组成的32字符串。#define WX_PartnerKey @"b5f9c901480*****0f4c6e659be0"
在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“信息”标签栏的“URL类型”添加“URL scheme”为你所注册AppId(如下图所示)
配置好上述参数后就可以写代码了,具体可查看@Github下载地址
- PCH头文件:在该文件配置好以下备注中要配置参数即可运行demo
#ifndef PrefixHeader_pch#define PrefixHeader_pch#pragma mark - #pragma mark - 微信支付配置参数// 开放平台登录https://open.weixin.qq.com的开发者中心获取APPID#define WX_APPID @"wxd21d89033***b4ca"// 开放平台登录https://open.weixin.qq.com的开发者中心获取AppSecret。#define WX_APPSecret @"fc32dfae99bc67e****5f77dddd4ea5"// 微信支付商户号#define MCH_ID @"1353***702"// 安全校验码(MD5)密钥,商户平台登录账户和密码登录http://pay.weixin.qq.com// 平台设置的“API密钥”,为了安全,请设置为以数字和字母组成的32字符串。#define WX_PartnerKey @"B6246A6D8***C730EEA0F78D3B461"#pragma mark - #pragma mark - 统一下单请求参数键值// 应用id#define WXAPPID @"appid"// 商户号#define WXMCHID @"mch_id"// 随机字符串#define WXNONCESTR @"nonce_str"// 签名#define WXSIGN @"sign"// 商品描述#define WXBODY @"body"// 商户订单号#define WXOUTTRADENO @"out_trade_no"// 总金额#define WXTOTALFEE @"total_fee"// 终端IP#define WXEQUIPMENTIP @"spbill_create_ip"// 通知地址#define WXNOTIFYURL @"notify_url"// 交易类型#define WXTRADETYPE @"trade_type"// 预支付交易会话#define WXPREPAYID @"prepay_id"#pragma mark - #pragma mark - 微信下单接口// 微信统一下单接口连接#define WXUNIFIEDORDERURL @"https://api.mch.weixin.qq.com/pay/unifiedorder"#endif /* PrefixHeader_pch */
#import "AppDelegate.h"#import "WXApiManager.h"@interface AppDelegate ()@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{ // 注册微信 [WXApi registerApp:WX_APPID withDescription:@"demo 2.0"]; return YES;}- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{ return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];}- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];}@end
微信支付
#import "WXApiManager.h"@implementation WXApiManager#pragma mark - 单粒+(instancetype)sharedManager { static dispatch_once_t onceToken; static WXApiManager *instance; dispatch_once(&onceToken, ^{ instance = [[WXApiManager alloc] init]; }); return instance;}#pragma mark - WXApiDelegate- (void)onResp:(BaseResp *)resp{ if([resp isKindOfClass:[PayResp class]]){ //支付返回结果,实际支付结果需要去微信服务器端查询 NSString *strMsg; switch (resp.errCode) { case WXSuccess: strMsg = @"支付结果:成功!"; NSLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode); break; default: strMsg = [NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode,resp.errStr]; NSLog(@"错误,retcode = %d, retstr = %@", resp.errCode,resp.errStr); break; } }}@end
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
#import "ViewController.h"#import "WXApiRequestHandler.h"@interface ViewController ()@end@implementation ViewController- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ // 发起支付 [WXApiRequestHandler jumpToBizPay];}@end
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
#import <Foundation/Foundation.h>#import "WXApiObject.h"@interface WXApiRequestHandler : NSObject+ (NSString *)jumpToBizPay;@end
#import "WXApi.h"#import "WXApiRequestHandler.h"#import "WXApiManager.h"#import "DataMD5.h"#import "XMLDictionary.h"#import <AFNetworking.h>#pragma mark - 用于获取设备ip地址#include <ifaddrs.h>#include <arpa/inet.h>@implementation WXApiRequestHandler#pragma mark - 产生随机字符串//生成随机数算法 ,随机字符串,不长于32位//微信支付API接口协议中包含字段nonce_str,主要保证签名不可预测。//我们推荐生成随机数算法如下:调用随机数函数生成,将得到的值转换为字符串。+ (NSString *)generateTradeNO { static int kNumber = 15; NSString *sourceStr = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; NSMutableString *resultStr = [[NSMutableString alloc] init]; // srand函数是初始化随机数的种子,为接下来的rand函数调用做准备。 // time(0)函数返回某一特定时间的小数值。 // 这条语句的意思就是初始化随机数种子,time函数是为了提高随机的质量(也就是减少重复)而使用的。 // srand(time(0)) 就是给这个算法一个启动种子,也就是算法的随机种子数,有这个数以后才可以产生随机数,用1970.1.1至今的秒数,初始化随机数种子。 // Srand是种下随机种子数,你每回种下的种子不一样,用Rand得到的随机数就不一样。为了每回种下一个不一样的种子,所以就选用Time(0),Time(0)是得到当前时时间值(因为每时每刻时间是不一样的了)。 srand(time(0)); // 此行代码有警告: for (int i = 0; i < kNumber; i++) { unsigned index = rand() % [sourceStr length]; NSString *oneStr = [sourceStr substringWithRange:NSMakeRange(index, 1)]; [resultStr appendString:oneStr]; } return resultStr;}#pragma mark - 获取设备ip地址+ (NSString *)fetchIPAddress { NSString *address = @"error"; struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; // retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { // Loop through linked list of interfaces temp_addr = interfaces; while(temp_addr != NULL) { if(temp_addr->ifa_addr->sa_family == AF_INET) { // Check if interface is en0 which is the wifi connection on the iPhone if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { // Get NSString from C String address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; } } temp_addr = temp_addr->ifa_next; } } // Free memory freeifaddrs(interfaces); return address;}#pragma mark - Public Methods// 发起微信支付+ (void)jumpToWxPay {#pragma mark 客户端操作时候的代码 \ 但是这些步骤应该放在服务端操作//============================================================ // V3&V4支付流程实现 // 注意:参数配置请查看服务器端Demo // 更新时间:2015年11月20日 //============================================================// 交易类型#define TRADE_TYPE @"APP"// 交易结果通知网站此处用于测试,随意填写,正式使用时填写正确网站#define NOTIFY_URL @"http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php"// 交易价格1表示0.01元,10表示0.1元#define PRICE @"1" // 随机字符串变量 这里最好使用和安卓端一致的生成逻辑 NSString *nonce_str = [self generateTradeNO]; // 设备IP地址,请再wifi环境下测试,否则获取的ip地址为error,正确格式应该是8.8.8.8 NSString *addressIP = [self fetchIPAddress]; // 随机产生订单号用于测试,正式使用请换成你从自己服务器获取的订单号 NSString *orderno = [NSString stringWithFormat:@"%ld",time(0)]; // 获取SIGN签名 DataMD5 *data = [[DataMD5 alloc] initWithAppid:WX_APPID mch_id:MCH_ID nonce_str:nonce_str partner_id:WX_PartnerKey body:@"充值" out_trade_no:orderno total_fee:PRICE spbill_create_ip:addressIP notify_url:NOTIFY_URL trade_type:TRADE_TYPE]; // 转换成xml字符串 NSString *string = [[data dic] XMLString]; 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:WXUNIFIEDORDERURL forHTTPHeaderField:@"SOAPAction"]; [session.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) { return string; }]; [session POST:WXUNIFIEDORDERURL parameters:string progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { // 输出XML数据 NSString *responseString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding] ; // 将微信返回的xml数据解析转义成字典 NSDictionary *dic = [NSDictionary dictionaryWithXMLString:responseString]; // 判断返回的许可 if ([[dic objectForKey:@"result_code"] isEqualToString:@"SUCCESS"] &&[[dic objectForKey:@"return_code"] isEqualToString:@"SUCCESS"] ) { // 发起微信支付,设置参数 PayReq *request = [[PayReq alloc] init]; request.openID = [dic objectForKey:WXAPPID]; request.partnerId = [dic objectForKey:WXMCHID]; request.prepayId= [dic objectForKey:WXPREPAYID]; request.package = @"Sign=WXPay"; request.nonceStr= [dic objectForKey:WXNONCESTR]; // 将当前时间转化成时间戳 NSDate *datenow = [NSDate date]; NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]]; UInt32 timeStamp =[timeSp intValue]; request.timeStamp= timeStamp; // 签名加密 DataMD5 *md5 = [[DataMD5 alloc] init]; request.sign = [dic objectForKey:@"sign"]; request.sign=[md5 createMD5SingForPay:request.openID partnerid:request.partnerId prepayid:request.prepayId package:request.package noncestr:request.nonceStr timestamp:request.timeStamp]; // 调用微信 [WXApi sendReq:request]; } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }];#pragma mark 服务端操作微信支付 / 上述客户端操作可以忽略(仅供参考)没办法,靠后台还不如靠自己,先自己了解客户端实现支付的操作 NSMutableDictionary *params = [NSMutableDictionary dictionary]; params[WXTOTALFEE] = @"1"; params[WXEQUIPMENTIP] = [self fetchIPAddress]; AFHTTPSessionManager *session = [AFHTTPSessionManager manager]; [session POST:URLSTRING parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { NSLog(@"responseObject = %@",responseObject); // 判断返回的许可 if ([[responseObject objectForKey:@"result_code"] isEqualToString:@"SUCCESS"] &&[[responseObject objectForKey:@"return_code"] isEqualToString:@"SUCCESS"] ) { // 发起微信支付,设置参数 PayReq *request = [[PayReq alloc] init]; request.openID = [responseObject objectForKey:WXAPPID]; request.partnerId = [responseObject objectForKey:WXMCHID]; request.prepayId = [responseObject objectForKey:WXPREPAYID]; request.package = @"Sign=WXPay"; request.nonceStr = [responseObject objectForKey:WXNONCESTR]; request.timeStamp = [[responseObject objectForKey:@"timestamp"] intValue]; request.sign = [responseObject objectForKey:@"sign"]; // 调用微信支付 [WXApi sendReq:request]; }else{ // 显示错误信息 [LyonKeyWindow.rootViewController showHint:responseObject[@"err_code_des"]]; } } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { NSLog(@"%@",error); }];}@end
以下皆是客户端的签名方法类
#import "DataMD5.h"#import <CommonCrypto/CommonDigest.h>@interface DataMD5()@property (nonatomic,strong) NSString *appid;@property (nonatomic,strong) NSString *mch_id;@property (nonatomic,strong) NSString *nonce_str;@property (nonatomic,strong) NSString *partnerkey;@property (nonatomic,strong) NSString *body;@property (nonatomic,strong) NSString *out_trade_no;@property (nonatomic,strong) NSString *total_fee;@property (nonatomic,strong) NSString *spbill_create_ip;@property (nonatomic,strong) NSString *notify_url;@property (nonatomic,strong) NSString *trade_type;@end@implementation DataMD5#pragma makr - 懒加载- (NSMutableDictionary *)dic{ if (!_dic) { _dic = [NSMutableDictionary dictionary]; } return _dic;}#pragma mark - Config-(instancetype)initWithAppid:(NSString *)appid_key mch_id:(NSString *)mch_id_key nonce_str:(NSString *)noce_str_key partner_id:(NSString *)partner_id body:(NSString *)body_key out_trade_no :(NSString *)out_trade_no_key total_fee:(NSString *)total_fee_key spbill_create_ip:(NSString *)spbill_create_ip_key notify_url:(NSString *)notify_url_key trade_type:(NSString *)trade_type_key{ if (self = [super init]) { _appid = appid_key; _mch_id = mch_id_key; _nonce_str = noce_str_key; _partnerkey = partner_id; _body = body_key; _out_trade_no = out_trade_no_key; _total_fee = total_fee_key; _spbill_create_ip = spbill_create_ip_key; _notify_url = notify_url_key; _trade_type = trade_type_key; [self.dic setValue:_appid forKey:WXAPPID]; [self.dic setValue:_mch_id forKey:WXMCHID]; [self.dic setValue:_nonce_str forKey:WXNONCESTR]; [self.dic setValue:_body forKey:WXBODY]; [self.dic setValue:_out_trade_no forKey:WXOUTTRADENO]; [self.dic setValue:_total_fee forKey:WXTOTALFEE]; [self.dic setValue:_spbill_create_ip forKey:WXEQUIPMENTIP]; [self.dic setValue:_notify_url forKey:WXNOTIFYURL]; [self.dic setValue:_trade_type forKey:WXTRADETYPE]; [self createMd5Sign:self.dic]; } return self;}//创建签名//签名算法//签名生成的通用步骤如下://第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。//特别注意以下重要规则://◆ 参数名ASCII码从小到大排序(字典序);//◆ 如果参数的值为空不参与签名;//◆ 参数名区分大小写;//◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。//◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段//第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。//key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置-(void)createMd5Sign:(NSMutableDictionary*)dict{ NSMutableString *contentString =[NSMutableString string]; NSArray *keys = [dict allKeys]; //按字母顺序排序 NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1 compare:obj2 options:NSNumericSearch]; }]; //拼接字符串 for (NSString *categoryId in sortedArray) { if ( ![[dict objectForKey:categoryId] isEqualToString:@""] && ![[dict objectForKey:categoryId] isEqualToString:@"sign"] && ![[dict objectForKey:categoryId] isEqualToString:@"key"] ) { [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]]; } } //添加商户密钥key字段 [contentString appendFormat:@"key=%@",_partnerkey]; NSLog(@"contentString = %@",contentString); //MD5 获取Sign签名 NSString *md5Sign =[self md5:contentString]; // [self.dic setValue:md5Sign forKey:@"sign"];}//创建发起支付时的sige签名-(NSString *)createMD5SingForPay:(NSString *)appid_key partnerid:(NSString *)partnerid_key prepayid:(NSString *)prepayid_key package:(NSString *)package_key noncestr:(NSString *)noncestr_key timestamp:(UInt32)timestamp_key{ NSMutableDictionary *signParams = [NSMutableDictionary dictionary]; [signParams setObject:appid_key forKey:@"appid"]; [signParams setObject:noncestr_key forKey:@"noncestr"]; [signParams setObject:package_key forKey:@"package"]; [signParams setObject:partnerid_key forKey:@"partnerid"]; [signParams setObject:prepayid_key forKey:@"prepayid"]; [signParams setObject:[NSString stringWithFormat:@"%u",(unsigned int)timestamp_key] forKey:@"timestamp"]; NSMutableString *contentString =[NSMutableString string]; NSArray *keys = [signParams allKeys]; //按字母顺序排序 NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) { return [obj1 compare:obj2 options:NSNumericSearch]; }]; //拼接字符串 for (NSString *categoryId in sortedArray) { if ( ![[signParams objectForKey:categoryId] isEqualToString:@""] && ![[signParams objectForKey:categoryId] isEqualToString:@"sign"] && ![[signParams objectForKey:categoryId] isEqualToString:@"key"] ) { [contentString appendFormat:@"%@=%@&", categoryId, [signParams objectForKey:categoryId]]; } } //添加商户密钥key字段#warning 注意此处一定要添加上商户密钥 [contentString appendFormat:@"key=%@", WX_PartnerKey]; NSString *result = [self md5:contentString]; NSLog(@"result = %@",result); return result;}// MD5加密算法-(NSString *) md5:(NSString *)str{ const char *cStr = [str UTF8String]; //加密规则,因为逗比微信没有出微信支付demo,这里加密规则是参照安卓demo来得 unsigned char result[16]= "0123456789abcdef"; CC_MD5(cStr, (CC_LONG)strlen(cStr), result); //这里的x是小写则产生的md5也是小写,x是大写则md5是大写,这里只能用大写,逗比微信的大小写验证很逗 return [NSString stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", result[0], result[1], result[2], result[3], result[4], result[5], result[6], result[7], result[8], result[9], result[10], result[11], result[12], result[13], result[14], result[15] ];}@end
0 0
- ios 微信支付开发
- iOS微信支付开发
- iOS微信支付开发
- iOS 开发微信支付
- iOS开发 微信支付
- IOS开发--微信支付
- ios 微信支付开发
- 《IOS开发--微信支付》
- iOS开发- 微信支付
- IOS开发--微信支付
- iOS开发支付之微信支付
- iOS微信支付和支付宝支付开发详解
- iOS之微信支付开发
- IOS开发中的微信支付功能
- iOS开发之微信支付
- iOS微信支付开发--cocoachina muxiang
- iOS开发之微信支付
- 【iOS开发系列】微信支付
- ZooKeeper 原理及其在 Hadoop 和 HBase 中的应用
- Linux系统启动过程分析(基于CentOS 6.0系统)
- 数据库SQL 优化方案
- Java中将对象加入集合,实质是加入对象的引用的复制
- 浅谈cookie跨域的解决方案——document.domain
- iOS开发- 微信支付
- Spring Boot 入门
- c++ 从文件获取输入
- POJ-1018
- 素数距离问题
- JavaScript、JQuery-数组
- 小伙的java之旅——注解
- SylixOS DNS浅析
- 典型关联分析(CCA)原理总结