小程序接入微信支付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详细阅读以下源码。

相关文章
|
3天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的校园水电费管理微信小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的校园水电费管理微信小程序的详细设计和实现
43 0
|
3天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的优购电商小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的优购电商小程序的详细设计和实现
40 0
|
3天前
|
小程序 Java API
微信小程序和springboot实现微信支付
微信小程序和springboot实现微信支付
12 0
|
3天前
|
小程序 API
微信小程序-微信支付
微信小程序-微信支付
7 0
|
3天前
|
小程序 开发工具 Android开发
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(二)
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(二)
|
3天前
|
小程序 JavaScript 开发工具
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(一)
微信小程序开发工具的使用,各个配置文件详解,小程序开发快速入门(一)
|
3天前
|
移动开发 小程序 安全
使用阿里云短信+微信短链接跳转微信小程序
此内容是关于使用阿里云短信带传递参数的微信短链接跳转到微信小程序。首先,需要准备微信开发者工具和一个已认证的小程序。接着,开通云开发并配置云开发权限。然后,配置H5静态网页,包括设置云开发权限和处理不同设备的跳转方式。最后,上传云函数并修改其权限,获取微信短信链接,配置短链接参数,并开通阿里云短信服务以进行测试验证。整个过程涉及到了微信开发者工具、云开发、H5页面配置、云函数的创建和部署以及阿里云短信服务的开通和使用等步骤。
52 0
|
3天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的在线课堂微信小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的在线课堂微信小程序的详细设计和实现
35 3
|
3天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信课堂助手小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信课堂助手小程序的详细设计和实现
60 3
|
3天前
|
小程序 JavaScript Java
基于SpringBoot+Vue+uniapp微信小程序的微信点餐小程序的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的微信点餐小程序的详细设计和实现
31 1

热门文章

最新文章