commit
629cb18cbd
30
README.md
30
README.md
@ -154,6 +154,36 @@ following fields:
|
|||||||
| lang | String | The user language |
|
| lang | String | The user language |
|
||||||
| country | String | The user country |
|
| country | String | The user country |
|
||||||
|
|
||||||
|
#### authByScan([scope, nonceStr, onQRGet]) 微信扫码授权登录
|
||||||
|
|
||||||
|
- `appId` {String} the appId you get from WeChat dashboard
|
||||||
|
- `appSecret` {String} the appSecret you get from WeChat dashboard
|
||||||
|
- `onQRGet` (String) => void
|
||||||
|
|
||||||
|
调用 authByScan 后,需要监听二维码的获取,展示完二维码,用户扫码登录完成后才会回调 callback,字段如下
|
||||||
|
|
||||||
|
| field | type | description |
|
||||||
|
| ------- | ------ | ----------------------------------- |
|
||||||
|
| errCode | Number | Error Code |
|
||||||
|
| errStr | String | Error message if any error occurred |
|
||||||
|
| nickname | String | 微信昵称 |
|
||||||
|
| headimgurl | String | 微信头像链接 |
|
||||||
|
| openid | String | openid |
|
||||||
|
| unionid | String | unionid |
|
||||||
|
|
||||||
|
|
||||||
|
示例如下
|
||||||
|
|
||||||
|
```js
|
||||||
|
const ret = await WeChat.authByScan(WeiXinId, WeiXinSecret, (qrcode) => {
|
||||||
|
console.log(qrcode)
|
||||||
|
// 拿到 qrcode 用 Image 去渲染
|
||||||
|
});
|
||||||
|
console.log('登录信息', ret);
|
||||||
|
```
|
||||||
|
|
||||||
|
如有不懂,可以查看[微信官方文档](https://developers.weixin.qq.com/doc/oplatform/Mobile_App/WeChat_Login/Login_via_Scan.html)
|
||||||
|
|
||||||
#### ShareText(ShareTextMetadata) 分享文本
|
#### ShareText(ShareTextMetadata) 分享文本
|
||||||
|
|
||||||
ShareTextMetadata
|
ShareTextMetadata
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import android.graphics.Bitmap;
|
|||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.util.Base64;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
@ -25,11 +26,17 @@ import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
|||||||
import com.facebook.react.bridge.Arguments;
|
import com.facebook.react.bridge.Arguments;
|
||||||
import com.facebook.react.bridge.Callback;
|
import com.facebook.react.bridge.Callback;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||||
import com.facebook.react.bridge.ReactMethod;
|
import com.facebook.react.bridge.ReactMethod;
|
||||||
import com.facebook.react.bridge.ReadableMap;
|
import com.facebook.react.bridge.ReadableMap;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||||
|
import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
|
||||||
|
import com.tencent.mm.opensdk.diffdev.DiffDevOAuthFactory;
|
||||||
|
import com.tencent.mm.opensdk.diffdev.IDiffDevOAuth;
|
||||||
|
import com.tencent.mm.opensdk.diffdev.OAuthErrCode;
|
||||||
|
import com.tencent.mm.opensdk.diffdev.OAuthListener;
|
||||||
import com.tencent.mm.opensdk.modelbase.BaseReq;
|
import com.tencent.mm.opensdk.modelbase.BaseReq;
|
||||||
import com.tencent.mm.opensdk.modelbase.BaseResp;
|
import com.tencent.mm.opensdk.modelbase.BaseResp;
|
||||||
import com.tencent.mm.opensdk.modelbiz.ChooseCardFromWXCardPackage;
|
import com.tencent.mm.opensdk.modelbiz.ChooseCardFromWXCardPackage;
|
||||||
@ -149,6 +156,42 @@ public class WeChatLibModule extends ReactContextBaseJavaModule implements IWXAP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendEvent(ReactContext reactContext, String eventName, WritableMap params) {
|
||||||
|
reactContext.getJSModule(RCTNativeAppEventEmitter.class).emit(eventName, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ReactMethod
|
||||||
|
public void authByScan(String appid, String nonceStr, String timeStamp, String scope, String signature, String schemeData, final Callback callback) {
|
||||||
|
if (api == null) {
|
||||||
|
callback.invoke(NOT_REGISTERED);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IDiffDevOAuth oauth = DiffDevOAuthFactory.getDiffDevOAuth();
|
||||||
|
oauth.stopAuth();
|
||||||
|
oauth.auth(appid, scope, nonceStr, timeStamp, signature, new OAuthListener() {
|
||||||
|
@Override
|
||||||
|
public void onAuthGotQrcode(String var1, byte[] var2){
|
||||||
|
WritableMap map = Arguments.createMap();
|
||||||
|
String base64String = Base64.encodeToString(var2, Base64.DEFAULT);
|
||||||
|
map.putString("qrcode", base64String);
|
||||||
|
sendEvent(getReactApplicationContext(), "onAuthGotQrcode", map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQrcodeScanned() {
|
||||||
|
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onAuthFinish(OAuthErrCode var1, String var2){
|
||||||
|
WritableMap map = Arguments.createMap();
|
||||||
|
map.putString("authCode", var2);
|
||||||
|
map.putInt("errCode", var1.getCode());
|
||||||
|
callback.invoke(null, map);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void registerApp(String appid, String universalLink, Callback callback) {
|
public void registerApp(String appid, String universalLink, Callback callback) {
|
||||||
this.appId = appid;
|
this.appId = appid;
|
||||||
|
|||||||
@ -5,6 +5,13 @@
|
|||||||
#import <React/RCTEventDispatcher.h>
|
#import <React/RCTEventDispatcher.h>
|
||||||
#import <React/RCTImageLoader.h>
|
#import <React/RCTImageLoader.h>
|
||||||
#import <React/RCTLog.h>
|
#import <React/RCTLog.h>
|
||||||
|
#import "WechatAuthSDK.h"
|
||||||
|
|
||||||
|
|
||||||
|
@interface WechatLib () <WechatAuthAPIDelegate>
|
||||||
|
@property (nonatomic, strong) WechatAuthSDK *authSDK;
|
||||||
|
@property (nonatomic, strong) RCTResponseSenderBlock scanCallback;
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation WechatLib
|
@implementation WechatLib
|
||||||
|
|
||||||
@ -20,6 +27,8 @@ RCT_EXPORT_MODULE()
|
|||||||
self = [super init];
|
self = [super init];
|
||||||
if (self) {
|
if (self) {
|
||||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:@"RCTOpenURLNotification" object:nil];
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:@"RCTOpenURLNotification" object:nil];
|
||||||
|
self.authSDK = [[WechatAuthSDK alloc] init];
|
||||||
|
self.authSDK.delegate = self;
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -644,4 +653,56 @@ RCT_EXPORT_METHOD(openCustomerServiceChat
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark - WechatAuthAPIDelegate
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(addListener:(NSString *)eventName) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(removeListeners:(double)count) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RCT_EXPORT_METHOD(authByScan:(NSString *)appid
|
||||||
|
nonceStr:(NSString *)nonceStr
|
||||||
|
timeStamp:(NSString *)timeStamp
|
||||||
|
scope:(NSString *)scope
|
||||||
|
signature:(NSString *)signature
|
||||||
|
schemeData:(nullable NSString *)schemeData
|
||||||
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
|
self.scanCallback = callback;
|
||||||
|
[self.authSDK StopAuth];
|
||||||
|
[self.authSDK Auth:appid nonceStr:nonceStr timeStamp:timeStamp scope:scope signature:signature schemeData:schemeData];
|
||||||
|
}
|
||||||
|
|
||||||
|
//得到二维码
|
||||||
|
- (void)onAuthGotQrcode:(UIImage *)image {
|
||||||
|
NSLog(@"onAuthGotQrcode");
|
||||||
|
NSData *imageData = UIImagePNGRepresentation(image);
|
||||||
|
if (!imageData) {
|
||||||
|
imageData = UIImageJPEGRepresentation(image, 1);
|
||||||
|
}
|
||||||
|
NSString *base64String = [imageData base64EncodedStringWithOptions:0];
|
||||||
|
[self.bridge.eventDispatcher sendDeviceEventWithName:@"onAuthGotQrcode" body:@{@"qrcode": base64String}];
|
||||||
|
}
|
||||||
|
|
||||||
|
//二维码被扫描
|
||||||
|
- (void)onQrcodeScanned {
|
||||||
|
NSLog(@"onQrcodeScanned");
|
||||||
|
}
|
||||||
|
|
||||||
|
//成功登录
|
||||||
|
- (void)onAuthFinish:(int)errCode AuthCode:(nullable NSString *)authCode {
|
||||||
|
NSLog(@"onAuthFinish");
|
||||||
|
if (self.scanCallback) {
|
||||||
|
self.scanCallback(@[[NSNull null], @{@"authCode": authCode?:@"", @"errCode": @(errCode)}]);
|
||||||
|
self.scanCallback = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)supportedEvents
|
||||||
|
{
|
||||||
|
return @[@"onAuthGotQrcode", @"onQrcodeScanned", @"onAuthFinish"];
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@ -71,14 +71,16 @@
|
|||||||
"react-native": "0.70.6",
|
"react-native": "0.70.6",
|
||||||
"react-native-builder-bob": "^0.20.0",
|
"react-native-builder-bob": "^0.20.0",
|
||||||
"release-it": "^15.0.0",
|
"release-it": "^15.0.0",
|
||||||
"typescript": "^4.5.2"
|
"typescript": "^4.5.2",
|
||||||
|
"js-sha1": "^0.7.0"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"@types/react": "17.0.21"
|
"@types/react": "17.0.21"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "*",
|
"react": "*",
|
||||||
"react-native": "*"
|
"react-native": "*",
|
||||||
|
"js-sha1": "*"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 16.0.0"
|
"node": ">= 16.0.0"
|
||||||
@ -160,4 +162,4 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"events": "^3.3.0"
|
"events": "^3.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
10
src/index.d.ts
vendored
10
src/index.d.ts
vendored
@ -46,10 +46,20 @@ declare module 'react-native-wechat-lib' {
|
|||||||
state?: string;
|
state?: string;
|
||||||
returnKey?: string;
|
returnKey?: string;
|
||||||
}
|
}
|
||||||
|
export interface ScanLoginResp {
|
||||||
|
nickname?: string;
|
||||||
|
headimgurl?: string;
|
||||||
|
openid?: string;
|
||||||
|
unionid?: string;
|
||||||
|
errCode?: number;
|
||||||
|
errStr?: string;
|
||||||
|
}
|
||||||
export function sendAuthRequest(
|
export function sendAuthRequest(
|
||||||
scope: string | string[],
|
scope: string | string[],
|
||||||
state?: string
|
state?: string
|
||||||
): Promise<AuthResponse>;
|
): Promise<AuthResponse>;
|
||||||
|
export function authByScan(appId: string, appSecret: string, onQRGet: (qrcode: string)=>void): Promise<ScanLoginResp>;
|
||||||
|
|
||||||
export interface ShareMetadata {
|
export interface ShareMetadata {
|
||||||
type:
|
type:
|
||||||
| 'news'
|
| 'news'
|
||||||
|
|||||||
153
src/index.js
153
src/index.js
@ -1,7 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
import { DeviceEventEmitter, NativeModules, Platform } from 'react-native';
|
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
import { sha1 } from 'js-sha1';
|
||||||
|
import { DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
||||||
|
|
||||||
let isAppRegistered = false;
|
let isAppRegistered = false;
|
||||||
let { WeChat, WechatLib } = NativeModules;
|
let { WeChat, WechatLib } = NativeModules;
|
||||||
@ -170,6 +171,152 @@ const nativeSubscribeMessage = wrapApi(WeChat.subscribeMessage);
|
|||||||
|
|
||||||
const nativeChooseInvoice = wrapApi(WeChat.chooseInvoice);
|
const nativeChooseInvoice = wrapApi(WeChat.chooseInvoice);
|
||||||
const nativeShareFile = wrapApi(WeChat.shareFile);
|
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
|
* @method sendAuthRequest
|
||||||
@ -224,8 +371,8 @@ export function chooseInvoice(data = {}) {
|
|||||||
const cardItemList = JSON.parse(resp.cardItemList);
|
const cardItemList = JSON.parse(resp.cardItemList);
|
||||||
resp.cards = cardItemList
|
resp.cards = cardItemList
|
||||||
? cardItemList.map((item) => ({
|
? cardItemList.map((item) => ({
|
||||||
cardId: item.card_id,
|
cardId: item.card_id,
|
||||||
encryptCode: item.encrypt_code,
|
encryptCode: item.encrypt_code,
|
||||||
}))
|
}))
|
||||||
: [];
|
: [];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user