本文章是介绍java对接(微信小程序)微信支付,包括微信预下单、支付、退款等等。
一、微信配置申请
1、微信支付配置申请
详细操作流程参考官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml#part-1
配置完成需要以下信息:
- APPID
- 商户号(mchid)
- 商户API私钥(apiclient_key.pem)
- 商户证书序列号
- 商户APIv3密钥
二、开发环境
1、开发环境
开发语言:java ,编译工具:idea ,框架:springboot ,仓库:maven
2、maven依赖
com.github.wechatpay-apiv3
wechatpay-java
0.2.10
3、application.yml文件配置
#微信支付配置
wx:
pay:
#应用id(小程序id)
appId: wx6b5xxxxxxxxxxxx
#商户号
merchantId: 1xxxxxxxxx
#商户API私钥
privateKey: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#商户证书序列号
merchantSerialNumber: 315DDXXXXXXXXXXXXXXXXXXXXXXXXXXX
#商户APIv3密钥
apiV3Key: XXXXXXXXXXXXXXXXXXXXXXXXXX
#支付通知地址
payNotifyUrl: https://xxx.xxxx.xxx.xxx/xx/xxxx/xxxx/openapi/wx/payNotify
#退款通知地址
refundNotifyUrl: https://xxx.xxx.xxx.xxx/xxxx/xxxx/xxxx/openapi/wx/refundNotify
三、代码开发
1、配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author caozhen
* @ClassName WxPayConfig
* @description: 微信支付配置类
* @date 2024年01月03日
* @version: 1.0
*/
@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {
//APPID
private String appId;
//mchid
private String merchantId;
//商户API私钥
private String privateKey;
//商户证书序列号
private String merchantSerialNumber;
//商户APIv3密钥
private String apiV3Key;
//支付通知地址
private String payNotifyUrl;
//退款通知地址
private String refundNotifyUrl;
}
2、初始化商户配置
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* @author caozhen
* @ClassName WxPayAutoCertificateConfig
* @description: 微信支付证书自动更新配置
* @date 2024年01月03日
* @version: 1.0
*/
@Configuration
public class WxPayAutoCertificateConfig {
@Resource
private WxPayConfig wxPayConfig;
/**
* 初始化商户配置
* @return
*/
@Bean
public RSAAutoCertificateConfig rsaAutoCertificateConfig() {
RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(wxPayConfig.getMerchantId())
.privateKey(wxPayConfig.getPrivateKey())
.merchantSerialNumber(wxPayConfig.getMerchantSerialNumber())
.apiV3Key(wxPayConfig.getApiV3Key())
.build();
return config;
}
}
3、JSAPI微信预下单
3.1、先建个WxPayService服务类
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.hvit.user.exception.DataAccessException;
import com.hvit.user.util.DateUtils;
import com.hvit.user.util.R;
import com.hvit.user.yst.entity.WxOrderEntity;
import com.hvit.user.yst.entity.WxPayLogEntity;
import com.hvit.user.yst.request.CreateOrderReq;
import com.hvit.user.yst.request.QueryOrderReq;
import com.hvit.user.yst.request.WxNotifyReq;
import com.hvit.user.yst.service.WKShoppingMallService;
import com.hvit.user.yst.service.data.WxOrderDataService;
import com.hvit.user.yst.service.data.WxPayLogDataService;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.*;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.models.auth.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author caozhen
* @ClassName WxPayService
* @description: 微信支付
* @date 2024年01月03日
* @version: 1.0
*/
@Slf4j
@Service
public class WxPayService {
@Resource
private WxPayConfig wxPayConfig;
@Autowired
private RSAAutoCertificateConfig rsaAutoCertificateConfig;
@Autowired
private WxOrderDataService wxOrderDataService;
@Autowired
private WxPayLogDataService wxPayLogDataService;
/***
* 预支付订单
* @param req
* @return
*/
public R createOrder(CreateOrderReq req) throws Exception {
if (req == null) {
return R.error("创建订单失败,缺少参数!");
}
//先解密
String orderNo = req.getOutTradeNo();
Integer totalFee = req.getTotal();
//创建初始化订单
//todo,创建订单这边你们自己来(后面我会放出表结构)
//请求微信支付相关配置
JsapiServiceExtension service =
new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig)
.signType("RSA") // 不填默认为RSA
.build();
PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
try {
PrepayRequest request = new PrepayRequest();
request.setAppid(wxPayConfig.getAppId());
request.setMchid(wxPayConfig.getMerchantId());
request.setDescription(description);
request.setOutTradeNo(orderNo);
request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
Amount amount = new Amount();
//amount.setTotal(totalFee.multiply(new BigDecimal("100")).intValue());
amount.setTotal(totalFee);
request.setAmount(amount);
Payer payer = new Payer();
payer.setOpenid(req.getWxOpenId());
request.setPayer(payer);
log.info("请求预支付下单,请求参数:{}", JSONObject.toJSONString(request));
// 调用预下单接口
response = service.prepayWithRequestPayment(request);
log.info("订单【{}】发起预支付成功,返回信息:{}", orderNo, response);
} catch (HttpException e) { // 发送HTTP请求失败
log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
return R.error("下单失败");
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
return R.error("下单失败");
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
return R.error("下单失败");
}
return R.ok().put("data", response);
}
}
3.1、R实体类
import java.util.HashMap;
import java.util.Map;
/**
*
* @author 曹震
* @date 2024-1-03
*/
public class R extends HashMap {
private static final long serialVersionUID = 1L;
public R() {
put("code", 0);
}
public R(Integer code) {
put("code", code);
put("data", new HashMap());
}
public R(Integer code, String msg) {
put("code", code);
put("msg", msg);
put("data", new HashMap());
}
public static R error() {
return error(500, "未知异常,请联系管理员");
}
public static R errorDebug(String message) {
return error(500, "未知异常 " + message + ",请联系管理员");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public R errorInfo(String msg) {
this.put("errorMsg", msg);
return this;
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
r.put("data", new HashMap());
return r;
}
public static R ok(Map map) {
R r = new R();
r.putAll(map);
r.put("data", new HashMap());
return r;
}
public static R ok() {
return new R().put("msg", "success").put("data", new HashMap());
}
public static R ok(Integer size) {
return new R().put("data", new HashMap((int)Math.round(size / 0.75)));
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
/**
* 添加返回结果数据
*
* @param key
* @param value
* @return
*/
public R putData(String key, Object value) {
Map map = (HashMap)this.get("data");
map.put(key, value);
return this;
}
}
3.2、CreateOrderReq类
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author caozhen
* @ClassName CreateOrderReq
* @description: TODO
* @date 2024年01月03日
* @version: 1.0
*/
@Data
public class CreateOrderReq {
@ApiModelProperty(name = "description", value = "商品描述")
private String description;
@ApiModelProperty(name = "wxOpenId", value = "用户小程序openid")
private String wxOpenId;
@ApiModelProperty(name = "outTradeNo", value = "商户订单号")
private String outTradeNo;
@ApiModelProperty(name = "totalFee", value = "支付金额,单位:分")
private Long totalFee;
4、微信支付回调通知
/***
* 微信支付回调通知
* @param request
* @return
* @throws IOException
*/
@Transactional
public synchronized String payNotify(HttpServletRequest request) throws Exception {
log.info("------收到支付通知------");
// 请求头Wechatpay-Signature
String signature = request.getHeader("Wechatpay-Signature");
// 请求头Wechatpay-nonce
String nonce = request.getHeader("Wechatpay-Nonce");
// 请求头Wechatpay-Timestamp
String timestamp = request.getHeader("Wechatpay-Timestamp");
// 微信支付证书序列号
String serial = request.getHeader("Wechatpay-Serial");
// 签名方式
String signType = request.getHeader("Wechatpay-Signature-Type");
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serial)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(signType)
.body(HttpServletUtils.getRequestBody(request))
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
// 以支付通知回调为例,验签、解密并转换成 Transaction
log.info("验签参数:{}", requestParam);
Transaction transaction = parser.parse(requestParam, Transaction.class);
log.info("验签成功!-支付回调结果:{}", transaction.toString());
Map returnMap = new HashMap(2);
returnMap.put("code", "FAIL");
returnMap.put("message", "失败");
//修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post
Wrapper wrapper = new EntityWrapper();
wrapper.eq("out_trade_no", transaction.getOutTradeNo());
//wrapper.eq("transaction_id", transaction.getTransactionId());
WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
if (wxOrderEntity != null) {
if (wxOrderEntity.getPayStatus() == 1) {
//此时已经是支付成功,不在处理订单信息
returnMap.put("code", "SUCCESS");
returnMap.put("message", "成功");
return JSONObject.toJSONString(returnMap);
}
}
if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
if (wxOrderEntity != null) {
wxOrderEntity.setUpdateTime(new Date());
wxOrderEntity.setPayStatus(2);
wxOrderEntity.setPayNonce(nonce);
wxOrderEntity.setTransactionId(transaction.getTransactionId());
//修改订单信息
wxOrderDataService.updateById(wxOrderEntity);
}
return JSONObject.toJSONString(returnMap);
}
if (wxOrderEntity != null) {
wxOrderEntity.setUpdateTime(new Date());
wxOrderEntity.setPayTime(DateUtils.stringToDateTime(transaction.getSuccessTime()));
wxOrderEntity.setPayDate(DateUtils.stringToDateTime(transaction.getSuccessTime()));
wxOrderEntity.setPayStatus(1);
wxOrderEntity.setPayNonce(nonce);
wxOrderEntity.setTransactionId(transaction.getTransactionId());
//修改订单信息
wxOrderDataService.updateById(wxOrderEntity);
//同时处理支付记录
Wrapper payWrapper = new EntityWrapper();
wrapper.eq("out_trade_no", transaction.getOutTradeNo());
wrapper.eq("pay_status", 1);//支付
WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
if (wxPayLogEntity == null) {
wxPayLogEntity = new WxPayLogEntity();
wxPayLogEntity.setCreateTime(new Date());
wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
wxPayLogEntity.setPayStatus(1);
wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
wxPayLogEntity.setTransactionId(wxOrderEntity.getTransactionId());
wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
wxPayLogDataService.insert(wxPayLogEntity);
}
}
returnMap.put("code", "SUCCESS");
returnMap.put("message", "成功");
return JSONObject.toJSONString(returnMap);
}
5、根据商户订单号查询订单(out_trade_no)
/***
* 根据商户订单号查询订单 outTradeNo
* @param req
* @return
*/
@Transactional
public R queryOrderByOrderNo(QueryOrderReq req) {
QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
queryRequest.setMchid(wxPayConfig.getMerchantId());
queryRequest.setOutTradeNo(req.getOrderNo());
try {
JsapiServiceExtension service =
new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig)
.signType("RSA") // 不填默认为RSA
.build();
Transaction result = service.queryOrderByOutTradeNo(queryRequest);
LinkedHashMap retmap = new LinkedHashMap();
//支付成功
if (Transaction.TradeStateEnum.SUCCESS == result.getTradeState()) {
log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
retmap.put("out_trade_no", result.getOutTradeNo());
retmap.put("transaction_id", result.getTransactionId());
retmap.put("success", true);
retmap.put("msg", "支付成功!");
retmap.put("success_time", DateUtils.getDateTimeString(DateUtils.stringToDateTime(result.getSuccessTime())));
//主动查询
Wrapper wrapper = new EntityWrapper();
wrapper.eq("out_trade_no", req.getOrderNo());
WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
if (wxOrderEntity != null) {
if (wxOrderEntity.getPayStatus() != 1) {
wxOrderEntity.setPayStatus(1);
wxOrderEntity.setTransactionId(result.getTransactionId());
wxOrderEntity.setPayDate(DateUtils.stringToDateTime(result.getSuccessTime()));
wxOrderEntity.setPayTime(DateUtils.stringToDateTime(result.getSuccessTime()));
wxOrderEntity.setUpdateTime(new Date());
wxOrderDataService.updateById(wxOrderEntity);
//同时处理支付记录
Wrapper payWrapper = new EntityWrapper();
wrapper.eq("out_trade_no", req.getOrderNo());
WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
if (wxPayLogEntity == null) {
wxPayLogEntity = new WxPayLogEntity();
wxPayLogEntity.setCreateTime(new Date());
wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
wxPayLogEntity.setPayStatus(1);
wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
wxPayLogEntity.setTransactionId(result.getTransactionId());
wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
wxPayLogDataService.insert(wxPayLogEntity);
}
}
}
} else {
log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
retmap.put("out_trade_no", result.getOutTradeNo());
retmap.put("transaction_id", result.getTransactionId());
retmap.put("success", false);
retmap.put("msg", "支付失败!");
retmap.put("success_time", null);
}
return R.ok().put("data", retmap);
} catch (ServiceException e) {
log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
return R.error("订单查询失败!");
}
}
5.1 QueryOrderReq类
mport io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author caozhen
* @ClassName QueryOrderReq
* @description: 支付查询
* @date 2024年01月04日
* @version: 1.0
*/
@Data
public class QueryOrderReq {
@ApiModelProperty(name = "paymentNo", value = "微信支付订单号,同:transaction_id")
private String paymentNo;
@ApiModelProperty(name = "orderNo", value = "商户订单号,同:out_trade_no")
private String orderNo;
}
6、根据支付订单号查询订单 (transaction_id)
/***
* 根据支付订单号查询订单 paymentNo
* @param req
* @return
*/
@Transactional
public R queryOrderByPaymentNo(QueryOrderReq req) {
QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
queryRequest.setMchid(wxPayConfig.getMerchantId());
queryRequest.setTransactionId(req.getPaymentNo());
try {
JsapiServiceExtension service =
new JsapiServiceExtension.Builder()
.config(rsaAutoCertificateConfig)
.signType("RSA") // 不填默认为RSA
.build();
Transaction result = service.queryOrderById(queryRequest);
LinkedHashMap map = new LinkedHashMap();
//支付成功
if (Transaction.TradeStateEnum.SUCCESS == result.getTradeState()) {
log.info("内部订单号【{}】,微信支付订单号【{}】支付成功", result.getOutTradeNo(), result.getTransactionId());
map.put("out_trade_no", result.getOutTradeNo());
map.put("transaction_id", result.getTransactionId());
map.put("success", true);
map.put("msg", "支付成功!");
map.put("success_time", DateUtils.getDateTimeString(DateUtils.stringToDateTime(result.getSuccessTime())));
//主动查询
Wrapper wrapper = new EntityWrapper();
wrapper.eq("transaction_id", req.getPaymentNo());
WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
if (wxOrderEntity != null) {
if (wxOrderEntity.getPayStatus() != 1) {
wxOrderEntity.setPayStatus(1);
wxOrderEntity.setPayDate(DateUtils.stringToDateTime(result.getSuccessTime()));
wxOrderEntity.setPayTime(DateUtils.stringToDateTime(result.getSuccessTime()));
wxOrderEntity.setUpdateTime(new Date());
wxOrderDataService.updateById(wxOrderEntity);
//同时处理支付记录
Wrapper payWrapper = new EntityWrapper();
wrapper.eq("transaction_id", req.getPaymentNo());
WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
if (wxPayLogEntity == null) {
wxPayLogEntity = new WxPayLogEntity();
wxPayLogEntity.setCreateTime(new Date());
wxPayLogEntity.setOutTradeNo(wxOrderEntity.getOutTradeNo());
wxPayLogEntity.setPayStatus(1);
wxPayLogEntity.setTotalFee(wxOrderEntity.getTotalFee());
wxPayLogEntity.setTransactionId(result.getTransactionId());
wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
wxPayLogDataService.insert(wxPayLogEntity);
}
}
}
} else {
log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
map.put("out_trade_no", result.getOutTradeNo());
map.put("transaction_id", result.getTransactionId());
map.put("success", false);
map.put("msg", "支付失败!");
map.put("success_time", null);
}
return R.ok().put("data", map);
} catch (ServiceException e) {
log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
return R.error("订单查询失败!");
}
}
7、微信申请退款
/***
* 微信申请退款
* @param outTradeNo 商户订单号
* @param totalAmount
* @return
*/
public R createRefund(String outTradeNo, Long totalAmount) {
//返回参数
LinkedHashMap map = new LinkedHashMap();
map.put("out_trade_no", outTradeNo);
map.put("success", false);
map.put("msg", "正在申请退款中!");
String outRefundNo = "REFUND_" + outTradeNo;
map.put("out_refund_no", outRefundNo);
//申请退款订单,需要变更订单记录
Wrapper wrapper = new EntityWrapper();
wrapper.eq("out_trade_no", outTradeNo);
WxOrderEntity wxOrderEntity = wxOrderDataService.selectOne(wrapper);
if (wxOrderEntity == null) {
return R.error("订单不存在,申请退款不存在!");
}
wxOrderEntity.setPayStatus(4);//退款中
wxOrderEntity.setUpdateTime(new Date());
wxOrderDataService.updateById(wxOrderEntity);
try {
// 构建退款service
RefundService service = new RefundService.Builder()
.config(rsaAutoCertificateConfig)
.build();
CreateRequest request = new CreateRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
request.setOutTradeNo(outTradeNo);
request.setOutRefundNo(outRefundNo);
AmountReq amount = new AmountReq();
amount.setTotal(totalAmount);
amount.setRefund(totalAmount);
amount.setCurrency("CNY");
request.setAmount(amount);
request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl());
//接收退款返回参数
Refund refund = service.create(request);
log.info("退款返回信息:{}", refund);
if (refund.getStatus().equals(Status.SUCCESS)) {
map.put("success", true);
map.put("msg", "退款成功!");
//说明退款成功,开始接下来的业务操作
//主动查询
Wrapper againWrapper = new EntityWrapper();
againWrapper.eq("out_trade_no", outTradeNo);
WxOrderEntity orderEntity = wxOrderDataService.selectOne(againWrapper);
if (orderEntity != null) {
orderEntity.setPayStatus(3);//退款成功
orderEntity.setUpdateTime(new Date());
wxOrderDataService.updateById(orderEntity);
//同时处理退款记录
Wrapper payWrapper = new EntityWrapper();
payWrapper.eq("out_trade_no", outTradeNo);
payWrapper.eq("pay_status", 2);//退款
WxPayLogEntity wxPayLogEntity = wxPayLogDataService.selectOne(payWrapper);
if (wxPayLogEntity == null) {
wxPayLogEntity = new WxPayLogEntity();
wxPayLogEntity.setCreateTime(new Date());
wxPayLogEntity.setOutTradeNo(outTradeNo);
wxPayLogEntity.setPayStatus(2);
wxPayLogEntity.setTotalFee(totalAmount.intValue());
wxPayLogEntity.setTransactionId(wxOrderEntity.getTransactionId());
wxPayLogEntity.setOutRefundNo(outRefundNo);
wxPayLogEntity.setWxOpenId(wxOrderEntity.getWxOpenId());
wxPayLogDataService.insert(wxPayLogEntity);
}
}
}
} catch (ServiceException e) {
log.error("退款失败!,错误信息:{}", e.getMessage());
return R.error("退款失败!");
} catch (Exception e) {
log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
return R.error("退款失败!");
}
return R.ok().put("data", map);
}
8、退款回调通知
待续……有需要的再联系我!
四、mysql表结构
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_wx_order
-- ----------------------------
DROP TABLE IF EXISTS `t_wx_order`;
CREATE TABLE `t_wx_order` (
`uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`trade_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '商品名称',
`description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '订单描述',
`out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '(商户)订单流水号',
`transaction_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信订单号',
`total_fee` int(10) NULL DEFAULT NULL COMMENT '订单金额(单位:分)',
`pay_nonce` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '支付成功后的随机32位字符串',
`pay_time` datetime NULL DEFAULT NULL COMMENT '支付时间',
`pay_date` date NULL DEFAULT NULL COMMENT '支付日期',
`pay_status` int(3) NULL DEFAULT 0 COMMENT '0:待支付,1:支付成功,2:支付失败,3:退款成功,4:正在退款中,5:未知',
`wx_open_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信小程序openid',
`status` int(2) NULL DEFAULT 0 COMMENT '0:未删除,1:已删除',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建订单时间',
`update_time` datetime NULL DEFAULT NULL COMMENT '修改订单时间',
PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '微信商品订单表' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for t_wx_pay_log
-- ----------------------------
DROP TABLE IF EXISTS `t_wx_pay_log`;
CREATE TABLE `t_wx_pay_log` (
`uuid` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`wx_open_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信用户openid',
`out_trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '(商户)订单流水号',
`out_refund_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '(商户)退款流水号',
`transaction_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '微信订单号',
`total_fee` int(10) NULL DEFAULT NULL COMMENT '支付金额',
`pay_status` int(2) NULL DEFAULT NULL COMMENT '1:支付,2:退款',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`uuid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '微信用户支付记录表' ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
五、controller类
退款回调通知,和controller就不写了,如果需要,联系我即可!
哎呀呀呀,缺少了一个类,代码如下:
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author caozhen
* @ClassName HttpServletUtils
* @description: TODO
* @date 2024年01月04日
* @version: 1.0
*/
public class HttpServletUtils {
/**
* 获取请求体
*
* @param request
* @return
* @throws IOException
*/
public static String getRequestBody(HttpServletRequest request) throws IOException {
ServletInputStream stream = null;
BufferedReader reader = null;
StringBuffer sb = new StringBuffer();
try {
stream = request.getInputStream();
// 获取响应
reader = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new IOException("读取返回支付接口数据流出现异常!");
} finally {
reader.close();
}
return sb.toString();
}
}
如果你在微信支付回调通知中出现 “签名错误”,并且你是windows服务器,请在HttpServletUtils 类中,将reader = new BufferedReader(new InputStreamReader(stream)); 替换成:reader = new BufferedReader(new InputStreamReader(stream,”UTF-8″));
替换完整代码:
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
/**
* @author caozhen
* @ClassName HttpServletUtils
* @description: TODO
* @date 2024年01月04日
* @version: 1.0
*/
public class HttpServletUtils {
/**
* 获取请求体
*
* @param request
* @return
* @throws IOException
*/
public static String getRequestBody(HttpServletRequest request) throws IOException {
ServletInputStream stream = null;
BufferedReader reader = null;
StringBuffer sb = new StringBuffer();
try {
stream = request.getInputStream();
// 获取响应
reader = new BufferedReader(new InputStreamReader(stream,"UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
throw new IOException("读取返回支付接口数据流出现异常!");
} finally {
reader.close();
}
return sb.toString();
}
}