微信小程序支付Java工具类

2024-04-16 16:24:32 浏览数 (2)

准备工作

微信支付开发前,需要先获取商家信息,包括商户号AppId证书和密钥

  • • 获取商户号:微信商户平台 申请成为商户 => 提交资料 => 签署协议 => 获取商户号
  • • 获取AppID:微信公众平台 注册服务号 => 服务号认证 => 获取APPID => 绑定商户号
  • • 申请商户证书:登录商户平台 => 选择 账户中心 => 安全中心 => API安全 => 申请API证书 包括商户证书和商户私钥
  • • 获取微信的证书:获取APIv3秘钥 登录商户平台 => 选择 账户中心 => 安全中心 => API安全 => 设置APIv3密钥

工具类

添加依赖

引入微信支付开放平台的 API 依赖,以便能够使用 Java 调用相关 API 接口。

微信支付 Java SDK 地址:

https://github.com/wechatpay-apiv3/wechatpay-java

代码语言:javascript复制
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-java</artifactId>
    <version>0.2.12</version>
</dependency>

支付工具类

  • • JSAPI支付下单:生成预支付订单并返回支付参数
  • • 关闭订单
  • • 微信支付订单号查询订单
  • • 商户订单号查询订单
  • • 申请退款:微信支付订单号和商家订单号二选一;
  • • 退款查询
代码语言:javascript复制
package com.tansci.utils;

import com.alibaba.fastjson2.JSON;
import com.wechat.pay.java.core.Config;
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.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
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.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.system.ApplicationHome;

import java.util.Objects;

/**
 * @path:com.tansci.utils.WxPayUtil.java
 * @className:WxPayUtil.java
 * @description: 微信小程序支付工具类
 * @author:tanyp
 * @editNote:
 */
@Slf4j
public class WxPayUtil {

    // appID
    private static String appid = "wx1fdfgfh149c6353";
    // 商户号
    private static String merchantId = "124589286";
    // 商户证书序列号
    private static String merchantSerialNumber = "3D3D4ADM154FDG44DFG45GF1SDF4JFA8DF95";
    // 商户APIV3密钥
    private static String apiV3Key = "yyh1466255d14dggh524dg666983286";
    // 商户API私钥
    private static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPH5wD/SO6 3jMUdUio0awAwR Ni7s22Csqk8EQLoYbeOzCN 4 bKJ4/jAuJ8CuqaCCCXtCDRlriLW35C7uzeuOiL3tN";
    // 支付回调地址
    private static String payNotifyUrl = "https://tansci.top/api/pay/payCallback";
    // 支付回调地址
    private static String refunNotifyUrl = "https://tansci.top/api/pay/refunCallback";

    private static JsapiServiceExtension jsapiService;

    private static RefundService refundService;

    public static void initConfig() {
        // 初始化商户配置
        Config config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();

        // 初始化服务
        jsapiService = new JsapiServiceExtension.Builder().config(config).build();
        refundService = new RefundService.Builder().config(config).build();
    }

    /**
     * @methodName:jsapi
     * @description:JSAPI支付下单
     * @author:tanyp
     * @Params: [orderId, goodsName, openId, amount]
     * @Return: java.lang.String
     * @editNote:
     */
    public static Object jsapi(String orderId, String goodsName, String openId, Integer amount) {
        try {
            if (Objects.isNull(jsapiService)) {
                initConfig();
            }
            PrepayRequest request = new PrepayRequest();
            request.setAppid(appid);
            request.setMchid(merchantId);
            request.setOutTradeNo(orderId);
            request.setDescription(goodsName);
            request.setNotifyUrl(payNotifyUrl);
            // 支付者
            Payer payer = new Payer();
            payer.setOpenid(openId);
            request.setPayer(payer);
            // 订单金额,单位为分(正数)
            Amount _amount = new Amount();
            _amount.setCurrency("CNY");
            _amount.setTotal(amount);
            request.setAmount(_amount);

            log.info("JSAPI 支付下单请求参数:{}", JSON.toJSON(request));
            PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(request);
            log.info("JSAPI 支付下单返回参数:{}", JSON.toJSON(response));
            return response;
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("支付异常,发送HTTP请求失败:{}", e);
            return e.getMessage();
        } catch (ServiceException e) {
            log.error("支付异常,服务返回状态异常:{}", e);
            return e.getErrorMessage();
        } catch (MalformedMessageException e) {
            log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
            return e.getMessage();
        }
    }

    /**
     * @methodName:closeOrder
     * @description:关闭订单
     * @author:tanyp
     * @Params: [orderId]
     * @Return: java.lang.String
     * @editNote:
     */
    public static String closeOrder(String orderId) {
        try {
            if (Objects.isNull(jsapiService)) {
                initConfig();
            }
            CloseOrderRequest request = new CloseOrderRequest();
            request.setMchid(merchantId);
            request.setOutTradeNo(orderId);
            jsapiService.closeOrder(request);
            return "ok";
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("支付异常,发送HTTP请求失败:{}", e);
            return e.getMessage();
        } catch (ServiceException e) {
            log.error("支付异常,服务返回状态异常:{}", e);
            return e.getErrorMessage();
        } catch (MalformedMessageException e) {
            log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
            return e.getMessage();
        }
    }

    /**
     * @methodName:queryOrderById
     * @description:微信支付订单号查询订单
     * @author:tanyp
     * @Params: [transactionId]
     * @Return: com.wechat.pay.java.service.payments.model.Transaction
     * @editNote:
     */
    public static Transaction queryOrderById(String transactionId) {
        try {
            if (Objects.isNull(jsapiService)) {
                initConfig();
            }
            QueryOrderByIdRequest request = new QueryOrderByIdRequest();
            request.setMchid(merchantId);
            request.setTransactionId(transactionId);
            return jsapiService.queryOrderById(request);
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("支付异常,发送HTTP请求失败:{}", e);
        } catch (ServiceException e) {
            log.error("支付异常,服务返回状态异常:{}", e);
        } catch (MalformedMessageException e) {
            log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
        }
        return null;
    }

    /**
     * @methodName:queryOrderByOutTradeNo
     * @description:商户订单号查询订单
     * @author:tanyp
     * @Params: [orderId]
     * @Return: com.wechat.pay.java.service.payments.model.Transaction
     * @editNote:
     */
    public static Transaction queryOrderByOutTradeNo(String orderId) {
        try {
            if (Objects.isNull(jsapiService)) {
                initConfig();
            }
            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setMchid(merchantId);
            request.setOutTradeNo(orderId);
            return jsapiService.queryOrderByOutTradeNo(request);
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("支付异常,发送HTTP请求失败:{}", e);
        } catch (ServiceException e) {
            log.error("支付异常,服务返回状态异常:{}", e);
        } catch (MalformedMessageException e) {
            log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
        }
        return null;
    }

    /**
     * @methodName:refund
     * @description:申请退款
     * @author:tanyp
     * @Params: [transactionId, orderId, refunId, reason, refunAmount, payAmount]
     * @Return: com.wechat.pay.java.service.refund.model.Refund
     * @editNote:
     */
    public static Refund refund(String transactionId, String orderId, String refunId, String reason, Integer refunAmount, Integer payAmount) {
        try {
            if (Objects.isNull(refundService)) {
                initConfig();
            }
            CreateRequest request = new CreateRequest();
            if (Objects.isNull(transactionId)) {
                request.setTransactionId(transactionId);
            }
            request.setOutTradeNo(orderId);
            request.setOutRefundNo(refunId);
            request.setNotifyUrl(refunNotifyUrl);
            request.setReason(reason);
            // 订单金额,单位为分(正数)
            AmountReq _amount = new AmountReq();
            _amount.setCurrency("CNY");
            _amount.setRefund(refunAmount.longValue());
            _amount.setTotal(payAmount.longValue());
            request.setAmount(_amount);

            return refundService.create(request);
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("退款异常,发送HTTP请求失败:{}", e);
        } catch (ServiceException e) {
            log.error("退款异常,服务返回状态异常:{}", e);
        } catch (MalformedMessageException e) {
            log.error("退款异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
        }
        return null;
    }

    /**
     * @methodName:queryByOutRefundNo
     * @description:退款查询
     * @author:tanyp
     * @Params: [refunId]
     * @Return: com.wechat.pay.java.service.refund.model.Refund
     * @editNote:
     */
    public static Refund queryByOutRefundNo(String refunId) {
        try {
            if (Objects.isNull(refundService)) {
                initConfig();
            }
            QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
            request.setOutRefundNo(refunId);
            return refundService.queryByOutRefundNo(request);
        } catch (HttpException e) {
            // 发送HTTP请求失败
            log.error("退款异常,发送HTTP请求失败:{}", e);
        } catch (ServiceException e) {
            log.error("退款异常,服务返回状态异常:{}", e);
        } catch (MalformedMessageException e) {
            log.error("退款异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
        }
        return null;
    }

}

回调通知

创建一个公开的 HTTP 端点,接受来自微信支付的支付、退款回调通知。

PayController.java

代码语言:javascript复制
package com.tansci.controller;

import com.tansci.common.WrapMapper;
import com.tansci.common.Wrapper;
import com.tansci.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * @ClassName: PayController.java
 * @Description: 支付、退款回调通知
 * @Author: tanyp
 **/
@RestController
@RequestMapping("/api/pay")
public class PayController {

    @Autowired
    private PayService payService;

    @PostMapping("/payCallback")
    public Wrapper payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return WrapMapper.ok(payService.payCallback(request, response));
    }

    @PostMapping("/refunCallback")
    public Wrapper refunCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
        return WrapMapper.ok(payService.refunCallback(request, response));
    }

}

PayService.java

代码语言:javascript复制
package com.tansci.service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

public interface PayService {

    Object payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception;

    Object refunCallback(HttpServletRequest request, HttpServletResponse response) throws Exception;

}

PayServiceImpl.java

代码语言:javascript复制
package com.tansci.service.impl;

import com.alibaba.fastjson2.JSON;
import com.tansci.service.PayService;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
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.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Objects;

import static com.wechat.pay.java.core.http.Constant.*;

@Slf4j
@Service
public class PayServiceImpl implements PayService {

    // 商户号
    private static String merchantId = "124589286";
    // 商户证书序列号
    private static String merchantSerialNumber = "3D3D4ADM154FDG44DFG45GF1SDF4JFA8DF95";
    // 商户APIV3密钥
    private static String apiV3Key = "yyh1466255d14dggh524dg666983286";
    // 商户API私钥
    private static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPH5wD/SO6 3jMUdUio0awAwR Ni7s22Csqk8EQLoYbeOzCN 4 bKJ4/jAuJ8CuqaCCCXtCDRlriLW35C7uzeuOiL3tN";

    @Override
    public Object payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //读取请求体的信息
        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,可直接使用
        // 没有的话,则构造一个
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .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();
        Transaction parse = parser.parse(requestParam, Transaction.class);
        log.info("支付回调参数:{}", JSON.toJSON(parse));

        try {
            /**
             * trade_state:
             * SUCCESS:支付成功
             * REFUND:转入退款
             * NOTPAY:未支付
             * CLOSED:已关闭
             * REVOKED:已撤销(付款码支付)
             * USERPAYING:用户支付中(付款码支付)
             * PAYERROR:支付失败(其他原因,如银行返回失败)
             */
            if ("SUCCESS".equals(parse.getTradeState().toString())) {
                // 成功

                log.info("==========支付回调【支付成功】=============");
            } else if ("USERPAYING".equals(parse.getTradeState().toString())
                    || "NOTPAY".equals(parse.getTradeState().toString())) {
                // 未支付

                 log.info("==========支付回调【未支付】=============");
            } else if ("PAYERROR".equals(parse.getTradeState().toString())
                    || "REVOKED".equals(parse.getTradeState().toString())
                    || "REFUND".equals(parse.getTradeState().toString())
                    || "CLOSED".equals(parse.getTradeState().toString())) {
                // 支付失败

                 log.info("==========支付回调【支付失败】=============");
            }
        } catch (Exception e) {
            log.error("支付回调处理异常:{}", e);
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        log.info("-----------------------支付回调完成-----------------------");
        return HttpStatus.OK;
    }

    @Override
    public Object refunCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //读取请求体的信息
        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,可直接使用
        // 没有的话,则构造一个
        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .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);
        log.info("退款回调参数:{}", JSON.toJSON(parse));

        try {
            /**
             * refund_status:
             * SUCCESS:退款成功
             * CLOSED:退款关闭
             * ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款
             */
            if ("SUCCESS".equals(parse.getRefundStatus().toString())) {
                // 成功

                log.info("==========退款回调【退款失败】=============");
            } else if ("CLOSED".equals(parse.getRefundStatus().toString()) || "ABNORMAL".equals(parse.getRefundStatus().toString())) {
                // 失败

                log.info("==========退款回调【退款失败】=============");
            }
        } catch (Exception e) {
            log.error("退款回调处理异常:{}", e);
            return HttpStatus.INTERNAL_SERVER_ERROR;
        }
        log.info("-----------------------退款回调完成-----------------------");
        return HttpStatus.OK;
    }

}

小程序统一发送消息

微信发送模板消息

0 人点赞