# 微信支付 **Repository Path**: Curtain-lichunhui/wechat-payment ## Basic Information - **Project Name**: 微信支付 - **Description**: 基于微信wechatpay-java开发的微信支付功能 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-05-15 - **Last Updated**: 2023-05-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: wechatpay-java, 微信支付 ## README # 微信支付 #### 介绍 基于微信wechatpay-java开发的微信支付 #### 安装教程 1. 引入依赖 ```xml com.github.wechatpay-apiv3 wechatpay-java 0.2.7 ``` 2. 编写配置项 ```yaml wechatpay: #appId appId: xxxxx #商户号 mchId: xxxxx #apiv3秘钥 apiV3Key: xxxxxx #证书序列号 merchantSerialNumber: xxxx #支付回调地址 payNotifyUrl: https://xxxx/wxpay/payNotify #退款回调地址 refundNotifyUrl: https://xxxxx/wxpay/refundNotify #私钥 privateKey: xxxx ``` 3. 新增配置类 ```java @Configuration @ConfigurationProperties(prefix = "wechatpay") @Data public class WxPayConfig { private String appId; private String mchId; private String apiV3Key; private String privateKey; private String merchantSerialNumber; private String payNotifyUrl; private String refundNotifyUrl; private RSAAutoCertificateConfig config; @PostConstruct public void initConfig() { if (config == null) { config = new RSAAutoCertificateConfig.Builder() .merchantId(mchId) .privateKey(privateKey) .merchantSerialNumber(merchantSerialNumber) .apiV3Key(apiV3Key) .build(); } } @Bean public RSAAutoCertificateConfig getConfig() { return config; } } ``` 4. 新增微信服务类 ```java @Service @Slf4j public class NativeService { @Resource private WxPayConfig wxPayConfig; @Resource private RSAAutoCertificateConfig config; /** * 下单 * @param orderNo 订单号 * @param money 支付金额(元) * @param describe 订单描述 * @return 支付链接 */ public PrepayResponse prepay(String orderNo, BigDecimal money, String describe){ NativePayService service = new NativePayService.Builder().config(config).build(); PrepayRequest request = new PrepayRequest(); Amount amount = new Amount(); amount.setTotal(money.multiply(new BigDecimal("100")).intValue()); request.setAmount(amount); request.setAppid(wxPayConfig.getAppId()); request.setMchid(wxPayConfig.getMchId()); request.setDescription(describe); request.setOutTradeNo(orderNo); request.setNotifyUrl(wxPayConfig.getPayNotifyUrl()); log.info("请求参数 ===> {}",request); PrepayResponse prepay = null; try{ prepay = service.prepay(request); }catch (HttpException e){ // 发送HTTP请求失败 log.info(String.valueOf(e.getHttpRequest())); }catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500 log.info(String.valueOf(e.getResponseBody())); } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败 // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义 log.info(String.valueOf(e.getMessage())); } return prepay; } /** * 根据商户订单号查询订单 * @param orderNo 订单号 * @return 订单信息 */ public Transaction queryOrderByOrderNo(String orderNo){ NativePayService service = new NativePayService.Builder().config(config).build(); QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest(); request.setMchid(wxPayConfig.getMchId()); request.setOutTradeNo(orderNo); Transaction transaction = null; try{ transaction = service.queryOrderByOutTradeNo(request); }catch (HttpException e){ // 发送HTTP请求失败 log.info(String.valueOf(e.getHttpRequest())); }catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500 log.info(String.valueOf(e.getResponseBody())); } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败 // 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义 log.info(String.valueOf(e.getMessage())); } return transaction; } /** * 根据退款单号查询退款信息 * @param refundNo 退款单号 * @return 退款信息 */ public Refund queryRefundByRefundNo(String refundNo){ RefundService service = new RefundService.Builder().config(config).build(); QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest(); request.setOutRefundNo(refundNo); Refund refund = null; try{ refund = service.queryByOutRefundNo(request); }catch (HttpException e){ // 发送HTTP请求失败 log.info(String.valueOf(e.getHttpRequest())); }catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500 log.info(String.valueOf(e.getResponseBody())); } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败 log.info(String.valueOf(e.getMessage())); } return refund; } /** * 根据订单号申请退款 * @param orderNo 订单号 * @param refundNo 退款单号 * @param money 退款金额(元) * @param reason 退退款原因 * @return 退款信息 */ public Refund refundByOrderNo(String orderNo, String refundNo, BigDecimal money, String reason){ RefundService service = new RefundService.Builder().config(config).build(); CreateRequest request = new CreateRequest(); AmountReq amount = new AmountReq(); long refundMoney = money.multiply(new BigDecimal("100")).longValue(); amount.setRefund(refundMoney); amount.setTotal(refundMoney); amount.setCurrency("CNY"); request.setAmount(amount); request.setOutTradeNo(orderNo); request.setOutRefundNo(refundNo); request.setReason(reason); request.setNotifyUrl(wxPayConfig.getRefundNotifyUrl()); Refund refund = null; try{ refund = service.create(request); }catch (HttpException e){ // 发送HTTP请求失败 log.info(String.valueOf(e.getHttpRequest())); }catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500 log.info(String.valueOf(e.getResponseBody())); } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败 log.info(String.valueOf(e.getMessage())); } log.info("退款通知 ===> {}",refund); return refund; } /** * 回调解析 * @param request 请求 * @param returnType 转化对象:Transaction(支付回调对象)、RefundNotification(退款回调对象) * @return payNotify * @throws IOException 异常 */ public T parseNotify(HttpServletRequest request, Class returnType) throws IOException { String signature = request.getHeader("Wechatpay-Signature"); String serialNumber = request.getHeader("Wechatpay-Serial"); String nonce = request.getHeader("Wechatpay-Nonce"); String timestamp = request.getHeader("Wechatpay-Timestamp"); String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); // 构造 RequestParam RequestParam requestParam = new RequestParam.Builder() .serialNumber(serialNumber) .nonce(nonce) .signature(signature) .timestamp(timestamp) .body(requestBody) .build(); // 初始化 NotificationParser NotificationParser parser = new NotificationParser(config); //验签、解密并转换成 Transaction(支付回调对象)、RefundNotification(退款回调对象) return parser.parse(requestParam,returnType); } } ``` 5. 回调接口 ```java //微信支付回调接口 @RestController @Slf4j @RequestMapping("/wxpay") public class WxpayController { @Resource private NativeService nativeService; //支付结果回调 @PostMapping("/payNotify") public String payNotify(HttpServletRequest request, HttpServletResponse response){ Map map = new HashMap<>(); try { //支付解析 Transaction payNotify = nativeService.parseNotify(request, Transaction.class); log.info("====> 支付回调通知 ===> {} <====",payNotify); //交易状态 Transaction.TradeStateEnum status = payNotify.getTradeState(); if (Transaction.TradeStateEnum.SUCCESS.equals(status)){ //处理订单支付状态 //TODO //成功应答 response.setStatus(200); map.put("code","SUCCESS"); map.put("message","成功"); }else { //失败应答 log.info("====> 失败应答交易状态 ===> {} <====",status); response.setStatus(500); map.put("code","ERROR"); map.put("message","失败"); } return JSONObject.toJSONString(map); } catch (IOException e) { e.printStackTrace(); //失败应答 response.setStatus(500); map.put("code","ERROR"); map.put("message","失败"); return JSONObject.toJSONString(map); } } //退款结果回调 @PostMapping("/refundNotify") public String refundNotify(HttpServletRequest request, HttpServletResponse response){ Map map = new HashMap<>(); try { //退款解析 RefundNotification refundNotify = nativeService.parseNotify(request, RefundNotification.class); log.info("====> 退款回调通知 ===> {} <====",refundNotify); Status status = refundNotify.getRefundStatus(); if (Status.SUCCESS.equals(status)){ //处理退款状态 //TODO //成功应答 response.setStatus(200); map.put("code","SUCCESS"); map.put("message","成功"); }else { //失败应答 response.setStatus(500); map.put("code","ERROR"); map.put("message","失败"); } return JSONObject.toJSONString(map); } catch (IOException e) { e.printStackTrace(); //失败应答 response.setStatus(500); map.put("code","ERROR"); map.put("message","失败"); return JSONObject.toJSONString(map); } } } ```