微信刷脸SDK

This commit is contained in:
张雪 2025-04-20 09:11:11 +08:00
parent ee0b1ac055
commit 6dac863d45
19 changed files with 2066 additions and 0 deletions

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,253 @@
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 = "wx.mp")
public class WxMpProperties {
/**
* 是否使用redis存储access token
*/
private Boolean useRedis = false;
/**
* redis 配置
*/
private RedisConfig redisConfig;
@Data
public static class RedisConfig {
/**
* redis服务器 主机地址
*/
private String host;
/**
* redis服务器 端口号
*/
private Integer port;
/**
* redis服务器 密码
*/
private String password;
/**
* redis 服务连接超时时间
*/
private Integer timeout;
}
/**
* 多个公众号配置信息
*/
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 MpUrl url;
@Data
public static class MpUrl {
/**
* H5地址
*/
private String h5;
/**
* 接口地址
*/
private String server;
}
/**
* 模板消息id
*/
private MpTemplate template;
@Data
public static class MpTemplate {
/**
* 缴费通知
*/
private String payMsg;
/**
* 流程待办
*/
private String test;
/**
* 流程待办
*/
private String test2;
}
/**
* 微信商户支付配置
*/
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;
}
}

View File

@ -0,0 +1,156 @@
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 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 javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.security.Provider;
import java.security.Security;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.TreeSet;
/**
* @description:
* @author: Zhangxue
* @time: 2024/11/28 16:43
*/
@Slf4j
@RestController
@RequestMapping("/wxpayFace")
public class WxpayFaceTestController {
@Resource
private CallWxpayFaceService callWxpayFaceService;
@Resource
private WeChatPayFaceService weChatPayFaceService;
/**
* 1 程序启动时初始化 :程序启动时初始化initWxpayface
* @return
* @throws JsonProcessingException
*/
@RequestMapping(value = "/initWxpayface", method = RequestMethod.POST)
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<Map<String, Object>> getWxpayfaceAuthinfo() throws Exception {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceRawdata","1",System.currentTimeMillis()/1000);
WxFacePayResp wxFacePayResp1 = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
System.out.println("**************2、获取数据返回值"+wxFacePayResp1.toString());
String rawdata = wxFacePayResp1.getRawdata();
// 构建请求参数的JSON字符串
Map<String, Object> wxFacePayResp = weChatPayFaceService.getWxFaceAuthInfoReqMap(rawdata);
System.out.println("**************333"+wxFacePayResp.toString());
return Result.ok(wxFacePayResp);
}
/**
* 4进行人脸识别getWxpayfaceCode获取支付凭证
* @return
* @throws Exception
*/
@RequestMapping(value = "/getWxpayfaceCode", method = RequestMethod.POST)
public Result<WxFacePayAuthinfoResp> getWxpayfaceCode() throws Exception {
// 构建请求参数的JSON字符串
WxFacePayReq wxFacePayReq = new WxFacePayReq("getWxpayfaceCode","1",System.currentTimeMillis()/1000);
WxFacePayAuthinfoResp authinfoResp = weChatPayFaceService.doWxPayMethod(wxFacePayReq);
System.out.println("**************4、进行人脸识别"+authinfoResp.toString());
return Result.ok(authinfoResp);
}
//5进行发起订单支付
public static void main(String[] args) throws Exception {
//微信配置
CallWxpayFaceService callWxpayFaceService = new CallWxpayFaceServiceImpl();
WeChatPayFaceService weChatPayFaceService = new WeChatPayFaceServiceImpl();
WxFacePayReq wxFacePayReq = new WxFacePayReq("initWxpayface","1",System.currentTimeMillis()/1000);
WxFacePayResp wxFacePayResp = weChatPayFaceService.doWxPayIniMethod(wxFacePayReq);
System.out.println("**************1、程序启动时初始化"+wxFacePayResp.toString());
}
//可以打印 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,116 @@
package com.dpkj.modules.scanface.wx.dll;
import com.dpkj.modules.scanface.ali.dll.AliScanFaceDll;
import com.sun.jna.Library;
import com.sun.jna.Native;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
/**
* @description:
* @author: Zhangxue
* @time: 2025/4/17 17:02
*/
@Slf4j
public class WxpayFaceSDKDll {
private static final String DLL_NAME = "WxpayFaceSDK.dll"; // 确保文件名与实际资源一致
//win32-x86-64/WxpayFaceSDK.dll
private static final String RESOURCE_PATH = "win32-x86-64/" + DLL_NAME; // 例如: resources/native/WxpayFaceSDK.dll
private static volatile boolean dllLoaded = false;
static {
loadDllFromResources();
}
private static void loadDllFromResources() {
if (dllLoaded) return;
try {
// resources 中读取 DLL 文件流
InputStream inputStream = WxpayFaceSDKDll.class.getClassLoader().getResourceAsStream(RESOURCE_PATH);
if (inputStream == null) {
throw new RuntimeException("DLL 未找到: " + RESOURCE_PATH);
}
// 创建临时目录并提取 DLL
Path tempDir = Files.createTempDirectory("native-libs");
Path tempDll = tempDir.resolve(DLL_NAME);
Files.copy(inputStream, tempDll, StandardCopyOption.REPLACE_EXISTING);
inputStream.close();
// 显式加载 DLL
System.load(tempDll.toAbsolutePath().toString());
dllLoaded = true;
// 可选JVM 退出时清理临时文件
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
try {
Files.deleteIfExists(tempDll);
Files.deleteIfExists(tempDir);
} catch (Exception e) {
e.printStackTrace();
}
}));
} catch (Exception e) {
throw new RuntimeException("加载 DLL 失败", e);
}
}
public static Dll instance() throws DllRegistrationException {
try {
// 加载已提取的 DLL
return Native.load(DLL_NAME, Dll.class);
} catch (UnsatisfiedLinkError e) {
log.error("[WxpayFaceSDK][instance] 加载失败", e);
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,71 @@
package com.dpkj.modules.scanface.wx.dll;
import com.sun.jna.Library;
import com.sun.jna.Native;
import lombok.extern.slf4j.Slf4j;
/**
* @description:
* @author: Zhangxue
* @time: 2025/4/17 17:02
*/
@Slf4j
public class WxpayFaceSDKDll_old {
/**
* 获取 Dll 实例同时注册 Dll 控件
*
* @return WxpayFaceSDKDll 实例
* @throws WxpayFaceSDKDll_old.DllRegistrationException 如果注册控件失败抛出此异常
*/
public static WxpayFaceSDKDll_old.Dll instance() throws DllRegistrationException {
try {
return Native.load("WxpayFaceSDK", WxpayFaceSDKDll_old.Dll.class);
} catch (UnsatisfiedLinkError e) {
log.info("[WxpayFaceSDK][instance][微信扫脸动态库] SDK注册失败{}", e.getMessage());
throw new WxpayFaceSDKDll_old.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,18 @@
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,53 @@
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
* @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>
*/
Map<String, Object> getWxFaceAuthInfoReqMap(String rawData) throws Exception;
/**
* 调用方法
* 4进行人脸识别getWxpayfaceCode获取支付凭证
* @param wxFacePayReq
* @return
* @throws UnsupportedEncodingException
* @throws JsonProcessingException
*/
WxFacePayAuthinfoResp doWxPayMethod(WxFacePayReq wxFacePayReq) throws UnsupportedEncodingException, JsonProcessingException, WxpayFaceSDKDll.DllRegistrationException;
}

View File

@ -0,0 +1,78 @@
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.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());
}
//赋值
private static WxMpProperties wxMpProperties;
public static void setWxMpConfiguration(WxMpProperties wxMpProperties) {
CallWxpayFaceServiceImpl.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("-----------调用微信刷脸请求数据----------"+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);
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("-----------调用服务成功结果: " + resStr);
//释放
dll.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,385 @@
package com.dpkj.modules.scanface.wx.service.impl;
import com.dpkj.modules.scanface.ali.dll.AliScanFaceDll;
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.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;
//赋值
private static WxMpProperties wxMpProperties;
public static void setWxMpConfiguration(WxMpProperties wxMpProperties) {
WeChatPayFaceServiceImpl.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("-----------调用微信刷脸请求数据----------" + 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);
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("-----------调用服务成功结果: " + resStr);
//释放
dll.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 Map<String, Object> getWxFaceAuthInfoReqMap(String rawData) throws Exception {
SortedMap<String, String> map = new TreeMap<String, String>();
//门店编号 由商户定义 各门店唯一
map.put("store_id", "1111111111111");
//门店名称由商户定义可用于展示;中文会导致调用失败
String text = "刷脸设备一号";
String storeName = Base64.getEncoder().encodeToString(text.getBytes());
map.put("store_name", storeName);
//终端设备编号由商户定义
map.put("device_id", "test111");
//初始化数据由微信人脸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", "1");
//随机字符串不长于32位:工具类微信随机数
map.put("nonce_str", WxRandomUtils.getNonceStr());
//参数签名,使用MD5
map.put("sign_type", "MD5");
//加密和生成微信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.GET_WXPAYFACE_AUTHINFO,
HttpMethod.POST,
stringHttpEntity,
String.class);
log.info("--------发起http调用结果【{}】", exchange.getBody());
//转成map方便取值
Map<String, Object> stringMap = XmlUtils.xmlParser(exchange.getBody(), "xml");
//转成返回对象
ObjectMapper mapper = new ObjectMapper();
WxFacePayAuthinfoResp wxFacePayAuthinfoResp = mapper.readValue(exchange.getBody(), WxFacePayAuthinfoResp.class);
log.info("--------发起http调用结果转换", wxFacePayAuthinfoResp.toString());
return stringMap;
}
/**
* 调用方法
* 4进行人脸识别getWxpayfaceCode获取支付凭证
*
* @param wxFacePayReq
* @return
* @throws UnsupportedEncodingException
* @throws JsonProcessingException
*/
@Override
public WxFacePayAuthinfoResp doWxPayMethod(WxFacePayReq wxFacePayReq) throws JsonProcessingException, UnsupportedEncodingException, WxpayFaceSDKDll.DllRegistrationException {
// 构建请求参数的JSON字符串
ObjectMapper mapper = new ObjectMapper();
String req = mapper.writeValueAsString(wxFacePayReq);
// 创建一个Pointer来接收响应
List<String> pResp = new ArrayList<>();
String result = callWxpayFaceService.callWxpayFaceService(req, pResp);
//响应结果
WxFacePayAuthinfoResp authinfoResp = mapper.readValue(result, WxFacePayAuthinfoResp.class);
return authinfoResp;
}
//5进行发起订单支付
public Map<String, Object> 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, Object> 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, Object> 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, Object> 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, Object> 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, Object> 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,85 @@
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.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, Object> xmlParser(String xml, String filterRootEleName) {
Map<String, Object> 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, Object> retMap) {
StringBuilder builder = new StringBuilder(eleKey.toString());
if (StringUtils.isEmpty(firstEleName)
|| firstEleName.equals(ele.getName())) {
firstEleName = null;
builder.append("->" + ele.getName());
if (org.apache.commons.lang3.StringUtils.isNotEmpty(org.apache.commons.lang3.StringUtils.stripToEmpty(ele.getData() + ""))) {
retMap.put(builder.toString(), org.apache.commons.lang3.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);
}
}
}

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,124 @@
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的有效时间, 单位秒
*/
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;
}

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,154 @@
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;
/**
* 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;
}
}

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;
}