Fixing .gitignore
This commit is contained in:
@@ -1,238 +1,238 @@
|
||||
package com.peanut.modules.pay.weChatPay.config;
|
||||
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.*;
|
||||
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 io.swagger.annotations.ApiOperation;
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.Enumeration;
|
||||
|
||||
|
||||
/**
|
||||
* @Author:
|
||||
* @Description:
|
||||
**/
|
||||
@Configuration
|
||||
@Data
|
||||
@Slf4j
|
||||
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
|
||||
@PropertySource(value = "classpath:weChatConfig.properties" ) //读取配置文件
|
||||
//@Component
|
||||
public class WechatPayConfig implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* APPID
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
private String domain;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//获取私钥工具
|
||||
public PrivateKey getPrivateKey(String keyPemPath) {
|
||||
try {
|
||||
/*
|
||||
|
||||
解决方案
|
||||
1. 将私钥文件放在应用的外部,然后通过文件路径来读取。例如:
|
||||
privateKeyPath = "/etc/myapp/apiclient_key.pem";
|
||||
File file = new File(privateKeyPath);
|
||||
return PemUtil.loadPrivateKey(new FileInputStream(file));
|
||||
这样你就可以将 privateKeyPath 设置为任意位置的文件路径。
|
||||
|
||||
|
||||
2.如果你确实需要将私钥文件打包到应用中,你可以使用 ResourceUtils.getURL(filename).openStream() 来获取文件内容,例如:
|
||||
privateKeyPath = "classpath:cert/apiclient_key.pem";
|
||||
return PemUtil.loadPrivateKey(ResourceUtils.getURL(privateKeyPath).openStream());
|
||||
这样你就可以从应用的 classpath 中读取文件内容。
|
||||
|
||||
ResourceUtils.getURL(filename).openStream() 来获取资源,这是与平台无关的,所以在Windows、Linux以及其他任何支持Java的平台上都能正常使用。只要你的资源文件(在这个例子中是私钥文件)被正确的包含在了你的应用的classpath中,那么你就可以在任何平台上使用这个方法来读取资源文件的内容。
|
||||
这里需要注意的是,classpath: 是一个特殊的协议前缀,它表示资源是从classpath中获取的。当你的资源文件被打包进jar或war时,它们就位于应用的classpath中,因此你可以使用 classpath: 前缀来获取这些文件。
|
||||
因此,无论你的应用运行在Windows还是Linux,或者是其他任何操作系统上,使用方法2都不会有问题。只要你的资源文件被正确地打包进了应用,那么就可以使用这种方法来读取文件内容。
|
||||
*/
|
||||
|
||||
|
||||
// 修改后的方法
|
||||
|
||||
return PemUtil.loadPrivateKey(ResourceUtils.getURL(keyPemPath).openStream());
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("私钥文件不存在", e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("私钥文件流失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取证书管理器实例 签名验证器
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取支付http请求对象
|
||||
*
|
||||
* @param verifier
|
||||
* @return
|
||||
*/
|
||||
@Bean(name = "wxPayClient")
|
||||
|
||||
|
||||
public CloseableHttpClient getWxPayClient(Verifier verifier) {
|
||||
log.info("获取HttpClient");
|
||||
|
||||
//获取商户私钥
|
||||
|
||||
PrivateKey privateKey = getPrivateKey(keyPemPath);
|
||||
|
||||
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(mchId, serialNo, privateKey)
|
||||
.withValidator(new WechatPay2Validator(verifier));
|
||||
CloseableHttpClient httpClient =builder.build();
|
||||
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
package com.peanut.modules.pay.weChatPay.config;
|
||||
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.*;
|
||||
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 io.swagger.annotations.ApiOperation;
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.Enumeration;
|
||||
|
||||
|
||||
/**
|
||||
* @Author:
|
||||
* @Description:
|
||||
**/
|
||||
@Configuration
|
||||
@Data
|
||||
@Slf4j
|
||||
@ConfigurationProperties(prefix = "wxpay") //读取wxpay节点
|
||||
@PropertySource(value = "classpath:weChatConfig.properties" ) //读取配置文件
|
||||
//@Component
|
||||
public class WechatPayConfig implements Serializable {
|
||||
|
||||
|
||||
/**
|
||||
* APPID
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
private String domain;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//获取私钥工具
|
||||
public PrivateKey getPrivateKey(String keyPemPath) {
|
||||
try {
|
||||
/*
|
||||
|
||||
解决方案
|
||||
1. 将私钥文件放在应用的外部,然后通过文件路径来读取。例如:
|
||||
privateKeyPath = "/etc/myapp/apiclient_key.pem";
|
||||
File file = new File(privateKeyPath);
|
||||
return PemUtil.loadPrivateKey(new FileInputStream(file));
|
||||
这样你就可以将 privateKeyPath 设置为任意位置的文件路径。
|
||||
|
||||
|
||||
2.如果你确实需要将私钥文件打包到应用中,你可以使用 ResourceUtils.getURL(filename).openStream() 来获取文件内容,例如:
|
||||
privateKeyPath = "classpath:cert/apiclient_key.pem";
|
||||
return PemUtil.loadPrivateKey(ResourceUtils.getURL(privateKeyPath).openStream());
|
||||
这样你就可以从应用的 classpath 中读取文件内容。
|
||||
|
||||
ResourceUtils.getURL(filename).openStream() 来获取资源,这是与平台无关的,所以在Windows、Linux以及其他任何支持Java的平台上都能正常使用。只要你的资源文件(在这个例子中是私钥文件)被正确的包含在了你的应用的classpath中,那么你就可以在任何平台上使用这个方法来读取资源文件的内容。
|
||||
这里需要注意的是,classpath: 是一个特殊的协议前缀,它表示资源是从classpath中获取的。当你的资源文件被打包进jar或war时,它们就位于应用的classpath中,因此你可以使用 classpath: 前缀来获取这些文件。
|
||||
因此,无论你的应用运行在Windows还是Linux,或者是其他任何操作系统上,使用方法2都不会有问题。只要你的资源文件被正确地打包进了应用,那么就可以使用这种方法来读取文件内容。
|
||||
*/
|
||||
|
||||
|
||||
// 修改后的方法
|
||||
|
||||
return PemUtil.loadPrivateKey(ResourceUtils.getURL(keyPemPath).openStream());
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("私钥文件不存在", e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("私钥文件流失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取证书管理器实例 签名验证器
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取支付http请求对象
|
||||
*
|
||||
* @param verifier
|
||||
* @return
|
||||
*/
|
||||
@Bean(name = "wxPayClient")
|
||||
|
||||
|
||||
public CloseableHttpClient getWxPayClient(Verifier verifier) {
|
||||
log.info("获取HttpClient");
|
||||
|
||||
//获取商户私钥
|
||||
|
||||
PrivateKey privateKey = getPrivateKey(keyPemPath);
|
||||
|
||||
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(mchId, serialNo, privateKey)
|
||||
.withValidator(new WechatPay2Validator(verifier));
|
||||
CloseableHttpClient httpClient =builder.build();
|
||||
// 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取HttpClient,无需进行应答签名验证,跳过验签的流程
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,451 +1,451 @@
|
||||
package com.peanut.modules.pay.weChatPay.controller;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.peanut.common.utils.R;
|
||||
import com.peanut.modules.book.entity.*;
|
||||
import com.peanut.modules.book.service.*;
|
||||
import com.peanut.modules.pay.weChatPay.dto.WechatDto;
|
||||
import com.peanut.modules.pay.weChatPay.config.WechatPayConfig;
|
||||
import com.peanut.modules.pay.weChatPay.util.HttpUtils;
|
||||
import com.peanut.modules.book.entity.BookBuyConfigEntity;
|
||||
import com.peanut.modules.pay.weChatPay.util.WechatPayValidator;
|
||||
import com.peanut.modules.pay.weChatPay.util.WxPayUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.soap.Text;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@CrossOrigin //跨域
|
||||
@RequestMapping("/pay")
|
||||
@Configuration
|
||||
public class WeChatPayController {
|
||||
|
||||
@Autowired private MyUserService userService;
|
||||
|
||||
@Autowired private BuyOrderService buyOrderService;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private WechatPayConfig wechatPayConfig;
|
||||
|
||||
@Autowired private BookBuyConfigService bookBuyConfigService;
|
||||
|
||||
@Autowired private PayWechatOrderService payWechatOrderService;
|
||||
|
||||
@Autowired private PayPaymentOrderService payPaymentOrderService;
|
||||
|
||||
@Autowired private TransactionDetailsService transactionDetailsService;
|
||||
|
||||
@Autowired
|
||||
private CloseableHttpClient wxPayNoSignClient; //无需应答签名
|
||||
@Autowired
|
||||
private CloseableHttpClient wxPayClient;
|
||||
@Autowired
|
||||
private WxPayUtil wxPayUtil;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
// 由微信生成的应用ID,全局唯一。
|
||||
public static final String appId= "wx47134a8f15083734";
|
||||
// 直连商户的商户号,由微信支付生成并下发。
|
||||
public static final String mchId = "1612860909";
|
||||
// 商户证书序列号 7B5676E3CDF56680D0414A009CE501C844DBE2D6 679AECB2F7AC4183033F713828892BA640E4EEE3
|
||||
public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3";
|
||||
// 微信下单Url
|
||||
public static final String payUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* App 微信下单
|
||||
*
|
||||
* @param
|
||||
* @return
|
||||
* @PathVariable
|
||||
*/
|
||||
|
||||
@RequestMapping(value = "/placeAnOrder/app")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R pay(@RequestBody WechatDto dto ) throws Exception{
|
||||
log.info("微信生成订单");
|
||||
List<BuyOrderEntity> one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper<BuyOrderEntity>().eq("order_sn", dto.getOrderSn()));
|
||||
BuyOrderEntity order = one.get(0);
|
||||
// 获取订单
|
||||
Map<String,Object> paramMap = new HashMap<>();
|
||||
paramMap.put("appid",appId);
|
||||
paramMap.put("mchid",mchId);
|
||||
paramMap.put("description","微信充值");
|
||||
// 订单编号
|
||||
paramMap.put("out_trade_no",order.getOrderSn());
|
||||
// paramMap.put("attach",""); //自定义数据 支付完成后才能显示 在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
paramMap.put("notify_url",wechatPayConfig.getNotifyUrl());
|
||||
|
||||
// paramMap.put("time_expire",afterString);
|
||||
// 实收金额0.38乘100=38
|
||||
BigDecimal realsmoney= order.getRealMoney();
|
||||
BigDecimal hand=new BigDecimal("100");
|
||||
realsmoney =realsmoney.multiply(hand) ;
|
||||
Map<String,Object> amountMap = new HashMap<>();
|
||||
amountMap.put("total",realsmoney);
|
||||
amountMap.put("currency","CNY");
|
||||
|
||||
|
||||
BigDecimal money= order.getRealMoney();
|
||||
BigDecimal han=new BigDecimal("100");
|
||||
realsmoney =realsmoney.multiply(han) ;
|
||||
Map<String,Object> amount = new HashMap<>();
|
||||
amount.put("total",realsmoney);
|
||||
amount.put("currency","CNY");
|
||||
|
||||
if (dto.getTotalAmount()!=order.getRealMoney()) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
paramMap.put("amount",amountMap);
|
||||
|
||||
JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap));
|
||||
log.info("请求参数"+paramMap);
|
||||
com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString());
|
||||
String prepayid = jsonObject1.getString("prepay_id");
|
||||
// 传入参数 payUrl 发送post请求
|
||||
HttpPost httpPost = new HttpPost(payUrl);
|
||||
// 将json数据转换成字符串
|
||||
StringEntity entity = new StringEntity(json.toString(),"utf-8");
|
||||
// 设置该请求的Content-Type为application/json 都是json格式
|
||||
entity.setContentType("application/json");
|
||||
// 将实体对象设置到HttpPost表示要传递该数据到服务器端。
|
||||
httpPost.setEntity(entity);
|
||||
// 设置请求头部的Accept属性为"application/json"表示客户端希望接收的为json。
|
||||
httpPost.setHeader("Accept", "application/json");
|
||||
CloseableHttpResponse response = wxPayClient.execute(httpPost);
|
||||
// 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。
|
||||
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
|
||||
// 时间戳
|
||||
Long timestamp = System.currentTimeMillis()/1000;
|
||||
// 随机串
|
||||
String nonceStr = UUID.randomUUID().toString().replace("-","");
|
||||
|
||||
String sign = wxPayUtil.getSign(WxPayUtil.appId,timestamp,nonceStr,prepayid);
|
||||
log.info("签名:"+sign);
|
||||
|
||||
Map Map = new HashMap();
|
||||
Map.put("prepayid",prepayid);
|
||||
Map.put("timestamp",timestamp+"");
|
||||
Map.put("noncestr",nonceStr);
|
||||
Map.put("sign",sign);
|
||||
Map.put("appid",appId);
|
||||
Map.put("package","Sign=WXPay");
|
||||
Map.put("extData","sign");
|
||||
Map.put("partnerid",mchId);
|
||||
try {
|
||||
int statusCode = response.getStatusLine().getStatusCode(); //响应状态码
|
||||
if (statusCode == 200) { //处理成功
|
||||
System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity())
|
||||
// 添加微信支付订单信息
|
||||
PayWechatOrderEntity wechat = new PayWechatOrderEntity();
|
||||
wechat.setCustomerId(order.getUserId()); //用户id
|
||||
wechat.setCreateTime(new Date()); //创建订单时间
|
||||
wechat.setOrderSn(order.getOrderSn()); //订单编号
|
||||
wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity())
|
||||
wechat.setTotalAmount(dto.getTotalAmount()); //支付实收金额
|
||||
wechat.setSystemLog(response.toString()); //日志
|
||||
wechat.setPayType(order.getOrderType()); //交易类型
|
||||
wechat.setOrderId(order.getOrderSn()); //订单号
|
||||
wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id
|
||||
// wechat.setEndtime(after);
|
||||
this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库
|
||||
|
||||
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||
System.out.println("成功");
|
||||
} else {
|
||||
System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString);
|
||||
throw new IOException("request failed");
|
||||
}
|
||||
}finally {
|
||||
response.close();
|
||||
}
|
||||
// 返回url和订单号
|
||||
return R.ok().put("paramMap" ,paramMap).put("Map",Map);
|
||||
|
||||
}
|
||||
/**
|
||||
* 获取私钥。
|
||||
*
|
||||
* @param filename 私钥文件路径 (required)
|
||||
* @return 私钥对象
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String filename) throws Exception {
|
||||
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
|
||||
try {
|
||||
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s+", "");
|
||||
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
return kf.generatePrivate(
|
||||
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("当前Java环境不支持RSA", e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new RuntimeException("无效的密钥格式");
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 微信支付回调
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/payNotify")
|
||||
public R payNotify(HttpServletRequest request, HttpServletResponse response) {
|
||||
log.info("##############################微信支付回调#######################");
|
||||
|
||||
|
||||
// 处理通知参数
|
||||
Map<String,Object> bodyMap = getNotifyBody(request);
|
||||
if(bodyMap==null){
|
||||
return null;
|
||||
}
|
||||
if(lock.tryLock()) {
|
||||
try {
|
||||
// 解密resource中的通知数据
|
||||
String resource = bodyMap.get("resource").toString();
|
||||
Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(),1);
|
||||
String orderNo = resourceMap.get("out_trade_no").toString();
|
||||
String transactionId = resourceMap.get("transaction_id").toString();
|
||||
// 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
|
||||
BuyOrderEntity order = this.buyOrderService.getOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", orderNo));
|
||||
PayWechatOrderEntity wechatEntity = new PayWechatOrderEntity();
|
||||
if(!ObjectUtils.isEmpty(order)){
|
||||
|
||||
wechatEntity = this.payWechatOrderService.getOne(new QueryWrapper<PayWechatOrderEntity>().eq("order_sn", order.getOrderSn()));
|
||||
}else{
|
||||
log.error("无效订单!");
|
||||
return R.error(500,"无效订单!");
|
||||
}
|
||||
// 1.根据订单id获取订单信息
|
||||
|
||||
if("order".equals(order.getOrderType())){
|
||||
|
||||
BuyOrderEntity orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", wechatEntity.getOrderSn()));
|
||||
BigDecimal realMoney = orderEntity.getRealMoney();
|
||||
|
||||
//更新 订单 记录
|
||||
if (wechatEntity.getTotalAmount().compareTo(realMoney) == 0) {
|
||||
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"0");
|
||||
}
|
||||
|
||||
}
|
||||
if("point".equals(order.getOrderType())){
|
||||
// List<BookBuyConfigEntity> bookBuyConfigEntity = bookBuyConfigService.getBaseMapper().selectList(new QueryWrapper<BookBuyConfigEntity>().eq("price_type_id", ""));
|
||||
// 插入花生币 变动记录
|
||||
PayWechatOrderEntity buy_order_id = payWechatOrderService.getBaseMapper().selectOne(new QueryWrapper<PayWechatOrderEntity>().eq("order_sn", order.getOrderSn()));
|
||||
Integer buyorder= buy_order_id.getBuyOrderId();
|
||||
BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(buyorder);
|
||||
|
||||
|
||||
String realMoney =bookBuyConfigEntity.getRealMoney();
|
||||
Integer money = Integer.valueOf(realMoney);
|
||||
userService.rechargeHSPoint(Integer.valueOf(order.getUserId()),money);
|
||||
TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity();
|
||||
transactionDetailsEntity.setUserId(order.getUserId());
|
||||
transactionDetailsEntity.setChangeAmount(new BigDecimal(money));
|
||||
transactionDetailsEntity.setOrderType("充值");
|
||||
// transactionDetailsEntity.setRelationId(wechatEntity.getId().intValue());
|
||||
transactionDetailsEntity.setRelationId(buy_order_id.getId().intValue());
|
||||
transactionDetailsEntity.setRemark("充值");
|
||||
|
||||
MyUserEntity user = userService.getById(Integer.valueOf(order.getUserId()));
|
||||
BigDecimal peanutCoin = user.getPeanutCoin();
|
||||
transactionDetailsEntity.setUserBalance(peanutCoin);
|
||||
transactionDetailsEntity.setUserName(user.getNickname());
|
||||
transactionDetailsEntity.setTel(user.getTel());
|
||||
transactionDetailsService.save(transactionDetailsEntity);
|
||||
// 插入 花生币 充值记录
|
||||
PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity();
|
||||
payPaymentOrderEntity.setUserId(Integer.valueOf(order.getUserId()));
|
||||
payPaymentOrderEntity.setOrderId(String.valueOf(buy_order_id.getId()));
|
||||
payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney()));
|
||||
payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney()));
|
||||
payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao());
|
||||
payPaymentOrderEntity.setRechargeStatus("success");
|
||||
payPaymentOrderEntity.setSuccessTime(new Date());
|
||||
payPaymentOrderEntity.setUserName(user.getNickname());
|
||||
payPaymentOrderEntity.setTel(user.getTel());
|
||||
payPaymentOrderService.save(payPaymentOrderEntity);
|
||||
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"2");
|
||||
}
|
||||
|
||||
} finally {
|
||||
// 要主动释放锁
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 成功应答
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private Map<String,Object> getNotifyBody(HttpServletRequest request ){
|
||||
// 处理通知参数
|
||||
String body = HttpUtils.readData(request);
|
||||
log.info("zhifu回调参数:{}",body);
|
||||
|
||||
// 转换为Map
|
||||
Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>(){});
|
||||
// 微信的通知ID(通知的唯一ID)
|
||||
String notifyId = bodyMap.get("id").toString();
|
||||
|
||||
// 验证签名信息
|
||||
try {
|
||||
WechatPayValidator wechatPayValidator
|
||||
= new WechatPayValidator(wechatPayConfig.getVerifier(), notifyId, body);
|
||||
if(!wechatPayValidator.validate(request)){
|
||||
|
||||
log.error("通知验签失败");
|
||||
return null;
|
||||
}
|
||||
log.info("通知验签成功");
|
||||
}catch (Exception e) {
|
||||
|
||||
}
|
||||
return bodyMap;
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/placeAnOrder/shoppingpay")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R shoppingpay(@RequestBody WechatDto dto ) throws Exception{
|
||||
log.info("生成订单");
|
||||
List<BuyOrderEntity> one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper<BuyOrderEntity>().eq("order_sn", dto.getOrderSn()));
|
||||
BuyOrderEntity order = one.get(0);
|
||||
|
||||
// 获取订单
|
||||
Map<String,Object> paramMap = new HashMap<>();
|
||||
paramMap.put("appid",appId);
|
||||
paramMap.put("mchid",mchId);
|
||||
paramMap.put("description","微信充值");
|
||||
// 订单编号
|
||||
paramMap.put("out_trade_no",order.getOrderSn());
|
||||
// paramMap.put("attach",""); //自定义数据 支付完成后才能显示 在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
paramMap.put("notify_url",wechatPayConfig.getNotifyUrl());
|
||||
BigDecimal realsmoney= dto.getTotalAmount();
|
||||
BigDecimal hand=new BigDecimal("100");
|
||||
realsmoney =realsmoney.multiply(hand) ;
|
||||
Map<String,Object> amountMap = new HashMap<>();
|
||||
amountMap.put("total",realsmoney);
|
||||
amountMap.put("currency","CNY");
|
||||
|
||||
|
||||
paramMap.put("amount",amountMap);
|
||||
|
||||
|
||||
|
||||
JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap));
|
||||
log.info("请求参数"+paramMap);
|
||||
com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString());
|
||||
String prepayid = jsonObject1.getString("prepay_id");
|
||||
// 传入参数 payUrl 发送post请求
|
||||
HttpPost httpPost = new HttpPost(payUrl);
|
||||
// 将json数据转换成字符串
|
||||
StringEntity entity = new StringEntity(json.toString(),"utf-8");
|
||||
// 设置该请求的Content-Type为application/json 都是json格式
|
||||
entity.setContentType("application/json");
|
||||
// 将实体对象设置到HttpPost表示要传递该数据到服务器端。
|
||||
httpPost.setEntity(entity);
|
||||
// 设置请求头部的Accept属性为"application/json"表示客户端希望接收的为json。
|
||||
httpPost.setHeader("Accept", "application/json");
|
||||
CloseableHttpResponse response = wxPayClient.execute(httpPost);
|
||||
// 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。
|
||||
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
|
||||
// 时间戳
|
||||
Long timestamp = System.currentTimeMillis()/1000;
|
||||
// 随机串
|
||||
String nonceStr = UUID.randomUUID().toString().replace("-","");
|
||||
|
||||
String sign = wxPayUtil.getSign(WxPayUtil.appId,timestamp,nonceStr,prepayid);
|
||||
log.info("签名:"+sign);
|
||||
|
||||
Map Map = new HashMap();
|
||||
Map.put("prepayid",prepayid);
|
||||
Map.put("timestamp",timestamp+"");
|
||||
Map.put("noncestr",nonceStr);
|
||||
Map.put("sign",sign);
|
||||
Map.put("appid",appId);
|
||||
Map.put("package","Sign=WXPay");
|
||||
Map.put("extData","sign");
|
||||
Map.put("partnerid",mchId);
|
||||
try {
|
||||
int statusCode = response.getStatusLine().getStatusCode(); //响应状态码
|
||||
if (statusCode == 200) { //处理成功
|
||||
System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity())
|
||||
// 添加微信支付订单信息
|
||||
PayWechatOrderEntity wechat = new PayWechatOrderEntity();
|
||||
wechat.setCustomerId(order.getUserId()); //用户id
|
||||
wechat.setCreateTime(new Date()); //创建订单时间
|
||||
wechat.setOrderSn(order.getOrderSn()); //订单编号
|
||||
wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity())
|
||||
wechat.setTotalAmount(order.getRealMoney()); //支付实收金额
|
||||
wechat.setSystemLog(response.toString()); //日志
|
||||
wechat.setPayType(order.getOrderType()); //交易类型
|
||||
wechat.setOrderId(order.getOrderSn()); //订单号
|
||||
wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id
|
||||
// wechat.setEndtime(after);
|
||||
this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库
|
||||
|
||||
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||
System.out.println("成功");
|
||||
} else {
|
||||
System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString);
|
||||
throw new IOException("request failed");
|
||||
}
|
||||
}finally {
|
||||
response.close();
|
||||
}
|
||||
// 返回url和订单号
|
||||
return R.ok().put("paramMap" ,paramMap).put("Map",Map);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.controller;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.peanut.common.utils.R;
|
||||
import com.peanut.modules.book.entity.*;
|
||||
import com.peanut.modules.book.service.*;
|
||||
import com.peanut.modules.pay.weChatPay.dto.WechatDto;
|
||||
import com.peanut.modules.pay.weChatPay.config.WechatPayConfig;
|
||||
import com.peanut.modules.pay.weChatPay.util.HttpUtils;
|
||||
import com.peanut.modules.book.entity.BookBuyConfigEntity;
|
||||
import com.peanut.modules.pay.weChatPay.util.WechatPayValidator;
|
||||
import com.peanut.modules.pay.weChatPay.util.WxPayUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.xml.soap.Text;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyFactory;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@CrossOrigin //跨域
|
||||
@RequestMapping("/pay")
|
||||
@Configuration
|
||||
public class WeChatPayController {
|
||||
|
||||
@Autowired private MyUserService userService;
|
||||
|
||||
@Autowired private BuyOrderService buyOrderService;
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private WechatPayConfig wechatPayConfig;
|
||||
|
||||
@Autowired private BookBuyConfigService bookBuyConfigService;
|
||||
|
||||
@Autowired private PayWechatOrderService payWechatOrderService;
|
||||
|
||||
@Autowired private PayPaymentOrderService payPaymentOrderService;
|
||||
|
||||
@Autowired private TransactionDetailsService transactionDetailsService;
|
||||
|
||||
@Autowired
|
||||
private CloseableHttpClient wxPayNoSignClient; //无需应答签名
|
||||
@Autowired
|
||||
private CloseableHttpClient wxPayClient;
|
||||
@Autowired
|
||||
private WxPayUtil wxPayUtil;
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
// 由微信生成的应用ID,全局唯一。
|
||||
public static final String appId= "wx47134a8f15083734";
|
||||
// 直连商户的商户号,由微信支付生成并下发。
|
||||
public static final String mchId = "1612860909";
|
||||
// 商户证书序列号 7B5676E3CDF56680D0414A009CE501C844DBE2D6 679AECB2F7AC4183033F713828892BA640E4EEE3
|
||||
public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3";
|
||||
// 微信下单Url
|
||||
public static final String payUrl = "https://api.mch.weixin.qq.com/v3/pay/transactions/app";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* App 微信下单
|
||||
*
|
||||
* @param
|
||||
* @return
|
||||
* @PathVariable
|
||||
*/
|
||||
|
||||
@RequestMapping(value = "/placeAnOrder/app")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R pay(@RequestBody WechatDto dto ) throws Exception{
|
||||
log.info("微信生成订单");
|
||||
List<BuyOrderEntity> one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper<BuyOrderEntity>().eq("order_sn", dto.getOrderSn()));
|
||||
BuyOrderEntity order = one.get(0);
|
||||
// 获取订单
|
||||
Map<String,Object> paramMap = new HashMap<>();
|
||||
paramMap.put("appid",appId);
|
||||
paramMap.put("mchid",mchId);
|
||||
paramMap.put("description","微信充值");
|
||||
// 订单编号
|
||||
paramMap.put("out_trade_no",order.getOrderSn());
|
||||
// paramMap.put("attach",""); //自定义数据 支付完成后才能显示 在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
paramMap.put("notify_url",wechatPayConfig.getNotifyUrl());
|
||||
|
||||
// paramMap.put("time_expire",afterString);
|
||||
// 实收金额0.38乘100=38
|
||||
BigDecimal realsmoney= order.getRealMoney();
|
||||
BigDecimal hand=new BigDecimal("100");
|
||||
realsmoney =realsmoney.multiply(hand) ;
|
||||
Map<String,Object> amountMap = new HashMap<>();
|
||||
amountMap.put("total",realsmoney);
|
||||
amountMap.put("currency","CNY");
|
||||
|
||||
|
||||
BigDecimal money= order.getRealMoney();
|
||||
BigDecimal han=new BigDecimal("100");
|
||||
realsmoney =realsmoney.multiply(han) ;
|
||||
Map<String,Object> amount = new HashMap<>();
|
||||
amount.put("total",realsmoney);
|
||||
amount.put("currency","CNY");
|
||||
|
||||
if (dto.getTotalAmount()!=order.getRealMoney()) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
paramMap.put("amount",amountMap);
|
||||
|
||||
JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap));
|
||||
log.info("请求参数"+paramMap);
|
||||
com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString());
|
||||
String prepayid = jsonObject1.getString("prepay_id");
|
||||
// 传入参数 payUrl 发送post请求
|
||||
HttpPost httpPost = new HttpPost(payUrl);
|
||||
// 将json数据转换成字符串
|
||||
StringEntity entity = new StringEntity(json.toString(),"utf-8");
|
||||
// 设置该请求的Content-Type为application/json 都是json格式
|
||||
entity.setContentType("application/json");
|
||||
// 将实体对象设置到HttpPost表示要传递该数据到服务器端。
|
||||
httpPost.setEntity(entity);
|
||||
// 设置请求头部的Accept属性为"application/json"表示客户端希望接收的为json。
|
||||
httpPost.setHeader("Accept", "application/json");
|
||||
CloseableHttpResponse response = wxPayClient.execute(httpPost);
|
||||
// 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。
|
||||
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
|
||||
// 时间戳
|
||||
Long timestamp = System.currentTimeMillis()/1000;
|
||||
// 随机串
|
||||
String nonceStr = UUID.randomUUID().toString().replace("-","");
|
||||
|
||||
String sign = wxPayUtil.getSign(WxPayUtil.appId,timestamp,nonceStr,prepayid);
|
||||
log.info("签名:"+sign);
|
||||
|
||||
Map Map = new HashMap();
|
||||
Map.put("prepayid",prepayid);
|
||||
Map.put("timestamp",timestamp+"");
|
||||
Map.put("noncestr",nonceStr);
|
||||
Map.put("sign",sign);
|
||||
Map.put("appid",appId);
|
||||
Map.put("package","Sign=WXPay");
|
||||
Map.put("extData","sign");
|
||||
Map.put("partnerid",mchId);
|
||||
try {
|
||||
int statusCode = response.getStatusLine().getStatusCode(); //响应状态码
|
||||
if (statusCode == 200) { //处理成功
|
||||
System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity())
|
||||
// 添加微信支付订单信息
|
||||
PayWechatOrderEntity wechat = new PayWechatOrderEntity();
|
||||
wechat.setCustomerId(order.getUserId()); //用户id
|
||||
wechat.setCreateTime(new Date()); //创建订单时间
|
||||
wechat.setOrderSn(order.getOrderSn()); //订单编号
|
||||
wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity())
|
||||
wechat.setTotalAmount(dto.getTotalAmount()); //支付实收金额
|
||||
wechat.setSystemLog(response.toString()); //日志
|
||||
wechat.setPayType(order.getOrderType()); //交易类型
|
||||
wechat.setOrderId(order.getOrderSn()); //订单号
|
||||
wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id
|
||||
// wechat.setEndtime(after);
|
||||
this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库
|
||||
|
||||
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||
System.out.println("成功");
|
||||
} else {
|
||||
System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString);
|
||||
throw new IOException("request failed");
|
||||
}
|
||||
}finally {
|
||||
response.close();
|
||||
}
|
||||
// 返回url和订单号
|
||||
return R.ok().put("paramMap" ,paramMap).put("Map",Map);
|
||||
|
||||
}
|
||||
/**
|
||||
* 获取私钥。
|
||||
*
|
||||
* @param filename 私钥文件路径 (required)
|
||||
* @return 私钥对象
|
||||
*/
|
||||
public static PrivateKey getPrivateKey(String filename) throws Exception {
|
||||
String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");
|
||||
try {
|
||||
String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
|
||||
.replace("-----END PRIVATE KEY-----", "")
|
||||
.replaceAll("\\s+", "");
|
||||
|
||||
KeyFactory kf = KeyFactory.getInstance("RSA");
|
||||
return kf.generatePrivate(
|
||||
new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("当前Java环境不支持RSA", e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new RuntimeException("无效的密钥格式");
|
||||
}
|
||||
}
|
||||
/**
|
||||
*
|
||||
* 微信支付回调
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/payNotify")
|
||||
public R payNotify(HttpServletRequest request, HttpServletResponse response) {
|
||||
log.info("##############################微信支付回调#######################");
|
||||
|
||||
|
||||
// 处理通知参数
|
||||
Map<String,Object> bodyMap = getNotifyBody(request);
|
||||
if(bodyMap==null){
|
||||
return null;
|
||||
}
|
||||
if(lock.tryLock()) {
|
||||
try {
|
||||
// 解密resource中的通知数据
|
||||
String resource = bodyMap.get("resource").toString();
|
||||
Map<String, Object> resourceMap = WechatPayValidator.decryptFromResource(resource, wechatPayConfig.getApiV3Key(),1);
|
||||
String orderNo = resourceMap.get("out_trade_no").toString();
|
||||
String transactionId = resourceMap.get("transaction_id").toString();
|
||||
// 根据订单号,做幂等处理,并且在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱
|
||||
BuyOrderEntity order = this.buyOrderService.getOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", orderNo));
|
||||
PayWechatOrderEntity wechatEntity = new PayWechatOrderEntity();
|
||||
if(!ObjectUtils.isEmpty(order)){
|
||||
|
||||
wechatEntity = this.payWechatOrderService.getOne(new QueryWrapper<PayWechatOrderEntity>().eq("order_sn", order.getOrderSn()));
|
||||
}else{
|
||||
log.error("无效订单!");
|
||||
return R.error(500,"无效订单!");
|
||||
}
|
||||
// 1.根据订单id获取订单信息
|
||||
|
||||
if("order".equals(order.getOrderType())){
|
||||
|
||||
BuyOrderEntity orderEntity = buyOrderService.getBaseMapper().selectOne(new QueryWrapper<BuyOrderEntity>().eq("order_sn", wechatEntity.getOrderSn()));
|
||||
BigDecimal realMoney = orderEntity.getRealMoney();
|
||||
|
||||
//更新 订单 记录
|
||||
if (wechatEntity.getTotalAmount().compareTo(realMoney) == 0) {
|
||||
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"0");
|
||||
}
|
||||
|
||||
}
|
||||
if("point".equals(order.getOrderType())){
|
||||
// List<BookBuyConfigEntity> bookBuyConfigEntity = bookBuyConfigService.getBaseMapper().selectList(new QueryWrapper<BookBuyConfigEntity>().eq("price_type_id", ""));
|
||||
// 插入花生币 变动记录
|
||||
PayWechatOrderEntity buy_order_id = payWechatOrderService.getBaseMapper().selectOne(new QueryWrapper<PayWechatOrderEntity>().eq("order_sn", order.getOrderSn()));
|
||||
Integer buyorder= buy_order_id.getBuyOrderId();
|
||||
BookBuyConfigEntity bookBuyConfigEntity = bookBuyConfigService.getById(buyorder);
|
||||
|
||||
|
||||
String realMoney =bookBuyConfigEntity.getRealMoney();
|
||||
Integer money = Integer.valueOf(realMoney);
|
||||
userService.rechargeHSPoint(Integer.valueOf(order.getUserId()),money);
|
||||
TransactionDetailsEntity transactionDetailsEntity = new TransactionDetailsEntity();
|
||||
transactionDetailsEntity.setUserId(order.getUserId());
|
||||
transactionDetailsEntity.setChangeAmount(new BigDecimal(money));
|
||||
transactionDetailsEntity.setOrderType("充值");
|
||||
// transactionDetailsEntity.setRelationId(wechatEntity.getId().intValue());
|
||||
transactionDetailsEntity.setRelationId(buy_order_id.getId().intValue());
|
||||
transactionDetailsEntity.setRemark("充值");
|
||||
|
||||
MyUserEntity user = userService.getById(Integer.valueOf(order.getUserId()));
|
||||
BigDecimal peanutCoin = user.getPeanutCoin();
|
||||
transactionDetailsEntity.setUserBalance(peanutCoin);
|
||||
transactionDetailsEntity.setUserName(user.getNickname());
|
||||
transactionDetailsEntity.setTel(user.getTel());
|
||||
transactionDetailsService.save(transactionDetailsEntity);
|
||||
// 插入 花生币 充值记录
|
||||
PayPaymentOrderEntity payPaymentOrderEntity = new PayPaymentOrderEntity();
|
||||
payPaymentOrderEntity.setUserId(Integer.valueOf(order.getUserId()));
|
||||
payPaymentOrderEntity.setOrderId(String.valueOf(buy_order_id.getId()));
|
||||
payPaymentOrderEntity.setRealAmount(new BigDecimal(bookBuyConfigEntity.getRealMoney()));
|
||||
payPaymentOrderEntity.setRechargeAmount(new BigDecimal(bookBuyConfigEntity.getMoney()));
|
||||
payPaymentOrderEntity.setRechargeChannel(bookBuyConfigEntity.getQudao());
|
||||
payPaymentOrderEntity.setRechargeStatus("success");
|
||||
payPaymentOrderEntity.setSuccessTime(new Date());
|
||||
payPaymentOrderEntity.setUserName(user.getNickname());
|
||||
payPaymentOrderEntity.setTel(user.getTel());
|
||||
payPaymentOrderService.save(payPaymentOrderEntity);
|
||||
buyOrderService.updateOrderStatus(Integer.valueOf(order.getUserId()),order.getOrderSn(),"2");
|
||||
}
|
||||
|
||||
} finally {
|
||||
// 要主动释放锁
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 成功应答
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private Map<String,Object> getNotifyBody(HttpServletRequest request ){
|
||||
// 处理通知参数
|
||||
String body = HttpUtils.readData(request);
|
||||
log.info("zhifu回调参数:{}",body);
|
||||
|
||||
// 转换为Map
|
||||
Map<String, Object> bodyMap = JSONObject.parseObject(body, new TypeReference<Map<String, Object>>(){});
|
||||
// 微信的通知ID(通知的唯一ID)
|
||||
String notifyId = bodyMap.get("id").toString();
|
||||
|
||||
// 验证签名信息
|
||||
try {
|
||||
WechatPayValidator wechatPayValidator
|
||||
= new WechatPayValidator(wechatPayConfig.getVerifier(), notifyId, body);
|
||||
if(!wechatPayValidator.validate(request)){
|
||||
|
||||
log.error("通知验签失败");
|
||||
return null;
|
||||
}
|
||||
log.info("通知验签成功");
|
||||
}catch (Exception e) {
|
||||
|
||||
}
|
||||
return bodyMap;
|
||||
}
|
||||
|
||||
|
||||
@RequestMapping(value = "/placeAnOrder/shoppingpay")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R shoppingpay(@RequestBody WechatDto dto ) throws Exception{
|
||||
log.info("生成订单");
|
||||
List<BuyOrderEntity> one = this.buyOrderService.getBaseMapper().selectList(new QueryWrapper<BuyOrderEntity>().eq("order_sn", dto.getOrderSn()));
|
||||
BuyOrderEntity order = one.get(0);
|
||||
|
||||
// 获取订单
|
||||
Map<String,Object> paramMap = new HashMap<>();
|
||||
paramMap.put("appid",appId);
|
||||
paramMap.put("mchid",mchId);
|
||||
paramMap.put("description","微信充值");
|
||||
// 订单编号
|
||||
paramMap.put("out_trade_no",order.getOrderSn());
|
||||
// paramMap.put("attach",""); //自定义数据 支付完成后才能显示 在查询API和支付通知中原样返回,可作为自定义参数使用
|
||||
paramMap.put("notify_url",wechatPayConfig.getNotifyUrl());
|
||||
BigDecimal realsmoney= dto.getTotalAmount();
|
||||
BigDecimal hand=new BigDecimal("100");
|
||||
realsmoney =realsmoney.multiply(hand) ;
|
||||
Map<String,Object> amountMap = new HashMap<>();
|
||||
amountMap.put("total",realsmoney);
|
||||
amountMap.put("currency","CNY");
|
||||
|
||||
|
||||
paramMap.put("amount",amountMap);
|
||||
|
||||
|
||||
|
||||
JSONObject json = JSONObject.parseObject(JSON.toJSONString(paramMap));
|
||||
log.info("请求参数"+paramMap);
|
||||
com.alibaba.fastjson.JSONObject jsonObject1 = wxPayUtil.doPostWexinV3("https://api.mch.weixin.qq.com/v3/pay/transactions/app", json.toJSONString());
|
||||
String prepayid = jsonObject1.getString("prepay_id");
|
||||
// 传入参数 payUrl 发送post请求
|
||||
HttpPost httpPost = new HttpPost(payUrl);
|
||||
// 将json数据转换成字符串
|
||||
StringEntity entity = new StringEntity(json.toString(),"utf-8");
|
||||
// 设置该请求的Content-Type为application/json 都是json格式
|
||||
entity.setContentType("application/json");
|
||||
// 将实体对象设置到HttpPost表示要传递该数据到服务器端。
|
||||
httpPost.setEntity(entity);
|
||||
// 设置请求头部的Accept属性为"application/json"表示客户端希望接收的为json。
|
||||
httpPost.setHeader("Accept", "application/json");
|
||||
CloseableHttpResponse response = wxPayClient.execute(httpPost);
|
||||
// 向微信支付平台发送请求,处理响应结果,并将订单信息保存到数据库中。
|
||||
String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
|
||||
// 时间戳
|
||||
Long timestamp = System.currentTimeMillis()/1000;
|
||||
// 随机串
|
||||
String nonceStr = UUID.randomUUID().toString().replace("-","");
|
||||
|
||||
String sign = wxPayUtil.getSign(WxPayUtil.appId,timestamp,nonceStr,prepayid);
|
||||
log.info("签名:"+sign);
|
||||
|
||||
Map Map = new HashMap();
|
||||
Map.put("prepayid",prepayid);
|
||||
Map.put("timestamp",timestamp+"");
|
||||
Map.put("noncestr",nonceStr);
|
||||
Map.put("sign",sign);
|
||||
Map.put("appid",appId);
|
||||
Map.put("package","Sign=WXPay");
|
||||
Map.put("extData","sign");
|
||||
Map.put("partnerid",mchId);
|
||||
try {
|
||||
int statusCode = response.getStatusLine().getStatusCode(); //响应状态码
|
||||
if (statusCode == 200) { //处理成功
|
||||
System.out.println("成功,返回结果 = " + bodyAsString); //返回响应体 EntityUtils.toString(response.getEntity())
|
||||
// 添加微信支付订单信息
|
||||
PayWechatOrderEntity wechat = new PayWechatOrderEntity();
|
||||
wechat.setCustomerId(order.getUserId()); //用户id
|
||||
wechat.setCreateTime(new Date()); //创建订单时间
|
||||
wechat.setOrderSn(order.getOrderSn()); //订单编号
|
||||
wechat.setPrepayId(prepayid); //预支付回话标识 标识为响应体EntityUtils.toString(response.getEntity())
|
||||
wechat.setTotalAmount(order.getRealMoney()); //支付实收金额
|
||||
wechat.setSystemLog(response.toString()); //日志
|
||||
wechat.setPayType(order.getOrderType()); //交易类型
|
||||
wechat.setOrderId(order.getOrderSn()); //订单号
|
||||
wechat.setBuyOrderId(dto.getBuyOrderId()); //购买配置id
|
||||
// wechat.setEndtime(after);
|
||||
this.payWechatOrderService.save(wechat); //微信订单表拿到数据保存数据库
|
||||
|
||||
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||
System.out.println("成功");
|
||||
} else {
|
||||
System.out.println("下单失败 = " + statusCode + ",返回结果 = " + bodyAsString);
|
||||
throw new IOException("request failed");
|
||||
}
|
||||
}finally {
|
||||
response.close();
|
||||
}
|
||||
// 返回url和订单号
|
||||
return R.ok().put("paramMap" ,paramMap).put("Map",Map);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import com.peanut.modules.pay.weChatPay.enums.WxNotifyType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@Data
|
||||
public class WeChatBasePayData {
|
||||
/**
|
||||
* 商品描述
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 商家订单号,对应 out_trade_no
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 订单金额
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 回调地址
|
||||
*/
|
||||
private WxNotifyType notify;
|
||||
|
||||
private Integer buyOrderId;
|
||||
|
||||
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import com.peanut.modules.pay.weChatPay.enums.WxNotifyType;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@Data
|
||||
public class WeChatBasePayData {
|
||||
/**
|
||||
* 商品描述
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 商家订单号,对应 out_trade_no
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 订单金额
|
||||
*/
|
||||
private BigDecimal price;
|
||||
|
||||
/**
|
||||
* 回调地址
|
||||
*/
|
||||
private WxNotifyType notify;
|
||||
|
||||
private Integer buyOrderId;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,43 +1,43 @@
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
@Data
|
||||
public class WechatDto implements Serializable {
|
||||
|
||||
|
||||
//totalAmount: payItem.realMoney,
|
||||
// customerId:
|
||||
|
||||
private String orderSn;
|
||||
|
||||
private Integer buyOrderId;
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(BigDecimal totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public String getOrderSn() {
|
||||
return orderSn;
|
||||
}
|
||||
|
||||
public void setOrderSn(String orderSn) {
|
||||
this.orderSn = orderSn;
|
||||
}
|
||||
|
||||
public Integer getBuyOrderId() {
|
||||
return buyOrderId;
|
||||
}
|
||||
|
||||
public void setBuyOrderId(Integer buyOrderId) {
|
||||
this.buyOrderId = buyOrderId;
|
||||
}
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
@Data
|
||||
public class WechatDto implements Serializable {
|
||||
|
||||
|
||||
//totalAmount: payItem.realMoney,
|
||||
// customerId:
|
||||
|
||||
private String orderSn;
|
||||
|
||||
private Integer buyOrderId;
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(BigDecimal totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public String getOrderSn() {
|
||||
return orderSn;
|
||||
}
|
||||
|
||||
public void setOrderSn(String orderSn) {
|
||||
this.orderSn = orderSn;
|
||||
}
|
||||
|
||||
public Integer getBuyOrderId() {
|
||||
return buyOrderId;
|
||||
}
|
||||
|
||||
public void setBuyOrderId(Integer buyOrderId) {
|
||||
this.buyOrderId = buyOrderId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +1,79 @@
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
/**
|
||||
* @author
|
||||
* @version 1.0
|
||||
* @description 微信支付退款回调返回的数据
|
||||
* @date
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class WxchatCallbackRefundData {
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
|
||||
/**
|
||||
* 商户退款单号,out_refund_no
|
||||
*/
|
||||
private String refundId;
|
||||
|
||||
/**
|
||||
* 微信支付系统生成的订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 微信支付系统生成的退款订单号
|
||||
*/
|
||||
private String transactionRefundId;
|
||||
|
||||
/**
|
||||
* 退款渠道
|
||||
* ORIGINAL:原路退款
|
||||
* BALANCE:退回到余额
|
||||
* OTHER_BALANCE:原账户异常退到其他余额账户
|
||||
* OTHER_BANKCARD:原银行卡异常退到其他银行卡
|
||||
*/
|
||||
private String channel;
|
||||
|
||||
/**
|
||||
* 退款成功时间
|
||||
* 当前退款成功时才有此返回值
|
||||
*/
|
||||
private Date successTime;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
* 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
|
||||
* SUCCESS:退款成功
|
||||
* CLOSED:退款关闭
|
||||
* PROCESSING:退款处理中
|
||||
* ABNORMAL:退款异常
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private BigDecimal refundMoney;
|
||||
|
||||
|
||||
public Date getSuccessTime() {
|
||||
return successTime;
|
||||
}
|
||||
|
||||
public void setSuccessTime(String successTime) {
|
||||
// Hutool工具包的方法,自动识别一些常用格式的日期字符串
|
||||
this.successTime = DateUtil.parse(successTime);
|
||||
}
|
||||
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
/**
|
||||
* @author
|
||||
* @version 1.0
|
||||
* @description 微信支付退款回调返回的数据
|
||||
* @date
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class WxchatCallbackRefundData {
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
|
||||
/**
|
||||
* 商户退款单号,out_refund_no
|
||||
*/
|
||||
private String refundId;
|
||||
|
||||
/**
|
||||
* 微信支付系统生成的订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 微信支付系统生成的退款订单号
|
||||
*/
|
||||
private String transactionRefundId;
|
||||
|
||||
/**
|
||||
* 退款渠道
|
||||
* ORIGINAL:原路退款
|
||||
* BALANCE:退回到余额
|
||||
* OTHER_BALANCE:原账户异常退到其他余额账户
|
||||
* OTHER_BANKCARD:原银行卡异常退到其他银行卡
|
||||
*/
|
||||
private String channel;
|
||||
|
||||
/**
|
||||
* 退款成功时间
|
||||
* 当前退款成功时才有此返回值
|
||||
*/
|
||||
private Date successTime;
|
||||
|
||||
/**
|
||||
* 退款状态
|
||||
* 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
|
||||
* SUCCESS:退款成功
|
||||
* CLOSED:退款关闭
|
||||
* PROCESSING:退款处理中
|
||||
* ABNORMAL:退款异常
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*/
|
||||
private BigDecimal refundMoney;
|
||||
|
||||
|
||||
public Date getSuccessTime() {
|
||||
return successTime;
|
||||
}
|
||||
|
||||
public void setSuccessTime(String successTime) {
|
||||
// Hutool工具包的方法,自动识别一些常用格式的日期字符串
|
||||
this.successTime = DateUtil.parse(successTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,77 +1,77 @@
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @version 1.0
|
||||
* @description 微信支付成功回调返回的数据
|
||||
* @date
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class WxchatCallbackSuccessData {
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 微信支付系统生成的订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 交易状态
|
||||
* SUCCESS:支付成功
|
||||
* REFUND:转入退款
|
||||
* NOTPAY:未支付
|
||||
* CLOSED:已关闭
|
||||
* REVOKED:已撤销(付款码支付)
|
||||
* USERPAYING:用户支付中(付款码支付)
|
||||
* PAYERROR:支付失败(其他原因,如银行返回失败)
|
||||
*/
|
||||
private String tradestate;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付完成时间
|
||||
*/
|
||||
private Date successTime;
|
||||
|
||||
/**
|
||||
* 交易类型
|
||||
* JSAPI:公众号支付
|
||||
* NATIVE:扫码支付
|
||||
* APP:APP支付
|
||||
* MICROPAY:付款码支付
|
||||
* MWEB:H5支付
|
||||
* FACEPAY:刷脸支付
|
||||
*/
|
||||
private String tradetype;
|
||||
|
||||
/**
|
||||
* 订单总金额
|
||||
*/
|
||||
private BigDecimal totalMoney;
|
||||
|
||||
|
||||
public Date getSuccessTime() {
|
||||
return successTime;
|
||||
}
|
||||
|
||||
public void setSuccessTime(String successTime) {
|
||||
// Hutool工具包的方法,自动识别一些常用格式的日期字符串
|
||||
this.successTime = DateUtil.parse(successTime);
|
||||
}
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.dto;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @version 1.0
|
||||
* @description 微信支付成功回调返回的数据
|
||||
* @date
|
||||
*/
|
||||
@Data
|
||||
@Slf4j
|
||||
public class WxchatCallbackSuccessData {
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String orderId;
|
||||
|
||||
/**
|
||||
* 微信支付系统生成的订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 交易状态
|
||||
* SUCCESS:支付成功
|
||||
* REFUND:转入退款
|
||||
* NOTPAY:未支付
|
||||
* CLOSED:已关闭
|
||||
* REVOKED:已撤销(付款码支付)
|
||||
* USERPAYING:用户支付中(付款码支付)
|
||||
* PAYERROR:支付失败(其他原因,如银行返回失败)
|
||||
*/
|
||||
private String tradestate;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付完成时间
|
||||
*/
|
||||
private Date successTime;
|
||||
|
||||
/**
|
||||
* 交易类型
|
||||
* JSAPI:公众号支付
|
||||
* NATIVE:扫码支付
|
||||
* APP:APP支付
|
||||
* MICROPAY:付款码支付
|
||||
* MWEB:H5支付
|
||||
* FACEPAY:刷脸支付
|
||||
*/
|
||||
private String tradetype;
|
||||
|
||||
/**
|
||||
* 订单总金额
|
||||
*/
|
||||
private BigDecimal totalMoney;
|
||||
|
||||
|
||||
public Date getSuccessTime() {
|
||||
return successTime;
|
||||
}
|
||||
|
||||
public void setSuccessTime(String successTime) {
|
||||
// Hutool工具包的方法,自动识别一些常用格式的日期字符串
|
||||
this.successTime = DateUtil.parse(successTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package com.peanut.modules.pay.weChatPay.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.peanut.common.utils.R;
|
||||
import com.peanut.modules.book.entity.PayWechatOrderEntity;
|
||||
import com.peanut.modules.pay.weChatPay.dto.WechatDto;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
@Service
|
||||
public interface WxpayService extends IService<PayWechatOrderEntity> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.peanut.common.utils.R;
|
||||
import com.peanut.modules.book.entity.PayWechatOrderEntity;
|
||||
import com.peanut.modules.pay.weChatPay.dto.WechatDto;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
|
||||
@Service
|
||||
public interface WxpayService extends IService<PayWechatOrderEntity> {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package com.peanut.modules.pay.weChatPay.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.peanut.modules.book.dao.PayWechatOrderDao;
|
||||
import com.peanut.modules.book.entity.PayWechatOrderEntity;
|
||||
import com.peanut.modules.pay.weChatPay.service.WxpayService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class WxpayServiceImpl extends ServiceImpl<PayWechatOrderDao ,PayWechatOrderEntity> implements WxpayService {
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.peanut.modules.book.dao.PayWechatOrderDao;
|
||||
import com.peanut.modules.book.entity.PayWechatOrderEntity;
|
||||
import com.peanut.modules.pay.weChatPay.service.WxpayService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class WxpayServiceImpl extends ServiceImpl<PayWechatOrderDao ,PayWechatOrderEntity> implements WxpayService {
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
import org.junit.rules.Verifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 将通知参数转化为字符串
|
||||
* @param request
|
||||
* @return
|
||||
*
|
||||
* WechatPayValidatorForRequest
|
||||
*/
|
||||
public static String readData(HttpServletRequest request) {
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
StringBuilder result = new StringBuilder();
|
||||
br = request.getReader();
|
||||
for (String line; (line = br.readLine()) != null; ) {
|
||||
if (result.length() > 0) {
|
||||
result.append("\n");
|
||||
}
|
||||
result.append(line);
|
||||
}
|
||||
return result.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}}}}
|
||||
|
||||
|
||||
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
import org.junit.rules.Verifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
|
||||
/**
|
||||
* 将通知参数转化为字符串
|
||||
* @param request
|
||||
* @return
|
||||
*
|
||||
* WechatPayValidatorForRequest
|
||||
*/
|
||||
public static String readData(HttpServletRequest request) {
|
||||
BufferedReader br = null;
|
||||
try {
|
||||
StringBuilder result = new StringBuilder();
|
||||
br = request.getReader();
|
||||
for (String line; (line = br.readLine()) != null; ) {
|
||||
if (result.length() > 0) {
|
||||
result.append("\n");
|
||||
}
|
||||
result.append(line);
|
||||
}
|
||||
return result.toString();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
} finally {
|
||||
if (br != null) {
|
||||
try {
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}}}}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,150 +1,150 @@
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
||||
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
|
||||
|
||||
/**
|
||||
* @Author:
|
||||
* @Description:
|
||||
**/
|
||||
@Slf4j
|
||||
public class WechatPayValidator {
|
||||
/**
|
||||
* 应答超时时间,单位为分钟
|
||||
*/
|
||||
private static final long RESPONSE_EXPIRED_MINUTES = 5;
|
||||
private final Verifier verifier;
|
||||
private final String requestId;
|
||||
private final String body;
|
||||
|
||||
|
||||
public WechatPayValidator(Verifier verifier, String requestId, String body) {
|
||||
this.verifier = verifier;
|
||||
this.requestId = requestId;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException parameterError(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("parameter error: " + message);
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException verifyFail(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("signature verify fail: " + message);
|
||||
}
|
||||
|
||||
public final boolean validate(HttpServletRequest request) {
|
||||
try {
|
||||
//处理请求参数
|
||||
validateParameters(request);
|
||||
|
||||
//构造验签名串
|
||||
String message = buildMessage(request);
|
||||
|
||||
String serial = request.getHeader(WECHAT_PAY_SERIAL);
|
||||
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||
|
||||
//验签
|
||||
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
|
||||
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
|
||||
serial, message, signature, requestId);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void validateParameters(HttpServletRequest request) {
|
||||
|
||||
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
|
||||
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
|
||||
|
||||
String header = null;
|
||||
for (String headerName : headers) {
|
||||
header = request.getHeader(headerName);
|
||||
if (header == null) {
|
||||
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
//判断请求是否过期
|
||||
String timestampStr = header;
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||
// 拒绝过期请求
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
||||
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildMessage(HttpServletRequest request) {
|
||||
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
private String getResponseBody(CloseableHttpResponse response) throws IOException {
|
||||
HttpEntity entity = response.getEntity();
|
||||
return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 对称解密,异步通知的加密数据
|
||||
* @param resource 加密数据
|
||||
* @param apiV3Key apiV3密钥
|
||||
* @param type 1-支付,2-退款
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> decryptFromResource(String resource,String apiV3Key,Integer type) {
|
||||
|
||||
String msg = type==1?"支付成功":"退款成功";
|
||||
log.info(msg+",回调通知,密文解密");
|
||||
try {
|
||||
//通知数据
|
||||
Map<String, Object> resourceMap = JSONObject.parseObject(resource, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
//数据密文
|
||||
String ciphertext = resourceMap.get("ciphertext").toString();
|
||||
//随机串
|
||||
String nonce = resourceMap.get("nonce").toString();
|
||||
//附加数据
|
||||
String associatedData = resourceMap.get("associated_data").toString();
|
||||
|
||||
log.info("密文: {}", ciphertext);
|
||||
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
|
||||
String resourceStr = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
nonce.getBytes(StandardCharsets.UTF_8),
|
||||
ciphertext);
|
||||
|
||||
log.info(msg+",回调通知,解密结果 明文: {}", resourceStr);
|
||||
return JSONObject.parseObject(resourceStr, new TypeReference<Map<String, Object>>(){});
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("回调参数,解密失败!");
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
||||
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
|
||||
|
||||
/**
|
||||
* @Author:
|
||||
* @Description:
|
||||
**/
|
||||
@Slf4j
|
||||
public class WechatPayValidator {
|
||||
/**
|
||||
* 应答超时时间,单位为分钟
|
||||
*/
|
||||
private static final long RESPONSE_EXPIRED_MINUTES = 5;
|
||||
private final Verifier verifier;
|
||||
private final String requestId;
|
||||
private final String body;
|
||||
|
||||
|
||||
public WechatPayValidator(Verifier verifier, String requestId, String body) {
|
||||
this.verifier = verifier;
|
||||
this.requestId = requestId;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException parameterError(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("parameter error: " + message);
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException verifyFail(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("signature verify fail: " + message);
|
||||
}
|
||||
|
||||
public final boolean validate(HttpServletRequest request) {
|
||||
try {
|
||||
//处理请求参数
|
||||
validateParameters(request);
|
||||
|
||||
//构造验签名串
|
||||
String message = buildMessage(request);
|
||||
|
||||
String serial = request.getHeader(WECHAT_PAY_SERIAL);
|
||||
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||
|
||||
//验签
|
||||
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
|
||||
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
|
||||
serial, message, signature, requestId);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void validateParameters(HttpServletRequest request) {
|
||||
|
||||
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
|
||||
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
|
||||
|
||||
String header = null;
|
||||
for (String headerName : headers) {
|
||||
header = request.getHeader(headerName);
|
||||
if (header == null) {
|
||||
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
//判断请求是否过期
|
||||
String timestampStr = header;
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||
// 拒绝过期请求
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
||||
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildMessage(HttpServletRequest request) {
|
||||
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
private String getResponseBody(CloseableHttpResponse response) throws IOException {
|
||||
HttpEntity entity = response.getEntity();
|
||||
return (entity != null && entity.isRepeatable()) ? EntityUtils.toString(entity) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 对称解密,异步通知的加密数据
|
||||
* @param resource 加密数据
|
||||
* @param apiV3Key apiV3密钥
|
||||
* @param type 1-支付,2-退款
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> decryptFromResource(String resource,String apiV3Key,Integer type) {
|
||||
|
||||
String msg = type==1?"支付成功":"退款成功";
|
||||
log.info(msg+",回调通知,密文解密");
|
||||
try {
|
||||
//通知数据
|
||||
Map<String, Object> resourceMap = JSONObject.parseObject(resource, new TypeReference<Map<String, Object>>() {
|
||||
});
|
||||
//数据密文
|
||||
String ciphertext = resourceMap.get("ciphertext").toString();
|
||||
//随机串
|
||||
String nonce = resourceMap.get("nonce").toString();
|
||||
//附加数据
|
||||
String associatedData = resourceMap.get("associated_data").toString();
|
||||
|
||||
log.info("密文: {}", ciphertext);
|
||||
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
|
||||
String resourceStr = aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
|
||||
nonce.getBytes(StandardCharsets.UTF_8),
|
||||
ciphertext);
|
||||
|
||||
log.info(msg+",回调通知,解密结果 明文: {}", resourceStr);
|
||||
return JSONObject.parseObject(resourceStr, new TypeReference<Map<String, Object>>(){});
|
||||
}catch (Exception e){
|
||||
throw new RuntimeException("回调参数,解密失败!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,97 +1,97 @@
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
|
||||
|
||||
public class WechatPayValidatorForRequest {
|
||||
protected static final Logger log = LoggerFactory.getLogger(WechatPayValidatorForRequest.class);
|
||||
/**
|
||||
* 应答超时时间,单位为分钟
|
||||
*/
|
||||
protected static final long RESPONSE_EXPIRED_MINUTES = 5;
|
||||
protected final Verifier verifier;
|
||||
protected final String body;
|
||||
protected final String requestId;
|
||||
|
||||
public WechatPayValidatorForRequest(Verifier verifier, String body, String requestId) {
|
||||
this.verifier = verifier;
|
||||
this.body = body;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException parameterError(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("parameter error: " + message);
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException verifyFail(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("signature verify fail: " + message);
|
||||
}
|
||||
|
||||
public final boolean validate(HttpServletRequest request) throws IOException {
|
||||
try {
|
||||
validateParameters(request);
|
||||
|
||||
String message = buildMessage(request);
|
||||
String serial = request.getHeader(WECHAT_PAY_SERIAL);
|
||||
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||
|
||||
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
|
||||
|
||||
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
|
||||
serial, message, signature, request.getHeader(REQUEST_ID));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected final void validateParameters(HttpServletRequest request) {
|
||||
|
||||
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
|
||||
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
|
||||
|
||||
String header = null;
|
||||
for (String headerName : headers) {
|
||||
header = request.getHeader(headerName);
|
||||
if (header == null) {
|
||||
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
String timestampStr = header;
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||
// 拒绝过期应答
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
||||
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
protected final String buildMessage(HttpServletRequest request) throws IOException {
|
||||
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.*;
|
||||
|
||||
public class WechatPayValidatorForRequest {
|
||||
protected static final Logger log = LoggerFactory.getLogger(WechatPayValidatorForRequest.class);
|
||||
/**
|
||||
* 应答超时时间,单位为分钟
|
||||
*/
|
||||
protected static final long RESPONSE_EXPIRED_MINUTES = 5;
|
||||
protected final Verifier verifier;
|
||||
protected final String body;
|
||||
protected final String requestId;
|
||||
|
||||
public WechatPayValidatorForRequest(Verifier verifier, String body, String requestId) {
|
||||
this.verifier = verifier;
|
||||
this.body = body;
|
||||
this.requestId = requestId;
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException parameterError(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("parameter error: " + message);
|
||||
}
|
||||
|
||||
protected static IllegalArgumentException verifyFail(String message, Object... args) {
|
||||
message = String.format(message, args);
|
||||
return new IllegalArgumentException("signature verify fail: " + message);
|
||||
}
|
||||
|
||||
public final boolean validate(HttpServletRequest request) throws IOException {
|
||||
try {
|
||||
validateParameters(request);
|
||||
|
||||
String message = buildMessage(request);
|
||||
String serial = request.getHeader(WECHAT_PAY_SERIAL);
|
||||
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||
|
||||
if (!verifier.verify(serial, message.getBytes(StandardCharsets.UTF_8), signature)) {
|
||||
|
||||
throw verifyFail("serial=[%s] message=[%s] sign=[%s], request-id=[%s]",
|
||||
serial, message, signature, request.getHeader(REQUEST_ID));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected final void validateParameters(HttpServletRequest request) {
|
||||
|
||||
// NOTE: ensure HEADER_WECHAT_PAY_TIMESTAMP at last
|
||||
String[] headers = {WECHAT_PAY_SERIAL, WECHAT_PAY_SIGNATURE, WECHAT_PAY_NONCE, WECHAT_PAY_TIMESTAMP};
|
||||
|
||||
String header = null;
|
||||
for (String headerName : headers) {
|
||||
header = request.getHeader(headerName);
|
||||
if (header == null) {
|
||||
throw parameterError("empty [%s], request-id=[%s]", headerName, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
String timestampStr = header;
|
||||
try {
|
||||
Instant responseTime = Instant.ofEpochSecond(Long.parseLong(timestampStr));
|
||||
// 拒绝过期应答
|
||||
if (Duration.between(responseTime, Instant.now()).abs().toMinutes() >= RESPONSE_EXPIRED_MINUTES) {
|
||||
throw parameterError("timestamp=[%s] expires, request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
} catch (DateTimeException | NumberFormatException e) {
|
||||
throw parameterError("invalid timestamp=[%s], request-id=[%s]", timestampStr, requestId);
|
||||
}
|
||||
}
|
||||
|
||||
protected final String buildMessage(HttpServletRequest request) throws IOException {
|
||||
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||
return timestamp + "\n"
|
||||
+ nonce + "\n"
|
||||
+ body + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,148 +1,148 @@
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
||||
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
||||
import lombok.Data;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
|
||||
@Component
|
||||
@Data
|
||||
public class WxPayUtil {
|
||||
|
||||
public static final String mchId = "1612860909"; // 商户号
|
||||
|
||||
public static final String appId = "wx47134a8f15083734"; // appId
|
||||
|
||||
public static final String apiV3Key = "4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF"; // apiV3秘钥
|
||||
//商户私钥路径
|
||||
public static final String privateKeyUrl = "/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem";
|
||||
// public static final String privateKeyUrl = "C:/Users/Administrator/IdeaProjects/peanut_book/src/main/resources/cent/apiclient_key.pem";
|
||||
|
||||
//平台证书路径
|
||||
public static final String wechatPayCertificateUrl = "/usr/local/hs/peanut_book/target/classes/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem";
|
||||
// public static final String wechatPayCertificateUrl = "C:/Users/Administrator/IdeaProjects/peanut_book/src/main/resources/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem";
|
||||
//第一步申请完证书后,在API证书哪里点击管理证书就能看到
|
||||
public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3"; // 商户证书序列号
|
||||
|
||||
private CloseableHttpClient httpClient;
|
||||
|
||||
public void setup() {
|
||||
PrivateKey merchantPrivateKey = null;
|
||||
X509Certificate wechatPayCertificate = null;
|
||||
|
||||
try {
|
||||
merchantPrivateKey = PemUtil.loadPrivateKey(
|
||||
new FileInputStream(privateKeyUrl));
|
||||
wechatPayCertificate = PemUtil.loadCertificate(
|
||||
new FileInputStream(wechatPayCertificateUrl));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
ArrayList<X509Certificate> listCertificates = new ArrayList<>();
|
||||
listCertificates.add(wechatPayCertificate);
|
||||
|
||||
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
|
||||
.withWechatPay(listCertificates);
|
||||
httpClient = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* wxMchid商户号
|
||||
* wxCertno证书编号
|
||||
* wxCertPath证书地址
|
||||
* wxPaternerKey v3秘钥
|
||||
* url 下单地址
|
||||
* body 构造好的消息体
|
||||
*/
|
||||
public JSONObject doPostWexinV3(String url, String body) {
|
||||
if (httpClient == null) {
|
||||
setup();
|
||||
}
|
||||
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.addHeader("Content-Type", "application/json;chartset=utf-8");
|
||||
httpPost.addHeader("Accept", "application/json");
|
||||
try {
|
||||
if (body == null) {
|
||||
throw new IllegalArgumentException("data参数不能为空");
|
||||
}
|
||||
StringEntity stringEntity = new StringEntity(body, "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
// 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
|
||||
HttpResponse httpResponse = httpClient.execute(httpPost);
|
||||
HttpEntity httpEntity = httpResponse.getEntity();
|
||||
|
||||
if (httpResponse.getStatusLine().getStatusCode() == 200) {
|
||||
String jsonResult = EntityUtils.toString(httpEntity);
|
||||
|
||||
return JSONObject.parseObject(jsonResult);
|
||||
} else {
|
||||
System.err.println("微信支付错误信息" + EntityUtils.toString(httpEntity));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
//获取签名
|
||||
public String getSign(String appId, long timestamp, String nonceStr, String pack){
|
||||
String message = buildMessage(appId, timestamp, nonceStr, pack);
|
||||
String paySign= null;
|
||||
try {
|
||||
paySign = sign(message.getBytes("utf-8"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return paySign;
|
||||
}
|
||||
|
||||
private String buildMessage(String appId, long timestamp, String nonceStr, String pack) {
|
||||
return appId + "\n"
|
||||
+ timestamp + "\n"
|
||||
+ nonceStr + "\n"
|
||||
+ pack + "\n";
|
||||
}
|
||||
private String sign(byte[] message) throws Exception{
|
||||
PrivateKey merchantPrivateKey = null;
|
||||
X509Certificate wechatPayCertificate = null;
|
||||
|
||||
try {
|
||||
merchantPrivateKey = PemUtil.loadPrivateKey(
|
||||
new FileInputStream(privateKeyUrl));
|
||||
wechatPayCertificate = PemUtil.loadCertificate(
|
||||
new FileInputStream(wechatPayCertificateUrl));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Signature sign = Signature.getInstance("SHA256withRSA");
|
||||
|
||||
sign.initSign(merchantPrivateKey);
|
||||
sign.update(message);
|
||||
return Base64.getEncoder().encodeToString(sign.sign());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
package com.peanut.modules.pay.weChatPay.util;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
||||
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
||||
import lombok.Data;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
|
||||
@Component
|
||||
@Data
|
||||
public class WxPayUtil {
|
||||
|
||||
public static final String mchId = "1612860909"; // 商户号
|
||||
|
||||
public static final String appId = "wx47134a8f15083734"; // appId
|
||||
|
||||
public static final String apiV3Key = "4aYFklzaULeGlr7oJPZ6rHWKcxjihZUF"; // apiV3秘钥
|
||||
//商户私钥路径
|
||||
public static final String privateKeyUrl = "/usr/local/hs/peanut_book/target/classes/cent/apiclient_key.pem";
|
||||
// public static final String privateKeyUrl = "C:/Users/Administrator/IdeaProjects/peanut_book/src/main/resources/cent/apiclient_key.pem";
|
||||
|
||||
//平台证书路径
|
||||
public static final String wechatPayCertificateUrl = "/usr/local/hs/peanut_book/target/classes/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem";
|
||||
// public static final String wechatPayCertificateUrl = "C:/Users/Administrator/IdeaProjects/peanut_book/src/main/resources/cent/wechatpay_7B5676E3CDF56680D0414A009CE501C844DBE2D6.pem";
|
||||
//第一步申请完证书后,在API证书哪里点击管理证书就能看到
|
||||
public static final String mchSerialNo = "679AECB2F7AC4183033F713828892BA640E4EEE3"; // 商户证书序列号
|
||||
|
||||
private CloseableHttpClient httpClient;
|
||||
|
||||
public void setup() {
|
||||
PrivateKey merchantPrivateKey = null;
|
||||
X509Certificate wechatPayCertificate = null;
|
||||
|
||||
try {
|
||||
merchantPrivateKey = PemUtil.loadPrivateKey(
|
||||
new FileInputStream(privateKeyUrl));
|
||||
wechatPayCertificate = PemUtil.loadCertificate(
|
||||
new FileInputStream(wechatPayCertificateUrl));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
ArrayList<X509Certificate> listCertificates = new ArrayList<>();
|
||||
listCertificates.add(wechatPayCertificate);
|
||||
|
||||
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(mchId, mchSerialNo, merchantPrivateKey)
|
||||
.withWechatPay(listCertificates);
|
||||
httpClient = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* wxMchid商户号
|
||||
* wxCertno证书编号
|
||||
* wxCertPath证书地址
|
||||
* wxPaternerKey v3秘钥
|
||||
* url 下单地址
|
||||
* body 构造好的消息体
|
||||
*/
|
||||
public JSONObject doPostWexinV3(String url, String body) {
|
||||
if (httpClient == null) {
|
||||
setup();
|
||||
}
|
||||
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.addHeader("Content-Type", "application/json;chartset=utf-8");
|
||||
httpPost.addHeader("Accept", "application/json");
|
||||
try {
|
||||
if (body == null) {
|
||||
throw new IllegalArgumentException("data参数不能为空");
|
||||
}
|
||||
StringEntity stringEntity = new StringEntity(body, "utf-8");
|
||||
httpPost.setEntity(stringEntity);
|
||||
// 直接执行execute方法,官方会自动处理签名和验签,并进行证书自动更新
|
||||
HttpResponse httpResponse = httpClient.execute(httpPost);
|
||||
HttpEntity httpEntity = httpResponse.getEntity();
|
||||
|
||||
if (httpResponse.getStatusLine().getStatusCode() == 200) {
|
||||
String jsonResult = EntityUtils.toString(httpEntity);
|
||||
|
||||
return JSONObject.parseObject(jsonResult);
|
||||
} else {
|
||||
System.err.println("微信支付错误信息" + EntityUtils.toString(httpEntity));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
//获取签名
|
||||
public String getSign(String appId, long timestamp, String nonceStr, String pack){
|
||||
String message = buildMessage(appId, timestamp, nonceStr, pack);
|
||||
String paySign= null;
|
||||
try {
|
||||
paySign = sign(message.getBytes("utf-8"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return paySign;
|
||||
}
|
||||
|
||||
private String buildMessage(String appId, long timestamp, String nonceStr, String pack) {
|
||||
return appId + "\n"
|
||||
+ timestamp + "\n"
|
||||
+ nonceStr + "\n"
|
||||
+ pack + "\n";
|
||||
}
|
||||
private String sign(byte[] message) throws Exception{
|
||||
PrivateKey merchantPrivateKey = null;
|
||||
X509Certificate wechatPayCertificate = null;
|
||||
|
||||
try {
|
||||
merchantPrivateKey = PemUtil.loadPrivateKey(
|
||||
new FileInputStream(privateKeyUrl));
|
||||
wechatPayCertificate = PemUtil.loadCertificate(
|
||||
new FileInputStream(wechatPayCertificateUrl));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Signature sign = Signature.getInstance("SHA256withRSA");
|
||||
|
||||
sign.initSign(merchantPrivateKey);
|
||||
sign.update(message);
|
||||
return Base64.getEncoder().encodeToString(sign.sign());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user