最近对接银联的云闪付小程序,发现有很多地方文档写的都不是很全,今天给大家说一下对接需要避免哪些坑。
前端项目
引入Js
接入方使用云闪付 APP 小程序 API 前,需在调用页面(必须为HTTPS)引入插件 upsdk.js 文件
// 普通项目引入
<script type="text/javascript" src="https://open.95516.com/s/open/js/upsdk.js"></script>
// vue项目引入
npm install upsdk-vue
版本要求:[云闪付 APP 安卓 8.0.5 及以上,IOS8.0.4 及以上]
初始化
upsdk.config({
// 必填,接入方的唯一标识
appId: "",
// 必填,生成签名的时间戳 单位是秒
timestamp: "",
// 必填,生成签名的随机串 16位字符串
nonceStr: "",
// 必填,签名因子包括 appId、frontToken、nonceStr、 timestamp、url
signature: "",
// true:开启,云闪付APP会将调试信息toast
debug: true
})
唤起支付
TN号的生成, 手机支付控件demo 传送
// 如果是Vue需要在Main.js引入以下代码
import upsdk from 'upsdk-vue'
upsdk.pay({
tn: '支付流水号 TN 号',
success: function(){
// 支付成功, 开发者执行后续操作。
},
fail: function(err){
// 支付失败, err.msg 是失败原因描述, 比如 TN 号不合法, 或者用户取消了交易等等。
}
});
后端项目
FrontToken的获取
/**
* 获取云闪付 frontToken
*
* {"resp":"00","msg":"成功","params":{"frontToken":"zOOrOQmURU2t7aJgUEDJxA==","expiresIn":7200}
* @return
*/
public String getYsfFrontToken() {
Map<String, String> sortedParams = new HashMap<>();
sortedParams.put("appId", "");
sortedParams.put("secret", "");
// 16位随机字符串
sortedParams.put("nonceStr", getRandomString(16));
// 以秒为单位
sortedParams.put("timestamp", System.currentTimeMillis() / 1000 + "");
// 签名值,签名因子包括(appId,nonceStr, secret, timestamp)
String signContent = SignUtils.getSignContent(sortedParams);
// 删除secret
sortedParams.remove("secret");
// appId,nonceStr, secret, timestamp
sortedParams.put("signature", sha256(signContent).getBytes());
String strJson = new JSONObject(sortedParams).toString();
String result = postJson("https://open.95516.com/open/access/1.0/frontToken", "application/json", strJson);
logger.info("获取云闪付frontToken===》" + result);
JSONObject resultJson = new JSONObject(result);
if ("00".equals(resultJson.getStr("resp"))) {
JSONObject paramsJson = new JSONObject(resultJson.getStr("params"));
return paramsJson.getStr("frontToken");
}
return null;
}
注意: 这个frontToken 有有效期,建议存放在Redis中。
获取 upsdk.config 参数
Map<String, String> map = new HashMap<String, String>();
map.put("appId", "");
map.put("frontToken", "");
map.put("url", url);
// 16位随机字符串
map.put("nonceStr", YSRequestUtil.getRandomString(16));
// 以秒为单位
map.put("timestamp", System.currentTimeMillis() / 1000 + "");
// 排序
String signContent = com.ydzy.common.util.yspay.util.SignUtils.getSignContent(map);
// 签名: appId、frontToken、nonceStr、 timestamp、url
map.put("signature", sha256(signContent).getBytes());
// 这个frontToken不需要传递给前端
map.remove("frontToken");
upsdk.config({
// 必填,接入方的唯一标识
appId: "",
// 必填,生成签名的时间戳 单位是秒
timestamp: "",
// 必填,生成签名的随机串 16位字符串
nonceStr: "",
// 必填,签名因子包括 appId、frontToken、nonceStr、 timestamp、url
signature: "",
// true:开启,云闪付APP会将调试信息toast
debug: true
})
相关公共方法
生成指定长度的字符串工具方法
/**
* 生成指定长度的字符串
*
* @param length 字符串长度
* @return
*/
public static String getRandomString(int length) {
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(3);
long result = 0;
switch (number) {
case 0:
result = Math.round(Math.random() * 25 + 65);
sb.append(String.valueOf((char) result));
break;
case 1:
result = Math.round(Math.random() * 25 + 97);
sb.append(String.valueOf((char) result));
break;
case 2:
sb.append(String.valueOf(new Random().nextInt(10)));
break;
}
}
return sb.toString();
}
Map排序工具方法
public static String getSignContent(Map<String, String> sortedParams) {
StringBuffer content = new StringBuffer();
List<String> keys = new ArrayList<>(sortedParams.keySet());
Collections.sort(keys);
int index = 0;
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = sortedParams.get(key);
if (StringUtils.areNotEmpty(key, value)) {
content.append((index == 0 ? "" : "&") + key + "=" + value);
index++;
}
}
return content.toString();
}
sha256工具方法
public static String sha256(byte[] data) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
return bytesToHex(md.digest(data));
} catch (Exception ex) {
logger.info("Never happen.", ex);
return null;
}
}
Http工具方法
public static String postJson(String generalUrl, String contentType, String params) {
try {
URL url = new URL(generalUrl);
// 打开和URL之间的连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
// 设置通用的请求属性
connection.setRequestProperty("Content-Type", contentType);
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
// 得到请求的输出流对象
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
out.write(params.getBytes("UTF-8"));
out.flush();
out.close();
// 建立实际的连接
connection.connect();
// 获取所有响应头字段
Map<String, List<String>> headers = connection.getHeaderFields();
// 定义 BufferedReader输入流来读取URL的响应
BufferedReader in = null;
in = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "UTF-8"));
String result = "";
String getLine;
while ((getLine = in.readLine()) != null) {
result += getLine;
}
in.close();
return result;
} catch (Exception e) {
log.info("发送请求失败:异常信息:" + e.getMessage());
e.printStackTrace();
}
return null;
}
相关文档
https://qxwouffjun.feishu.cn/docs/doccnZIjijXe0aDbP98mQ0Dvxhd#avGcJU
https://qxwouffjun.feishu.cn/docs/doccnKWnxdjnbNj2JZHhdsE0COf
https://qxwouffjun.feishu.cn/docs/doccntIbkpdwdce2meNup1Yk2Fb
目前云闪付最新的这个文档,没有获取frontToken接口。
云闪付小程序官方技术交流群:457767672,不懂的可以进群资讯。
以上代码写完记得上传发布,然后通过云闪付App进行测试,不要在 云闪付开发者工具 里面进行测试,这个开发者工具无法唤起支付。
打赏
当前共有 0 条评论