java微信小程序调用微信支付apiv3接口(最简洁代码)

2023年使用SDK实现小程序加Java后端完成V3接口支付_微信小程序支付sdk-CSDN博客

代码出自↑,借鉴前人栽树,后人乘凉的帖子,成功完成微信支付操作。

代码简洁的原因是调用了github的IJPAY的聚合支付

附上gitee地址:https://gitee.com/javen205/IJPay

感谢大佬们栽培的大树,让没接过支付的我也能成功上手

下面是代码层。

配置类(需要提前在yml中配置好所需参数

package com.ruoyi.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

/**
 * 小程序获取配置类
 *
 * @author pzy
 * @version 0.1.0
 * @description TODO
 */
@Slf4j
@Data
@Component
@ConfigurationProperties(prefix = "wechat.mini-app")
public class WechatConfigProperties {



    private String appId;

    private String appSecret;

    //# 证书序列号
    private String mchSerialNo;

    //#平台证书地址,平台证书需要手动下载或者使用v3接口进行下载
    private String platformCertPath;

    //客户证书地址,也就是apiclient_cert.pem的路径
    private String apiClientCertPath;

    //#证书密钥地址,也就是apiclient_key.pem的路径
    private String apiClientKeyPath;

    //商户号
    private String mchId;

    //apiv3秘钥w
    private String apiKey;

    //微信服务器地址
    private String domain;

    //回调,自己的回调地址,暂时内网穿透
    private String notifyDomain;

service类

package com.ruoyi.service;

import com.ijpay.wxpay.WxPayApiConfig;
import com.ruoyi.vo.IWxPayParamVO;

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

public interface WechatPayService {
    /**
     * 微信统一下单接口
     *
     * @return Map
     * @throws Exception 异常
     */
    Map<String, String> doUnifiedOrder(IWxPayParamVO iWxPayParamVO) throws Exception;

    /**
     * 获取v3的证书
     *
     * @return String类型
     * @throws IOException IO异常
     */
    String createPlatformCert() throws IOException;

    /**
     * 微信支付回调接口
     *
     * @param request  请求
     * @param response 详情
     */
    void callBack(HttpServletRequest request, HttpServletResponse response);

    /**
     * 查询账单
     *
     * @param outTradeNo
     * @return
     */
    String query(String outTradeNo);

serviceimpl类

package com.ruoyi.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.AuthTypeEnum;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.kit.AesUtil;
import com.ijpay.core.kit.HttpKit;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.core.utils.DateTimeZoneUtil;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.BasePayApiEnum;
import com.ijpay.wxpay.enums.v3.CertAlgorithmTypeEnum;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import com.ruoyi.config.WechatConfigProperties;
import com.ruoyi.domain.XcCustomer;
import com.ruoyi.mapper.XcCustomerMapper;
import com.ruoyi.service.WechatPayService;
import com.ruoyi.vo.IWxPayParamVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.FileOutputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.RefundModel;

import static com.ijpay.wxpay.WxPayApiConfigKit.getWxPayApiConfig;

@Service
@Slf4j
public class WechatPayServiceImpl implements WechatPayService {
    @Autowired
    private XcCustomerMapper xcCustomerMapper ;

    private String notifyUrl;
    private String refundNotifyUrl;


    private  final WechatConfigProperties wechatConfigProperties;

    public WechatPayServiceImpl(WechatConfigProperties wechatConfigProperties) {
        this.wechatConfigProperties = wechatConfigProperties;
    }


    /**
     * 微信统一下单接口
     *
     * @return Map
     * @throws Exception 异常
     */
    @Override
    public Map<String, String> doUnifiedOrder(IWxPayParamVO iWxPayParamVO) throws Exception {
        //根据用户手机号查openid
        String openId = "";
        XcCustomer xcCustomer = new XcCustomer();
        xcCustomer.setNumber(iWxPayParamVO.getAccount());
        List<XcCustomer> xcCustomers = xcCustomerMapper.selectXcCustomerList(xcCustomer);
        for (XcCustomer xcCustomer1:xcCustomers){
            openId = xcCustomer1.getOpenId();
        }
        //先写死1, 1就是1分钱,100=1元
        String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
        UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                .setAppid(wechatConfigProperties.getAppId())
                .setMchid(wechatConfigProperties.getMchId())
                .setDescription("支付说明")
                //这是随机数
                .setOut_trade_no(PayKit.generateStr())
                .setTime_expire(timeExpire)
                .setAttach("附加说明")
                //回调地址
                .setNotify_url(wechatConfigProperties.getNotifyDomain())
                .setAmount(new Amount().setTotal(iWxPayParamVO.getAmount()))
                .setPayer(new Payer().setOpenid(openId));
        log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));

        IJPayHttpResponse response = WxPayApi.v3(
                RequestMethodEnum.POST,
                WxDomainEnum.CHINA.toString(),
                BasePayApiEnum.JS_API_PAY.toString(),
                wechatConfigProperties.getMchId(),
                getSerialNumber(),
                null,
                wechatConfigProperties.getApiClientKeyPath(),
                JSONUtil.toJsonStr(unifiedOrderModel)
        );
        log.info("统一下单响应 {}", response);
        Map<String, String> map = new HashMap<>(16);
        //这个是证书文件,先写死,后续调整成读取证书文件的服务器存放地址,根据证书序列号查询对应的证书来验证签名结果
        boolean verifySignatures = WxPayKit.verifySignature(response, wechatConfigProperties.getApiClientCertPath());
        log.info("verifySignature: {}", verifySignatures);
        if (response.getStatus() == HttpServletResponse.SC_OK) {
            // 根据证书序列号查询对应的证书来验证签名结果
            String body = response.getBody();
            JSONObject jsonObject = JSONUtil.parseObj(body);
            String prepayId = jsonObject.getStr("prepay_id");
            // 私钥
            map = WxPayKit.jsApiCreateSign(wechatConfigProperties.getAppId(), prepayId, wechatConfigProperties.getApiClientKeyPath());
            log.info("唤起支付参数:{}", map);
        }
        // todo 微信预支付的订单新入库,为了业务查询记录
        return map;
    }

    /**
     * 微信支付回调接口
     *
     * @param request  请求
     * @param response 详情
     */
    @Override
    public void callBack(HttpServletRequest request, HttpServletResponse response) {
        log.info("收到微信支付回调");
        Map<String, String> map = new HashMap<>(12);
        try {
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");

            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpKit.readData(request);
            log.info("支付通知密文 {}", result);
            // 根据证书序列号查询对应的证书来验证签名结果
            String platformCertPath = this.wechatConfigProperties.getPlatformCertPath();
            //这个商户号对应的那个V3秘钥
            String mckKey = "";
            //需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp, mckKey, platformCertPath);
            log.info("支付通知明文 {}", plainText);
            //这个就是具体的业务情况
            savePayPlainText(plainText);
            //回复微信
            if (StrUtil.isNotEmpty(plainText)) {
                response.setStatus(200);
                map.put("code", "SUCCESS");
                map.put("message", "SUCCESS");
            } else {
                response.setStatus(500);
                map.put("code", "ERROR");
                map.put("message", "签名错误");
            }
            response.setHeader("Content-type", ContentType.JSON.toString());
            response.getOutputStream().write(JSONUtil.toJsonStr(map).getBytes(StandardCharsets.UTF_8));
            response.flushBuffer();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询账单
     *
     * @param outTradeNo
     * @return
     */
    @Override
    public String query(String outTradeNo) {
        try {
            Map<String, String> params = new HashMap<>(16);
            params.put("mchid", wechatConfigProperties.getMchId());

            log.info("统一下单参数 {}", JSONUtil.toJsonStr(params));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    String.format(BasePayApiEnum.ORDER_QUERY_BY_OUT_TRADE_NO.toString(), outTradeNo),
                    wechatConfigProperties.getMchId(),
                    getSerialNumber(),
                    null,
                    wechatConfigProperties.getApiClientKeyPath(),
                    params
            );
            log.info("查询响应 {}", response);
            if (response.getStatus() == HttpServletResponse.SC_OK) {
                // 根据证书序列号查询对应的证书来验证签名结果
                boolean verifySignature = WxPayKit.verifySignature(response,wechatConfigProperties.getPlatformCertPath());
                log.info("verifySignature: {}", verifySignature);
                return response.getBody();
            }
            return JSONUtil.toJsonStr(response);
        } catch (Exception e) {
            log.error("系统异常", e);
            return e.getMessage();
        }
    }

    /**
     * 获取v3的证书
     *
     * @return String类型
     */
    @Override
    public String createPlatformCert() {
        //这个商户号对应的那个V3秘钥
        String mckKey = "";
        // 获取平台证书列表
        try {
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    CertAlgorithmTypeEnum.getCertSuffixUrl(CertAlgorithmTypeEnum.RSA.getCode()),
                    wechatConfigProperties.getMchId(),
                    getSerialNumber(),
                    null,
                    wechatConfigProperties.getApiClientKeyPath(),
                    "",
                    AuthTypeEnum.RSA.getCode()
            );
            String serialNumber = response.getHeader("Wechatpay-Serial");
            String body = response.getBody();
            int status = response.getStatus();
            log.info("serialNumber: {}", serialNumber);
            log.info("status: {}", status);
//            log.info("body: {}", body);
            if (status == HttpServletResponse.SC_OK) {
                JSONObject jsonObject = JSONUtil.parseObj(body);
                JSONArray dataArray = jsonObject.getJSONArray("data");
                // 默认认为只有一个平台证书
                JSONObject encryptObject = dataArray.getJSONObject(0);
                JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
                String associatedData = encryptCertificate.getStr("associated_data");
                String cipherText = encryptCertificate.getStr("ciphertext");
                String nonce = encryptCertificate.getStr("nonce");
                String serialNo = encryptObject.getStr("serial_no");
                //生成第四个证书文件
                final String platSerialNo = savePlatformCert(associatedData, mckKey, nonce, cipherText, wechatConfigProperties.getPlatformCertPath());
                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
            }
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, wechatConfigProperties.getPlatformCertPath());
            if (verifySignature) {
                return body;
            } else {
                return "平台证书不正确";
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "不能获取平台证书";
        }
    }

    private String savePlatformCert(String associatedData, String apiKey3, String nonce, String cipherText, String certPath) {
        try {
            AesUtil aesUtil = new AesUtil(apiKey3.getBytes(StandardCharsets.UTF_8));
            // 平台证书密文解密
            // encrypt_certificate 中的  associated_data nonce  ciphertext
            String publicKey = aesUtil.decryptToString(
                    associatedData.getBytes(StandardCharsets.UTF_8),
                    nonce.getBytes(StandardCharsets.UTF_8),
                    cipherText
            );
//            log.info("获取证书key:{},保存路径platformCert:{}", publicKey, certPath);
            log.info("保存路径platformCert:{}", certPath);
            //将生成的证书写入指定路径,文件名为:cert.pem
            FileOutputStream fos = new FileOutputStream(certPath);
            fos.write(publicKey.getBytes());
            fos.close();
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            log.error("写入证书错误:{}", e);
            return e.getMessage();
        }
    }

    /**
     * 保存订单的支付通知明文
     *
     * @param plainText 纯文本
     */
    private void savePayPlainText(String plainText) {
        JSONObject jsonObject = JSONUtil.parseObj(plainText);
        //这个就是发起订单时的那个订单号
        String outTradeNo = jsonObject.getStr("out_trade_no");
        //todo 把微信支付回调的明文消息存进数据库,方便后续校验查看
        log.info("业务订单号,outTradeNo:{}", outTradeNo);
        //todo 把微信支付后需要处理的具体业务处理了
    }

    /**
     * 获取证书序列号
     *
     * @return String
     */
    private String getSerialNumber() {
        log.info("验证证书路径:{}", wechatConfigProperties.getApiClientCertPath());
        // 获取证书序列号
        X509Certificate certificate = PayKit.getCertificate(FileUtil.getInputStream(wechatConfigProperties.getApiClientCertPath()));
        String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
        log.info("获取证书序列号:{},", serialNo);
        return serialNo;
    }

controller接口

package com.ruoyi.controller;

import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.service.WechatPayService;
import com.ruoyi.vo.IWxPayParamVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

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

@Api(tags ="微信下单支付")
@RequiredArgsConstructor
@RestController
@RequestMapping("/payment")
public class WeChatPayController extends BaseController {
    private final WechatPayService wxPayNewService;

    /**
     * 微信统一下单接口
     *
     * @param iWxPayParamVO 业务需要的参数
     * @return
     */
    @ApiOperation("微信统一下单接口")
    @PostMapping("/doUnifiedOrder")
    public Map<String, String> doUnifiedOrder(IWxPayParamVO iWxPayParamVO) throws Exception {
        return wxPayNewService.doUnifiedOrder(iWxPayParamVO);
    }

    /**
     * 微信支付的回调接收接口
     *
     * @param request
     * @param response
     */
    @ApiOperation("微信支付的回调接收接口")
    @RequestMapping(value = "/payNotify", method = {org.springframework.web.bind.annotation.RequestMethod.POST, org.springframework.web.bind.annotation.RequestMethod.GET})
    public void callBack(HttpServletRequest request, HttpServletResponse response) {
        wxPayNewService.callBack(request, response);
    }

    /**
     * 生成v3证书
     */
    @ApiOperation("生成v3证书")
    @RequestMapping("/createPlatformCert")
    @ResponseBody
    public String createPlatformCert() throws IOException {
        return wxPayNewService.createPlatformCert();
    }

    /**
     * 通过订单号查询支付情况
     *
     * @param outTradeNo 订单号
     * @return String
     */
    @ApiOperation("通过订单号查询支付情况")
    @RequestMapping("/query")
    @ResponseBody
    public String query(@RequestParam String outTradeNo) {
        return wxPayNewService.query(outTradeNo);
    }


没了,就这些就够了,因为大部分的代码都被开源IJPAY的大佬们帮忙封装好了

成功的记录

评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值