退款
This commit is contained in:
6
pom.xml
6
pom.xml
@@ -36,7 +36,11 @@
|
|||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||||
|
<artifactId>wechatpay-java</artifactId>
|
||||||
|
<version>0.2.11</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
|
|||||||
8
src/main/java/com/yxt/pay/api/refund/Refund.java
Normal file
8
src/main/java/com/yxt/pay/api/refund/Refund.java
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package com.yxt.pay.api.refund;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/25 15:03
|
||||||
|
*/
|
||||||
|
public class Refund {
|
||||||
|
}
|
||||||
43
src/main/java/com/yxt/pay/api/refund/RefundOrder.java
Normal file
43
src/main/java/com/yxt/pay/api/refund/RefundOrder.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package com.yxt.pay.api.refund;
|
||||||
|
|
||||||
|
import com.yxt.common.base.utils.StringRandom;
|
||||||
|
import com.yxt.common.core.domain.BaseEntity;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/28 9:47
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RefundOrder extends BaseEntity {
|
||||||
|
private String outTradeNo;//订单编号
|
||||||
|
private int source;//来源 0云菜窖
|
||||||
|
private String name;//商品名
|
||||||
|
private String amount;//金额
|
||||||
|
private String openId;//
|
||||||
|
private String returnUrl;//业务回调
|
||||||
|
private String orderSid;//支付订单sid
|
||||||
|
private String reason;//退款原因
|
||||||
|
|
||||||
|
|
||||||
|
public String getTime() {
|
||||||
|
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
|
||||||
|
return format.format(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefundOrder(int source) {
|
||||||
|
String Randomstr = StringRandom.getRandomString(15);
|
||||||
|
if (source == 0) {//云菜窖
|
||||||
|
outTradeNo = "YCJ" + getTime() + Randomstr;
|
||||||
|
|
||||||
|
} else if (source == 1) {
|
||||||
|
|
||||||
|
} else if (source == 2) {
|
||||||
|
|
||||||
|
}
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main/java/com/yxt/pay/api/refund/RefundOrderDto.java
Normal file
20
src/main/java/com/yxt/pay/api/refund/RefundOrderDto.java
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package com.yxt.pay.api.refund;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/28 9:57
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RefundOrderDto {
|
||||||
|
private String outTradeNo;//订单编号
|
||||||
|
private int source;//来源 0云菜窖
|
||||||
|
private String name;//商品名
|
||||||
|
private String amount;//金额
|
||||||
|
private String openId;//
|
||||||
|
private String returnUrl;//业务回调
|
||||||
|
private String orderSid;//支付订单sid
|
||||||
|
private String reason;//退款原因
|
||||||
|
private String state;//1退款申请 2退款通过
|
||||||
|
}
|
||||||
19
src/main/java/com/yxt/pay/api/refund/RefundOrderVo.java
Normal file
19
src/main/java/com/yxt/pay/api/refund/RefundOrderVo.java
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package com.yxt.pay.api.refund;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/28 9:57
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class RefundOrderVo {
|
||||||
|
private String outTradeNo;//订单编号
|
||||||
|
private String source;//来源 0云菜窖
|
||||||
|
private String name;//商品名
|
||||||
|
private String amount;//金额
|
||||||
|
private String openId;//
|
||||||
|
private String returnUrl;//业务回调
|
||||||
|
private String orderSid;//支付订单sid
|
||||||
|
private String reason;//退款原因
|
||||||
|
}
|
||||||
53
src/main/java/com/yxt/pay/api/refund/WXRefundOrderReqVo.java
Normal file
53
src/main/java/com/yxt/pay/api/refund/WXRefundOrderReqVo.java
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package com.yxt.pay.api.refund;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/25 15:49
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WXRefundOrderReqVo {
|
||||||
|
/**
|
||||||
|
* 微信支付订单号,微信支付订单号和商家订单号二选一
|
||||||
|
*/
|
||||||
|
// @Schema(description = "微信支付订单号")
|
||||||
|
private String transactionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商家订单号,对应 out_trade_no,
|
||||||
|
*/
|
||||||
|
// @Schema(description = "商家订单号")
|
||||||
|
private String orderId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户退款单号,对应out_refund_no
|
||||||
|
*/
|
||||||
|
// @Schema(description = "商户退款单号")
|
||||||
|
private String outRefundNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款原因,选填
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款原因")
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回调地址
|
||||||
|
*/
|
||||||
|
// @Schema(description = "回调地址")
|
||||||
|
private String notify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款金额
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款金额")
|
||||||
|
private Integer refundMoney;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原订单金额,必填
|
||||||
|
*/
|
||||||
|
// @Schema(description = "原订单金额")
|
||||||
|
private Integer totalMoney;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package com.yxt.pay.api.refund;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/25 15:51
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class WXRefundOrderRespVo {
|
||||||
|
/**
|
||||||
|
* 微信支付退款号
|
||||||
|
*/
|
||||||
|
// @Schema(description = "微信支付退款号")
|
||||||
|
private String refundId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户退款单号
|
||||||
|
*/
|
||||||
|
// @Schema(description = "商户退款单号")
|
||||||
|
private String outRefundNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信支付订单号
|
||||||
|
*/
|
||||||
|
// @Schema(description = "微信支付订单号")
|
||||||
|
private String transactionId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商户订单号
|
||||||
|
*/
|
||||||
|
// @Schema(description = "商户订单号")
|
||||||
|
private String outTradeNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款渠道
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款渠道")
|
||||||
|
private String channel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款入账账户
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款入账账户")
|
||||||
|
private String userReceivedAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款成功时间
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款成功时间")
|
||||||
|
private String successTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款创建时间
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款创建时间")
|
||||||
|
private String createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退款状态
|
||||||
|
*/
|
||||||
|
// @Schema(description = "退款状态")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 资金账户
|
||||||
|
*/
|
||||||
|
// @Schema(description = "资金账户")
|
||||||
|
private String fundsAccount;
|
||||||
|
|
||||||
|
}
|
||||||
13
src/main/java/com/yxt/pay/biz/refund/WxRefundMapper.java
Normal file
13
src/main/java/com/yxt/pay/biz/refund/WxRefundMapper.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package com.yxt.pay.biz.refund;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.yxt.pay.api.refund.RefundOrder;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/25 15:02
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface WxRefundMapper extends BaseMapper<RefundOrder> {
|
||||||
|
}
|
||||||
15
src/main/java/com/yxt/pay/biz/refund/WxRefundMapper.xml
Normal file
15
src/main/java/com/yxt/pay/biz/refund/WxRefundMapper.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
|
<mapper namespace="com.yxt.pay.biz.refund.WxRefundMapper">
|
||||||
|
<select id="selectBySn" resultType="com.yxt.pay.api.refund.Refund">
|
||||||
|
select *
|
||||||
|
from pay_order
|
||||||
|
where outTradeNo = #{out_trade_no}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectOrderList" resultType="com.yxt.pay.api.refund.Refund">
|
||||||
|
select *
|
||||||
|
from pay_order
|
||||||
|
where state = 1
|
||||||
|
</select>
|
||||||
|
</mapper>
|
||||||
49
src/main/java/com/yxt/pay/biz/refund/WxRefundRest.java
Normal file
49
src/main/java/com/yxt/pay/biz/refund/WxRefundRest.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package com.yxt.pay.biz.refund;
|
||||||
|
|
||||||
|
import com.yxt.common.core.domain.BaseEntity;
|
||||||
|
import com.yxt.common.core.result.ResultBean;
|
||||||
|
import com.yxt.pay.api.refund.RefundOrder;
|
||||||
|
import com.yxt.pay.api.refund.RefundOrderDto;
|
||||||
|
import com.yxt.pay.api.refund.WXRefundOrderReqVo;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/25 15:02
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("refund")
|
||||||
|
public class WxRefundRest {
|
||||||
|
@Autowired
|
||||||
|
WxRefundService refundService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/createRefundOrder" )
|
||||||
|
public ResultBean createRefundOrder(@RequestBody RefundOrderDto refundOrderDto) {
|
||||||
|
return refundService.createRefundOrder(refundOrderDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/reviewRefund" )
|
||||||
|
public ResultBean reviewRefundById(@RequestParam String orderSid) throws Exception {
|
||||||
|
return refundService.refund(orderSid);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 微信退款回调
|
||||||
|
* @return R
|
||||||
|
*/
|
||||||
|
// @Operation(summary = "微信退款回调" , description = "微信退款回调" )
|
||||||
|
// @SysLog("微信退款回调" )
|
||||||
|
@PostMapping("/payNotify")
|
||||||
|
public ResultBean refundPayNotify(HttpServletRequest request) throws Exception {
|
||||||
|
ResultBean rb=new ResultBean();
|
||||||
|
refundService.refundNotify(request);//注意:回调接口需要暴露到公网上,且要放开token验证
|
||||||
|
return rb.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
365
src/main/java/com/yxt/pay/biz/refund/WxRefundService.java
Normal file
365
src/main/java/com/yxt/pay/biz/refund/WxRefundService.java
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
package com.yxt.pay.biz.refund;
|
||||||
|
import cn.hutool.core.bean.BeanUtil;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.wechat.pay.java.core.Config;
|
||||||
|
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
|
||||||
|
import com.wechat.pay.java.core.exception.ServiceException;
|
||||||
|
import com.wechat.pay.java.core.notification.NotificationConfig;
|
||||||
|
import com.wechat.pay.java.core.notification.NotificationParser;
|
||||||
|
import com.wechat.pay.java.core.notification.RequestParam;
|
||||||
|
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction;
|
||||||
|
import com.wechat.pay.java.service.refund.RefundService;
|
||||||
|
import com.wechat.pay.java.service.refund.model.*;
|
||||||
|
import com.yxt.common.base.service.MybatisBaseService;
|
||||||
|
import com.yxt.common.core.result.ResultBean;
|
||||||
|
import com.yxt.pay.api.order.PayOrder;
|
||||||
|
import com.yxt.pay.api.refund.RefundOrder;
|
||||||
|
import com.yxt.pay.api.refund.RefundOrderDto;
|
||||||
|
import com.yxt.pay.api.refund.WXRefundOrderReqVo;
|
||||||
|
import com.yxt.pay.api.refund.WXRefundOrderRespVo;
|
||||||
|
import com.yxt.pay.api.wxpay.WxPayVo;
|
||||||
|
import com.yxt.pay.biz.order.OrderService;
|
||||||
|
import com.yxt.pay.utils.UrlComponent;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.servlet.ServletInputStream;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.wechat.pay.java.core.http.Constant.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wangpengfei
|
||||||
|
* @date 2024/3/25 15:02
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class WxRefundService extends MybatisBaseService<WxRefundMapper,RefundOrder> {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UrlComponent urlComponent;
|
||||||
|
@Autowired
|
||||||
|
private OrderService orderService;
|
||||||
|
@Value("${wx.api-key}")
|
||||||
|
String apiKey;
|
||||||
|
@Value("${wx.mch-serial-no}")
|
||||||
|
String mchSerialNo;
|
||||||
|
@Value("${wx.key-path}")
|
||||||
|
String keyPath;
|
||||||
|
@Value("${wx.mch-id}")
|
||||||
|
String mchId;
|
||||||
|
|
||||||
|
public static int source;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ResultBean createRefundOrder(RefundOrderDto refundOrderDto){
|
||||||
|
ResultBean rb=new ResultBean();
|
||||||
|
RefundOrder refundOrder=new RefundOrder(refundOrderDto.getSource());
|
||||||
|
BeanUtil.copyProperties(refundOrderDto,refundOrder,"id","sid");
|
||||||
|
baseMapper.insert(refundOrder);
|
||||||
|
return rb.success().setData(refundOrder);
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 功能描述:
|
||||||
|
* 〈微信退款〉
|
||||||
|
* @Param: [req]
|
||||||
|
* @Return: [void]
|
||||||
|
* @Author: whl
|
||||||
|
* @Date: 2023/10/12 15:46
|
||||||
|
*/
|
||||||
|
public ResultBean refund(String orderSid) throws Exception {
|
||||||
|
ResultBean rb = ResultBean.fireFail();
|
||||||
|
try {
|
||||||
|
PayOrder payOrder=orderService.getOne(new QueryWrapper<PayOrder>().eq("sid",orderSid));
|
||||||
|
WxPayVo wxPayVo=new WxPayVo(payOrder.getSource());
|
||||||
|
source=payOrder.getSource();
|
||||||
|
// 使用自动更新平台证书的RSA配置
|
||||||
|
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
|
||||||
|
Config config =
|
||||||
|
new RSAAutoCertificateConfig.Builder()
|
||||||
|
.merchantId(mchId)
|
||||||
|
//使用 SDK 不需要计算请求签名和验证应答签名
|
||||||
|
// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
|
||||||
|
.privateKeyFromPath(keyPath)
|
||||||
|
.merchantSerialNumber(mchSerialNo)
|
||||||
|
.apiV3Key(apiKey)
|
||||||
|
.build();
|
||||||
|
// 构建退款service
|
||||||
|
RefundService service = new RefundService.Builder().config(config).build();
|
||||||
|
// request.setXxx(val)设置所需参数,具体参数可见Request定义
|
||||||
|
//构建退款请求
|
||||||
|
CreateRequest request = new CreateRequest();
|
||||||
|
//构建订单金额信息
|
||||||
|
AmountReq amountReq = new AmountReq();
|
||||||
|
// PayOrder payOrder=orderService.getOne(new QueryWrapper<PayOrder>().eq("sid",orderSid));
|
||||||
|
RefundOrder refundOrder=baseMapper.selectOne(new QueryWrapper<RefundOrder>().eq("orderSid",payOrder.getSid()));
|
||||||
|
//退款金额
|
||||||
|
amountReq.setRefund(Long.valueOf(payOrder.getTotalTee()));
|
||||||
|
// amountReq.setRefund(Long.valueOf("1"));
|
||||||
|
//原订单金额
|
||||||
|
amountReq.setTotal(Long.valueOf(payOrder.getTotalTee()));
|
||||||
|
// amountReq.setTotal(Long.valueOf("1"));
|
||||||
|
//货币类型(默认人民币)
|
||||||
|
amountReq.setCurrency("CNY");
|
||||||
|
request.setAmount(amountReq);
|
||||||
|
//商户退款单号
|
||||||
|
request.setOutRefundNo(String.valueOf(payOrder.getOutTradeNo()));
|
||||||
|
// request.setOutRefundNo(String.valueOf("ceshi"));
|
||||||
|
request.setOutTradeNo(refundOrder.getOutTradeNo());
|
||||||
|
// request.setOutTradeNo("ceshi");
|
||||||
|
request.setReason(refundOrder.getReason());
|
||||||
|
// request.setReason("ceshi");
|
||||||
|
//退款通知回调地址
|
||||||
|
// request.setNotifyUrl(wxPayV3Bean.getRefundNotifyUrl());
|
||||||
|
request.setNotifyUrl(urlComponent.getDoMainUrl() + "order/payNotify");
|
||||||
|
// 调用退款方法,得到应答
|
||||||
|
// 调用微信sdk接口
|
||||||
|
Refund refund = service.create(request);
|
||||||
|
//接收退款返回参数
|
||||||
|
Refund refundResponse = new Refund();
|
||||||
|
refundResponse.setStatus(refund.getStatus());
|
||||||
|
System.out.println(refund);;
|
||||||
|
if (refundResponse.getStatus().equals(Status.SUCCESS)){
|
||||||
|
//说明退款成功,开始接下来的业务操作
|
||||||
|
// WXRefundOrderRespVo refundOrderRespVO = new WXRefundOrderRespVo();
|
||||||
|
//你的业务代码
|
||||||
|
return rb.success().setData(refund);
|
||||||
|
}else{
|
||||||
|
|
||||||
|
return rb.setMsg("申请退款失败").setData(refund);
|
||||||
|
}
|
||||||
|
}catch (Exception e){
|
||||||
|
e.printStackTrace();
|
||||||
|
return rb.setMsg("退款失败,error=" + e.getMessage());
|
||||||
|
// throw new ResultException(ResultEnum.ERROR,e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能描述:
|
||||||
|
* 〈微信退款回调〉
|
||||||
|
* @Param: [request, response]
|
||||||
|
* @Return: [void]
|
||||||
|
* @Author: whl
|
||||||
|
* @Date: 2023/10/13 10:46
|
||||||
|
*/
|
||||||
|
public void refundNotify(HttpServletRequest request) throws Exception {
|
||||||
|
try {
|
||||||
|
WxPayVo wxPayVo=new WxPayVo(0);
|
||||||
|
//读取请求体的信息
|
||||||
|
ServletInputStream inputStream = request.getInputStream();
|
||||||
|
StringBuffer stringBuffer = new StringBuffer();
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
|
||||||
|
String s;
|
||||||
|
//读取回调请求体
|
||||||
|
while ((s = bufferedReader.readLine()) != null) {
|
||||||
|
stringBuffer.append(s);
|
||||||
|
}
|
||||||
|
String s1 = stringBuffer.toString();
|
||||||
|
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
|
||||||
|
String nonce = request.getHeader(WECHAT_PAY_NONCE);
|
||||||
|
String signType = request.getHeader("Wechatpay-Signature-Type");
|
||||||
|
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
|
||||||
|
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
|
||||||
|
// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
|
||||||
|
// 没有的话,则构造一个
|
||||||
|
// log.error(JSON.toJSONString(wxPayV3Bean));
|
||||||
|
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
|
||||||
|
.merchantId(mchId)
|
||||||
|
.privateKeyFromPath(keyPath)
|
||||||
|
.merchantSerialNumber(mchSerialNo)
|
||||||
|
.apiV3Key(apiKey)
|
||||||
|
.build();
|
||||||
|
// 初始化 NotificationParser
|
||||||
|
NotificationParser parser = new NotificationParser(config);
|
||||||
|
RequestParam requestParam=new RequestParam.Builder()
|
||||||
|
.serialNumber(serialNo)
|
||||||
|
.nonce(nonce)
|
||||||
|
.signature(signature)
|
||||||
|
.timestamp(timestamp)
|
||||||
|
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
|
||||||
|
.signType(signType)
|
||||||
|
.body(s1)
|
||||||
|
.build();
|
||||||
|
RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
|
||||||
|
System.out.println("退款回调结果: " + parse);
|
||||||
|
//parse.getRefundStatus().equals("SUCCESS");说明退款成功
|
||||||
|
if (parse.getRefundStatus().equals(Transaction.TradeStateEnum.SUCCESS)){
|
||||||
|
//你的业务代码
|
||||||
|
|
||||||
|
}else{
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// //url 微信退款请求地址 data 请求参数
|
||||||
|
// public static String doRefund(String url, String data) throws Exception {
|
||||||
|
//
|
||||||
|
// KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||||
|
// // 指定证书路径
|
||||||
|
// String path = "/cert/apiclient_cert.p12";
|
||||||
|
// ClassPathResource classPathResource = new ClassPathResource(path);
|
||||||
|
//
|
||||||
|
// //读取项目存放的PKCS12证书文件
|
||||||
|
// InputStream stream = classPathResource.getInputStream();
|
||||||
|
// String mchId = "自己的商户id";
|
||||||
|
// try {
|
||||||
|
// //指定PKCS12的初始密码为商户ID
|
||||||
|
// keyStore.load(stream, mchId.toCharArray());
|
||||||
|
// } finally {
|
||||||
|
// stream.close();
|
||||||
|
// }
|
||||||
|
// SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
|
||||||
|
// //指定TLS版本
|
||||||
|
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext,new String[] { "TLSv1"},null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
|
||||||
|
// //设置httpclient的SSLSocketFactory
|
||||||
|
// CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
|
||||||
|
// try {
|
||||||
|
// HttpPost httpost = new HttpPost(url); // 设置响应头信息
|
||||||
|
// httpost.addHeader("Connection", "keep-alive");
|
||||||
|
// httpost.addHeader("Accept", "*/*");
|
||||||
|
// httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
// httpost.addHeader("Host", "api.mch.weixin.qq.com");
|
||||||
|
// httpost.addHeader("X-Requested-With", "XMLHttpRequest");
|
||||||
|
// httpost.addHeader("Cache-Control", "max-age=0");
|
||||||
|
// httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
|
||||||
|
// httpost.setEntity(new StringEntity(data, "UTF-8"));
|
||||||
|
// CloseableHttpResponse response = httpclient.execute(httpost);
|
||||||
|
// try {
|
||||||
|
// HttpEntity entity = response.getEntity();
|
||||||
|
// String jsonStr = EntityUtils.toString(response.getEntity(), "UTF-8");
|
||||||
|
// EntityUtils.consume(entity);
|
||||||
|
// return jsonStr;
|
||||||
|
// } finally {
|
||||||
|
// response.close();
|
||||||
|
// }
|
||||||
|
// } finally {
|
||||||
|
// httpclient.close();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// /**
|
||||||
|
// * 退款
|
||||||
|
// * @param orderPrice 订单金额(分)
|
||||||
|
// * @param refundPrice 退款金额(分)
|
||||||
|
// * @param refundMark 退款备注
|
||||||
|
// * @param out_trade_no 退款商家订单号
|
||||||
|
// * @return
|
||||||
|
// * @throws Exception
|
||||||
|
// */
|
||||||
|
// public static String refund(Integer orderPrice,Integer refundPrice,String refundMark,String out_trade_no) throws Exception {
|
||||||
|
// Map<String, String> params = new HashMap<>();
|
||||||
|
//
|
||||||
|
// String outRefundNo = GenerateNum.getInstance().GenerateOrder();//可以自定义一个类来随机生成退款单号等等
|
||||||
|
//
|
||||||
|
// params.put("appid", "小程序Appid");
|
||||||
|
// params.put("mch_id", "商户号");
|
||||||
|
// //商户订单号
|
||||||
|
// params.put("out_trade_no", out_trade_no);
|
||||||
|
// //商户退款单号
|
||||||
|
// params.put("out_refund_no", outRefundNo);
|
||||||
|
// //总金额
|
||||||
|
// params.put("total_fee", String.valueOf(orderPrice));
|
||||||
|
// //退款金额
|
||||||
|
// params.put("refund_fee", String.valueOf(refundPrice));
|
||||||
|
// //退款原因
|
||||||
|
// params.put("refund_desc", refundMark);
|
||||||
|
// //退款结果回调地址
|
||||||
|
// params.put("notify_url", "回调域名");
|
||||||
|
// //随机字符串
|
||||||
|
// params.put("nonce_str", WXPayUtil.generateNonceStr());//使用微信随机字符串生成
|
||||||
|
// //生成sign
|
||||||
|
// String sign = WXPayUtil.generateSignature(params, "自己的商户密钥");
|
||||||
|
// params.put("sign", sign);
|
||||||
|
// //微信申请退款接口
|
||||||
|
// String wx_refund_url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
|
||||||
|
//
|
||||||
|
// String xmlStr = WXPayUtil.mapToXml(params);//转换成xml格式
|
||||||
|
// System.out.println(xmlStr);
|
||||||
|
// //发送双向证书请求给微信
|
||||||
|
// String resultXmlStr = doRefund(wx_refund_url, xmlStr);
|
||||||
|
// System.out.println(resultXmlStr);
|
||||||
|
// // 将返回的字符串转成Map集合
|
||||||
|
// Map<String, String> resultMap = WXPayUtil.xmlToMap(resultXmlStr);//转成map格式
|
||||||
|
//
|
||||||
|
// Map<String, String> map = new HashMap<>();
|
||||||
|
// if ("SUCCESS".equalsIgnoreCase(resultMap.get("result_code"))) {
|
||||||
|
// System.out.println("------申请退款成功,正在退款中----");
|
||||||
|
// //申请微信退款接口返回success,但是退款到账还需要时间;
|
||||||
|
// map.put("success", "REFUNDS");
|
||||||
|
// return "1";
|
||||||
|
// } else {
|
||||||
|
// map.put("success", resultMap.get("err_code_des"));
|
||||||
|
// System.out.println("-------------退款失败-------------------" + resultMap.get("err_code_des"));
|
||||||
|
// return "2";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// /*
|
||||||
|
// * 退款订单结果查询
|
||||||
|
// * 生成签名的xml
|
||||||
|
// * @return
|
||||||
|
// * */
|
||||||
|
// public static String selRefundOrder(String out_trade_no) {
|
||||||
|
// // 封装需要的信息
|
||||||
|
// Map<String, String> payMap = new HashMap<String, String>();
|
||||||
|
// String nonceStr = WXPayUtil.generateNonceStr();
|
||||||
|
// try {
|
||||||
|
// // 1. 拼接下单地址参数
|
||||||
|
// Map<String, String> param = new HashMap<String, String>();
|
||||||
|
// param.put("appid", "小程序appid");
|
||||||
|
// param.put("mch_id", "商户id");
|
||||||
|
// param.put("out_trade_no", out_trade_no); // 随机字符串
|
||||||
|
// param.put("nonce_str", nonceStr); // 账单类型
|
||||||
|
// param.put("sign_type", "MD5"); // 签名类型
|
||||||
|
// // 生成签名,官方默认MD5+商户秘钥+参数信息
|
||||||
|
// String sign = WXPayUtil.generateSignature(param, "商户密钥");
|
||||||
|
// param.put("sign", sign);
|
||||||
|
// // 将所有参数转换为xml形式
|
||||||
|
// String xmlParam = WXPayUtil.mapToXml(param);
|
||||||
|
//
|
||||||
|
// // 2. 发送请求
|
||||||
|
// HttpClient1 httpClient1 = new HttpClient1("https://api.mch.weixin.qq.com/pay/refundquery");
|
||||||
|
// httpClient1.setHttps(true);// https协议
|
||||||
|
// httpClient1.setXmlParam(xmlParam);
|
||||||
|
// httpClient1.post();
|
||||||
|
// // 获取结果
|
||||||
|
// String xmlResult = httpClient1.getContent();
|
||||||
|
// System.out.println(xmlResult);
|
||||||
|
// if (xmlResult.indexOf("SUCCESS") != -1) { // 只要执行了下单接口,都会包含SUCCESS的
|
||||||
|
// Map<String, String> map = WXPayUtil.xmlToMap(xmlResult);
|
||||||
|
// if (map.get("refund_fee").equals(map.get("total_fee"))){
|
||||||
|
// System.out.println(out_trade_no+"退款"+map.get("refund_fee")+"成功到"+map.get("refund_recv_accout_0"));
|
||||||
|
// return "1";
|
||||||
|
// }else {
|
||||||
|
// System.out.println(out_trade_no+"退款"+map.get("refund_fee")+"部分到"+map.get("refund_recv_accout_0"));
|
||||||
|
// return "2";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }else {
|
||||||
|
// System.out.println(out_trade_no+"退款失败,不存在");
|
||||||
|
// return "3";
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// } catch (Exception e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// payMap.put("msg", out_trade_no+"签名生成错误");
|
||||||
|
// return "签名失败";
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
BIN
src/main/resources/cert/apiclient_cert.p12
Normal file
BIN
src/main/resources/cert/apiclient_cert.p12
Normal file
Binary file not shown.
25
src/main/resources/cert/apiclient_cert.pem
Normal file
25
src/main/resources/cert/apiclient_cert.pem
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEMTCCAxmgAwIBAgIUdOW9WmRaHsToJigg4eKlKS20i50wDQYJKoZIhvcNAQEL
|
||||||
|
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
|
||||||
|
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
|
||||||
|
Q0EwHhcNMjQwMzI2MDI1OTA0WhcNMjkwMzI1MDI1OTA0WjCBijETMBEGA1UEAwwK
|
||||||
|
MTY2NDg4Mjc2NTEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTYwNAYDVQQL
|
||||||
|
DC3lrofkv6HpgJrliJvmlrDmioDmnK/vvIjmsrPljJfvvInmnInpmZDlhazlj7gx
|
||||||
|
CzAJBgNVBAYTAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEB
|
||||||
|
BQADggEPADCCAQoCggEBANVl55NDuqDEvx/zQdaCn4WKUC8wLUVEYWKLMlChSZer
|
||||||
|
VQuQpRlC2lKDW03hKB6cAYH+jQg/pmBQnWbIjiBggyA41EUrCY2ps+CWI77fIAVk
|
||||||
|
wPxKz1/gk4xoYKbKvnDsSW6A0yWBvma4ZVLt3O2JTEPVo9xaM6/+oWCs4An654rx
|
||||||
|
ZBwnxb7mzVCXWJeTldWLtjwjby0Y6fvR7wrj2QhSPocz171bQhi1fF8JuQPWyIp/
|
||||||
|
XkRRbVWjVIsEv/re80mhTH16ktJf6MhCtEM5l15+GWP8rjIDP2qqhn1m0nIRPTVy
|
||||||
|
Ut9SZS/bBCm9/Cywtw+C238mvjmItEH+uhK66L6PLzkCAwEAAaOBuTCBtjAJBgNV
|
||||||
|
HRMEAjAAMAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0
|
||||||
|
cDovL2V2Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIw
|
||||||
|
RTUwREJDMDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0
|
||||||
|
MjJFMTJCMjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUA
|
||||||
|
A4IBAQAgxASDSwdZ8aIVMKha7LJQu3UTGtseO+q8VvfQMmBCN+eB20b+NOLHvfTi
|
||||||
|
ijEl+x7UBFk7kCbKWPlxL6Osqe8npIxWbLolZfqxi1d+UzokmpkjXYyKtsZnWDPZ
|
||||||
|
B6Y1Bc/J7khma7GzuEa/d29Z+si+eeiyNHdZUwZUZi/PFwO61KbGP2r11AuXa6W5
|
||||||
|
ZYgQBw5sSfOxceoYBCIKTUO79hAOisSuGxP7yFqSalGYd85XG6t5UH5p224VHxVs
|
||||||
|
Dm+BoD1+w2jtBcAcdwrc3FGvR1mac1pd/LwMSYwfmDTHACWvCV88uN9TNvfbelHy
|
||||||
|
9f+Y8m563NzVPeWCD2sMqOJs6Ug5
|
||||||
|
-----END CERTIFICATE-----
|
||||||
28
src/main/resources/cert/apiclient_key.pem
Normal file
28
src/main/resources/cert/apiclient_key.pem
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDVZeeTQ7qgxL8f
|
||||||
|
80HWgp+FilAvMC1FRGFiizJQoUmXq1ULkKUZQtpSg1tN4SgenAGB/o0IP6ZgUJ1m
|
||||||
|
yI4gYIMgONRFKwmNqbPgliO+3yAFZMD8Ss9f4JOMaGCmyr5w7ElugNMlgb5muGVS
|
||||||
|
7dztiUxD1aPcWjOv/qFgrOAJ+ueK8WQcJ8W+5s1Ql1iXk5XVi7Y8I28tGOn70e8K
|
||||||
|
49kIUj6HM9e9W0IYtXxfCbkD1siKf15EUW1Vo1SLBL/63vNJoUx9epLSX+jIQrRD
|
||||||
|
OZdefhlj/K4yAz9qqoZ9ZtJyET01clLfUmUv2wQpvfwssLcPgtt/Jr45iLRB/roS
|
||||||
|
uui+jy85AgMBAAECggEAaL85mY6wSAPrMNqt4YaCmQEYdOE4B30WNFHRI8TXo8wk
|
||||||
|
1aVtdOXc1o3FvoebstKu5sk1HuxnVXWcnXwqT3gwRRvADiee69t2APKRnzpUtHEN
|
||||||
|
TYnHGu/YXR5K+vQf4LwLG2O+wPVbqX4vtjIttKclOVLfuOTJ5ESfoB2bZyqyhv8p
|
||||||
|
//odyC6+ryOs7+PbcDjChTjHkANT2y0JsQhkjgw9WmqjyrNH3BNAZ8djV4ilSJhz
|
||||||
|
XYiM+bkjwHEHwbQUJxhCUC0hKNNpxfxRC90J4ZNSJchqQdxG/zmn8Mw1rh239ydt
|
||||||
|
XXrs7Zeayx7mcr9UBBAjDDYBv+HAiHmieiNueg9wFQKBgQD92x1cOLzBD7YiGfvp
|
||||||
|
jmKsGZfAYmQQXCsP5thinPG95v5hR5LNoB6lZf27GhpdukKG58vsmJBW5iawF4ex
|
||||||
|
7SRBHWLEnTlP7SRpuycK0vrGzXkP5ohyR60bw5Sn5DYXokttiPxNYH6trFscGXb+
|
||||||
|
CiMJmz/Y04a/QG5loO3viERPVwKBgQDXM0/vWdWLEIxRDMAEa3qaV2yJeHg6MIsh
|
||||||
|
ltyZ8sJ2Pk9XJIwnKBHPeedqghVhgCyz6q7xpu0dcoj1OyBk6obqwpfe1xM06qK6
|
||||||
|
Omr64qSpJ2iNjpnWqpSiCcHxrxHHESOxTwtjZg57YI6TcVB5+Y40vcx51uMyOZgz
|
||||||
|
X3137Ear7wKBgGU9strpwdWZR4EYLdB6ub17Q3s1gkTIDWB9J0VE5GBhNIhlfBXH
|
||||||
|
yxEZuPSat3Mz6n5VCY4lVkNh7IIEEFkUg4X4i0Bwl7/ZitEkE7EMbXlFzwdZYId7
|
||||||
|
MH3HJY1DxZLtZfmcTUsqtbrb50YwnbU5I7ocEX2O9sllj+G2Kc6C4QNnAoGBAIjl
|
||||||
|
MWJXSUqPmlMswzGMlY99MaQ5qHGL09XoPCkKjr3dYWdT7nwyNzI+oAHXAnblrIo/
|
||||||
|
Lk63G3tyEsG9fRv1PSI165psQuGUqTPCWa+DYa/hS9dQ8XfnR2nUvzVZYvu5YXU3
|
||||||
|
jU0L7QZ7I/eTS9hr8hSG8H5JPnoCvVlg01XoTgStAoGAIw+E7j74n8LTx8xp7O38
|
||||||
|
QkUQhDs1ajOR3+Ct9VlhIgJ4khVADP550NLruPRO/E//gOWbOpqHNRkAXTJfJ04C
|
||||||
|
FHCxrhdv6nqaKH86AzazVaE9UH8nPtmoiiit1jLBXanqxZyOglog2PRcUReVfjzQ
|
||||||
|
uKWacPtR4jyQwZVIWdEbddg=
|
||||||
|
-----END PRIVATE KEY-----
|
||||||
Reference in New Issue
Block a user