├── .msfignore
├── .prettierrc.cjs
├── weixin.png
├── .eslintrc.cjs
├── renovate.json
├── android
├── gradle.properties
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ └── com
│ │ └── theweflex
│ │ └── react
│ │ ├── WechatPackage.java
│ │ └── WechatModule.java
├── proguard-rules.pro
└── build.gradle
├── tsconfig.json
├── .editorconfig
├── .gitignore
├── .github
├── workflows
│ └── nodejs.yml
└── ISSUE_TEMPLATE.md
├── ios
├── RCTWechat.h
├── RCTWechat.xcodeproj
│ └── project.pbxproj
└── RCTWechat.m
├── react-native-wechat.podspec
├── LICENSE
├── package.json
├── docs
├── build-setup-android.md
└── build-setup-ios.md
├── CODE_OF_CONDUCT.md
├── README.md
├── CHANGELOG.md
└── src
├── wechatInterface.ts
└── index.ts
/.msfignore:
--------------------------------------------------------------------------------
1 | .git
2 | .github
3 | node_modules
4 | android/build
5 | docs
6 |
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | module.exports = require('@shm-open/eslint-config-bundle/prettier');
2 |
--------------------------------------------------------------------------------
/weixin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shm-open/react-native-wechat/HEAD/weixin.png
--------------------------------------------------------------------------------
/.eslintrc.cjs:
--------------------------------------------------------------------------------
1 | // 基于rect
2 | module.exports = {
3 | extends: ['@shm-open/eslint-config-bundle/react-native'],
4 | };
5 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["config:base"],
3 | "devDependencies": {
4 | "automerge": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | ReactNativeWechat_compileSdkVersion=28
2 | ReactNativeWechat_buildToolsVersion=28.0.3
3 | ReactNativeWechat_targetSdkVersion=28
4 | ReactNativeWechat_minSdkVersion=16
5 | ReactNativeWechat_WechatSdkVersion=6.8.0
6 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react",
4 | "moduleResolution": "node",
5 | "target": "es6",
6 | "outDir": "lib",
7 | "lib": ["es5", "es2015", "es2016", "es2017"],
8 | "allowSyntheticDefaultImports": true
9 | },
10 | "include": ["src/*.*"]
11 | }
12 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.{html,css,less,json,xml,jade}]
13 | indent_style = space
14 | indent_size = 2
15 |
16 | [{M,m}akefile]
17 | indent_style = tab
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /android/build
4 | /android/RCTWeChat.iml
5 |
6 | # iOS
7 | ios/build
8 |
9 | # OSX
10 | #
11 | .DS_Store
12 |
13 | # Xcode
14 | #
15 | build/
16 | *.pbxuser
17 | !default.pbxuser
18 | *.mode1v3
19 | !default.mode1v3
20 | *.mode2v3
21 | !default.mode2v3
22 | *.perspectivev3
23 | !default.perspectivev3
24 | xcuserdata
25 | *.xccheckout
26 | *.moved-aside
27 | DerivedData
28 | *.hmap
29 | *.ipa
30 | *.xcuserstate
31 | project.xcworkspace
32 |
33 |
34 | # Android/IntelliJ
35 | build/
36 | .idea
37 | .gradle
38 | local.properties
39 | *.iml
40 |
41 |
42 | # outputs
43 | /lib
44 |
--------------------------------------------------------------------------------
/.github/workflows/nodejs.yml:
--------------------------------------------------------------------------------
1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
3 |
4 | name: Node.js CI
5 |
6 | on:
7 | push:
8 | branches: [master]
9 | pull_request:
10 | branches: [master]
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | strategy:
17 | matrix:
18 | node-version: [14.x]
19 |
20 | steps:
21 | - uses: actions/checkout@v3
22 | - name: Use Node.js ${{ matrix.node-version }}
23 | uses: actions/setup-node@v3
24 | with:
25 | node-version: ${{ matrix.node-version }}
26 | - run: npm ci
27 | - run: npm run build --if-present
28 | - run: npm test
29 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 'Bug report'
3 | labels: bug
4 | assignees: octocat
5 | ---
6 |
7 | Please read and follow the issue templates:
8 |
9 | 1. Bug Report or Documentation Issue or Questions and Help?
10 |
11 | 2. Which `react-native-wechat` version are you using?
12 |
13 | 3. What platform does your issue occur on? (Android/iOS/Both)
14 |
15 | 4. Please provide a clear and concise description of what the bug is as precisely as possible,you can:
16 |
17 | 1) post a screenshot to explain in which case you get the issue?
18 | 2) related `logs`?
19 | 3) show us the code you are using?
20 | 4) others.
21 |
22 | ### Your Environment
23 |
24 | | software | version
25 | | ---------------- | -------
26 | | react-native-wechat | ^ version
27 | | react-native | ^ version
28 | | node | ^ version
29 | | npm or yarn | ^ version
30 |
--------------------------------------------------------------------------------
/ios/RCTWechat.h:
--------------------------------------------------------------------------------
1 | // Created by shihuimiao on 2021/01/22.
2 | // Copyright © 2021 shihuimiao. All rights reserved.
3 |
4 | #import
5 | #import
6 |
7 | #import
8 | #import "WXApi.h"
9 |
10 | #define RCTLaunchFromWXEvent @"LaunchFromWX.Req"
11 | #define RCTWXSendMessageEvent @"SendMessageToWX.Resp"
12 | #define RCTWXSendAuthEvent @"SendAuth.Resp"
13 | #define RCTWXLaunchMiniprogramEvent @"LaunchMiniprogram.Resp"
14 | #define RCTWXPayEvent @"PayReq.Resp"
15 | #define RCTWXOpenBusinessWebViewEvent @"WXOpenBusinessWebview.Resp"
16 |
17 | @interface RCTWechat : NSObject
18 |
19 | @property (nonatomic, copy) NSString *appId;
20 |
21 | // handle AppDelegate openURL
22 | + (BOOL)handleOpenURL:(NSURL *)url;
23 |
24 | + (BOOL)handleOpenUniversalLink:(NSUserActivity *)userActivity;
25 |
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/android/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | # wechat sdk
24 | -keep class com.tencent.mm.opensdk.** { *; }
25 |
--------------------------------------------------------------------------------
/react-native-wechat.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod spec lint RCTWeChat.podspec' to ensure this is a
3 | # valid spec and to remove all comments including this before submitting the spec.
4 | #
5 | # To learn more about Podspec attributes see http://docs.cocoapods.org/specification.html
6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/
7 | #
8 |
9 | require 'json'
10 | package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
11 |
12 | Pod::Spec.new do |s|
13 | s.name = "react-native-wechat"
14 | s.version = package['version']
15 | s.summary = "Wechat library"
16 | s.description = package['description']
17 | s.author = package['author']
18 | s.homepage = package['homepage']
19 | s.license = "MIT"
20 | s.platform = :ios, "9.0"
21 | s.source = { :git => "#{s.homepage}", :tag => "v#{s.version}" }
22 | s.source_files = "ios/*.{h,m}"
23 | s.dependency "React"
24 | s.dependency "WechatOpenSDK", '1.8.7.1'
25 | s.requires_arc = true
26 | end
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021-present shihuimiao
4 |
5 | Copyright (c) 2017-2020 Yazhong Liu
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
24 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | def getExtOrDefault(name) {
4 | return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeWechat_' + name]
5 | }
6 |
7 | buildscript {
8 | repositories {
9 | mavenCentral()
10 | google()
11 | }
12 | dependencies {
13 | classpath 'com.android.tools.build:gradle:4.2.2'
14 | }
15 | }
16 |
17 |
18 |
19 | android {
20 | compileSdkVersion getExtOrDefault('compileSdkVersion')
21 | buildToolsVersion getExtOrDefault('buildToolsVersion')
22 |
23 | defaultConfig {
24 | minSdkVersion getExtOrDefault('minSdkVersion')
25 | targetSdkVersion getExtOrDefault('targetSdkVersion')
26 | versionCode 1
27 | versionName "1.0"
28 | }
29 | buildTypes {
30 | release {
31 | consumerProguardFiles 'proguard-rules.pro'
32 | }
33 | }
34 | }
35 |
36 | repositories {
37 | mavenCentral()
38 | google()
39 | maven {
40 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
41 | url "$rootDir/../node_modules/react-native/android"
42 | }
43 | }
44 |
45 | dependencies {
46 | implementation 'com.facebook.react:react-native:+'
47 | api "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${getExtOrDefault('WechatSdkVersion')}"
48 | }
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@shm-open/react-native-wechat",
3 | "version": "1.3.0",
4 | "description": "react-native library for wechat app",
5 | "type": "module",
6 | "main": "lib/index.js",
7 | "types": "lib/index.d.ts",
8 | "files": [
9 | "android/",
10 | "ios/",
11 | "lib/",
12 | "*.podspec"
13 | ],
14 | "scripts": {
15 | "build": "npm run clean && npm run compile",
16 | "clean": "rm -rf lib",
17 | "compile": "tsc -d",
18 | "start": "concurrently npm:watch-ts npm:sync-to-app",
19 | "watch-ts": "npm run compile -- --watch",
20 | "sync-to-app": "if [ $SHM_APP_SOURCE_PATH ]; then msf ./ $SHM_APP_SOURCE_PATH/node_modules/@shm-open/react-native-wechat; else tail -f /dev/null; fi",
21 | "release": "npm test && npm run build && standard-version && git push --follow-tags origin master && npm publish",
22 | "test": "eslint src/"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "https://github.com/shm-open/react-native-wechat.git"
27 | },
28 | "keywords": [
29 | "wechat",
30 | "react",
31 | "react-native",
32 | "react-component"
33 | ],
34 | "dependencies": {
35 | "@shm-open/utilities": "^1.7.0"
36 | },
37 | "peerDependencies": {
38 | "react-native": "*"
39 | },
40 | "devDependencies": {
41 | "@shm-open/eslint-config-bundle": "1.9.13",
42 | "@types/react-native": "0.72.8",
43 | "concurrently": "9.2.1",
44 | "msf-cli": "1.2.5",
45 | "standard-version": "9.5.0",
46 | "typescript": "5.9.3"
47 | },
48 | "homepage": "https://github.com/shm-open/react-native-wechat",
49 | "author": "shihuimiao",
50 | "license": "MIT"
51 | }
52 |
--------------------------------------------------------------------------------
/android/src/main/java/com/theweflex/react/WechatPackage.java:
--------------------------------------------------------------------------------
1 | package com.theweflex.react;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.TurboReactPackage;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.module.annotations.ReactModule;
8 | import com.facebook.react.module.model.ReactModuleInfo;
9 | import com.facebook.react.module.model.ReactModuleInfoProvider;
10 | import com.facebook.react.turbomodule.core.interfaces.TurboModule;
11 |
12 | import java.util.HashMap;
13 | import java.util.Map;
14 |
15 | public class WechatPackage extends TurboReactPackage implements ReactPackage {
16 | @Override
17 | public NativeModule getModule(String name, ReactApplicationContext reactContext) {
18 | switch (name) {
19 | case WechatModule.NAME:
20 | return new WechatModule(reactContext);
21 | default:
22 | throw new IllegalArgumentException("cannot find native module: " + name);
23 | }
24 | }
25 |
26 | @Override
27 | public ReactModuleInfoProvider getReactModuleInfoProvider() {
28 | Class extends NativeModule>[] moduleList = new Class[]{
29 | WechatModule.class
30 | };
31 |
32 | final Map reactModuleInfoMap = new HashMap<>();
33 | for (Class extends NativeModule> moduleClass : moduleList) {
34 | ReactModule reactModule = moduleClass.getAnnotation(ReactModule.class);
35 |
36 | reactModuleInfoMap.put(
37 | reactModule.name(),
38 | new ReactModuleInfo(
39 | reactModule.name(),
40 | moduleClass.getName(),
41 | true,
42 | reactModule.needsEagerInit(),
43 | reactModule.hasConstants(),
44 | reactModule.isCxxModule(),
45 | TurboModule.class.isAssignableFrom(moduleClass)));
46 | }
47 |
48 | return () -> reactModuleInfoMap;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/docs/build-setup-android.md:
--------------------------------------------------------------------------------
1 | # Build Setup for Android
2 |
3 | Copy lines to `proguard-rules.pro`:
4 |
5 | ```pro
6 | -keep class com.tencent.mm.sdk.** {
7 | *;
8 | }
9 | ```
10 |
11 | **Integrating with login and share**
12 |
13 | If you are going to integrate login or share functions, you need to
14 | create a package named 'wxapi' in your application package and a class
15 | named `WXEntryActivity` in it.
16 |
17 | ```java
18 | package your.package.wxapi;
19 |
20 | import android.app.Activity;
21 | import android.os.Bundle;
22 | import com.theweflex.react.WechatModule;
23 |
24 | public class WXEntryActivity extends Activity {
25 | @Override
26 | protected void onCreate(Bundle savedInstanceState) {
27 | super.onCreate(savedInstanceState);
28 | // 冷启动、热启动自己应用需要自己处理跳转到指定页面
29 | WechatModule.handleIntent(getIntent());
30 | finish();
31 | }
32 | }
33 | ```
34 |
35 | Then add the following node to `AndroidManifest.xml`:
36 |
37 | ```xml
38 |
39 |
40 |
45 |
46 |
47 | ```
48 |
49 | **Integrating the WeChat Payment**
50 |
51 | If you are going to integrate payment functionality by using this library, then
52 | create a package named also `wxapi` in your application package and a class named
53 | `WXPayEntryActivity`, this is used to bypass the response to JS level:
54 |
55 | ```java
56 | package your.package.wxapi;
57 |
58 | import android.app.Activity;
59 | import android.os.Bundle;
60 | import com.theweflex.react.WechatModule;
61 |
62 | public class WXPayEntryActivity extends Activity {
63 | @Override
64 | protected void onCreate(Bundle savedInstanceState) {
65 | super.onCreate(savedInstanceState);
66 | // 冷启动、热启动自己应用需要自己处理跳转到指定页面
67 | WechatModule.handleIntent(getIntent());
68 | finish();
69 | }
70 | }
71 | ```
72 |
73 | Then add the following node to `AndroidManifest.xml`:
74 |
75 | ```xml
76 |
77 |
78 |
83 |
84 |
85 | ```
86 |
--------------------------------------------------------------------------------
/docs/build-setup-ios.md:
--------------------------------------------------------------------------------
1 | # Build Setup for iOS
2 |
3 | ## Installation
4 |
5 | ```sh
6 | $ cd your project iOS path
7 | $ pod install
8 | ```
9 |
10 | #### URL Schemes
11 | Add "URL Scheme" as your wechat appId for "URL type" in Targets > info, See
12 | the following screenshot for the view on your XCode:
13 |
14 | 
15 |
16 | Cannot go back to APP from WeChat without configuration.
17 | 如果不配置,就无法从微信重新回到APP。
18 |
19 |
20 | #### LSApplicationQueriesSchemes
21 | On iOS 9+, add `wechat`、 `weixin` and `weixinULAPI` into `LSApplicationQueriesSchemes` in
22 | `Targets` > `info` > `Custom iOS Target Properties`. Or edit `Info.plist`
23 | then add:
24 |
25 | ```xml
26 | LSApplicationQueriesSchemes
27 |
28 | weixin
29 | wechat
30 | weixinULAPI
31 |
32 | ```
33 | If not configured, apple will prevent you from jumping to WeChat due to security permissions.
34 | 如果不配置,因为安全权限问题,苹果会阻止你跳转到微信。
35 |
36 |
37 | #### AppDelegate
38 | copy the following in `AppDelegate.m`:
39 |
40 | Wechat callback function, If not configured, When sharing is called, it appears "connecting" and then bounces back.
41 | 微信回调方法,如果不配置,分享的时候微信会出现"正在连接",然后直接弹回APP。
42 |
43 | ```objc
44 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url
45 | options:(NSDictionary *)options
46 | {
47 | BOOL handled = [RCTWechat handleOpenURL:url];
48 | if (handled) {
49 | return handled;
50 | }
51 | return [RCTLinkingManager application:application openURL:url options:options];
52 | }
53 |
54 | - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> * _Nullable))restorationHandler {
55 | BOOL handled = [RCTWechat handleOpenUniversalLink:userActivity];
56 | if (handled) {
57 | return handled;
58 | }
59 | return [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
60 | }
61 | ```
62 |
63 | #### Universal Links config
64 | Universal Links 如果不配置,就无法从微信重新回到APP。
65 | 在`选中project -> TARGETS -> Signing&Capabilities` 然后点击`+`添加一个`Associated Domains`, 在里面填写`Universal Links`配置的网址
66 |
67 | Apple Universal Link: [https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1](https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html#//apple_ref/doc/uid/TP40016308-CH12-SW1)
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at yorkiefixer@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-wechat 
2 |
3 | [React Native] bridging library that integrates WeChat SDKs
4 |
5 | 本库是在 [react-native-wechat](https://github.com/yorkie/react-native-wechat) 基础上进行重写; 优化了类型定义,方法调用,并且使用`CocoaPods`和`gradle`来管理原生模块依赖的`wechat SDK`.
6 |
7 | ## Table of Contents
8 |
9 | - [Getting Started](#getting-started)
10 | - [API Documentation](#api-documentation)
11 | - [Installation](#installation)
12 | - [Community](#community)
13 | - [Authors](#authors)
14 | - [License](#license)
15 |
16 | ## Getting Started
17 |
18 | `$ npm install @shm-open/react-native-wechat`
19 |
20 | - [Build setup on iOS](./docs/build-setup-ios.md)
21 | - [Build setup on Android](./docs/build-setup-android.md)
22 |
23 | ## API Documentation
24 |
25 | [react-native-wechat] uses Promises, therefore you can use `Promise`
26 | or `async/await` to manage your dataflow.
27 |
28 | #### registerApp(appId, universalLink)
29 |
30 | - `appId` {String} the appId you get from WeChat dashboard
31 | - `universalLink` {String} the iOS universalLink setting
32 | - returns {Boolean} explains if your application is registered done
33 |
34 | This method should be called once globally.
35 |
36 | ```js
37 | import * as WeChat from 'react-native-wechat';
38 |
39 | WeChat.registerApp('your wxid', 'your universal setting');
40 | ```
41 |
42 | #### isWXAppInstalled()
43 |
44 | - returns {Boolean} if WeChat is installed.
45 |
46 | Check if the WeChat app is installed on the device.
47 |
48 | #### isWXAppSupportApi()
49 |
50 | - returns {Boolean} Contains the result.
51 |
52 | Check if wechat support open url.
53 |
54 | #### getApiVersion()
55 |
56 | - returns {String} Contains the result.
57 |
58 | Get the WeChat SDK api version.
59 |
60 | #### openWXApp()
61 |
62 | - returns {Boolean}
63 |
64 | Open the WeChat app from your application.
65 |
66 | #### sendAuthRequest(scope, state)
67 |
68 | - `scope` {String} Scopes of auth request.
69 | snsapi_userinfo or snsapi_base
70 | - `state` {String} 用于保持请求和回调的状态,授权请求后原样带回给第三方
71 | - returns {Object}
72 |
73 | Send authentication request, and it returns an object with the
74 | following fields:
75 |
76 | | field | type | description |
77 | | ------- | ------ | ----------------------------------- |
78 | | errCode | Number | Error Code |
79 | | errStr | String | Error message if any error occurred |
80 | | code | String | Authorization code |
81 | | state | String | state_wx_login |
82 |
83 | #### pay(payload)
84 |
85 | - `partnerId` {String} 商家向财付通申请的商家 id
86 | - `prepayId` {String} 预支付订单 ID
87 | - `nonceStr` {String} 随机串,防重发
88 | - `timeStamp` {String} 时间戳,防重发
89 | - `package` {String} 商家根据财付通文档填写的数据和签名
90 | - `sign` {String} 商家根据微信开放平台文档对数据做的签名
91 | - returns {Object}
92 |
93 | Sends request for proceeding payment, then returns an object:
94 |
95 | | name | type | description |
96 | | ------- | ------ | ----------------------------------- |
97 | | errCode | Number | 0 if pay successed |
98 | | errStr | String | Error message if any error occurred |
99 |
100 | ## Installation
101 |
102 | ```sh
103 | $ npm install react-native-wechat --save
104 | ```
105 |
106 | ## License
107 |
108 | MIT
109 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 | ## [1.3.0](https://github.com/shm-open/react-native-wechat/compare/v1.2.3...v1.3.0) (2022-02-16)
6 |
7 |
8 | ### Features
9 |
10 | * update sdk to 6.8.0 ([2e41993](https://github.com/shm-open/react-native-wechat/commit/2e419937108b1ecd4ae94cdc7ab458afb363b446))
11 |
12 | ### [1.2.3](https://github.com/shm-open/react-native-wechat/compare/v1.2.2...v1.2.3) (2021-12-24)
13 |
14 |
15 | ### Bug Fixes
16 |
17 | * **android:** use TurboReactPackage to lazy load the native module ([3a25412](https://github.com/shm-open/react-native-wechat/commit/3a25412762e33b340c1c6582b3430b74269171c2))
18 |
19 | ### [1.2.2](https://github.com/shm-open/react-native-wechat/compare/v1.2.1...v1.2.2) (2021-09-02)
20 |
21 |
22 | ### Bug Fixes
23 |
24 | * **iOS:** unable to handle login delegate ([8dc71d8](https://github.com/shm-open/react-native-wechat/commit/8dc71d8ab823e28fe1ae9257afc34e23aaf9f5c5))
25 |
26 | ### [1.2.1](https://github.com/shm-open/react-native-wechat/compare/v1.2.0...v1.2.1) (2021-08-18)
27 |
28 |
29 | ### Bug Fixes
30 |
31 | * remove the use of deprecated DeviceEventEmitter.removeListener() ([afbc8fd](https://github.com/shm-open/react-native-wechat/commit/afbc8fd508f6eb58a8bc1a66d51298fb9689e601))
32 |
33 | ## [1.2.0](https://github.com/shm-open/react-native-wechat/compare/v1.1.2...v1.2.0) (2021-07-22)
34 |
35 |
36 | ### Features
37 |
38 | * **android:** make WechatSdkVersion configurable ([349ddda](https://github.com/shm-open/react-native-wechat/commit/349ddda8587d3c57e8e16b8d6d00720172846701))
39 |
40 |
41 | ### Bug Fixes
42 |
43 | * add lint support and fix issues ([6669f62](https://github.com/shm-open/react-native-wechat/commit/6669f627fcfda08173bc8f394ffb73c4afe31be6))
44 | * **deps:** update @shm-open/utilities to 1.7.0 ([4202b38](https://github.com/shm-open/react-native-wechat/commit/4202b38972ce56c41591b975c16ebf0309772983))
45 | * **deps:** update dependency @shm-open/utilities to v1.4.1 ([efe9b31](https://github.com/shm-open/react-native-wechat/commit/efe9b313d81703b8e4544fac5e2b2f63a5d5530b))
46 | * **deps:** update dependency @shm-open/utilities to v1.6.2 ([7e95906](https://github.com/shm-open/react-native-wechat/commit/7e9590673aaefb6147e12ea9cab61ed25103d96b))
47 |
48 | ### [1.1.2](https://github.com/shm-open/react-native-wechat/compare/v1.1.1...v1.1.2) (2021-03-08)
49 |
50 |
51 | ### Bug Fixes
52 |
53 | * **deps:** update dependency @shm-open/utilities to v1.3.0 ([612a68f](https://github.com/shm-open/react-native-wechat/commit/612a68f3559faef413dadb8f008c3a555a6044b7))
54 | * **iOS:** fix shareMiniprogram method not response ([16bc085](https://github.com/shm-open/react-native-wechat/commit/16bc085fc155d2d5c29caa2cb1c7f8a5b8e193ab))
55 |
56 | ### [1.1.1](https://github.com/shm-open/react-native-wechat/compare/v1.1.0...v1.1.1) (2021-02-05)
57 |
58 |
59 | ### Bug Fixes
60 |
61 | * **deps:** update dependency @shm-open/utilities to v1.2.2 ([e402e42](https://github.com/shm-open/react-native-wechat/commit/e402e42cca114b2e8372ffc418ba999b8e017adb))
62 |
63 | ## [1.1.0](https://github.com/shm-open/react-native-wechat/compare/v1.0.1...v1.1.0) (2021-01-29)
64 |
65 |
66 | ### Features
67 |
68 | * rename wechat ([7ce439c](https://github.com/shm-open/react-native-wechat/commit/7ce439cda67089faaeeef9530949013781b207cc))
69 |
70 |
71 | ### Bug Fixes
72 |
73 | * **deps:** update dependency @shm-open/utilities to v1.2.1 ([3c00cb5](https://github.com/shm-open/react-native-wechat/commit/3c00cb5d3fefa18ffc8beb7f5051db985b89351e))
74 | * 更新gradle,重命名java文件名 ([2c0c8f8](https://github.com/shm-open/react-native-wechat/commit/2c0c8f8d46776f5e2b8b80ca5d2ed11ff1f96fbe))
75 |
76 | ### [1.0.1](https://github.com/shm-open/react-native-wechat/compare/v1.0.0...v1.0.1) (2021-01-29)
77 |
78 | ## 1.0.0 (2021-01-29)
79 |
80 |
81 | ### Features
82 |
83 | * wechat库重构优化 ([523e13f](https://github.com/shm-open/react-native-wechat/commit/523e13f1f452c8448825abd6cb02290eb83b04db))
84 |
85 |
86 | ### Bug Fixes
87 |
88 | * **example:** upgrade the example project to RN 0.60 ([#507](https://github.com/shm-open/react-native-wechat/issues/507)) ([138f4f8](https://github.com/shm-open/react-native-wechat/commit/138f4f8edac0edf9147ca139c4452cef62919260))
89 |
--------------------------------------------------------------------------------
/src/wechatInterface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 微信小程序类型
3 | */
4 | export enum WechatMiniprogramTypeEnum {
5 | // 正式版
6 | RELEASE = 0,
7 | // 测试版
8 | TEST = 1,
9 | // 体验版
10 | PREVIEW = 2,
11 | }
12 |
13 | /**
14 | * 分享场景
15 | */
16 | export enum WechatSceneEnum {
17 | // 聊天界面
18 | SESSION = 0,
19 | // 朋友圈
20 | TIMELINE = 1,
21 | // 收藏
22 | FAVORITE = 2,
23 | }
24 |
25 | /**
26 | * 分享消息基础类型
27 | */
28 | export interface WechatShareBase {
29 | // 标题
30 | title?: string;
31 | // 描述
32 | description?: string;
33 | }
34 |
35 | /**
36 | * 小程序分享类型
37 | */
38 | export interface WechatShareMiniprogram extends WechatShareBase {
39 | // 兼容低版本的网页链接(长度不超过10KB)
40 | webpageUrl: string;
41 | // 小程序预览图url
42 | thumbImageUrl: string;
43 | miniprogram: WechatOpenMiniprogram;
44 | }
45 |
46 | /**
47 | * 打开小程序
48 | */
49 | export interface WechatOpenMiniprogram {
50 | // 小程序userName
51 | userName: string;
52 | // 小程序页面路径
53 | path: string;
54 | // 小程序的类型
55 | miniprogramType?: WechatMiniprogramTypeEnum;
56 | }
57 |
58 | /**
59 | * 分享文本
60 | */
61 | export interface WechatShareText extends WechatShareBase {
62 | text: string;
63 | // 发送场景
64 | scene: WechatSceneEnum;
65 | }
66 |
67 | /**
68 | * 分享图片
69 | */
70 | export interface WechatShareImage extends WechatShareBase {
71 | // 图片url
72 | imageUrl: string;
73 | // 发送场景
74 | scene: WechatSceneEnum;
75 | }
76 |
77 | /**
78 | * 分享网页
79 | */
80 | export interface WechatShareWebPage extends WechatShareBase {
81 | // 网页地址
82 | webpageUrl: string;
83 | // 图片地址
84 | thumbImageUrl: string;
85 | // 发送场景
86 | scene: WechatSceneEnum;
87 | }
88 |
89 | /**
90 | * 分享Video [videoUrl和videoLowBandUrl不能同时为空]
91 | */
92 | export interface WechatShareVideo extends WechatShareBase {
93 | // 视频链接
94 | videoUrl?: string;
95 | // 供低带宽的环境下使用的视频链接
96 | videoLowBandUrl?: string;
97 | // 缩略图
98 | thumbImageUrl?: string;
99 | }
100 |
101 | /**
102 | * 分享Music [videoUrl和videoLowBandUrl不能同时为空]
103 | */
104 | export interface WechatShareMusic extends WechatShareBase {
105 | // 音频网页的URL地址
106 | musicUrl?: string;
107 | // 供低带宽环境下使用的音频网页URL地址
108 | musicLowBandUrl?: string;
109 | // 音频数据的URL地址
110 | musicDataUrl?: string;
111 | // 供低带宽环境下使用的音频数据URL地址
112 | musicLowBandDataUrl?: string;
113 | // 缩略图
114 | thumbImageUrl?: string;
115 | }
116 |
117 | /**
118 | * 微信支付
119 | */
120 | export interface WechatPay {
121 | // 商家向财付通申请的商家id
122 | // eslint-disable-next-line @typescript-eslint/naming-convention
123 | partnerId: string;
124 | // 预支付订单
125 | // eslint-disable-next-line @typescript-eslint/naming-convention
126 | prepayId: string;
127 | // 随机串,防重发
128 | nonceStr: string;
129 | // 时间戳,防重发
130 | timeStamp: string;
131 | // 商家根据财付通文档填写的数据和签名
132 | package: string;
133 | // 商家根据微信开放平台文档对数据做的签名
134 | sign: string;
135 | }
136 |
137 | /**
138 | * 微信签约
139 | */
140 | export interface WechatEntrust {
141 | // eslint-disable-next-line @typescript-eslint/naming-convention
142 | preEntrustWebId: string;
143 | }
144 |
145 | /**
146 | * 错误码
147 | */
148 | export enum WXErrCodeEnum {
149 | // eslint-disable-next-line @typescript-eslint/naming-convention
150 | WXSuccess = 0, // 成功
151 | // eslint-disable-next-line @typescript-eslint/naming-convention
152 | WXErrCodeCommon = -1, // 普通错误类型
153 | // eslint-disable-next-line @typescript-eslint/naming-convention
154 | WXErrCodeUserCancel = -2, // 用户点击取消并返回
155 | // eslint-disable-next-line @typescript-eslint/naming-convention
156 | WXErrCodeSentFail = -3, // 发送失败
157 | // eslint-disable-next-line @typescript-eslint/naming-convention
158 | WXErrCodeAuthDeny = -4, // 授权失败
159 | // eslint-disable-next-line @typescript-eslint/naming-convention
160 | WXErrCodeUnsupport = -5, // 微信不支持
161 | // eslint-disable-next-line @typescript-eslint/naming-convention
162 | WXErrBan = -6, // Android禁止
163 | }
164 |
165 | /**
166 | * promises will reject with this error when API call finish with an errCode other than zero.
167 | */
168 | export class WechatError extends Error {
169 | public code: WXErrCodeEnum;
170 |
171 | constructor(code: WXErrCodeEnum, message: string) {
172 | super(message);
173 | this.name = 'WechatError';
174 | this.code = code;
175 | }
176 | }
177 |
178 | // 基础返回值
179 | export interface BaseWXResp {
180 | errCode: WXErrCodeEnum;
181 | errStr: string;
182 | }
183 |
184 | // SendMessageToWXResp 返回值定义
185 | export interface SendMessageToWXResp extends BaseWXResp {
186 | lang?: string;
187 | country?: string;
188 | }
189 |
190 | // SendAuthResp 返回值定义
191 | export interface SendAuthResp extends BaseWXResp {
192 | lang?: string;
193 | country?: string;
194 | state?: string;
195 | // eslint-disable-next-line @typescript-eslint/naming-convention
196 | appId?: string;
197 | code?: string;
198 | }
199 |
200 | // LaunchMiniprogramResp 返回值定义
201 | export interface LaunchMiniprogramResp extends BaseWXResp {
202 | // 微信关闭窗口后返回给APP的自定义信息
203 | extMsg?: string;
204 | }
205 |
206 | // PayResp 返回值定义
207 | export interface PayResp extends BaseWXResp {
208 | // 财付通返回给商家的信息
209 | returnKey?: string;
210 | }
211 |
212 | // PayResp 返回值定义
213 | export interface OpenBusinessWebViewResp extends BaseWXResp {
214 | // 第三方程序自定义简单数据,微信终端会回传给第三方程序处理
215 | resultInfo?: string;
216 | // 网页业务类型
217 | businessType?: number;
218 | }
219 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-unused-modules */
2 | import { ListenerHolder, ListenerSubscription } from '@shm-open/utilities';
3 | import { DeviceEventEmitter, EmitterSubscription, NativeModules } from 'react-native';
4 | import {
5 | WechatPay,
6 | WechatEntrust,
7 | WechatShareImage,
8 | WechatShareMiniprogram,
9 | WechatShareText,
10 | WechatShareWebPage,
11 | WechatShareVideo,
12 | WechatShareMusic,
13 | WXErrCodeEnum,
14 | WechatOpenMiniprogram,
15 | WechatError,
16 | BaseWXResp,
17 | SendMessageToWXResp,
18 | LaunchMiniprogramResp,
19 | OpenBusinessWebViewResp,
20 | PayResp,
21 | SendAuthResp,
22 | } from './wechatInterface';
23 |
24 | export {
25 | WechatMiniprogramTypeEnum,
26 | WechatSceneEnum,
27 | WechatShareBase,
28 | WechatShareMiniprogram,
29 | WechatOpenMiniprogram,
30 | WechatShareText,
31 | WechatShareImage,
32 | WechatShareWebPage,
33 | WechatShareVideo,
34 | WechatShareMusic,
35 | WechatPay,
36 | WechatEntrust,
37 | WXErrCodeEnum,
38 | WechatError,
39 | BaseWXResp,
40 | SendMessageToWXResp,
41 | SendAuthResp,
42 | LaunchMiniprogramResp,
43 | PayResp,
44 | OpenBusinessWebViewResp,
45 | } from './wechatInterface';
46 |
47 | type EventType =
48 | | 'SendMessageToWX.Resp'
49 | | 'LaunchMiniprogram.Resp'
50 | | 'PayReq.Resp'
51 | | 'WXOpenBusinessWebview.Resp'
52 | | 'SendAuth.Resp';
53 |
54 | /**
55 | * 微信是否已安装
56 | */
57 | export function isWXAppInstalled(): Promise {
58 | return NativeModules.Wechat.isWXAppInstalled();
59 | }
60 |
61 | /**
62 | * 注册wechat
63 | */
64 | export function registerApp(appId: string, universalLink: string): Promise {
65 | return NativeModules.Wechat.registerApp(appId, universalLink);
66 | }
67 |
68 | /**
69 | * 判断当前微信的版本是否支持
70 | */
71 | export function isWXAppSupportApi(): Promise {
72 | return NativeModules.Wechat.isWXAppSupportApi();
73 | }
74 |
75 | /**
76 | * 微信的安装地址字符串[only iOS]
77 | */
78 | export function getWXAppInstallUrl(): Promise {
79 | return new Promise((resolve, reject) => {
80 | NativeModules.Wechat.getWXAppInstallUrl((error: string, installURL: string) => {
81 | if (!error) {
82 | resolve(installURL);
83 | } else {
84 | reject(new WechatError(WXErrCodeEnum.WXErrCodeCommon, error));
85 | }
86 | });
87 | });
88 | }
89 |
90 | /**
91 | * 返回当前微信SDK的版本号[only iOS]
92 | */
93 | export function getApiVersion(): Promise {
94 | return new Promise((resolve, reject) => {
95 | NativeModules.Wechat.getApiVersion((error: string, version: string) => {
96 | if (!error) {
97 | resolve(version);
98 | } else {
99 | reject(new WechatError(WXErrCodeEnum.WXErrCodeCommon, error));
100 | }
101 | });
102 | });
103 | }
104 |
105 | /**
106 | * 打开微信
107 | */
108 | export function openWXApp(): Promise {
109 | return NativeModules.Wechat.openWXApp();
110 | }
111 |
112 | function promiseWrap(
113 | eventType: EventType,
114 | callWXApi: () => Promise,
115 | ): Promise {
116 | return new Promise((resolve, reject) => {
117 | // 异步结果,微信部分API成功结果没有回调。
118 | let subscription: EmitterSubscription;
119 | const listener = (resp: T) => {
120 | if (resp.errCode === WXErrCodeEnum.WXSuccess) {
121 | resolve(resp);
122 | } else {
123 | reject(new WechatError(resp.errCode, resp.errStr));
124 | }
125 | if (subscription) {
126 | subscription.remove();
127 | subscription = null;
128 | }
129 | };
130 | subscription = DeviceEventEmitter.addListener(eventType, listener);
131 | // 同步结果,微信同步成功回调不可信,同步失败回调可信。
132 | // TODO: do we need a timeout?
133 | callWXApi().catch((error: WechatError) => {
134 | if (subscription) {
135 | subscription.remove();
136 | subscription = null;
137 | }
138 | reject(error);
139 | });
140 | });
141 | }
142 |
143 | /**
144 | * @method 微信登录授权
145 | * @param scopes - 第三方程序要向微信申请认证,并请求某些权限
146 | * @param state - 第三方程序本身用来标识其请求的唯一性,最后跳转回第三方程序时,由微信终端回传。
147 | * @return Promise
148 | */
149 | export function sendAuthRequest(
150 | scopes: 'snsapi_userinfo' | 'snsapi_base',
151 | state?: string,
152 | ): Promise {
153 | return promiseWrap('SendAuth.Resp', () =>
154 | NativeModules.Wechat.sendAuthRequest(scopes, state),
155 | );
156 | }
157 |
158 | /**
159 | * @method 分享文本
160 | * @param shareData - 数据
161 | * @return Promise
162 | */
163 | export function shareText(shareData: WechatShareText): Promise {
164 | return promiseWrap('SendMessageToWX.Resp', () =>
165 | NativeModules.Wechat.shareText(shareData),
166 | );
167 | }
168 |
169 | /**
170 | * @method 分享图片
171 | * @param shareData - 数据
172 | * @return Promise
173 | */
174 | export function shareImage(shareData: WechatShareImage): Promise {
175 | return promiseWrap('SendMessageToWX.Resp', () =>
176 | NativeModules.Wechat.shareImage(shareData),
177 | );
178 | }
179 |
180 | /**
181 | * @method 分享网页(媒体数据)
182 | * @param shareData - 数据
183 | * @return Promise
184 | */
185 | export function shareWebpage(shareData: WechatShareWebPage): Promise {
186 | return promiseWrap('SendMessageToWX.Resp', () =>
187 | NativeModules.Wechat.shareWebpage(shareData),
188 | );
189 | }
190 |
191 | /**
192 | * @method 分享小程序
193 | * @param shareData - 数据
194 | * @return Promise
195 | */
196 | export function shareMiniprogram(shareData: WechatShareMiniprogram): Promise {
197 | return promiseWrap('SendMessageToWX.Resp', () =>
198 | NativeModules.Wechat.shareMiniprogram(shareData),
199 | );
200 | }
201 |
202 | /**
203 | * @method 分享Video
204 | * @param shareData - 数据
205 | * @return Promise
206 | */
207 | export function shareVideo(shareData: WechatShareVideo): Promise {
208 | return promiseWrap('SendMessageToWX.Resp', () =>
209 | NativeModules.Wechat.shareVideo(shareData),
210 | );
211 | }
212 |
213 | /**
214 | * @method 分享Music
215 | * @param shareData - 数据
216 | * @return Promise
217 | */
218 | export function shareMusic(shareData: WechatShareMusic): Promise {
219 | return promiseWrap('SendMessageToWX.Resp', () =>
220 | NativeModules.Wechat.shareMusic(shareData),
221 | );
222 | }
223 |
224 | /**
225 | * @method 打开小程序
226 | * @param shareData - 数据
227 | * @return Promise
228 | */
229 | export function openMiniprogram(shareData: WechatOpenMiniprogram): Promise {
230 | return promiseWrap('LaunchMiniprogram.Resp', () =>
231 | NativeModules.Wechat.openMiniprogram(shareData),
232 | );
233 | }
234 |
235 | /**
236 | * @method 微信支付
237 | * @param payData - 数据
238 | * @return Promise
239 | */
240 | export function pay(payData: WechatPay): Promise {
241 | return promiseWrap('PayReq.Resp', () => NativeModules.Wechat.pay(payData));
242 | }
243 |
244 | /**
245 | * @method 微信签约
246 | * @param entrustData - 数据
247 | * @return Promise
248 | */
249 | export function entrust(entrustData: WechatEntrust): Promise {
250 | return promiseWrap('WXOpenBusinessWebview.Resp', () =>
251 | NativeModules.Wechat.entrust(entrustData),
252 | );
253 | }
254 |
255 | // Wechat handler APP
256 | export interface LaunchFromWXReq {
257 | lang?: string;
258 | country?: string;
259 | extInfo?: string;
260 | }
261 | type LaunchFromWXReqEventType = 'LaunchFromWX.Req';
262 | type LaunchFromWXReqEventHandler = (data: LaunchFromWXReq) => void;
263 |
264 | type WechatEventType = LaunchFromWXReqEventType;
265 | type WechatEventHandler = LaunchFromWXReqEventHandler;
266 | const handlerHolder = new ListenerHolder();
267 |
268 | export function addWechatListener(
269 | type: LaunchFromWXReqEventType,
270 | listener: LaunchFromWXReqEventHandler,
271 | );
272 |
273 | export function addWechatListener(
274 | type: WechatEventType,
275 | listener: WechatEventHandler,
276 | ): ListenerSubscription {
277 | return handlerHolder.addListener(type, listener);
278 | }
279 |
280 | DeviceEventEmitter.addListener('LaunchFromWX.Req', (data) => {
281 | handlerHolder.dispatch('LaunchFromWX.Req', data);
282 | });
283 |
--------------------------------------------------------------------------------
/ios/RCTWechat.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 10D6D7A91F160C250066F0F3 /* libRCTWechat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 86D238421BD0BB9E00C75D01 /* libRCTWechat.a */; };
11 | 867B79F6235EB79E00EE529A /* RCTWechat.m in Sources */ = {isa = PBXBuildFile; fileRef = 867B79F4235EB79E00EE529A /* RCTWechat.m */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXContainerItemProxy section */
15 | 10D6D7AA1F160C250066F0F3 /* PBXContainerItemProxy */ = {
16 | isa = PBXContainerItemProxy;
17 | containerPortal = 86D2383A1BD0BB9E00C75D01 /* Project object */;
18 | proxyType = 1;
19 | remoteGlobalIDString = 86D238411BD0BB9E00C75D01;
20 | remoteInfo = RCTWechat;
21 | };
22 | /* End PBXContainerItemProxy section */
23 |
24 | /* Begin PBXCopyFilesBuildPhase section */
25 | 86D238401BD0BB9E00C75D01 /* CopyFiles */ = {
26 | isa = PBXCopyFilesBuildPhase;
27 | buildActionMask = 2147483647;
28 | dstPath = "include/$(PRODUCT_NAME)";
29 | dstSubfolderSpec = 16;
30 | files = (
31 | );
32 | runOnlyForDeploymentPostprocessing = 0;
33 | };
34 | /* End PBXCopyFilesBuildPhase section */
35 |
36 | /* Begin PBXFileReference section */
37 | 10D6D7A41F160C250066F0F3 /* RCTWechatTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RCTWechatTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
38 | 867B79F4235EB79E00EE529A /* RCTWechat.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWechat.m; sourceTree = ""; };
39 | 867B79F5235EB79E00EE529A /* RCTWechat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWechat.h; sourceTree = ""; };
40 | 86D238421BD0BB9E00C75D01 /* libRCTWechat.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTWechat.a; sourceTree = BUILT_PRODUCTS_DIR; };
41 | /* End PBXFileReference section */
42 |
43 | /* Begin PBXFrameworksBuildPhase section */
44 | 10D6D7A11F160C250066F0F3 /* Frameworks */ = {
45 | isa = PBXFrameworksBuildPhase;
46 | buildActionMask = 2147483647;
47 | files = (
48 | 10D6D7A91F160C250066F0F3 /* libRCTWechat.a in Frameworks */,
49 | );
50 | runOnlyForDeploymentPostprocessing = 0;
51 | };
52 | 86D2383F1BD0BB9E00C75D01 /* Frameworks */ = {
53 | isa = PBXFrameworksBuildPhase;
54 | buildActionMask = 2147483647;
55 | files = (
56 | );
57 | runOnlyForDeploymentPostprocessing = 0;
58 | };
59 | /* End PBXFrameworksBuildPhase section */
60 |
61 | /* Begin PBXGroup section */
62 | 86D238391BD0BB9E00C75D01 = {
63 | isa = PBXGroup;
64 | children = (
65 | 867B79F5235EB79E00EE529A /* RCTWechat.h */,
66 | 867B79F4235EB79E00EE529A /* RCTWechat.m */,
67 | 86D238431BD0BB9E00C75D01 /* Products */,
68 | );
69 | sourceTree = "";
70 | };
71 | 86D238431BD0BB9E00C75D01 /* Products */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 86D238421BD0BB9E00C75D01 /* libRCTWechat.a */,
75 | 10D6D7A41F160C250066F0F3 /* RCTWechatTests.xctest */,
76 | );
77 | name = Products;
78 | sourceTree = "";
79 | };
80 | /* End PBXGroup section */
81 |
82 | /* Begin PBXNativeTarget section */
83 | 10D6D7A31F160C250066F0F3 /* RCTWechatTests */ = {
84 | isa = PBXNativeTarget;
85 | buildConfigurationList = 10D6D7AC1F160C250066F0F3 /* Build configuration list for PBXNativeTarget "RCTWechatTests" */;
86 | buildPhases = (
87 | 10D6D7A01F160C250066F0F3 /* Sources */,
88 | 10D6D7A11F160C250066F0F3 /* Frameworks */,
89 | 10D6D7A21F160C250066F0F3 /* Resources */,
90 | );
91 | buildRules = (
92 | );
93 | dependencies = (
94 | 10D6D7AB1F160C250066F0F3 /* PBXTargetDependency */,
95 | );
96 | name = RCTWechatTests;
97 | productName = RCTWechatTests;
98 | productReference = 10D6D7A41F160C250066F0F3 /* RCTWechatTests.xctest */;
99 | productType = "com.apple.product-type.bundle.unit-test";
100 | };
101 | 86D238411BD0BB9E00C75D01 /* RCTWechat */ = {
102 | isa = PBXNativeTarget;
103 | buildConfigurationList = 86D2384B1BD0BB9E00C75D01 /* Build configuration list for PBXNativeTarget "RCTWechat" */;
104 | buildPhases = (
105 | 86D2383E1BD0BB9E00C75D01 /* Sources */,
106 | 86D2383F1BD0BB9E00C75D01 /* Frameworks */,
107 | 86D238401BD0BB9E00C75D01 /* CopyFiles */,
108 | );
109 | buildRules = (
110 | );
111 | dependencies = (
112 | );
113 | name = RCTWechat;
114 | productName = RCTWechat;
115 | productReference = 86D238421BD0BB9E00C75D01 /* libRCTWechat.a */;
116 | productType = "com.apple.product-type.library.static";
117 | };
118 | /* End PBXNativeTarget section */
119 |
120 | /* Begin PBXProject section */
121 | 86D2383A1BD0BB9E00C75D01 /* Project object */ = {
122 | isa = PBXProject;
123 | attributes = {
124 | LastUpgradeCheck = 0830;
125 | ORGANIZATIONNAME = WeFlex;
126 | TargetAttributes = {
127 | 10D6D7A31F160C250066F0F3 = {
128 | CreatedOnToolsVersion = 8.3.2;
129 | DevelopmentTeam = QMP96B5DPW;
130 | ProvisioningStyle = Automatic;
131 | };
132 | 86D238411BD0BB9E00C75D01 = {
133 | CreatedOnToolsVersion = 7.0;
134 | DevelopmentTeam = JP4369ZCVM;
135 | };
136 | };
137 | };
138 | buildConfigurationList = 86D2383D1BD0BB9E00C75D01 /* Build configuration list for PBXProject "RCTWechat" */;
139 | compatibilityVersion = "Xcode 3.2";
140 | developmentRegion = English;
141 | hasScannedForEncodings = 0;
142 | knownRegions = (
143 | English,
144 | Base,
145 | );
146 | mainGroup = 86D238391BD0BB9E00C75D01;
147 | productRefGroup = 86D238431BD0BB9E00C75D01 /* Products */;
148 | projectDirPath = "";
149 | projectRoot = "";
150 | targets = (
151 | 86D238411BD0BB9E00C75D01 /* RCTWechat */,
152 | 10D6D7A31F160C250066F0F3 /* RCTWechatTests */,
153 | );
154 | };
155 | /* End PBXProject section */
156 |
157 | /* Begin PBXResourcesBuildPhase section */
158 | 10D6D7A21F160C250066F0F3 /* Resources */ = {
159 | isa = PBXResourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | );
163 | runOnlyForDeploymentPostprocessing = 0;
164 | };
165 | /* End PBXResourcesBuildPhase section */
166 |
167 | /* Begin PBXSourcesBuildPhase section */
168 | 10D6D7A01F160C250066F0F3 /* Sources */ = {
169 | isa = PBXSourcesBuildPhase;
170 | buildActionMask = 2147483647;
171 | files = (
172 | );
173 | runOnlyForDeploymentPostprocessing = 0;
174 | };
175 | 86D2383E1BD0BB9E00C75D01 /* Sources */ = {
176 | isa = PBXSourcesBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | 867B79F6235EB79E00EE529A /* RCTWechat.m in Sources */,
180 | );
181 | runOnlyForDeploymentPostprocessing = 0;
182 | };
183 | /* End PBXSourcesBuildPhase section */
184 |
185 | /* Begin PBXTargetDependency section */
186 | 10D6D7AB1F160C250066F0F3 /* PBXTargetDependency */ = {
187 | isa = PBXTargetDependency;
188 | target = 86D238411BD0BB9E00C75D01 /* RCTWechat */;
189 | targetProxy = 10D6D7AA1F160C250066F0F3 /* PBXContainerItemProxy */;
190 | };
191 | /* End PBXTargetDependency section */
192 |
193 | /* Begin XCBuildConfiguration section */
194 | 10D6D7AD1F160C250066F0F3 /* Debug */ = {
195 | isa = XCBuildConfiguration;
196 | buildSettings = {
197 | CLANG_ANALYZER_NONNULL = YES;
198 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
199 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
200 | CLANG_WARN_INFINITE_RECURSION = YES;
201 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
202 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
203 | DEVELOPMENT_TEAM = QMP96B5DPW;
204 | INFOPLIST_FILE = RCTWechatTests/Info.plist;
205 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
206 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
207 | PRODUCT_BUNDLE_IDENTIFIER = yorkie.RCTWechatTests;
208 | PRODUCT_NAME = "$(TARGET_NAME)";
209 | };
210 | name = Debug;
211 | };
212 | 10D6D7AE1F160C250066F0F3 /* Release */ = {
213 | isa = XCBuildConfiguration;
214 | buildSettings = {
215 | CLANG_ANALYZER_NONNULL = YES;
216 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
217 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
218 | CLANG_WARN_INFINITE_RECURSION = YES;
219 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
220 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
221 | DEVELOPMENT_TEAM = QMP96B5DPW;
222 | INFOPLIST_FILE = RCTWechatTests/Info.plist;
223 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
224 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
225 | PRODUCT_BUNDLE_IDENTIFIER = yorkie.RCTWechatTests;
226 | PRODUCT_NAME = "$(TARGET_NAME)";
227 | };
228 | name = Release;
229 | };
230 | 86D238491BD0BB9E00C75D01 /* Debug */ = {
231 | isa = XCBuildConfiguration;
232 | buildSettings = {
233 | ALWAYS_SEARCH_USER_PATHS = NO;
234 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
235 | CLANG_CXX_LIBRARY = "libc++";
236 | CLANG_ENABLE_MODULES = YES;
237 | CLANG_ENABLE_OBJC_ARC = YES;
238 | CLANG_WARN_BOOL_CONVERSION = YES;
239 | CLANG_WARN_CONSTANT_CONVERSION = YES;
240 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
241 | CLANG_WARN_EMPTY_BODY = YES;
242 | CLANG_WARN_ENUM_CONVERSION = YES;
243 | CLANG_WARN_INFINITE_RECURSION = YES;
244 | CLANG_WARN_INT_CONVERSION = YES;
245 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
246 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
247 | CLANG_WARN_UNREACHABLE_CODE = YES;
248 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
249 | COPY_PHASE_STRIP = NO;
250 | DEBUG_INFORMATION_FORMAT = dwarf;
251 | ENABLE_STRICT_OBJC_MSGSEND = YES;
252 | ENABLE_TESTABILITY = YES;
253 | GCC_C_LANGUAGE_STANDARD = gnu99;
254 | GCC_DYNAMIC_NO_PIC = NO;
255 | GCC_NO_COMMON_BLOCKS = YES;
256 | GCC_OPTIMIZATION_LEVEL = 0;
257 | GCC_PREPROCESSOR_DEFINITIONS = (
258 | "DEBUG=1",
259 | "$(inherited)",
260 | );
261 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
262 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
263 | GCC_WARN_UNDECLARED_SELECTOR = YES;
264 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
265 | GCC_WARN_UNUSED_FUNCTION = YES;
266 | GCC_WARN_UNUSED_VARIABLE = YES;
267 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
268 | MTL_ENABLE_DEBUG_INFO = YES;
269 | ONLY_ACTIVE_ARCH = YES;
270 | SDKROOT = iphoneos;
271 | };
272 | name = Debug;
273 | };
274 | 86D2384A1BD0BB9E00C75D01 /* Release */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | ALWAYS_SEARCH_USER_PATHS = NO;
278 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
279 | CLANG_CXX_LIBRARY = "libc++";
280 | CLANG_ENABLE_MODULES = YES;
281 | CLANG_ENABLE_OBJC_ARC = YES;
282 | CLANG_WARN_BOOL_CONVERSION = YES;
283 | CLANG_WARN_CONSTANT_CONVERSION = YES;
284 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
285 | CLANG_WARN_EMPTY_BODY = YES;
286 | CLANG_WARN_ENUM_CONVERSION = YES;
287 | CLANG_WARN_INFINITE_RECURSION = YES;
288 | CLANG_WARN_INT_CONVERSION = YES;
289 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
290 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
291 | CLANG_WARN_UNREACHABLE_CODE = YES;
292 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
293 | COPY_PHASE_STRIP = NO;
294 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
295 | ENABLE_NS_ASSERTIONS = NO;
296 | ENABLE_STRICT_OBJC_MSGSEND = YES;
297 | GCC_C_LANGUAGE_STANDARD = gnu99;
298 | GCC_NO_COMMON_BLOCKS = YES;
299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
301 | GCC_WARN_UNDECLARED_SELECTOR = YES;
302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
303 | GCC_WARN_UNUSED_FUNCTION = YES;
304 | GCC_WARN_UNUSED_VARIABLE = YES;
305 | IPHONEOS_DEPLOYMENT_TARGET = 9.0;
306 | MTL_ENABLE_DEBUG_INFO = NO;
307 | SDKROOT = iphoneos;
308 | VALIDATE_PRODUCT = YES;
309 | };
310 | name = Release;
311 | };
312 | 86D2384C1BD0BB9E00C75D01 /* Debug */ = {
313 | isa = XCBuildConfiguration;
314 | buildSettings = {
315 | HEADER_SEARCH_PATHS = (
316 | "$(inherited)",
317 | "$(SRCROOT)/../../React/**",
318 | "$(SRCROOT)/../react-native/React/**",
319 | "$(SRCROOT)/../node_modules/react-native/React/**",
320 | "$(SRCROOT)/../node_modules/react-native/Libraries/Image/**",
321 | );
322 | LIBRARY_SEARCH_PATHS = (
323 | "$(inherited)",
324 | "$(PROJECT_DIR)",
325 | );
326 | OTHER_LDFLAGS = "-ObjC";
327 | PRODUCT_NAME = "$(TARGET_NAME)";
328 | SKIP_INSTALL = YES;
329 | };
330 | name = Debug;
331 | };
332 | 86D2384D1BD0BB9E00C75D01 /* Release */ = {
333 | isa = XCBuildConfiguration;
334 | buildSettings = {
335 | HEADER_SEARCH_PATHS = (
336 | "$(inherited)",
337 | "$(SRCROOT)/../../React/**",
338 | "$(SRCROOT)/../react-native/React/**",
339 | "$(SRCROOT)/../node_modules/react-native/React/**",
340 | "$(SRCROOT)/../node_modules/react-native/Libraries/Image/**",
341 | );
342 | LIBRARY_SEARCH_PATHS = (
343 | "$(inherited)",
344 | "$(PROJECT_DIR)",
345 | );
346 | OTHER_LDFLAGS = "-ObjC";
347 | PRODUCT_NAME = "$(TARGET_NAME)";
348 | SKIP_INSTALL = YES;
349 | };
350 | name = Release;
351 | };
352 | /* End XCBuildConfiguration section */
353 |
354 | /* Begin XCConfigurationList section */
355 | 10D6D7AC1F160C250066F0F3 /* Build configuration list for PBXNativeTarget "RCTWechatTests" */ = {
356 | isa = XCConfigurationList;
357 | buildConfigurations = (
358 | 10D6D7AD1F160C250066F0F3 /* Debug */,
359 | 10D6D7AE1F160C250066F0F3 /* Release */,
360 | );
361 | defaultConfigurationIsVisible = 0;
362 | defaultConfigurationName = Release;
363 | };
364 | 86D2383D1BD0BB9E00C75D01 /* Build configuration list for PBXProject "RCTWechat" */ = {
365 | isa = XCConfigurationList;
366 | buildConfigurations = (
367 | 86D238491BD0BB9E00C75D01 /* Debug */,
368 | 86D2384A1BD0BB9E00C75D01 /* Release */,
369 | );
370 | defaultConfigurationIsVisible = 0;
371 | defaultConfigurationName = Release;
372 | };
373 | 86D2384B1BD0BB9E00C75D01 /* Build configuration list for PBXNativeTarget "RCTWechat" */ = {
374 | isa = XCConfigurationList;
375 | buildConfigurations = (
376 | 86D2384C1BD0BB9E00C75D01 /* Debug */,
377 | 86D2384D1BD0BB9E00C75D01 /* Release */,
378 | );
379 | defaultConfigurationIsVisible = 0;
380 | defaultConfigurationName = Release;
381 | };
382 | /* End XCConfigurationList section */
383 | };
384 | rootObject = 86D2383A1BD0BB9E00C75D01 /* Project object */;
385 | }
386 |
--------------------------------------------------------------------------------
/ios/RCTWechat.m:
--------------------------------------------------------------------------------
1 | //
2 | // RCTWechat.m
3 | // RCTWechat
4 | //
5 | // Created by shihuimiao on 2021/01/22.
6 | // Copyright © 2021 shihuimiao. All rights reserved.
7 | //
8 |
9 | #import "RCTWechat.h"
10 | #import "WXApiObject.h"
11 | #import
12 | #import
13 | #import
14 | #import
15 |
16 | // Define error messages
17 | #define NOT_REGISTERED (@"registerApp required")
18 | #define INVOKE_FAILED (@"Wechat API invoke returns false")
19 |
20 | // pending LaunchFromWX event if wechat not register
21 | static NSURL *pendingWeChatDelegateURL = nil;
22 | // wechat实例,如果为nil,代表SDK未初始化
23 | static RCTWechat *instanceManager = nil;
24 |
25 | static inline NSString* getWXCommonErrorCode() {
26 | return [NSString stringWithFormat:@"%d", WXErrCodeCommon];
27 | }
28 |
29 | @implementation RCTWechat
30 |
31 | @synthesize bridge = _bridge;
32 |
33 | RCT_EXPORT_MODULE(Wechat)
34 |
35 | - (dispatch_queue_t)methodQueue
36 | {
37 | return dispatch_get_main_queue();
38 | }
39 |
40 | + (BOOL)requiresMainQueueSetup
41 | {
42 | return YES;
43 | }
44 |
45 | + (BOOL)handleOpenURL:(NSURL *)url {
46 | if (!instanceManager) { // wechat sdk not init
47 | pendingWeChatDelegateURL = url;
48 | return NO;
49 | }
50 | return [WXApi handleOpenURL:url delegate:instanceManager];
51 | }
52 |
53 | + (BOOL)handleOpenUniversalLink:(NSUserActivity *)userActivity {
54 | if (!instanceManager) { // wechat sdk not init
55 | pendingWeChatDelegateURL = userActivity.webpageURL;
56 | return NO;
57 | }
58 |
59 | return [WXApi handleOpenUniversalLink:userActivity delegate:instanceManager];
60 | }
61 |
62 | - (void)handleWechatDeepLink {
63 | if (pendingWeChatDelegateURL) {
64 | [WXApi handleOpenURL:pendingWeChatDelegateURL delegate:instanceManager];
65 | pendingWeChatDelegateURL = nil;
66 | return;
67 | }
68 | }
69 |
70 | - (void)dealloc
71 | {
72 | [[NSNotificationCenter defaultCenter] removeObserver:self];
73 | }
74 |
75 | - (void)createImageRequest:(NSString *)url
76 | callback:(RCTImageLoaderCompletionBlock)callback
77 | {
78 | if (!url) {
79 | NSError *error = [NSError errorWithDomain:@"com.wechat.share"
80 | code:-1
81 | userInfo:@{NSLocalizedDescriptionKey:@"The value of url cannot be empty"}];
82 | callback(error, nil);
83 | return;
84 | }
85 | NSURL *imageURL = [NSURL URLWithString:url];
86 | if (!imageURL) {
87 | NSError *error = [NSError errorWithDomain:@"com.wechat.share"
88 | code:-1
89 | userInfo:@{NSLocalizedDescriptionKey:@"imageURL is invalid"}];
90 | callback(error, nil);
91 | return;
92 | }
93 | NSURLRequest *imageRequest = [NSURLRequest requestWithURL:imageURL];
94 | [[self.bridge moduleForClass:[RCTImageLoader class]] loadImageWithURLRequest:imageRequest callback:callback];
95 | }
96 |
97 |
98 | /// 图片压缩
99 | /// @param image 待压缩的图片
100 | /// @param maxLength 压缩到多大(单位:byte)
101 | - (NSData *)compressImage:(UIImage *)image toByte:(NSUInteger)maxLength
102 | {
103 | CGFloat compression = 1;
104 | NSData *data = UIImageJPEGRepresentation(image, compression);
105 | if (data.length < maxLength)
106 | {
107 | return data;
108 | }
109 |
110 | CGFloat max = 1;
111 | CGFloat min = 0;
112 | for (int i = 0; i < 6; ++i)
113 | {
114 | compression = (max + min) / 2;
115 | data = UIImageJPEGRepresentation(image, compression);
116 | if (data.length < maxLength * 0.9)
117 | {
118 | min = compression;
119 | }
120 | else if (data.length > maxLength)
121 | {
122 | max = compression;
123 | }
124 | else
125 | {
126 | break;
127 | }
128 | }
129 | UIImage *resultImage = [UIImage imageWithData:data];
130 | if (data.length < maxLength) return data;
131 |
132 | // Compress by size
133 | NSUInteger lastDataLength = 0;
134 | while (data.length > maxLength && data.length != lastDataLength)
135 | {
136 | lastDataLength = data.length;
137 | CGFloat ratio = (CGFloat)maxLength / data.length;
138 | CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)),
139 | (NSUInteger)(resultImage.size.height * sqrtf(ratio)));
140 | UIGraphicsBeginImageContext(size);
141 | [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)];
142 | resultImage = UIGraphicsGetImageFromCurrentImageContext();
143 | UIGraphicsEndImageContext();
144 | data = UIImageJPEGRepresentation(resultImage, compression);
145 | }
146 |
147 | if (data.length > maxLength)
148 | {
149 | return [self compressImage:resultImage toByte:maxLength];
150 | }
151 |
152 | return data;
153 | }
154 |
155 | /// wechat sdk register
156 | /// @param callback callback
157 | RCT_EXPORT_METHOD(registerApp:(NSString *)appid
158 | universalLink:(NSString *)universalLink
159 | resolver:(RCTPromiseResolveBlock)resolve
160 | rejecter:(RCTPromiseRejectBlock)reject)
161 | {
162 | instanceManager = self;
163 | self.appId = appid;
164 |
165 | BOOL registerAPP = [WXApi registerApp:appid universalLink:universalLink];
166 | if (registerAPP) {
167 | resolve([NSNull null]);
168 | }
169 | else {
170 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
171 | }
172 | [self handleWechatDeepLink];
173 | }
174 |
175 | RCT_EXPORT_METHOD(isWXAppInstalled:(RCTPromiseResolveBlock)resolve
176 | rejecter:(RCTPromiseRejectBlock)reject)
177 | {
178 | resolve(@([WXApi isWXAppInstalled]));
179 | }
180 |
181 | RCT_EXPORT_METHOD(isWXAppSupportApi:(RCTPromiseResolveBlock)resolve
182 | rejecter:(RCTPromiseRejectBlock)reject)
183 | {
184 | resolve(@([WXApi isWXAppSupportApi]));
185 | }
186 |
187 | // only iOS
188 | RCT_EXPORT_METHOD(getWXAppInstallUrl:(RCTResponseSenderBlock)callback)
189 | {
190 | callback(@[[NSNull null], [WXApi getWXAppInstallUrl]]);
191 | }
192 |
193 | // only iOS
194 | RCT_EXPORT_METHOD(getApiVersion:(RCTResponseSenderBlock)callback)
195 | {
196 | callback(@[[NSNull null], [WXApi getApiVersion]]);
197 | }
198 |
199 | RCT_EXPORT_METHOD(openWXApp:(RCTPromiseResolveBlock)resolve
200 | rejecter:(RCTPromiseRejectBlock)reject)
201 | {
202 | BOOL open = [WXApi openWXApp];
203 | if (open) {
204 | resolve([NSNull null]);
205 | }
206 | else {
207 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
208 | }
209 | }
210 |
211 | RCT_EXPORT_METHOD(openMiniprogram:(NSDictionary *)data
212 | resolver:(RCTPromiseResolveBlock)resolve
213 | rejecter:(RCTPromiseRejectBlock)reject)
214 | {
215 | WXLaunchMiniProgramReq *launchMiniProgramReq = [WXLaunchMiniProgramReq object];
216 | launchMiniProgramReq.userName = data[@"userName"];
217 | launchMiniProgramReq.path = data[@"path"];
218 | if (![data objectForKey:@"miniprogramType"]) {
219 | launchMiniProgramReq.miniProgramType = WXMiniProgramTypeRelease;
220 | }
221 | else {
222 | launchMiniProgramReq.miniProgramType = [data[@"miniprogramType"] integerValue];
223 | }
224 |
225 | [WXApi sendReq:launchMiniProgramReq completion:^(BOOL success) {
226 | if (success) {
227 | resolve([NSNull null]);
228 | }
229 | else {
230 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
231 | }
232 | }];
233 | }
234 |
235 | RCT_EXPORT_METHOD(sendAuthRequest:(NSString *)scope
236 | state:(NSString *)state
237 | resolver:(RCTPromiseResolveBlock)resolve
238 | rejecter:(RCTPromiseRejectBlock)reject)
239 | {
240 | if (!instanceManager) {
241 | reject(getWXCommonErrorCode(), NOT_REGISTERED, nil);
242 | return;
243 | }
244 | SendAuthReq *req = [[SendAuthReq alloc] init];
245 | req.scope = scope;
246 | req.state = state;
247 |
248 | [WXApi sendAuthReq:req
249 | viewController:RCTKeyWindow().rootViewController
250 | delegate:instanceManager
251 | completion:^(BOOL success) {
252 | if (success) {
253 | resolve([NSNull null]);
254 | }
255 | else {
256 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
257 | }
258 | }];
259 | }
260 |
261 |
262 | /// 微信签约支付
263 | /// @param callback callback
264 | RCT_EXPORT_METHOD(entrust:(NSDictionary *)data
265 | resolver:(RCTPromiseResolveBlock)resolve
266 | rejecter:(RCTPromiseRejectBlock)reject)
267 | {
268 | NSString *preEntrustWebId = [data objectForKey:@"preEntrustWebId"];
269 | if (!preEntrustWebId) {
270 | reject(getWXCommonErrorCode(), @"entrust: The value of preEntrustWebId cannot be empty", nil);
271 | return;
272 | }
273 | WXOpenBusinessWebViewReq *req = [[WXOpenBusinessWebViewReq alloc] init];
274 | req.businessType = 12; //固定值
275 | NSMutableDictionary *queryInfoDic = [NSMutableDictionary dictionary];
276 | [queryInfoDic setObject:preEntrustWebId forKey:@"pre_entrustweb_id"];
277 | req.queryInfoDic = queryInfoDic;
278 |
279 | [WXApi sendReq:req completion:^(BOOL success) {
280 | if (success) {
281 | resolve([NSNull null]);
282 | }
283 | else {
284 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
285 | }
286 | }];
287 | }
288 |
289 | RCT_EXPORT_METHOD(shareText:(NSDictionary *)data
290 | resolver:(RCTPromiseResolveBlock)resolve
291 | rejecter:(RCTPromiseRejectBlock)reject)
292 | {
293 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
294 | req.bText = YES;
295 | req.text = [data objectForKey:@"text"];
296 | req.scene = [data objectForKey:@"scene"] ? [[data objectForKey:@"scene"] intValue] : WXSceneSession;
297 | [WXApi sendReq:req completion:^(BOOL success) {
298 | if (success) {
299 | resolve([NSNull null]);
300 | }
301 | else {
302 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
303 | }
304 | }];
305 | }
306 |
307 | RCT_EXPORT_METHOD(shareImage:(NSDictionary *)data
308 | resolver:(RCTPromiseResolveBlock)resolve
309 | rejecter:(RCTPromiseRejectBlock)reject)
310 | {
311 | [self createImageRequest:[data objectForKey:@"imageUrl"] callback:^(NSError *err, UIImage *image) {
312 | if (err) {
313 | reject(getWXCommonErrorCode(), @"the imageUrl download failed", nil);
314 | return;
315 | }
316 | NSData *imageData = UIImageJPEGRepresentation(image, 1);
317 | WXImageObject *imageObject = [WXImageObject object];
318 | imageObject.imageData = imageData;
319 |
320 | WXMediaMessage *message = [WXMediaMessage message];
321 | message.mediaObject = imageObject;
322 | message.title = data[@"title"];
323 | message.description = data[@"description"];
324 |
325 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
326 | req.bText = NO;
327 | req.message = message;
328 | req.scene = [data[@"scene"] intValue];
329 | [WXApi sendReq:req completion:^(BOOL success) {
330 | if (success) {
331 | resolve([NSNull null]);
332 | }
333 | else {
334 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
335 | }
336 | }];
337 | }];
338 | }
339 |
340 | RCT_EXPORT_METHOD(shareMusic:(NSDictionary *)data
341 | resolver:(RCTPromiseResolveBlock)resolve
342 | rejecter:(RCTPromiseRejectBlock)reject)
343 | {
344 | [self createImageRequest:[data objectForKey:@"thumbImageUrl"] callback:^(NSError *err, UIImage *image) {
345 | if (err) {
346 | reject(getWXCommonErrorCode(), @"the imageUrl download failed", nil);
347 | return;
348 | }
349 | WXMusicObject *media = [WXMusicObject object];
350 | media.musicUrl = data[@"musicUrl"];
351 | media.musicLowBandUrl = data[@"musicLowBandUrl"];
352 | media.musicDataUrl = data[@"musicDataUrl"];
353 | media.musicLowBandDataUrl = data[@"musicLowBandDataUrl"];
354 |
355 | WXMediaMessage *message = [WXMediaMessage message];
356 | message.title = data[@"title"];
357 | message.description = data[@"description"];
358 | [message setThumbImage:image];
359 | message.mediaObject = media;
360 |
361 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
362 | req.bText = NO;
363 | req.message = message;
364 | req.scene = data[@"scene"] ? [data[@"scene"] intValue] : WXSceneSession;
365 | [WXApi sendReq:req completion:^(BOOL success) {
366 | if (success) {
367 | resolve([NSNull null]);
368 | }
369 | else {
370 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
371 | }
372 | }];
373 | }];
374 | }
375 |
376 | RCT_EXPORT_METHOD(shareVideo:(NSDictionary *)data
377 | resolver:(RCTPromiseResolveBlock)resolve
378 | rejecter:(RCTPromiseRejectBlock)reject)
379 | {
380 | [self createImageRequest:[data objectForKey:@"thumbImageUrl"] callback:^(NSError *err, UIImage *image) {
381 | WXVideoObject *media = [WXVideoObject object];
382 | media.videoUrl = data[@"videoUrl"];
383 | media.videoLowBandUrl = data[@"videoLowBandUrl"];
384 |
385 | WXMediaMessage *message = [WXMediaMessage message];
386 | message.title = data[@"title"];
387 | message.description = data[@"description"];
388 | [message setThumbImage:image];
389 | message.mediaObject = media;
390 |
391 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
392 | req.bText = NO;
393 | req.message = message;
394 | req.scene = data[@"scene"] ? [data[@"scene"] intValue] : WXSceneSession;
395 | [WXApi sendReq:req completion:^(BOOL success) {
396 | if (success) {
397 | resolve([NSNull null]);
398 | }
399 | else {
400 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
401 | }
402 | }];
403 | }];
404 | }
405 |
406 | RCT_EXPORT_METHOD(shareWebpage:(NSDictionary *)data
407 | resolver:(RCTPromiseResolveBlock)resolve
408 | rejecter:(RCTPromiseRejectBlock)reject)
409 | {
410 | NSString *imageURL = [data objectForKey:@"thumbImageUrl"];
411 | [self createImageRequest:imageURL callback:^(NSError *err, UIImage *image) {
412 | WXWebpageObject *webpageObject = [WXWebpageObject object];
413 | webpageObject.webpageUrl = [data objectForKey:@"webpageUrl"];
414 | WXMediaMessage *message = [WXMediaMessage message];
415 | message.title = [data objectForKey:@"title"];
416 | message.description = [data objectForKey:@"description"];
417 | [message setThumbImage:image];
418 | message.mediaObject = webpageObject;
419 |
420 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
421 | req.bText = NO;
422 | req.message = message;
423 | req.scene = data[@"scene"] ? [data[@"scene"] intValue] : WXSceneSession;
424 | [WXApi sendReq:req completion:^(BOOL success) {
425 | if (success) {
426 | resolve([NSNull null]);
427 | }
428 | else {
429 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
430 | }
431 | }];
432 | }];
433 | }
434 |
435 | RCT_EXPORT_METHOD(shareFile:(NSDictionary *)data
436 | resolver:(RCTPromiseResolveBlock)resolve
437 | rejecter:(RCTPromiseRejectBlock)reject)
438 | {
439 | WXFileObject *media = [WXFileObject object];
440 | media.fileData = [NSData dataWithContentsOfFile:data[@"filePath"]];
441 | media.fileExtension = data[@"fileExtension"];
442 |
443 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
444 | req.bText = NO;
445 | req.message = media;
446 | req.scene = data[@"scene"] ? [data[@"scene"] intValue] : WXSceneSession;
447 | [WXApi sendReq:req completion:^(BOOL success) {
448 | if (success) {
449 | resolve([NSNull null]);
450 | }
451 | else {
452 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
453 | }
454 | }];
455 | }
456 |
457 | RCT_EXPORT_METHOD(shareMiniprogram:(NSDictionary *)data
458 | resolver:(RCTPromiseResolveBlock)resolve
459 | rejecter:(RCTPromiseRejectBlock)reject)
460 | {
461 | NSString *hdImageUrl = data[@"thumbImageUrl"] ? data[@"thumbImageUrl"] : @"";
462 | [self createImageRequest:hdImageUrl callback:^(NSError *err, UIImage *image) {
463 | NSDictionary *miniprogram = [data objectForKey:@"miniprogram"];
464 | WXMiniProgramObject *object = [WXMiniProgramObject object];
465 | object.userName = [miniprogram objectForKey:@"userName"];
466 | object.path = [miniprogram objectForKey:@"path"];
467 | // 压缩图片到小于128KB
468 | object.hdImageData = image ? [self compressImage:image toByte:131072] : nil;
469 | object.miniProgramType = [miniprogram objectForKey:@"miniprogramType"] ? [[miniprogram objectForKey:@"miniprogramType"] integerValue] : WXMiniProgramTypeRelease;
470 | object.webpageUrl = [data objectForKey:@"webpageUrl"];
471 |
472 | WXMediaMessage *message = [WXMediaMessage message];
473 | message.title = data[@"title"];
474 | message.description = data[@"description"];
475 | message.mediaObject = object;
476 |
477 | SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init];
478 | req.bText = NO;
479 | req.message = message;
480 | req.scene = data[@"scene"] ? [data[@"scene"] intValue] : WXSceneSession;
481 |
482 | [WXApi sendReq:req completion:^(BOOL success) {
483 | if (success) {
484 | resolve([NSNull null]);
485 | }
486 | else {
487 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
488 | }
489 | }];
490 | }];
491 | }
492 |
493 | RCT_EXPORT_METHOD(pay:(NSDictionary *)data
494 | resolver:(RCTPromiseResolveBlock)resolve
495 | rejecter:(RCTPromiseRejectBlock)reject)
496 | {
497 | PayReq *req = [[PayReq alloc] init];
498 | req.partnerId = data[@"partnerId"];
499 | req.prepayId = data[@"prepayId"];
500 | req.nonceStr = data[@"nonceStr"];
501 | req.timeStamp = [data[@"timeStamp"] unsignedIntValue];
502 | req.package = data[@"package"];
503 | req.sign = data[@"sign"];
504 |
505 | [WXApi sendReq:req completion:^(BOOL success) {
506 | if (success) {
507 | resolve([NSNull null]);
508 | }
509 | else {
510 | reject(getWXCommonErrorCode(), INVOKE_FAILED, nil);
511 | }
512 | }];
513 | }
514 |
515 | #pragma mark - wx callback
516 |
517 | - (void)onReq:(BaseReq*)req
518 | {
519 | if ([req isKindOfClass:[LaunchFromWXReq class]]) {
520 | LaunchFromWXReq *launchReq = (LaunchFromWXReq *)req;
521 | NSString *appParameter = launchReq.message.messageExt;
522 | NSMutableDictionary *body = [[NSMutableDictionary alloc] init];
523 | body[@"type"] = @"LaunchFromWX.Req";
524 | body[@"lang"] = launchReq.lang;
525 | body[@"country"] = launchReq.country;
526 | body[@"extInfo"] = appParameter;
527 |
528 | [self.bridge.eventDispatcher sendDeviceEventWithName:RCTLaunchFromWXEvent body:body];
529 | }
530 | }
531 |
532 | - (void)onResp:(BaseResp*)resp
533 | {
534 | if ([resp isKindOfClass:[SendMessageToWXResp class]])
535 | {
536 | SendMessageToWXResp *r = (SendMessageToWXResp *)resp;
537 |
538 | NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy;
539 | body[@"errStr"] = r.errStr;
540 | body[@"lang"] = r.lang;
541 | body[@"country"] = r.country;
542 | [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXSendMessageEvent body:body];
543 | }
544 | else if ([resp isKindOfClass:[SendAuthResp class]])
545 | {
546 | SendAuthResp *r = (SendAuthResp *)resp;
547 | NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy;
548 | body[@"errStr"] = r.errStr;
549 | body[@"state"] = r.state;
550 | body[@"lang"] = r.lang;
551 | body[@"country"] = r.country;
552 | body[@"code"] = r.code;
553 |
554 | [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXSendAuthEvent body:body];
555 | }
556 | else if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]])
557 | {
558 | WXLaunchMiniProgramResp *r = (WXLaunchMiniProgramResp *)resp;
559 | NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy;
560 | body[@"data"] = r.extMsg;
561 | [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXLaunchMiniprogramEvent body:body];
562 | }
563 | else if ([resp isKindOfClass:[PayResp class]])
564 | {
565 | PayResp *r = (PayResp *)resp;
566 | NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy;
567 | body[@"errStr"] = r.errStr;
568 | body[@"returnKey"] = r.returnKey;
569 | [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXPayEvent body:body];
570 | }
571 | else if ([resp isKindOfClass:[WXOpenBusinessWebViewResp class]]) {
572 | WXOpenBusinessWebViewResp *business = (WXOpenBusinessWebViewResp *)resp;
573 | NSMutableDictionary *body = @{@"errCode":@(business.errCode)}.mutableCopy;
574 | body[@"errStr"] = business.errStr;
575 | body[@"result"] = business.result;
576 | body[@"businessType"] = @(business.businessType);
577 | [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXOpenBusinessWebViewEvent body:body];
578 | }
579 | }
580 |
581 | @end
582 |
--------------------------------------------------------------------------------
/android/src/main/java/com/theweflex/react/WechatModule.java:
--------------------------------------------------------------------------------
1 | package com.theweflex.react;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.net.Uri;
7 |
8 | import com.facebook.common.executors.UiThreadImmediateExecutorService;
9 | import com.facebook.common.references.CloseableReference;
10 | import com.facebook.common.util.UriUtil;
11 | import com.facebook.datasource.DataSource;
12 | import com.facebook.drawee.backends.pipeline.Fresco;
13 | import com.facebook.imagepipeline.core.ImagePipeline;
14 | import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
15 | import com.facebook.imagepipeline.image.CloseableImage;
16 | import com.facebook.imagepipeline.request.ImageRequest;
17 | import com.facebook.imagepipeline.request.ImageRequestBuilder;
18 | import com.facebook.react.bridge.Arguments;
19 | import com.facebook.react.bridge.Promise;
20 | import com.facebook.react.bridge.ReactApplicationContext;
21 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
22 | import com.facebook.react.bridge.ReactMethod;
23 | import com.facebook.react.bridge.ReadableMap;
24 | import com.facebook.react.bridge.WritableMap;
25 | import com.facebook.react.module.annotations.ReactModule;
26 | import com.facebook.react.modules.core.DeviceEventManagerModule;
27 | import com.tencent.mm.opensdk.constants.ConstantsAPI;
28 | import com.tencent.mm.opensdk.modelbase.BaseReq;
29 | import com.tencent.mm.opensdk.modelbase.BaseResp;
30 | import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram;
31 | import com.tencent.mm.opensdk.modelbiz.WXOpenBusinessWebview;
32 | import com.tencent.mm.opensdk.modelmsg.SendAuth;
33 | import com.tencent.mm.opensdk.modelmsg.SendMessageToWX;
34 | import com.tencent.mm.opensdk.modelmsg.ShowMessageFromWX;
35 | import com.tencent.mm.opensdk.modelmsg.WXAppExtendObject;
36 | import com.tencent.mm.opensdk.modelmsg.WXFileObject;
37 | import com.tencent.mm.opensdk.modelmsg.WXImageObject;
38 | import com.tencent.mm.opensdk.modelmsg.WXMediaMessage;
39 | import com.tencent.mm.opensdk.modelmsg.WXMiniProgramObject;
40 | import com.tencent.mm.opensdk.modelmsg.WXMusicObject;
41 | import com.tencent.mm.opensdk.modelmsg.WXTextObject;
42 | import com.tencent.mm.opensdk.modelmsg.WXVideoObject;
43 | import com.tencent.mm.opensdk.modelmsg.WXWebpageObject;
44 | import com.tencent.mm.opensdk.modelpay.PayReq;
45 | import com.tencent.mm.opensdk.modelpay.PayResp;
46 | import com.tencent.mm.opensdk.openapi.IWXAPI;
47 | import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
48 | import com.tencent.mm.opensdk.openapi.WXAPIFactory;
49 |
50 | import java.io.ByteArrayOutputStream;
51 | import java.util.ArrayList;
52 | import java.util.HashMap;
53 | import java.util.UUID;
54 |
55 | import javax.annotation.Nullable;
56 |
57 | @ReactModule(name = WechatModule.NAME)
58 | public class WechatModule extends ReactContextBaseJavaModule implements IWXAPIEventHandler {
59 | public final static String NAME = "Wechat";
60 | private String appId;
61 | private IWXAPI api = null;
62 | private final static String NOT_REGISTERED = "registerApp required.";
63 | private final static String INVOKE_FAILED = "Wechat API invoke returns false.";
64 | private final static String INVALID_ARGUMENT = "invalid argument.";
65 | private final static int THUMB_SIZE = 32; // The size of thumb image in KB.
66 | private static Intent wechatIntent;
67 |
68 | private static byte[] bitmapToBytesArray(Bitmap bitmap, final boolean needRecycle) {
69 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
70 | bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
71 | if (needRecycle) {
72 | bitmap.recycle();
73 | }
74 | try {
75 | baos.close();
76 | } catch (Exception e) {
77 | e.printStackTrace();
78 | }
79 | return baos.toByteArray();
80 | }
81 |
82 | /**
83 | * Compress the bitmap image
84 | */
85 | private static byte[] bitmapResizeGetBytes(Bitmap image, int size, final boolean needRecycle) {
86 | // FIXME(little-snow-fox): 该算法存在效率问题,希望有"义士"可以进行优化
87 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
88 | // 质量压缩方法,这里100表示第一次不压缩,把压缩后的数据缓存到 baos
89 | image.compress(Bitmap.CompressFormat.JPEG, 10, baos);
90 | int options = 10;
91 | // 循环判断压缩后依然大于 32kb 则继续压缩
92 | while (baos.toByteArray().length / 1024 > size) {
93 | // 重置baos即清空baos
94 | baos.reset();
95 | // 每次都减少1
96 | options += 1;
97 | // 这里压缩options%,把压缩后的数据存放到baos中
98 | image.compress(Bitmap.CompressFormat.JPEG, 10 / options * 10, baos);
99 | }
100 | if (needRecycle)
101 | image.recycle();
102 |
103 | return baos.toByteArray();
104 | }
105 |
106 | public WechatModule(ReactApplicationContext context) {
107 | super(context);
108 | }
109 |
110 | @Override
111 | public String getName() {
112 | return NAME;
113 | }
114 |
115 | /**
116 | * fix Native module WechatModule tried to override WechatModule for module name RCTWechat.
117 | * If this was your intention, return true from WechatModule#canOverrideExistingModule() bug
118 | *
119 | * @return
120 | */
121 | public boolean canOverrideExistingModule() {
122 | return true;
123 | }
124 |
125 | private static ArrayList modules = new ArrayList<>();
126 |
127 | @Override
128 | public void initialize() {
129 | super.initialize();
130 | modules.add(this);
131 | }
132 |
133 | @Override
134 | public void onCatalystInstanceDestroy() {
135 | super.onCatalystInstanceDestroy();
136 | if (api != null) {
137 | api = null;
138 | }
139 | modules.remove(this);
140 | }
141 |
142 | /**
143 | * @param intent 冷启动需要将intent信息缓存,等到微信注册好再处理intent数据
144 | */
145 | public static void handleIntent(Intent intent) {
146 | wechatIntent = intent;
147 | for (WechatModule mod : modules) {
148 | if (mod.api != null)
149 | mod.api.handleIntent(wechatIntent, mod);
150 | }
151 | }
152 |
153 | @ReactMethod
154 | public void registerApp(String appid, String universalLink, Promise promise) {
155 | this.appId = appid;
156 | api = WXAPIFactory.createWXAPI(this.getReactApplicationContext().getBaseContext(), appid, true);
157 | boolean result = api.registerApp(appid);
158 | if (result) {
159 | promise.resolve(null);
160 | for (WechatModule mod : modules) {
161 | if (mod.api != null && wechatIntent != null)
162 | mod.api.handleIntent(wechatIntent, mod);
163 | }
164 | } else {
165 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", "register failed");
166 | }
167 | }
168 |
169 | @ReactMethod
170 | public void isWXAppInstalled(Promise promise) {
171 | if (api == null) {
172 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", NOT_REGISTERED);
173 | return;
174 | }
175 | promise.resolve(api.isWXAppInstalled());
176 | }
177 |
178 | @ReactMethod
179 | public void isWXAppSupportApi(Promise promise) {
180 | if (api == null) {
181 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", NOT_REGISTERED);
182 | return;
183 | }
184 | promise.resolve(api.getWXAppSupportAPI());
185 | }
186 |
187 | @ReactMethod
188 | public void openWXApp(Promise promise) {
189 | if (api == null) {
190 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", NOT_REGISTERED);
191 | return;
192 | }
193 | boolean result = api.openWXApp();
194 | if (result) {
195 | promise.resolve(null);
196 | } else {
197 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", "register failed");
198 | }
199 |
200 | }
201 |
202 | @ReactMethod
203 | public void openMiniprogram(ReadableMap data, Promise promise) {
204 | if (api == null) {
205 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", NOT_REGISTERED);
206 | return;
207 | }
208 | WXLaunchMiniProgram.Req req = new WXLaunchMiniProgram.Req();
209 | // 填小程序原始ID
210 | req.userName = data.getString("userName");
211 | // 拉起小程序页面的可带参路径,不填默认拉起小程序首页
212 | req.path = data.getString("path");
213 | // 可选打开开发版,体验版和正式版
214 | req.miniprogramType = data.getInt("miniprogramType");
215 | boolean success = api.sendReq(req);
216 | if (!success) {
217 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", INVALID_ARGUMENT);
218 | }
219 | }
220 |
221 | @ReactMethod
222 | public void sendAuthRequest(String scope, String state, Promise promise) {
223 | if (api == null) {
224 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", NOT_REGISTERED);
225 | return;
226 | }
227 | SendAuth.Req req = new SendAuth.Req();
228 | req.scope = scope;
229 | req.state = state;
230 | boolean result = api.sendReq(req);
231 | if (result) {
232 | promise.resolve(null);
233 | } else {
234 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", "send Auth Request failed");
235 | }
236 | }
237 |
238 | private void sendShareRequest(final WXMediaMessage.IMediaObject media, final ReadableMap data, final Promise promise) {
239 | if (data.hasKey("thumbImageUrl")) {
240 | createImageRequest(Uri.parse(data.getString("thumbImageUrl")), new ImageCallback() {
241 | @Override
242 | public void invoke(@Nullable Bitmap thumb) {
243 | WechatModule.this.sendShareRequest(media, thumb, data, promise);
244 | }
245 | });
246 | } else {
247 | this.sendShareRequest(media, null, data, promise);
248 | }
249 | }
250 |
251 | private void sendShareRequest(WXMediaMessage.IMediaObject media, Bitmap thumb, ReadableMap data, Promise promise) {
252 | WXMediaMessage message = new WXMediaMessage();
253 | message.mediaObject = media;
254 | if (data.hasKey("title")) {
255 | message.title = data.getString("title");
256 | }
257 | if (data.hasKey("description")) {
258 | message.description = data.getString("description");
259 | }
260 | if (data.hasKey("mediaTagName")) {
261 | message.mediaTagName = data.getString("mediaTagName");
262 | }
263 | if (data.hasKey("messageAction")) {
264 | message.messageAction = data.getString("messageAction");
265 | }
266 | if (data.hasKey("messageExt")) {
267 | message.messageExt = data.getString("messageExt");
268 | }
269 | if (thumb != null) {
270 | byte[] thumbData = bitmapToBytesArray(thumb, false);
271 | if (thumbData.length / 1024 > THUMB_SIZE) {
272 | message.thumbData = bitmapResizeGetBytes(thumb, THUMB_SIZE, true);
273 | } else {
274 | message.thumbData = thumbData;
275 | thumb.recycle();
276 | }
277 | }
278 |
279 | SendMessageToWX.Req req = new SendMessageToWX.Req();
280 | req.scene = data.hasKey("scene") ? data.getInt("scene") : SendMessageToWX.Req.WXSceneSession;
281 | req.transaction = UUID.randomUUID().toString();
282 | req.message = message;
283 | boolean result = api.sendReq(req);
284 | if (result) {
285 | promise.resolve(null);
286 | } else {
287 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", "sendReq failed");
288 | }
289 | }
290 |
291 | /**
292 | * Share the plain text
293 | *
294 | * @param data
295 | * @param promise
296 | */
297 | @ReactMethod
298 | public void shareText(ReadableMap data, Promise promise) {
299 | WXTextObject media = new WXTextObject();
300 | media.text = data.getString("text");
301 | this.sendShareRequest(media, null, data, promise);
302 | }
303 |
304 | /**
305 | * 分享图片
306 | *
307 | * @param data
308 | * @param promise
309 | */
310 | @ReactMethod
311 | public void shareImage(final ReadableMap data, final Promise promise) {
312 | Uri imgUrl;
313 | try {
314 | imgUrl = Uri.parse(data.getString("imageUrl"));
315 | if (imgUrl.getScheme() == null) {
316 | // handle static resource if no schema is provided.
317 | // todo Drawable TYPE
318 | imgUrl = getResourceDrawableURI(getReactApplicationContext(), "");
319 | }
320 | } catch (Exception ex) {
321 | imgUrl = null;
322 | }
323 |
324 | if (imgUrl == null) {
325 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", "imgUrl is null");
326 | return;
327 | }
328 | createImageRequest(imgUrl, new ImageCallback() {
329 | @Override
330 | public void invoke(@Nullable Bitmap image) {
331 | WXImageObject media = new WXImageObject(image);
332 | WechatModule.this.sendShareRequest(media, image/* as thumb */, data, promise);
333 | }
334 | });
335 | }
336 |
337 | /**
338 | * 分享音乐
339 | *
340 | * @param data
341 | * @param promise
342 | */
343 | @ReactMethod
344 | public void shareMusic(final ReadableMap data, final Promise promise) {
345 | WXMusicObject media = new WXMusicObject();
346 | media.musicUrl = data.hasKey("musicUrl") ? data.getString("musicUrl") : null;
347 | media.musicLowBandUrl = data.hasKey("musicLowBandUrl") ? data.getString("musicLowBandUrl") : null;
348 | media.musicDataUrl = data.hasKey("musicDataUrl") ? data.getString("musicDataUrl") : null;
349 | media.musicUrl = data.hasKey("musicUrl") ? data.getString("musicUrl") : null;
350 | media.musicLowBandDataUrl = data.hasKey("musicLowBandDataUrl") ? data.getString("musicLowBandDataUrl") : null;
351 | this.sendShareRequest(media, data, promise);
352 | }
353 |
354 | /**
355 | * 分享视频
356 | *
357 | * @param data
358 | * @param promise
359 | */
360 | @ReactMethod
361 | public void shareVideo(final ReadableMap data, final Promise promise) {
362 | WXVideoObject media = new WXVideoObject();
363 | media.videoUrl = data.hasKey("videoUrl") ? data.getString("videoUrl") : null;
364 | media.videoLowBandUrl = data.hasKey("videoLowBandUrl") ? data.getString("videoLowBandUrl") : null;
365 | this.sendShareRequest(media, data, promise);
366 | }
367 |
368 | /**
369 | * 分享网页
370 | *
371 | * @param data
372 | * @param promise
373 | */
374 | @ReactMethod
375 | public void shareWebpage(final ReadableMap data, final Promise promise) {
376 | WXWebpageObject media = new WXWebpageObject();
377 | media.webpageUrl = data.hasKey("webpageUrl") ? data.getString("webpageUrl") : null;
378 | this.sendShareRequest(media, data, promise);
379 | }
380 |
381 | /**
382 | * 分享网页
383 | *
384 | * @param data
385 | * @param promise
386 | */
387 | @ReactMethod
388 | public void shareFile(final ReadableMap data, final Promise promise) {
389 | WXFileObject media = new WXFileObject(data.getString("filePath"));
390 | this.sendShareRequest(media, data, promise);
391 | }
392 |
393 | /**
394 | * 分享小程序
395 | *
396 | * @param data
397 | * @param promise
398 | */
399 | @ReactMethod
400 | public void shareMiniprogram(final ReadableMap data, final Promise promise) {
401 | WXMiniProgramObject media = new WXMiniProgramObject();
402 | ReadableMap miniprogram = data.getMap("miniprogram");
403 | media.webpageUrl = data.hasKey("webpageUrl") ? data.getString("webpageUrl") : null;
404 | media.miniprogramType = miniprogram.hasKey("miniprogramType") ? miniprogram.getInt("miniprogramType") : WXMiniProgramObject.MINIPTOGRAM_TYPE_RELEASE;
405 | media.userName = miniprogram.hasKey("userName") ? miniprogram.getString("userName") : null;
406 | media.path = miniprogram.hasKey("path") ? miniprogram.getString("path") : null;
407 | this.sendShareRequest(media, data, promise);
408 |
409 | }
410 |
411 | @ReactMethod
412 | public void pay(ReadableMap data, Promise promise) {
413 | PayReq payReq = new PayReq();
414 | if (data.hasKey("partnerId")) {
415 | payReq.partnerId = data.getString("partnerId");
416 | }
417 | if (data.hasKey("prepayId")) {
418 | payReq.prepayId = data.getString("prepayId");
419 | }
420 | if (data.hasKey("nonceStr")) {
421 | payReq.nonceStr = data.getString("nonceStr");
422 | }
423 | if (data.hasKey("timeStamp")) {
424 | payReq.timeStamp = data.getString("timeStamp");
425 | }
426 | if (data.hasKey("sign")) {
427 | payReq.sign = data.getString("sign");
428 | }
429 | if (data.hasKey("package")) {
430 | payReq.packageValue = data.getString("package");
431 | }
432 | if (data.hasKey("extData")) {
433 | payReq.extData = data.getString("extData");
434 | }
435 | payReq.appId = appId;
436 | boolean result = api.sendReq(payReq);
437 | if (result) {
438 | promise.resolve(null);
439 | } else {
440 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", INVOKE_FAILED);
441 | }
442 | }
443 |
444 | @ReactMethod
445 | public void entrust(final ReadableMap data, final Promise promise) {
446 | if (!data.hasKey("preEntrustWebId")) {
447 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", INVALID_ARGUMENT);
448 | return;
449 | }
450 |
451 | WXOpenBusinessWebview.Req req = new WXOpenBusinessWebview.Req();
452 | req.businessType = 12;//固定值
453 | HashMap queryInfo = new HashMap<>();
454 | queryInfo.put("pre_entrustweb_id", data.getString("preEntrustWebId"));
455 | req.queryInfo = queryInfo;
456 | boolean result = api.sendReq(req);
457 | if (result) {
458 | promise.resolve(null);
459 | } else {
460 | promise.reject(BaseResp.ErrCode.ERR_COMM + "", INVOKE_FAILED);
461 | }
462 | }
463 |
464 | private static void createImageRequest(Uri uri, final ImageCallback imageCallback) {
465 | BaseBitmapDataSubscriber dataSubscriber = new BaseBitmapDataSubscriber() {
466 | @Override
467 | protected void onNewResultImpl(Bitmap bitmap) {
468 | if (bitmap != null) {
469 | if (bitmap.getConfig() != null) {
470 | bitmap = bitmap.copy(bitmap.getConfig(), true);
471 | imageCallback.invoke(bitmap);
472 | } else {
473 | bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
474 | imageCallback.invoke(bitmap);
475 | }
476 | } else {
477 | imageCallback.invoke(null);
478 | }
479 | }
480 |
481 | @Override
482 | protected void onFailureImpl(DataSource> dataSource) {
483 | imageCallback.invoke(null);
484 | }
485 | };
486 |
487 | ImageRequestBuilder builder = ImageRequestBuilder.newBuilderWithSource(uri);
488 | // if (resizeOptions != null) {
489 | // builder = builder.setResizeOptions(resizeOptions);
490 | // }
491 | ImageRequest imageRequest = builder.build();
492 | ImagePipeline imagePipeline = Fresco.getImagePipeline();
493 | DataSource> dataSource = imagePipeline.fetchDecodedImage(imageRequest, null);
494 | dataSource.subscribe(dataSubscriber, UiThreadImmediateExecutorService.getInstance());
495 | }
496 |
497 | private static Uri getResourceDrawableURI(Context ctx, String name) {
498 | if (name == null || name.isEmpty()) {
499 | return null;
500 | }
501 |
502 | // FIXME(Yorkie): what's this for?
503 | name = name.toLowerCase().replace("-", "_");
504 | int id = ctx.getResources().getIdentifier(name, "drawable", ctx.getPackageName());
505 | if (id == 0) {
506 | return null;
507 | } else {
508 | return new Uri.Builder()
509 | .scheme(UriUtil.LOCAL_RESOURCE_SCHEME)
510 | .path(String.valueOf(id))
511 | .build();
512 | }
513 | }
514 |
515 | @Override
516 | public void onReq(BaseReq req) {
517 | switch (req.getType()) {
518 | case ConstantsAPI.COMMAND_SHOWMESSAGE_FROM_WX:
519 | goToShowMsg((ShowMessageFromWX.Req) req);
520 | break;
521 | default:
522 | break;
523 | }
524 | }
525 |
526 | private void goToShowMsg(ShowMessageFromWX.Req showReq) {
527 | WXMediaMessage wxMsg = showReq.message;
528 | WXAppExtendObject obj = (WXAppExtendObject) wxMsg.mediaObject;
529 | WritableMap map = Arguments.createMap();
530 | map.putString("country", showReq.country);
531 | map.putString("lang", showReq.lang);
532 | map.putString("extInfo", obj.extInfo);
533 | this.getReactApplicationContext()
534 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
535 | .emit("LaunchFromWX.Req", map);
536 | }
537 |
538 | @Override
539 | public void onResp(BaseResp baseResp) {
540 | WritableMap map = Arguments.createMap();
541 | String emitType = null;
542 | map.putInt("errCode", baseResp.errCode);
543 | map.putString("errStr", baseResp.errStr);
544 | map.putString("openId", baseResp.openId);
545 | map.putString("transaction", baseResp.transaction);
546 |
547 | if (baseResp instanceof SendAuth.Resp) {
548 | SendAuth.Resp resp = (SendAuth.Resp) (baseResp);
549 | emitType = "SendAuth.Resp";
550 | map.putString("code", resp.code);
551 | map.putString("state", resp.state);
552 | map.putString("url", resp.url);
553 | map.putString("lang", resp.lang);
554 | map.putString("country", resp.country);
555 | } else if (baseResp instanceof SendMessageToWX.Resp) {
556 | SendMessageToWX.Resp resp = (SendMessageToWX.Resp) (baseResp);
557 | emitType = "SendMessageToWX.Resp";
558 | } else if (baseResp instanceof WXOpenBusinessWebview.Resp) {
559 | emitType = "WXOpenBusinessWebview.Resp";
560 | map.putInt("businessType", ((WXOpenBusinessWebview.Resp) baseResp).businessType);
561 | map.putString("resultInfo", ((WXOpenBusinessWebview.Resp) baseResp).resultInfo);
562 | } else if (baseResp.getType() == ConstantsAPI.COMMAND_LAUNCH_WX_MINIPROGRAM) {
563 | WXLaunchMiniProgram.Resp resp = (WXLaunchMiniProgram.Resp) (baseResp);
564 | emitType = "LaunchMiniprogram.Resp";
565 | map.putString("data", resp.extMsg);
566 | } else if (baseResp instanceof PayResp) {
567 | PayResp resp = (PayResp) (baseResp);
568 | emitType = "PayReq.Resp";
569 | map.putString("returnKey", resp.returnKey);
570 | }
571 | if (emitType != null) {
572 | this.getReactApplicationContext()
573 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
574 | .emit(emitType, map);
575 | }
576 | }
577 |
578 | private interface ImageCallback {
579 | void invoke(@Nullable Bitmap bitmap);
580 | }
581 |
582 | private interface MediaObjectCallback {
583 | void invoke(@Nullable WXMediaMessage.IMediaObject mediaObject);
584 | }
585 |
586 | }
587 |
--------------------------------------------------------------------------------