diff --git a/.idea/compiler.xml b/.idea/compiler.xml index de38128..853f6ea 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -7,25 +7,24 @@ - + + + - - - \ No newline at end of file diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/pay/WeChatPayConfig2.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/pay/WeChatPayConfig2.java deleted file mode 100644 index 478ee8b..0000000 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/pay/WeChatPayConfig2.java +++ /dev/null @@ -1,207 +0,0 @@ -package org.jeecg.config.pay; - -import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder; -import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner; -import com.wechat.pay.contrib.apache.httpclient.auth.Verifier; -import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials; -import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator; -import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager; -import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException; -import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException; -import com.wechat.pay.contrib.apache.httpclient.util.PemUtil; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; -import org.apache.http.impl.client.CloseableHttpClient; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.stereotype.Component; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.security.PrivateKey; - -/** - * @author java996.icu - * @title: WeChatPayConfig2 - * @projectName chemu - * @description: TODO - * @date 2022/12/7 16:36 - * @Version V1.0 - */ -@Component -@Data -@Slf4j -@ConfigurationProperties(prefix = "wxpay") -public class WeChatPayConfig2 { - - /** - * 应用编号 - */ - private String appId; - /** - * 商户号 - */ - private String mchId; - /** - * 服务商商户号 - */ - private String slMchId; - /** - * APIv2密钥 - */ - private String apiKey; - /** - * APIv3密钥 - */ - private String apiV3Key; - /** - * 支付通知回调地址 - */ - private String notifyUrl; - /** - * 退款回调地址 - */ - private String refundNotifyUrl; - - /** - * API 证书中的 key.pem - */ - private String keyPemPath; - - /** - * 商户序列号 - */ - private String serialNo; - - /** - * 微信支付V3-url前缀 - */ - private String baseUrl; - - - /** - * 获取商户的私钥文件 - * @param keyPemPath - * @return - */ - public PrivateKey getPrivateKey(String keyPemPath){ - - InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath); - if(inputStream==null){ - throw new RuntimeException("私钥文件不存在"); - } - return PemUtil.loadPrivateKey(inputStream); - } -// -// /** -// * 获取证书管理器实例 -// * @return -// */ -// @Bean -// public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException { -// -// log.info("获取证书管理器实例"); -// -// //获取商户私钥 -// PrivateKey privateKey = getPrivateKey(keyPemPath); -// -// //私钥签名对象 -// PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey); -// -// //身份认证对象 -// WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner); -// -// // 使用定时更新的签名验证器,不需要传入证书 -// CertificatesManager certificatesManager = CertificatesManager.getInstance(); -// certificatesManager.putMerchant(mchId,wechatPay2Credentials,apiV3Key.getBytes(StandardCharsets.UTF_8)); -// -// return certificatesManager.getVerifier(mchId); -// } - - - - @Bean - public Verifier getVerifier() throws GeneralSecurityException, IOException, HttpCodeException, NotFoundException { - log.info("开始获取微信支付证书管理器实例"); - - // 验证关键参数 - log.debug("商户号: {}", mchId); - log.debug("证书序列号: {}", serialNo); - log.debug("APIv3密钥长度: {}", apiV3Key.length()); // 不记录具体密钥内容 - - // 获取商户私钥 - PrivateKey privateKey = getPrivateKey(keyPemPath); - if (privateKey == null) { - throw new IllegalArgumentException("无法从路径加载私钥: " + keyPemPath); - } - - // 创建签名器 - PrivateKeySigner privateKeySigner = new PrivateKeySigner(serialNo, privateKey); - WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner); - - try { - CertificatesManager certificatesManager = CertificatesManager.getInstance(); - - // 显式设置域名(如果库需要) - // certificatesManager.setDomain("api.mch.weixin.qq.com"); - - // 添加商户信息 - certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8)); - - // 获取验证器 - Verifier verifier = certificatesManager.getVerifier(mchId); - log.info("成功获取证书管理器实例"); - return verifier; - } catch (HttpCodeException e) { - log.error("微信支付API返回错误: "+ e); - throw e; - } catch (Exception e) { - log.error("获取证书管理器实例时发生错误", e); - throw e; - } - } - - - /** - * 获取支付http请求对象 - * @param verifier - * @return - */ - @Bean(name = "wxPayClient") - public CloseableHttpClient getWxPayClient(Verifier verifier) { - - //获取商户私钥 - PrivateKey privateKey = getPrivateKey(keyPemPath); - - WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() - .withMerchant(mchId, serialNo, privateKey) - .withValidator(new WechatPay2Validator(verifier)); - - // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 - return builder.build(); - } - - /** - * 获取HttpClient,无需进行应答签名验证,跳过验签的流程 - */ - @Bean(name = "wxPayNoSignClient") - public CloseableHttpClient getWxPayNoSignClient(){ - - //获取商户私钥 - PrivateKey privateKey = getPrivateKey(keyPemPath); - - //用于构造HttpClient - WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create() - //设置商户信息 - .withMerchant(mchId, serialNo, privateKey) - //无需进行签名验证、通过withValidator((response) -> true)实现 - .withValidator((response) -> true); - - // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新 - return builder.build(); - } - - -} diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java index 1b286eb..c52cba9 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java @@ -76,6 +76,7 @@ public class ShiroConfig { filterChainDefinitionMap.put("/login/**", "anon"); filterChainDefinitionMap.put("/token/**", "anon"); filterChainDefinitionMap.put("/city/**", "anon"); + filterChainDefinitionMap.put("/cashout/**", "anon"); diff --git a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/cityMoneyLog/controller/CityMoneyLogController.java b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/cityMoneyLog/controller/CityMoneyLogController.java index c8ca0c2..2a9bb40 100644 --- a/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/cityMoneyLog/controller/CityMoneyLogController.java +++ b/jeecg-boot-base/jeecg-boot-base-core/src/main/java/org/jeecg/modules/cityMoneyLog/controller/CityMoneyLogController.java @@ -20,7 +20,6 @@ import org.jeecg.common.exception.JeecgBootException; import org.jeecg.common.system.query.QueryGenerator; import org.jeecg.common.util.RedisUtil; import org.jeecg.common.util.oConvertUtils; -import org.jeecg.config.pay.WeChatPayConfig2; import org.jeecg.modules.cityMoneyLog.entity.CityMoneyLog; import org.jeecg.modules.cityMoneyLog.req.TransferBatchesDetailsRequest; import org.jeecg.modules.cityMoneyLog.req.TransferBatchesRequest; @@ -68,8 +67,6 @@ public class CityMoneyLogController extends JeecgController edit(@RequestBody CityMoneyLog hanHaiWater) { - HanHaiMember hanHaiMember = hanHaiMemberService.getById(hanHaiWater.getUserId()); - Integer n = (Integer) redisUtil.get("WITHDRAWAL:" + hanHaiWater.getId()); - if (n != null) { - throw new JeecgBootException("请勿重复点击,后果自负!"); - } - redisUtil.set("WITHDRAWAL:" + hanHaiWater.getId(), 1, 5); - - //微信-商家转账到零钱 - - String idStr = "H" + IdWorker.getIdStr(); - TransferBatchesRequest transferBatchesRequest = new TransferBatchesRequest(); - - transferBatchesRequest.setAppid(appId); - transferBatchesRequest.setOutBillNo(idStr); - transferBatchesRequest.setTransferRemark("商家提现"); - transferBatchesRequest.setOpenid(hanHaiMember.getAppletOpenid()); - transferBatchesRequest.setTransferSceneId("1005"); - transferBatchesRequest.setNotifyUrl(withdrawalNotifyUrl); - String serialNo = null; - //加密真实姓名 - try { - - X509Certificate certificate = weChatPayConfig2.getVerifier().getValidCertificate(); - serialNo = certificate.getSerialNumber().toString(16).toUpperCase(); - //这里放真实姓名,目前你的数据库表没有这个字段 - String encryptOAEP = RsaCryptoUtil.encryptOAEP(hanHaiWater.getName(), certificate); - transferBatchesRequest.setUserName(encryptOAEP); - } catch (Exception e) { - log.info("真实姓名加密失败"); - e.printStackTrace(); - throw new JeecgBootException("真实姓名加密失败"); - } - transferBatchesRequest.setTransferAmount(MoneyUtil.Yuan2Fen(hanHaiWater.getPrice().doubleValue())); - List transferBatchesDetailsRequests = new ArrayList<>(); - TransferBatchesDetailsRequest transferBatchesDetailsRequest = new TransferBatchesDetailsRequest(); - transferBatchesDetailsRequest.setInfoType("岗位类型"); - transferBatchesDetailsRequest.setInfoContent("销售员"); - TransferBatchesDetailsRequest transferBatchesDetailsRequest2 = new TransferBatchesDetailsRequest(); - transferBatchesDetailsRequest2.setInfoType("报酬说明"); - transferBatchesDetailsRequest2.setInfoContent("佣金报酬"); - - - transferBatchesDetailsRequests.add(transferBatchesDetailsRequest); - transferBatchesDetailsRequests.add(transferBatchesDetailsRequest2); - transferBatchesRequest.setTransferDetailList(transferBatchesDetailsRequests); - String jsonString = JSONObject.toJSONString(transferBatchesRequest); - - log.info("请求参数:"+jsonString); - - String postTransBatRequest = HttpRequestUtil.postTransBatRequest(transferBatchUrl, jsonString, serialNo, wxsSerialNo, mchId, pemPath); - log.error("返回结果1:" + postTransBatRequest); - TransferBatchesResp transferBatchesResp = JSON.parseObject(postTransBatRequest, TransferBatchesResp.class); - log.error("返回结果2:" + transferBatchesResp); - if (transferBatchesResp == null || org.apache.commons.lang3.StringUtils.isBlank(transferBatchesResp.getOutBillNo()) || transferBatchesResp.getState().equals("FAIL")) { - throw new JeecgBootException("打款失败"); - } - - //这里写自己的逻辑,比如改变这条记录的状态,变成提现成功 - - CityMoneyLog hanHaiWater1 = new CityMoneyLog(); - hanHaiWater1.setId(hanHaiWater.getId()); - hanHaiWater1.setState(1); - hanHaiWater1.setPackageInfo(transferBatchesResp.getPackageInfo()); - hanHaiWater1.setOutBatchNo(transferBatchesResp.getOutBillNo()); - hanHaiWater1.setBatchId(transferBatchesResp.getTransferBillNo()); - cityMoneyLogService.updateById(hanHaiWater1); - - - return Result.OK("提现成功"); - } - +// +// /** +// * 编辑 +// * +// * @param hanHaiWater +// * @return +// */ +// @AutoLog(value = "佣金流水-编辑") +// @ApiOperation(value="佣金流水-编辑", notes="佣金流水-编辑") +// @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) +// public Result edit(@RequestBody CityMoneyLog hanHaiWater) { +// HanHaiMember hanHaiMember = hanHaiMemberService.getById(hanHaiWater.getUserId()); +// Integer n = (Integer) redisUtil.get("WITHDRAWAL:" + hanHaiWater.getId()); +// if (n != null) { +// throw new JeecgBootException("请勿重复点击,后果自负!"); +// } +// redisUtil.set("WITHDRAWAL:" + hanHaiWater.getId(), 1, 5); +// +// //微信-商家转账到零钱 +// +// String idStr = "H" + IdWorker.getIdStr(); +// TransferBatchesRequest transferBatchesRequest = new TransferBatchesRequest(); +// +// transferBatchesRequest.setAppid(appId); +// transferBatchesRequest.setOutBillNo(idStr); +// transferBatchesRequest.setTransferRemark("商家提现"); +// transferBatchesRequest.setOpenid(hanHaiMember.getAppletOpenid()); +// transferBatchesRequest.setTransferSceneId("1005"); +// transferBatchesRequest.setNotifyUrl(withdrawalNotifyUrl); +// String serialNo = null; +// //加密真实姓名 +// try { +// +// X509Certificate certificate = weChatPayConfig2.getVerifier().getValidCertificate(); +// serialNo = certificate.getSerialNumber().toString(16).toUpperCase(); +// //这里放真实姓名,目前你的数据库表没有这个字段 +// String encryptOAEP = RsaCryptoUtil.encryptOAEP(hanHaiWater.getName(), certificate); +// transferBatchesRequest.setUserName(encryptOAEP); +// } catch (Exception e) { +// log.info("真实姓名加密失败"); +// e.printStackTrace(); +// throw new JeecgBootException("真实姓名加密失败"); +// } +// transferBatchesRequest.setTransferAmount(MoneyUtil.Yuan2Fen(hanHaiWater.getPrice().doubleValue())); +// List transferBatchesDetailsRequests = new ArrayList<>(); +// TransferBatchesDetailsRequest transferBatchesDetailsRequest = new TransferBatchesDetailsRequest(); +// transferBatchesDetailsRequest.setInfoType("岗位类型"); +// transferBatchesDetailsRequest.setInfoContent("销售员"); +// TransferBatchesDetailsRequest transferBatchesDetailsRequest2 = new TransferBatchesDetailsRequest(); +// transferBatchesDetailsRequest2.setInfoType("报酬说明"); +// transferBatchesDetailsRequest2.setInfoContent("佣金报酬"); +// +// +// transferBatchesDetailsRequests.add(transferBatchesDetailsRequest); +// transferBatchesDetailsRequests.add(transferBatchesDetailsRequest2); +// transferBatchesRequest.setTransferDetailList(transferBatchesDetailsRequests); +// String jsonString = JSONObject.toJSONString(transferBatchesRequest); +// +// log.info("请求参数:"+jsonString); +// +// String postTransBatRequest = HttpRequestUtil.postTransBatRequest(transferBatchUrl, jsonString, serialNo, wxsSerialNo, mchId, pemPath); +// log.error("返回结果1:" + postTransBatRequest); +// TransferBatchesResp transferBatchesResp = JSON.parseObject(postTransBatRequest, TransferBatchesResp.class); +// log.error("返回结果2:" + transferBatchesResp); +// if (transferBatchesResp == null || org.apache.commons.lang3.StringUtils.isBlank(transferBatchesResp.getOutBillNo()) || transferBatchesResp.getState().equals("FAIL")) { +// throw new JeecgBootException("打款失败"); +// } +// +// //这里写自己的逻辑,比如改变这条记录的状态,变成提现成功 +// +// CityMoneyLog hanHaiWater1 = new CityMoneyLog(); +// hanHaiWater1.setId(hanHaiWater.getId()); +// hanHaiWater1.setState(1); +// hanHaiWater1.setPackageInfo(transferBatchesResp.getPackageInfo()); +// hanHaiWater1.setOutBatchNo(transferBatchesResp.getOutBillNo()); +// hanHaiWater1.setBatchId(transferBatchesResp.getTransferBillNo()); +// cityMoneyLogService.updateById(hanHaiWater1); +// +// +// return Result.OK("提现成功"); +// } +// /** * 通过id删除 * diff --git a/jeecg-boot-module-system/pom.xml b/jeecg-boot-module-system/pom.xml index 22295d6..635de10 100644 --- a/jeecg-boot-module-system/pom.xml +++ b/jeecg-boot-module-system/pom.xml @@ -82,6 +82,7 @@ + yaodu-api org.springframework.boot diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/CashoutService.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/CashoutService.java new file mode 100644 index 0000000..f6e8903 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/CashoutService.java @@ -0,0 +1,20 @@ +package org.jeecg.modules.api.service; + +import io.swagger.annotations.ApiOperation; +import org.jeecg.common.api.vo.Result; +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.RequestMethod; + +public interface CashoutService { + //会员中心-开通会员 + @ApiOperation(value="测试提现-提现", notes="测试提现-提现") + @RequestMapping(value = "/cashout", method = {RequestMethod.POST}) + public Result cashout(); + + //开通会员支付回调 + //支付回调 + @PostMapping("/cashoutNotify") + public Object cashoutNotify(@RequestBody String requestBody); +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/impl/CashoutServiceImpl.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/impl/CashoutServiceImpl.java new file mode 100644 index 0000000..26fae4c --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/service/impl/CashoutServiceImpl.java @@ -0,0 +1,29 @@ +package org.jeecg.modules.api.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.api.service.CashoutService; +import org.jeecg.modules.transferTest.TransferToUser; +import org.springframework.stereotype.Service; + +@Slf4j +@Service +public class CashoutServiceImpl implements CashoutService { + @Override + public Result cashout() { + String mchid = "1673516176"; + String certiticateSerialNo ="525CDBD76E640EFB008288572C97D2715F3F18B2"; + String privateKeyFilePath = "jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem"; + String wechatPayPublicKeyId = "PUB_KEY_ID_0116735161762025040100448900000949"; + String wechatPayPublicKeyFilePaht = "jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem"; + TransferToUser transferToUser = new TransferToUser(mchid, certiticateSerialNo, privateKeyFilePath, wechatPayPublicKeyId, wechatPayPublicKeyFilePaht ); + transferToUser.run(); + return Result.OK("测试提现结束"); + } + + @Override + public Object cashoutNotify(String requestBody) { + System.out.println("测试提现回调"); + return Result.OK("测试回调"); + } +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/yaoduapi/CashoutController.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/yaoduapi/CashoutController.java new file mode 100644 index 0000000..4b55af1 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/api/yaoduapi/CashoutController.java @@ -0,0 +1,35 @@ +package org.jeecg.modules.api.yaoduapi; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.vo.Result; +import org.jeecg.modules.api.service.CashoutService; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +@Api(tags="测试提现接口-提现") +@RestController +@RequestMapping("/cashout") +@Slf4j +public class CashoutController { + /******************************************************************************************************************/ + //会员信息 + @Resource + private CashoutService cashoutService; + /******************************************************************************************************************/ + //会员中心-开通会员 + @ApiOperation(value="测试提现-提现", notes="测试提现-提现") + @RequestMapping(value = "/cashout", method = {RequestMethod.POST}) + public Result cashout(){ + return cashoutService.cashout(); + } + + //提现回调 + @PostMapping("/cashoutNotify") + public Object cashoutNotify(@RequestBody String requestBody){ + return cashoutService.cashoutNotify(requestBody); + } + +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/TransferToUser.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/TransferToUser.java new file mode 100644 index 0000000..aba921d --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/TransferToUser.java @@ -0,0 +1,257 @@ +package org.jeecg.modules.transferTest; + +import com.google.gson.annotations.SerializedName; +import okhttp3.*; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.util.ArrayList; +import java.util.List; + +/** + * 发起转账 + */ +public class TransferToUser { + private static String HOST = "https://api.mch.weixin.qq.com"; + private static String METHOD = "POST"; + private static String PATH = "/v3/fund-app/mch-transfer/transfer-bills"; + + public static void main(String[] args) { + // TODO: 请准备商户开发必要参数,参考:https://pay.weixin.qq.com/doc/v3/merchant/4013070756 +// String mchid = "1712378227"; +// String certiticateSerialNo ="33E9FE8076531A7C7AD401DC34E053DBD7C28E22"; +// String privateKeyFilePath = "jeecg-boot-module-system/src/main/resources/apiclient_key.pem"; +// String wechatPayPublicKeyId = "PUB_KEY_ID_0117123782272025033100396400002931"; +// String wechatPayPublicKeyFilePaht = "jeecg-boot-module-system/src/main/resources/pub_key.pem"; + String mchid = "1673516176"; + String certiticateSerialNo ="525CDBD76E640EFB008288572C97D2715F3F18B2"; + String privateKeyFilePath = "jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem"; + String wechatPayPublicKeyId = "PUB_KEY_ID_0116735161762025040100448900000949"; + String wechatPayPublicKeyFilePaht = "jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem"; + TransferToUser client = new TransferToUser( + mchid, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 + certiticateSerialNo, // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 + privateKeyFilePath, // 商户API证书私钥文件路径,本地文件路径 + wechatPayPublicKeyId, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 + wechatPayPublicKeyFilePaht // 微信支付公钥文件路径,本地文件路径 + ); + + String appid = "wxa4d29e67e8a58d38"; + String openid = "oFzrW4migndUepy7zYgYO2YoZ5to"; + String notifyUrl = "https://admin.hhlm1688.com/api/hello/"; + TransferToUserRequest request = new TransferToUserRequest(); + request.appid = appid; + request.outBillNo = "plfk2020042013"; + request.transferSceneId = "1000"; + request.openid = openid; + request.userName = client.encrypt("唐斌"); + request.transferAmount = 400L; + request.transferRemark = "新会员开通有礼"; + request.notifyUrl = notifyUrl; + request.userRecvPerception = "现金奖励"; + request.transferSceneReportInfos = new ArrayList<>(); + { + TransferSceneReportInfo item0 = new TransferSceneReportInfo(); + item0.infoType = "活动名称"; + item0.infoContent = "新会员有礼"; + request.transferSceneReportInfos.add(item0); + TransferSceneReportInfo item1 = new TransferSceneReportInfo(); + item1.infoType = "奖励说明"; + item1.infoContent = "注册会员抽奖一等奖"; + request.transferSceneReportInfos.add(item1); + + + + }; + try { + TransferToUserResponse response = client.run(request); + + // TODO: 请求成功,继续业务逻辑 + System.out.println(response); + } catch (WXPayUtility.ApiException e) { + // TODO: 请求失败,根据状态码执行不同的逻辑 + e.printStackTrace(); + } + } + + public void run(){ + String mchid = "1673516176"; + String certiticateSerialNo ="525CDBD76E640EFB008288572C97D2715F3F18B2"; + String privateKeyFilePath = "jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem"; + String wechatPayPublicKeyId = "PUB_KEY_ID_0116735161762025040100448900000949"; + String wechatPayPublicKeyFilePaht = "jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem"; + TransferToUser client = new TransferToUser( + mchid, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 + certiticateSerialNo, // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 + privateKeyFilePath, // 商户API证书私钥文件路径,本地文件路径 + wechatPayPublicKeyId, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 + wechatPayPublicKeyFilePaht // 微信支付公钥文件路径,本地文件路径 + ); + + String appid = "wxa4d29e67e8a58d38"; + String openid = "oFzrW4migndUepy7zYgYO2YoZ5to"; + String notifyUrl = "https://admin.hhlm1688.com/api/cashout/cashout/"; + TransferToUserRequest request = new TransferToUserRequest(); + request.appid = appid; + request.outBillNo = "plfk2020042013"; + request.transferSceneId = "1000"; + request.openid = openid; + request.userName = client.encrypt("唐斌"); + request.transferAmount = 400L; + request.transferRemark = "新会员开通有礼"; + request.notifyUrl = notifyUrl; + request.userRecvPerception = "现金奖励"; + request.transferSceneReportInfos = new ArrayList<>(); + { + TransferSceneReportInfo item0 = new TransferSceneReportInfo(); + item0.infoType = "活动名称"; + item0.infoContent = "新会员有礼"; + request.transferSceneReportInfos.add(item0); + TransferSceneReportInfo item1 = new TransferSceneReportInfo(); + item1.infoType = "奖励说明"; + item1.infoContent = "注册会员抽奖一等奖"; + request.transferSceneReportInfos.add(item1); + + + + }; + try { + TransferToUserResponse response = client.run(request); + + // TODO: 请求成功,继续业务逻辑 + System.out.println(response); + } catch (WXPayUtility.ApiException e) { + // TODO: 请求失败,根据状态码执行不同的逻辑 + e.printStackTrace(); + } + } + + public TransferToUserResponse run(TransferToUserRequest request) { + String uri = PATH; + String reqBody = WXPayUtility.toJson(request); + + Request.Builder reqBuilder = new Request.Builder().url(HOST + uri); + reqBuilder.addHeader("Accept", "application/json"); + reqBuilder.addHeader("Wechatpay-Serial", wechatPayPublicKeyId); + reqBuilder.addHeader("Authorization", WXPayUtility.buildAuthorization(mchid, certificateSerialNo,privateKey, METHOD, uri, reqBody)); + reqBuilder.addHeader("Content-Type", "application/json"); + RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), reqBody); + reqBuilder.method(METHOD, requestBody); + Request httpRequest = reqBuilder.build(); + + // 发送HTTP请求 + OkHttpClient client = new OkHttpClient.Builder().build(); + try (Response httpResponse = client.newCall(httpRequest).execute()) { + String respBody = WXPayUtility.extractBody(httpResponse); + if (httpResponse.code() >= 200 && httpResponse.code() < 300) { + // 2XX 成功,验证应答签名 + WXPayUtility.validateResponse(this.wechatPayPublicKeyId, this.wechatPayPublicKey, + httpResponse.headers(), respBody); + + // 从HTTP应答报文构建返回数据 + return WXPayUtility.fromJson(respBody, TransferToUserResponse.class); + } else { + throw new WXPayUtility.ApiException(httpResponse.code(), respBody, httpResponse.headers()); + } + } catch (IOException e) { + throw new UncheckedIOException("Sending request to " + uri + " failed.", e); + } + } + + private final String mchid; + private final String certificateSerialNo; + private final PrivateKey privateKey; + private final String wechatPayPublicKeyId; + private final PublicKey wechatPayPublicKey; + + public TransferToUser(String mchid, String certificateSerialNo, String privateKeyFilePath, String wechatPayPublicKeyId, String wechatPayPublicKeyFilePath) { + this.mchid = mchid; + this.certificateSerialNo = certificateSerialNo; + this.privateKey = WXPayUtility.loadPrivateKeyFromPath(privateKeyFilePath); + this.wechatPayPublicKeyId = wechatPayPublicKeyId; + this.wechatPayPublicKey = WXPayUtility.loadPublicKeyFromPath(wechatPayPublicKeyFilePath); + } + + public String encrypt(String plainText) { + return WXPayUtility.encrypt(this.wechatPayPublicKey, plainText); + } + + public static class TransferToUserResponse { + @SerializedName("out_bill_no") + public String outBillNo; + + @SerializedName("transfer_bill_no") + public String transferBillNo; + + @SerializedName("create_time") + public String createTime; + + @SerializedName("state") + public TransferBillStatus state; + + @SerializedName("package_info") + public String packageInfo; + } + + public enum TransferBillStatus { + @SerializedName("ACCEPTED") + ACCEPTED, + @SerializedName("PROCESSING") + PROCESSING, + @SerializedName("WAIT_USER_CONFIRM") + WAIT_USER_CONFIRM, + @SerializedName("TRANSFERING") + TRANSFERING, + @SerializedName("SUCCESS") + SUCCESS, + @SerializedName("FAIL") + FAIL, + @SerializedName("CANCELING") + CANCELING, + @SerializedName("CANCELLED") + CANCELLED + } + + public static class TransferSceneReportInfo { + @SerializedName("info_type") + public String infoType; + + @SerializedName("info_content") + public String infoContent; + } + + public static class TransferToUserRequest { + @SerializedName("appid") + public String appid; + + @SerializedName("out_bill_no") + public String outBillNo; + + @SerializedName("transfer_scene_id") + public String transferSceneId; + + @SerializedName("openid") + public String openid; + + @SerializedName("user_name") + public String userName; + + @SerializedName("transfer_amount") + public Long transferAmount; + + @SerializedName("transfer_remark") + public String transferRemark; + + @SerializedName("notify_url") + public String notifyUrl; + + @SerializedName("user_recv_perception") + public String userRecvPerception; + + @SerializedName("transfer_scene_report_infos") + public List transferSceneReportInfos; + } + +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/WXPayUtility.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/WXPayUtility.java new file mode 100644 index 0000000..30c5374 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/WXPayUtility.java @@ -0,0 +1,381 @@ +package org.jeecg.modules.transferTest; + +import com.google.gson.*; +import com.google.gson.annotations.Expose; +import okhttp3.Headers; +import okhttp3.Response; +import okio.BufferedSource; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.time.DateTimeException; +import java.time.Duration; +import java.time.Instant; +import java.util.Base64; +import java.util.Map; +import java.util.Objects; + +public class WXPayUtility { + private static final Gson gson = new GsonBuilder() + .disableHtmlEscaping() + .addSerializationExclusionStrategy(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes fieldAttributes) { + final Expose expose = fieldAttributes.getAnnotation(Expose.class); + return expose != null && !expose.serialize(); + } + + @Override + public boolean shouldSkipClass(Class aClass) { + return false; + } + }) + .addDeserializationExclusionStrategy(new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes fieldAttributes) { + final Expose expose = fieldAttributes.getAnnotation(Expose.class); + return expose != null && !expose.deserialize(); + } + + @Override + public boolean shouldSkipClass(Class aClass) { + return false; + } + }) + .create(); + private static final char[] SYMBOLS = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray(); + private static final SecureRandom random = new SecureRandom(); + + /** + * 将 Object 转换为 JSON 字符串 + */ + public static String toJson(Object object) { + return gson.toJson(object); + } + + /** + * 将 JSON 字符串解析为特定类型的实例 + */ + public static T fromJson(String json, Class classOfT) throws JsonSyntaxException { + return gson.fromJson(json, classOfT); + } + + /** + * 从公私钥文件路径中读取文件内容 + * + * @param keyPath 文件路径 + * @return 文件内容 + */ + private static String readKeyStringFromPath(String keyPath) { + try { + return new String(Files.readAllBytes(Paths.get(keyPath)), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * 读取 PKCS#8 格式的私钥字符串并加载为私钥对象 + * + * @param keyString 私钥文件内容,以 -----BEGIN PRIVATE KEY----- 开头 + * @return PrivateKey 对象 + */ + public static PrivateKey loadPrivateKeyFromString(String keyString) { + try { + keyString = keyString.replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + return KeyFactory.getInstance("RSA").generatePrivate( + new PKCS8EncodedKeySpec(Base64.getDecoder().decode(keyString))); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException(e); + } catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * 从 PKCS#8 格式的私钥文件中加载私钥 + * + * @param keyPath 私钥文件路径 + * @return PrivateKey 对象 + */ + public static PrivateKey loadPrivateKeyFromPath(String keyPath) { + return loadPrivateKeyFromString(readKeyStringFromPath(keyPath)); + } + + /** + * 读取 PKCS#8 格式的公钥字符串并加载为公钥对象 + * + * @param keyString 公钥文件内容,以 -----BEGIN PUBLIC KEY----- 开头 + * @return PublicKey 对象 + */ + public static PublicKey loadPublicKeyFromString(String keyString) { + try { + keyString = keyString.replace("-----BEGIN PUBLIC KEY-----", "") + .replace("-----END PUBLIC KEY-----", "") + .replaceAll("\\s+", ""); + return KeyFactory.getInstance("RSA").generatePublic( + new X509EncodedKeySpec(Base64.getDecoder().decode(keyString))); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException(e); + } catch (InvalidKeySpecException e) { + throw new IllegalArgumentException(e); + } + } + + /** + * 从 PKCS#8 格式的公钥文件中加载公钥 + * + * @param keyPath 公钥文件路径 + * @return PublicKey 对象 + */ + public static PublicKey loadPublicKeyFromPath(String keyPath) { + return loadPublicKeyFromString(readKeyStringFromPath(keyPath)); + } + + /** + * 创建指定长度的随机字符串,字符集为[0-9a-zA-Z],可用于安全相关用途 + */ + public static String createNonce(int length) { + char[] buf = new char[length]; + for (int i = 0; i < length; ++i) { + buf[i] = SYMBOLS[random.nextInt(SYMBOLS.length)]; + } + return new String(buf); + } + + /** + * 使用公钥按照 RSA_PKCS1_OAEP_PADDING 算法进行加密 + * + * @param publicKey 加密用公钥对象 + * @param plaintext 待加密明文 + * @return 加密后密文 + */ + public static String encrypt(PublicKey publicKey, String plaintext) { + final String transformation = "RSA/ECB/OAEPWithSHA-1AndMGF1Padding"; + + try { + Cipher cipher = Cipher.getInstance(transformation); + cipher.init(Cipher.ENCRYPT_MODE, publicKey); + return Base64.getEncoder().encodeToString(cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8))); + } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { + throw new IllegalArgumentException("The current Java environment does not support " + transformation, e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("RSA encryption using an illegal publicKey", e); + } catch (BadPaddingException | IllegalBlockSizeException e) { + throw new IllegalArgumentException("Plaintext is too long", e); + } + } + + /** + * 使用私钥按照指定算法进行签名 + * + * @param message 待签名串 + * @param algorithm 签名算法,如 SHA256withRSA + * @param privateKey 签名用私钥对象 + * @return 签名结果 + */ + public static String sign(String message, String algorithm, PrivateKey privateKey) { + byte[] sign; + try { + Signature signature = Signature.getInstance(algorithm); + signature.initSign(privateKey); + signature.update(message.getBytes(StandardCharsets.UTF_8)); + sign = signature.sign(); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException("The current Java environment does not support " + algorithm, e); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException(algorithm + " signature uses an illegal privateKey.", e); + } catch (SignatureException e) { + throw new RuntimeException("An error occurred during the sign process.", e); + } + return Base64.getEncoder().encodeToString(sign); + } + + /** + * 使用公钥按照特定算法验证签名 + * + * @param message 待签名串 + * @param signature 待验证的签名内容 + * @param algorithm 签名算法,如:SHA256withRSA + * @param publicKey 验签用公钥对象 + * @return 签名验证是否通过 + */ + public static boolean verify(String message, String signature, String algorithm, + PublicKey publicKey) { + try { + Signature sign = Signature.getInstance(algorithm); + sign.initVerify(publicKey); + sign.update(message.getBytes(StandardCharsets.UTF_8)); + return sign.verify(Base64.getDecoder().decode(signature)); + } catch (SignatureException e) { + return false; + } catch (InvalidKeyException e) { + throw new IllegalArgumentException("verify uses an illegal publickey.", e); + } catch (NoSuchAlgorithmException e) { + throw new UnsupportedOperationException("The current Java environment does not support" + algorithm, e); + } + } + + /** + * 根据微信支付APIv3请求签名规则构造 Authorization 签名 + * + * @param mchid 商户号 + * @param certificateSerialNo 商户API证书序列号 + * @param privateKey 商户API证书私钥 + * @param method 请求接口的HTTP方法,请使用全大写表述,如 GET、POST、PUT、DELETE + * @param uri 请求接口的URL + * @param body 请求接口的Body + * @return 构造好的微信支付APIv3 Authorization 头 + */ + public static String buildAuthorization(String mchid, String certificateSerialNo, + PrivateKey privateKey, + String method, String uri, String body) { + String nonce = createNonce(32); + long timestamp = Instant.now().getEpochSecond(); + + String message = String.format("%s\n%s\n%d\n%s\n%s\n", method, uri, timestamp, nonce, + body == null ? "" : body); + + String signature = sign(message, "SHA256withRSA", privateKey); + + return String.format( + "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",signature=\"%s\"," + + "timestamp=\"%d\",serial_no=\"%s\"", + mchid, nonce, signature, timestamp, certificateSerialNo); + } + + /** + * 对参数进行 URL 编码 + * + * @param content 参数内容 + * @return 编码后的内容 + */ + public static String urlEncode(String content) { + try { + return URLEncoder.encode(content, StandardCharsets.UTF_8.name()); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * 对参数Map进行 URL 编码,生成 QueryString + * + * @param params Query参数Map + * @return QueryString + */ + public static String urlEncode(Map params) { + if (params == null || params.isEmpty()) { + return ""; + } + + int index = 0; + StringBuilder result = new StringBuilder(); + for (Map.Entry entry : params.entrySet()) { + result.append(entry.getKey()) + .append("=") + .append(urlEncode(entry.getValue().toString())); + index++; + if (index < params.size()) { + result.append("&"); + } + } + return result.toString(); + } + + /** + * 从应答中提取 Body + * + * @param response HTTP 请求应答对象 + * @return 应答中的Body内容,Body为空时返回空字符串 + */ + public static String extractBody(Response response) { + if (response.body() == null) { + return ""; + } + + try { + BufferedSource source = response.body().source(); + return source.readUtf8(); + } catch (IOException e) { + throw new RuntimeException(String.format("An error occurred during reading response body. Status: %d", response.code()), e); + } + } + + /** + * 根据微信支付APIv3应答验签规则对应答签名进行验证,验证不通过时抛出异常 + * + * @param wechatpayPublicKeyId 微信支付公钥ID + * @param wechatpayPublicKey 微信支付公钥对象 + * @param headers 微信支付应答 Header 列表 + * @param body 微信支付应答 Body + */ + public static void validateResponse(String wechatpayPublicKeyId, PublicKey wechatpayPublicKey, + Headers headers, + String body) { + String timestamp = headers.get("Wechatpay-Timestamp"); + try { + Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestamp)); + // 拒绝过期请求 + if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= 5) { + throw new IllegalArgumentException( + String.format("Validate http response,timestamp[%s] of httpResponse is expires, " + + "request-id[%s]", + timestamp, headers.get("Request-ID"))); + } + } catch (DateTimeException | NumberFormatException e) { + throw new IllegalArgumentException( + String.format("Validate http response,timestamp[%s] of httpResponse is invalid, " + + "request-id[%s]", timestamp, + headers.get("Request-ID"))); + } + String message = String.format("%s\n%s\n%s\n", timestamp, headers.get("Wechatpay-Nonce"), + body == null ? "" : body); + String serialNumber = headers.get("Wechatpay-Serial"); + if (!Objects.equals(serialNumber, wechatpayPublicKeyId)) { + throw new IllegalArgumentException( + String.format("Invalid Wechatpay-Serial, Local: %s, Remote: %s", wechatpayPublicKeyId, + serialNumber)); + } + String signature = headers.get("Wechatpay-Signature"); + + boolean success = verify(message, signature, "SHA256withRSA", wechatpayPublicKey); + if (!success) { + throw new IllegalArgumentException( + String.format("Validate response failed,the WechatPay signature is incorrect.%n" + + "Request-ID[%s]\tresponseHeader[%s]\tresponseBody[%.1024s]", + headers.get("Request-ID"), headers, body)); + } + } + + /** + * 微信支付API错误异常,发送HTTP请求成功,但返回状态码不是 2XX 时抛出本异常 + */ + public static class ApiException extends RuntimeException { + public final int statusCode; + public final String body; + public final Headers headers; + public ApiException(int statusCode, String body, Headers headers) { + super(String.format("微信支付API访问失败,StatusCode: %s, Body: %s", statusCode, body)); + this.statusCode = statusCode; + this.body = body; + this.headers = headers; + } + } +} diff --git a/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/test.java b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/test.java new file mode 100644 index 0000000..8dcae59 --- /dev/null +++ b/jeecg-boot-module-system/src/main/java/org/jeecg/modules/transferTest/test.java @@ -0,0 +1,13 @@ +package org.jeecg.modules.transferTest; + +public class test { + public static void main(String[] args) { + String mchid = "1673516176"; + String certiticateSerialNo ="525CDBD76E640EFB008288572C97D2715F3F18B2"; + String privateKeyFilePath = "jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem"; + String wechatPayPublicKeyId = "PUB_KEY_ID_0116735161762025040100448900000949"; + String wechatPayPublicKeyFilePaht = "jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem"; + TransferToUser transferToUser = new TransferToUser(mchid, certiticateSerialNo, privateKeyFilePath, wechatPayPublicKeyId, wechatPayPublicKeyFilePaht ); + transferToUser.run(); + } +} diff --git a/jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem b/jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem new file mode 100644 index 0000000..034d3f3 --- /dev/null +++ b/jeecg-boot-module-system/src/main/resources/apiclient_key_yaodu.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDFHyPaZuXY6GCd +mC/CId2jMQ3Iun2cZycw2LxdWLNwtKGYKVk+GWa4upm60Zl+qrNpLam3qTD5Oyy3 +T8Mz9EWHwBYr2xTSKINRbIjabdVyV0/H2RLitwRg3XyHQPxYFcvI9t9F2GHELsAA +qGmFp0+PiCVMzT+nB3sU+yLaNyiMC8rcprjh4tBV857K3Y5rzljbJsxa9WVzS0EM +GQRwPV2LaWPApcGZIKJLRAUa8yGMKMINyb9iJjdA1/4vD8tLWmvFhXtTYmrWmFhu +5QsyKDClOek2gqGgxDL9Hvvx5PYR59Mg57kyz0vSKhYa0MKgsFLuu7QJOFXk2bJo +DJymcLypAgMBAAECggEBAL+1626riHsOdXiP3FLYEPB38sn35dZI1GrDP18ht1Kz +uj18aVjl52tdv8lbtAbnCZoPWPJQUFr0XCbkIhrTRRQjkuyQI43I7P4xql+VVnPf +yq24xo9MI6v5fPUmFMWuXQVUZA1PxrXAKef54raj49LaPDyXmYJe2iurm1fTMVIR +Jba1rBi0hp5W1fMCiCPk1SMfANTE0UOTYdSRhNVSgf51aJHFI+FR8iSmf8rEv+B0 +s8uDXvxTGgbEaZsO7wcjQfsbQixoqEmfTzEoBTHkQ1npigG32gsz8QO6P4dhJRX7 +WJ3N8x0vgJqxCLeRLEb6c8ZJ62SuEi6PtETVWXujeCECgYEA/gP8fyp5lBKF/weC +UzMCEAJzwTKjmC0IOWnoIwQTSKZTCKHmz5LMNYn3nTf2d8LpbB9GYmBhUdiO/88f +AZ/vbuskkoH0rmOgelVx13tDaVuNOOVmtKmKvzYOxiGeL6ooX2cwfRnVOMIPTBFM +G1lAzxeowTy5uLKvuuMGBVJj7MUCgYEAxqleqjcp3+4QoE9Z/2hEt6sYnI/I1T/h +kbgTO8wQSGG8ambl/qJu+9ON/Ag1jK2amrNKP1vXbTQsjkUby6U5MgVwGDB4bNN1 ++wgwxMXJ74QJtbnXp7XBqC96S6Pyw+cLEjvWk0xJ14Os2oCO1OvZibu5KHrsPECn +3OmddJkjFpUCgYEAoiwHW2TRxCBjXiP8J4QMUA5QusrKuVAezRD5fMmQSjSuFHfQ +9Tsilxfjd4OQHnvZLQd2lz4zQ96/xUAF6rKiWa1UZxkDDwdaIGBG0yzGKBCkQ+vp +u3P2ugcYPZSe+o1nQymNQoFoqNj0jTsJ3PgJsW3Idr5/UBT8rpNcd69XToUCgYB8 +AQTCIyTUTnm6V03KC3+5VedK8sVdtz5KAyieTsZrJ/bAQ/KUezfjoS4jf8xNP6Ad +qIRUADP8SnD1bVXoS/3jp1lNABRreaNPStGGQh/GjhixgouGeAGlxd0EkhXbCsDy +ZL+PujLtf5fJ3C1L4twrCS6OggwroAAn+Pr76Qrp8QKBgQD9uH47NRToKkaTPflY +mVlbLKCWnAjoqlxHrANjM6lOeC4SfAYaZRIpCK9B7LFR7eeqIb0Y/RjfwtLdFKQF +1hVCnLYTsEJ5gG64qqInVb4e0FCF/2w1uoBGwB2pUWjd0DUXH3yHc3LVCBhxOkRa +oULtLnG34V6fFclMh3Stk/pMVg== +-----END PRIVATE KEY----- diff --git a/jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem b/jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem new file mode 100644 index 0000000..abafafe --- /dev/null +++ b/jeecg-boot-module-system/src/main/resources/pub_key_yaodu.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxK2wqQooeGKkAQchZdwK +U3qFiPSyf83l7USGGAdEhb78iq0Dvi7RfgdHuqSVBv0fnSQJicNO+s10ofi9waxl +SCPnuO8t9MeOuS4IpZ54VWY/9pJ5A2Z0x49L0djoFWStFCpKzsg2fWBvc/7kYVFr +nq/jFyRho8/GZtxL9RLZjWLyfnpe+erxSNFEnQLoW6LC4D5L0w9oiHboHmN9Igzc +uB6pIuMwccImX1xPeu/jx2QMYrxAW/2bW3e6z4ojQWqhRtFq55INnXLV8VXE7rwI +1L0RB4R39JOsKroVE/g7SiHRbGEem2BbNaAFhjCMuZzpWSCFQkTlxLJMxxTt9j0S ++wIDAQAB +-----END PUBLIC KEY-----