小程序接入微信支付V3接口开发教程

2023-10-27

前言

最近做了一个小程序对接微信支付的需求,查看微信支付文档,还是感觉有点凌乱,所以做一个统一整理,供大家参考。

API参考官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_3.shtml

支付流程

业务流程图

重点步骤说明:

开发步骤

1 接入前准备

1.1 微信支付配置申请

详细操作流程参考官方文档:https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_8_1.shtml#part-1

配置完成需要以下信息:

  • APPID
  • 商户号(mchid)
  • 商户API私钥(apiclient_key.pem)
  • 商户证书序列号
  • 商户APIv3密钥
1.2 引入开发库
Gradle
implementation 'com.github.wechatpay-apiv3:wechatpay-java:0.2.10'
Maven
<dependency>
  <groupId>com.github.wechatpay-apiv3</groupId>
  <artifactId>wechatpay-java</artifactId>
  <version>0.2.10</version>
</dependency>
1.3 创建配置
配置信息
#微信支付配置
wx:
  pay:
    appId: 
    merchantId: 
    privateKey: 
    merchantSerialNumber: 
    apiV3Key: 
    payNotifyUrl: 
配置类
package com.xh.server.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author yupf
 * @description 微信支付配置类
 * @date 2023/7/26 09:30
 */
@Data
@Component
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayConfig {

    //APPID
    private String appId;
    //mchid
    private String merchantId;
    //商户API私钥
    private String privateKey;
    //商户证书序列号
    private String merchantSerialNumber;
    //商户APIv3密钥
    private String apiV3Key;
    //支付通知地址
    private String payNotifyUrl;
}

1.4 初始化商户配置
package com.xh.server.config;

import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

/**
 * @author yupf
 * @description 微信支付证书自动更新配置
 * @date 2023/7/26 14:37
 */

@Configuration
public class WxPayAutoCertificateConfig {

    @Resource
    private WxPayConfig wxPayConfig;

    /**
     * 初始化商户配置
     * @return
     */
    @Bean
    public RSAAutoCertificateConfig rsaAutoCertificateConfig() {
        RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(wxPayConfig.getMerchantId())
                .privateKey(wxPayConfig.getPrivateKey())
                .merchantSerialNumber(wxPayConfig.getMerchantSerialNumber())
                .apiV3Key(wxPayConfig.getApiV3Key())
                .build();
        return config;
    }
}

2 JSAPI下单

文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml

    @ApiOperation(value = "预支付订单", notes = "预支付订单")
    @PostMapping("/create")
    public R<PrepayWithRequestPaymentResponse> createOrder(@Validated @RequestBody CreateOrderReq req) {
        //创建初始化订单
      		***
         
        //请求微信支付相关配置
        JsapiServiceExtension service =
                new JsapiServiceExtension.Builder()
                        .config(rsaAutoCertificateConfig)
                        .signType("RSA") // 不填默认为RSA
                        .build();
        PrepayWithRequestPaymentResponse response = new PrepayWithRequestPaymentResponse();
        try {
            PrepayRequest request = new PrepayRequest();
            request.setAppid(wxPayConfig.getAppId());
            request.setMchid(wxPayConfig.getMerchantId());
            request.setDescription(order.getDescription());
            request.setOutTradeNo(order.getOrderNo());
            request.setNotifyUrl(wxPayConfig.getPayNotifyUrl());
            Amount amount = new Amount();
            amount.setTotal(order.getAmount().multiply(new BigDecimal("100")).intValue());
            request.setAmount(amount);
            Payer payer = new Payer();
            payer.setOpenid(req.getWxOpenId());
            request.setPayer(payer);
            log.info("请求预支付下单,请求参数:{}", JSONObject.toJSONString(request));
            // 调用预下单接口
            response = service.prepayWithRequestPayment(request);
            log.info("订单【{}】发起预支付成功,返回信息:{}", order.getOrderNo(), response);
        } catch (HttpException e) { // 发送HTTP请求失败
            log.error("微信下单发送HTTP请求失败,错误信息:{}", e.getHttpRequest());
            return R.error().message("下单失败");
        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
            log.error("微信下单服务状态错误,错误信息:{}", e.getErrorMessage());
            return R.error().message("下单失败");
        } catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
            log.error("服务返回成功,返回体类型不合法,或者解析返回体失败,错误信息:{}", e.getMessage());
            return R.error().message("下单失败");
        }
      
        //更新订单状态
					***
        return R.ok().data(response);
    }

这里有几个注意点:

  1. 小程序预支付下单需要获取用户的openid;
  2. 预支付下单请求及返回类所在包为com.wechat.pay.java.service.payments.jsapi;
  3. PrepayWithRequestPaymentResponse对应的是wx.requestPayment接口中的请求参数,前端可直接使用;

3 查询订单

文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_2.shtml

微信提供了两种查询订单方式:

  1. 微信支付订单号查询;
  2. 商户订单号查询
    @ApiOperation(value = "根据支付订单号查询订单", notes = "根据支付订单号查询订单")
    @PostMapping("/queryOrder")
    public R queryOrder(@Validated @RequestBody QueryOrderReq req) {
        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setTransactionId(req.paymentNo);
        try {
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
            Transaction result = service.queryOrderById(queryRequest);
            if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
            }
        } catch (ServiceException e) {
            log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
            return R.error();
        }
        return R.ok();
    }
    @ApiOperation(value = "根据商户订单号查询订单", notes = "根据商户订单号查询订单")
    @PostMapping("/queryOrder")
    public R queryOrder(@Validated @RequestBody QueryOrderReq req) {
        QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
        queryRequest.setMchid(wxPayConfig.getMerchantId());
        queryRequest.setOutTradeNo(req.orderNo);
        try {
            JsapiServiceExtension service =
                    new JsapiServiceExtension.Builder()
                            .config(rsaAutoCertificateConfig)
                            .signType("RSA") // 不填默认为RSA
                            .build();
            Transaction result = service.queryOrderByOutTradeNo(queryRequest);
            if (Transaction.TradeStateEnum.SUCCESS != result.getTradeState()) {
                log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", result.getOutTradeNo(), result.getTransactionId());
            }
        } catch (ServiceException e) {
            log.error("订单查询失败,返回码:{},返回信息:{}", e.getErrorCode(), e.getErrorMessage());
            return R.error();
        }
        return R.ok();
    }

4 支付通知

文档地址:https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_5.shtml

回调地址notify_url设置规范:

  1. 异步接收微信结果通知回调地址,通知url必须为外网可访问的url;
  2. 不能携带任何参数;
  3. 公网域名必须为https,现使用http域名能正常接收回调的用户,建议更换https,避免后期出现回调通知无法接收的情况;
  4. 不支持携带端口号

其他注意事项详见文档:https://pay.weixin.qq.com/wiki/doc/apiv3/Practices/chapter1_1_5.shtml

    @ApiOperation(value = "预支付-回调")
    @PostMapping("/payNotify")
    public synchronized String payNotify(HttpServletRequest request) throws IOException {
        log.info("------收到支付通知------");
        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(HttpServletUtils.getRequestBody(request))
                .build();

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
        // 以支付通知回调为例,验签、解密并转换成 Transaction
        log.info("验签参数:{}", requestParam);
        Transaction transaction = parser.parse(requestParam, Transaction.class);
        log.info("验签成功!-支付回调结果:{}", transaction.toString());
        

        Map<String, String> returnMap = new HashMap<>(2);
        returnMap.put("code", "FAIL");
        returnMap.put("message", "失败");
        if (Transaction.TradeStateEnum.SUCCESS != transaction.getTradeState()) {
            log.info("内部订单号【{}】,微信支付订单号【{}】支付未成功", transaction.getOutTradeNo(), transaction.getTransactionId());
            return JSONObject.toJSONString(returnMap);
        }

        //修改订单前,建议主动请求微信查询订单是否支付成功,防止恶意post
        	***

        //修改订单信息
        	***

        returnMap.put("code", "SUCCESS");
        returnMap.put("message", "成功");
        return JSONObject.toJSONString(returnMap);
    }
package com.xh.server.util;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
 * @author yupf
 * @date 2023/07/26 上午9:17
 **/
public class HttpServletUtils {

    /**
     * 获取请求体
     *
     * @param request
     * @return
     * @throws IOException
     */
    public static String getRequestBody(HttpServletRequest request) throws IOException {
        ServletInputStream stream = null;
        BufferedReader reader = null;
        StringBuffer sb = new StringBuffer();
        try {
            stream = request.getInputStream();
            // 获取响应
            reader = new BufferedReader(new InputStreamReader(stream));
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
        } catch (IOException e) {
            throw new IOException("读取返回支付接口数据流出现异常!");
        } finally {
            reader.close();
        }
        return sb.toString();
    }

}

总结

以上便是小程序对接微信支付的全部流程,其他如APP或者H5对接流程大同小异,只不过引用的包有所不同,建议下载开发库wechatpay-java详细阅读以下源码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

小程序接入微信支付V3接口开发教程 的相关文章

  • 从用户家里采集IPTV码流

    具体的方法是通过在机顶盒和电视信号接口ONU之间增加一个镜像路由器 截取数据 再通过Wireshark软件或者自行开发的软件分析数据 WireShark抓取数据包 分析结果 机顶盒 机顶盒实质上是一个网页浏览器 每次开机之后会访问固定的网页
  • pytorch 神经网络构造

    call 在python中 类的 call 方法可以使得类对象具有类似函数的功能 这一点在pytorch经常有应用 理解这一点才能理解pytorch中forward函数等的作用 call 方法的使用示例 class A def call s
  • C语言malloc与calloc区别

    C语言malloc与calloc区别 区别 开门见山 malloc与calloc在内存分配时 前者分配一整块 后者分配n块 并且后者在分配时会将内存置为0 前者不会内存里是垃圾数据 另外它们参数不同 malloc参数只有一个无符号整型的nu
  • C语言如何自定义头文件——一看就废!!!

    为什么要自定义头文件 如果在一个文件中 写上成百上千行的代码 那么这些代码让人阅读起来是真的烦 因此 我们可以引入头文件 把自己写的函数放入头文件中 然后直接调用到主程序中 这样在主程序中看起来就比较清晰 那如何自定义头文件呢 第一步 我们

随机推荐

  • 开源堡垒机jumpserver的搭建与使用

    目录 一 准备 Python3 和 Python 虚拟环境 二 安装 Jumpserver 三 安装 SSH Server 和 WebSocket Server Coco 四 安装 Web Terminal 前端 Luna 五 安装 Win
  • 2080Ti NVIDIA显卡安装教程记录

    1 硬件安装 找到卡槽 顺位安装 找到电源插头 配套孔位即可 2 软件安装 搞个NVIDIA控制面板 此外 安装CUDA和cuDNN win10下CUDA和CUDNN的安装 超详细 亲测有效 u011473714的专栏 CSDN博客
  • Centos6.5下搭建nagios详解

    一 LAMP环境部署 1 安装php 1 安装yum源 rpm Uvh http download fedoraproject org pub epel 6 x86 64 epel release 6 8 noarch rpm rpm Uv
  • 用于灵敏性分析的方法模型(Matlab代码实现)

    目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码 1 概述 本代码用于灵敏性分析的方法模型 2 运行结果 3 参考文献 1 姚宏亮 苌健 王浩 李俊照 灵敏性分析下的因果网络参数的扰动学习研究 J 计算机科学与探索 2012
  • MySQL之面试真题详解

    目录 1 前言 1 1InnDB存储引擎与MyISAM存储引擎的区别 1 2关系型数据库常用的三个操作 1 3行转列 2 面试题详解 2 1建立数据表 2 1 1要求 2 1 2代码 2 2试题讲解与展示 1 前言 1 1InnDB存储引擎
  • 前端实习面试总结

    文章目录 HTML CSS 1 如何理解HTML语义化 2 哪些HTML是块级元素 内联元素 行内元素 行内块元素 3 盒模型的宽度和高度如何计算 4 margin纵向重叠的问题 5 margin负值的问题 6 BFC的理解与应用 7 fl
  • 【深度学习】空洞卷积(扩张卷积)和一般卷积的区别 Dilated Convolution

    文章目录 前言 一 感受野 二 空洞卷积 三 补充 padding 前言 目前来说有一个想法 距离寒假还有一个月 想在放寒假之前把深度学习这半年来的基础知识都补充回归一下 以写成博客的形式进行汇总 今天回顾的主要是理论方面的 空洞卷积 一
  • Python第三章 程序控制结构

    Python第三章 程序控制结构 if 选择语句 while 循环语句 for 循环语句 循环嵌套 跳转语句 if 选择语句 基础语法 if 表达式1 语句块1 elif 表达式2 语句块2 elif 表达式3 语句块3 else 语句块n
  • 周期性线程池newScheduledThreadPool详解

    线程池支持定时以及周期性执行任务 创建一个corePoolSize为传入参数 最大线程数为整形的最大数的线程池 public static ScheduledExecutorService newScheduledThreadPool in
  • 【计算机网络】实验六 综合设计实验

    实验六 综合设计实验 一 实验目的 规划设计中小型园区网络系统设计方案 给出需求分析 设计规划 设备选型等 二 实验属性 设计性实验 三 实验仪器设备及器材 Cisco2950交换机 Cisco 3560交换机 Cisco 2621 Cis
  • 记录一次在centos 8 下docker 安装reids 一直报错无法启动问题

    目录 1 首先翻看了网上大部分启动失败原因总结如下 可以使用docker logs reids查看启动失败原因 1 1启动挂载目录失败 通常是没有创建目录 1 2启动闪退 通常为配置文件 daemonize yes 没有改成 no 导致 1
  • 【RASA】KafkaEventBroker性能优化

    项目背景 实际项目中 对话数据需要存储到数据库中 就先使用rasa自带的KafkaEventBroker 用于对话消息生产 再用flink对对话数据进行消费 不过在使用的过程中会遇到性能的瓶颈 具体如下 先贴一下KafkaEventBrok
  • 区间预测

    区间预测 MATLAB实现QRGRU门控循环单元分位数回归时间序列区间预测 目录 区间预测 MATLAB实现QRGRU门控循环单元分位数回归时间序列区间预测 效果一览 基本介绍 模型描述 程序设计 参考资料 效果一览 进阶版 基础版 基本介
  • 若依quartz定时任务添加SimpleTrigger(解决超过60分钟的定时)

    在用到若依quartz时 业务的定时间隔不能整除60 例如31 91 181分钟 用Cron的表达式实现31分钟的运行时间并不是每次都间隔31分钟 Cron表达式的另一种实现逻辑就是用Cron的确切日期30 14 18 23 9 指定每次执
  • 使用JS实现一个简单的观察者模式(Observer)

    聚沙成塔 每天进步一点点 专栏简介 手撸Observer 写在最后 专栏简介 前端入门之旅 探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅 这个专栏是为那些对Web开发感兴趣 刚刚
  • 向管理转型:思维篇 转

    作者 阿蒙 在我的文章里出现最多的词语估计就是 思维 二字了 我认为一个人的思维能力是非常重要的 思维是智力的核心 思维能力包含的范围很广 常见的有逆向思维 发散思维 融合思维等等 记得曾经看过一本好象是老美写的书 思维决定一切 书里强调思
  • 前端系列之jQuery(jQueryDOM操作)

    一 如何筛选jQuery对象 div div div
  • Unity如何调用手机拍照缩放功能

    上级交代了一个任务 需要软件调用手机相机拍照缩放功能 方便用户自行搭配图片 第一个问题 如何获取手机的摄像头所拍摄的画面 unity中有一个类 WebCamTexture 继承自Texture类 访问它的静态成员devices 即可获得所有
  • openGauss学习笔记-22 openGauss 简单数据管理-HAVING子句

    文章目录 openGauss学习笔记 22 openGauss 简单数据管理 HAVING子句 22 1 语法格式 22 2 参数说明 22 3 示例 openGauss学习笔记 22 openGauss 简单数据管理 HAVING子句 H
  • 小程序接入微信支付V3接口开发教程

    前言 最近做了一个小程序对接微信支付的需求 查看微信支付文档 还是感觉有点凌乱 所以做一个统一整理 供大家参考 API参考官方文档 https pay weixin qq com wiki doc apiv3 open pay chapte