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

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

前言

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

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详细阅读以下源码。

相关文章
|
6天前
|
自然语言处理 搜索推荐 小程序
微信公众号接口:解锁公众号开发的无限可能
微信公众号接口是微信官方提供的API,支持开发者通过编程与公众号交互,实现自动回复、消息管理、用户管理和数据分析等功能。本文深入探讨接口的定义、类型、优势及应用场景,如智能客服、内容分发、电商闭环等,并介绍开发流程和工具,帮助运营者提升用户体验和效率。未来,随着微信生态的发展,公众号接口将带来更多机遇,如小程序融合、AI应用等。
|
4月前
|
人工智能 JavaScript 定位技术
微信的接口都有哪些?
【10月更文挑战第17天】微信的接口都有哪些?
448 43
|
17天前
|
人工智能 开发框架 机器人
AstrBot:轻松将大模型接入QQ、微信等消息平台,打造多功能AI聊天机器人的开发框架,附详细教程
AstrBot 是一个开源的多平台聊天机器人及开发框架,支持多种大语言模型和消息平台,具备多轮对话、语音转文字等功能。
2733 15
AstrBot:轻松将大模型接入QQ、微信等消息平台,打造多功能AI聊天机器人的开发框架,附详细教程
|
4月前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
953 1
|
15天前
|
人工智能 小程序 程序员
【视频测评 DEMO 参考】VSCode 神级 AI 插件通义灵码:完全免费+实战教程+微信贪吃蛇小程序
VSCode 神级 AI 插件通义灵码:完全免费+实战教程+微信贪吃蛇小程序
252 7
|
22天前
|
存储 小程序 前端开发
微信小程序与Java后端实现微信授权登录功能
微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验
201 12
|
1天前
|
JSON 缓存 小程序
微信小程序组件封装与复用:提升开发效率
本文深入探讨了微信小程序的组件封装与复用,涵盖组件的意义、创建步骤、属性与事件处理,并通过自定义弹窗组件的案例详细说明。组件封装能提高代码复用性、开发效率和可维护性,确保UI一致性。掌握这些技能有助于构建更高质量的小程序。
|
6天前
|
小程序 测试技术 数据安全/隐私保护
微信公众号接口测试实战指南
微信公众号接口测试是确保系统稳定性和功能完整性的重要环节。本文详细介绍了测试全流程,包括准备、工具选择(如Postman、JMeter)、用例设计与执行,以及常见问题的解决方法。通过全面测试,可以提前发现潜在问题,优化用户体验,确保公众号上线后稳定运行。内容涵盖基础接口、高级接口、微信支付和数据统计接口的测试,强调了功能验证、性能优化、安全保护及用户体验的重要性。未来,随着微信生态的发展,接口测试将面临更多挑战和机遇,如小程序融合、AI应用和国际化拓展。
|
4月前
|
存储 小程序 安全
微信的开发管理都需要配置什么?
【10月更文挑战第17天】微信的开发管理都需要配置什么?
71 0
|
1月前
|
移动开发 小程序
thinkphp+uniapp开发的多端商城系统源码/H5/小程序/APP支持DIY模板直播分销
thinkphp+uniapp开发的多端商城系统源码/H5/小程序/APP支持DIY模板直播分销
34 0

热门文章

最新文章