Merge remote-tracking branch 'origin/1.0' into 1.0

This commit is contained in:
余文财 2025-06-18 18:40:42 +08:00
commit 265644b473
34 changed files with 3764 additions and 0 deletions

20
pom.xml
View File

@ -135,6 +135,26 @@
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<!--微信支付-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.7.0</version>
</dependency>
<!--添加JDOM库-->
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,42 @@
package com.dpkj.common.constant;
/**
* @description:
* @author: Zhangxue
* @time: 2025/5/28 11:04
*/
public interface WxConstant {
/**
* 门店编号 由商户定义 各门店唯一
* TODO 修改
*/
String STORE_ID = "1111111111111";
//门店名称由商户定义可用于展示;中文会导致调用失败
String STORE_TEXT = "device8998No1";
//终端设备编号由商户定义
String DEVICE_ID = "device8998";
//版本号固定为1
String VERSION = "1";
//参数签名,使用MD5
String SING_TYPE = "MD5";
/**
* 支付类型
*/
String FACEPAY = "FACEPAY";
/**
* 结果状态 成功
*/
String STATE_SUCCESS = "SUCCESS";
/**
* 结果状态 失败
*/
String STATE_FAIL = "FAIL";
}

View File

@ -0,0 +1,52 @@
package com.dpkj.modules.scanface.ali.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @description: 支付宝刷脸配置信息
* @author: Zhangxue
* @time: 2025/4/16 20:23
*/
@Data
@Component
@ConfigurationProperties(prefix = "dpkj.ali.face")
public class AliFaceConfig {
/**
* ABCP_SDK部署后DLL文件路径
*/
private String dllPath;
/**
* 应用管理-appid
*/
private String appId;
/**
* IOT应用版本
*/
private String appVersion;
/**
* 签约商家的 PID 2088 开头,企业主体
*/
private String merchantId;
/**
* 商家机具终端编号每台设备保持唯一
*/
private String deviceNum;
/**
* 服务商的 PID
*/
private String partnerId;
/**
* 核心入参 serviceId
*/
private String serviceId;
}

View File

@ -0,0 +1,25 @@
package com.dpkj.modules.scanface.ali.constants;
/**
* @description: 支付宝刷脸模块常量
* @author: Zhangxue
* @time: 2025/4/24 9:25
*/
public final class AliFaceConstants {
/**
* 刷脸去初始化服务
* https://ant-iot.alipay.com/open/iotbpaas/service/serviceManage/serviceDetail
*/
public static final String SMILEVERIFYNIN_V1 = "BPaaSSmileVerifyNonInitV1";
/**
* 刷脸支付服务
* <a href="https://ant-iot.alipay.com/open/iotbpaas/app/appManage/newAppPublish/serviceDetail">...</a>
*/
public static final String SMILEPAY = "BPaaSFaceSmilePayVerify";
}

View File

@ -0,0 +1,114 @@
package com.dpkj.modules.scanface.ali.controller;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.scanface.ali.config.AliFaceConfig;
import com.dpkj.modules.scanface.ali.constants.AliFaceConstants;
import com.dpkj.modules.scanface.ali.dll.AbcpInvoke;
import com.dpkj.modules.scanface.ali.service.IAliScanFaceService;
import com.dpkj.modules.scanface.ali.vo.AliOrderVo;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.File;
import java.util.Map;
/**
* @Auther: 萧道子
* @Date: 2025/4/16 16:47
* @Description:
*/
@Slf4j
@AllArgsConstructor
@RestController
@RequestMapping("/scanface/ali")
public class AliScanFaceController {
private final IAliScanFaceService aliScanFaceService;
@Resource
private AliFaceConfig aliFaceConfig;
/**
* 完整的统一的支付宝刷脸支付
* 1调用硬件刷脸获取到ftoken
* 2调用后端的支付宝支付存入hispay
* @param aliOrderVo
* @return
* @throws Exception
*/
@PostMapping("/aliFacePay")
public Result<JSONObject> aliFacePay(@RequestBody AliOrderVo aliOrderVo) throws Exception {
return aliScanFaceService.aliFacePay(aliOrderVo);
}
/**
* ABCP初始化
* 文档地址https://opendocs.alipay.com/iot/05e9ye
* ABCP初始化商家App 启动时通过调用接口 abcp_init 执行 ABCP SDK 的初始化初始化动作只需启动时调用一次初始化的时候需保证机具网络处于联网状态
*
* @return
*/
@GetMapping("iniAbcp")
Result<Object> iniAbcp() {
return aliScanFaceService.iniAbcpAbsolute();
}
/**
* ABCP服务调用 刷脸去初始化服务,获取ftoken
* 文档地址https://opendocs.alipay.com/iot/05e9ye
* 初始化成功后商家App 可根据业务需求调用接口 abcp_start_service 执行 ABCP 所提供的服务
* 上述服务调用过程可重复多次调用通过传入不同的 service_code 来调用不同的 ABCP 服务
*
* @return
*/
@GetMapping("abcpStartServiceIni")
Result<Object> abcpStartServiceIni() {
aliScanFaceService.iniAbcpAbsolute();
//参数
JSONObject zolozConfig = new JSONObject().fluentPut("installAngle",90);
JSONObject params = new JSONObject()
.fluentPut("serviceId", aliFaceConfig.getServiceId())
.fluentPut("zolozConfig",zolozConfig);
String json = params.toJSONString();
String service_code = AliFaceConstants.SMILEVERIFYNIN_V1; //调用的组件编码:初始化
return aliScanFaceService.startServiceIni(json, service_code);
}
/**
* ABCP服务停用
* 终止正在进行中的某次service_code + traceId或某类service_code服务调用
* 如处于支付环节此次服务调用将不接受停止
* @param traceId
* @return
*/
@PostMapping("stopService")
Result<Object> stopService(@RequestParam String traceId) {
aliScanFaceService.iniAbcpAbsolute();
//参数
JSONObject params = new JSONObject()
.fluentPut("traceId",traceId);
//.fluentPut("service_code",AliFaceConstants.SMILEVERIFYNIN_V1); //
String json = params.toJSONString();
String service_code = AliFaceConstants.SMILEVERIFYNIN_V1; //调用的组件编码
return aliScanFaceService.stopService(json, service_code);
}
}

View File

@ -0,0 +1,288 @@
package com.dpkj.modules.scanface.ali.dll;
import com.dpkj.common.vo.Result;
import com.sun.jna.Callback;
import com.sun.jna.Native;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j
public class AbcpInvoke {
public interface CallbackStr {
public void OnString(String result);
}
public interface CallbackRsp {
public void OnProcess(int code, String subCode, String subMsg, String result);
public void OnFinish(int code, String subCode, String subMsg, String result);
}
public static void SetAPIPathFile(String fileDylib) {
if (msAbcpNativeDll == null) {
File file = new File(fileDylib);
if (file.exists()) {
try {
msAbcpNativeDll = (AbcpNativeDll) Native.load(fileDylib, AbcpNativeDll.class);
} catch (Throwable e) {
msAbcpNativeDll = null;
log.error("## 本地库未加载,返回错误 ERROR :{},{} ", fileDylib, e.getMessage());
}
} else {
log.error("## ERROR : dylib NOT exist ## %s %n", fileDylib);
}
}
}
public static void AbcpInit(String appId, String appVersion, String json, CallbackRsp callback) {
if (msAbcpNativeDll == null) {
callback.OnFinish(3, "E69001", "[java] api load failed!", "");
return;
}
appId = FixNullString(appId); // involved null ptr!
appVersion = FixNullString(appVersion); // involved null ptr!
json = FixNullString(json); // involved null ptr!
msLock.lock();
Integer seedId = msSeedId++;
msMapAbcpReponse.put(seedId, callback);
msLock.unlock();
try {
msAbcpNativeDll.bp_api_set_isv_lan(3);// 0:cpp | 1:cshap | 2:jni | 3:jna
} catch (Throwable e) {
log.error("## ERROR ## %s %n", e.getMessage());
}
try {
msAbcpNativeDll.abcp_init(seedId, appId, appVersion, json, msABCPProcess, msABCPFinish);
} catch (Throwable e) {
callback.OnFinish(3, "E69001", e.getMessage(), "");
log.error("## ERROR ## %s %n", e.getMessage());
}
}
public static void AbcpGetMetaInfo(String appId, String json, CallbackRsp callback) {
if (msAbcpNativeDll == null) {
callback.OnFinish(3, "E69001", "[java] api load failed!", "");
return;
}
appId = FixNullString(appId); // involved null ptr!
json = FixNullString(json); // involved null ptr!
msLock.lock();
Integer seedId = msSeedId++;
msMapAbcpReponse.put(seedId, callback);
msLock.unlock();
try {
msAbcpNativeDll.abcp_get_meta_info(seedId, appId, json, msABCPProcess, msABCPFinish);
} catch (Throwable e) {
callback.OnFinish(3, "E69001", e.getMessage(), "");
log.error("## ERROR ## %s %n", e.getMessage());
}
}
public static void AbcpStartService(String appId, String serviceCode, String json, CallbackRsp callback) {
if (msAbcpNativeDll == null) {
callback.OnFinish(3, "E69001", "[java] api load failed!", "");
return;
}
appId = FixNullString(appId); // involved null ptr!
serviceCode = FixNullString(serviceCode); // involved null ptr!
json = FixNullString(json); // involved null ptr!
Integer seedId;
msLock.lock();
try {
seedId = msSeedId++;
msMapAbcpReponse.put(seedId, callback);
log.info("注册回调: seedId={}", seedId);
} finally {
msLock.unlock();
}
try {
log.info("调用 abcp_start_service: seedId={}, appId={}, serviceCode={}, jsonLength={}", seedId, appId, serviceCode, json.length());
msAbcpNativeDll.abcp_start_service(seedId, appId, serviceCode, json, msABCPProcess, msABCPFinish);
} catch (Throwable e) {
log.error("##本地方法调用异常 ERROR ## %s %n", e.getMessage());
callback.OnFinish(3, "E69001", e.getMessage(), "");
}
}
public static void AbcpTaskStopService(String appId, String serviceCode, String json, CallbackRsp callback) {
if (msAbcpNativeDll == null) {
callback.OnFinish(3, "E69001", "[java] api load failed!", "");
return;
}
appId = FixNullString(appId); // involved null ptr!
serviceCode = FixNullString(serviceCode); // involved null ptr!
json = FixNullString(json); // involved null ptr!
msLock.lock();
Integer seedId = msSeedId++;
msMapAbcpReponse.put(seedId, callback);
msLock.unlock();
try {
log.info("[调用abcp_stop_service]:seedId={}, appId={}, serviceCode={}, json={}", seedId, appId, serviceCode, json);
msAbcpNativeDll.abcp_stop_service(seedId, appId, serviceCode, json, msABCPProcess, msABCPFinish);
} catch (Throwable e) {
callback.OnFinish(3, "E69001", e.getMessage(), "");
log.error("## ERROR ## %s %n", e.getMessage());
}
}
public static void AbcpZimIdInitTest(String metainfo, String smileType, String smileFlag, CallbackStr callback) {
if (msAbcpNativeDll == null) {
callback.OnString("{}");
return;
}
metainfo = FixNullString(metainfo); // involved null ptr!
smileType = FixNullString(smileType); // involved null ptr!
smileFlag = FixNullString(smileFlag); // involved null ptr!
msLock.lock();
Integer seedId = msSeedId++;
msMapAbcpString.put(seedId, callback);
msLock.unlock();
try {
msAbcpNativeDll.abcp_zimid_init_for_test(seedId, metainfo, smileType, smileFlag, msABCPString);
} catch (Throwable e) {
callback.OnString("{}");
log.error("## ERROR ## %s %n", e.getMessage());
}
}
public static void AbcpTaskSubscribeEvent(String appId, String json, CallbackRsp callback) {
if (msAbcpNativeDll == null) {
callback.OnFinish(3, "E69001", "[java] api load failed!", "");
return;
}
appId = FixNullString(appId); // involved null ptr!
json = FixNullString(json); // involved null ptr!
msLock.lock();
Integer seedId = msSeedId++;
msMapAbcpReponse.put(seedId, callback);
msLock.unlock();
try {
msAbcpNativeDll.abcp_call(seedId, appId, "SubscribeEvent", json, msABCPProcess, msABCPFinish);
} catch (Throwable e) {
callback.OnFinish(3, "E69001", e.getMessage(), "");
log.error("## ERROR ## %s %n", e.getMessage());
}
}
public static void AbcpTaskUnsubscribeEvent(String appId, String json, CallbackRsp callback) {
if (msAbcpNativeDll == null) {
callback.OnFinish(3, "E69001", "[java] api load failed!", "");
return;
}
appId = FixNullString(appId); // involved null ptr!
json = FixNullString(json); // involved null ptr!
msLock.lock();
Integer seedId = msSeedId++;
msMapAbcpReponse.put(seedId, callback);
msLock.unlock();
try {
msAbcpNativeDll.abcp_call(seedId, appId, "UnsubscribeEvent", json, msABCPProcess, msABCPFinish);
} catch (Throwable e) {
callback.OnFinish(3, "E69001", e.getMessage(), "");
log.error("## ERROR ## %s %n", e.getMessage());
}
}
// =====================================
private static AbcpNativeDll msAbcpNativeDll = null;
private static ABCPProcess msABCPProcess = new ABCPProcess();
private static ABCPFinish msABCPFinish = new ABCPFinish();
private static ABCPString msABCPString = new ABCPString();
private static Lock msLock = new ReentrantLock();
private static Integer msSeedId = Integer.valueOf(256);
private static Map<Integer, CallbackStr> msMapAbcpString = new HashMap<Integer, CallbackStr>();
private static Map<Integer, CallbackRsp> msMapAbcpReponse = new HashMap<Integer, CallbackRsp>();
private static String FixNullString(String str) {
if (str.equals(null)) {
return "";
}
return str;
}
public static Integer getMsSeedId() {
return msSeedId;
}
// =====================================
public interface FuncProcess extends Callback {
public void onProcess(int seed_id, int code, String subCode, String subMsg, String result);
}
public interface FuncFinish extends Callback {
public void onFinish(int seed_id, int code, String subCode, String subMsg, String result);
}
public interface FuncString extends Callback {
public void onString(int seed_id, String result);
}
public static class ABCPProcess implements FuncProcess {
public void onProcess(int seed_id, int _code, String subCode, String subMsg, String result) {
log.info("[ABCPProcess][onProcess][243][onProcess][seed_id:{}],[_code:{}],[subMsg:{}],[result:{}]", seed_id, _code, subCode, subMsg, result);
Integer seedId = Integer.valueOf(seed_id);
CallbackRsp callback = null;
msLock.lock();
if (msMapAbcpReponse.containsKey(seedId)) {
callback = msMapAbcpReponse.get(seedId);
}
msLock.unlock();
if (callback != null) {
Integer code = Integer.valueOf(_code);
callback.OnProcess(code, subCode, subMsg, result);
}
}
}
public static class ABCPFinish implements FuncFinish {
public void onFinish(int seed_id, int _code, String subCode, String subMsg, String result) {
//log.info("[ABCPFinish][onFinish][261][ABCPFinish数据][seed_id:{}],[_code:{}],[subMsg:{}],[result:{}]", seed_id, _code, subCode, subMsg, result);
Integer seedId = Integer.valueOf(seed_id);
CallbackRsp callback = null;
msLock.lock();
if (msMapAbcpReponse.containsKey(seedId)) {
callback = msMapAbcpReponse.remove(seedId);
}
msLock.unlock();
if (callback != null) {
Integer code = Integer.valueOf(_code);
callback.OnFinish(code, subCode, subMsg, result);
}
}
}
public static class ABCPString implements FuncString {
public void onString(int seed_id, String result) {
Integer seedId = Integer.valueOf(seed_id);
CallbackStr callback = null;
msLock.lock();
if (msMapAbcpString.containsKey(seedId)) {
callback = msMapAbcpString.remove(seedId);
}
msLock.unlock();
if (callback != null) {
callback.OnString(result);
}
}
}
}

View File

@ -0,0 +1,22 @@
package com.dpkj.modules.scanface.ali.dll;
import com.sun.jna.Callback;
import com.sun.jna.Library;
public interface AbcpNativeDll extends Library {
void bp_api_set_isv_lan(Integer type_isv_language);// 0:cpp | 1:cshap | 2:jni | 3:jna
void abcp_init(Integer arg, String app_id, String app_version, String json_param, Callback on_process, Callback on_finish);
void abcp_start_service(Integer arg, String app_id, String service_code, String json_param, Callback on_process, Callback on_finish);
void abcp_stop_service(Integer arg, String app_id, String service_code, String json_param, Callback on_process, Callback on_finish);
void abcp_get_meta_info(Integer arg, String app_id, String json_param, Callback on_process, Callback on_finish);
void abcp_stop_smile(Integer arg, String app_id, String json_param, Callback on_process, Callback on_finish);
void abcp_zimid_init_for_test(Integer arg, String zimmetainfo, String smileType, String smileFlag, Callback callback);
void abcp_call(Integer arg, String app_id, String call_method, String json_param, Callback on_process, Callback on_finish);
}

View File

@ -0,0 +1,47 @@
package com.dpkj.modules.scanface.ali.service;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.scanface.ali.vo.AliOrderVo;
public interface IAliScanFaceService {
/**
* 完整的统一的支付宝刷脸支付
* 1调用硬件刷脸获取到ftoken
* 2调用后端的支付宝支付存入hispay记录
* @param aliOrderVo
* @return
* @throws Exception
*/
Result<JSONObject> aliFacePay(AliOrderVo aliOrderVo);
/**
* 初始化调用阿里的ABCP_SDK部署出来的代码中的API文件
* https://opendocs.alipay.com/iot/05e9ye#ABCP%E5%88%9D%E5%A7%8B%E5%8C%96
*/
Result<Object> iniAbcpAbsolute();
/**
* ABCP服务调用 刷脸去初始化服务,获取ftoken
* 接受调用者传入的参数信息执行指定服务service_code然后通过回调返回服务结果
* https://opendocs.alipay.com/iot/05e9ye#ABCP%E6%9C%8D%E5%8A%A1%E8%B0%83%E7%94%A8
* @param json 组装参数
* @param service_code 所要调用的组件编码
* @return
*/
Result<Object> startServiceIni(String json,String service_code);
/**
* ABCP服务停用
* 终止正在进行中的某次service_code + traceId或某类service_code服务调用然后通过回调返回服务终止结果
* 如处于支付环节此次服务调用将不接受停止
* @param json 组装参数
* @param service_code 组件编码
* @return
*/
Result<Object> stopService(String json,String service_code);
}

View File

@ -0,0 +1,323 @@
package com.dpkj.modules.scanface.ali.service.impl;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSON;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.scanface.ali.config.AliFaceConfig;
import com.dpkj.modules.scanface.ali.constants.AliFaceConstants;
import com.dpkj.modules.scanface.ali.dll.AbcpInvoke;
import com.dpkj.modules.scanface.ali.service.IAliScanFaceService;
import com.dpkj.modules.scanface.ali.vo.AliOrderVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import javax.annotation.Resource;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Auther: 萧道子
* @Date: 2025/4/16 16:47
* @Description:
*/
@Slf4j
@Service
@Component
public class AliScanFaceServiceImpl implements IAliScanFaceService {
@Value("${dpkj.serverurl}")
private String serverUrl;
@Resource
private AliFaceConfig aliFaceConfig;
/**
* @description: * 完整的统一的支付宝刷脸支付
* 1调用硬件刷脸获取到ftoken
* 2调用后端的支付宝支付存入hispay
* @author: zhangxue
* @date: 2025/5/7 9:45
* @Param [aliOrderVo]
* @return: com.dpkj.common.vo.Result<com.alibaba.fastjson.JSONObject>
*/
@Override
public Result<JSONObject> aliFacePay(AliOrderVo aliOrderVo) {
try {
/**
* 1获取刷脸"刷脸去初始化服务"的ftoken返回值
*/
this.iniAbcpAbsolute();
//参数
JSONObject zolozConfig = new JSONObject().fluentPut("installAngle", 90);
JSONObject params = new JSONObject()
.fluentPut("serviceId", aliFaceConfig.getServiceId())
.fluentPut("zolozConfig", zolozConfig);
String json = params.toJSONString();
String service_code = AliFaceConstants.SMILEVERIFYNIN_V1; //调用的组件编码:初始化
Result<Object> startServiceIniResult = this.startServiceIni(json, service_code);
if (startServiceIniResult.isSuccess()) {
Map<String, String> res = (Map<String, String>) startServiceIniResult.getResult();
String ftoken = res.get("ftoken");
/**
* 2调用后端的支付宝统一收单交易支付接口存入hisPay
*/
aliOrderVo.setAuthCode(ftoken);//Demo值"fp128d26333fa66e66e7f34c493d30cdh76"
JSONObject serverParams = (JSONObject) JSON.toJSON(aliOrderVo);
String url = serverUrl + "openapi/aliPayOrderApi/createOrder";
String req = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8))
.body(serverParams.toJSONString())
.execute()
.body();
JSONObject serverResult = JSONObject.parseObject(req);
return Result.ok(serverResult);
} else {
//调用ABCP 刷脸初始化服务失败
log.error("[AliScanFaceServiceImpl][aliFacePay][299]调用ABCP 刷脸初始化服务失败 {}", startServiceIniResult.getMessage());
return Result.error(startServiceIniResult.getMessage());
}
} catch (Exception e) {
e.printStackTrace();
log.error("[AliScanFaceServiceImpl][aliFacePay][302][整个支付宝刷脸模块出现失败:] {}", e.getMessage());
return Result.error("支付宝刷脸失败:" + e.getMessage());
}
}
/**
* @description: 使用绝对路径调用ABCP_SDK部署出来的AbcpInvoke类
* @author: zhangxue
* @date: 2025/5/7 9:45
* @Param []
* @return: com.dpkj.common.vo.Result<java.lang.Object>
*/
@Override
public Result<Object> iniAbcpAbsolute() {
try {
File dllFile = new File(aliFaceConfig.getDllPath());
System.out.println("DLL 存在: " + dllFile.exists());
if (dllFile.exists()) {
//组装参数
JSONObject params = new JSONObject()
.fluentPut("appId", aliFaceConfig.getAppId()) //应用ID
.fluentPut("merchantId", aliFaceConfig.getMerchantId()) //签约商家的 PID 2088 开头
.fluentPut("deviceNum", aliFaceConfig.getDeviceNum())//商家机具终端编号每台设备保持唯一
.fluentPut("partnerId", aliFaceConfig.getPartnerId());//服务商的 PID
String json = params.toJSONString();
//指定支付宝LOT SDK的本地库路径
AbcpInvoke.SetAPIPathFile(aliFaceConfig.getDllPath());
//创建回调实例
AbcpInvoke.CallbackRsp callbackRsp = new AbcpInvoke.CallbackRsp() {
@Override
public void OnProcess(int code, String subCode, String subMsg, String result) {
log.info("[AliScanFaceServiceImpl][OnProcess][123][ABCP调用iniAbcpAbsolute][code:{}][subCode:{}][subMsg:{}][result:{}]", code, subCode, subMsg, result);
}
@Override
public void OnFinish(int code, String subCode, String subMsg, String result) {
log.info("[AliScanFaceServiceImpl][OnFinish][128][ABCP调用iniAbcpAbsolute][code:{}][subCode:{}][subMsg:{}][result:{}]", code, subCode, subMsg, result);
}
};
//初始化
AbcpInvoke.AbcpInit(aliFaceConfig.getAppId(), aliFaceConfig.getAppVersion(), json, callbackRsp);
return Result.ok("支付宝ABCP初始化成功");
} else {
log.info("[AliScanFaceServiceImpl][iniAbcpAbsolute][55][{} :文件不存在]", aliFaceConfig.getDllPath());
return Result.error("支付宝ABCP初始化失败" + aliFaceConfig.getDllPath() + "不存在");
}
} catch (Exception e) {
e.printStackTrace();
return Result.error("初始化失败");
}
}
/**
* ABCP服务调用 刷脸初始化服务,获取ftoken
* 接受调用者传入的参数信息执行指定服务service_code然后通过回调返回服务结果
* https://opendocs.alipay.com/iot/05e9ye#ABCP%E6%9C%8D%E5%8A%A1%E8%B0%83%E7%94%A8
*
* @param json 组装参数
* @param service_code 所要调用的组件编码
* @return
*/
@Override
public Result<Object> startServiceIni(String json, String service_code) {
try {
String appId = aliFaceConfig.getAppId();
// 使用 CountDownLatch 实现线程同步
CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latchFinish = new CountDownLatch(1);
AtomicReference<Result<Object>> finishResultRef = new AtomicReference<>();
//获取返回数据
AtomicReference<Integer> processCode = new AtomicReference<>();
AtomicReference<String> processResult = new AtomicReference<>();
AtomicReference<Integer> finishCode = new AtomicReference<>();
AtomicReference<String> finishResult = new AtomicReference<>();
//接收结果
Map<String, String> res = new HashMap<>();
AbcpInvoke.CallbackRsp callbackRsp = new AbcpInvoke.CallbackRsp() {
@Override
public void OnProcess(int code, String subCode, String subMsg, String result) {
log.info("[AliScanFaceServiceImpl][OnProcess][123][service_code:{}][code:{}][subCode:{}][subMsg:{}][result:{}]", service_code, code, subCode, subMsg, result);
try {
processCode.set(code);
processResult.set(result);
} finally {
latch.countDown(); // 确保无论如何都释放锁
}
}
@Override
public void OnFinish(int code, String subCode, String subMsg, String result) {
log.info("[AliScanFaceServiceImpl][OnFinish][128][service_code:{}][code:{}][subCode:{}][subMsg:{}][result:{}]", service_code, code, subCode, subMsg, result);
/**Demo示例记录
* [service_code:BPaaSSmileVerifyNonInitV1][code:1000][subCode:E00000][subMsg:SUCCESS][result:{"code":1000,"subCode":"OK_SUCCESS","subMessage":"SUCCESS","barCode":"281215320962898068","ftoken":"fp1efd3d4c0230a28f5261efe7c5050eh28","alipayUid":"2088812449506047","accountList":"[\"104***@qq.com\"]","authToken":"44686f7195c77ee2e09c09bcdc657dd5h28i","result":{"accountList":["104***@qq.com"],"alipayUid":"2088812449506047","allowRetry":false,"authToken":"44686f7195c77ee2e09c09bcdc657dd5h28i","barCode":"281215320962898068","certName":"您好,*雪","ftoken":"fp1efd3d4c0230a28f5261efe7c5050eh28","type":"selectUid"},"easterEgg":false,"zolozConfig":{"installAngle":90},"serviceId":"pay","traceId":"2444-44-1745802328","callStartTimeMs":1745802328114,"localTime":"2025-04-28-09-05-41-791"}]
*/
try {
finishCode.set(code);
finishResult.set(result);
} finally {
latchFinish.countDown(); // 确保无论如何都释放锁
}
}
};
//调用
log.info("[AliScanFaceServiceImpl][startService][141][ABCP调用刷脸初始化服务-调用AbcpStartService参数][appId:{}][service_code:{}][json:{}] ", appId, service_code, json.toString());
AbcpInvoke.AbcpStartService(appId, service_code, json, callbackRsp);
// 等待process回调完成设置超时避免死锁
boolean awaitSuccess = latch.await(10, TimeUnit.SECONDS);
if (!awaitSuccess) {
return Result.error("等待process回调超时");
} else {
if (processCode.get() == 0) {
JSONObject jsonObject = JSONObject.parseObject(processResult.get());
if (jsonObject.containsKey("traceId")) {
res.put("traceId", jsonObject.getString("traceId"));
} else {
return Result.error("ABCP调用刷脸初始化服务process失败返回结果无traceId");
}
} else {
return Result.error("ABCP调用刷脸初始化服务process失败");
}
}
// 等待finish回调完成设置超时避免死锁
boolean awaitFinishSuccess = latchFinish.await(120, TimeUnit.SECONDS);
if (!awaitFinishSuccess) {
return Result.error("等待finish回调超时");
} else {
if (finishCode.get() == 1000) {
JSONObject jsonObject = JSONObject.parseObject(finishResult.get());
if (jsonObject.containsKey("ftoken")) {
res.put("ftoken", jsonObject.getString("ftoken"));//ftoken参数的有效期为2分钟
res.put("barCode", jsonObject.getString("barCode"));
finishResultRef.set(Result.ok("ABCP调用刷脸初始化finish服务成功", res));
} else {
return Result.ok("ABCP调用刷脸初始化服务finish失败返回结果无ftoken");
}
} else {
return Result.error("ABCP调用刷脸初始化服务finish失败");
}
}
//结果返回
return finishResultRef.get();
} catch (Exception e) {
e.printStackTrace();
return Result.error("ABCP调用刷脸初始化服务失败" + e.getMessage());
}
}
/**
* ABCP服务停用
* 终止正在进行中的某次service_code + traceId或某类service_code服务调用然后通过回调返回服务终止结果
* 如处于支付环节此次服务调用将不接受停止
*
* @param json 组装参数
* @param service_code 组件编码
* @return
*/
@Override
public Result<Object> stopService(String json, String service_code) {
try {
String appId = aliFaceConfig.getAppId();
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Result<Object>> resultRef = new AtomicReference<>();
AtomicReference<Integer> returnCode = new AtomicReference<>();
AtomicReference<String> returnResult = new AtomicReference<>();
AbcpInvoke.CallbackRsp callbackRsp = new AbcpInvoke.CallbackRsp() {
@Override
public void OnProcess(int code, String subCode, String subMsg, String result) {
log.info("[AliScanFaceServiceImpl][261][ABCP服务停用-OnProcess][code:{}][subCode:{}][subMsg:{}][result:{}]", code, subCode, subMsg, result);
}
@Override
public void OnFinish(int code, String subCode, String subMsg, String result) {
log.info("[AliScanFaceServiceImpl][OnFinish][266][ABCP服务停用-OnFinish] [code:{}][subCode:{}][subMsg:{}][result:{}]", code, subCode, subMsg, result);
try {
returnCode.set(code);
returnResult.set(result);
} finally {
latch.countDown(); // 确保无论如何都释放锁
}
}
};
//调用
log.info("[AliScanFaceServiceImpl][stopService][222][调用AbcpTaskStopService参数][appId:{}][service_code:{}][json:{}] ", appId, service_code, json.toString());
AbcpInvoke.AbcpTaskStopService(appId, service_code, json, callbackRsp);
// 等待回调完成设置超时避免死锁
boolean awaitSuccess = latch.await(60, TimeUnit.SECONDS);
if (!awaitSuccess) {
return Result.error("等待回调超时");
} else {
if (returnCode.get() == 0) {
System.out.println("停止结果:" + returnResult.get());
} else {
return Result.error("ABCP停止服务失败");
}
}
//结果返回
return resultRef.get();
} catch (Exception e) {
e.printStackTrace();
return Result.error("ABCP服务停用失败" + e.getMessage());
}
}
}

View File

@ -0,0 +1,81 @@
package com.dpkj.modules.scanface.ali.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* @description: 支付宝支付参数传入统一下单接口
* @author: Zhangxue
* @time: 2025/4/29 14:24
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class AliOrderVo implements Serializable {
/**
* 付款模块
*/
private String eventModule;
/**
* 患者Id
*/
private String patientId;
/**
* 商户机具终端编号
*/
private String terminalId;
/**
* 系统订单编号
*/
private String outTradeNo;
/**
* 用户支付金额
*/
//@ApiModelProperty(value = "用户支付金额")
private String totalAmount;
/**
* 订单标题
*/
//@ApiModelProperty(value = "订单标题")
private String subject;
/**
* 支付授权码
* 刷脸标识串fp开头的35位字符串
* 从调用"BPaaSSmileVerifyNonInitV1"服务中获取
*/
//@ApiModelProperty(value = "支付授权码")
private String authCode;
/**
* 支付场景
* bar_code当面付条码支付场景默认值
* security_code当面付刷脸支付场景对应的auth_code为fp开头的刷脸标识串
*/
//@ApiModelProperty(value = "支付授权码")
private String scene;
/**
* 产品码:商家和支付宝签约的产品码
* 当面付场景下如果签约的是当面付快捷版则传 OFFLINE_PAYMENT;
* 其它支付宝当面付产品传 FACE_TO_FACE_PAYMENT
* 不传则默认使用FACE_TO_FACE_PAYMENT
*/
//@ApiModelProperty(value = "订单标题")
private String productCode;
/**
* 卖家支付宝用户ID
*/
//@ApiModelProperty(value = "订单标题")
private String sellerId;
}

View File

@ -0,0 +1,81 @@
package com.dpkj.modules.scanface.wx.config;
/**
* @description:
* @author: Zhangxue
* @time: 2024/11/28 9:38
*/
public class WechatUrlConfig {
/**
* 获取证书
*/
public static final String CERTIFICATESURL = "https://api.mch.weixin.qq.com/v3/certificates";
/**
* 退款地址
*/
public static final String REFUNDSURL = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
/**
* 创建支付分订单
*/
public static final String SERVICEORDER = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder";
/**
* 发起订单支付
*/
public static final String CREATEORDER = "https://api.mch.weixin.qq.com/pay/micropay";
/**
* 查询订单状态
*/
public static final String ORDERQUERY = "https://api.mch.weixin.qq.com/pay/orderquery";
/**
* 撤销交易
*/
public static final String REVERSE = "https://api.mch.weixin.qq.com/secapi/pay/reverse";
/**
* 取消支付分订单前缀
*/
public static final String ORDERCANCELPREFIX = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder/";
/**
* 取消支付分订单后缀
*/
public static final String ORDERCANCELSUFFIX = "/cancel";
/**
* 完结支付分订单前缀
*/
public static final String SERVICEORDERCOMPLETEPREFIX = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder/";
/**
* 完结支付分订单后缀
*/
public static final String SERVICEORDERCOMPLETESUFFIX = "/complete";
/**
* 获取调用凭证
*/
public static final String GET_WXPAYFACE_AUTHINFO = "https://payapp.weixin.qq.com/face/get_wxpayface_authinfo";
/**
* 查询订单
*/
public static final String FINDBYID = "https://api.mch.weixin.qq.com/v3/payscore/serviceorder";
/**
* 查询退款订单
*/
public static final String FINDREFUND = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/";
}

View File

@ -0,0 +1,196 @@
package com.dpkj.modules.scanface.wx.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 公众号配置
*/
@Data
@Component
@ConfigurationProperties(prefix = "dpkj.wx")
public class WxMpProperties {
/**
* 多个公众号配置信息
*/
private List<MpConfig> configs;
@Data
public static class MpConfig {
/**
* 设置微信公众号的appid
*/
private String appId;
/**
* 设置微信公众号的app secret
*/
private String secret;
/**
* 设置微信公众号的token
*/
private String token;
/**
* 设置微信公众号的EncodingAESKey
*/
private String aesKey;
/**
* 微信调用接口所需service_id
*/
private String serviceId;
/**
* 微信商户号
*/
private String mchId;
/**
* 微信支付商户密钥
*/
private String mchKey;
/**
* 微信报文解密V3密钥key
*/
private String v3Key;
/**
* 微信 API密钥
*/
private String keyApi;
/**
* 微信密钥apiclient_key.pem地址
p12证书的位置可以指定绝对路径也可以指定类路径以classpath;开头
*/
private String keyPath;
/**
* 微信商家api序列号
*/
private String mchSerialNo;
/**
* #JSAPI--公众号支付 NATIVE--原生扫码支付 APP--app支付
*/
private String tradeType;
/**
* 微信支付回调地址
*/
private String payNotifyUrl;
/**
* 微信退款回调地址
*/
private String refundNotifyUrl;
}
/**
* 微信商户支付配置
*/
private MchConfig mchConfig;
@Data
public static class MchConfig{
/**
* 微信APPID
*/
private String appId;
private String secret;
/**
* 微信商户号
*/
private String mchId;
//微信支付商户密钥
private String mchKey;
/**
* 微信报文解密V3密钥key
*/
private String v3Key;
/**
* 微信 API密钥
*/
private String keyApi;
/**
* 微信密钥apiclient_key.pem地址
p12证书的位置可以指定绝对路径也可以指定类路径以classpath;开头
*/
private String keyPath;
/**
* 退款证书 private-key-path V3
* apiclient_key.pem 证书文件的绝对路径或者以classpath:开头的类路径.
*/
private String privateKeyPath;
/**
* 退款证书 private-cert-path
* apiclient_cert.pem 证书文件的绝对路径或者以classpath:开头的类路径.
*/
private String privateCertPath;
/**
* 微信商家api序列号
*/
private String mchSerialNo;
/**
* 微信调用接口所需service_id
*/
private String serviceId;
/**
* 微信支付回调地址
*/
private String payNotifyUrl;
/**
* 微信退款回调地址
*/
private String refundNotifyUrl;
/**
* #JSAPI--公众号支付 NATIVE--原生扫码支付 APP--app支付
*/
private String tradeType;
/**
* NATIVE--原生扫码支付
*/
private String tradeTypeNative;
}
/**
* 访问地址
*/
private MpUrl url;
@Data
public static class MpUrl {
/**
* H5地址
*/
private String h5;
/**
* 接口地址
*/
private String server;
}
}

View File

@ -0,0 +1,516 @@
package com.dpkj.modules.scanface.wx.controller;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.constant.WxConstant;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.scanface.wx.config.WxMpProperties;
import com.dpkj.modules.scanface.wx.dll.WxpayFaceSDKDll;
import com.dpkj.modules.scanface.wx.service.CallWxpayFaceService;
import com.dpkj.modules.scanface.wx.service.WeChatPayFaceService;
import com.dpkj.modules.scanface.wx.vo.WxFaceOrderVo;
import com.dpkj.modules.scanface.wx.vo.WxFacePayAuthinfoResp;
import com.dpkj.modules.scanface.wx.vo.WxFacePayReq;
import com.dpkj.modules.scanface.wx.vo.WxFacePayResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.binarywang.wxpay.bean.request.WxPayOrderReverseRequest;
import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryResult;
import com.github.binarywang.wxpay.bean.result.WxPayOrderReverseResult;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* @description: 微信刷脸支付
* @author: Zhangxue
* @time: 2025/5/28 14:20
*/
@Slf4j
@RestController
@RequestMapping("/wxpayFace")
public class WxFacePayController {
@Resource
private CallWxpayFaceService callWxpayFaceService;
@Resource
private WeChatPayFaceService weChatPayFaceService;
@Value("${dpkj.serverurl}")
private String serverUrl;
//赋值
@Autowired
private WxMpProperties wxMpProperties;
/**
* 调用整个刷脸的完整流程
*
* @param wxFaceOrderVo
* @return
* @throws Exception
*/
@RequestMapping(value = "/doFacePay", method = RequestMethod.POST)
public Result<String> doFacePay(@RequestBody WxFaceOrderVo wxFaceOrderVo) throws Exception {
Result<String> result = new Result<>();
try {
//1 初始化
this.initWxpayface();
//2 获取数据getWxpayfaceRawdata
WxFacePayResp wxFacePayResp = this.getWxpayfaceRawdata();
//String rawdata = wxFacePayResp.getRawdata();
//3获取调用凭证get_wxpayface_authinfo(rawdata)获取调用凭证
WxFacePayAuthinfoResp wxFacePayResp0 = this.getWxpayfaceAuthinfo(wxFacePayResp.getRawdata());
//4进行人脸识别getWxpayfaceCode获取支付凭证
WxFacePayAuthinfoResp authinfoResp = this.getWxpayfaceCode(wxFaceOrderVo, wxFacePayResp0);
WxPayMicropayResult micropayResult = new WxPayMicropayResult();
if (StringUtil.isNotBlank(authinfoResp.getFace_code())) {
//5调用后台人脸支付API发起支付
wxFaceOrderVo.setOutTradeNo(getOutTradeNo());
micropayResult = this.toCreateWxOrder(authinfoResp, wxFaceOrderVo, wxFacePayResp0);
//6向后端查询订单状态 WxPayOrderQueryResult queryResult = queryOrderByNo(micropayResult);
boolean isSuccess = pollOrderStatus(micropayResult);
if (isSuccess) {
//8更新支付结果updateWxpayfacePayResult
WxFacePayResp updFacePayResp = this.updateWxpayfacePayResult(wxFacePayResp0);
if (ObjUtil.isNotEmpty(updFacePayResp) && WxConstant.STATE_SUCCESS.equals(updFacePayResp.getReturn_code())) {
//11向后端发起更新订单进行相应的操作
this.updatePayResult(micropayResult);
result = Result.ok("微信刷脸成功!", micropayResult.getOutTradeNo());
}
} else { //查询失败或者长时间未有结果
//7撤销交易reverse
WxPayOrderReverseRequest reverseRequest = new WxPayOrderReverseRequest();
reverseRequest.setTransactionId(micropayResult.getTransactionId());
reverseRequest.setOutTradeNo(micropayResult.getOutTradeNo());
WxPayOrderReverseResult reverseResult = this.toReverseOrder(reverseRequest);
result = Result.error("微信刷脸失败后已经撤销订单!");
}
} else {
result = Result.error("获取人脸识别支付凭证为空值");
}
} catch (Exception e) {
e.printStackTrace();
log.info("[WxFacePayController][doFacePay][84][微信刷脸完成流程出错] {}", e.getMessage());
result = Result.error("微信刷脸完成流程出错:" + e.getMessage());
}
return result;
}
/**
* 1 程序启动时初始化 :程序启动时初始化initWxpayface
*
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/initWxpayface", method = RequestMethod.POST)
public WxFacePayResp initWxpayface() throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("initWxpayface", "1", System.currentTimeMillis() / 1000, 1);
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
log.info("[WxFacePayController][initWxpayface][132] [1、程序启动时初始化]{}", wxFacePayResp.toString());
return wxFacePayResp;
}
/**
* 2 获取数据getWxpayfaceRawdata
*
* @return
* @throws JsonProcessingException
*/
public WxFacePayResp getWxpayfaceRawdata() throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceRawdata", "1", System.currentTimeMillis() / 1000);
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
log.info("[WxFacePayController][getWxpayfaceRawdata][146][2、获取数据getWxpayfaceRawdata] {}", wxFacePayResp.toString());
return wxFacePayResp;
}
/**
* 3获取调用凭证get_wxpayface_authinfo(rawdata)获取调用凭证
*
* @return
* @throws Exception
*/
public WxFacePayAuthinfoResp getWxpayfaceAuthinfo(String rawdata) throws Exception {
// 构建请求参数的JSON字符串
WxFacePayAuthinfoResp wxFacePayResp = weChatPayFaceService.getWxFaceAuthInfoReqMap(rawdata);
log.info("[WxFacePayController][getWxpayfaceAuthinfo][159][3、获取调用凭证get_wxpayface_authinfo] {}", wxFacePayResp.toString());
return wxFacePayResp;
}
/**
* 4进行人脸识别getWxpayfaceCode获取支付凭证
*
* @param wxFaceOrderVo
* @return
* @throws Exception
*/
@RequestMapping(value = "/getWxpayfaceCode", method = RequestMethod.POST)
public WxFacePayAuthinfoResp getWxpayfaceCode(@RequestBody WxFaceOrderVo wxFaceOrderVo, WxFacePayAuthinfoResp wxFacePayResp0) throws Exception {
//4进行人脸识别getWxpayfaceCode获取支付凭证
String outTradeNo = getOutTradeNo();//获取流水号
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceCode", "1", System.currentTimeMillis() / 1000);
wxFacePayReq.setAuthinfo(wxFacePayResp0.getAuthinfo())
.setOut_trade_no(outTradeNo)//订单流水号
.setTotal_fee(wxFaceOrderVo.getTotalAmount())//金额
/**
* 目标face_code类型可选值"1"刷卡付款码18位数字通过付款码支付/被扫支付接口完成支付
* 如果不填写则默认为"0"人脸付款码数字字母混合通过刷脸支付接口完成支付
*/
.setFace_code_type("1");
WxFacePayAuthinfoResp authinfoResp = weChatPayFaceService.getWxpayfaceCode(wxFacePayReq);
log.info("[WxFacePayController][getWxpayfaceCode][189][4、进行人脸识别结果] {}", authinfoResp.toString());
return authinfoResp;
}
/**
* 从后端获取流水号保证唯一性
*
* @return
*/
private String getOutTradeNo() {
String url0 = serverUrl + "openapi/wxFacePayOrderApi/getOutTradeNo";
log.info("[WxFacePayController][getWxpayfaceCode][112][获取流水号地址] {}", url0);
String req0 = HttpRequest.post(url0)
.header(Header.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8))
.execute()
.body();
JSONObject tradeNoResult = JSONObject.parseObject(req0);
String outTradeNo = tradeNoResult.getString("result");
log.info("[WxFacePayController][getOutTradeNo][208][获取流水号] {}", outTradeNo);
return outTradeNo;
}
/**
* 5向后端进行发起订单支付
*
* @return
*/
private WxPayMicropayResult toCreateWxOrder(WxFacePayAuthinfoResp authinfoResp, WxFaceOrderVo wxFaceOrderVo, WxFacePayAuthinfoResp wxFacePayResp0) {
authinfoResp
.setOut_trade_no(wxFaceOrderVo.getOutTradeNo())//订单流水号
.setTotal_fee(wxFaceOrderVo.getTotalAmount())//金额
.setPatientId(wxFaceOrderVo.getPatientId())//患者Id
.setEventModule(wxFaceOrderVo.getEventModule()) //付费模块
.setTerminalId(wxFaceOrderVo.getTerminalId()) //商户机具终端编号
.setAuthinfo(wxFacePayResp0.getAuthinfo())
.setNonce_str(wxFacePayResp0.getNonce_str())
.setSign(wxFacePayResp0.getSign());
JSONObject serverParams = (JSONObject) JSON.toJSON(authinfoResp);
String url = serverUrl + "openapi/wxFacePayOrderApi/createFaceOrder";
log.info("[WxFacePayController][getWxpayfaceCode][153][向后台发起微信刷脸订单创建路径] {}", url);
log.info("[WxFacePayController][getWxpayfaceCode][153][调用后台人脸支付API发起支付参数] {}", serverParams.toString());
String req = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8))
.body(serverParams.toString())
.execute()
.body();
log.info("[WxFacePayController][toCreateWxOrder][237][5、向后台发起进行发起订单支付 结果] {}", req);
JSONObject serverResult = JSONObject.parseObject(req);
Map<String, Object> result = (Map<String, Object>) serverResult.get("result");
log.info("[WxFacePayController][toCreateWxOrder][140][5、向后端进行发起订单支付请求结果] {}", result.toString());
WxPayMicropayResult micropayResult = new WxPayMicropayResult();
log.info("[WxFacePayController][toCreateWxOrder][243][5、调用后台人脸支付API发起支付结果] {}", result.toString());
if (ObjUtil.isNotEmpty(result)) {
micropayResult.setOutTradeNo(result.get("outTradeNo").toString());
micropayResult.setTransactionId(result.get("transactionId").toString());
micropayResult.setTotalFee(Integer.valueOf(result.get("totalFee").toString()));
micropayResult.setReturnCode(result.get("returnCode").toString());
}
return micropayResult;
}
/**
* 轮询逻辑封装方法
* 查询微信刷脸订单状态
* 30秒
* 调用步骤6
*
* @param micropayResult
* @return
*/
public boolean pollOrderStatus(WxPayMicropayResult micropayResult) {
final long timeoutMillis = 30_000; // 30秒超时毫秒
final long pollInterval = 2_000; // 轮询间隔2秒
long startTime = System.currentTimeMillis();
while (true) {
// 1. 查询订单状态
WxPayOrderQueryResult queryResult = this.queryOrderByNo(micropayResult);
log.info("[WxFacePayController][pollOrderStatus][272][1. 查询订单状态:] {}", queryResult);
// 2. 检查成功状态
if (WxConstant.STATE_SUCCESS.equals(queryResult.getReturnCode())) {
log.info("[WxFacePayController][pollOrderStatus][275][订单支付成功!]");
return true;
}
// 3. 检查超时
long elapsed = System.currentTimeMillis() - startTime;
if (elapsed >= timeoutMillis) {
log.info("[WxFacePayController][pollOrderStatus][284][支付超时,未获取成功状态]");
return false;
}
// 4. 等待下一次轮询
try {
long nextPoll = Math.min(pollInterval, timeoutMillis - elapsed);
TimeUnit.MILLISECONDS.sleep(nextPoll);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.info("[WxFacePayController][pollOrderStatus][293] [轮询被中断]");
return false;
}
}
}
/**
* 6向后端查询订单状态
*
* @param micropayResult
* @return
*/
public WxPayOrderQueryResult queryOrderByNo(WxPayMicropayResult micropayResult) {
WxPayOrderQueryResult queryResult = new WxPayOrderQueryResult();
if (ObjUtil.isNotEmpty(micropayResult)) {
JSONObject serverParams = (JSONObject) JSON.toJSON(micropayResult);
String url = serverUrl + "openapi/wxFacePayOrderApi/queryOrderByNo";
log.info("[WxFacePayController][getWxpayfaceCode][184][6、向后台发起查询订单状态创建路径] {}", url);
log.info("[WxFacePayController][getWxpayfaceCode][186][6、向后端查询订单状态参数] {}", serverParams.toString());
String req = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8))
.body(serverParams.toString())
.execute()
.body();
log.info("[WxFacePayController][queryOrderByNo][318][6、后台发起查询订单 结果:] {}", req);
JSONObject serverResult = JSONObject.parseObject(req);
Map<String, Object> result = (Map<String, Object>) serverResult.get("result");
log.info("[WxFacePayController][toCreateWxOrder][140][6、后台发起查询订单 请求结果] {}", result.toString());
if (ObjUtil.isNotEmpty(result)) {
queryResult.setReturnCode(result.get("returnCode").toString());
queryResult.setOutTradeNo(result.get("outTradeNo").toString());
queryResult.setTransactionId(result.get("transactionId").toString());
queryResult.setTotalFee(Integer.valueOf(result.get("totalFee").toString()));
}
log.info("[WxFacePayController][toCreateWxOrder][140][ 6、向后端查询订单状态结果] {}", micropayResult.toString());
}
return queryResult;
}
/**
* 7撤销交易reverse
* 支付交易返回失败或支付系统超时调用该接口撤销交易
* 如果此订单用户支付失败微信支付系统会将此订单关闭
* 如果用户支付成功微信支付系统会将此订单资金退还给用户
*
* @param reverseRequest
* @return
*/
@RequestMapping(value = "/toReverseOrder", method = RequestMethod.POST)
public WxPayOrderReverseResult toReverseOrder(@RequestBody WxPayOrderReverseRequest reverseRequest) {
WxPayOrderReverseResult reverseResult = new WxPayOrderReverseResult();
if (ObjUtil.isNotEmpty(reverseRequest)) {
JSONObject serverParams = (JSONObject) JSON.toJSON(reverseRequest);
String url = serverUrl + "openapi/wxFacePayOrderApi/reverseOrder";
log.info("[WxFacePayController][toReverseOrder][303][向后台发起撤销交易reverse路径] {}", url);
log.info("[WxFacePayController][toReverseOrder][304][调用后台发起撤销交易参数] {}", serverParams.toString());
String req = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8))
.body(serverParams.toString())
.execute()
.body();
log.info("[WxFacePayController][toReverseOrder][359][向后台发起撤销交易 结果:] {}", req);
JSONObject serverResult = JSONObject.parseObject(req);
reverseResult = (WxPayOrderReverseResult) serverResult.get("result");
log.info("[WxFacePayController][toReverseOrder][314][7、向后台发起撤销交易请求结果] {}", reverseResult.toString());
}
return reverseResult;
}
/**
* 8更新支付结果updateWxpayfacePayResult
*
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/updateWxpayfacePayResult", method = RequestMethod.POST)
public WxFacePayResp updateWxpayfacePayResult(WxFacePayAuthinfoResp wxFacePayResp0) throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("updateWxpayfacePayResult", "1", System.currentTimeMillis() / 1000);
wxFacePayReq.setAppid(wxMpProperties.getMchConfig().getAppId())
.setMch_id(wxMpProperties.getMchConfig().getMchId())
.setStore_id(WxConstant.STORE_ID)
.setPayresult("SUCCESS")
.setAuthinfo(wxFacePayResp0.getAuthinfo());
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
log.info("[WxFacePayController][updateWxpayfacePayResult][385][8、更新支付结果updateWxpayfacePayResult] {}", wxFacePayResp.toString());
return wxFacePayResp;
}
/**
* 9停止刷脸支付stopWxpayface
* 仅在人脸凭证/付款码face_code第4步骤未返回前可用face_code返回后不可停止
*
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/stopWxpayface", method = RequestMethod.POST)
public WxFacePayResp stopWxpayface(WxFacePayAuthinfoResp wxFacePayResp0) throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("stopWxpayface", "1", System.currentTimeMillis() / 1000);
wxFacePayReq.setAppid(wxMpProperties.getMchConfig().getAppId())
.setMch_id(wxMpProperties.getMchConfig().getMchId())
.setStore_id(WxConstant.STORE_ID)
.setAuthinfo(wxFacePayResp0.getAuthinfo());
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
log.info("[WxFacePayController][stopWxpayface][407][9、停止刷脸支付stopWxpayface的结果] {}", wxFacePayResp.toString());
return wxFacePayResp;
}
/**
* 10释放资源releaseWxpayface
* 首次刷脸需要initWxpayface刷脸结束后不需要调用releaseWxpayface否则下次刷脸需要重新initWxpayface影响启动耗时
*
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/releaseWxpayface", method = RequestMethod.POST)
public WxFacePayResp releaseWxpayface(WxFacePayAuthinfoResp wxFacePayResp0) throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("releaseWxpayface", "1", System.currentTimeMillis() / 1000);
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
log.info("[WxFacePayController][releaseWxpayface][425][10、释放资源releaseWxpayface的结果] {}", wxFacePayResp.toString());
return wxFacePayResp;
}
/**
* 11向后端发起更新状态为成功进行相应的操作
*
* @param micropayResult
* @return
*/
public WxPayOrderQueryResult updatePayResult(WxPayMicropayResult micropayResult) {
WxPayOrderQueryResult queryResult = new WxPayOrderQueryResult();
if (ObjUtil.isNotEmpty(micropayResult)) {
JSONObject serverParams = (JSONObject) JSON.toJSON(micropayResult);
String url = serverUrl + "openapi/wxFacePayOrderApi/updatePayResult";
log.info("[WxFacePayController][updatePayResult][184][11、向后端发起更新状态为成功创建路径] {}", url);
log.info("[WxFacePayController][updatePayResult][186][11、向后端发起更新状态为成功参数] {}", serverParams.toString());
String req = HttpRequest.post(url)
.header(Header.CONTENT_TYPE, ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8))
.body(serverParams.toString())
.execute()
.body();
log.info("[WxFacePayController][updatePayResult][447][11、向后端发起更新状态为成功 结果:] {}", req.toString());
JSONObject jsonObject = JSONObject.parseObject(req);
Map<String, Object> result = (Map<String, Object>) jsonObject.get("result");
log.info("[WxFacePayController][updatePayResult][140][11、向后端发起更新状态为成功 请求结果] {}", result.toString());
}
return queryResult;
}
/**
* 4进行人脸识别getWxpayfaceCode获取支付凭证
* @return
* @throws Exception
*/
/*@RequestMapping(value = "/getWxpayfaceCode", method = RequestMethod.POST)
public Result<WxFacePayAuthinfoResp> getWxpayfaceCode(@RequestBody WxFaceOrderVo wxFaceOrderVo) throws Exception {
//1 初始化
this.initWxpayface();
//2 获取数据getWxpayfaceRawdata
WxFacePayResp wxFacePayResp = this.getWxpayfaceRawdata();
//String rawdata = wxFacePayResp.getRawdata();
//3获取调用凭证get_wxpayface_authinfo(rawdata)获取调用凭证
WxFacePayAuthinfoResp wxFacePayResp0 = this.getWxpayfaceAuthinfo(wxFacePayResp.getRawdata());
//4进行人脸识别getWxpayfaceCode获取支付凭证
String outTradeNo = getOutTradeNo();//获取流水号
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceCode", "1", System.currentTimeMillis() / 1000);
wxFacePayReq.setAuthinfo(wxFacePayResp0.getAuthinfo())
.setOut_trade_no(outTradeNo)//订单流水号
.setTotal_fee(wxFaceOrderVo.getTotalAmount())//金额
*/
/**
* 目标face_code类型可选值"1"刷卡付款码18位数字通过付款码支付/被扫支付接口完成支付
* 如果不填写则默认为"0"人脸付款码数字字母混合通过刷脸支付接口完成支付
*/
/*
.setFace_code_type("1");
WxFacePayAuthinfoResp authinfoResp = weChatPayFaceService.getWxpayfaceCode(wxFacePayReq);
System.out.println("**************4、进行人脸识别结果" + authinfoResp.toString());
if (StringUtil.isNotBlank(authinfoResp.getFace_code())) {
//5调用后台人脸支付API发起支付
WxPayMicropayResult micropayResult = this.toCreateWxOrder(authinfoResp, wxFaceOrderVo, wxFacePayResp0);
//6向后端查询订单状态
//WxPayOrderQueryResult queryResult = queryOrderByNo(micropayResult);
boolean isSuccess = pollOrderStatus(micropayResult);
if (isSuccess) {
}
}
return Result.ok(authinfoResp);
}
*/
}

View File

@ -0,0 +1,174 @@
package com.dpkj.modules.scanface.wx.controller;
import com.dpkj.common.vo.Result;
import com.dpkj.modules.scanface.wx.dll.WxpayFaceSDKDll;
import com.dpkj.modules.scanface.wx.service.CallWxpayFaceService;
import com.dpkj.modules.scanface.wx.service.WeChatPayFaceService;
import com.dpkj.modules.scanface.wx.service.impl.CallWxpayFaceServiceImpl;
import com.dpkj.modules.scanface.wx.service.impl.WeChatPayFaceServiceImpl;
import com.dpkj.modules.scanface.wx.vo.WxFacePayAuthinfoResp;
import com.dpkj.modules.scanface.wx.vo.WxFacePayReq;
import com.dpkj.modules.scanface.wx.vo.WxFacePayResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.thymeleaf.util.StringUtils;
import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
/**
* @description:
* @author: Zhangxue
* @time: 2024/11/28 16:43
*/
@Slf4j
@RestController
@RequestMapping("/wxpayFaceTest")
public class WxpayFaceTestController {
@Resource
private CallWxpayFaceService callWxpayFaceService;
@Resource
private WeChatPayFaceService weChatPayFaceService;
/**
* 1 程序启动时初始化 :程序启动时初始化initWxpayface
*
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/initWxpayface", method = RequestMethod.GET)
public Result<WxFacePayResp> initWxpayface() throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("initWxpayface", "1", System.currentTimeMillis() / 1000);
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
System.out.println("**************1、程序启动时初始化" + wxFacePayResp.toString());
return Result.ok(wxFacePayResp);
}
/**
* 2 获取数据getWxpayfaceRawdata
*
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/getWxpayfaceRawdata", method = RequestMethod.POST)
public Result<WxFacePayResp> getWxpayfaceRawdata() throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceRawdata", "1", System.currentTimeMillis() / 1000);
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
System.out.println("**************2、获取数据" + wxFacePayResp.toString());
String rawdata = wxFacePayResp.getRawdata();
return Result.ok(wxFacePayResp);
}
/**
* 3获取调用凭证get_wxpayface_authinfo(rawdata)获取调用凭证
*
* @return
* @throws Exception
*/
@RequestMapping(value = "/getWxpayfaceAuthinfo", method = RequestMethod.POST)
public Result<WxFacePayAuthinfoResp> getWxpayfaceAuthinfo() throws Exception {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceRawdata", "1", System.currentTimeMillis() / 1000);
WxFacePayResp wxFacePayResp1 = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
System.out.println("**************3、获取数据getWxpayfaceRawdata" + wxFacePayResp1.toString());
WxFacePayAuthinfoResp wxFacePayResp = new WxFacePayAuthinfoResp();
if ("SUCCESS".equals(wxFacePayResp1.getReturn_code())) {
String rawdata = wxFacePayResp1.getRawdata();
// 构建请求参数的JSON字符串
wxFacePayResp = weChatPayFaceService.getWxFaceAuthInfoReqMap(rawdata);
System.out.println("**************3、获取调用凭证get_wxpayface_authinfo" + wxFacePayResp.toString());
}
return Result.ok(wxFacePayResp);
}
/**
* 4进行人脸识别getWxpayfaceCode获取支付凭证
*
* @return
* @throws Exception
*/
@RequestMapping(value = "/getWxpayfaceCode", method = RequestMethod.POST)
public Result<WxFacePayAuthinfoResp> getWxpayfaceCode() throws Exception {
WxFacePayAuthinfoResp wxFacePayResp0 = new WxFacePayAuthinfoResp();
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq0 = new WxFacePayReq("getWxpayfaceRawdata", "1", System.currentTimeMillis() / 1000);
WxFacePayResp wxFacePayResp1 = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq0);
System.out.println("**************3、获取数据getWxpayfaceRawdata" + wxFacePayResp1.toString());
if ("SUCCESS".equals(wxFacePayResp1.getReturn_code())) {
String rawdata = wxFacePayResp1.getRawdata();
// 构建请求参数的JSON字符串
wxFacePayResp0 = weChatPayFaceService.getWxFaceAuthInfoReqMap(rawdata);
System.out.println("**************3、获取调用凭证get_wxpayface_authinfo" + wxFacePayResp0.toString());
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceCode", "1", System.currentTimeMillis() / 1000);
wxFacePayReq.setAuthinfo(wxFacePayResp0.getAuthinfo())
.setOut_trade_no("")//订单流水号
.setTotal_fee(new String("1"));
WxFacePayAuthinfoResp authinfoResp = weChatPayFaceService.getWxpayfaceCode(wxFacePayReq);
System.out.println("**************4、进行人脸识别结果" + authinfoResp.toString());
if (StringUtil.isNotBlank(authinfoResp.getFace_code())) {
//调用后台人脸支付API发起支付
authinfoResp.setOut_trade_no(wxFacePayReq.getOut_trade_no())//订单流水号
.setTotal_fee(wxFacePayReq.getTotal_fee());
}
return Result.ok(authinfoResp);
} else {
return Result.error("微信获取调用凭证失败");
}
}
//5进行发起订单支付
//可以打印 JDK 中的 Provider 列表以及所有签名算法
public void outProvider() {
TreeSet<String> algorithms = new TreeSet<>();
Provider[] providers = Security.getProviders();
System.out.println("-----Provider 列表如下:-----");
for (Provider provider : providers) {
System.out.println(provider.getName());
}
System.out.println("-----支持的签名算法如下:-----");
for (Provider provider : providers) {
for (Provider.Service service : provider.getServices())
if (service.getType().equals("Signature")) {
algorithms.add(service.getAlgorithm());
}
}
for (String algorithm : algorithms) {
System.out.println(algorithm);
}
}
}

View File

@ -0,0 +1,71 @@
package com.dpkj.modules.scanface.wx.dll;
import com.sun.jna.Library;
import com.sun.jna.Native;
import lombok.extern.slf4j.Slf4j;
/**
* @description: 32位的WxpayFaceSDK /resources/win32-x86/WxpayFaceSDK.dll
* @author: Zhangxue
* @time: 2025/4/17 17:02
*/
@Slf4j
public class WxpayFaceSDKDll {
/**
* 获取 Dll 实例同时注册 Dll 控件
*
* @return WxpayFaceSDKDll 实例
* @throws DllRegistrationException 如果注册控件失败抛出此异常
*/
public static Dll instance() throws DllRegistrationException {
try {
return Native.load("WxpayFaceSDK", Dll.class);
} catch (UnsatisfiedLinkError e) {
log.info("[WxpayFaceSDK][instance][微信扫脸动态库] SDK注册失败{}", e.getMessage());
throw new DllRegistrationException("Failed to load WxpayFaceSDK library: ", e);
}
}
/**
* 定义自定义异常类用于表示注册控件时发生的错误
*/
public static class DllRegistrationException extends Exception {
public DllRegistrationException(String message) {
super(message);
}
public DllRegistrationException(String message, Throwable cause) {
super(message, cause);
}
}
/**
* 定义接口映射本地库中的函数
*/
public interface Dll extends Library {
/**
* 调用人脸服务
* 注意这里的方法签名需要与DLL中的C函数签名一致
* req和resp都是JSON字符串需要转换为Pointer类型
* char * -->String; char ** -->String[]
* int -->int; int* --> IntByReference
* @param reqBuf 请求参数(JSON字符串)
* @param reqSize 请求参数长度
* @param pRespBuf 用来接收响应结果(JSON字符串)的char**指针
* @param pRespSize 用来接收响应结果长度的unsigned int*指针
* @return 如果成功返回0失败则返回非0
*/
//int wxpayCallFaceService(String reqBuf, int reqSize, String[] pRespBuf, IntByReference pRespSize);
int wxpayCallFaceService(String reqBuf, int reqSize, long[] pRespBuf, int[] pRespSize);
/**
* char ** -->String[]
* 释放人脸服务的响应字符串调用wxpayCallFaceService成功后务必调用此函数释放内存
* @param pRespBuf 指向响应结果(JSON字符串)的指针
*/
void wxpayReleaseResponse(String[] pRespBuf);
}
}

View File

@ -0,0 +1,19 @@
package com.dpkj.modules.scanface.wx.service;
import com.dpkj.modules.scanface.wx.dll.WxpayFaceSDKDll;
import java.io.UnsupportedEncodingException;
import java.util.List;
public interface CallWxpayFaceService {
/**
* 调用SDK微信刷脸服务
* @param reqJson
* @param respJson
* @return
*/
String callWxpayFaceService(String reqJson, List<String> respJson) throws UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException;
}

View File

@ -0,0 +1,54 @@
package com.dpkj.modules.scanface.wx.service;
import com.dpkj.modules.scanface.wx.dll.WxpayFaceSDKDll;
import com.dpkj.modules.scanface.wx.vo.WxFacePayAuthinfoResp;
import com.dpkj.modules.scanface.wx.vo.WxFacePayReq;
import com.dpkj.modules.scanface.wx.vo.WxFacePayResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import java.io.UnsupportedEncodingException;
import java.util.Map;
/**
* @description: 调用微信刷脸方法
* @author: Zhangxue
* @time: 2024/12/6 10:45
*/
public interface WeChatPayFaceService {
/**
* 调用方法
* 1程序启动时初始化initWxpayface2获取数据getWxpayfaceRawdata
* 8更新支付结果updateWxpayfacePayResult
* 9停止刷脸支付stopWxpayface
* @param wxFacePayReq
* @return
* @throws UnsupportedEncodingException
* @throws JsonProcessingException
*/
WxFacePayResp doWxPayIniMethod(WxFacePayReq wxFacePayReq) throws UnsupportedEncodingException, JsonProcessingException, WxpayFaceSDKDll.DllRegistrationException;
/**
* @description: 3获取调用凭证get_wxpayface_authinfo(rawdata)
* @author: zhangxue
* @date: 2024/12/4 14:35
* @Param rawData 初始化数据由微信人脸SDK的接口返回
* @return: java.util.Map<java.lang.String,java.lang.String>
*/
WxFacePayAuthinfoResp getWxFaceAuthInfoReqMap(String rawData) throws Exception;
/**
* 调用方法
* 4进行人脸识别getWxpayfaceCode获取支付凭证
* @param wxFacePayReq
* @return
* @throws UnsupportedEncodingException
* @throws JsonProcessingException
*/
WxFacePayAuthinfoResp getWxpayfaceCode(WxFacePayReq wxFacePayReq) throws UnsupportedEncodingException, JsonProcessingException, WxpayFaceSDKDll.DllRegistrationException;
}

View File

@ -0,0 +1,79 @@
package com.dpkj.modules.scanface.wx.service.impl;
import com.dpkj.modules.scanface.wx.config.WxMpProperties;
import com.dpkj.modules.scanface.wx.dll.WxpayFaceSDKDll;
import com.dpkj.modules.scanface.wx.service.CallWxpayFaceService;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.List;
/**
* @description:
* @author: Zhangxue
* @time: 2024/11/28 10:04
*/
@Slf4j
@Service
public class CallWxpayFaceServiceImpl implements CallWxpayFaceService {
// BouncyCastleProvider 添加到 Provider 列表中
static {
Security.addProvider(new BouncyCastleProvider());
}
//赋值
@Autowired
private WxMpProperties wxMpProperties;
/**
* 调用SDK微信刷脸服务
* @param reqJson
* @param respJson
* @return
*/
@Override
public String callWxpayFaceService(String reqJson, List<String> respJson) throws WxpayFaceSDKDll.DllRegistrationException {
//组装请求数据
Pointer reqPointer = new Memory(reqJson.length() + 1);
reqPointer.setString(0, reqJson);
//请求数据长度
int reqSize = reqPointer.getString(0).length();
System.out.println("-----------调用微信刷脸DLL请求数据----------"+reqPointer.getString(0));
//接收响应
long[] pRespBuf = new long[1];
int[] respSize = new int[1];
String resStr = new String();
// 调用本地方法
WxpayFaceSDKDll.Dll dll = WxpayFaceSDKDll.instance();
int result = dll.wxpayCallFaceService(reqPointer.getString(0), reqSize, pRespBuf, respSize);
//使用C:\Windows\System32目录下 int result = WxpayFaceSDK.INSTANCE.wxpayCallFaceService(reqPointer.getString(0), reqSize, pRespBuf, respSize);
if (result == 0) {
Pointer pointer = new Pointer(pRespBuf[0]);
byte[] byteArray = pointer.getByteArray(0, respSize[0]);
resStr = new String(byteArray, StandardCharsets.UTF_8);
System.out.println("-----------调用微信刷脸DLL请求结果: " + resStr);
dll.wxpayReleaseResponse(new String[2]);
//释放 C:\Windows\System32目录下 WxpayFaceSDK.INSTANCE.wxpayReleaseResponse(new String[2]);
} else {
Pointer pointer = new Pointer(pRespBuf[0]);
byte[] byteArray = pointer.getByteArray(0, respSize[0]);
resStr = new String(byteArray, StandardCharsets.UTF_8);
System.err.println("-----------调用人脸服务失败: " +resStr);
}
return resStr;
}
}

View File

@ -0,0 +1,406 @@
package com.dpkj.modules.scanface.wx.service.impl;
import com.dpkj.common.constant.WxConstant;
import com.dpkj.modules.scanface.wx.config.WechatUrlConfig;
import com.dpkj.modules.scanface.wx.config.WxMpProperties;
import com.dpkj.modules.scanface.wx.dll.WxpayFaceSDKDll;
import com.dpkj.modules.scanface.wx.service.CallWxpayFaceService;
import com.dpkj.modules.scanface.wx.service.WeChatPayFaceService;
import com.dpkj.modules.scanface.wx.util.WXPayUtil;
import com.dpkj.modules.scanface.wx.util.WxRandomUtils;
import com.dpkj.modules.scanface.wx.util.XmlParserUtil;
import com.dpkj.modules.scanface.wx.util.XmlUtils;
import com.dpkj.modules.scanface.wx.vo.WxFacePayAuthinfoResp;
import com.dpkj.modules.scanface.wx.vo.WxFacePayMicroPayResp;
import com.dpkj.modules.scanface.wx.vo.WxFacePayOrderResp;
import com.dpkj.modules.scanface.wx.vo.WxFacePayReq;
import com.dpkj.modules.scanface.wx.vo.WxFacePayResp;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @description: 调用微信刷脸方法
* @author: Zhangxue
* @time: 2024/12/6 10:45
*/
@Slf4j
@Service
public class WeChatPayFaceServiceImpl implements WeChatPayFaceService {
@Autowired
private CallWxpayFaceService callWxpayFaceService;
//赋值
@Autowired
private WxMpProperties wxMpProperties;
/**
* 调用方法
* 1程序启动时初始化initWxpayface2获取数据getWxpayfaceRawdata
*
* @param wxFacePayReq
* @return
* @throws UnsupportedEncodingException
* @throws JsonProcessingException
*/
@Override
public WxFacePayResp doWxPayIniMethod(WxFacePayReq wxFacePayReq) throws JsonProcessingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
ObjectMapper mapper = new ObjectMapper();
String reqJson = mapper.writeValueAsString(wxFacePayReq);
// 创建一个Pointer来接收响应
//List<String> pResp= new ArrayList<>();
//String result = callWxpayFaceService.callWxpayFaceService(req,pResp);
//组装请求数据
Pointer reqPointer = new Memory(reqJson.length() + 1);
reqPointer.setString(0, reqJson);
//请求数据长度
int reqSize = reqPointer.getString(0).length();
System.out.println("-----------调用微信刷脸DLL请求数据----------" + reqPointer.getString(0));
//接收响应
long[] pRespBuf = new long[1];
int[] respSize = new int[1];
String resStr = new String();
// 调用本地方法
WxpayFaceSDKDll.Dll dll = WxpayFaceSDKDll.instance();
int result = dll.wxpayCallFaceService(reqPointer.getString(0), reqSize, pRespBuf, respSize);
//使用C:\Windows\System32目录下 int result = WxpayFaceSDK.INSTANCE.wxpayCallFaceService(reqPointer.getString(0), reqSize, pRespBuf, respSize);
log.info("[WeChatPayFaceServiceImpl][doWxPayIniMethod][95] DLL 返回码: {}", result);
if (result == 0) {
Pointer pointer = new Pointer(pRespBuf[0]);
byte[] byteArray = pointer.getByteArray(0, respSize[0]);
resStr = new String(byteArray, StandardCharsets.UTF_8);
System.out.println("-----------微信刷脸DLL调用服务成功结果: " + resStr);
//释放
dll.wxpayReleaseResponse(new String[2]);
//释放 C:\Windows\System32目录下 WxpayFaceSDK.INSTANCE.wxpayReleaseResponse(new String[2]);
} else {
Pointer pointer = new Pointer(pRespBuf[0]);
byte[] byteArray = pointer.getByteArray(0, respSize[0]);
resStr = new String(byteArray, StandardCharsets.UTF_8);
System.err.println("-----------调用人脸服务失败: " + resStr);
}
//响应结果
WxFacePayResp wxFacePayResp = mapper.readValue(resStr, WxFacePayResp.class);
return wxFacePayResp;
}
/**
* @description: 3获取调用凭证get_wxpayface_authinfo(rawdata)
* @author: zhangxue
* @date: 2024/12/4 14:35
* @Param rawData 初始化数据由微信人脸SDK的接口返回
* @return: java.util.Map<java.lang.String, java.lang.String>
*/
@Override
public WxFacePayAuthinfoResp getWxFaceAuthInfoReqMap(String rawData) throws Exception {
try {
SortedMap<String, String> map = new TreeMap<String, String>();
//门店编号 由商户定义 各门店唯一
map.put("store_id", WxConstant.STORE_ID);
//门店名称由商户定义可用于展示;中文会导致调用失败
String text = WxConstant.STORE_TEXT;
String storeName = Base64.getEncoder().encodeToString(text.getBytes());
map.put("store_name", storeName);
//终端设备编号由商户定义
map.put("device_id", WxConstant.DEVICE_ID);
//初始化数据由微信人脸SDK的接口返回
map.put("rawdata", rawData);
//商户号绑定的公众号/小程序 appid
map.put("appid", wxMpProperties.getMchConfig().getAppId());
//商户号
map.put("mch_id", wxMpProperties.getMchConfig().getMchId());
//取当前时间10位unix时间戳
long timeStampSec = System.currentTimeMillis() / 1000;
String timestamp = String.format("%010d", timeStampSec);
map.put("now", timestamp);
//版本号固定为1
map.put("version", WxConstant.VERSION);
//随机字符串不长于32位:工具类微信随机数
map.put("nonce_str", WxRandomUtils.getNonceStr());
//参数签名,使用MD5
map.put("sign_type", WxConstant.SING_TYPE);
//加密和生成微信v2指定的xml格式
// WXPayUtil.createSign("UTF-8",map,wxMpProperties.getMchConfig().getKeyApi());
String sign = WXPayUtil.generateSignedXml(map, wxMpProperties.getMchConfig().getKeyApi(), WxPayConstants.SignType.MD5);
log.info("--------3、获取调用凭证 构建微信获取刷脸授权XML参数{}", sign);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> stringHttpEntity = new HttpEntity<String>(sign, headers);
RestTemplate restTemplate = new RestTemplate();
//发起http调用
ResponseEntity<String> exchange = restTemplate.exchange(WechatUrlConfig.GET_WXPAYFACE_AUTHINFO,
HttpMethod.POST,
stringHttpEntity,
String.class);
log.info("--------3、获取调用凭证 发起http调用结果{}", exchange.getBody());
XmlParserUtil.extractAuthInfo(exchange.getBody());
//转成map方便取值
Map<String, String> stringMap = XmlUtils.xmlParser(exchange.getBody(), "xml");
log.info("------3、获取调用凭证 调用结果转换为map{}", stringMap);
//转成返回对象
WxFacePayAuthinfoResp wxFacePayAuthinfoResp = XmlUtils.mapToObject(
stringMap,
WxFacePayAuthinfoResp.class
);
log.info("--------发起http调用结果转换为WxFacePayAuthinfoResp", wxFacePayAuthinfoResp.toString());
return wxFacePayAuthinfoResp;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 调用方法
* 4进行人脸识别getWxpayfaceCode获取支付凭证
*
* @param wxFacePayReq
* @return
* @throws UnsupportedEncodingException
* @throws JsonProcessingException
*/
@Override
public WxFacePayAuthinfoResp getWxpayfaceCode(WxFacePayReq wxFacePayReq) throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
//设置参数
wxFacePayReq.setAppid(wxMpProperties.getMchConfig().getAppId())
.setMch_id(wxMpProperties.getMchConfig().getMchId())
.setStore_id(WxConstant.STORE_ID)
.setFace_authtype(WxConstant.FACEPAY)
.setAuthinfo(wxFacePayReq.getAuthinfo());
// 构建请求参数的JSON字符串
ObjectMapper mapper = new ObjectMapper();
String req = mapper.writeValueAsString(wxFacePayReq);
// 创建一个Pointer来接收响应
List<String> pResp = new ArrayList<>();
String result = callWxpayFaceService.callWxpayFaceService(req, pResp);
System.out.println("4、进行人脸识别getWxpayfaceCode结果"+result);
log.info("[WeChatPayFaceServiceImpl][getWxpayfaceCode][220][4、进行人脸识别getWxpayfaceCode结果] {}", result);
//响应结果
WxFacePayAuthinfoResp authinfoResp = mapper.readValue(result, WxFacePayAuthinfoResp.class);
return authinfoResp;
}
//5进行发起订单支付
public Map<String, String> createWxOrder() throws Exception {
SortedMap<String, String> map = new TreeMap<String, String>();
//微信分配的公众账号ID企业号corpid即为此appId
map.put("appid", wxMpProperties.getMchConfig().getAppId());
//商户号
map.put("mch_id", wxMpProperties.getMchConfig().getMchId());
//随机字符串不长于32位:工具类微信随机数
map.put("nonce_str", WxRandomUtils.getNonceStr());
//签名 map.put("sign", "");
//商品简单描述
map.put("body", "");
//商户系统内部订单号要求32个字符内只能是数字大小写字母_-|*且在同一个商户号下唯一
map.put("out_trade_no", "");
//Int 订单总金额单位为分只能为整数
map.put("total_fee", "");
//支持IPV4和IPV6两种格式的IP地址调用微信支付API的机器IP
map.put("spbill_create_ip", "");
//扫码支付付款码设备读取用户微信中的条码或者二维码信息
map.put("auth_code", "");
//加密和生成微信v2指定的xml格式
// WXPayUtil.createSign("UTF-8",map,wxMpProperties.getMchConfig().getKeyApi());
String sign = WXPayUtil.generateSignedXml(map, wxMpProperties.getMchConfig().getKeyApi(), WxPayConstants.SignType.MD5);
log.info("--------构建微信获取刷脸授权XML参数【{}】", sign);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> stringHttpEntity = new HttpEntity<String>(sign, headers);
RestTemplate restTemplate = new RestTemplate();
//发起http调用
ResponseEntity<String> exchange = restTemplate.exchange(WechatUrlConfig.CREATEORDER,
HttpMethod.POST,
stringHttpEntity,
String.class);
log.info("--------5、进行发起订单支付:发起http调用结果【{}】", exchange.getBody());
//转成map方便取值
Map<String, String> stringMap = XmlUtils.xmlParser(exchange.getBody(), "xml");
//转成返回对象
ObjectMapper mapper = new ObjectMapper();
WxFacePayMicroPayResp microPayResp = mapper.readValue(exchange.getBody(), WxFacePayMicroPayResp.class);
log.info("--------5、进行发起订单支付,发起http调用结果转换", microPayResp.toString());
return stringMap;
}
//6查询订单状态
public Map<String, String> orderquery() throws Exception {
SortedMap<String, String> map = new TreeMap<String, String>();
//微信支付分配的公众账号ID企业号corpid即为此appId
map.put("appid", wxMpProperties.getMchConfig().getAppId());
//微信支付分配的商户号
map.put("mch_id", wxMpProperties.getMchConfig().getMchId());
//微信的订单号建议优先使用必填
map.put("transaction_id", "");
//商户系统内部订单号必填
map.put("out_trade_no", "");
//随机字符串不长于32位:工具类微信随机数
map.put("nonce_str", WxRandomUtils.getNonceStr());
//参数签名,使用MD5
map.put("sign_type", "MD5");
//sign:通过签名算法计算得出的签名值
String sign = WXPayUtil.generateSignedXml(map, wxMpProperties.getMchConfig().getKeyApi(), WxPayConstants.SignType.MD5);
log.info("--------构建微信获取刷脸授权XML参数【{}】", sign);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> stringHttpEntity = new HttpEntity<String>("", headers);
RestTemplate restTemplate = new RestTemplate();
//发起http调用
ResponseEntity<String> exchange = restTemplate.exchange(WechatUrlConfig.ORDERQUERY,
HttpMethod.POST,
stringHttpEntity,
String.class);
log.info("--------6、查询订单状态,发起http调用结果【{}】", exchange.getBody());
//转成map方便取值
Map<String, String> stringMap = XmlUtils.xmlParser(exchange.getBody(), "xml");
//转成返回对象
ObjectMapper mapper = new ObjectMapper();
WxFacePayOrderResp orderResp = mapper.readValue(exchange.getBody(), WxFacePayOrderResp.class);
log.info("--------6、查询订单状态,发起http调用结果转换", orderResp.toString());
return stringMap;
}
//7撤销交易
public Map<String, String> reverse() throws Exception {
SortedMap<String, String> map = new TreeMap<String, String>();
//微信分配的公众账号ID企业号corpid即为此appId
map.put("appid", wxMpProperties.getMchConfig().getAppId());
//商户号
map.put("mch_id", wxMpProperties.getMchConfig().getMchId());
//微信的订单号优先使用
map.put("transaction_id", "");
//商户系统内部的订单号,transaction_idout_trade_no二选一如果同时存在优先级transaction_id> out_trade_no
map.put("out_trade_no", "");
//随机字符串不长于32位
map.put("nonce_str", WxRandomUtils.getNonceStr());
//加密和生成微信v2指定的xml格式
String sign = WXPayUtil.generateSignedXml(map, wxMpProperties.getMchConfig().getKeyApi(), WxPayConstants.SignType.MD5);
log.info("--------构建微信XML参数【{}】", sign);
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> stringHttpEntity = new HttpEntity<String>("", headers);
RestTemplate restTemplate = new RestTemplate();
//发起http调用
ResponseEntity<String> exchange = restTemplate.exchange(WechatUrlConfig.REVERSE,
HttpMethod.POST,
stringHttpEntity,
String.class);
log.info("--------7、撤销交易,发起http调用结果【{}】", exchange.getBody());
//转成map方便取值
Map<String, String> stringMap = XmlUtils.xmlParser(exchange.getBody(), "xml");
//转成返回对象
ObjectMapper mapper = new ObjectMapper();
WxFacePayOrderResp orderResp = mapper.readValue(exchange.getBody(), WxFacePayOrderResp.class);
log.info("--------7、撤销交易,发起http调用结果转换", orderResp.toString());
return stringMap;
}
//8更新支付结果通知人脸SDK更新支付结果
public WxFacePayResp updateWxpayfacePayResult(String storeId, String authInfo, String payresult) throws Exception {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("updateWxpayfacePayResult", "1", System.currentTimeMillis() / 1000);
//微信分配的公众账号ID企业号corpid即为此appId
wxFacePayReq.setAppid(wxMpProperties.getMchConfig().getAppId());
//商户号
wxFacePayReq.setMch_id(wxMpProperties.getMchConfig().getMchId());
//门店编号
wxFacePayReq.setStore_id(storeId);
//调用凭证
wxFacePayReq.setAuthinfo(authInfo);
//支付结果可取值SUCCESS: 支付成功ERROR: 支付失败
wxFacePayReq.setPayresult(payresult);
// 构建请求参数的JSON字符串
ObjectMapper mapper = new ObjectMapper();
String req = mapper.writeValueAsString(wxFacePayReq);
// 创建一个Pointer来接收响应
List<String> pResp = new ArrayList<>();
String result = callWxpayFaceService.callWxpayFaceService(req, pResp);
//响应结果
WxFacePayResp authinfoResp = mapper.readValue(result, WxFacePayResp.class);
return authinfoResp;
}
//9停止刷脸支付
public WxFacePayResp stopWxpayface(String storeId, String authInfo, String payresult) throws Exception {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("stopWxpayface", "1", System.currentTimeMillis() / 1000);
//微信分配的公众账号ID企业号corpid即为此appId
wxFacePayReq.setAppid(wxMpProperties.getMchConfig().getAppId());
//商户号
wxFacePayReq.setMch_id(wxMpProperties.getMchConfig().getMchId());
//调用凭证
wxFacePayReq.setAuthinfo(authInfo);
// 构建请求参数的JSON字符串
ObjectMapper mapper = new ObjectMapper();
String req = mapper.writeValueAsString(wxFacePayReq);
// 创建一个Pointer来接收响应
List<String> pResp = new ArrayList<>();
String result = callWxpayFaceService.callWxpayFaceService(req, pResp);
//响应结果
WxFacePayResp authinfoResp = mapper.readValue(result, WxFacePayResp.class);
return authinfoResp;
}
}

View File

@ -0,0 +1,48 @@
package com.dpkj.modules.scanface.wx.util;
import java.security.MessageDigest;
/**
* @description: MD5工具类
* @author: Zhangxue
* @time: 2024/12/5 11:34
*/
public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}

View File

@ -0,0 +1,115 @@
package com.dpkj.modules.scanface.wx.util;
import org.jdom2.Content;
import org.jdom2.Document;
import org.jdom2.Element;
import javax.xml.bind.DatatypeConverter;
import java.io.StringReader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
/**
* @description: 生成签名
* @author: Zhangxue
* @time: 2024/12/4 14:27
*/
public class WXPayUtil {
/**
* 生成微信API请求的MD5签名并返回XML格式的字符串
*
* @param params 参数Map
* @param apiKey 微信API密钥
* @return 微信v2指定的XML格式字符串
*/
public static String generateSignedXml(Map<String, String> params, String apiKey,String signType) {
// 将参数Map按照key的字典顺序排序
Map<String, String> sortedParams = new TreeMap<>(params);
// 构建签名原文
StringBuilder signSrc = new StringBuilder();
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
if (!"sign".equals(entry.getKey())) {
signSrc.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
}
}
signSrc.append("key=").append(apiKey);
// 计算MD5签名
String sign = getMD5(signSrc.toString(),signType).toUpperCase();
// 构建XML格式的字符串
StringBuilder xmlBuilder = new StringBuilder();
xmlBuilder.append("<xml>");
for (Map.Entry<String, String> entry : sortedParams.entrySet()) {
xmlBuilder.append("<").append(entry.getKey()).append(">").append(entry.getValue()).append("</").append(entry.getKey()).append(">");
}
xmlBuilder.append("<sign><![CDATA[").append(sign).append("]]></sign>");
xmlBuilder.append("</xml>");
return xmlBuilder.toString();
}
/**
* 使用MD5算法计算字符串的签名
*
* @param input 输入字符串
* @return MD5签名字符串
*/
private static String getMD5(String input,String signType) {
try {
MessageDigest md = MessageDigest.getInstance(signType);
byte[] digest = md.digest(input.getBytes());
return DatatypeConverter.printHexBinary(digest).toLowerCase();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5算法不支持", e);
}
}
public static Map<String, String> xmlToMap(String xml) throws Exception {
Map<String, String> map = new HashMap<>();
Document document = new Document((List<? extends Content>) new StringReader(xml));
Element root = document.getRootElement();
for (Element child : root.getChildren()) {
map.put(child.getName(), child.getText());
}
return map;
}
//定义签名微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序
public static String createSign(String characterEncoding, SortedMap<String,String> parameters,String key){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
Object v = entry.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
sb.append(k + "=" + v + "&");
}
}
sb.append("key=" + key);//最后加密时添加商户密钥由于key值放在最后所以不用添加到SortMap里面去单独处理编码方式采用UTF-8
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
}

View File

@ -0,0 +1,38 @@
package com.dpkj.modules.scanface.wx.util;
import java.security.SecureRandom;
import java.util.Random;
public class WxRandomUtils {
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Random RANDOM = new SecureRandom();
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String getNonceStr() {
char[] nonceChars = new char[32];
for (int index = 0; index < nonceChars.length; ++index) {
nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));
}
return new String(nonceChars);
}
/**
* 拼接参数
*
* @return
*/
private static String buildMessageTwo(String appId, long timestamp, String nonceStr, String packag) {
return appId + "\n"
+ timestamp + "\n"
+ nonceStr + "\n"
+ packag + "\n";
}
}

View File

@ -0,0 +1,45 @@
package com.dpkj.modules.scanface.wx.util;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import java.io.ByteArrayInputStream;
/**
* @description: 解析微信返回的xml数据
* @author: Zhangxue
* @time: 2025/5/28 10:10
*/
public class XmlParserUtil {
/**
* 解析微信返回的xml数据获取到authinfo
* @param xmlResponse
* @return
* @throws Exception
*/
public static String extractAuthInfo(String xmlResponse) throws Exception {
// 1. 创建DocumentBuilder解析XML
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
// 2. 将字符串转为输入流
ByteArrayInputStream input = new ByteArrayInputStream(xmlResponse.getBytes("UTF-8"));
Document doc = builder.parse(input);
// 3. 创建XPath表达式定位<authinfo>节点
XPath xpath = XPathFactory.newInstance().newXPath();
String expression = "//authinfo"; // 使用XPath查找所有authinfo节点
// 4. 提取节点文本内容自动处理CDATA
Node authInfoNode = (Node) xpath.evaluate(expression, doc, XPathConstants.NODE);
if (authInfoNode != null) {
return authInfoNode.getTextContent();
} else {
throw new RuntimeException("未找到<authinfo>节点");
}
}
}

View File

@ -0,0 +1,114 @@
package com.dpkj.modules.scanface.wx.util;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* @description: xml解析为Map
* @author: Zhangxue
* @time: 2024/12/5 11:42
*/
public class XmlUtils {
/**
* xml解析器
*
* @param xml xml字符串
* @param filterRootEleName 匹配的根节点名
* @return
* @返回map格式
* @返回属性值->节点名=>属性名属性值
* @返回节点值->节点名
* @返回子节点属性值->节点名->子节点名=>属性名属性值
* @返回子节点值->节点名->子节点名属性值
*/
public static Map<String, String> xmlParser(String xml, String filterRootEleName) {
Map<String, String> retMap = new HashMap<>();
//1.创建Reader对象
try {
SAXReader reader = new SAXReader();
InputStream targetStream = IOUtils.toInputStream(xml, StandardCharsets.UTF_8.name());
Document document = reader.read(targetStream);
//3.获取根节点
Element rootElement = document.getRootElement();
StringBuilder builder = new StringBuilder();
parser(rootElement, builder, filterRootEleName, retMap);
} catch (Exception ex) {
ex.printStackTrace();
}
return retMap;
}
/**
* xml递归解析器
*
* @param ele 解析节点
* @param eleKey 上级节点key
* @param retMap 返回map
*/
private static void parser(Element ele, StringBuilder eleKey, String firstEleName, Map<String, String> retMap) {
StringBuilder builder = new StringBuilder();//eleKey.toString()
if (StringUtils.isEmpty(firstEleName)
|| firstEleName.equals(ele.getName())) {
firstEleName = null;
//builder.append("->" + ele.getName());
if (StringUtils.isNotEmpty(StringUtils.stripToEmpty(ele.getData() + ""))) {
retMap.put(ele.getName(), StringUtils.stripToEmpty(ele.getData() + ""));
}
List<Attribute> attributes = ele.attributes();
for (Attribute attribute : attributes) {
StringBuilder builder1 = new StringBuilder(builder.toString());
builder1.append("=>" + attribute.getName());
if (StringUtils.isNotEmpty(attribute.getValue())) {
retMap.put(builder1.toString(), attribute.getValue());
}
}
}
Iterator iterator1 = ele.elementIterator();
while (iterator1.hasNext()) {
Element eleChild = (Element) iterator1.next();
parser(eleChild, builder, firstEleName, retMap);
}
}
/**
* 通用方法将Map转换为Java对象
*/
public static <T> T mapToObject(Map<String, String> map, Class<T> clazz) throws Exception {
T obj = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
String value = map.get(fieldName);
if (value != null) {
Class<?> type = field.getType();
// 类型转换逻辑
if (type == int.class || type == Integer.class) {
field.set(obj, Integer.parseInt(value));
} else if (type == boolean.class || type == Boolean.class) {
field.set(obj, Boolean.parseBoolean(value));
} else {
field.set(obj, value);
}
}
}
return obj;
}
}

View File

@ -0,0 +1,20 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Map;
/**
* @description: 硬件提供参数
* @author: Zhangxue
* @time: 2024/11/28 10:01
*/
@Data
@Accessors(chain = true)
public class WxFaceAuthInfoReq {
private String deviceId;
private Map<String,String> rawData;
}

View File

@ -0,0 +1,43 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @description: 微信刷脸订单参数
* @author: Zhangxue
* @time: 2025/5/28 15:35
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxFaceOrderVo {
/**
* 付款模块
*/
private String eventModule;
/**
* 患者Id
*/
private String patientId;
/**
* 商户机具终端编号
*/
private String terminalId;
/**
* 系统订单编号
*/
private String outTradeNo;
/**
* 用户支付金额
*/
private String totalAmount;
}

View File

@ -0,0 +1,167 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @description: 3获取调用凭证返回值
* @author: Zhangxue
* @time: 2024/12/6 9:23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxFacePayAuthinfoResp {
/**
* 错误码
*/
private String return_code;
/**
* 对错误码的描述
*/
private String return_msg;
/**
* 版本号
*/
private String version;
/**
* 初始化数据用于接口调用获取authinfo
*/
private String rawdata;
/**
* 3获取调用凭证
* 必填
* SDK调用凭证用于调用SDK的人脸识别接口
*/
private String authinfo;
/**
* 3获取调用凭证
* 必填
* authinfo的有效时间, 单位秒
* 在有效时间内, 对于同一台终端设备相同的参数的前提下(相同的公众号商户号 门店编号等可以用同一个authinfo多次调用SDK的getWxpayfaceCode接口
*/
private int expires_in;
/**
* 3获取调用凭证
* 必填
* 随机字符串
*/
private String nonce_str;
/**
* 3获取调用凭证
* 必填
* 响应结果签名
*/
private String sign;
/**
* 3获取调用凭证
* 必填
* 公众号
*/
private String appid;
/**
* 3获取调用凭证
* 必填
* 商户号
*/
private String mch_id;
/**
* 3获取调用凭证
* 必填
* 子商户公众账号ID(服务商模式)
*/
private String sub_appid;
/**
* 3获取调用凭证
* 必填
* 子商户号(服务商模式)
*/
private String sub_mch_id;
/**
* 4进行人脸识别
* 是否必填S
* 人脸凭证, 用于刷脸支付
*/
private String face_code;
/**
* 4进行人脸识别
* 是否必填S
* openid
*/
private String openid;
/**
* 4进行人脸识别
* 是否必填
* 子商户号下的openid(服务商模式)
*/
private String sub_openid;
/**
* 4进行人脸识别
* 是否必填
* 用户身份信息查询凭证
*/
private String face_sid;
/**
* 传参到后台发起微信订单方法
* 4进行人脸识别
* 必填
* 订单金额(数字), 单位分该字段在在face_code_type为"1"时可不填"0"时必填
*/
private String total_fee;
/**
* 传参到后台发起微信订单方法
* 4进行人脸识别
* 必填
* 商户订单号须与调用支付接口时字段一致该字段在在face_code_type为"1"时可不填"0"时必填
*/
private String out_trade_no;
/**
* 传参到后台发起微信订单方法
* 商品描述
*/
private String remark;
/**
* 传参到后台发起微信订单方法
* 患者Id
*/
private String patientId;
/**
* 传参到后台发起微信订单方法
* 付款模块
*/
private String eventModule;
/**
* 传参到后台发起微信订单方法
* 商户机具终端编号
*/
private String terminalId;
}

View File

@ -0,0 +1,95 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @description: 5进行发起订单支付 返回值
* @author: Zhangxue
* @time: 2024/12/6 9:23
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxFacePayMicroPayResp extends WxFacePayOrderResp{
/************当return_code 和result_code都为SUCCESS的时还会包括以下字段*******************/
/**
* 用户在商户appid 下的唯一标识
*/
private String openid;
/**
* 用户是否关注公众账号仅在公众账号类型支付有效取值范围Y或N;Y-关注;N-未关注
*/
private String is_subscribe;
/**
* MICROPAY 付款码支付
*/
private String trade_type;
/**
* 银行类型采用字符串类型的银行标识
*/
private String bank_type;
/**
* 符合ISO 4217标准的三位字母代码默认人民币CNY
*/
private String fee_type;
/**
* 订单总金额单位为分只能为整数
*/
private Integer total_fee;
/**
* 当订单使用了免充值型优惠券后返回该参数应结订单金额=订单金额-免充值优惠券金额
*/
private Integer settlement_total_fee;
/**
* 代金券金额<=订单金额订单金额-代金券金额=现金支付金额
*/
private Integer coupon_fee;
/**
* 符合ISO 4217标准的三位字母代码默认人民币CNY
*/
private String cash_fee_type;
/**
* 订单现金支付金额
*/
private Integer cash_fee;
/**
* 微信支付订单号
*/
private String transaction_id;
/**
* 商户系统内部订单号要求32个字符内只能是数字大小写字母_-|*且在同一个商户号下唯一
*/
private String out_trade_no;
/**
* 商家数据包原样返回
*/
private String attach;
/**
* 订单生成时间格式为yyyyMMddHHmmss
*/
private String time_end;
/**
* 新增返回单品优惠功能字段
*/
private String promotion_detail;
}

View File

@ -0,0 +1,136 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @description: 5进行发起订单支付 返回值;6查询订单状态
* @author: Zhangxue
* @time: 2024/12/10 15:30
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxFacePayOrderResp {
/**
* 错误码
* 文档https://pay.weixin.qq.com/wiki/doc/wxfacepay/develop/windows/facepay.html#_5%E3%80%81%E8%BF%9B%E8%A1%8C%E5%8F%91%E8%B5%B7%E8%AE%A2%E5%8D%95%E6%94%AF%E4%BB%98
*/
private String return_code;
/**
* 对错误码的描述
*/
private String return_msg;
/**
* 调用接口提交的公众账号ID
*/
private String appid;
/**
* 调用接口提交的商户号
*/
private String mch_id;
/**
* 调用接口提交的终端设备号
*/
private String device_info;
/**
* 微信返回的随机字符串
*/
private String nonce_str;
/**
* 微信返回的签名
*/
private String sign;
/**
* SUCCESS/FAIL
*/
private String result_code;
/**
* 详细参见错误列表
*/
private String err_code;
/**
* 错误返回的信息描述
*/
private String err_code_des;
/**
* SUCCESS支付成功,
* REFUND转入退款当result_code为FAIL时返回错误代码
* NOTPAY未支付当result_code为FAIL时返回错误描述
* CLOSED已关闭REVOKED已撤销付款码支付
* USERPAYING--用户支付中付款码支付
* PAYERROR--支付失败(其他原因如银行返回失败)
*/
private String trade_state;
/**
* 商户系统内部订单号要求32个字符内只能是数字大小写字母_-|*@ 且在同一个商户号下唯一
*/
private String out_trade_no;
/**
* 附加数据原样返回
*/
private String attach;
/**
* 用户在商户appid下的唯一标识
*/
private String openid;
/**
* 用户是否关注公众账号Y-关注N-未关注
*/
private String is_subscribe;
/**
* 调用接口提交的交易类型取值如下JSAPINATIVEAPPMICROPAY
*/
private String trade_type;
/**
* 银行类型采用字符串类型的银行标识
*/
private String bank_type;
/**
* 订单总金额单位为分
*/
private Integer total_fee;
/**
* 现金支付金额订单现金支付金额
*/
private Integer cash_fee;
/**
* 微信支付订单号
*/
private String transaction_id;
/**
* 订单支付时间格式为yyyyMMddHHmmss如20091225091010
*/
private String time_end;
/**
* 对当前查询订单状态的描述和下一步操作的指引
*/
private int trade_state_desc;
}

View File

@ -0,0 +1,169 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @description:WxFacePayReq
* @author: Zhangxue
* @time: 2024/11/29 17:18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxFacePayReq {
/**
* 命令字即各接口名
* initWxpayface
* getWxpayfaceRawdata
* getWxpayfaceCode
* releaseWxpayface
*/
private String cmd;
/**
* 版本号, 固定填写1
*/
private String version;
/**
* 取当前时间的10位unix时间戮
*/
private long now;
/**
* 对摄像头画面进行旋转1为+90度2为180度3为-90度
*/
private int camera_rotation;
/**
* 4进行人脸识别
* 必填
* 商户号绑定的公众号/小程序 appid
*/
private String appid;
/**
* 4进行人脸识别
* 必填
* 商户号
*/
private String mch_id;
/**
* 4进行人脸识别
* 必填
* 子商户绑定的公众号/小程序 appid(可不填)
*/
//private String sub_appid;
/**
* 4进行人脸识别
* 必填
* 子商户号(非服务商模式不填)
*/
//private String sub_mch_id;
/**
* 4进行人脸识别
* 必填
* 门店编号
*/
private String store_id;
/**
* 4进行人脸识别
* 必填
* 通过getWxpayfaceUserInfo获取的openid传入后可使用快捷支付模式1.24版本以上支持该参数
*/
//private String openid;
/**
* 4进行人脸识别
* 必填
* 目标face_code类型可选值"1"刷卡付款码18位数字通过付款码支付/被扫支付接口完成支付
* 如果不填写则默认为"0"人脸付款码数字字母混合通过刷脸支付接口完成支付1.16版本以上支持该参数
*/
private String face_code_type;
/**
* 4进行人脸识别
* 必填
* 订单金额(数字), 单位分该字段在在face_code_type为"1"时可不填"0"时必填
*/
private String total_fee;
/**
* 4进行人脸识别
* 必填
* 商户订单号须与调用支付接口时字段一致该字段在在face_code_type为"1"时可不填"0"时必填
*/
private String out_trade_no;
/**
* 4进行人脸识别
* 必填
* FACEPAY: 人脸凭证(face_code)用于刷脸支付 FACE_AUTH: 实名认证(需联系微信支付开通权限)
*/
private String face_authtype;
/**
* 4进行人脸识别
* 必填
* 调用凭证
*/
private String authinfo;
/**
* 4进行人脸识别
* 必填
* 指定刷脸界面显示的屏幕编号
* 编号1为主屏幕其余屏幕按系统设置中的顺序从2开始编号常用场景举例双屏机器上传"2"即可显示在副屏上如果不填写则默认显示在主屏幕1.18版本以上支持该参数
*/
//private String screen_index;
/**
* 4进行人脸识别
* 必填
* 设置不接受外接键盘输入可选值"1"禁用1.23版本以上支持该参数
*/
//private String disable_keyboard;
/**
* 4进行人脸识别
* 必填
* 设置刷脸窗口以无焦点方式启动可选值"1"无焦点启动窗口1.26版本以上支持该参数
*/
//private String use_window_nofocus;
/**
* 8更新支付结果
* 支付结果可取值SUCCESS: 支付成功ERROR: 支付失败
*/
private String payresult;
//构建
public WxFacePayReq(String cmd, String version, long now) {
this.cmd= cmd;
this.version =version;
this.now = now;
}
//构建
public WxFacePayReq(String cmd, String version, long now,int camera_rotation) {
this.cmd= cmd;
this.version =version;
this.now = now;
this.camera_rotation = camera_rotation;
}
}

View File

@ -0,0 +1,40 @@
package com.dpkj.modules.scanface.wx.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @description:WxFacePayResp 调用微信刷脸的返回值
* @author: Zhangxue
* @time: 2024/11/29 17:18
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class WxFacePayResp {
/**
* 错误码
*/
private String return_code;
/**
* 对错误码的描述
*/
private String return_msg;
/**
* 版本号
*/
private String version;
/**
* 2获取数据
* 初始化数据用于接口调用获取authinfo
*/
private String rawdata;
}

View File

@ -1,4 +1,6 @@
dpkj:
#后端项目访问地址 #https://yinyitong.yzqingyan.cn/ http://172.16.11.13:15946/ ttps://yinyitong.yzqingyan.cn
serverurl: http://localhost:5946/api/
# 医保配置
chs:
# 医保机构编码
@ -20,6 +22,66 @@ dpkj:
port-name:
# 波特率 串口连接下使用
baud-rate:
#支付宝刷脸
ali:
face:
#dll文件路径
dll-path: C:/opt/ant-abcp/bpaas_api.dll
#IOT 应用管理-appid
app-id: 2021005138656502
#IOT应用版本
app-version: 1.0.0.0
#签约商家的 PID以 2088 开头,企业主体
merchant-id: 2088641941653700
#商家机具终端编号,每台设备保持唯一
device-num: P060003750
#服务商的 PID
partner-id: 2088641941653700
# 核心入参 serviceId
service-id: pay
#微信模块
wx:
configs: #清研家
- app-id: wxe8334dd2140bb0e1
# 公众号的appsecret
secret: f83420d79cc6ecd1d7fe9684ac9cdfe4
# 接口配置里的Token值
token: dpkjylwjvote
# 接口配置里的EncodingAESKey值
aes-key: go2uM3ASe2rEyeoNsZHoPCiKGgpku0Bi49P5IypdQWT
mch-config: #商户信息 清研家关联的商户:驿路万家-扬州清研软件科技
app-id: wxe8334dd2140bb0e1
secret: f83420d79cc6ecd1d7fe9684ac9cdfe4
#调用接口所需service_id
service-id: service_id
#商户号
mch-id: 1557642321
#商户秘钥
mch-key: yndpkj15288216506YndpkjKsjytZx12
#报文解密 APIv3密钥
v3-key: yndpkj15288216506YndpkjKsjytZx12
#微信: 商户APIv2密钥
key-api: yndpkj15288216506YndpkjKsjytZx12
#p12证书的位置可以指定绝对路径也可以指定类路径以classpath;开头)
key-path: classpath:test\apiclient_cert.p12
private-key-path: classpath:test\apiclient_key.pem
private-cert-path: classpath:test\apiclient_cert.pem
#微信商家api序列号
mch-serial-no:
#JSAPI--公众号支付 NATIVE--原生扫码支付 APP--app支付
trade-type: JSAPI
trade-type-native: NATIVE
#支付回调方法地址
pay-notify-url: ${dpkj.url.server-url}/openapi/wxPayOrderApi/notify
#退款回调方法地址
refund-notify-url: ${dpkj.url.server-url}/openapi/wxPayOrderApi/refundNotify
# 路径
url:
# 后端接口地址
# server: https://102760424tfyw.vicp.fun/api
server: ${dpkj.url.server-url}
# h5地址
h5: ${dpkj.url.h5-url}
# 身份证读取等待时间
IDCardReader:

View File

@ -1,4 +1,6 @@
dpkj:
#后端项目访问地址 #https://yinyitong.yzqingyan.cn/ http://172.16.11.13:15946/ ttps://yinyitong.yzqingyan.cn
serverurl: http://localhost:5946/api/
# 医保配置
chs:
# 医保机构编码
@ -20,6 +22,66 @@ dpkj:
port-name:
# 波特率 串口连接下使用
baud-rate:
#支付宝刷脸
ali:
face:
#dll文件路径
dll-path: C:/opt/ant-abcp/bpaas_api.dll
#IOT 应用管理-appid
app-id: 2021005138656502
#IOT应用版本
app-version: 1.0.0.0
#签约商家的 PID以 2088 开头,企业主体
merchant-id: 2088641941653700
#商家机具终端编号,每台设备保持唯一
device-num: P060003750
#服务商的 PID
partner-id: 2088641941653700
# 核心入参 serviceId
service-id: pay
#微信模块
wx:
configs: #清研家
- app-id: wxe8334dd2140bb0e1
# 公众号的appsecret
secret: f83420d79cc6ecd1d7fe9684ac9cdfe4
# 接口配置里的Token值
token: dpkjylwjvote
# 接口配置里的EncodingAESKey值
aes-key: go2uM3ASe2rEyeoNsZHoPCiKGgpku0Bi49P5IypdQWT
mch-config: #商户信息 清研家关联的商户:驿路万家-扬州清研软件科技
app-id: wxe8334dd2140bb0e1
secret: f83420d79cc6ecd1d7fe9684ac9cdfe4
#调用接口所需service_id
service-id: service_id
#商户号
mch-id: 1557642321
#商户秘钥
mch-key: yndpkj15288216506YndpkjKsjytZx12
#报文解密 APIv3密钥
v3-key: yndpkj15288216506YndpkjKsjytZx12
#微信: 商户APIv2密钥
key-api: yndpkj15288216506YndpkjKsjytZx12
#p12证书的位置可以指定绝对路径也可以指定类路径以classpath;开头)
key-path: classpath:test\apiclient_cert.p12
private-key-path: classpath:test\apiclient_key.pem
private-cert-path: classpath:test\apiclient_cert.pem
#微信商家api序列号
mch-serial-no:
#JSAPI--公众号支付 NATIVE--原生扫码支付 APP--app支付
trade-type: JSAPI
trade-type-native: NATIVE
#支付回调方法地址
pay-notify-url: ${dpkj.url.server-url}/openapi/wxPayOrderApi/notify
#退款回调方法地址
refund-notify-url: ${dpkj.url.server-url}/openapi/wxPayOrderApi/refundNotify
# 路径
url:
# 后端接口地址
# server: https://102760424tfyw.vicp.fun/api
server: ${dpkj.server-url}
# h5地址
h5: ${dpkj.h5-url}
# 身份证读取等待时间

Binary file not shown.