引入官方sdk

<!--微信v3支付sdk {https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient}-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>

新建配置类

/**
* @Description : 微信支付配置
* @Author : wzkris
* @Version : V1.0.0
* @Date : 2022/11/27 15:41
*/
@ConfigurationProperties("wx.pay")
@Component
@Data
public class WxPayConfig {
/** appid*/
public String appId;
/** appSecret*/
private String appSecret;
/** 商户号 */
public String merchantId;
/** 商户API私钥路径 */
public String privateKeyPath;
/**商户API私钥*/
public PrivateKey privateKey;
/** 商户证书序列号 */
public String merchantSerialNumber;
/**v3密钥*/
public String apiV3key;
}

新建service

/**
* @Description : 微信v3支付服务
* @Author : wzkris
* @Version : V1.0.0
* @Date : 2022/11/27 15:48
* @Description : 微信支付服务,金额一律为分;对账单中的交易金额单位为元
*/
@Component
public class WxPayService { @Resource
private WxPayConfig wxconfig; @Resource
private GtConfig gtConfig; /**
* 请求客户端
*/
private static CloseableHttpClient httpClient; /**
* 微信平台证书验证器
*/
private static Verifier verifier; @PostConstruct
public void init() {
try {
String privateKey = FileUtils.readFileToString(wxconfig.privateKeyPath);
wxconfig.setPrivateKey(PemUtil.loadPrivateKey(privateKey));
//初始化微信平台证书容器,certificatesManager可以放多个商户
CertificatesManager certificatesManager = CertificatesManager.getInstance();
certificatesManager.putMerchant(
wxconfig.merchantId,
new WechatPay2Credentials(wxconfig.merchantId, new PrivateKeySigner(wxconfig.merchantSerialNumber, wxconfig.privateKey)),
wxconfig.getApiV3key().getBytes(StandardCharsets.UTF_8)
);
verifier = certificatesManager.getVerifier(wxconfig.merchantId);
//初始化httpClient
httpClient = WechatPayHttpClientBuilder
.create()
.withMerchant(wxconfig.merchantId, wxconfig.merchantSerialNumber, PemUtil.loadPrivateKey(privateKey))
.withValidator(new WechatPay2Validator(verifier))
.build();
} catch (Exception e) {
e.printStackTrace();
} } /**
* 添加子商户进件申请
*/
public JSONObject addSubMerchant(SubMerchantApplyment applyment) {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.addHeader("Wechatpay-Serial", String.valueOf(verifier.getValidCertificate().getSerialNumber()));
//敏感信息加密
encryptSubMerchantInfo(applyment); httpPost.setEntity(new StringEntity(JSONObject.toJSONString(applyment), StandardCharsets.UTF_8));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
JSONObject jsonObject = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(jsonObject);
}
return jsonObject;
} catch (IOException e) {
throw new RuntimeException(e);
}
} /**
* 进件敏感信息加密
*
* @param applyment 特约商户进件
*/
public void encryptSubMerchantInfo(SubMerchantApplyment applyment) {
//1.超级管理员信息加密
ContactInfo contactInfo = applyment.getContactInfo();
contactInfo.setContactName(encrypt(contactInfo.getContactName()))
.setOpenId(encrypt(contactInfo.getOpenId()))
.setMobilePhone(encrypt(contactInfo.getMobilePhone()))
.setContactEmail(encrypt(contactInfo.getContactEmail()));
//1.1经办人类型
if (contactInfo.getContactType().equals(ContactInfo.contactType.SUPER.toString())) {
contactInfo.setContactIdNumber(encrypt(contactInfo.getContactIdNumber()));
}
//2.主体资料
SubjectInfo subjectInfo = applyment.getSubjectInfo();
//2.1受益人信息加密
if (!subjectInfo.getUboInfoList().isEmpty()) {
//每个受益人信息都加密
for (UboInfo uboInfo : subjectInfo.getUboInfoList()) {
uboInfo.setUboIdDocName(encrypt(uboInfo.getUboIdDocName()))
.setUboIdDocNumber(encrypt(uboInfo.getUboIdDocNumber()))
.setUboIdDocAddress(encrypt(uboInfo.getUboIdDocAddress()));
}
}
//3.结算银行账户
BankAccountInfo bankAccountInfo = applyment.getBankAccountInfo();
bankAccountInfo.setAccountName(encrypt(bankAccountInfo.getAccountName()))
.setAccountNumber(encrypt(bankAccountInfo.getAccountNumber()));
} /**
* 查看申请单状态
*
* @param businessCode 业务申请编号
*/
public ApplymentResult searchApplymentStatus(String businessCode) {
HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/applyment4sub/applyment/business_code/" + businessCode);
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(JSONObject.parseObject(EntityUtils.toString(response.getEntity())));
}
return JSONObject.parseObject(EntityUtils.toString(response.getEntity()), ApplymentResult.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
} /**
* @param bo 支付参数
* @param enums 支付渠道
* @return 返回参数
* @Description 服务商付款
*/
public JSONObject pay(WxPayParamsBO bo, PayEnums enums) {
//1.组装参数
JSONObject requestParams = new JSONObject();
requestParams.put("sp_appid", wxconfig.getAppId());
requestParams.put("sp_mchid", wxconfig.getMerchantId());
//子商户appid
requestParams.put("sub_appid", bo.getSubAppId());
//子商户号
requestParams.put("sub_mchid", bo.getSubMchId());
requestParams.put("description", bo.getDescription());
requestParams.put("out_trade_no", bo.getOrderNo());
requestParams.put("notify_url", gtConfig.getUrl() + bo.getNotifyUrl());
JSONObject amount = new JSONObject();
amount.put("total", bo.getAmount());
requestParams.put("amount", amount);
JSONObject payer = new JSONObject();
if (enums.equals(PayEnums.JSAPI)) {
payer.put("sp_openid", bo.getOpenId());
requestParams.put("payer", payer);
} else if (enums.equals(PayEnums.H5)) {
JSONObject sceneInfo = new JSONObject();
//拿到HttpServletRequest解析ip
HttpServletRequest request = ServletUtils.getRequest();
sceneInfo.put("payer_client_ip", IpUtils.getIpAddr(request));
JSONObject h5Info = new JSONObject();
h5Info.put("type", IpUtils.getOperatorSys(request));
sceneInfo.put("h5_info", h5Info);
requestParams.put("scene_info", sceneInfo);
} //2.初始化post请求
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/partner/transactions/" + enums.getPath());
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
//拿到返回参数
JSONObject resParams = JSONObject.parseObject(EntityUtils.toString(response.getEntity()));
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(resParams);
}
return resParams;
} catch (IOException e) {
throw new ServiceException(e.getMessage());
} } /**
* @param bo 查询订单详情
* @Description 查询订单信息
*/
public OrderResultInfo queryOrder(QueryOrderBO bo) {
String url = " https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/"
+ bo.getOrderNo()
+ "?" + "sp_mchid=" + bo.getSpMchid()
+ "&" + "sub_mchid=" + bo.getSubMchid();
HttpGet httpGet = new HttpGet(url);
httpGet.addHeader("Accept", "application/json");
try {
CloseableHttpResponse response = httpClient.execute(httpGet);
String bodyAsString = EntityUtils.toString(response.getEntity());
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(JSONObject.parseObject(bodyAsString));
}
return JSONObject.parseObject(bodyAsString, OrderResultInfo.class);
} catch (IOException e) {
throw new ServiceException(e);
}
} /**
* @param orderNo 订单号
* @param subMchid 子商户号
* @description: 关闭订单
*/
public void closeOrder(String orderNo, String subMchid) {
String url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/" + orderNo + "/close";
//构造参数
JSONObject requestParams = new JSONObject();
requestParams.put("sp_mchid", wxconfig.getMerchantId());
requestParams.put("sub_mchid", subMchid);
//构造请求
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
try {
CloseableHttpResponse response = httpClient.execute(httpPost);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_NO_CONTENT) {
throw new ServiceException(response.getEntity());
}
} catch (IOException e) {
throw new ServiceException(e.getMessage());
} } /**
* 退款
*/
public RefundResultInfo refund(RefundBO bo) {
//1. 构造参数
JSONObject requestParams = new JSONObject();
//子商户号
requestParams.put("sub_mchid", bo.getSubMchId());
//服务商订单号
requestParams.put("out_trade_no", bo.getOrderNo());
//退款单号
requestParams.put("out_refund_no", bo.getRefundNo());
requestParams.put("reason", bo.getReason());
JSONObject amount = new JSONObject();
//退款金额,单位分
amount.put("refund", bo.getRefund());
//原订单金额,单位分
amount.put("total", bo.getTotal());
amount.put("currency", "CNY");
requestParams.put("amount", amount); //2. 初始化post请求
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
httpPost.setEntity(new StringEntity(requestParams.toJSONString(), StandardCharsets.UTF_8));
try {
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
String resString = EntityUtils.toString(httpResponse.getEntity());
if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException(JSONObject.parseObject(resString));
}
return JSONObject.parseObject(resString, RefundResultInfo.class);
} catch (IOException e) {
throw new ServiceException(e);
}
} /**
* @param request
* @Description 回调请求的验签、解密
*/
public OrderResultInfo asyncCallback(HttpServletRequest request) {
//1.获取请求头
String body = getRequestBody(request);
String serialNumber = request.getHeader("Wechatpay-Serial");
String timeStamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
//2.构造请求
NotificationRequest notificationRequest = new NotificationRequest.Builder().withSerialNumber(serialNumber)
.withNonce(nonce)
.withTimestamp(timeStamp)
.withSignature(signature)
.withBody(body)
.build();
NotificationHandler handler = new NotificationHandler(verifier, wxconfig.getApiV3key().getBytes(StandardCharsets.UTF_8));
// 验签和解析请求体
try {
Notification notification = handler.parse(notificationRequest);
return JSONObject.parseObject(notification.getDecryptData(), OrderResultInfo.class);
} catch (ValidationException | ParseException e) {
throw new ServiceException(e);
}
} /**
* 微信上传图片/视频
*/
public String upload(MultipartFile multipartFile, UploadEnums enums) throws IOException {
String sha256 = DigestUtils.sha256Hex(multipartFile.getBytes());
HttpPost httpPost = new WechatPayUploadHttpPost
.Builder(URI.create("https://api.mch.weixin.qq.com/v3/merchant/media/" + enums.getValue()))
.withImage(multipartFile.getOriginalFilename(), sha256,
new ByteArrayInputStream(multipartFile.getBytes()))
.build();
CloseableHttpResponse response = httpClient.execute(httpPost);
String resString = EntityUtils.toString(response.getEntity());
JSONObject resParams = JSONObject.parseObject(resString);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new ServiceException("请求失败", resParams);
}
return resParams.getString("media_id");
} /**
* @param text 明文
* @return 加密后的数据
*/
public String encrypt(String text) {
// 建议从Verifier中获得微信支付平台证书,或使用预先下载到本地的平台证书文件中
X509Certificate certificate = verifier.getValidCertificate();
try {
return RsaCryptoUtil.encryptOAEP(text, certificate);
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
}
return null;
} /**
* @param ciphertext 密文
* @return 解密后端数据
*/
public String decrypt(String ciphertext) {
// 使用商户私钥解密
try {
return RsaCryptoUtil.decryptOAEP(ciphertext, wxconfig.privateKey);
} catch (BadPaddingException e) {
e.printStackTrace();
}
return null;
} /**
* 获取POST请求的json参数
*/
private String getRequestBody(HttpServletRequest request) {
try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8))) {
StringBuilder responseStrBuilder = new StringBuilder();
String inputStr;
while ((inputStr = streamReader.readLine()) != null) {
responseStrBuilder.append(inputStr);
}
return responseStrBuilder.toString();
} catch (IOException e) {
throw new RuntimeException(e);
}
} }

service里面有个方法是服务商进件的方法,里面的请求参数非常多,建议参考微信支付-开发者文档 (qq.com)

最新文章

  1. Linux命令【第二篇】
  2. Unity性能优化(4)-官方教程Optimizing graphics rendering in Unity games翻译
  3. 禁止换行“white-space:nowrap;”!
  4. 解密jQuery内核 DOM操作
  5. Flex 对象克隆
  6. FPGA技术的发展历史和动向
  7. 18.用两个栈实现队列[2StacksToImplementQueue]
  8. 浅谈MySQL索引背后的数据结构及算法
  9. DevExpress控件GridView挂下拉控件无法对上值
  10. 通过库函数API和C代码中嵌入汇编代码剖析系统调用的工作机制
  11. less-3-混合
  12. air2调用本地exe的文章
  13. Jquery获取元素坐标
  14. 关于c++的引用
  15. H5音频处理的一些小知识
  16. ActiveMQ学习系列(三)----下载github源码并编译
  17. js 写21点
  18. GitHub看板系统(Project)
  19. php开发APP接口(总结一)
  20. C语言编程练习

热门文章

  1. Win10微软官方镜像iso文件下载
  2. C语言经典100例【1、2】
  3. git常用命令与AndroidStudio常用快捷键
  4. vim中的命令行 %! 是啥意思?
  5. 怎么解决CMD下执行Go出现中文乱码问题?
  6. 7.golang语言学习,标识符的命名规范
  7. NTP时钟服务器(时间同步服务器)在大数据时代的重要性
  8. labwindows excel 不同电脑上无法保存
  9. js检测邮箱格式,正则检测邮箱格式
  10. linux 防火墙管理