赞
踩
接入微信支付前需要再微信开放平台、公众号平台申请appid和商家号并绑定
微信支付申请操作流程
<!-- 微信支付SDK -->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.5</version>
</dependency>
public class WechatPayConstant {
public static final String CANCEL_PAY_URL="/v3/pay/transactions/out-trade-no/%s/close";
public static final String CREATE_PAY_URL="/v3/pay/transactions/native";
public static final String QUERY_PAY_URL="/v3/pay/transactions/out-trade-no/%s?mchid=%s";
public static final String CREATE_REFUND_URL="/v3/refund/domestic/refunds";
public static final String QUERY_REFUND_URL="/v3/refund/domestic/refunds/%s";
public static final String TRADE_BILL_URL="/v3/bill/tradebill?bill_date=%s&bill_type=%s";
public static final String FLOW_BILL_URL="/v3/bill/fundflowbill?bill_date=%s";
public static final String TRADE_STATE_SUCCESS="SUCCESS";
public static final String REFUND_STATE_SUCCESS="SUCCESS";
}
/**
* @Author:
* @Description:
**/
@Component
@Data
@Slf4j
@ConfigurationProperties(prefix = "wxpay")
public class WechatPayConfig {
/**
* 应用编号
*/
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;
/**
* key.pem
*/
private String privateKey;
/**
* 证书序列号
*/
private String merchantSerialNumber;
/**
* 微信支付V3-url前缀
*/
private String baseUrl;
public PrivateKey getPrivateKey(String keyPemPath){
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(keyPemPath);
if(inputStream == null){
throw new RuntimeException("私钥文件不存在");
}
return PemUtil.loadPrivateKey(inputStream);
}
/**
* 获取签名验证器
*/
public Verifier getVerifier() {
// 获取商户私钥
PrivateKey privateKey = getPrivateKey(keyPemPath);
// 私钥签名对象
PrivateKeySigner privateKeySigner = new PrivateKeySigner(merchantSerialNumber, privateKey);
// 身份认证对象
WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);
// 获取证书管理器实例
CertificatesManager certificatesManager = CertificatesManager.getInstance();
try {
// 向证书管理器增加需要自动更新平台证书的商户信息
certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
} catch (IOException | GeneralSecurityException | HttpCodeException e) {
e.printStackTrace();
}
try {
return certificatesManager.getVerifier(mchId);
} catch (NotFoundException e) {
e.printStackTrace();
throw new RuntimeException("获取签名验证器失败");
}
}
/**
* 给容器中加入WechatPay的HttpClient,虽然它是WechatPay的,
* 但可以用它给任何外部发请求,因为它只对发给WechatPay的请求做处理而不对发给别的的请求做处理.
*/
@Bean
public CloseableHttpClient httpClient(){
//私钥
PrivateKey merchantPrivateKey = getPrivateKey(keyPemPath);
Verifier verifier = getVerifier();
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId,merchantSerialNumber,merchantPrivateKey)
.withValidator(new WechatPay2Validator(verifier));
// 不需要签名验证
// withValidator(response->true);
CloseableHttpClient httpClient = builder.build();
return httpClient;
}
}
public void genQRCode(HttpServletResponse httpResponse, PayDTO payDTO) throws Exception {
String key = RedisContents.WX_QR_PREFIX + payDTO.getOrderId();
SimpleDateFormat sdfTime = commonUtils.getSdfTime();
Date openDate = sdfTime.parse(payDTO.getTimeExpire());
long nowTime = new Date().getTime();
long openTime = openDate.getTime();
long sur = openTime - nowTime;
String qrCode = (String) redisTemplate.opsForValue().get(key);
if (qrCode != null){
QrCodeUtil.generate(qrCode, 300, 300,"png", httpResponse.getOutputStream());
return ;
}
//请求构造
HttpPost httpPost = new HttpPost(wechatPayConfig.getBaseUrl()
+ WechatPayConstant.CREATE_PAY_URL);
String orderId = payDTO.getOrderId();
Map<String, Object> bodyData = new HashMap<>();
bodyData.put("appid", wechatPayConfig.getAppId());
bodyData.put("mchid", wechatPayConfig.getMchId());
bodyData.put("description", payDTO.getSubject());
bodyData.put("out_trade_no", orderId);
bodyData.put("notify_url", wechatPayConfig.getNotifyUrl());
// 设置绝对超时时间,即开标时间
SimpleDateFormat wxpaySdfDate = commonUtils.getWxpaySdfDate();
bodyData.put("time_expire",wxpaySdfDate.format(openDate));
// 金额单位是分
HashMap<String,Integer> amount = new HashMap<>();
//单位是分
BigDecimal bigDecimal = new BigDecimal(payDTO.getOrderAmount()).multiply(BigDecimal.valueOf(100));
amount.put("total",bigDecimal.intValue());
bodyData.put("amount",amount);
String jsonReqData = new Gson().toJson(bodyData);
StringEntity entity = new StringEntity(jsonReqData,"utf-8");
entity.setContentType("application/json");
httpPost.setEntity(entity);
//请求头
httpPost.setHeader("Accept","application/json");
//完成签名并执行请求
CloseableHttpResponse wxpayResponse = httpClient.execute(httpPost);
Map<String,String> dataMap = null;
try{
int statusCode = wxpayResponse.getStatusLine().getStatusCode();
//成功
if(statusCode==200){
String body= EntityUtils.toString(wxpayResponse.getEntity());
dataMap = new Gson().fromJson(body,HashMap.class);
String qrCodeUrl = dataMap.get("code_url");
redisTemplate.opsForValue().set(key,qrCodeUrl,sur, TimeUnit.MILLISECONDS);
QrCodeUtil.generate(qrCodeUrl, 300, 300,"png", httpResponse.getOutputStream());
}
//失败
else{
if(statusCode!=204){
String body = EntityUtils.toString(wxpayResponse.getEntity());
log.error(body);
return ;
}
}
}
finally{
wxpayResponse.close();
}
}
@PostMapping("/wxpay/notify")
public HashMap<String,String> wxPayNotify(@RequestBody Map<String,Object> signalRes, HttpServletResponse response){
log.debug("收到微信回调");
try{
//验签,用密文解密出明文
Map<String,String> resource = (Map<String,String>)signalRes.get("resource");
String ciphertext = resource.get("ciphertext");
String associatedData = resource.get("associated_data");
String nonce = resource.get("nonce");
String plainText = new AesUtil(wechatPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8))
.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8)
,nonce.getBytes(StandardCharsets.UTF_8)
,ciphertext);
//转换
HashMap<String,Object> data = new Gson().fromJson(plainText,HashMap.class);
// 查询是否支付
LambdaQueryWrapper<PaymentInfo> pWrapper = new LambdaQueryWrapper<>();
pWrapper.eq(PaymentInfo::getTradeNo,(String)data.get("transaction_id"));
PaymentInfo paymentInfoDb = paymentService.getOne(pWrapper);
if (paymentInfoDb != null){
return null;
}
synchronized(this){
// 保存支付信息
PaymentInfo paymentInfo = new PaymentInfo();
long id = commonUtils.nextId("wx:pay");
paymentInfo.setId(id);
paymentInfo.setOrderId((String)data.get("out_trade_no"));
paymentInfo.setTradeNo((String)data.get("transaction_id"));
paymentInfo.setPaymentType("wechat");
paymentInfo.setTradeType((String)data.get("trade_type"));
paymentInfo.setTradeState((String)data.get("trade_state"));
// 获取金额
LinkedTreeMap<String,Object> amountMap = (LinkedTreeMap<String, Object>) data.get("amount");
BigDecimal totalAmount = new BigDecimal((Double) amountMap.get("total"))
.divide(new BigDecimal(100))
.setScale(2, BigDecimal.ROUND_DOWN);
paymentInfo.setTotalAmount(totalAmount);
Date date = new Date();
paymentInfo.setTradeTime(date);
paymentInfo.setCreateTime(date);
paymentInfo.setUpdateTime(date);
//交易类型(扫码 刷脸等等)
paymentInfo.setTradeType((String)data.get("trade_type"));
paymentInfo.setTradeState((String)data.get("trade_state"));
//存放全部数据(json)以备不时之需
paymentInfo.setContent(plainText);
paymentService.save(paymentInfo);
log.info("订单{}的支付记录添加成功,支付记录id为{}.",(String)data.get("out_trade_no"),paymentInfo.getId());
}
return null;
} catch(Exception e){
log.error(e.getMessage());
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
HashMap<String,String> map=new HashMap<>();
map.put("code","FAIL");
map.put("message","支付失败");
return map;
}
}
public boolean wechatRefund(String orderId,String tradeNo,String refundAmount) throws IOException {
// 请求构造
HttpPost httpPost = new HttpPost(wechatPayConfig.getBaseUrl()
+ WechatPayConstant.CREATE_REFUND_URL );
// 构造请求体
HashMap<String,Object> reqData = new HashMap<>();
reqData.put("out_trade_no",orderId);//订单编号
reqData.put("out_refund_no",tradeNo);//退款单编号
reqData.put("reason","xxxx");//退款原因
reqData.put("notify_url",wechatPayConfig.getRefundNotifyUrl());//退款通知地址
HashMap<String,Object> amount = new HashMap<>();
// 单位是分
BigDecimal bigDecimal = new BigDecimal(refundAmount)
.multiply(BigDecimal.valueOf(100));
//退款金额
amount.put("refund",bigDecimal.intValue());
//原订单金额
amount.put("total",bigDecimal.intValue());
amount.put("currency","CNY");//币种
reqData.put("amount",amount);
//将参数转换成json字符串
String jsonData = new Gson().toJson(reqData);
log.info("请求参数 ===> {}"+jsonData);
StringEntity entity = new StringEntity(jsonData,"utf-8");
httpPost.setEntity(entity);//将请求报文放入请求对象
//请求头
httpPost.setHeader("content-type","application/json");
httpPost.setHeader("Accept","application/json");//设置响应报文格式
//完成签名并执行请求
CloseableHttpResponse response = httpClient.execute(httpPost);
try{
//解析响应结果
String bodyAsString = EntityUtils.toString(response.getEntity());
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode == 200){
log.info("成功, 退款返回结果 = "+bodyAsString);
return true;
} else{
if(statusCode!=204){
log.warn("退款异常:"+bodyAsString);
}
return false;
}
}
finally{
response.close();
}
}
接入支付宝支付前需要在支付宝开放平台创建应用和申请商户,并将appid和商家号并绑定
支付宝支付申请流程
<!-- 支付宝支付SDK -->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<!-- 对接正式环境需要这个版本及以上-->
<version>4.38.183.ALL</version>
</dependency>
支付宝接口参数
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayProperty {
// 应用id
private String appId;
// 应用私钥
private String appPrivateKey;
// 支付宝公钥
private String alipayPublicKey;
// 支付宝异步通知地址
private String notifyUrl;
// 支付宝网关
private String gatewayUrl;
}
配置AlipayClient
/**
* 支付宝支付客户端
* 验签时 charset要一致,不然会验签失败
* @return
*/
@Bean
public AlipayClient alipayClient(){
return new DefaultAlipayClient(
alipayProperty.getGatewayUrl(),
alipayProperty.getAppId(),
alipayProperty.getAppPrivateKey(),
"json",
AlipayConstants.CHARSET_UTF8,
alipayProperty.getAlipayPublicKey(),
AlipayConstants.SIGN_TYPE_RSA2
);
}
/**
* 生成支付二维码
* @return
*/
public void genQRCode(HttpServletResponse httpResponse,PayDTO payDTO) throws Exception {
String key = RedisContents.ZFB_QR_PREFIX + payDTO.getOrderId();
String timeExpire = payDTO.getTimeExpire();
Date nowDate = new Date();
SimpleDateFormat sdfTime = commonUtils.getSdfTime();
Date openDate = sdfTime.parse(timeExpire);
long nowTime = nowDate.getTime();
long openTime = openDate.getTime();
String qrCode = (String) redisTemplate.opsForValue().get(key);
if (qrCode != null){
QrCodeUtil.generate(qrCode, 300, 300,"png", httpResponse.getOutputStream());
return ;
}
AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
// 异步通知支付结果
request.setNotifyUrl(alipayProperty.getNotifyUrl());
//将商品的属性填写进去
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", payDTO.getOrderId());
bizContent.put("total_amount", payDTO.getOrderAmount());
bizContent.put("subject", payDTO.getSubject());
// 设置绝对超时时间,即开标时间
bizContent.put("time_expire",timeExpire);
request.setBizContent(bizContent.toString());
AlipayTradePrecreateResponse response = null;
try {
response = alipayClient.execute(request);
} catch (AlipayApiException e) {
throw new RuntimeException(e);
}
if(response.isSuccess()){
log.info("++++++++++++++++调用成功++++++++++++++++");
String respQrCode = response.getQrCode();
// 保存respQrCode到redis
redisTemplate.opsForValue().set(key,respQrCode,sur, TimeUnit.MILLISECONDS);
// 生成支付二维码并输出到响应
QrCodeUtil.generate(respQrCode, 300, 300,"png", httpResponse.getOutputStream());
// JSAPI支付宝收银台
// String form = "";
// form = alipayClient.pageExecute(request).getBody();
// //设置响应结果,将返回的内容写出到浏览器
// httpResponse.setContentType("text/html;charset=UTF-8");
// httpResponse.getWriter().write(form);//直接将完整的表单html输出到页面
// httpResponse.getWriter().flush();
// httpResponse.getWriter().close();
}
/**
* 支付宝异步回调通知
* @return
*/
@AutoLog(value = "支付宝支付通知")
@PostMapping("/alipay/notify")
public void alipayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
log.info("+++++++++++支付宝异步通知+++++++++++");
String outTradeNo = request.getParameter("out_trade_no");
String tradeNo = request.getParameter("trade_no");
String totalAmount = request.getParameter("total_amount");
// TODO 业务处理
// 商户订单号(out_trade_no):商户网站生成的唯一订单号。
// 支付宝交易号(trade_no):支付宝交易凭证号。
// 交易状态(trade_status):交易的当前状态,包括支付成功、支付失败等。
// 通知时间(notify_time):支付宝服务器发送通知的时间。
// 交易金额(total_amount):本次交易支付的订单金额。
Map<String, String[]> requestParams = request.getParameterMap();
PrintWriter out = response.getWriter();
try {
// 这里首先验签 验证是支付宝发来的请求
HashMap<String, String> params = new HashMap<>();
for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {
String name = iter.next();
String[] values = requestParams.get(name);
String valueStr = "";
for (int i = 0; i < values.length; i++) {
valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
}
// valueStr = new String(valueStr.getBytes(),"UTF-8");
params.put(name, valueStr);
}
// params.put("subject","保费");
log.info("sign params:{}",params);
log.info("alipay params:{}",alipayProperty);
// 验证签名,确保是支付宝发送的
// 验签失败可以原因
// 1、参数2为支付宝公钥
// 2、字符编码不统一 AlipayClient的字符编码要与参数3的字符编码统一
boolean signVerified = AlipaySignature.rsaCheckV1(params,
alipayProperty.getAlipayPublicKey(),
AlipayConstants.CHARSET_UTF8,
AlipayConstants.SIGN_TYPE_RSA2);
if (!signVerified) {
log.error("验签失败");
out.print("fail");
return;
}
// 查询是否支付
LambdaQueryWrapper<PaymentInfo> pWrapper = new LambdaQueryWrapper<>();
pWrapper.eq(PaymentInfo::getTradeNo,tradeNo);
PaymentInfo paymentInfoDb = paymentService.getOne(pWrapper);
if (paymentInfoDb != null){
return;
}
// 保存支付信息
PaymentInfo paymentInfo = new PaymentInfo();
long id = commonUtils.nextId("ali:pay");
paymentInfo.setId(id);
paymentInfo.setOrderId(outTradeNo);
paymentInfo.setTradeNo(tradeNo);
paymentInfo.setPaymentType("alipay");
paymentInfo.setTradeType("NATIVE");
paymentInfo.setTradeState(request.getParameter("trade_status"));
paymentInfo.setTotalAmount(new BigDecimal(totalAmount));
Date date = new Date();
paymentInfo.setTradeTime(date);
paymentInfo.setCreateTime(date);
paymentInfo.setUpdateTime(date);
paymentService.save(paymentInfo);
}catch (Exception e){
}
// 获取支付订单号
log.info("异步通知参数outTradeNo:{}",outTradeNo);
log.info("异步通知参数tradeNo:{}",tradeNo);
}
/**
* 支付宝退款
* @return
*/
public boolean alipayRefund(String tradeNo,String refundAmount) {
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
// TODO 业务代码查询订单信息
JSONObject bizContent = new JSONObject();
bizContent.put("trade_no", tradeNo);
bizContent.put("refund_amount", refundAmount);
bizContent.put("out_request_no", "HZ01RF001");
request.setBizContent(bizContent.toString());
try{
AlipayTradeRefundResponse response = alipayClient.execute(request);
if(response.isSuccess()){
System.out.println("退款成功");
return true;
} else {
System.out.println("退款失败");
return false;
}
}catch (Exception e){
e.printStackTrace();
return false;
}
}
1、解决微信支付 java.security.InvalidKeyException: Illegal key size
2、关于支付宝、微信支付成功回调验签失败问题,仔细检查公钥,字符集编码,参数等
…
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。