Springboot对接微信支付SDK,超简单版:wechatpay-apiv3

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

一:引入依赖

我用的是微信官方推荐的java版本的SDK,maven坐标如下

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

二:准备微信支付所必要的商户参数,具体如下:

xxx:
  pay:
    wechat-pay:
      #商户号
      merchantId: xxxxxxx
      #商户API私钥(很长)
      privateKey: xxxxxxx...
      #商户API公钥 (也很长)
      publicKey: xxxxxx....
      #微信支付的商户平台证书序列号
      merchantSerialNo: xxxxxxxxxxx
      #微信公众号ID
      appid: xxxxxxxxx
      #支付回调地址,必须是外网可访问的,文中会提供对应工具
      notify_url: https://xxxxx
      #支付回调地址,同上,我写成了两个接口分开处理业务
      refunds_notify_url: https://xxxxxxx
      #秘钥, 申请微信支付商户平台证书的时候能看见
      apiV3Key: xxxxxxx 
      #域名
      domain: https://api.mch.weixin.qq.com

商户api证书:

申请完成之后商户api证书会下载成压缩包,解压之后就是上图的文件了,其中apiclient_key.pem就是商户私钥, 另外一个就是公钥了,具体都是做什么的可以去看官网或者其他文章

        私钥的加载方式有两种

               1.一种是将私钥中的字符串直接复制到代码中(或者.yaml配置文件中),切记:不能有换行.

               2.第二种是用加载文件的方式, 将api证书文件存到你的电脑或者服务器上,在配置文件中分别记录对应的位置即可,这种方式的具体加载方式看官网即可.

三:编写配置类WechatPayConfig.java

import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.billdownload.BillDownloadServiceExtension;
import com.wechat.pay.java.service.payments.app.AppServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.refund.RefundService;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;
import java.util.List;

/**
 * @author: wxj
 * @Desc:
 * @create: 2023-11-06 13:15:34
 **/

@Data
@Configuration
@NoArgsConstructor
public class WechatPayConfig {
    @Value("${}")
    private String merchantId;
    @Value("${}")
    private String privateKey;
    @Value("${}")
    private String publicKey;
    @Value("${}")
    private String serialNo;
    @Value("${}")
    private String appid;
    @Value("${}")
    private String notifyUrl;
    @Value("${}")
    private String apiV3Key;
    @Value("${}")
    private String domain;
    @Value("${}")
    private String refundNotifyUrl;


    /**
     * 初始化最基础的商户配置
     *
     * @return Config
     */
    @Bean
    @Order(0)
    public Config initConfig() {
        return new RSAAutoCertificateConfig.Builder()
            .merchantId(merchantId)
            .privateKey(privateKey)
            .merchantSerialNumber(serialNo)
            .apiV3Key(apiV3Key)
            .build();
    }

    /**
     * 初始化小程序需要的SDK支持服务
     *
     * @return JsapiService
     */
    @Bean
    @Order(1)
    public JsapiServiceExtension initJsapiService() {
        Config config = SpringUtils.getBean(Config.class);
        return new JsapiServiceExtension.Builder().config(config).build();
    }

    /**
     * 初始化安卓app需要的SDK支持服务
     *
     * @return AppService
     */
    @Bean
    @Order(2)
    public AppServiceExtension initAppService() {
        Config config = SpringUtils.getBean(Config.class);
        return new AppServiceExtension.Builder().config(config).build();
    }

    /**
     * 初始化退款服务需要的SDK支持服务
     *
     * @return RefundService
     */
    @Bean
    @Order(3)
    public RefundService initRefundService() {
        Config config = SpringUtils.getBean(Config.class);
        return new RefundService.Builder().config(config).build();
    }

    @Bean
    @Order(4)
    public BillDownloadServiceExtension initBillDownloadServiceExtension() {
        Config config = SpringUtils.getBean(Config.class);
        return new BillDownloadServiceExtension.Builder().config(config).build();
    }
    /**
     * 加载私钥
     */
    public PrivateKey getPrivateKey() {
        try {
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        } catch (Exception e) {
            throw new RuntimeException("加载私钥异常,请检查私钥文件", e);
        }
    }


}

                业务代码就使用小程序支付代码为例

小程序api文档地址

四:下单相关接口(4个)

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson2.JSONObject;
import com.wechat.pay.java.core.Config;
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.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 lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Slf4j
@Component
public class WechatAppletPayService {
    @Resource
    private Config config;
    @Resource
    private JsapiServiceExtension jsapiService;
    @Resource
    private RefundService refundService;
    @Resource
    private WechatPayConfig payConfig;
    private final ReentrantLock lock = new ReentrantLock();

    /**
     * 创建订单
     *
     * @param orderBo 订单参数对象
     * @return
     */
    public R<PrepayWithRequestPaymentResponse> createOrder(CourseOrderBo orderBo) {

        // 创建未支付订单对象并插入数据库, 具体根据需求实现
        // Order 创建的订单对象 cusWechat用户在咱们小程序对应的对象, 主要需要用户的openId,openId是某个微信用户在第一次进小程序的时候就分配的唯一ID,不会变,还是不知道openId是啥的去百度搜吧
        return this.pushOrder(order, cusWechat);
    }

    /**
     * 拉起支付
     *
     * @param orderBo
     * @param cusWechat
     * @return
     */
    public R<PrepayWithRequestPaymentResponse> pushOrder(CourseOrderBo orderBo, CusWechat cusWechat) {
        String appid = cusWechat.getAppid();
        String openid = cusWechat.getOpenid();

        PrepayRequest request = new PrepayRequest();
        request.setMchid(payConfig.getMerchantId());
        request.setAppid(appid);
        request.setDescription(orderBo.getGoodsName());
        request.setOutTradeNo(orderBo.getOrderId());
        // 2023-11-30T15:45:48+08:00
        String timeExpire = DateUtil.offsetMinute(DateUtil.date(), 10).toString().replace(" ", "T") + "+08:00";
        request.setTimeExpire(timeExpire);
        JSONObject attach = new JSONObject(1);
        attach.put("couponCode", orderBo.getCouponCode());
        request.setAttach(attach.toString());
        request.setNotifyUrl(String.format(payConfig.getNotifyUrl(), WechatConstants.SourcePathEnum.PAY_FROM_APPLET));
        Amount amount = new Amount();
        amount.setTotal(orderBo.getTotalAmount().intValue());
        amount.setCurrency("CNY");
        request.setAmount(amount);
        Payer payer = new Payer();
        payer.setOpenid(openid);
        request.setPayer(payer);
        PrepayWithRequestPaymentResponse prepay = jsapiService.prepayWithRequestPayment(request);
        return R.ok(prepay);
    }

    /**
     * 通过微信交易ID查询订单
     *
     * @param id
     * @param transactionId
     * @return
     */
    public R getOrderByTransactionId(Long id, String transactionId) {
        QueryOrderByIdRequest request = new QueryOrderByIdRequest();
        request.setMchid(payConfig.getMerchantId());
        request.setTransactionId(transactionId);
        Transaction transactionResp = jsapiService.queryOrderById(request);
        Transaction.TradeStateEnum tradeState = transactionResp.getTradeState();
        CourseTransactionVo transactionVo = transactionService.queryById(id);
        
    }

    public R getOrderByOrderId(String orderId) {
        QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
        request.setMchid(payConfig.getMerchantId());
        request.setOutTradeNo(orderId);
        Transaction transactionResp = jsapiService.queryOrderByOutTradeNo(request);
        String appid = transactionResp.getAppid();
        String openId = transactionResp.getPayer().getOpenid();
        Long aLong = transactionResp.getAmount().getTotal().longValue();
        Long aLong1 = transactionResp.getAmount().getPayerTotal().longValue();
        String transactionId = transactionResp.getTransactionId();
        Transaction.TradeStateEnum tradeState = transactionResp.getTradeState();
        // 具体业务自行处理
        return R.ok();
    }

    @Transactional(rollbackFor = Exception.class)
    public R<String> closeOrder(String orderId) {
        try {
            CloseOrderRequest request = new CloseOrderRequest();
            request.setMchid(payConfig.getMerchantId());
            request.setOutTradeNo(orderId);
            jsapiService.closeOrder(request);
            // 具体业务自行处理
            return R.ok();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

这里包含了下单,根据商户订单号查询微信支付订单,根据微信支付订单号查询微信支付订单以及关闭订单四个接口,具体业务代码需要兄弟你自己根据你们项目需求写.

五:退款相关接口(2个)

import cn.hutool.core.util.EnumUtil;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.*;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @author: wxj
 * @Desc: 微信支付退款服务类
 * @create: 2023-12-06 17:17:33
 **/

@Service
public class WechatRefundService {

    @Resource
    private WechatPayConfig payConfig;
    @Resource
    private RefundService refundService;

    /**
     * 后台向微信支付申请退款
     *
     * @param bo 参数对象
     *           orderId 退款的订单ID
     *           refundAmount 实际退款金额(由相关人员与客户商谈)
     *           refundReason 退款理由
     * @return 处理结果
     */
    public R<String> refundsOrder(CourseOrderBo bo) {
        try {
            // 具体业务自行实现
            CreateRequest request = new CreateRequest();
            request.setReason(reasonEnum.getReason());
            Integer platform = order.getPlatform();
            WechatConstants.PlatformEnum platformEnum = EnumUtil.likeValueOf(WechatConstants.PlatformEnum.class, platform);
            AmountReq amount = new AmountReq();
            amount.setRefund(refundAmount);
            amount.setTotal(order.getTotalAmount());
            amount.setCurrency("CNY");
            request.setAmount(amount);
            request.setOutRefundNo(refundTransaction.getId().toString());
            request.setOutTradeNo(orderId);
            Refund refund = refundService.create(request);

            Status status_wx = refund.getStatus();
            
            String refundId_wx = refund.getRefundId();
            String transactionId_wx = refund.getTransactionId();
            String orderId_wx = refund.getOutTradeNo();
            com.wechat.pay.java.service.refund.model.Amount amount_wx = refund.getAmount();
            long total_wx = amount_wx.getTotal();
            long refundAmount_wx = amount_wx.getRefund();
            Long playTotal_wx = amount_wx.getPayerTotal();
            refundTransaction.setTransactionId(transactionId_wx);
            refundTransaction.setPayAmount(playTotal_wx);
            refundTransaction.setTotalAmount(total_wx);
            refundTransaction.setOrderId(orderId_wx);
            refundTransaction.setRefundId(refundId_wx);
            transactionService.updateByBo(BeanCopyUtils.copy(refundTransaction, CourseTransactionBo.class));
            return R.ok("申请成功, 请注意查收退款记录");
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        }
    }

    public R getRefundsOrder(Long id) {
        try {
            // 具体业务自行处理 此处ID为商户系统流水ID
            QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
            request.setSubMchid(payConfig.getMerchantId());
            request.setOutRefundNo(transaction.getId().toString());
            Refund refundResp = refundService.queryByOutRefundNo(request);
           
            return R.fail();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

六:下载账单类接口(2个)

import cn.hutool.core.date.DateUtil;
import com.wechat.pay.java.service.billdownload.BillDownloadServiceExtension;
import com.wechat.pay.java.service.billdownload.DigestBillEntity;
import com.wechat.pay.java.service.billdownload.model.AccountType;
import com.wechat.pay.java.service.billdownload.model.BillType;
import com.wechat.pay.java.service.billdownload.model.GetFundFlowBillRequest;
import com.wechat.pay.java.service.billdownload.model.GetTradeBillRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;

/**
 * @author: wxj
 * @Desc: 微信支付下载服务类
 * @create: 2023-12-06 17:37:49
 **/

@Slf4j
@Service
public class WechatBillDownloadService {
    @Resource
    private BillDownloadServiceExtension downloadService;

    /**
     * @param billDate    日期 不填默认昨天
     * @param billTypeStr 导出内容类型
     *                    ALL: 返回当日所有订单信息(不含充值退款订单)
     *                    SUCCESS: 返回当日成功支付的订单(不含充值退款订单)
     *                    REFUND: 返回当日退款订单(不含充值退款订单)
     *                    RECHARGE_REFUND: 返回当日充值退款订单
     *                    ALL_SPECIAL: 返回个性化账单当日所有订单信息
     *                    SUC_SPECIAL: 返回个性化账单当日成功支付的订单
     *                    REF_SPECIAL: 返回个性化账单当日退款订单
     * @param tarType     压缩类型 不填默认ZGIP
     * @return 流
     */

    public R<InputStream> exportTransactions(String billDate, String tarType, String billTypeStr, HttpServletResponse resp) {
        if (StringUtils.isEmpty(billDate)) {
            billDate = DateUtil.format(DateUtil.yesterday(), "yyyy-MM-DD");
        }
        BillType billType = BillType.ALL;
        try {
            billType = BillType.valueOf(billTypeStr);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        GetTradeBillRequest request = new GetTradeBillRequest();
        request.setBillDate(billDate);
        request.setBillType(billType);
        request.setTarType(null);
        DigestBillEntity tradeBill = downloadService.getTradeBill(request);
        return R.ok(tradeBill.getInputStream());
    }


    public R<InputStream> exportFundflow(String billDate, String accountTypeStr, HttpServletResponse resp) {
        if (StringUtils.isEmpty(billDate)) {
            billDate = DateUtil.format(DateUtil.yesterday(), "yyyy-MM-DD");
        }
        AccountType accountType = AccountType.ALL;
        try {
            accountType = AccountType.valueOf(accountTypeStr);
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        GetFundFlowBillRequest request = new GetFundFlowBillRequest();
        request.setBillDate(billDate);
        request.setTarType(null);
        request.setAccountType(accountType);
        DigestBillEntity fundFlowBill = downloadService.getFundFlowBill(request);
        return R.ok(fundFlowBill.getInputStream());
    }

}

       

 安卓app的接口大差不差也都这样,不知道方法或者参数接收的兄弟可以看SDK源码,实在找不到idea不能搜索jar中的代码,建议反编译之后再用idea打开搜索,毕竟我就是这么做的...

这些接口暂时还没完整测试, 过段时间测通过上线了再把支付宝的也分享出来...

                         ~~写完了,溜溜球~~~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值