这两天一直在搞微信支付,头都大了,感觉官方文档写的并不完善,还是有不少坑的,现在把自己做的过程总结一下.注:本文只集成了微信支付功能,不包含微信的其他功能.
1.下载微信终端SDK文件并搭建开发环境
-
SDK文件包括 libWeChatSDK.a,WXApi.h,WXApiObject.h,WechatAuthSDK.h 四个。
-
将SDK文件导入项目,可能会报错,报错的话可能是缺少以下包:CoreTelephony.framework,libsqlite3.0.tbd,libz.tbd,导入即可.
-
在Xcode中,选择你的工程设置项,选中“TARGETS”一栏,在“info”标签栏的“URL type“添加“URL scheme”为你所注册的应用程序id(如下图所示)。
2.在代码中使用开发工具包
- 在代码中向微信终端注册你的id。(如下所示,在 AppDelegate 的 didFinishLaunchingWithOptions 函数中向微信注册id)。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 创建窗口
self.window = [[UIWindow alloc] init];
self.window.frame = [UIScreen mainScreen].bounds;
// 设置根控制器
DBTabBarController *tabBarController = [[DBTabBarController alloc] init];
self.window.rootViewController = tabBarController;
// 显示窗口
[self.window makeKeyAndVisible];
// 向微信注册
[WXApi registerApp:DBAppid];
return YES;
}
- 重写AppDelegate的openURL方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)optionsOpenURL
{
// 这里仿照官方SDKSample的方法将delegate设置给一个WXApiManager单例,注意这里将delegate设置给谁,将来的onResp:方法就写在哪里
return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];
}
3.代码
- 支付入口
#import "WXApiRequestHandler.h"
#import "WXApi.h"
/**
* 微信支付
*/
- (IBAction)wXPay {
__block NSString *res = [WXApiRequestHandler jumpToBizPay];
if(res){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"支付失败" message:nil preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:okAction];
[self presentViewController:alertController animated:YES completion:nil];
}
}
- 调起支付,主要分两步:预支付和支付
#import "WXApi.h"
#import "PrePayParams.h"
#import "PayParams.h"
#import <MJExtension.h>
#import <XMLDictionary.h>
#import "NSString+Extension.h"
@implementation WXApiRequestHandler
static NSString * const kPrePayRequestURLStr = @"https://api.mch.weixin.qq.com/pay/unifiedorder";
+ (NSString *)jumpToBizPay {
__block NSString *result;
// 预支付参数模型
PrePayParams *prePayParams = [[PrePayParams alloc] init];
// 总金额,单位为分
prePayParams.total_fee = [NSString stringWithFormat:@"%d", 1];
prePayParams.nonce_str = [NSString ret32bitString];
// 获取当前时间
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *currentDateTime = [formatter stringFromDate:[NSDate date]];
prePayParams.out_trade_no = [NSString stringWithFormat:@"%@%d", currentDateTime, 1];
// 预支付参数字典
NSMutableDictionary *prePayParamsDict = prePayParams.mj_keyValues;
// 创建请求
NSMutableURLRequest *mRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:kPrePayRequestURLStr]];
[mRequest setHTTPMethod:@"POST"];
NSString *xmlStr = [self prePayXmlStrFromDictionary:prePayParamsDict];
[mRequest setHTTPBody:[xmlStr dataUsingEncoding:NSUTF8StringEncoding]];
// 通过request初始化task
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:mRequest completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
// 预支付返回参数字典
NSDictionary *dict = [[XMLDictionaryParser sharedInstance] dictionaryWithData:data];
if (dict) {
// 支付参数模型
PayParams *payParams = [[PayParams alloc] init];
payParams.prepayid = dict[@"prepay_id"];
payParams.noncestr = dict[@"nonce_str"];
NSMutableString *retcode = [dict objectForKey:@"retcode"];
if (retcode.intValue == 0 && payParams.prepayid) {
// 支付请求体
PayReq *request = [[PayReq alloc] init];
request.partnerId = payParams.partnerid;
request.prepayId= payParams.prepayid;
request.package = payParams.package;
request.nonceStr= payParams.noncestr;
request.timeStamp= payParams.timestamp.intValue;
request.sign= payParams.sign;
// 调起微信支付
[WXApi sendReq:request];
result = @"";
} else {
result = [dict objectForKey:@"retmsg"];
}
} else {
result = @"服务器返回错误,未获取到json对象";
}
} else {
result = @"服务器返回错误";
}
}];
[task resume];
return result;
}
+ (NSString *)prePayXmlStrFromDictionary:(NSDictionary *)dict
{
__block NSString *prePayXmlStr = @"<xml>";
[dict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, id _Nonnull obj, BOOL * _Nonnull stop) {
prePayXmlStr = [prePayXmlStr stringByAppendingFormat:@"<%@>%@</%@>", key, obj, key];
}];
prePayXmlStr = [prePayXmlStr stringByAppendingString:@"</xml>"];
return prePayXmlStr;
}
@end
- 支付结果回调
@implementation WXApiManager
+(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
{
//支付返回结果,实际支付结果需要去微信服务器端查询
NSString *strMsg, *strTitle = [NSString stringWithFormat:@"支付结果"];
switch (resp.errCode) {
case WXSuccess:
strMsg = @"支付结果:成功!";
DBLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
break;
default:
strMsg = [NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode, resp.errStr];
DBLog(@"错误,retcode = %d, retstr = %@", resp.errCode, resp.errStr);
break;
}
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:strTitle message:strMsg preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"好的" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:okAction];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
@end
- 两个model
@interface PrePayParams : NSObject
/** 应用ID */
@property (nonatomic, copy) NSString *appid;
/** 商户号 */
@property (nonatomic, copy) NSString *mch_id;
/** 随机字符串 */
@property (nonatomic, copy) NSString *nonce_str;
/** 商品描述,APP名字-实际商品名称 */
@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;
/** 签名 */
@property (nonatomic, copy) NSString *sign;
@end
#import "PrePayParams.h"
#import "NSString+Hash.h"
@implementation PrePayParams
- (NSString *)appid
{
return DBAppid;
}
- (NSString *)mch_id
{
return DBMch_id;
}
- (NSString *)body
{
return @"自己写";
}
- (NSString *)spbill_create_ip
{
return @"127.0.0.1";
}
- (NSString *)notify_url
{
return DBNotify_url;
}
- (NSString *)trade_type
{
return @"APP";
}
- (NSString *)sign
{
NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
[signParams setObject:self.appid forKey:@"appid"];
[signParams setObject:self.body forKey:@"body"];
[signParams setObject:self.mch_id forKey:@"mch_id"];
[signParams setObject:self.nonce_str forKey:@"nonce_str"];
[signParams setObject:self.notify_url forKey:@"notify_url"];
[signParams setObject:self.out_trade_no forKey:@"out_trade_no"];
[signParams setObject:self.spbill_create_ip forKey:@"spbill_create_ip"];
[signParams setObject:self.total_fee forKey:@"total_fee"];
[signParams setObject:self.trade_type forKey:@"trade_type"];
NSMutableString *contentString =[NSMutableString string];
NSArray *keys = [signParams allKeys];
//按字母顺序排序
NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *key in sortedKeys) {
if (![[signParams objectForKey:key] isEqualToString:@""])
{
[contentString appendFormat:@"%@=%@&", key, [signParams objectForKey:key]];
} else {
DBLog(@"PrePayParams的%@参数为空值", key);
}
}
//添加商户密钥key字段 API 密钥
[contentString appendFormat:@"key=%@", @"密钥"];
NSString *result = [contentString md5String].uppercaseString;//md5加密并转大写
return result;
}
@end
@interface PayParams : NSObject
@property (nonatomic, copy) NSString *appid; // 应用ID
@property (nonatomic, copy) NSString *partnerid; // 商户号
@property (nonatomic, copy) NSString *prepayid; // 预支付交易会话ID
@property (nonatomic, copy) NSString *package; // 扩展字段,暂填写固定值Sign=WXPay
@property (nonatomic, copy) NSString *noncestr; // 随机字符串
@property (nonatomic, copy) NSString *timestamp; // 时间戳
@property (nonatomic, copy) NSString *sign; // 签名
@end
#import "PayParams.h"
#import "NSString+Extension.h"
#import "NSString+Hash.h"
@implementation PayParams
- (NSString *)appid
{
return DBAppid;
}
- (NSString *)partnerid
{
return DBMch_id;
}
- (NSString *)package
{
return @"Sign=WXPay";
}
- (NSString *)timestamp
{
NSTimeInterval time = [[NSDate date] timeIntervalSince1970];
return [NSString stringWithFormat:@"%lld", (long long int)time];
}
- (NSString *)sign
{
NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
[signParams setObject:self.appid forKey:@"appid"];
[signParams setObject:self.partnerid forKey:@"partnerid"];
[signParams setObject:self.prepayid forKey:@"prepayid"];
[signParams setObject:self.package forKey:@"package"];
[signParams setObject:self.noncestr forKey:@"noncestr"];
[signParams setObject:self.timestamp forKey:@"timestamp"];
NSMutableString *contentString =[NSMutableString string];
NSArray *keys = [signParams allKeys];
//按字母顺序排序
NSArray *sortedKeys = [keys sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *key in sortedKeys) {
if (![[signParams objectForKey:key] isEqualToString:@""])
{
[contentString appendFormat:@"%@=%@&", key, [signParams objectForKey:key]];
}
}
//添加商户密钥key字段 API 密钥
[contentString appendFormat:@"key=%@", @"密钥"];
NSString *result = [contentString md5String].uppercaseString;//md5加密并转大写
return result;
}
@end
代码太多,其中还是有些坑的,实在是不想解释了,正在做的人应该能看懂吧.