React Native Expo版本的微信SDK。 您可以基于此框架实现微信登录、微信支付、分享到微信、打开微信小程序,唤起客服聊天等功能。 WeChat SDK for React Native Expo. You can implement WeChat login, WeChat Pay, sharing to WeChat, opening WeChat Mini Programs, and launching customer service chat based on this framework.
- ✅完整的微信SDK功能。
- ✅零原生代码配置。无需打开原生项目,无需修改原生代码,所有配置项均在
app.json中完成。 - ✅完整的TypeScript支持,完整的代码提示。
- ✅跨平台支持。同时支持安卓和iOS。在web上你也可以调用,只是不会有任何效果,不会报错。
- ✅售后保证。在使用过程中遇到任何问题都可以联系我,有Expo相关的技术问题也欢迎交流。
npx expo install expo-wechat
# 不带支付版本
# npx expo install expo-wechat-no-payiOS需要配置通用链接和URL Scheme。安卓上需要配置混淆规则。这些都可以通过app.json或app.config.js来配置。
什么是URL Scheme和通用链接?简单来说这就是iOS上微信授权完成后,跳回你的app的两种途径。 URL Scheme用于给你的应用注册一个独一无二的链接,使别的软件可以通过这个链接直接唤起你的App。 是微信回调起你的App的保底方案,当通用链接唤起失败后,微信会尝试使用URL Scheme来唤起你的App。这个URL Scheme就是微信开放平台给你的微信id,类似于
wx1234567890这种格式的。通用链接是微信首推的唤起微信和你的App的方案,当通用链接没有配置好的时候,才会回退到URL Scheme方案。 通用链接允许你向苹果注册一个URL地址,当访问这个地址的时候,系统优先唤起你的App,而不是网页。简单来说,它是一种比URL Scheme更好的唤起App的解决方案。
通用链接如何生成,以及如何向苹果注册,也许你需要参照一下苹果官方文档。
如果对于
app.json和app.config.js有疑问,请参阅Expo Configure with app config 文档。其实都是一个东西,只是后者能让你写JS代码,更灵活一些。
在app.json或app.config.js添加如下配置:
"expo": {
"scheme": [
// 微信开放平台给你的微信id,类似于wx1234567890这种格式的。
"wx1234567890"
],
"ios": {
"associatedDomains": [
// 你的通用链接地址,前缀applinks是固定的,需要保证后边的域名和苹果,以及微信上注册的一致。
"applinks:example.com"
]
},
"plugins": [
[
// expo-build-properties是另外一个expo官方的插件,它能让你自定义很多构建阶段的参数,包括自定义混淆规则和maven仓库等。
// 记得先npx expo install expo-build-properties
"expo-build-properties",
{
"android": {
"extraProguardRules": "把下面的混淆规则放到这里",
},
}
],
"expo-wechat"
]
}-keep class com.tencent.mm.opensdk.** {
*;
}
-keep class com.tencent.wxop.** {
*;
}
-keep class com.tencent.mm.sdk.** {
*;
}
请注意,由于包含了自定义的原生代码,无法在expo go中直接使用。你应该使用npx expo run:android或者npx expo run:ios,编译原生app。详情参见官方DevClient文档。
import ExpoWeChat from 'expo-wechat'
/// 初始化微信SDK。一般都在用户接受了隐私政策后,调用此方法。
const result = await ExpoWeChat.registerApp(wechatAppId, universalLink);| 属性名 | 类型 | 说明 |
|---|---|---|
isRegistered |
boolean |
是否已经成功调用registerApp方法 |
registerApp(appId: string, universalLink: string): Promise<boolean>初始化微信SDK。返回初始化结果。
参数:
appId: 微信App IDuniversalLink: 通用链接地址(iOS必需)
返回值: Promise<boolean> - 初始化是否成功
isWXAppInstalled(): Promise<boolean>检查用户是否已安装微信客户端。
返回值: Promise<boolean> - 是否已安装微信
getApiVersion(): Promise<string>获取微信SDK的版本号。
返回值: Promise<string> - SDK版本号
getWXAppInstallUrl(): Promise<string | null>获取微信安装的URL。
返回值: Promise<string | null> - 安装URL或null
openWXApp(): Promise<boolean>打开微信App。
返回值: Promise<boolean> - 打开是否成功
checkUniversalLinkReady(): Promise<void>启动微信自检流程,打印自检日志。
注意: iOS Only
sendAuthRequest(
scope: "snsapi_userinfo" | string,
state: string
): Promise<boolean>发送微信授权登录请求。
参数:
scope: 微信scope字段,如snsapi_userinfostate: 微信state字段,用于维持请求和回调的状态
返回值: Promise<boolean> - 请求是否发送成功
注意: 此方法返回的是请求发送结果,不是授权结果。授权结果需通过监听onAuthResult事件获取。
sendAuthByQRRequest(options: AuthByQROptions): Promise<string>发送微信扫码登录请求。
参数:
interface AuthByQROptions {
appId: string; // 微信App ID
appSecret: string; // 微信App Secret
scope: "snsapi_userinfo" | string; // 授权范围
schemeData?: string; // 可选的scheme数据
}返回值: Promise<string> - 二维码base64编码的图片数据
shareText(text: string, scene: ShareScene): Promise<boolean>分享文字到微信。
参数:
text: 要分享的文字内容scene: 分享目标场景 ('session' | 'timeline' | 'favorite' | 'status' | 'specifiedContact')
返回值: Promise<boolean> - 分享请求是否发送成功
shareImage(options: ShareImageOptions): Promise<boolean>分享图片到微信。
参数:
interface ShareImageOptions {
base64OrImageUri: string; // 图片内容(URI或base64)
scene: ShareScene; // 分享场景
thumbBase64OrImageUri?: string; // 缩略图内容(可选)
imageDataHash?: string | null; // 图片哈希值(可选)
miniProgramId?: string | null; // 小程序原始id(可选)
miniProgramPath?: string | null; // 小程序路径(可选)
}返回值: Promise<boolean> - 分享请求是否发送成功
shareFile(
base64OrFileUri: string,
title: string,
scene: ShareScene
): Promise<boolean>分享文件到微信。
参数:
base64OrFileUri: 文件内容(URI或base64)title: 文件标题scene: 分享目标场景
返回值: Promise<boolean> - 分享请求是否发送成功
shareMusic(options: ShareMusicOptions): Promise<boolean>分享音乐到微信。
参数:
interface ShareMusicOptions {
musicWebpageUrl: string; // 音乐网页URL
musicFileUri: string; // 音乐文件URI
singerName: string; // 歌手名称
duration: number; // 音乐时长(秒)
scene: ShareScene; // 分享场景
songLyric?: string; // 歌词(可选)
hdAlbumThumbFilePath?: string; // 高清专辑缩略图文件路径(安卓)
hdAlbumThumbBase64OrImageUri?: string; // 高清专辑缩略图(iOS)
hdAlbumThumbFileHash?: string; // 高清专辑缩略图文件哈希值
albumName?: string; // 专辑名称
title?: string; // 标题(可选)
description?: string; // 描述(可选)
thumbBase64OrImageUri?: string; // 缩略图(可选)
}返回值: Promise<boolean> - 分享请求是否发送成功
shareVideo(options: ShareVideoOptions): Promise<boolean>分享视频到微信。
参数:
interface ShareVideoOptions {
videoUri: string; // 视频文件URI
scene: ShareScene; // 分享场景
lowQualityVideoUri?: string; // 低质量视频URI(可选)
thumbBase64OrImageUri?: string; // 缩略图(可选)
title?: string; // 标题(可选)
description?: string; // 描述(可选)
}返回值: Promise<boolean> - 分享请求是否发送成功
shareWebpage(options: ShareWebpageOptions): Promise<boolean>分享网页到微信。
参数:
interface ShareWebpageOptions {
url: string; // 网页URL
scene: ShareScene; // 分享场景
title?: string; // 标题(可选)
description?: string; // 描述(可选)
thumbBase64OrImageUri?: string; // 缩略图(可选)
extraInfo?: string; // 额外信息(可选)
}返回值: Promise<boolean> - 分享请求是否发送成功
shareMiniProgram(options: ShareMiniProgramOptions): Promise<boolean>分享小程序到微信。
参数:
interface ShareMiniProgramOptions {
id: string; // 小程序原始id
type: WeChatMiniProgramType; // 小程序类型 ('release' | 'test' | 'preview')
path?: string; // 小程序路径(可选)
scene: ShareScene; // 分享场景
webpageUrl?: string; // 网页URL(可选)
title?: string; // 标题(可选)
description?: string; // 描述(可选)
thumbBase64OrImageUri?: string; // 缩略图(可选)
withShareTicket?: boolean; // 是否携带shareTicket(可选)
}返回值: Promise<boolean> - 分享请求是否发送成功
launchMiniProgram(options: LaunchMiniProgramOptions): Promise<boolean>打开微信小程序。
参数:
interface LaunchMiniProgramOptions {
id: string; // 小程序原始id
type: WeChatMiniProgramType; // 小程序类型
path?: string; // 小程序路径(可选)
extraData?: string; // 额外数据(可选)
}返回值: Promise<boolean> - 打开请求是否发送成功
pay(options: PayOptions): Promise<boolean>发起微信支付。
参数:
interface PayOptions {
partnerId: string; // 商户号
prepayId: string; // 预支付交易会话ID
nonceStr: string; // 随机字符串
timeStamp: number; // 时间戳
sign: string; // 签名
package: string; // 扩展字段
extraData: string; // 额外数据
}返回值: Promise<boolean> - 支付请求是否发送成功
openWeChatCustomerServiceChat(cropId: string, url: string): Promise<boolean>打开微信客服聊天。
参数:
cropId: 企业IDurl: 客服URL
返回值: Promise<boolean> - 打开请求是否发送成功
sendSubscribeMessage(
scene: number,
templateId: string,
reserved: string
): Promise<boolean>发送订阅消息。
参数:
scene: 场景templateId: 模板IDreserved: 保留字段
返回值: Promise<boolean> - 发送请求是否成功
openWebView(url: string): Promise<boolean>在微信内置 WebView 中打开指定链接。
参数:
url: 要打开的链接,建议使用有效的http(s)URL
返回值: Promise<boolean> - 打开请求是否发送成功
调用API返回的Promise仅仅代表调用的成功与否,不代表最终的微信返回结果。对于需要观测结果的API,应当使用事件监听的方式来实现:
| 事件名 | 说明 | 回调参数类型 |
|---|---|---|
onQRCodeAuthGotQRCode |
二维码登录时,得到二维码图片 | { image: string } |
onQRCodeAuthUserScanned |
二维码登录时,用户成功扫描二维码 | void |
onQRCodeAuthResult |
二维码登录结果 | { errorCode: number; authCode: string } |
onShowMessageFromWeChat |
从微信打开应用时收到的消息 | ShowMessageFromWeChatPayload |
onAuthResult |
授权登录结果 | AuthResultPayload |
onPayResult |
支付结果 | PayResultPayload |
onLaunchMiniProgramResult |
打开小程序结果 | { extraInfo?: string } |
export type AuthResultPayload = {
code: string; // 授权码
state: string; // 状态值
url: string; // 完整URL
authResult: boolean; // 授权是否成功
lang: string; // 语言
country: string; // 国家
errorCode: number; // 错误码
errorMessage: string; // 错误信息
openId: string; // 开放ID
transaction: string; // 事务ID
};export type PayResultPayload = {
prepayId?: string; // 预支付ID
returnKey?: string; // 返回键
extraInfo?: string; // 额外信息
errorCode: number; // 错误码
errorMessage: string; // 错误信息
openId: string; // 开放ID
transaction: string; // 事务ID
};export enum ResultErrorCode {
ok = 0, // 成功
common = -1, // 普通错误
userCancel = -2, // 用户取消
sentFailed = -3, // 发送失败
authDenied = -4, // 授权被拒绝
unsupported = -5, // 不支持的操作
ban = -6, // 被禁止
}import ExpoWeChat from 'expo-wechat'
import { useEvent, useEffect } from 'expo'
// 初始化
const initializeWeChat = async () => {
const result = await ExpoWeChat.registerApp('wx1234567890', 'https://example.com/');
console.log('微信SDK初始化结果:', result);
};
// 使用useEvent监听授权结果
const authResult = useEvent(ExpoWeChat, 'onAuthResult');
useEffect(() => {
if (authResult) {
if (authResult.errorCode === 0) {
console.log('授权成功!');
console.log('授权码:', authResult.code);
console.log('状态:', authResult.state);
// 在这里使用code向服务器换取token
} else {
console.log('授权失败:', authResult.errorMessage);
}
}
}, [authResult]);
// 发送授权请求
const handleWeChatLogin = async () => {
// 检查微信是否安装
const isInstalled = await ExpoWeChat.isWXAppInstalled();
if (!isInstalled) {
alert('请先安装微信');
return;
}
// 发送授权请求
const requestResult = await ExpoWeChat.sendAuthRequest('snsapi_userinfo', 'state123');
console.log('授权请求发送结果:', requestResult);
};import ExpoWeChat from 'expo-wechat'
import { useEvent, useEffect } from 'expo'
// 监听二维码相关事件
const qrCodeResult = useEvent(ExpoWeChat, 'onQRCodeAuthGotQRCode');
const qrCodeScanResult = useEvent(ExpoWeChat, 'onQRCodeAuthUserScanned');
const qrCodeLoginResult = useEvent(ExpoWeChat, 'onQRCodeAuthResult');
useEffect(() => {
if (qrCodeResult) {
console.log('二维码生成成功,可以显示了');
// 这里可以使用qrCodeResult.image来渲染二维码
}
}, [qrCodeResult]);
useEffect(() => {
if (qrCodeScanResult) {
console.log('用户已扫描二维码,请确认');
}
}, [qrCodeScanResult]);
useEffect(() => {
if (qrCodeLoginResult) {
if (qrCodeLoginResult.errorCode === 0) {
console.log('二维码登录成功!');
console.log('授权码:', qrCodeLoginResult.authCode);
// 在这里使用authCode向服务器换取token
} else {
console.log('二维码登录失败:', qrCodeLoginResult.errorCode);
}
}
}, [qrCodeLoginResult]);
// 生成登录二维码
const generateLoginQRCode = async () => {
try {
const qrCode = await ExpoWeChat.sendAuthByQRRequest({
appId: 'wx1234567890',
appSecret: 'your_app_secret',
scope: 'snsapi_userinfo'
});
console.log('二维码生成成功');
} catch (error) {
console.error('二维码生成失败:', error);
}
};import ExpoWeChat from 'expo-wechat'
import { useEvent, useEffect } from 'expo'
// 监听支付结果
const payResult = useEvent(ExpoWeChat, 'onPayResult');
useEffect(() => {
if (payResult) {
if (payResult.errorCode === 0) {
console.log('支付成功!');
// 支付成功后的处理逻辑
} else {
console.log('支付失败:', payResult.errorMessage);
// 支付失败后的处理逻辑
}
}
}, [payResult]);
// 发起支付
const handlePay = async () => {
try {
// 从服务器获取支付参数
const payParams = await getPayParamsFromServer();
const result = await ExpoWeChat.pay({
partnerId: payParams.partnerId,
prepayId: payParams.prepayId,
nonceStr: payParams.nonceStr,
timeStamp: payParams.timeStamp,
sign: payParams.sign,
package: payParams.package,
extraData: payParams.extraData
});
console.log('支付请求发送结果:', result);
} catch (error) {
console.error('支付失败:', error);
}
};import ExpoWeChat from 'expo-wechat'
// 分享网页到微信会话
const shareToWeChat = async () => {
try {
const result = await ExpoWeChat.shareWebpage({
url: 'https://example.com',
title: '分享标题',
description: '分享描述内容',
thumbBase64OrImageUri: 'base64编码的图片或图片URI',
scene: 'session'
});
console.log('分享请求发送结果:', result);
} catch (error) {
console.error('分享失败:', error);
}
};开启日志调试,首先你需要调用ExpoWeChat.startLogByLevel('verbose')方法,即可打开全部日志。
为了将日志打印到JS控制台,你需要监听onLog事件,然后将其打印出来即可,具体可以参考example下的App.tsx文件。
注意: 所有API返回的Promise仅表示调用是否成功发送,不代表最终操作结果。需要通过相应的事件监听获取实际操作结果。
useEvent是expo提供的一个模块事件监听的工具,你完全可以使用ExpoWeChat.addListener('onAuthResult', (result) => {})这种语法来监听事件结果,但千万不要忘记在组件卸载时移除事件监听,否则会导致内存泄漏。
克隆本仓库,并启动Example示例项目的步骤如下
- 克隆本仓库后,在根目录执行
npm i - 在根目录执行
npm run build plugin,然后按下ctrl + c退出命令即可。 - 进入example文件夹,执行
npm i安装依赖。 - 启动之前,请在
.env文件内配置微信AppId和Key,去app.json文件内配置scheme和associatedDomains,切记确保与微信后台配置的一致。
本框架积极维护,如有任何问题,欢迎提交issue或者PR。也欢迎进群交流:682911244。
- 实现选择发票功能
- 发布不带支付功能的SDK
- 完善文档
报错 could not find module ExpoModulesCore for target '86_64-apple-ios-simulator'; found: arm64-apple-ios-simulator
以下方案是我们的一些经验,你可以都试一下:
- 启动Rosetta模拟器。
- 使用真机运行项目。
- 升级Xcode到16.4以及以后。
- 使用expo-fix-ios-simulator-arch插件来自动帮你解决这个问题。
原因分析 微信授权结束后,会通过URL Scheme和通用链接来回到你的应用,并把授权结果带在路径上作为参数。比如
/wxauth/wx1234567890/oauth如果你使用了expo-router,或者配置好了React Navigation的deep linking,那么导航器会尝试跳转到这个URL对应的路由下,由于路由不存在,就会显示404页面。
既然这个问题产生的原因其实就是微信打开你的app的链接和你准备接收回调的链接没有“对准”,那么解决方案就是你想办法让它俩对准即可。在通用链接上和路由配置上想想办法。
expo-router提供了一个API,可以让你拦截所有deep link,并手动控制是否处理。详见Customizing links。
简单来说,你需要新建一个+native-intent.tsx文件,按照文档导出一个函数,并根据条件,重定向到你的登录页面即可。
本框架参考了许多react-native-wechat-lib的实现,实现了基本上所有的API的功能,在此基础上,极大的简化了配置流程,并使用了最新的微信SDK,感谢前人!