java?Springboot對接開發(fā)微信支付詳細(xì)流程
一、微信配置申請
1、微信支付配置申請
詳細(xì)操作流程參考官方文檔:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml#part-1
配置完成需要以下信息:
- APPID
- 商戶號(mchid)
- 商戶API私鑰(apiclient_key.pem)
- 商戶證書序列號
- 商戶APIv3密鑰
二、開發(fā)環(huán)境
1、開發(fā)環(huán)境
開發(fā)語言:java ,編譯工具:idea ,框架:springboot ,倉庫:maven
2、maven依賴
<dependency> <groupId>com.github.wechatpay-apiv3</groupId> <artifactId>wechatpay-java</artifactId> <version>0.2.10</version> </dependency>
3、application.yml文件配置
#微信支付配置 wx: pay: #應(yīng)用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
三、代碼開發(fā)
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微信預(yù)下單
3.1、先建個WxPayService服務(wù)類
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; /*** * 預(yù)支付訂單 * @param req * @return */ public R createOrder(CreateOrderReq req) throws Exception { if (req == null) { return R.error("創(chuàng)建訂單失敗,缺少參數(shù)!"); } //先解密 String orderNo = req.getOutTradeNo(); Integer totalFee = req.getTotal(); //創(chuàng)建初始化訂單 //todo,創(chuàng)建訂單這邊你們自己來(后面我會放出表結(jié)構(gòu)) //請求微信支付相關(guān)配置 JsapiServiceExtension service = new JsapiServiceExtension.Builder() .config(rsaAutoCertificateConfig) .signType("RSA") // 不填默認(rèn)為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("請求預(yù)支付下單,請求參數(shù):{}", JSONObject.toJSONString(request)); // 調(diào)用預(yù)下單接口 response = service.prepayWithRequestPayment(request); log.info("訂單【{}】發(fā)起預(yù)支付成功,返回信息:{}", orderNo, response); } catch (HttpException e) { // 發(fā)送HTTP請求失敗 log.error("微信下單發(fā)送HTTP請求失敗,錯誤信息:{}", e.getHttpRequest()); return R.error("下單失敗"); } catch (ServiceException e) { // 服務(wù)返回狀態(tài)小于200或大于等于300,例如500 log.error("微信下單服務(wù)狀態(tài)錯誤,錯誤信息:{}", e.getErrorMessage()); return R.error("下單失敗"); } catch (MalformedMessageException e) { // 服務(wù)返回成功,返回體類型不合法,或者解析返回體失敗 log.error("服務(wù)返回成功,返回體類型不合法,或者解析返回體失敗,錯誤信息:{}", 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<String, Object> { private static final long serialVersionUID = 1L; public R() { put("code", 0); } public R(Integer code) { put("code", code); put("data", new HashMap<String, Object>()); } public R(Integer code, String msg) { put("code", code); put("msg", msg); put("data", new HashMap<String, Object>()); } public static R error() { return error(500, "未知異常,請聯(lián)系管理員"); } public static R errorDebug(String message) { return error(500, "未知異常 " + message + ",請聯(lián)系管理員"); } 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<String, Object>()); return r; } public static R ok(Map<String, Object> map) { R r = new R(); r.putAll(map); r.put("data", new HashMap<String, Object>()); return r; } public static R ok() { return new R().put("msg", "success").put("data", new HashMap<String, Object>()); } public static R ok(Integer size) { return new R().put("data", new HashMap<String, Object>((int)Math.round(size / 0.75))); } @Override public R put(String key, Object value) { super.put(key, value); return this; } /** * 添加返回結(jié)果數(shù)據(jù) * * @param key * @param value * @return */ public R putData(String key, Object value) { Map<String, Object> map = (HashMap<String, Object>)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、微信支付回調(diào)通知
/*** * 微信支付回調(diào)通知 * @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"); // 構(gòu)造 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); // 以支付通知回調(diào)為例,驗簽、解密并轉(zhuǎn)換成 Transaction log.info("驗簽參數(shù):{}", requestParam); Transaction transaction = parser.parse(requestParam, Transaction.class); log.info("驗簽成功!-支付回調(diào)結(jié)果:{}", transaction.toString()); Map<String, String> returnMap = new HashMap<>(2); returnMap.put("code", "FAIL"); returnMap.put("message", "失敗"); //修改訂單前,建議主動請求微信查詢訂單是否支付成功,防止惡意post Wrapper wrapper = new EntityWrapper<WxOrderEntity>(); 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) { //此時已經(jīng)是支付成功,不在處理訂單信息 returnMap.put("code", "SUCCESS"); returnMap.put("message", "成功"); return JSONObject.toJSONString(returnMap); } } if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) { log.info("內(nèi)部訂單號【{}】,微信支付訂單號【{}】支付未成功", 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<WxPayLogEntity>(); 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、根據(jù)商戶訂單號查詢訂單(out_trade_no)
/*** * 根據(jù)商戶訂單號查詢訂單 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") // 不填默認(rèn)為RSA .build(); Transaction result = service.queryOrderByOutTradeNo(queryRequest); LinkedHashMap retmap = new LinkedHashMap(); //支付成功 if (Transaction.TradeStateEnum.SUCCESS == result.getTradeState()) { log.info("內(nèi)部訂單號【{}】,微信支付訂單號【{}】支付成功", 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<WxOrderEntity>(); 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<WxPayLogEntity>(); 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("內(nèi)部訂單號【{}】,微信支付訂單號【{}】支付未成功", 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、根據(jù)支付訂單號查詢訂單 (transaction_id)
/*** * 根據(jù)支付訂單號查詢訂單 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") // 不填默認(rèn)為RSA .build(); Transaction result = service.queryOrderById(queryRequest); LinkedHashMap map = new LinkedHashMap(); //支付成功 if (Transaction.TradeStateEnum.SUCCESS == result.getTradeState()) { log.info("內(nèi)部訂單號【{}】,微信支付訂單號【{}】支付成功", 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<WxOrderEntity>(); 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<WxPayLogEntity>(); 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("內(nèi)部訂單號【{}】,微信支付訂單號【{}】支付未成功", 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) { //返回參數(shù) 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<WxOrderEntity>(); 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 { // 構(gòu)建退款service RefundService service = new RefundService.Builder() .config(rsaAutoCertificateConfig) .build(); CreateRequest request = new CreateRequest(); // 調(diào)用request.setXxx(val)設(shè)置所需參數(shù),具體參數(shù)可見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()); //接收退款返回參數(shù) Refund refund = service.create(request); log.info("退款返回信息:{}", refund); if (refund.getStatus().equals(Status.SUCCESS)) { map.put("success", true); map.put("msg", "退款成功!"); //說明退款成功,開始接下來的業(yè)務(wù)操作 //主動查詢 Wrapper againWrapper = new EntityWrapper<WxOrderEntity>(); 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<WxPayLogEntity>(); 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("服務(wù)返回成功,返回體類型不合法,或者解析返回體失敗,錯誤信息:{}", e.getMessage()); return R.error("退款失??!"); } return R.ok().put("data", map); }
8、退款回調(diào)通知
待續(xù)......
四、mysql表結(jié)構(gòu)
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 '支付成功后的隨機(jī)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 '創(chuàng)建訂單時間', `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 '創(chuàng)建時間', PRIMARY KEY (`uuid`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '微信用戶支付記錄表' ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
五、controller類
退款回調(diào)通知,和controller就不寫了,如果需要,聯(lián)系我即可!
哎呀呀呀,缺少了一個類,代碼如下:
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(); // 獲取響應(yīng) reader = new BufferedReader(new InputStreamReader(stream)); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { throw new IOException("讀取返回支付接口數(shù)據(jù)流出現(xiàn)異常!"); } finally { reader.close(); } return sb.toString(); } }
如果你在微信支付回調(diào)通知中出現(xiàn) “簽名錯誤”,并且你是windows服務(wù)器,請在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(); // 獲取響應(yīng) reader = new BufferedReader(new InputStreamReader(stream,"UTF-8")); String line; while ((line = reader.readLine()) != null) { sb.append(line); } } catch (IOException e) { throw new IOException("讀取返回支付接口數(shù)據(jù)流出現(xiàn)異常!"); } finally { reader.close(); } return sb.toString(); } }
總結(jié)
到此這篇關(guān)于java Springboot對接開發(fā)微信支付的文章就介紹到這了,更多相關(guān)Springboot開發(fā)微信支付內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot實現(xiàn)微信支付接口調(diào)用及回調(diào)函數(shù)(商戶參數(shù)獲取)
- SpringBoot對接小程序微信支付的實現(xiàn)
- Springboot整合微信支付(訂單過期取消及商戶主動查單)
- UniApp?+?SpringBoot?實現(xiàn)微信支付和退款功能
- SpringBoot實現(xiàn)整合微信支付方法詳解
- springboot對接微信支付的完整流程(附前后端代碼)
- 一篇文章帶你入門Springboot整合微信登錄與微信支付(附源碼)
- springboot整合微信支付sdk過程解析
- SpringBoot+MyBatis集成微信支付實現(xiàn)示例
相關(guān)文章
解決springboot的JPA在Mysql8新增記錄失敗的問題
這篇文章主要介紹了解決springboot的JPA在Mysql8新增記錄失敗的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06基于Java的度分秒坐標(biāo)轉(zhuǎn)純經(jīng)緯度坐標(biāo)的漂亮國基地信息管理的方法
本文以java語言為例,詳細(xì)介紹如何管理漂亮國的基地信息,為下一步全球的空間可視化打下堅實的基礎(chǔ),首先介紹如何對數(shù)據(jù)進(jìn)行去重處理,然后介紹在java當(dāng)中如何進(jìn)行度分秒位置的轉(zhuǎn)換,最后結(jié)合實現(xiàn)原型進(jìn)行詳細(xì)的說明,感興趣的朋友跟隨小編一起看看吧2024-06-065種必會的Java異步調(diào)用轉(zhuǎn)同步的方法你會幾種
這篇文章主要介紹了5種必會的Java異步調(diào)用轉(zhuǎn)同步的方法你會幾種,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12springBoot使用openfeign來遠(yuǎn)程調(diào)用的實現(xiàn)
這篇文章主要介紹了springBoot使用openfeign來遠(yuǎn)程調(diào)用的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03解決Springboot整合shiro時靜態(tài)資源被攔截的問題
這篇文章主要介紹了解決Springboot整合shiro時靜態(tài)資源被攔截的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01