React Native之接入微信支付(Android&iOS)

本文章将介绍如何将微信支付App支付功能,接入到React Native应用中,包含Android、iOS,还有JS的调用示例三大模块。

Android

一、目录结构

文章所述将需要改动以下文件:

1
2
3
4
5
6
7
8
9
10
app/
└── build.gradle
app/src/main/
└── AndroidManifest.xml
app/src/main/java/com/xxx/mobile
└── App.kt
app/src/main/java/com/xxx/mobile/wxapi
├── WXPayEntryActivity.kt
├── WxpayModule.kt
└── WxpayPackage.kt

二、获取AppID

在微信开放平台上管理中心 / 应用详情获取得到AppID

三、获取应用签名(MD5)

具体获取步骤(示范):

1
2
3
4
5
$ keytool -list -v -keystore ~/.android/demo.keystore | awk '/MD5/{print $2}'
输入密钥库口令: 123456
8B:8A:62:7E:EC:16:00:E4:00:89:13:81:B0:84:C5:D2
$ python -c 'print("8B:8A:62:7E:EC:16:00:E4:00:89:13:81:B0:84:C5:D2".replace(":", "").lower())'
8b8a627eec1600e400891381b084c5d2

PS:需要的是MD5值,小写,而不是别的,之前因为其他同事把这个填写错了,导致每个用户只有微信支付一次,排查了半天…

在微信开放平台上管理中心 / 修改应用 / 修改开发信息填入信息:

三、集成微信支付SDK

1
2
3
4
dependencies {
implementation "com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+"
// ... more ...
}

PS: 这样子的集成方式,感觉要比直接导入.jar.aar都舒服不少呀。

四、创建wxapi包名

com.xx.mobile创建包名wxapi,注意此处包名一定要为wxapi,否则后续将无法处理回调

五、WxpayModule.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.xxx.mobile.wxapi

import android.util.Log
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
import com.tencent.mm.opensdk.modelpay.PayReq
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory

internal class WxpayModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
companion object {
val TAG = "WxpayModule"
var APP_ID = ""
var promise: Promise? = null
}

private val api: IWXAPI = WXAPIFactory.createWXAPI(reactContext, null)

override fun getName(): String {
return "Wxpay"
}

@ReactMethod
fun registerApp(APP_ID: String) { // 向微信注册
WxpayModule.APP_ID = APP_ID
api.registerApp(APP_ID)
}

@ReactMethod
fun isSupported(promise: Promise) { // 判断是否支持调用微信SDK
val isSupported = api.isWXAppInstalled
promise.resolve(isSupported)
}

@ReactMethod
fun pay(order: ReadableMap, promise: Promise) {
Log.d(TAG, "wxpay pay start")
WxpayModule.promise = promise
val request = PayReq()
request.appId = order.getString("appid") // 应用ID
request.partnerId = order.getString("partnerid") // 商户号
request.prepayId = order.getString("prepayid") // 预支付交易会话ID
request.packageValue = order.getString("package") // 扩展字段(暂定固定值Sign=WXPay)
request.nonceStr = order.getString("noncestr") // 随机字符串
request.timeStamp = order.getInt("timestamp").toString() // 时间戳 epoch
request.sign = order.getString("sign") // 签名,签名方式一定要与统一下单接口使用的一致
api.sendReq(request)
}
}

六、WxpayPackage.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.xxx.mobile.wxapi

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager

class WxpayPackage : ReactPackage {

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}

override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return listOf(WxpayModule(reactContext))
}
}

七、WXPayEntryActivity.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.xxx.mobile.wxapi

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import com.facebook.react.bridge.Arguments
import com.tencent.mm.opensdk.constants.ConstantsAPI
import com.tencent.mm.opensdk.modelbase.BaseReq
import com.tencent.mm.opensdk.modelbase.BaseResp
import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
import com.tencent.mm.opensdk.openapi.WXAPIFactory

class WXPayEntryActivity : Activity(), IWXAPIEventHandler {
companion object {
val TAG = "WXPayEntryActivity"
}

private var api: IWXAPI? = null

public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
api = WXAPIFactory.createWXAPI(this, WxpayModule.APP_ID)
api?.handleIntent(intent, this)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
api?.handleIntent(intent, this)
}

override fun onReq(req: BaseReq) {}

override fun onResp(resp: BaseResp) {
Log.d(TAG, "wxpay onPayFinish, errCode = " + resp.errCode)
if (resp.type == ConstantsAPI.COMMAND_PAY_BY_WX) {
val map = Arguments.createMap()
map.putInt("errCode", resp.errCode)
WxpayModule.promise!!.resolve(map)
finish()
}
}
}

八、App.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import com.xxx.mobile.wxapi.WxpayPackage // ← here
class App : Application(), ReactApplication {

private val mReactNativeHost = object : ReactNativeHost(this) {
override fun getUseDeveloperSupport(): Boolean {
return BuildConfig.DEBUG
}
override fun getPackages(): List<ReactPackage> {
return listOf(
MainReactPackage(),
// ...
WxpayPackage() // ← here
)
}
override fun getJSMainModuleName(): String {
return "index"
}
}
// ...
}

PS: 此App.kt实际是由MainApplication.java转为Kotlin语言的文件

九、AndroidManifest.xml

在AndroidManifest.xml中添加微信支付的activity:

1
2
<!--微信支付-->
<activity android:name=".wxapi.WXPayEntryActivity" android:exported="true"/>

iOS

一、目录结构

文章所述将涉及以下文件的改动:

1
2
3
4
5
6
7
8
9
10
├── AppDelegate.m
└── Info.plist
Wxapi
├── WXApi.h
├── WXApiObject.h
├── WechatAuthSDK.h
├── WxpayModule.h
└── WxpayModule.m
thirdparty/wechat
└── libWeChatSDK.a

二、集成微信SDK

SDK下载链接:WeChatSDK1.8.4.zip

WeChatSDK1.8.4.zip解压到thirdparty/wechat,目录结构如下:

1
2
thirdparty/wechat
└── libWeChatSDK.a

XcodeProjectTargetGeneralLinked Frameworks and Libraries,添加以下项:

三、创建Wxapi的Group

XcodeProjectNew GroupWxapi,并将SDK的头文件拷贝过去,拷贝完成后,目录结构如下:

1
2
3
4
Wxapi/
├── WXApi.h
├── WXApiObject.h
└── WechatAuthSDK.h

四、WxpayModule.h

1
2
3
4
5
6
7
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>
#import "WXApiObject.h"
#import "WXApi.h"

@interface WxpayModule : NSObject <RCTBridgeModule, WXApiDelegate>
@end

五、WxpayModule.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

#import <Foundation/Foundation.h>
#import "WxpayModule.h"

@implementation WxpayModule
{
RCTPromiseResolveBlock resolveBlock;
}

- (instancetype)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleWXPay:) name:@"WXPay" object:nil];
}
return self;
}

- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)handleWXPay:(NSNotification *)aNotification
{
NSString *errCode = [aNotification userInfo][@"errCode"];
NSLog(@"wxpay finished, errCode = %@", errCode);
resolveBlock(@{ @"errCode": errCode });
}

RCT_EXPORT_METHOD(registerApp:(NSString *)APP_ID) {
NSLog(@"wxpay registerApp, appID %@", APP_ID);
[WXApi registerApp:APP_ID]; //向微信注册
}

RCT_EXPORT_METHOD(pay:(NSDictionary *)order
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSLog(@"wxpay pay start");
resolveBlock = resolve;
//调起微信支付
PayReq *req = [[PayReq alloc] init];
req.partnerId = [order objectForKey:@"partnerid"];
req.prepayId = [order objectForKey:@"prepayid"];
req.nonceStr = [order objectForKey:@"noncestr"];
req.timeStamp = [[order objectForKey:@"timestamp"] intValue];
req.package = [order objectForKey:@"package"];
req.sign = [order objectForKey:@"sign"];
[WXApi sendReq:req];
}

RCT_REMAP_METHOD(isSupported, // 判断是否支持调用微信SDK
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
if (![WXApi isWXAppInstalled]) resolve(@NO);
else resolve(@YES);
}

RCT_EXPORT_MODULE(Wxpay);
@end

六、AppDelegate.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
return [WXApi handleOpenURL:url delegate:self];
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options
{
// 微信支付
if ([url.scheme isEqualToString:@"<YOUR_APPID>"]) {
return [WXApi handleOpenURL:url delegate:self];
}
return NO;
}

//ios9以后的方法
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
NSLog(@"%s url: %@, sourceApplication: %@", __PRETTY_FUNCTION__, url, sourceApplication);
// 微信支付 com.tencent.xin
if ([sourceApplication isEqualToString:@"com.tencent.xin"]) {
return [WXApi handleOpenURL:url delegate:self];
}
return NO;
}

#pragma mark - wx callback
- (void)onReq:(BaseReq *)req
{
// TODO Something
}

- (void)onResp:(BaseResp *)resp
{
//判断是否是微信支付回调 (注意是PayResp 而不是PayReq)
if ([resp isKindOfClass:[PayResp class]]) {
//发出通知 从微信回调回来之后,发一个通知,让请求支付的页面接收消息,并且展示出来,或者进行一些自定义的展示或者跳转
NSNotification *notification = [NSNotification notificationWithName:@"WXPay" object:nil userInfo:@{ @"errCode": @(resp.errCode) }];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
}

七、Info.plist

1) XcodeProjectTargetInfoURL Types,填入信息:

  • Identifier:weixin
  • URL Schemes: YOUR_APPID

2) XcodeProjectTargetInfoLSApplicationQueriesSchemes,添加两个item:

JavaScript

1)在React Native的App.js入口处,向微信注册:

1
2
3
componentWillMount() {
wxpay.registerApp('<YOUR_APPID>');
}

2)在需要发起微信支付的pay.js

1
2
3
4
5
6
7
wxpay.pay(params)
.then(v => {
__DEV__ && console.log('_wechatPay: result:', v);
})
.catch(e => {
console.warn('_wechatPay: error:', e);
});

参考链接:

  1. 一步一步将微信支付集成到 react-native 应用
  2. 官网:SDK与DEMO下载
  3. 官网: 业务流程
  4. 官网: 统一下单
  5. 官网: 调起支付接口
  6. 微信开放平台apk应用签名获取