Java接入微信支付详细教程——提供资料

2023-12-13 05:34:39

目录

1 前提

2 准备环境

2.1 基本依赖

2.2 准备配置

2.3 测试环境

3 交易类型说明

4 使用

支付下单

支付通知

查询支付

取消支付

申请退款

退款通知

查询退款

5 前端页面

6 前后端联调

6.1 点击支付

6.2 支付通知

6.3 查询支付

6.4 退款


说明:微信支付有两种版本:V3和V2,本文重点讲V3,V2大家有需要的话可以在评论区说出来

V2版接口和V3版接口实际上是基于两种接口标准设计的两套接口。目前大部分接口已升级为V3接口,其余V2接口后续也将逐步升级为V3接口。

1 前提

1.获取商户号

微信商户平台:微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式?步骤:申请成为商户 => 提交资料 => 签署协议 => 获取商户号

2.获取AppID

微信公众平台:微信公众平台?步骤:注册服务号 => 服务号认证 => 获取APPID => 绑定商户号

3.申请商户证书

步骤:登录商户平台 => 选择 账户中心 => 安全中心 => API安全 => 申请API证书 包括商户证书和商户私钥

4.获取微信的证书

可以预先下载,也可以通过编程的方式获取。

5.获取APIv3秘钥(在微信支付回调通知和商户获取平台证书使用APIv3密钥)

步骤:登录商户平台 => 选择 账户中心 => 安全中心 => API安全 => 设置APIv3密钥

注意:这些资料本文都有提供(资料在最后面)

2 准备环境

项目采用SpringBoot

jdk本文使用11版本

2.1 基本依赖

<!--微信支付-->
<dependency>
    <groupId>com.github.wechatpay-apiv3</groupId>
    <artifactId>wechatpay-apache-httpclient</artifactId>
    <version>0.4.9</version>
</dependency>
<!--实体对象工具类-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!--工具类-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>
<!--web应用-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2.2 准备配置

配置文件:

wxpay:
   # APIv3密钥
   api-v3-key: UDuLFDcmy5Eb6o0nTNZdu6ek4DDh4K8B
   # APPID
   appid: wx74862e0dfcf69954
   # 商户ID
   mch-id: 1558950191
   # 商户API证书序列号
   mch-serial-no: 34345964330B66427E0D3D28826C4993C77E631F
   # 接收结果通知地址
   notify-domain: ?http://r2agtr.natappfree.cc?
   # 商户私钥文件路径
   private-key-path: apiclient_key.pem

注意:上面的接收结果通知地址写你自己的其中http://r2agtr.natappfree.cc是我使用内网穿透的地址。

什么是内网穿透?简单来说就是能让所有人都能访问到你,我明天会出一篇详细的笔记来讲内网穿透。

为什么在微信支付中要用到内网穿透?

因为用户付完钱后,微信这边总得通知你一声吧,把支付信息都告诉你,但是因为你的服务在本地,微信访问不到你这里,这时候内网穿透就能解决这个问题,通过映射到本地的端口,比如我在内网穿透工具上映射的是我本机8080的端口,启动的时候它就会生成:一个url,访问这个路径相当于访问了我本机的8080端口
?

?配置私钥文件路径:

把资料中的apiclient_key.pem放在与src同级目录下

?

?配置类:

@Configuration
@Slf4j
@Data
public class WechatpayConfig {
    // 商户ID
    @Value("${wxpay.mch-id}")
    private String mchId;

    // 商户API证书序列号
    @Value("${wxpay.mch-serial-no}")
    private String mchSerialNo;

    // 商户私钥文件
    @Value("${wxpay.private-key-path}")
    private String privateKeyPath;

    // APIv3密钥
    @Value("${wxpay.api-v3-key}")
    private String apiV3Key;

    // APPID
    @Value("${wxpay.appid}")
    private String appid;

    // 接收结果通知地址
    @Value("${wxpay.notify-domain}")
    private String notifyDomain;



    /**
     * 获取HttpClient对象
     * @return
     */
    @Bean
    public CloseableHttpClient getVerifier(){
        /**
         * 首先,获取证书管理器的实例,用于管理微信支付平台的证书。
         * 然后,向证书管理器增加需要自动更新平台证书的商户信息,包括商户号、商户私钥和API v3密钥。
         * 接着,创建一个私钥签名器,用于对请求进行签名。
         * 然后,创建一个身份认证对象,用于携带商户号和签名信息。
         * 接着,从证书管理器中获取一个验签器,用于对响应进行验签。
         * 然后,创建一个微信支付HTTP客户端构建器,用于设置商户信息和验签器。
         * 最后,通过构建器构造一个HTTP客户端,用于发送请求和接收响应。
         */
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        // 向证书管理器增加需要自动更新平台证书的商户信息
        PrivateKey privateKey = null;
        try {
            // 拿到api v3密钥
            privateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKeyPath));
        } catch (FileNotFoundException e) {
            throw new RuntimeException("私钥文件不存在", e);
        }
        // 创建了一个私钥签名器
        PrivateKeySigner privateKeySigner = new PrivateKeySigner(mchSerialNo, privateKey);

        // 身份认证对象(签名)
        WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(mchId, privateKeySigner);

        try {
            certificatesManager.putMerchant(mchId, wechatPay2Credentials, apiV3Key.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("定时更新签名验证器错误:{}",e);
            throw new RuntimeException(e);
        }
        // 从证书管理器中获取verifier
        Verifier verifier = null;
        try {
            verifier = certificatesManager.getVerifier(mchId);
        } catch (NotFoundException e) {
            log.error("从证书管理器中获取verifier失败:{}",e);
            throw new RuntimeException(e);
        }
        // 用于构造HttpClient
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo,privateKey )
                .withValidator(new WechatPay2Validator(verifier));

        // 通过WechatPayHttpClientBuilder构造的HttpClient,会自动的处理签名和验签,并进行证书自动更新
        CloseableHttpClient httpClient = builder.build();
        return httpClient;
    }
}

2.3 测试环境

@RestController
public class TestController {

    @Resource
    private WechatpayConfig wechatpayConfig;
    @GetMapping("/")
    public String index() {
        System.out.println(wechatpayConfig);
        return "成功";
    }
}

访问:localhost:8080

?

控制台出现:
?

?到这里的话环境已经没问题了。

3 交易类型说明

? 微信支付的交易类型有好几种方式:官方文档

#1. 付款码支付

付款码支付,即日常所说的被扫支付,这是一种纯用于线下场景的支付方式,由用户出示微信客户端内展示的付款二维码,商户使用扫码设备扫码后完成支付

#2. Native原生支付

Native原生支付,即日常所说的扫码支付,商户根据微信支付协议格式生成的二维码,用户通过微信“扫一扫”扫描二维码后即进入付款确认界面,输入密码即完成支付。

#3. JSAPI网页支付

JSAPI网页支付,即日常所说的公众号支付,可在微信公众号、朋友圈、聊天会话中点击页面链接,或者用微信“扫一扫”扫描页面地址二维码在微信中打开商户HTML5页面,在页面内下单完成支付。

#4. App支付

App支付是指商户已有的App,通过对接微信支付API及SDK,实现从商户App发起交易后跳转到微信App,用户完成支付后跳回商户App的场景。

#5. 小程序支付

小程序支付是指在商户既有的小程序内通过对接微信支付API,实现用户在小程序内完成交易的场景。

本次交易类型为:NATIVE-二维码支付

4 使用

支付下单

官方文档:下单文档

/**
     * Native下单
     *  调用统一下单API,生成支付二维码
     */
    @PostMapping("/native")
    public Map<String,String> nativePay() throws Exception {
        log.info("生成订单");
        //生成订单
        log.info("存入数据库...");

        log.info("调用统一下单API");
        //调用统一下单API
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");

        // 订单号
        String orderNo = IdUtil.simpleUUID();
        // 请求body参数 看官方文档
        Map paramsMap = new HashMap();
        paramsMap.put("appid", wechatpayConfig.getAppid());
        paramsMap.put("mchid", wechatpayConfig.getMchId());
        paramsMap.put("description", "iPhone 15 Pro Max 5G");
        paramsMap.put("out_trade_no", orderNo);
        // 回调的地址
        paramsMap.put("notify_url",wechatpayConfig.getNotifyDomain()+"/native/notify");
        Map amountMap = new HashMap();
        //订单总金额,单位为分。
        amountMap.put("total", 1);
        //CNY:人民币,境内商户号仅支持人民币。
        amountMap.put("currency", "CNY");

        paramsMap.put("amount", amountMap);
        //将参数转换成json字符串
        String jsonParams = JSONUtil.toJsonStr(paramsMap);
        log.info("请求参数:" + jsonParams);

        StringEntity entity = new StringEntity(jsonParams,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");
        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpPost);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功, 返回结果 = " + bodyAsString);
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功");
            } else {
                log.info("Native下单失败,响应码 = " + statusCode+ ",返回结果 = " +
                        bodyAsString);
                throw new IOException("request failed");
            }
            //响应结果
            Map<String, String> resultMap = JSONUtil.toBean(bodyAsString,HashMap.class);
            //二维码
            String codeUrl = resultMap.get("code_url");
            Map<String, String> map = new HashMap<>();
            map.put("codeUrl", codeUrl);
            map.put("orderNo", orderNo.toString());
            return map;
        } finally {
            response.close();
        }
    }

支付通知

支付通知文档

/**
     * 支付通知
     * 微信支付通过支付通知接口将用户支付成功消息通知给商户
     */
    @PostMapping("/native/notify")
    public Map<String,String> nativeNotify(@RequestBody Map<String,Object> signalRes, HttpServletResponse response){
        Map<String, String> map = new HashMap<>();//应答对象
        log.info("支付通知的完整数据 ===> {}", signalRes);
        try {
            //用密文解密出明文
            Map<String,String> resource=(Map<String,String>)signalRes.get("resource");
            String ciphertext=resource.get("ciphertext");
            String associatedData=resource.get("associated_data");
            String nonce=resource.get("nonce");
            // 拿到明文
            String plainText=new AesUtil(wechatpayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8)).decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);
            //转换
            HashMap<String,Object> data= JSONUtil.toBean(plainText,HashMap.class);
            log.info("解密后的完整数据:{}",data);
            //处理订单
            log.info("处理订单...");
            //成功应答:成功应答必须为200或204,否则就是失败应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
        } catch (GeneralSecurityException e) {
            response.setStatus(500);
            map.put("code", "FAIL");
            map.put("message","失败");
        }
        return map;
    }

查询支付

查询支付官方文档

/**
     * 查询订单
     * @param orderNo
     * @return
     * @throws Exception
     */
    @GetMapping("/query/{orderNo}")
    public String queryOrder(@PathVariable String orderNo) throws Exception {
        log.info("查询订单");
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/id/%s", orderNo);
        url = url+"?mchid="+wechatpayConfig.getMchId();
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpGet);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());//响应体
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功, 返回结果 = " + bodyAsString);
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功");
            } else {
                log.info("查单接口调用,响应码 = " + statusCode+ ",返回结果 = " + bodyAsString);
                throw new IOException("request failed");
            }
            return bodyAsString;
        } finally {
            response.close();
        }

    }

取消支付

取消支付官方文档

/**
     * 用户取消订单
     * @param orderNo   订单id
     */
    @PostMapping("/cancel/{orderNo}")
    public String cancel(@PathVariable String orderNo) throws Exception {
        log.info("用户取消订单");
        //创建远程请求对象
        String url = String.format("https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close", orderNo);
        HttpPost httpPost = new HttpPost(url);

        //组装json请求体
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("mchid", wechatpayConfig.getMchId());
        String jsonParams = JSONUtil.toJsonStr(paramsMap);
        log.info("请求参数 ===> {}", jsonParams);

        //将请求参数设置到请求对象中
        StringEntity entity = new StringEntity(jsonParams,"utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpPost);
        try {
            int statusCode = response.getStatusLine().getStatusCode();//响应状态码
            if (statusCode == 200) { //处理成功
                log.info("成功200");
            } else if (statusCode == 204) { //处理成功,无返回Body
                log.info("成功204");
            } else {
                log.info("Native下单失败,响应码 = " + statusCode);
                throw new IOException("request failed");
            }
            return "订单取消成功";
        } finally {
            response.close();
        }
    }

申请退款

退款文档

/**
     * 申请退款
     * @param orderNo   订单编号
     */
    @PostMapping("/refunds/{orderNo}")
    public Map<String,Object> refunds(@PathVariable String orderNo) throws Exception {

        log.info("创建退款单记录...");
        //根据订单编号创建退款单

        log.info("调用退款API");

        //调用统一下单API
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");

        // 请求body参数
        Map paramsMap = new HashMap();
        paramsMap.put("out_trade_no", orderNo);//订单编号

        // 给它一个退款单号
        paramsMap.put("out_refund_no", "tk202412120001");//退款单编号
        paramsMap.put("reason", "买了不发货");//退款原因
        paramsMap.put("notify_url", wechatpayConfig.getNotifyDomain()+"/refunds/notify");//退款通知地址

        Map amountMap = new HashMap();
        amountMap.put("refund", 1);//退款金额
        amountMap.put("total", 1);//原订单金额
        amountMap.put("currency", "CNY");//退款币种
        paramsMap.put("amount", amountMap);

        //将参数转换成json字符串
        String jsonParams = JSONUtil.toJsonStr(paramsMap);
        log.info("请求参数 ===> {}" + jsonParams);

        StringEntity entity = new StringEntity(jsonParams, "utf-8");
        entity.setContentType("application/json");//设置请求报文格式
        httpPost.setEntity(entity);//将请求报文放入请求对象
        httpPost.setHeader("Accept", "application/json");//设置响应报文格式

        //完成签名并执行请求,并完成验签
        CloseableHttpResponse response = wxPayClient.execute(httpPost);

        try {
            //解析响应结果
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                log.info("成功, 退款返回结果 = " + bodyAsString);
            } else if (statusCode == 204) {
                log.info("成功");
            } else {
                throw new RuntimeException("退款异常, 响应码 = " + statusCode + ", 退款返回结果 = " + bodyAsString);
            }
            log.info("更新订单状态......");
            log.info("更新退款单......");
            return JSONUtil.toBean(bodyAsString,Map.class);
        } finally {
            response.close();
        }
    }

退款通知

/**
     * 退款结果通知
     * 退款状态改变后,微信会把相关退款结果发送给商户。
     */
    @PostMapping("/refunds/notify")
    public String refundsNotify(@RequestBody Map<String,Object> signalRes, HttpServletResponse response){
        log.info("退款通知执行");
        Map<String, String> map = new HashMap<>();//应答对象
        try {
            Map<String,String> resource=(Map<String,String>)signalRes.get("resource");
            String ciphertext=resource.get("ciphertext");
            String associatedData=resource.get("associated_data");
            String nonce=resource.get("nonce");
            // 拿到明文
            String plainText=new AesUtil(wechatpayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8)).decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);

            //转换
            HashMap<String,Object> data= JSONUtil.toBean(plainText,HashMap.class);
            log.info("解密后的完整数据:{}",data);
            log.info("处理退款单................................");
            log.info("更新订单状态................................");

            //成功应答
            response.setStatus(200);
            map.put("code", "SUCCESS");
            map.put("message", "成功");
            return JSONUtil.toJsonStr(map);
        } catch (Exception e) {
            e.printStackTrace();
            //失败应答
            response.setStatus(500);
            map.put("code", "ERROR");
            map.put("message", "失败");
            return JSONUtil.toJsonStr(map);
        }
    }

查询退款

/**
     * 查询退款
     * @param refundNo  退款订单
     */
    @GetMapping("/query-refund/{refundNo}")
    public String queryRefund(@PathVariable String refundNo) throws Exception {

        log.info("查询退款接口调用 ===> {}", refundNo);
        String url =  String.format("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/%s", refundNo);
        //创建远程Get 请求对象
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("Accept", "application/json");
        //完成签名并执行请求
        CloseableHttpResponse response = wxPayClient.execute(httpGet);
        try {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                log.info("成功, 查询退款返回结果 = " + bodyAsString);
            } else if (statusCode == 204) {
                log.info("成功");
            } else {
                throw new RuntimeException("查询退款异常, 响应码 = " + statusCode+ ", 查询退款返回结果 = " + bodyAsString);
            }
            return bodyAsString;
        } finally {
            response.close();
        }
    }

5 前端页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>微信支付</title>
    <script src="/js/jquery.min.js"></script>
    <script src="/js/axios.js"></script>
    <script src="/js/qrcode.min.js"></script>
    <style>
        .container {
            width: 80%;
            margin: 0 auto;
        }

        .btn {
            display: inline-block;
            padding: 10px 20px;
            background-color: #1AAD19;
            color: white;
            border-radius: 5px;
            cursor: pointer;
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.5);
        }



        .result {
            display: none;
            margin-top: 20px;
            padding: 10px;
            border: 1px solid #ccc;
        }
        .btn-small {
            padding: 5px 10px;
        }
    </style>
</head>
<body>
<div class="container">
    <button class="btn" id="pay">支付</button>
    <button class="btn" id="query">查询支付</button>
    <button class="btn" id="refund">申请退款</button>
    <button class="btn-small" id="cancelOrder">取消订单</button>
    <button class="btn-small" id="queryRefund">查询退款状态</button>
    <div class="qrcode" id="qrcode"></div>
    <div class="result" id="result"></div>
</div>
<script>
    var order_id = "";
    var pay_url = "";

    //二维码
    var qRCode='';
    //  支付
    $("#pay").on("click", function () {
        axios.post("/native")
            .then(function (response) {
                order_id = response.data.orderNo;
                pay_url = response.data.codeUrl;
                //生成二维码放入”Orcode_div“ div
                qRCode = new QRCode(document.getElementById("qrcode"),pay_url);
            })
            .catch(function (error) {
                alert(error.message);
            });
    });

    // 查询订单
    $("#query").on("click", function () {
        if (order_id === "") {
            alert("请先支付");
        } else {
            axios.get("/query/" + order_id)
                .then(function (response) {
                    $("#result").text(response.data)
                })
                .catch(function (error) {
                    alert(error.message);
                });
        }
    });

    // 取消订单
    $("#cancelOrder").on("click", function () {
        // 取消订单
        if (order_id === "") {
            alert("请先支付");
        } else {
            axios.post("/cancel/" + order_id)
                .then(function (response) {
                    //清除二维码
                    document.getElementById("qrcode").innerHTML = '';
                    // 处理取消订单成功的逻辑
                    alert(response.data);
                })
                .catch(function (error) {
                    // 处理取消订单失败的逻辑
                    alert(error.message);
                });
        }
    });

    // 查询退款
    $("#queryRefund").on("click", function () {
        // 查询退款状态
        if (order_id === "") {
            alert("请先支付");
        } else {
            // 假设这里使用退款单号作为参数,您可以根据实际情况调整
            axios.get("/query-refund/" + order_id)
                .then(function (response) {
                    // 处理查询退款状态的逻辑
                    alert(response.data);
                })
                .catch(function (error) {
                    // 处理查询退款状态失败的逻辑
                    alert(error.message);
                });
        }
    });

    // 退款
    $("#refund").on("click", function () {
        if (order_id === "") {
            alert("请先支付");
        } else {
            axios.post("/refunds/" + order_id)
                .then(function (response) {
                    $("#result").text(response.data)
                })
                .catch(function (error) {
                    alert(error.message);
                });
        }
    });
</script>
</body>
</html>

6 前后端联调

6.1 点击支付

控制台

6.2 支付通知

返回:

{
  "transaction_id": "4200002111202312128239200217",
  "amount": {
    "total": 1,
    "payer_total": 1,
    "currency": "CNY",
    "payer_currency": "CNY"
  },
  "mchid": 1558950191,
  "out_trade_no": 202412120001,
  "trade_state": "SUCCESS",
  "bank_type": "OTHERS",
  "appid": "wx74862e0dfcf69954",
  "trade_state_desc": "支付成功",
  "trade_type": "NATIVE",
  "attach": null,
  "success_time": "2023-12-12T21:36:09+08:00",
  "payer": {
    "openid": "oHwsHuKAElSTZ37f5F-Zv4Jg5eIo"
  }
}

6.3 查询支付

返回:

{
  "amount": {
    "currency": "CNY",
    "payer_currency": "CNY",
    "payer_total": 1,
    "total": 1
  },
  "appid": "wx74862e0dfcf69954",
  "attach": "",
  "bank_type": "OTHERS",
  "mchid": "1558950191",
  "out_trade_no": "202412120001",
  "payer": {
    "openid": "oHwsHuKAElSTZ37f5F-Zv4Jg5eIo"
  },
  "promotion_detail": [],
  "success_time": "2023-12-12T21:36:09+08:00",
  "trade_state": "SUCCESS",
  "trade_state_desc": "支付成功",
  "trade_type": "NATIVE",
  "transaction_id": "4200002111202312128239200217"
}

6.4 退款

返回:
?

{
  "amount": {
    "currency": "CNY",
    "discount_refund": 0,
    "from": [],
    "payer_refund": 1,
    "payer_total": 1,
    "refund": 1,
    "refund_fee": 0,
    "settlement_refund": 1,
    "settlement_total": 1,
    "total": 1
  },
  "channel": "ORIGINAL",
  "create_time": "2023-12-12T21:51:55+08:00",
  "funds_account": "UNAVAILABLE",
  "out_refund_no": "tk202412120001",
  "out_trade_no": "202412120001",
  "promotion_detail": [],
  "refund_id": "50310208212023121204787113276",
  "status": "PROCESSING",
  "transaction_id": "4200002111202312128239200217",
  "user_received_account": "支付用户零钱"
}

apiclient_key.pem文件的内容:

-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDnSAKI8sea8p+d
OBVPWlZmxqJfPbdhzZxdI5Kx1j5SJNZwXWtr43/giw38pwzSlBI+bubBcYlkFTI0
guigMZO/yueb1mZChaY/JG1vsT02Ubj0xkVvBwKNbYS48NEpZhK61Mia09R4n1iH
1vip9kt8J6Zrx+xIqwmuCNWigyivGrvY9AdevCNlNSVdHVOZUJiJ6UGtvVmgZb0u
RTwBzfkjnwTgEcsrZMmF15nFubFsyJLyF/zY4NhrISc8H/rbjgleqa8ybYL26iTS
gfPCXe4U9f8fNFF2bSA06GTiB2R93q2B0zHeUYrpgF4XOGlIAqH+Ea4Vn+aOj6I0
pduh03idAgMBAAECggEBAJ+4SB/hYd1szrPZhkXtwhtp87pIObtuLhzYMzdjGFjM
HdctfMDeNHKSNU+U4bMPFOZO2kcfLF2Ukb5X5WSzuDBMZNRnJOmtuJiEhJsM0JQR
reREhLDfK3EWAAFkNV4corSpu/vIbEP87zuoRsPBVnHgQ/rM7y1kCORKL5bycwcw
5BI4xhULKAu14LEcDL3+xDJo39w+WCFlxuP+6Bs7+vIeavs+AC3TJkA4kg2nyWd3
W07xPjHl64f17icqsFhuFZ+VuSf5CAgQGWDbC7BHqRkDStUDSiiUiFushouKCLdK
MpA0x4ogb2ZwfZDRhZHiLNAGe4QovYCcXWBydzuT0WECgYEA828Bo1JAHE5kdnsO
E9+enH/yMcOKTRnuYPiXsFXNvqofc5tZiXJmVE/+EKv7LFmtUA6qqKC7FDek8TpP
SkfXmSDAgfM6AdzT0YoHH23FRVewnFMEYumtogXsXJTyI5siBSJp16s9Rn/YwESt
JqjW5+9Ck1dkU+UJCZ4lOw4HeGkCgYEA8zho2BKQTh3P/xcFcoTcunVZpRayVkHM
g8Ef6RGGo4vM1oshQLvXyPqCmhAIf6j71I9WPqUwjmeGyaR7Hir0dbgTCm2fJPFW
lxAvgbCISxEPz10RYBcR2umMSlJLfZfhqv1CyfU4vfCTbdOimgsz2039E3oLTbzg
eDe/mdzu2BUCgYEAleKjf4wFLWiXMtxRrqrhXjrpRPrBDPgKbmqh+1DZfawB8YyV
dKublg4qwNkjrgsJS2G8cleE2M3qIR1l9LaHaSFhZqH79WmigkIaYJ+V9zwm4hm7
eaun3TsIbXjIHmRGbiLiSIiHEgFl0/x1IHiU2fnXZCFLBNzg06ssAVCCCQECgYA1
4BfxTONkOlxZgAr33BBcySPLWuS0EK0xvjTIVtaBIbWFDJqYEUPyQ/NsFwMa7B6k
bf/HrqW71ZjYz7Np8k/mR5kIJVIsR71Lhw1O6AC4yBW9dDsmEtYkrLkjuWj5cAxP
6PvDaqtf/4tYt5l8D+Ezwem+R7l7RcxfNNIfTf4mJQKBgE57dnRx+Ijx7VHjJvjl
X2jB/VSVGpK5OADykmmZ/wvHPlQcyzd+5kAIoJhSuY48CFeI1DOogR2p01LEFQEL
j4AI5FqOOQwRJvNmfoKcKwO36tSxSEGSM8POKOsa21PG/gvDpJjVFo2hn5QcMHWn
z5SjsgA/1YbXejubdLxT/3pl
-----END PRIVATE KEY-----

到这里就结束,制作不宜,喜欢博主的可以点点关注,每日分享编程知识!!!

文章来源:https://blog.csdn.net/YYVeryGood/article/details/134928949
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。