作者 AnstJJ

one

正在显示 66 个修改的文件 包含 1391 行增加0 行删除
# react-native-jj-unionpay
[![npm version](https://img.shields.io/npm/v/react-native-jy-unionpay.svg)](https://www.npmjs.com/package/react-native-jy-unionpay)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](http://makeapullrequest.com)
本插件参考 [银联官方 - 手机支付控件(含安卓Pay)文档](https://open.unionpay.com/tjweb/acproduct/list?apiservId=450)
## 安装
### React Native 0.60 或以上
```sh
yarn add react-native-jy-unionpay
cd ios && pod install # for iOS
```
### React Native 0.59 以下
```sh
yarn add react-native-jy-unionpay
react-native link react-native-jy-unionpay
```
### iOS 配置
- 在工程 info.plist 设置中添加一个 URL Types 回调协议,用于在支付完成后返回商户客户端。请注意 URL Schemes 需要是唯一的。
<p align="center"><img src="https://raw.githubusercontent.com/caipeiming/react-native-jy-unionpay/master/ios_url_type.png" alt="Xcode set iOS urltypes"></p>
- http 请求设置(ats)
在测试环境测试时,需要在工程对应的 plist 文件中添加 NSAppTransportSecurity Dictionary 并同时设置里面NSAllowsArbitraryLoads 属性值为 YES,具体设置可参 照以下截图:
<p align="center"><img src="https://raw.githubusercontent.com/caipeiming/react-native-jy-unionpay/master/ios_ns_allows_arbitrary_loads.png" alt="Xcode set iOS NSAppTransportSecurity"></p>
向 Apple 发布正式版本时请删除此设置。
- 添加协议白名单
在 Xcode7.0 之后的版本中进行开发,需要在工程对应的 plist 文件中,添加 LSApplicationQueriesSchemes Array 并加入 uppaysdk、uppaywallet、uppayx1、 uppayx2、uppayx3 五个 item,具体设置可参考以下截图:
<p align="center"><img src="https://raw.githubusercontent.com/caipeiming/react-native-jy-unionpay/master/ios_ls_application_queries_schemes.png" alt="Xcode set Info.plist LSApplicationQueriesSchemes"></p>
或者直接添加如下代码到 plist 文件中:
```
<key>LSApplicationQueriesSchemes</key>
<array>
<string>uppaysdk</string>
<string>uppaywallet</string>
<string>uppayx1</string>
<string>uppayx2</string>
<string>uppayx3</string>
</array>
```
- AppDelegate.m 的 下面 @end 前面添加下面代码
```
// iOS 9.x or newer
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
// iOS 8.x or older
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
return [RCTLinkingManager application:application openURL:url sourceApplication:sourceApplication annotation:annotation];
}
```
## 使用
```javascript
import {Unionpay, UnionpayEmitter, UNIONPAY_MODAL_PRODUCTION, UNIONPAY_MODAL_DEVELOPMENT} from 'react-native-china-unionpay';
```
## Api
| Method | Return Type | iOS | Android |
| ----------------------------------------------------------------- | ------------------- | :--: | :-----: |
| [startPay(tn: string, mode: string)](#startpaytn-string-mode-string) | `void` | ✅ | ✅ |
| [startSEPay(tn: string, mode: string, seType: string)](#startsepaytn-string-mode-string-setype-string) | `void` | ❌ | ✅ |
| [getSEPayInfo()](#getsepayinfo) | `Promise<object>` | ❌ | ✅ |
| [checkWalletInstalled()](#checkwalletinstalled) | `Promise<boolean>` | ❌ | ✅ |
| [isPaymentAppInstalled()](#ispaymentappinstalled) | `Promise<boolean>` | ✅ | ❌ |
---
### startPay(tn: string, mode: string)
tn - 交易流水号
mode - 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
通过银联工具类启动支付插件,支付结果在 [`UnionpayEmitter`](#UnionpayEmitter) 事件中获取。
#### Examples
```js
Unionpay.startPay(tn, UNIONPAY_MODAL_DEVELOPMENT);
```
### startSEPay(tn: string, mode: string, seType: string)
tn - 交易流水号
mode - 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
seType - 手机pay支付类别
调用指定手机Pay支付接口(startSEPay())之前,需要先调用检查手机Pay状态接口(getSEPayInfo())获取seType,startSEPay()调用方式同startPay()。
#### Examples
```js
Unionpay.startSEPay(tn, UNIONPAY_MODAL_DEVELOPMENT, seType);
```
### getSEPayInfo()
检查手机 Pay 状态接口
#### Examples
```js
Unionpay.getSEPayInfo().then(data => {
console.log(data);
}).catch(error => {
console.log(error.message);
});
```
### checkWalletInstalled()
检测是否已安装云闪付客户端接口调用
#### Examples
```js
Unionpay.checkWalletInstalled().then(data => {
console.log(data);
}).catch(error => {
console.log(error.message);
});
```
### isPaymentAppInstalled()
检测是否已安装银联 App 接口调用
#### Examples
```js
Unionpay.isPaymentAppInstalled().then(data => {
console.log(data);
});
```
### UnionpayEmitter
用于支付结果的事件订阅。支付结果的 `pay_result` 参数有3个选项:
- success 支付成功
- fail 支付失败
- cancel 用户取消了支付
#### Examples
```js
export default class App extends Component {
constructor(props) {
super(props);
this.onUnionPayResponse = this._onUnionPayResponse.bind(this);
}
componentDidMount() {
UnionpayEmitter.on("UnionPayResponse", this.onUnionPayResponse);
}
componentWillUnmount() {
this.isMount = false;
UnionpayEmitter.removeListener('UnionPayResponse', this.onUnionPayResponse);
}
_onUnionPayResponse(data) {
console.log(data);
}
}
```
### mode 常量
连接环境,有2个常量选项
- UNIONPAY_MODAL_PRODUCTION
银联正式环境
- UNIONPAY_MODAL_DEVELOPMENT
银联测试环境,该环境中不发生真实交易
## 测试账号
参考官网 [测试环境的测试卡信息](https://open.unionpay.com/tjweb/support/faq/mchlist?id=4)
测试卡信息-前台类交易
```
招商银行借记卡:6226090000000048
手机号:18100000000
密码:111101
短信验证码:111111(先点获取验证码之后再输入)
证件类型:01
证件号:510265790128303
姓名:张三
```
```
华夏银行贷记卡:6226388000000095
手机号:18100000000
cvn2:248
有效期:1225(后台接口注意格式YYMM需倒一下)
短信验证码:111111(先点获取验证码之后再输入)
证件类型:01
证件号:510265790128303
姓名:张三
```
```
平安银行借记卡:6216261000000000018
手机号:13552535506
证件类型:01
证件号:341126197709218366
密码:123456
姓名:全渠道
短信验证码:111111(先点获取验证码之后再输入)
```
```
平安银行贷记卡:6221558812340000
手机号:13552535506
cvn2:123
有效期:1123(后台接口注意格式YYMM需倒一下)
短信验证码:111111(先点获取验证码之后再输入)
姓名:互联网
证件类型:01
证件号:341126197709218366
```
```
农行贷记卡:5200831111111113
手机号:13552535506
cvn2:123
有效期:1125(后台接口注意格式YYMM需倒一下)
短信验证码:111111(先点获取验证码之后再输入)
姓名:全渠道
证件类型:01
身份证号:341126197709218366
```
```
平安银行贷记卡:6221558812340013(信用卡还款被还款信用卡)
手机号:13552535506
cvn2:123
有效期:1123(后台接口注意格式YYMM需倒一下)
短信验证码:111111(先点获取验证码之后再输入)
姓名:全渠道
证件类型:01
证件号:341126197709218366
```
# 示例
- 参考 [example](https://github.com/caipeiming/react-native-china-unionpay/tree/master/example)
- 安卓手机也可以直接安装已编译的 [apk](https://github.com/caipeiming/react-native-china-unionpay/releases)
... ...
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.1'
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 28
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
}
sourceSets.main {
jni.srcDirs = []
// 让 android studio 识别libs下的 .so 第三方包
jniLibs.srcDirs = ['libs']
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
\ No newline at end of file
... ...
#Sun Apr 12 11:37:40 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip
... ...
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
... ...
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
... ...
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
不能预览此文件类型
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jy.rn.unionpay">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc.hce" />
<uses-permission android:name="org.simalliance.openmobileapi.SMARTCARD" />
<application>
<uses-library
android:name="org.simalliance.openmobileapi"
android:required="false" />
<activity
android:name="com.unionpay.uppay.PayActivity"
android:configChanges="orientation|keyboardHidden"
android:excludeFromRecents="true"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
<activity
android:name="com.unionpay.UPPayWapActivity"
android:configChanges="orientation|keyboardHidden|fontScale"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"></activity>
</application>
</manifest>
... ...
不能预览此文件类型
package com.jy.rn.unionpay;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.unionpay.UPPayAssistEx;
import com.unionpay.UPQuerySEPayInfoCallback;
import com.unionpay.UPSEInfoResp;
import org.json.JSONException;
import org.json.JSONObject;
import javax.annotation.Nonnull;
public class RCTUnionpayModule extends ReactContextBaseJavaModule implements ActivityEventListener {
private final String TAG = "RCTUnionpayModule";
private ReactApplicationContext reactContext;
public RCTUnionpayModule(@Nonnull ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
this.reactContext.addActivityEventListener(this);
}
@Nonnull
@Override
public String getName() {
return "UnionPayModule";
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
// 处理银联手机支付控件返回的支付结果
if (data == null) {
return;
}
WritableMap response = Arguments.createMap();
String msg = "";
/*
* 支付控件返回字符串:success、fail、cancel 分别代表支付成功,支付失败,支付取消
*/
String str = data.getExtras().getString("pay_result");
if (str.equalsIgnoreCase("success")) {
// 如果想对结果数据验签,可使用下面这段代码,但建议不验签,直接去商户后台查询交易结果
// result_data结构见c)result_data参数说明
if (data.hasExtra("result_data")) {
String result = data.getExtras().getString("result_data");
try {
JSONObject resultJson = new JSONObject(result);
String sign = resultJson.getString("sign");
String dataOrg = resultJson.getString("data");
response.putString("sign", sign);
response.putString("data", dataOrg);
/*
// 此处的verify建议送去商户后台做验签
// 如要放在手机端验,则代码必须支持更新证书
boolean ret = verify(dataOrg, sign, mMode);
if (ret) {
// 验签成功,显示支付结果
msg = "支付成功!";
} else {
// 验签失败
msg = "支付失败!";
}
*/
} catch (JSONException e) {
}
}
// 结果result_data为成功时,去商户后台查询一下再展示成功
msg = "支付成功!";
} else if (str.equalsIgnoreCase("fail")) {
msg = "支付失败!";
} else if (str.equalsIgnoreCase("cancel")) {
msg = "用户取消了支付";
}
response.putString("pay_result", str);
response.putString("msg", msg);
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("UnionPayResponse", response);
}
@Override
public void onNewIntent(Intent intent) {
}
/**
* 通过银联工具类启动支付插件
*
* @param tn 交易流水号
* @param mode 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
*/
@ReactMethod
public void startPay(String tn, String mode) {
UPPayAssistEx.startPay(getCurrentActivity(), null, null, tn, mode);
}
/**
* 指定手机 pay 支付接口
*
* @param tn 交易流水号
* @param mode 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
* @param seType 手机pay支付类别
*/
@ReactMethod
public void startSEPay(String tn, String mode, String seType) {
UPPayAssistEx.startSEPay(getCurrentActivity(), null, null, tn, mode, seType);
}
/**
* 检查手机 pay 状态的接口
*
* @param promise
*/
@ReactMethod
public void getSEPayInfo(final Promise promise) {
UPPayAssistEx.getSEPayInfo(this.reactContext, new UPQuerySEPayInfoCallback() {
@Override
public void onResult(String SEName, String seType, int cardNumbers, Bundle reserved) {
WritableMap response = Arguments.createMap();
response.putString("SEName", SEName);
response.putString("seType", seType);
response.putInt("cardNumbers", cardNumbers);
response.putMap("reserved", Arguments.fromBundle(reserved));
promise.resolve(response);
}
@Override
public void onError(String SEName, String seType, String errorCode, String errorDesc) {
if (errorDesc == null) {
if (errorCode.equals(UPSEInfoResp.ERROR_NOT_SUPPORT)) {
errorDesc = "硬件不支持 XXpay";
} else if (errorCode.equals(UPSEInfoResp.ERROR_NOT_READY)) {
errorDesc = "未开通 XXpay";
} else if (errorCode.equals(UPSEInfoResp.ERROR_NONE)) {
errorDesc = "可用卡数为0";
} else if (errorCode.equals(UPSEInfoResp.ERROR_TSM_UNINSTALLED)) {
errorDesc = "检测未安装 TSM 控件";
} else if (errorCode.equals(UPSEInfoResp.ERROR_TIMEOUT)) {
errorDesc = "接口超时";
}
}
WritableMap response = Arguments.createMap();
response.putString("SEName", SEName);
response.putString("seType", seType);
response.putString("errorCode", errorCode);
response.putString("errorDesc", errorDesc);
promise.reject(errorCode, errorDesc, response);
}
});
}
/**
* 检查是否安装云闪付客户端的接口
*
* @param promise
*/
@ReactMethod
public void checkWalletInstalled(Promise promise) {
boolean res = UPPayAssistEx.checkWalletInstalled(getCurrentActivity());
promise.resolve(res);
}
}
... ...
package com.jy.rn.unionpay;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class RCTUnionpayPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Arrays.<NativeModule>asList(new RCTUnionpayModule(reactContext));
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
... ...
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Unionpay</string>
</resources>
\ No newline at end of file
... ...
#import <React/RCTBridgeModule.h>
#import "RCTEventEmitter.h"
@interface RNUnionpay : RCTEventEmitter <RCTBridgeModule>
@property NSString* schemeStr;
@end
... ...
#import "RNUnionpay.h"
#import "UPPaymentControl.h"
@implementation RNUnionpay
- (instancetype)init
{
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:@"RCTOpenURLNotification" object:nil];
}
self.schemeStr = nil;
NSArray *urlTypes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"];
if (urlTypes != nil && urlTypes.count > 0) {
NSArray *urlSchemes = [urlTypes.firstObject objectForKey:@"CFBundleURLSchemes"];
if(urlSchemes.count > 0) {
self.schemeStr = [urlSchemes firstObject];
}
}
return self;
}
+ (BOOL)requiresMainQueueSetup{
return YES;
}
- (NSArray<NSString *> *)supportedEvents
{
return @[@"UnionPayResponse"];
}
- (BOOL)handleOpenURL:(NSNotification *)aNotification
{
NSString * aURLString = [aNotification userInfo][@"url"];
NSURL * aURL = [NSURL URLWithString:aURLString];
[[UPPaymentControl defaultControl] handlePaymentResult:aURL completeBlock:^(NSString *code, NSDictionary *data) {
NSString *sign;
NSMutableDictionary *body = [[NSMutableDictionary alloc]init];
[body setValue:code forKey:@"pay_result"];
//结果code为成功时,去商户后台查询一下确保交易是成功的再展示成功
if([code isEqualToString:@"success"]) {
[body setValue:@"支付成功!" forKey:@"msg"];
//判断签名数据是否存在
//如果没有签名数据,建议商户app后台查询交易结果
if(data == nil){
[body setValue:nil forKey:@"sign"];
} else {
//数据从NSDictionary转换为NSString
NSData *signData = [NSJSONSerialization dataWithJSONObject:data
options:0
error:nil];
sign = [[NSString alloc] initWithData:signData encoding:NSUTF8StringEncoding];
[body setValue:sign forKey:@"sign"];
}
}
else if([code isEqualToString:@"fail"]) { //交易失败
[body setValue:@"支付失败!" forKey:@"msg"];
}
else if([code isEqualToString:@"cancel"]) { //交易取消
[body setValue:@"用户取消了支付" forKey:@"msg"];
}
[self sendEventWithName:@"UnionPayResponse" body:body];
}];
return YES;
}
RCT_EXPORT_MODULE(UnionPayModule)
RCT_EXPORT_METHOD(startPay:(NSString*)tn :(NSString*)mode)
{
//当获得的tn不为空时,调用支付接口
if (tn != nil && tn.length > 0){
dispatch_async(dispatch_get_main_queue(), ^(void) {
UIWindow *window = [UIApplication sharedApplication].keyWindow;
UIViewController *rootViewController = window.rootViewController;
[[UPPaymentControl defaultControl] startPay:tn
fromScheme:self.schemeStr mode:mode
viewController:rootViewController];
});
}
}
/// 当判断用户手机上已安装银联App,商户客户端可以做相应个性化处理
/// @param resolve
/// @param reject
RCT_EXPORT_METHOD(isPaymentAppInstalled:(RCTPromiseResolveBlock)resolve :(RCTPromiseRejectBlock)reject)
{
if([[UPPaymentControl defaultControl] isPaymentAppInstalled]) {
resolve(@(YES));
} else {
resolve(@(NO));
}
}
@end
... ...
require "json"
package = JSON.parse(File.read('../package.json'))
Pod::Spec.new do |s|
s.name = "RNUnionpay"
s.version = package['version']
s.summary = package["description"]
s.license = "MIT"
s.author = "qijingyu2013"
s.platform = :ios, "8.0"
s.homepage = "https://github.com/qijingyu2013/react-native-jy-unionpay"
s.source = { :git => "https://github.com/qijingyu2013/react-native-jy-unionpay", :tag => "1.1.0" }
s.source_files = "RNUnionpay/**/*.{h,m}"
s.requires_arc = true
s.frameworks = 'CFNetwork','SystemConfiguration'
s.libraries = 'z'
s.vendored_libraries = "RNUnionpay/paymentcontrol/*.a"
s.dependency "React"
end
... ...
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
B3E7B58A1CC2AC0600A0062D /* RNUnionpay.m in Sources */ = {isa = PBXBuildFile; fileRef = B3E7B5891CC2AC0600A0062D /* RNUnionpay.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
58B511D91A9E6C8500147676 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134814201AA4EA6300B7C361 /* libRNUnionpay.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNUnionpay.a; sourceTree = BUILT_PRODUCTS_DIR; };
A31BAFC2242C5E4B00674CE4 /* UPPaymentControl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UPPaymentControl.h; sourceTree = "<group>"; };
A31BAFC3242C5E4B00674CE4 /* libPaymentControl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libPaymentControl.a; sourceTree = "<group>"; };
B3E7B5881CC2AC0600A0062D /* RNUnionpay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RNUnionpay.h; sourceTree = "<group>"; };
B3E7B5891CC2AC0600A0062D /* RNUnionpay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RNUnionpay.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
58B511D81A9E6C8500147676 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
134814211AA4EA7D00B7C361 /* Products */ = {
isa = PBXGroup;
children = (
134814201AA4EA6300B7C361 /* libRNUnionpay.a */,
);
name = Products;
sourceTree = "<group>";
};
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
A31BAFC1242C5E4B00674CE4 /* paymentcontrol */,
B3E7B5881CC2AC0600A0062D /* RNUnionpay.h */,
B3E7B5891CC2AC0600A0062D /* RNUnionpay.m */,
134814211AA4EA7D00B7C361 /* Products */,
);
sourceTree = "<group>";
};
A31BAFC1242C5E4B00674CE4 /* paymentcontrol */ = {
isa = PBXGroup;
children = (
A31BAFC2242C5E4B00674CE4 /* UPPaymentControl.h */,
A31BAFC3242C5E4B00674CE4 /* libPaymentControl.a */,
);
path = paymentcontrol;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
58B511DA1A9E6C8500147676 /* RNUnionpay */ = {
isa = PBXNativeTarget;
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNUnionpay" */;
buildPhases = (
58B511D71A9E6C8500147676 /* Sources */,
58B511D81A9E6C8500147676 /* Frameworks */,
58B511D91A9E6C8500147676 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = RNUnionpay;
productName = RCTDataManager;
productReference = 134814201AA4EA6300B7C361 /* libRNUnionpay.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
58B511D31A9E6C8500147676 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0830;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
58B511DA1A9E6C8500147676 = {
CreatedOnToolsVersion = 6.1.1;
};
};
};
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNUnionpay" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = 58B511D21A9E6C8500147676;
productRefGroup = 58B511D21A9E6C8500147676;
projectDirPath = "";
projectRoot = "";
targets = (
58B511DA1A9E6C8500147676 /* RNUnionpay */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
58B511D71A9E6C8500147676 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B3E7B58A1CC2AC0600A0062D /* RNUnionpay.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
58B511ED1A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
name = Debug;
};
58B511EE1A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
58B511F01A9E6C8500147676 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNUnionpay;
SKIP_INSTALL = YES;
};
name = Debug;
};
58B511F11A9E6C8500147676 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../react-native/React/**",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = RNUnionpay;
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RNUnionpay" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511ED1A9E6C8500147676 /* Debug */,
58B511EE1A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RNUnionpay" */ = {
isa = XCConfigurationList;
buildConfigurations = (
58B511F01A9E6C8500147676 /* Debug */,
58B511F11A9E6C8500147676 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
}
... ...
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
... ...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
... ...
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>RNUnionpay.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
... ...
// !$*UTF8*$!
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:RNUnionpay.xcodeproj">
</FileRef>
</Workspace>
\ No newline at end of file
... ...
//
// PaymentControl.h
// PaymentControl
//
// Created by qcao on 15/10/20.
// Copyright © 2015年 China Unionpay Co.,Ltd. All rights reserved.
// v3.3.14 bulid1
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef void (^UPPaymentResultBlock)(NSString* code, NSDictionary* data);
@interface UPPaymentControl : NSObject
/**
* 创建支付单例服务
*
* @return 返回单例对象
*/
+ (UPPaymentControl *)defaultControl;
/**
* 支付接口
*
* @param tn 订单信息
* @param schemeStr 调用支付的app注册在info.plist中的scheme
* @param mode 支付环境
* @param viewController 启动支付控件的viewController
* @return 返回成功失败
*/
- (BOOL)startPay:(NSString*)tn
fromScheme:(NSString *)schemeStr
mode:(NSString*)mode
viewController:(UIViewController*)viewController;
/**
* APP是否已安装检测接口,通过该接口得知用户是否安装银联支付的APP。
*
* @return 返回是否已经安装了银联支付APP
*/
- (BOOL)isPaymentAppInstalled;
/**
* 处理钱包或者独立快捷app支付跳回商户app携带的支付结果Url
*
* @param url 支付结果url,传入后由SDK解析
* @param completionBlock 结果回调,保证跳转钱包支付过程中,即使调用方app被系统kill时,能通过这个回调取到支付结果。
*/
- (void)handlePaymentResult:(NSURL*)url completeBlock:(UPPaymentResultBlock)completionBlock;
@end
... ...
不能预览此文件类型
{
"name": "react-native-jy-unionpay",
"version": "1.0.3",
"description": "银联的react-native模块",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"react",
"native",
"rn",
"wechat",
"alipay",
"pay",
"jy",
"react-native",
"react-native-jy-unionpay",
"china unionpay",
"unionpay",
"china",
"银联",
"中国银联",
"银联支付",
"中国银联支付"
],
"author": "jingyu.qi",
"homepage": "https://github.com/qijingyu2013/react-native-jy-unionpay.git",
"license": "MIT",
"peerDependencies": {
"react-native": "^0.41.2"
},
"dependencies": {
"events": "^3.1.0"
}
}
... ...
import { NativeModules } from 'react-native';
const { UnionPayModule } = NativeModules;
class Api {
/**
* 通过银联工具类启动支付插件
* @param {[type]} tn: string 交易流水号
* @param {[type]} mode: string 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
*/
startPay(tn: string, mode: string) {
UnionPayModule.startPay(tn, mode);
}
/**
* 指定手机 pay 支付接口
* @param {[type]} tn: string 交易流水号
* @param {[type]} mode: string 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
* @param {[type]} seType: string 手机pay支付类别
*/
startSEPay(tn: string, mode: string, seType: string) {
UnionPayModule.startSEPay(tn, mode, seType);
}
/**
* 检查手机 pay 状态的接口
* @return {[type]} Promise
*/
getSEPayInfo(): Promise {
return UnionPayModule.getSEPayInfo();
}
/**
* 检查是否安装云闪付客户端的接口
* @return {[type]} Promise
*/
checkWalletInstalled(): Promise {
return UnionPayModule.checkWalletInstalled();
}
}
export const Unionpay = new Api();
\ No newline at end of file
... ...
import { NativeModules } from 'react-native';
const { UnionPayModule } = NativeModules;
class Api {
/**
* 通过银联工具类启动支付插件
* @param {[type]} tn: string 交易流水号
* @param {[type]} mode: string 连接环境:"00" - 银联正式环境 "01" - 银联测试环境,该环境中不发生真实交易
*/
startPay(tn: string, mode: string) {
UnionPayModule.startPay(tn, mode);
}
/**
* 检查是否安装银联 App 的接口
* @return {[type]} Promise
*/
isPaymentAppInstalled(): Promise {
return UnionPayModule.isPaymentAppInstalled();
}
}
export const Unionpay = new Api();
\ No newline at end of file
... ...
export * from './api';
export * from './unionpay-event';
export * from './unionpay-modal';
\ No newline at end of file
... ...
import { NativeModules } from 'react-native';
export const NativeModule = NativeModules.UnionPayModule
... ...
import { NativeEventEmitter } from 'react-native'
import EventEmitter from 'events';
import { NativeModule } from './native-module'
class MyEmitter extends EventEmitter {}
export const UnionpayEmitter = new MyEmitter();
const nativeEventEmitter = new NativeEventEmitter(NativeModule);
nativeEventEmitter.addListener('UnionPayResponse', (data) => {
UnionpayEmitter.emit('UnionPayResponse', data);
});
\ No newline at end of file
... ...
export const UNIONPAY_MODAL_PRODUCTION = "00";
export const UNIONPAY_MODAL_DEVELOPMENT = "01";
\ No newline at end of file
... ...