這兩天一直在搞微信支付,頭都大了,感覺官方文檔寫的并不完善,還是有不少坑的,現(xiàn)在把自己做的過程總結(jié)一下.注:本文只集成了微信支付功能,不包含微信的其他功能.
1.下載微信終端SDK文件并搭建開發(fā)環(huán)境
-
SDK文件包括 libWeChatSDK.a,WXApi.h,WXApiObject.h,WechatAuthSDK.h 四個(gè)。
微信終端SDK文件 -
將SDK文件導(dǎo)入項(xiàng)目,可能會(huì)報(bào)錯(cuò),報(bào)錯(cuò)的話可能是缺少以下包:CoreTelephony.framework,libsqlite3.0.tbd,libz.tbd,導(dǎo)入即可.
可能缺少的包文件 -
在Xcode中,選擇你的工程設(shè)置項(xiàng),選中“TARGETS”一欄,在“info”標(biāo)簽欄的“URL type“添加“URL scheme”為你所注冊(cè)的應(yīng)用程序id(如下圖所示)。
URL type
2.在代碼中使用開發(fā)工具包
- 在代碼中向微信終端注冊(cè)你的id。(如下所示,在 AppDelegate 的 didFinishLaunchingWithOptions 函數(shù)中向微信注冊(cè)id)。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 創(chuàng)建窗口
self.window = [[UIWindow alloc] init];
self.window.frame = [UIScreen mainScreen].bounds;
// 設(shè)置根控制器
DBTabBarController *tabBarController = [[DBTabBarController alloc] init];
self.window.rootViewController = tabBarController;
// 顯示窗口
[self.window makeKeyAndVisible];
// 向微信注冊(cè)
[WXApi registerApp:DBAppid];
return YES;
}
- 重寫AppDelegate的openURL方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)optionsOpenURL
{
// 這里仿照官方SDKSample的方法將delegate設(shè)置給一個(gè)WXApiManager單例,注意這里將delegate設(shè)置給誰,將來的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];
}
}
- 調(diào)起支付,主要分兩步:預(yù)支付和支付
#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;
// 預(yù)支付參數(shù)模型
PrePayParams *prePayParams = [[PrePayParams alloc] init];
// 總金額,單位為分
prePayParams.total_fee = [NSString stringWithFormat:@"%d", 1];
prePayParams.nonce_str = [NSString ret32bitString];
// 獲取當(dāng)前時(shí)間
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"yyyyMMddHHmmss";
NSString *currentDateTime = [formatter stringFromDate:[NSDate date]];
prePayParams.out_trade_no = [NSString stringWithFormat:@"%@%d", currentDateTime, 1];
// 預(yù)支付參數(shù)字典
NSMutableDictionary *prePayParamsDict = prePayParams.mj_keyValues;
// 創(chuàng)建請(qǐng)求
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) {
// 預(yù)支付返回參數(shù)字典
NSDictionary *dict = [[XMLDictionaryParser sharedInstance] dictionaryWithData:data];
if (dict) {
// 支付參數(shù)模型
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) {
// 支付請(qǐng)求體
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;
// 調(diào)起微信支付
[WXApi sendReq:request];
result = @"";
} else {
result = [dict objectForKey:@"retmsg"];
}
} else {
result = @"服務(wù)器返回錯(cuò)誤,未獲取到j(luò)son對(duì)象";
}
} else {
result = @"服務(wù)器返回錯(cuò)誤";
}
}];
[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
- 支付結(jié)果回調(diào)
@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
{
//支付返回結(jié)果,實(shí)際支付結(jié)果需要去微信服務(wù)器端查詢
NSString *strMsg, *strTitle = [NSString stringWithFormat:@"支付結(jié)果"];
switch (resp.errCode) {
case WXSuccess:
strMsg = @"支付結(jié)果:成功!";
DBLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
break;
default:
strMsg = [NSString stringWithFormat:@"支付結(jié)果:失敗!retcode = %d, retstr = %@", resp.errCode, resp.errStr];
DBLog(@"錯(cuò)誤,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
- 兩個(gè)model
@interface PrePayParams : NSObject
/** 應(yīng)用ID */
@property (nonatomic, copy) NSString *appid;
/** 商戶號(hào) */
@property (nonatomic, copy) NSString *mch_id;
/** 隨機(jī)字符串 */
@property (nonatomic, copy) NSString *nonce_str;
/** 商品描述,APP名字-實(shí)際商品名稱 */
@property (nonatomic, copy) NSString *body;
/** 商戶訂單號(hào) */
@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的%@參數(shù)為空值", key);
}
}
//添加商戶密鑰key字段 API 密鑰
[contentString appendFormat:@"key=%@", @"密鑰"];
NSString *result = [contentString md5String].uppercaseString;//md5加密并轉(zhuǎn)大寫
return result;
}
@end
@interface PayParams : NSObject
@property (nonatomic, copy) NSString *appid; // 應(yīng)用ID
@property (nonatomic, copy) NSString *partnerid; // 商戶號(hào)
@property (nonatomic, copy) NSString *prepayid; // 預(yù)支付交易會(huì)話ID
@property (nonatomic, copy) NSString *package; // 擴(kuò)展字段,暫填寫固定值Sign=WXPay
@property (nonatomic, copy) NSString *noncestr; // 隨機(jī)字符串
@property (nonatomic, copy) NSString *timestamp; // 時(shí)間戳
@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加密并轉(zhuǎn)大寫
return result;
}
@end
代碼太多,其中還是有些坑的,實(shí)在是不想解釋了,正在做的人應(yīng)該能看懂吧.