前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot整合IJpay实现微信支付

springboot整合IJpay实现微信支付

作者头像
Blue_007
发布2023-10-21 12:40:12
9263
发布2023-10-21 12:40:12
举报
文章被收录于专栏:代码生涯代码生涯

一、IJPay 介绍

进入IJpay官网,一眼便能看见 让支付触手可及很自信也很强

它聚合了各种支付,封装了微信支付QQ支付支付宝支付京东支付银联支付PayPal支付等常用的支付方式以及各种常用的接口。不依赖任何第三方 mvc 框架,仅仅作为工具使用简单快速完成支付模块的开发,可轻松嵌入到任何系统里,同时在gitee中也是高 Stars 的开源项目。

二、使用步骤

这里的案例为 集成微信支付

1. 准备必要信息

1.1 在小程序端关联商户号
1.2 配置相关信息

在application.yml文件中

这是微信平台下载的证书

代码语言:javascript
复制
如:
certPath :apiclient_cert.pem
certKeyPath apiclient_key.pem
platFormPath platform.pem
1.3 导入IJpay依赖
代码语言:javascript
复制
<dependency>
    <groupId>com.github.javen205</groupId>
    <artifactId>IJPay-WxPay</artifactId>
    <version>2.9.6</version>
</dependency>

2. 具体操作

2.1 java代码

注:Controller 层 本不应该存在 try-catch 代码块,这里放在一起只是为了方便理解代码,真实项目中还是能抽就抽出来😋

代码语言:javascript
复制
package cn.cnvp.web.api.wechart;
 
import cn.cnvp.web.token.message.JsonResult;
import cn.cnvp.web.utils.WxUtils;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpStatus;
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.RequestMethodEnum;
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.OtherApiEnum;
import com.ijpay.wxpay.model.v3.Amount;
import com.ijpay.wxpay.model.v3.Payer;
import com.ijpay.wxpay.model.v3.UnifiedOrderModel;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
 
/**
 * @Author: Blue 2113438464@QQ.com
 * @Date: 2023/8/21 23:52
 * @Description: 微信支付控制器
 */
@Slf4j
@Api(tags = "微信支付控制器")
@RestController
@RequestMapping("/api/wx/pay/v1")
@Scope("prototype")
public class WxPayApiController {
 
    /**微信小程序appid**/
    @Value("${wechat.ma.appId}")
    String appid;
 
    /**微信小程序secretId**/
    @Value("${wechat.ma.secret}")
    String secret;
 
    /**商户号**/
    @Value("${wechat.ma.mchid}")
    String mchid;
 
    /**商户密钥**/
    @Value("${wechat.ma.mchKey}")
    String mchKey;
 
    /**回调地址**/
    @Value("${wechat.ma.notifyUrl}")
    String notifyUrl;
 
    /**证书地址**/
    @Value("${wechat.ma.certPath}")
    String certPath;
    
    /**证书密钥地址**/
    @Value("${wechat.ma.certKeyPath}")
    String certKeyPath;
    
    /**微信平台证书**/
    @Value("${wechat.ma.platFormPath}")
    String platFormPath;
 
    /**
     * 微信支付
     * @param openId  用户openId
     */
    @RequestMapping("/jsApiPay")
    @ResponseBody
    public JsonResult jsApiPay(@RequestParam(value = "openId", required = false, defaultValue = "o-_-itxuXeGW3O1cxJ7FXNmq8Wf8") String openId) {
        try {
            String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
            UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
                    // APPID
                    .setAppid(appid)
                    // 商户号
                    .setMchid(mchid)
                    .setDescription("IJPay 让支付触手可及")
                    .setOut_trade_no(PayKit.generateStr())
                    .setTime_expire(timeExpire)
                    .setAttach("微信系开发脚手架 https://gitee.com/javen205/TNWX")
                    .setNotify_url(notifyUrl)
                    .setAmount(new Amount().setTotal(1))
                    .setPayer(new Payer().setOpenid(openId));
 
            log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.POST,
                    WxDomainEnum.CHINA.toString(),
                    BasePayApiEnum.JS_API_PAY.toString(),
                    mchid,
                    getSerialNumber(),
                    null,
                    certKeyPath,
                    JSONUtil.toJsonStr(unifiedOrderModel)
            );
            log.info("统一下单响应 {}", response);
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, platFormPath);
            log.info("verifySignature: {}", verifySignature);
            if (response.getStatus() == HttpStatus.HTTP_OK && verifySignature) {
                String body = response.getBody();
                JSONObject jsonObject = JSONUtil.parseObj(body);
                String prepayId = jsonObject.getStr("prepay_id");
                Map<String, String> map = WxPayKit.jsApiCreateSign(appid, prepayId, certKeyPath);
                log.info("唤起支付参数:{}", map);
                return JsonResult.success("获取成功",JSONUtil.toJsonStr(map));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return JsonResult.error("唤起失败");
    }
 
    /**
     * 微信支付回调
     */
    @RequestMapping(value = "/payNotify")
    public void payNotify(HttpServletRequest request, HttpServletResponse response) {
        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);
 
            // 需要通过证书序列号查找对应的证书,verifyNotify 中有验证证书的序列号
            String plainText = WxPayKit.verifyNotify(serialNo, result, signature, nonce, timestamp,
                    mchKey, platFormPath);
 
            log.info("支付通知明文 {}", 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();
        }
    }
    
    private String getSerialNumber() {
        // 获取证书序列号
        X509Certificate certificate = PayKit.getCertificate(certPath);
        if (null != certificate) {
            String serialNo = certificate.getSerialNumber().toString(16).toUpperCase();
            // 提前两天检查证书是否有效
            boolean isValid = PayKit.checkCertificateIsValid(certificate, mchid, -2);
            log.info("证书是否可用 {} 证书有效期为 {}", isValid, DateUtil.format(certificate.getNotAfter(), DatePattern.NORM_DATETIME_PATTERN));
            System.out.println("serialNo:" + serialNo);
            return serialNo;
        }
        return null;
    }
 
    @RequestMapping("/get")
    @ResponseBody
    public String v3Get() {
        // 获取平台证书列表
        try {
            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    OtherApiEnum.GET_CERTIFICATES.toString(),
                    mchid,
                    getSerialNumber(),
                    null,
                    certKeyPath,
                    ""
            );
            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);
            int isOk = 200;
            if (status == isOk) {
                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, nonce, cipherText, platFormPath);
                log.info("平台证书序列号: {} serialNo: {}", platSerialNo, serialNo);
            }
            // 根据证书序列号查询对应的证书来验证签名结果
            boolean verifySignature = WxPayKit.verifySignature(response, platFormPath);
            System.out.println("verifySignature:" + verifySignature);
            return body;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
 
    /**
     * 保存平台证书
     * @param associatedData    associated_data
     * @param nonce             nonce
     * @param cipherText        cipherText
     * @param certPath          证书保存路径
     */
    private String savePlatformCert(String associatedData, String nonce, String cipherText, String certPath) {
        try {
            AesUtil aesUtil = new AesUtil(mchKey.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
            );
            // 保存证书
            cn.hutool.core.io.file.FileWriter writer = new FileWriter(certPath);
            writer.write(publicKey);
            // 获取平台证书序列号
            X509Certificate certificate = PayKit.getCertificate(new ByteArrayInputStream(publicKey.getBytes()));
            return certificate.getSerialNumber().toString(16).toUpperCase();
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }
 
}

网上的案例,代码都很简单,读一下就ok了

2.2 下载平台证书

证书文件为:platForm.pem,我们调用上面的get方法,就可以下载证书,下载完成的会在yml配置的路径生成platForm.pem 证书

2.4 启动测试

调用jsApiPay接口传入openID(后续可换其他方式),返回如下json字符串表示成功

2.5 小程序唤起微信支付
代码语言:javascript
复制
<template>
    <view class="content">
        <button @click="pay">支付</button>
    </view>
</template>
 
<script>
    export default {
        data() {
            return {
                title: 'Hello',
                sign :{}
            }
        },
        onLoad() {
        
        },
        methods: {
            pay(){
                uni.request({
                    // 获取微信支付签名地址
                    url:"http://192.168.0.231:8087/api/wx/pay/v1/jsApiPay?openId=oEmg75RAQX-7uRqGUkSy0tGcfzpI",
                     // 成功回调
                     success: (res) => {
                        let sign =JSON.parse(res.data.data);
                        // 仅作为示例,非真实参数信息。
                        uni.requestPayment({
                            provider: 'wxpay',
                            timeStamp: sign.timeStamp,
                            nonceStr: sign.nonceStr,
                            package: sign.package,
                            signType: sign.signType,
                            paySign: sign.paySign,
                            success: function (res) {
                                console.log('success:' + JSON.stringify(res));
                            },
                            fail: function (err) {
                                console.log('fail:' + JSON.stringify(err));
                            }
                        });
                    }
                })
                
            }
            
        }
    }
</script>
 
<style>
    .content {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
    }
 
    .logo {
        height: 200rpx;
        width: 200rpx;
        margin-top: 200rpx;
        margin-left: auto;
        margin-right: auto;
        margin-bottom: 50rpx;
    }
 
    .text-area {
        display: flex;
        justify-content: center;
    }
 
    .title {
        font-size: 36rpx;
        color: #8f8f94;
    }
</style>
2.6 异步回调

在yml配置的异步回调地址(需要外网访问)

三、总结

本文仅仅简单介绍了springboot整合IJpay进行微信支付的使用,IJpay还有很多功能,本文是为记录,大家可以继续深入了解😙

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 代码生涯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、IJPay 介绍
  • 二、使用步骤
    • 1. 准备必要信息
      • 1.1 在小程序端关联商户号
      • 1.2 配置相关信息
      • 1.3 导入IJpay依赖
    • 2. 具体操作
      • 2.1 java代码
      • 2.2 下载平台证书
      • 2.4 启动测试
      • 2.5 小程序唤起微信支付
      • 2.6 异步回调
    • 三、总结
    相关产品与服务
    云开发 CloudBase
    云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档