微信小程序支付V3(JAVA版)

微信支付必要参数
微信支付参数获取文档

  1. 小程序APP_ID
  2. 商户号mchid
  3. 微信支付V3秘钥
  4. 微信支付证书编号
  5. 微信支付证书下载

微信支付POM

<dependency>
      <groupId>com.github.wechatpay-apiv3</groupId>
      <artifactId>wechatpay-java</artifactId>
      <version>0.2.11</version>
</dependency>

定义支付接口

import com.wechat.pay.java.service.payments.model.Transaction;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author yao_bing
 * @since 2023-08-03
 */
public interface PaymentStrategy {
    /**
     * 支付
     *
     * @param orderNo 商家订单号
     * @param amount 金额
     * @return
     */
    String pay(String orderNo , String amount);

    /**
     * 关闭支付
     *
     * @param outTradeNo
     */
    void closePay(String outTradeNo);

    /**
     * 查询支付信息
     *
     * @param transactionId
     * @param orderQueryType 1:支付编号 2:商家支付编号
     * @return
     */
    PayInfo findPayInfo(String transactionId , Integer orderQueryType);

    /**
     * 支付回调
     *
     * @param request
     * @return
     * @throws IOException
     */
    Transaction callback(HttpServletRequest request) throws IOException;
}

参数配置

/**
 * 微信支付HTTP请求头相关常量
 * @author yao_bing
 * @return
 */
public final class WechatPayHttpHeaders {
    /**
     * 微信回调参数==>微信序列号
     */
    public static final String WECHATPAY_SERIAL = "Wechatpay-Serial";

    /**
     * 微信回调参数==>应答随机串
     */
    public static final String WECHATPAY_NONCE="Wechatpay-Nonce";
    
    /**
     * 微信回调参数==>应答时间戳
     */
    public static final String WECHATPAY_TIMESTAMP="Wechatpay-Timestamp";
    
    /**
     * 微信回调参数==>应答签名
     */
    public static final String WECHATPAY_SIGNATURE="Wechatpay-Signature";
}

接口实现

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.exception.ValidationException;
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.core.util.PemUtil;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.CloseOrderRequest;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByIdRequest;
import com.wechat.pay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.youying.common.config.FriendBetterConfig;
import com.youying.common.enums.Enums.OrderQueryType;
import com.youying.common.utils.SecurityUtils;
import com.youying.common.utils.StringUtils;
import com.youying.common.utils.uuid.IdUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.util.Base64;

/**
 * @author yao_bing
 * @since 2023-08-03
 */
@Slf4j
public class WechatPayment implements PaymentStrategy {
    private static final String APP_ID = "xxxxxxx";
    private static final String MCH_ID = "xxxxxxx";
    private static final String WECHAT_PAY_PWD = "xxxxxxx";
    private static final String MERCHANT_SERIAL_NUMBER = "xxxxxxx";

    /**
     * 微信支付
     *
     * @param price
     */
    @Override
    @Transactional
    public String pay(String orderNo, String price) {
        log.info("微信支付 >>>>>>>>>>>>>>>>> 金额:{}" , price);
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        // 微信支付需要精确到分,外面传的为元。单位转换。
        BigDecimal total = new BigDecimal(price).multiply(new BigDecimal("100"));
        amount.setTotal(total.intValue());
        Payer payer = new Payer();
        String openId = "自行获取用户openId";
        if (StringUtils.isBlank(openId)) {
            throw new com.youying.common.exception.ServiceException("参数不完整");
        }
        payer.setOpenid(openId);
        request.setAmount(amount);
        request.setPayer(payer);
        request.setAppid(APP_ID);
        request.setMchid(MCH_ID);
        request.setDescription("备注");
        // 回调地址,YML参数配置
        request.setNotifyUrl(FriendBetterConfig.getNotifyUrl());
        request.setOutTradeNo(orderNo);
        
        PrepayResponse response = getJsapiService().prepay(request);
        return response.getPrepayId();
    }

    /**
     * 关闭微信支付
     *
     */
    @Override
    public void closePay(String outTradeNo) {
        CloseOrderRequest closeRequest = new CloseOrderRequest();
        closeRequest.setMchid(MCH_ID);
        closeRequest.setOutTradeNo(outTradeNo);
        getJsapiService().closeOrder(closeRequest);
    }

    /**
     * 查询支付信息
     *
     * @param transactionId
     * @param orderQueryType 1:支付编号 2:商家支付编号 (我是使用的枚举,自行定义)
     * @return
     */
    @Override
    public PayInfo findPayInfo(String transactionId , Integer orderQueryType) {
        PayInfo payInfo = new PayInfo();
        if (OrderQueryType.TRANSACTION_ID.getCode().equals(orderQueryType)) {
            QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
            queryRequest.setTransactionId(transactionId);
            queryRequest.setMchid(MCH_ID);
            try {
                payInfo.setTransaction(getJsapiService().queryOrderById(queryRequest));
            } catch (ServiceException e) {
                log.error(e.getErrorCode(), e);
            }
        } else {
            QueryOrderByOutTradeNoRequest orderByOutTradeNoRequest = new QueryOrderByOutTradeNoRequest();
            orderByOutTradeNoRequest.setOutTradeNo(transactionId);
            orderByOutTradeNoRequest.setMchid(MCH_ID);
            try {
                payInfo.setTransaction(getJsapiService().queryOrderByOutTradeNo(orderByOutTradeNoRequest));
            } catch (ServiceException e) {
                log.error(e.getErrorCode(), e);
            }
        }
        return payInfo;
    }

    /**
     * 微信回调
     *
     * @param request
     * @return
     * @throws IOException
     */
    @Override
    @Transactional
    public Transaction callback(HttpServletRequest request) throws IOException {
        log.info("微信回调 >>>>>>>>>>>>>>>>> ");
        BufferedReader br = request.getReader();
        String str;
        StringBuilder builder = new StringBuilder();
        while ((str = br.readLine()) != null) {
            builder.append(str);
        }

        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(request.getHeader(WechatPayHttpHeaders.WECHATPAY_SERIAL))
                .nonce(request.getHeader(WechatPayHttpHeaders.WECHATPAY_NONCE))
                .timestamp(request.getHeader(WechatPayHttpHeaders.WECHATPAY_TIMESTAMP))
                .signature(request.getHeader(WechatPayHttpHeaders.WECHATPAY_SIGNATURE))
                .body(builder.toString())
                .build();

        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(MCH_ID)
                .privateKeyFromPath(FriendBetterConfig.getPrivateKeyFromPath())
                .merchantSerialNumber(MERCHANT_SERIAL_NUMBER)
                .apiV3Key(WECHAT_PAY_PWD)
                .build();

        NotificationParser parser = new NotificationParser(config);

        try {
            // 以支付通知回调为例,验签、解密并转换成 Transaction
            return parser.parse(requestParam, Transaction.class);
        } catch (ValidationException e) {
            log.error("sign verification failed", e);
            log.error(e.getMessage(), e);
        }
        return new Transaction();
    }

    /**
     * 创建小程序支付服务
     *
     * @return
     */
    protected JsapiService getJsapiService() {
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(MCH_ID)
                        .privateKeyFromPath(FriendBetterConfig.getPrivateKeyFromPath())
                        .merchantSerialNumber(MERCHANT_SERIAL_NUMBER)
                        .apiV3Key(WECHAT_PAY_PWD)
                        .build();
        config.createSigner().getAlgorithm();
        return new JsapiService.Builder().config(config).build();
    }

    /**
     * 获取私钥
     *
     * @return
     */
    public static PrivateKey getPrivateKey() {
    	// 路径为全路径,YML参数配置
        return PemUtil.loadPrivateKeyFromPath(FriendBetterConfig.getPrivateKeyFromPath());
    }

    /**
     * 获取支付签名
     *
     * @param prepayId
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeyException
     * @throws SignatureException
     */
    public static WechatPay sign(String prepayId) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        String timeStamp = System.currentTimeMillis() / 1000 + "";
        String nonceStr = IdUtils.simpleUUID();
        String packageStr = "prepay_id=" + prepayId;

		// 不能去除'.append("\n")',否则失败
        StringBuilder signStr = new StringBuilder();
        signStr.append(APP_ID).append("\n");
        signStr.append(timeStamp).append("\n");
        signStr.append(nonceStr).append("\n");
        signStr.append(packageStr).append("\n");

        byte[] message = signStr.toString().getBytes(StandardCharsets.UTF_8);

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey());
        sign.update(message);
        String signStrBase64 = Base64.getEncoder().encodeToString(sign.sign());

        WechatPay wechatPay = new WechatPay();
        wechatPay.setPrepayId(prepayId);
        wechatPay.setTimeStamp(timeStamp);
        wechatPay.setNonceStr(nonceStr);
        wechatPay.setPackageStr(packageStr);
        wechatPay.setSign(signStrBase64);
        return wechatPay;
    }
}

使用设计模式,便于以后扩充支付方式

import com.wechat.pay.java.service.payments.model.Transaction;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * @author yao_bing
 * @since 2023-08-03
 */
public class PaymentContext {
    private PaymentStrategy paymentStrategy;

    public PaymentStrategy getPaymentStrategy() {
        return paymentStrategy;
    }

    public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
        this.paymentStrategy = paymentStrategy;
    }

    public String executePayment(String orderNo, String amount) {
        return paymentStrategy.pay(orderNo, amount);
    }

    public Transaction callback(HttpServletRequest request) throws IOException {
        return paymentStrategy.callback(request);
    }

    public PayInfo findPayInfo(String transactionId , Integer orderQueryType){
        return paymentStrategy.findPayInfo(transactionId, orderQueryType);
    }
}

支付、支付回调使用。

	// 微信回调地址必须与这一致
	@PostMapping("/pay/wechatPayCallback")
    public void wechatCallback(HttpServletRequest request) throws IOException {
        log.info("支付回调 =======================》 {}" , request);
        PaymentContext context = new PaymentContext();
        PaymentStrategy weChatPayment = new WechatPayment();
        context.setPaymentStrategy(weChatPayment);
        Transaction transaction = context.callback(request);
        // 自行业务处理
        userOrderService.callback(transaction);
    }

    /**
     * 支付
     *
     * @param userReceivingRecordsId
     */
    @PostMapping("/pay/{userReceivingRecordsId}")
    @Log(title = "支付", businessType = BusinessType.INSERT)
    public R<WechatPay> pay(@PathVariable("userReceivingRecordsId") Long userReceivingRecordsId) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        PaymentContext context = new PaymentContext();
        PaymentStrategy weChatPayment = new WechatPayment();
        context.setPaymentStrategy(weChatPayment);
        String prepayId = context.executePayment(orderNo, String.valueOf(price));

		WechatPay wechatPay = WechatPayment.sign(prepayId);
        return wechatPay;
    }

微信支付返回参数

/**
 * @author yao_bing
 * @since 2023-08-19
 */
@Data
public class WechatPay {
    /**
     * 预支付编号
     */
    private String prepayId;

    /**
     * 时间戳
     */
    private String timeStamp;

    /**
     * 随机字符串
     */
    private String nonceStr;

    private String packageStr;

    /**
     * 支付签名
     */
    private String sign;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值