基于WxJava的微信支付

1,783 阅读2分钟

WxJava是一款基于Java语言的微信开发Java SDK,它提供了微信支付,开放平台,小程序,企业微信,公众号等多个平台的API接口,并将其封装为易于调用的Java方法,方便Java开发者快速开发与微信相关的应用。

GitHub地址:https://github.com/Wechat-Group/WxJava

如果不想使用WxJava,使用官方Api,可以查看,我的另一篇文章:微信支付JAVA V3APP下单整合

  • 使用WxJava只需要引入开发相关模块的maven依赖即可
<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-pay</artifactId>
    <version>4.5.2.B</version>
</dependency>
  • 微信配置参数
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

/**
 * @author chilei
 * @description
 * @since 2023/7/20
 */
@Component
@RefreshScope
@Getter
@Setter
public class WxPayProperties {
    /**
     * 设置微信公众号或者小程序等的appid
     */
    @Value("${wx.pay.appid:}")
    private String appid;
    /**
     * 微信支付商户号
     */
    @Value("${wx.pay.mchId:}")
    private String mchId;

    @Value("${wx.pay.privateKeyContent:}")
    private String privateKeyContent;

    @Value("${wx.pay.privateCertContent:}")
    private String privateCertContent;

    /**
     * 下单回调地址
     */
    @Value("${wx.pay.notifyUrl:}")
    private String notifyUrl;

    /**
     * 退款回调地址
     */
    @Value("${wx.pay.refundUrl:}")
    private String refundUrl;

    /**
     * apiV3key
     */
    @Value("${wx.pay.apiV3key:}")
    private String apiV3key;
}
  • 微信支付配置类

import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;

/**
 * @author chilei
 * @description 微信支付配置类
 * @since 2023/7/20
 */
@Configuration
@ConditionalOnClass(WxPayService.class)
public class WxPayConfiguration {

    @Resource
    private WxPayProperties wxPayProperties;

    @Autowired
    public WxPayConfiguration(WxPayProperties wxPayProperties) {
        this.wxPayProperties = wxPayProperties;
    }

    @Bean
    @ConditionalOnMissingBean
    public WxPayService wxService() {
        WxPayService wxPayService = new WxPayServiceImpl();
        if (StringUtils.isNotBlank(this.wxPayProperties.getAppid())) {
            wxPayService.setConfig(wxPayConfig());
        }
        return wxPayService;
    }

    @Bean
    public WxPayConfig wxPayConfig() {
        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(StringUtils.trimToNull(this.wxPayProperties.getAppid()));
        payConfig.setMchId(StringUtils.trimToNull(this.wxPayProperties.getMchId()));
        payConfig.setPrivateKeyContent(this.wxPayProperties.getPrivateKeyContent().getBytes(StandardCharsets.UTF_8));
        payConfig.setPrivateCertString(this.wxPayProperties.getPrivateCertContent());
        payConfig.setApiV3Key(this.wxPayProperties.getApiV3key());
        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);
        return payConfig;
    }
}
  • 微信支付V3 api
import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.shinsson.heeiya.app.config.wx.WxPayProperties;
import com.shinsson.utils.MyDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.Signature;
import java.time.OffsetDateTime;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author chilei
 * @description 微信支付V3 api
 * @since 2023/7/20
 */
@Component
@Slf4j
public class WxPayUtil {
    @Resource
    private WxPayService wxPayService;
    @Resource
    private WxPayProperties wxPayProperties;

    /**
     * 统一支付下单接口
     *
     * @param outTradeNo     商户订单号
     * @param totalFee       下单金额(单位:分)
     * @param productContent 商品描述
     * @param timeExpire     遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE 示例值:2018-06-08T10:34:56+08:00
     */
    public Map<String, String> createOrder(String outTradeNo, Integer totalFee, String productContent, OffsetDateTime timeExpire) {
        WxPayUnifiedOrderV3Request wxPayUnifiedOrderV3Request = new WxPayUnifiedOrderV3Request();
        wxPayUnifiedOrderV3Request.setDescription(productContent);
        wxPayUnifiedOrderV3Request.setTimeExpire(timeExpire.format(MyDateUtil.Y_M_D_T_H_M_S_8));
        WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
        amount.setTotal(totalFee);
        wxPayUnifiedOrderV3Request.setAmount(amount);
        wxPayUnifiedOrderV3Request.setOutTradeNo(outTradeNo);
        wxPayUnifiedOrderV3Request.setNotifyUrl(wxPayProperties.getNotifyUrl());
        try {
            WxPayUnifiedOrderV3Result.AppResult appResult = wxPayService.createOrderV3(TradeTypeEnum.APP, wxPayUnifiedOrderV3Request);
            return this.startWxPay(appResult);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 商户订单号查询订单  微信官方文档: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_2.shtml
     *
     * @param outTradeNo 商户订单号
     * @return WxPayOrderQueryV3Result 返回对象
     */
    public WxPayOrderQueryV3Result selectOrder(String outTradeNo) {
        WxPayOrderQueryV3Request wxPayOrderQueryV3Request = new WxPayOrderQueryV3Request();
        wxPayOrderQueryV3Request.setOutTradeNo(outTradeNo);
        try {
            return wxPayService.queryOrderV3(wxPayOrderQueryV3Request);
        } catch (WxPayException e) {
            throw new RuntimeException(e.getMessage());
        }
    }


    /**
     * 退款调用
     *
     * @param outTradeNo  商户订单号
     * @param outRefundNo 商户退款单号
     * @param reason      退款原因
     * @param refund      退款金额(单位:分)
     * @param total       原订单金额(单位:分)
     * @return WxPayRefundV3Result 返回值参考: https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_9.shtml
     */
    public WxPayRefundV3Result refundOrder(String outTradeNo, String outRefundNo, String reason, Integer refund, Integer total) {
        WxPayRefundV3Request wxPayRefundV3Request = new WxPayRefundV3Request();
        wxPayRefundV3Request.setOutTradeNo(outTradeNo);
        wxPayRefundV3Request.setOutRefundNo(outRefundNo);
        wxPayRefundV3Request.setReason(reason);
        wxPayRefundV3Request.setNotifyUrl(wxPayProperties.getRefundUrl());
        WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
        amount.setRefund(refund);
        amount.setTotal(total);
        amount.setCurrency("CNY");
        wxPayRefundV3Request.setAmount(amount);
        try {
            return wxPayService.refundV3(wxPayRefundV3Request);
        } catch (WxPayException e) {
            throw new RuntimeException(e.getMessage());
        }
    }


    /**
     * 封装二次加签数据 APP调起支付API
     *
     * @param appResult 预下单数据
     * @return Map 返回值
     */
    public Map<String, String> startWxPay(WxPayUnifiedOrderV3Result.AppResult appResult) {
        Map<String, String> map = new HashMap<>(7);
        //时间戳
        long timestamp = System.currentTimeMillis() / 1000;
        String token = getSign(appResult.getNoncestr(), appResult.getAppid(), appResult.getPrepayId(), timestamp);
        map.put("appid", appResult.getAppid());
        map.put("partnerId", appResult.getPartnerId());
        map.put("prepayId", appResult.getPrepayId());
        map.put("package", appResult.getPackageValue());
        map.put("nonceStr", appResult.getNoncestr());
        map.put("timestamp", String.valueOf(timestamp));
        map.put("sign", token);
        return map;
    }

    /**
     * 获取签名
     *
     * @param nonceStr  随机数
     * @param appId     微信公众号或者小程序等的appid
     * @param prepayId  预支付交易会话ID
     * @param timestamp 时间戳 10位
     * @return String 新签名
     */
    String getSign(String nonceStr, String appId, String prepayId, long timestamp) {

        //从下往上依次生成
        String message = buildMessage(appId, timestamp, nonceStr, prepayId);
        //签名
        return sign(message.getBytes(StandardCharsets.UTF_8));
    }

    String sign(byte[] message) {
        try {
            //签名方式
            Signature sign = Signature.getInstance("SHA256withRSA");
            //私钥,通过MyPrivateKey来获取,这是个静态类可以接调用方法 ,需要的是_key.pem文件的绝对路径配上文件名
            sign.initSign(wxPayService.getConfig().getPrivateKey());
            sign.update(message);
            return Base64.getEncoder().encodeToString(sign.sign());
        } catch (Exception e) {
            throw new RuntimeException("签名异常,请检查参数或商户私钥");
        }
    }

    /**
     * 按照前端签名文档规范进行排序,\n是换行
     *
     * @param nonceStr  随机数
     * @param appId     微信公众号或者小程序等的appid
     * @param prepayId  预支付交易会话ID
     * @param timestamp 时间戳 10位
     * @return String 新签名
     */
    String buildMessage(String appId, long timestamp, String nonceStr, String prepayId) {
        return appId + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + prepayId + "\n";
    }
}
  • 简单调用
@Service
public class WxOrderService {

    @Resource
    private WxPayUtil wxPayUtil;

    /**
     * 创建微信订单
     *
     * @param orderNo       订单号
     * @param orderRequest  创建订单参数
     * @return 换起押金支付的字符串
     */
    public String createWxOrder(String orderNo, OrderRequest orderRequest) {
        //微信支付,金额 单位为:分
        BigDecimal decimal = orderRequest.getAmount().multiply(new BigDecimal(100));
        Map<String, String> wxOrder = wxPayUtil.createOrder(orderNo, decimal.intValue(), "描述", OffsetDateTime.now());
        String orderStr = JSON.toJSONString(wxOrder);
        return orderStr;
    }
}

如有错误的地方,请指出,感谢路过的各位大佬。