iOS 集成微信支付

更新日期:20200522
微信支付SDK 版本:1.8.6

iOS 接入指南

本文讲解 iOS 接入微信SDK,仅记录关键部分代码,其余在官方文档或网络文章中都已详述备尽,此处不再赘述。

1. 向微信注册你的应用程序 id

(略。。。)

2. 下载微信终端 SDK 文件

集成微信 SDK 有两种方式:

  1. 手动集成,下载 SDK 然后拖拽相关库文件到你的项目中;
  2. 通过 CocoaPods 集成:
    pod 'WechatOpenSDK', '~> 1.8.6'
    

3. 开发环境搭建

3.1 设置 TARGETS ,在 URL Types 中添加应用程序 id。

(略。。。)

3.2 设置微信白名单

在 Xcode 中,选择你的工程设置项,选中 “TARGETS” 一栏,在 “info” 标签栏的 “LSApplicationQueriesSchemes“ 添加 weixinwechatweixinULAPI 字段:

⚠️ 设置这个步骤的作用是用于判断微信是否安装([WXApi isWXAppInstalled]

详细可以参见 LSApplicationQueriesSchemes-- 关于 info.plist 第三方登录 添加 URL Schemes 白名单

4. 在项目中添加代码

为了使项目代码更清晰简洁(解耦合),我创建了一个范畴(Category)类专门存放微信支付相关的配置代码:

4.1 AppDelegate+WechatPayService.h

#import "AppDelegate.h"

NS_ASSUME_NONNULL_BEGIN

/**
 集成微信支付
 */
@interface AppDelegate (WechatPayService)

// 配置方法,用于向微信终端注册应用 id
- (void)hql_configureForWechatPay;

@end

NS_ASSUME_NONNULL_END

4.2 AppDelegate+WechatPayService.m

#import "AppDelegate+WechatPayService.h"
#import <WechatOpenSDK/WXApi.h> // 引入微信 SDK

@interface AppDelegate () <WXApiDelegate> // 遵守微信 SDK 协议

@end

@implementation AppDelegate (WechatPayService)

#pragma mark - Public

- (void)hql_configureForWechatPay {
    //向微信注册
    //注:WechatAppId 是常量,已经写在了预编译配置文件中
    //#define WechatAppId @"wx12345...67890"
    [WXApi registerApp:WechatAppId];
}

#pragma mark - WeChatPay

// NOTE: 9.0以后使用新API接口
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options {
    return [WXApi handleOpenURL:url delegate:(id<WXApiDelegate>)self];
}

// NOTE: 20200522 更新,发现集成最新的 1.8.6 SDK 时,只有通过以下的方法才能获取到回调!
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler
{
    [WXApi handleOpenURL:userActivity.webpageURL delegate:(id<WXApiDelegate>)self];
    return YES;
}

/*! @brief 发送一个sendReq后,收到微信的回应
 *
 * 收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。
 * 可能收到的处理结果有SendMessageToWXResp、SendAuthResp等。
 * @param resp具体的回应内容,是自动释放的
 */
-(void) onResp:(BaseResp*)resp {
    // 微信支付成功/失败,发起通知查询
    // 注:HQLWechatPayOnResponceNotification 通知方法名是常量,统一写在配置文件中
    // #define HQLWechatPayOnResponceNotification @"HQLWechatPayOnResponceNotification"
    [[NSNotificationCenter defaultCenter] postNotificationName:HQLWechatPayOnResponceNotification
                                                        object:(BaseReq *)resp];
}

@end

4.3 AppDelegate.m

AppDelegate+WechatPayService 文件写好后,这里可以直接使用了。

#import "AppDelegate.h"
#import "AppDelegate+WechatPayService.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [self hql_configureForWechatPay]; // 是的,只有一行配置代码
    return YES;
}

4.4 开启调试模式

如果你是首次集成微信 SDK,现在还可以使用 SDK 中的自检函数,用于排查问题:

- (void)hql_configureForWechatPay {
    
    //在 register 之前打开 log, 后续可以根据 log 排查问题
    [WXApi startLogByLevel:WXLogLevelDetail logBlock:^(NSString * _Nonnull log) {
        NSLog(@"WeChat Log Info: %@",log);
    }];
    
    //向微信注册
    [WXApi registerApp:@"微信返回给你的 App ID" universalLink:@"Universal Link"];
    
    //调用自检函数
    /*! @brief 测试函数,用于排查当前 App 通过 Universal Link 方式分享到微信的流程
       注意1:  调用自检函数之前必须要先调用 registerApp:universalLink 接口, 并确认调用成功
       注意2:  自检过程中会有 Log 产生,可以先调用 startLogByLevel 函数,根据 Log 排查问题
       注意3:  会多次回调 block
       注意4:  仅用于新接入 SDK 时调试使用,请勿在正式环境的调用
    *
    *  当completion回调的 step 为 WXULCheckStepFinal 时,表示检测通过,Universal Link 接入成功
    *  @param completion 回调Block
    */
    [WXApi checkUniversalLinkReady:^(WXULCheckStep step, WXCheckULStepResult* result) {
        
        NSDictionary *resultInfo = @{
            @"当前检查步骤": @(step),
            @"当前检查结果": [NSString stringWithFormat:@"%u",result.success],
            @"Error Info": result.errorInfo,
            @"修复建议": result.suggestion
        };
        NSLog(@"%@",resultInfo);
    }];
}

5. 发起微信支付代码

交互数据模型

先来看一下商户服务端返回的 JSON 数据格式:

{
    appid = "wx12345...678906"; (应用 ID)
    noncestr = "FS7JBpYQ5TzkyfcV"; (随机字符串)
    package = "Sign=WXPay"; (扩展字段,默认值)
    partnerid = "12345674321"; (商户号)
    prepayid = "wx2530149711249kd7b531c00a2446020522"; (预支付交易会话 ID)
    sign = "BEE9FA6FCD14D286CCCD4EE7DC74578B"; (签名)
    timestamp = 1545704053 (时间戳,精确到秒,10位!!!)
}

然后写一个模型类来映射 JSON 数据,如下,JSON 解析可以使用 YYModel 库。

HQLWechatPayRequestModel.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/**
 微信支付,服务器返回支付参数模型
 */
@interface HQLWechatPayRequestModel : NSObject

/** 应用 ID */
@property (nonatomic, copy, readonly) NSString *appid;
/** 商家向财付通申请的商家 ID */
@property (nonatomic, copy, readonly) NSString *partnerid;
/** 预支付订单 */
@property (nonatomic, copy, readonly) NSString *prepayid;
/** 商家根据财付通文档填写的数据和签名,默认值为 Sign=WXPay */
@property (nonatomic, copy, readonly) NSString *package;
/** 随机串,防重发 */
@property (nonatomic, copy, readonly) NSString *noncestr;
/** 时间戳,防重发 */
@property (nonatomic, assign, readonly) UInt32 timestamp;
/** 商家根据微信开放平台文档对数据做的签名 */
@property (nonatomic, copy, readonly) NSString *sign;

@end

NS_ASSUME_NONNULL_END

HQLWechatPayRequestModel.m

#import "HQLWechatPayRequestModel.h"

@interface HQLWechatPayRequestModel ()

@property (nonatomic, copy, readwrite) NSString *appid;
@property (nonatomic, copy, readwrite) NSString *partnerid;
@property (nonatomic, copy, readwrite) NSString *prepayid;
@property (nonatomic, copy, readwrite) NSString *package;
@property (nonatomic, copy, readwrite) NSString *noncestr;
@property (nonatomic, assign, readwrite) UInt32 timestamp;
@property (nonatomic, copy, readwrite) NSString *sign;

@end

@implementation HQLWechatPayRequestModel

#pragma mark - NSObject

- (NSString *)description {
    return [self modelDescription];
}

@end

发起微信支付

#import "HQLWeChatPayViewController.h"

// Frameworks
#import <WechatOpenSDK/WXApi.h>
#import <WechatOpenSDK/WXApiObject.h>

// Models
#import "HQLWechatPayRequestModel.h"

@implementation HQLThirdPartyPayViewController

#pragma mark - Lifecycle

- (void)dealloc {
    // 移除微信支付通知
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)viewDidLoad {
    [super viewDidLoad];
  
    // 添加微信支付通知
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(respondsToWechatPayNotification:)
                                                 name:HQLWechatPayOnResponceNotification
                                               object:nil];
}

#pragma mark - Private

// 判断用户设备是否支持微信支付
- (BOOL)isSupportWechatPay {
    // 1.判断是否安装微信
    if (![WXApi isWXAppInstalled]) {
        // 业务代码,提示微信未安装...
        return NO;
    }
    // 2.判断微信的版本是否支持最新API
    if (![WXApi isWXAppSupportApi]) {
        // 业务代码,提示微信当前版本不支持此功能...
        return NO;
    }
    return YES;
}

// 响应微信支付回调通知
- (void)respondsToWechatPayNotification:(NSNotification *)notification {
    BaseResp *responds = notification.object;
    switch (responds.errCode) {
        case WXSuccess: {
            // 1. 微信支付成功
            // ...
            break;
        }
        case WXErrCodeCommon: {
            // 2. 微信支付失败
            // ...
            break;
        }
        case WXErrCodeUserCancel: {
            // 3. 用户点击取消并返回
            // ...
            break;
        }
        default: {
            break;
        }
    }
}

// 发起支付请求
- (void)dealWithOrderPay {
    // 业务逻辑,先向服务器发起预支付请求,服务器返回支持订单信息。
    // ...
    
    // 先判断是否支持微信支付
    if ([self isSupportWechatPay]) {
        // 解析服务端返回的支付参数
        NSDictionary *payDictionary;
        // 使用 YYModel 将 JSON 数据转化为数据模型 HQLWechatPayRequestModel
        HQLWechatPayRequestModel *payRequestModel = [HQLWechatPayRequestModel modelWithJSON:payDictionary];
        // 向微信终端发起支付的消息结构体
        PayReq *request = [[PayReq alloc] init];
        request.partnerId = payRequestModel.partnerid;
        request.prepayId = payRequestModel.prepayid;
        request.package = payRequestModel.package;
        request.nonceStr = payRequestModel.noncestr;
        request.timeStamp = payRequestModel.timestamp;
        request.sign = payRequestModel.sign;
        // 发起微信支付
        [WXApi sendReq:request];
    }
}

@end

iOS 应用中集成微信支付的交互流程:

  1. 「你的应用」在「支付页面」通过 sendReq: 方法向「微信 APP」发送支付消息;
  2. 系统判断「微信 APP」已经安装,并自动跳转到「微信 APP」引导用户支付;
  3. 用户支付完成后,「微信 APP」自动跳回「你的应用」,「你的应用」通过实现 WXApiDelegate 代理中的 onResp: 方法接收微信返回的支付消息;
  4. 「你的应用」收到的返回消息是通过 AppDelegate 来接收的, AppDelegate 需要通过 NSNotificationCenter 通知中心将返回的数据发送给 「支付页面」,「支付页面」再进行相关的业务处理。

That's all.

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容