From 86bc8bd0317eb3c0df83087e4affefeec9351cf1 Mon Sep 17 00:00:00 2001 From: Norcy Date: Mon, 19 Aug 2024 16:51:41 +0800 Subject: [PATCH] =?UTF-8?q?JS=20=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/wechatlib/WeChatLibModule.java | 2 +- ios/WechatLib.mm | 2 +- src/index.d.ts | 10 ++ src/index.js | 153 +++++++++++++++++- 4 files changed, 162 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/wechatlib/WeChatLibModule.java b/android/src/main/java/com/wechatlib/WeChatLibModule.java index 8a00f2b..9c1bd99 100644 --- a/android/src/main/java/com/wechatlib/WeChatLibModule.java +++ b/android/src/main/java/com/wechatlib/WeChatLibModule.java @@ -187,7 +187,7 @@ public class WeChatLibModule extends ReactContextBaseJavaModule implements IWXAP WritableMap map = Arguments.createMap(); map.putString("authCode", var2); map.putInt("errCode", var1.getCode()); - callback.invoke(map); + callback.invoke(null, map); } }); } diff --git a/ios/WechatLib.mm b/ios/WechatLib.mm index c94f3f6..ce0f9f4 100644 --- a/ios/WechatLib.mm +++ b/ios/WechatLib.mm @@ -695,7 +695,7 @@ RCT_EXPORT_METHOD(authByScan:(NSString *)appid - (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode { NSLog(@"onAuthFinish"); if (self.scanCallback) { - self.scanCallback(@[@{@"authCode": authCode?:@"", @"errCode": @(errCode)}]); + self.scanCallback(@[[NSNull null], @{@"authCode": authCode?:@"", @"errCode": @(errCode)}]); self.scanCallback = nil; } } diff --git a/src/index.d.ts b/src/index.d.ts index 43c09c9..49756ce 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -46,10 +46,20 @@ declare module 'react-native-wechat-lib' { state?: string; returnKey?: string; } + export interface ScanLoginResp { + nickname?: string; + headimgurl?: string; + openid?: string; + unionid?: string; + errCode?: number; + errStr?: string; + } export function sendAuthRequest( scope: string | string[], state?: string ): Promise; + export function authByScan(appId: string, appSecret: string, onQRGet: (qrcode: string)=>void): Promise; + export interface ShareMetadata { type: | 'news' diff --git a/src/index.js b/src/index.js index c1e98fe..a822b50 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,8 @@ 'use strict'; -import { DeviceEventEmitter, NativeModules, Platform } from 'react-native'; import { EventEmitter } from 'events'; +import { sha1 } from 'js-sha1'; +import { DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform } from 'react-native'; let isAppRegistered = false; let { WeChat, WechatLib } = NativeModules; @@ -170,6 +171,152 @@ const nativeSubscribeMessage = wrapApi(WeChat.subscribeMessage); const nativeChooseInvoice = wrapApi(WeChat.chooseInvoice); const nativeShareFile = wrapApi(WeChat.shareFile); +const nativeScan = wrapApi(WeChat.authByScan); + +// https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html +const getAccessToken = async (WeiXinId, WeiXinSecret) => { + let url = + 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + + WeiXinId + + '&secret=' + + WeiXinSecret; + const response = await fetch(url); + const res = await response.json(); + return res.access_token; +}; + +const getSDKTicket = async (accessToken) => { + let url = + 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=2&access_token=' + + accessToken; + const response = await fetch(url); + const res = await response.json(); + return res.ticket; +}; + +const createSignature = ( + WeiXinId, + nonceStr, + sdkTicket, + timestamp +) => { + const origin = + 'appid=' + + WeiXinId + + '&noncestr=' + + nonceStr + + '&sdk_ticket=' + + sdkTicket + + '×tamp=' + + timestamp; + const ret = sha1(origin); + // console.log('wx scan signature', origin, ret); + return ret; +}; + +const getUserInfo = ( + WeiXinId, + WeiXinSecret, + code, + callback +) => { + let accessTokenUrl = + 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' + + WeiXinId + + '&secret=' + + WeiXinSecret + + '&code=' + + code + + '&grant_type=authorization_code'; + fetch(accessTokenUrl) + .then((res) => { + return res.json(); + }) + .then((res) => { + // console.log('wechat get access code success: ', res.access_token); + let userInfoUrl = + 'https://api.weixin.qq.com/sns/userinfo?access_token=' + + res.access_token + + '&openid=' + + res.openid; + fetch(userInfoUrl) + .then((res2) => { + return res2.json(); + }) + .then((json) => { + // console.log('wechat get user info success: ', json); + callback({ + nickname: json.nickname, + headimgurl: json.headimgurl, + openid: json.openid, + unionid: json.unionid, + }); + }) + .catch((e) => { + console.warn('wechat get user info fail ', e); + callback({ error: e }); + }); + }) + .catch((e) => { + console.warn('wechat get access code fail ', e); + callback({ error: e }); + }); +}; + +const generateObjectId = () => { + var timestamp = ((new Date().getTime() / 1000) | 0).toString(16); // eslint-disable-line no-bitwise + return ( + timestamp + + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function () { + return ((Math.random() * 16) | 0).toString(16).toLowerCase(); // eslint-disable-line no-bitwise + }) + ); +} + +/** + * @method authByScan + * @param {String} appId - the app id + * @param {String} appSecret - the app secret + * @param {Function} onQRGet - (qrcode: string) => void + * @return {Promise} + */ +export function authByScan(appId, appSecret, onQRGet) { + return new Promise(async (resolve, reject) => { + const accessToken = await getAccessToken(appId, appSecret); + const ticket = await getSDKTicket(accessToken); + const nonceStr = generateObjectId(); + const timestamp = String(Math.round(Date.now() / 1000)); + const signature = createSignature(appId, nonceStr, ticket, timestamp); + + const qrcodeEmitter = new NativeEventEmitter(NativeModules.WeChat); + + const subscription = qrcodeEmitter.addListener('onAuthGotQrcode', (res) => + onQRGet && onQRGet(res.qrcode) + ); + + const ret = await nativeScan(appId, nonceStr, timestamp, 'snsapi_userinfo', signature, ''); + // console.log('扫码结果', ret) + subscription.remove(); + if (!ret?.authCode) { + reject(new WechatError({ + errStr: 'Auth code 获取失败', + errCode: -1 + })) + return; + } + getUserInfo(appId, appSecret, ret?.authCode, (result) => { + // console.log('扫码登录结果', result) + if (!result.error) { + resolve(result) + } else { + reject(new WechatError({ + errStr: '扫码登录失败' + JSON.stringify(e), + errCode: -2 + })) + } + }); + }); +} /** * @method sendAuthRequest @@ -224,8 +371,8 @@ export function chooseInvoice(data = {}) { const cardItemList = JSON.parse(resp.cardItemList); resp.cards = cardItemList ? cardItemList.map((item) => ({ - cardId: item.card_id, - encryptCode: item.encrypt_code, + cardId: item.card_id, + encryptCode: item.encrypt_code, })) : []; }