杰瑞科技汇

微信支付Java开发教程从哪开始?

目录

  1. 第一部分:准备工作

    微信支付Java开发教程从哪开始?-图1
    (图片来源网络,侵删)
    • 1 注册微信商户平台
    • 2 获取必要参数
    • 3 配置支付目录
    • 4 下载API证书
    • 5 开发环境准备
  2. 第二部分:后端项目搭建 (Spring Boot)

    • 1 创建Spring Boot项目
    • 2 添加依赖
    • 3 配置文件
  3. 第三部分:核心代码实现

    • 1 统一下单 (创建订单)
    • 2 生成前端调起支付所需的参数
    • 3 查询订单
    • 4 退款
    • 5 退款查询
  4. 第四部分:前端页面集成

    • 1 调用后端下单接口
    • 2 使用微信JS-SDK调起支付
  5. 第五部分:重要注意事项

    微信支付Java开发教程从哪开始?-图2
    (图片来源网络,侵删)
    • 1 签名机制详解
    • 2 回调通知处理
    • 3 错误码与调试

第一部分:准备工作

在开始编码之前,你必须完成以下准备工作,这是所有微信支付功能的基础。

1 注册微信商户平台

  1. 访问 微信商户平台
  2. 使用你的企业或个体工商户账号登录,如果你还没有,需要先完成入驻。
  3. 完成账户验证和产品开通,确保你的账户状态正常。

2 获取必要参数

登录商户平台后,在 "产品中心" -> "产品大全" 中,确保你已经开通了 "JSAPI支付""Native支付"(用于生成二维码)。

"账户中心" -> "API安全" 页面,找到并记录以下关键信息:

  • APIv3密钥 (APIv3 Key): 用于生成和验证签名。非常重要,请妥善保管!
  • API证书序列号: 下载证书后可以看到。
  • API证书密钥: 下载证书后可以看到。
  • 商户号 (mchid): 你的商户平台ID。

3 配置支付目录

"产品中心" -> "开发配置" 中,设置你的支付授权目录。

微信支付Java开发教程从哪开始?-图3
(图片来源网络,侵删)
  • JSAPI支付: 填写你的H5网站域名或小程序的AppID,如果你的前端页面是 https://www.yourdomain.com/pay.html,你就需要把这个域名添加进去。不配置将无法调起支付
  • Native支付: 填写你的服务器域名。

4 下载API证书

"账户中心" -> "API安全" -> "API证书" 中,下载 "商户API证书",下载后你会得到一个 .p12 文件,一个证书序列号和一个证书密钥,在Java代码中,你需要使用 .p12 文件和证书密钥来加载证书。

5 开发环境准备

  • JDK: 1.8 或更高版本。
  • IDE: IntelliJ IDEA 或 Eclipse。
  • 构建工具: Maven 或 Gradle。
  • Spring Boot: 2.x 或 3.x 版本。

第二部分:后端项目搭建 (Spring Boot)

我们将使用Spring Boot来快速搭建项目。

1 创建Spring Boot项目

你可以通过 Spring Initializr 快速创建一个项目,选择以下依赖:

  • Spring Web
  • Spring Boot DevTools (可选,用于热部署)
  • Lombok (可选,简化代码)

2 添加依赖

pom.xml 文件中,添加微信支付官方提供的SDK依赖和HTTP客户端依赖。

<dependencies>
    <!-- Spring Boot Web Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!-- 微信支付V3 SDK -->
    <dependency>
        <groupId>com.github.wechatpay-java</groupId>
        <artifactId>wechatpay-java</artifactId>
        <version>0.2.12</version> <!-- 请使用最新版本 -->
    </dependency>
    <!-- 微信支付V3服务核心库 -->
    <dependency>
        <groupId>com.github.wechatpay-java</groupId>
        <artifactId>wechatpay-java-service</artifactId>
        <version>0.2.12</version> <!-- 请使用最新版本 -->
    </dependency>
</dependencies>

3 配置文件

application.ymlapplication.properties 中配置微信支付的相关参数。

application.yml

server:
  port: 8080
# 微信支付配置
wechat:
  # 商户号
  mchid: your_mchid
  # APIv3密钥
  api-v3-key: your_api_v3_key
  # API证书序列号
  cert_serial_no: your_cert_serial_no
  # API证书密钥 (p12文件密码)
  cert-key: your_cert_key
  # 证书文件路径 (将p12文件放在resources目录下)
  cert-path: classpath:apiclient_cert.p12

第三部分:核心代码实现

1 统一下单 (创建订单)

这是支付流程的核心,后端需要调用微信的统一下单接口,创建一个预支付交易单。

创建配置类

// src/main/java/com/example/config/WechatPayConfig.java
package com.example.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat")
public class WechatPayConfig {
    private String mchid;
    private String apiV3Key;
    private String certSerialNo;
    private String certKey;
    private String certPath;
}

创建支付服务类

// src/main/java/com/example/service/WechatPayService.java
package com.example.service;
import com.example.config.WechatPayConfig;
import com.github.wechatpay.java.core.RSAAutoCertificateConfig;
import com.github.wechatpay.java.service.payments.jsapi.JsapiService;
import com.github.wechatpay.java.service.payments.jsapi.model.PrepayRequest;
import com.github.wechatpay.java.service.payments.jsapi.model.PrepayResponse;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.security.PrivateKey;
import java.util.UUID;
@Service
public class WechatPayService {
    private final WechatPayConfig config;
    private final JsapiService jsapiService;
    public WechatPayService(WechatPayConfig config) throws Exception {
        this.config = config;
        // 加载商户API证书
        InputStream certStream = this.getClass().getClassLoader().getResourceAsStream(config.getCertPath());
        PrivateKey merchantPrivateKey = RSAAutoCertificateConfig.loadPrivateKeyFromPKCS12(certStream, config.getCertKey());
        // 初始化配置
        RSAAutoCertificateConfig rsaConfig = new RSAAutoCertificateConfig.Builder()
                .merchantId(config.getMchid())
                .privateKey(merchantPrivateKey)
                .apiV3Key(config.getApiV3Key())
                .build();
        // 创建JsapiService实例
        this.jsapiService = new JsapiService.Builder().config(rsaConfig).build();
    }
    /**
     * 创建预支付订单
     * @param openid 用户的OpenID (通过微信登录获取)
     * @param description 商品描述
     * @param amount 总金额,单位为分
     * @return 预支付订单ID
     */
    public String createPrepayOrder(String openid, String description, Integer amount) {
        // 生成商户订单号
        String outTradeNo = UUID.randomUUID().toString().replace("-", "");
        // 构建请求体
        PrepayRequest request = new PrepayRequest();
        request.setAppid("你的AppID"); // 你的小程序或公众号的AppID
        request.setMchid(config.getMchid());
        request.setDescription(description);
        request.setOutTradeNo(outTradeNo);
        request.setNotifyUrl("http://your-domain.com/api/pay/notify"); // 回调通知地址
        request.setAmount(new PrepayRequest.Amount().setTotal(amount));
        request.setPayer(new PrepayRequest.Payer().setOpenid(openid));
        try {
            // 发起请求
            PrepayResponse response = jsapiService.prepay(request);
            // 返回预支付会话标识
            return response.getPrepayId();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("创建预支付订单失败: " + e.getMessage());
        }
    }
}

2 生成前端调起支付所需的参数

微信JSAPI支付需要特定的参数才能在前端调起,我们需要创建一个Controller来提供这些参数。

// src/main/java/com/example/controller/PayController.java
package com.example.controller;
import com.example.service.WechatPayService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/pay")
@RequiredArgsConstructor
public class PayController {
    private final WechatPayService wechatPayService;
    /**
     * 创建订单并返回前端调起支付所需参数
     */
    @PostMapping("/create-order")
    public Map<String, Object> createOrder(@RequestBody CreateOrderRequest request) {
        // 1. 调用统一下单接口
        String prepayId = wechatPayService.createPrepayOrder(request.getOpenid(), request.getDescription(), request.getAmount());
        // 2. 生成前端调起支付所需参数
        Map<String, Object> payParams = new HashMap<>();
        payParams.put("appId", "你的AppID");
        payParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000));
        payParams.put("nonceStr", java.util.UUID.randomUUID().toString().replace("-", ""));
        payParams.put("package", "prepay_id=" + prepayId);
        payParams.put("signType", "RSA");
        // 3. 生成paySign (签名)
        // 注意:签名逻辑需要根据微信官方文档实现,这里简化处理
        // 实际项目中,应该使用SDK提供的签名工具
        String paySign = generatePaySign(payParams);
        payParams.put("paySign", paySign);
        return payParams;
    }
    // 这是一个简化的签名方法,实际请参考官方SDK
    private String generatePaySign(Map<String, Object> params) {
        // 1. 将参数按ASCII码从小到大排序
        // 2. 使用URL键值对的格式拼接成字符串 stringA
        // 3. 在stringA最后拼接上key(APIv3密钥)
        // 4. 对stringA进行SHA256加密,得到sign
        // 5. 将sign所有字符转换为大写
        // 这里为了演示,直接返回一个假签名,请务必替换为正确的签名逻辑!
        return "SIMULATED_PAY_SIGN";
    }
}
// 请求DTO
class CreateOrderRequest {
    private String openid;
    private String description;
    private Integer amount; // 单位:分
    // Getters and Setters
    public String getOpenid() { return openid; }
    public void setOpenid(String openid) { this.openid = openid; }
    public String getDescription() { return description; }
    public void setDescription(String description) { this.description = description; }
    public Integer getAmount() { return amount; }
    public void setAmount(Integer amount) { this.amount = amount; }
}

3 查询订单

// 在 WechatPayService.java 中添加
import com.github.wechatpay.java.service.payments.jsapi.model.QueryOrderByOutTradeNoRequest;
import com.github.wechatpay.java.service.payments.jsapi.model.QueryOrderResponse;
public QueryOrderResponse queryOrder(String outTradeNo) {
    QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
    request.setMchid(config.getMchid());
    request.setOutTradeNo(outTradeNo);
    try {
        return jsapiService.queryOrderByOutTradeNo(request);
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("查询订单失败: " + e.getMessage());
    }
}

4 退款

// 在 WechatPayService.java 中添加
import com.github.wechatpay.java.service.payments.model.RefundRequest;
import com.github.wechatpay.java.service.payments.model.RefundResponse;
import java.math.BigDecimal;
public RefundResponse refund(String outTradeNo, String outRefundNo, BigDecimal refundAmount) {
    RefundRequest request = new RefundRequest();
    request.setOutTradeNo(outTradeNo);
    request.setOutRefundNo(outRefundNo);
    request.setNotifyUrl("http://your-domain.com/api/pay/refund-notify");
    request.setAmount(new RefundRequest.Amount()
            .setTotal(new BigDecimal("100")) // 原订单总金额
            .setRefund(refundAmount) // 退款金额
            .setCurrency("CNY"));
    try {
        return jsapiService.refund(request);
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("退款失败: " + e.getMessage());
    }
}

5 退款查询

// 在 WechatPayService.java 中添加
import com.github.wechatpay.java.service.payments.model.QueryRefundRequest;
import com.github.wechatpay.java.service.payments.model.QueryRefundResponse;
public QueryRefundResponse queryRefund(String outRefundNo) {
    QueryRefundRequest request = new QueryRefundRequest();
    request.setOutRefundNo(outRefundNo);
    try {
        return jsapiService.queryRefund(request);
    } catch (Exception e) {
        e.printStackTrace();
        throw new RuntimeException("退款查询失败: " + e.getMessage());
    }
}

第四部分:前端页面集成

这里我们模拟一个简单的HTML页面,通过JavaScript调起支付。

pay.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">微信支付测试</title>
    <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
<body>
    <h1>微信支付测试</h1>
    <button onclick="handlePay()">立即支付</button>
    <script>
        function handlePay() {
            // 1. 调用后端接口获取支付参数
            fetch('/api/pay/create-order', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    openid: '用户的OpenID', // 必须通过微信登录获取
                    description: '测试商品',
                    amount: 1 // 1分钱
                })
            })
            .then(response => response.json())
            .then(data => {
                if (data.appId) {
                    // 2. 调用微信JS-SDK调起支付
                    wx.chooseWXPay({
                        appId: data.appId,
                        timestamp: data.timeStamp,
                        nonceStr: data.nonceStr,
                        package: data.package,
                        signType: data.signType,
                        paySign: data.paySign,
                        success: function (res) {
                            alert('支付成功!');
                            console.log('支付成功', res);
                        },
                        fail: function (res) {
                            alert('支付失败,请重试。');
                            console.error('支付失败', res);
                        },
                        cancel: function (res) {
                            alert('您已取消支付。');
                            console.log('取消支付', res);
                        }
                    });
                } else {
                    alert('获取支付参数失败: ' + JSON.stringify(data));
                }
            })
            .catch(error => {
                console.error('Error:', error);
                alert('请求失败,请检查网络。');
            });
        }
    </script>
</body>
</html>

第五部分:重要注意事项

1 签名机制详解

微信支付V3使用 SHA256withRSA 签名算法,签名流程如下:

  1. 构造签名串: 将请求URL(不含query)、请求方法(如POST)、请求时间戳、请求随机串和请求体(按参数名ASCII从小到大排序后拼接)组合成一个字符串。
  2. 计算签名: 使用商户的私钥对签名串进行SHA256withRSA签名。
  3. 发送签名: 将签名值放在HTTP请求头的 Wechatpay-TimestampWechatpay-NonceWechatpay-Signature 中。

强烈建议:直接使用官方提供的SDK,它已经封装了所有复杂的签名逻辑,避免自己实现出错。

2 回调通知处理

当支付状态改变(成功、失败)或退款完成后,微信服务器会主动向你在统一下单时设置的 notify_url 发送一个POST请求,你的服务器必须处理这个回调。

回调Controller

// PayController.java
@PostMapping("/notify")
public String payNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
    // 1. 获取微信服务器发送的请求体
    String body = IOUtils.toString(request.getInputStream(), "UTF-8");
    // 2. 获取请求头中的签名信息
    String wechatpaySignature = request.getHeader("Wechatpay-Signature");
    String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");
    String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
    // 3. 验证签名
    // 你的WechatPayConfig实例需要暴露出来或者注入
    // 这里假设你已经有了rsaConfig
    if (!rsaConfig.verifySignature(wechatpayTimestamp, wechatpayNonce, body, wechatpaySignature)) {
        response.setStatus(400);
        return "FAIL";
    }
    // 4. 解析通知数据
    JSONObject jsonObject = JSON.parseObject(body);
    String resourceType = jsonObject.getString("resource_type");
    if ("encrypt-resource".equals(resourceType)) {
        String ciphertext = jsonObject.getJSONObject("resource").getString("ciphertext");
        String associatedData = jsonObject.getJSONObject("resource").getString("associated_data");
        String nonce = jsonObject.getJSONObject("resource").getString("nonce");
        // 使用APIv3Key解密
        String decryptedData = AesUtils.decryptToString(associatedData, nonce, ciphertext, config.getApiV3Key());
        JSONObject decryptedJson = JSON.parseObject(decryptedJson);
        // 5. 根据业务逻辑处理
        String outTradeNo = decryptedJson.getString("out_trade_no");
        String transactionId = decryptedJson.getString("transaction_id");
        String tradeState = decryptedJson.getString("trade_state");
        if ("SUCCESS".equals(tradeState)) {
            // 业务处理:更新订单状态为已支付
            System.out.println("订单支付成功: " + outTradeNo);
            response.setStatus(200);
            return "SUCCESS";
        } else {
            // 处理其他状态
            System.out.println("订单支付状态: " + tradeState);
            response.setStatus(200);
            return "SUCCESS";
        }
    }
    response.setStatus(400);
    return "FAIL";
}

注意:你需要引入 commons-iofastjson 等依赖来处理IO和JSON。

3 错误码与调试

  • 日志: 在你的代码中添加详细的日志,特别是调用微信API前后的请求和响应,这是排查问题的最有效手段。
  • 微信商户平台: 在商户平台的 "交易" -> "订单查询""资金" -> "退款订单" 中可以查到订单的详细状态。
  • 官方文档: 遇到问题时,第一时间查阅 微信支付官方文档,文档是最权威的信息来源。

这份教程为你提供了一个完整的微信支付Java开发流程,从环境搭建到核心功能实现,再到前端集成和关键注意事项,请务必仔细阅读官方文档,并根据你的实际业务需求进行调整。

分享:
扫描分享到社交APP
上一篇
下一篇