前言

在开发电商、SaaS等需要在线支付的应用时,微信支付和支付宝是最常用的两种支付方式。本文将详细介绍如何在Java后端项目中接入这两种支付方式,包括:

  • 如何申请微信支付和支付宝商户账号

  • 如何配置沙箱环境进行测试

  • Java后端的配置类编写

  • 完整的支付流程代码示例

适用版本:微信支付 V3 API、支付宝开放平台最新API


一、微信支付接入

1.1 申请微信支付商户号

申请条件
  • 已认证的微信公众号(服务号)或小程序

  • 企业营业执照

  • 法人身份证

  • 对公银行账户

申请流程
  1. 登录微信公众平台

  2. 进入微信支付申请页面

  3. 填写申请信息

    商户简称:显示在用户账单中的名称
    客服电话:用户可联系的电话
    经营类目:根据实际业务选择
    营业执照:上传清晰的营业执照照片
    法人身份证:正反面照片
  4. 等待审核

    • 审核时间:1-5个工作日

    • 审核通过后会收到邮件通知

  5. 账户验证

    • 使用对公账户向腾讯指定账户打款(随机金额)

    • 验证通过后即可获得商户号

获取关键参数

申请成功后,在商户平台(微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式)获取以下信息:

商户号(mchId):如 1600000001
API密钥(apiKey):在API安全中设置,32位字符串
APIv3密钥(apiV3Key):V3接口使用
商户证书序列号(merchantCertificateSerial):在API安全中下载证书
商户私钥(merchantPrivateKey):证书中的私钥文件

1.2 配置微信支付沙箱环境

微信支付提供了沙箱环境用于开发测试。

获取沙箱参数
  1. 访问 微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式

  2. 进入:开发配置 → 沙箱环境

  3. 获取沙箱商户号和沙箱API密钥

注意:微信支付沙箱环境功能相对有限,建议使用真实商户号的小额支付进行测试。

沙箱环境地址
沙箱API地址:https://api.mch.weixin.qq.com/sandboxnew/
正式API地址:https://api.mch.weixin.qq.com/

1.3 Java后端配置

1.3.1 添加Maven依赖
<dependencies>
    <!-- 微信支付SDK -->
    <dependency>
        <groupId>com.github.wechatpay-apiv3</groupId>
        <artifactId>wechatpay-java</artifactId>
        <version>0.2.14</version>
    </dependency>
    
    <!-- HTTP客户端 -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.14</version>
    </dependency>
    
    <!-- JSON处理 -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
</dependencies>
1.3.2 配置文件 application.yml
wechat:
  pay:
    # 商户号
    mch-id: "1600000001"
    # 商户API密钥(V2接口使用)
    api-key: "your-api-key-here"
    # 商户APIv3密钥
    api-v3-key: "your-api-v3-key-here"
    # 商户证书序列号
    merchant-certificate-serial: "your-certificate-serial"
    # 商户私钥文件路径(classpath下的文件名)
    merchant-private-key-path: "classpath:cert/apiclient_key.pem"
    # 支付结果通知地址(必须是公网可访问的HTTPS地址)
    notify-url: "https://your-domain.com/api/pay/wechat/notify"
    # 退款结果通知地址
    refund-notify-url: "https://your-domain.com/api/pay/wechat/refund-notify"
1.3.3 微信支付配置类
package com.example.config;
​
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
​
/**
 * 微信支付配置类
 */
@Configuration
public class WechatPayConfig {
​
    @Value("${wechat.pay.mch-id}")
    private String merchantId;
​
    @Value("${wechat.pay.api-v3-key}")
    private String apiV3Key;
​
    @Value("${wechat.pay.merchant-certificate-serial}")
    private String merchantCertificateSerial;
​
    @Value("${wechat.pay.merchant-private-key-path}")
    private String merchantPrivateKeyPath;
​
    @Value("${wechat.pay.notify-url}")
    private String notifyUrl;
​
    /**
     * 配置微信支付核心配置
     */
    @Bean
    public Config wechatPayConfig() throws IOException {
        // 读取商户私钥
        String privateKey = loadPrivateKey();
​
        // 构建配置(自动更新平台证书)
        return new RSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKey(privateKey)
                .merchantSerialNumber(merchantCertificateSerial)
                .apiV3Key(apiV3Key)
                .build();
    }
​
    /**
     * 配置Native支付服务(PC扫码支付)
     */
    @Bean
    public NativePayService nativePayService(Config config) {
        return new NativePayService.Builder().config(config).build();
    }
​
    /**
     * 配置JSAPI支付服务(微信内支付)
     */
    @Bean
    public JsapiServiceExtension jsapiServiceExtension(Config config) {
        return new JsapiServiceExtension.Builder().config(config).build();
    }
​
    /**
     * 配置通知解析器
     */
    @Bean
    public NotificationParser notificationParser(Config config) {
        return new NotificationParser((NotificationConfig) config);
    }
​
    /**
     * 读取商户私钥文件
     */
    private String loadPrivateKey() throws IOException {
        try (InputStream inputStream = getClass().getClassLoader()
                .getResourceAsStream("cert/apiclient_key.pem")) {
            if (inputStream == null) {
                throw new IOException("商户私钥文件不存在");
            }
            return new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
        }
    }
}
1.3.4 微信支付Service类
package com.example.service;
​
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
​
import java.util.HashMap;
import java.util.Map;
​
/**
 * 微信支付Service
 */
@Service
public class WechatPayService {
​
    private static final Logger log = LoggerFactory.getLogger(WechatPayService.class);
​
    @Autowired
    private NativePayService nativePayService;
​
    @Autowired
    private JsapiServiceExtension jsapiServiceExtension;
​
    @Value("${wechat.pay.notify-url}")
    private String notifyUrl;
​
    /**
     * 创建Native支付订单(PC扫码支付)
     * 
     * @param orderId   商户订单号
     * @param amount    支付金额(单位:分)
     * @param description 商品描述
     * @return 二维码链接
     */
    public String createNativeOrder(String orderId, Long amount, String description) {
        try {
            // 构建请求参数
            PrepayRequest request = new PrepayRequest();
            request.setAppid("your-appid");           // 公众号或小程序appid
            request.setMchid("your-mchid");           // 商户号
            request.setDescription(description);
            request.setOutTradeNo(orderId);
            request.setNotifyUrl(notifyUrl);
​
            // 设置金额
            Amount amountObj = new Amount();
            amountObj.setTotal(amount.intValue());
            amountObj.setCurrency("CNY");
            request.setAmount(amountObj);
​
            // 调用下单接口
            PrepayResponse response = nativePayService.prepay(request);
            log.info("Native支付订单创建成功,订单号:{},二维码:{}", orderId, response.getCodeUrl());
​
            return response.getCodeUrl();
        } catch (Exception e) {
            log.error("Native支付订单创建失败,订单号:{}", orderId, e);
            throw new RuntimeException("创建支付订单失败", e);
        }
    }
​
    /**
     * 创建JSAPI支付订单(微信内H5支付)
     * 
     * @param orderId     商户订单号
     * @param amount      支付金额(单位:分)
     * @param description 商品描述
     * @param openid      用户openid
     * @return 支付参数Map,前端用于调起支付
     */
    public Map<String, String> createJsapiOrder(String orderId, Long amount, 
                                                String description, String openid) {
        try {
            // 构建请求参数
            PrepayRequest request = new PrepayRequest();
            request.setAppid("your-appid");
            request.setMchid("your-mchid");
            request.setDescription(description);
            request.setOutTradeNo(orderId);
            request.setNotifyUrl(notifyUrl);
​
            // 设置金额
            Amount amountObj = new Amount();
            amountObj.setTotal(amount.intValue());
            amountObj.setCurrency("CNY");
            request.setAmount(amountObj);
​
            // 设置支付者信息
            Payer payer = new Payer();
            payer.setOpenid(openid);
            request.setPayer(payer);
​
            // 调用下单接口,获取前端调起支付所需的参数
            PrepayWithRequestPaymentResponse response = jsapiServiceExtension.prepayWithRequestPayment(request);
​
            // 封装返回给前端的参数
            Map<String, String> paymentParams = new HashMap<>();
            paymentParams.put("appId", response.getAppId());
            paymentParams.put("timeStamp", response.getTimeStamp());
            paymentParams.put("nonceStr", response.getNonceStr());
            paymentParams.put("package", response.getPackageVal());
            paymentParams.put("signType", response.getSignType());
            paymentParams.put("paySign", response.getPaySign());
​
            log.info("JSAPI支付订单创建成功,订单号:{}", orderId);
            return paymentParams;
        } catch (Exception e) {
            log.error("JSAPI支付订单创建失败,订单号:{}", orderId, e);
            throw new RuntimeException("创建支付订单失败", e);
        }
    }
​
    /**
     * 查询订单状态
     * 
     * @param orderId 商户订单号
     * @return 订单状态
     */
    public Transaction queryOrder(String orderId) {
        try {
            QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
            request.setMchid("your-mchid");
            request.setOutTradeNo(orderId);
​
            Transaction transaction = nativePayService.queryOrderByOutTradeNo(request);
            log.info("订单查询成功,订单号:{},状态:{}", orderId, transaction.getTradeState());
            return transaction;
        } catch (Exception e) {
            log.error("订单查询失败,订单号:{}", orderId, e);
            throw new RuntimeException("查询订单失败", e);
        }
    }
​
    /**
     * 关闭订单
     * 
     * @param orderId 商户订单号
     */
    public void closeOrder(String orderId) {
        try {
            CloseOrderRequest request = new CloseOrderRequest();
            request.setMchid("your-mchid");
            request.setOutTradeNo(orderId);
​
            nativePayService.closeOrder(request);
            log.info("订单关闭成功,订单号:{}", orderId);
        } catch (Exception e) {
            log.error("订单关闭失败,订单号:{}", orderId, e);
            throw new RuntimeException("关闭订单失败", e);
        }
    }
}
1.3.5 微信支付Controller类
package com.example.controller;
​
import com.example.service.WechatPayService;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
​
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.util.HashMap;
import java.util.Map;
​
/**
 * 微信支付Controller
 */
@RestController
@RequestMapping("/api/pay/wechat")
public class WechatPayController {
​
    private static final Logger log = LoggerFactory.getLogger(WechatPayController.class);
​
    @Autowired
    private WechatPayService wechatPayService;
​
    @Autowired
    private NotificationParser notificationParser;
​
    /**
     * 创建Native支付订单(返回二维码链接)
     */
    @PostMapping("/native/create")
    public Map<String, Object> createNativeOrder(@RequestParam String orderId,
                                                  @RequestParam Long amount,
                                                  @RequestParam String description) {
        Map<String, Object> result = new HashMap<>();
        try {
            String codeUrl = wechatPayService.createNativeOrder(orderId, amount, description);
            result.put("code", 200);
            result.put("data", codeUrl);
            result.put("message", "创建成功");
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", e.getMessage());
        }
        return result;
    }
​
    /**
     * 创建JSAPI支付订单(返回前端调起支付的参数)
     */
    @PostMapping("/jsapi/create")
    public Map<String, Object> createJsapiOrder(@RequestParam String orderId,
                                                 @RequestParam Long amount,
                                                 @RequestParam String description,
                                                 @RequestParam String openid) {
        Map<String, Object> result = new HashMap<>();
        try {
            Map<String, String> paymentParams = wechatPayService.createJsapiOrder(
                    orderId, amount, description, openid);
            result.put("code", 200);
            result.put("data", paymentParams);
            result.put("message", "创建成功");
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", e.getMessage());
        }
        return result;
    }
​
    /**
     * 微信支付异步通知回调
     */
    @PostMapping("/notify")
    public Map<String, String> handleNotify(HttpServletRequest request) {
        Map<String, String> result = new HashMap<>();
        try {
            // 读取请求体
            String body = readRequestBody(request);
            
            // 获取请求头
            String signature = request.getHeader("Wechatpay-Signature");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String serial = request.getHeader("Wechatpay-Serial");
​
            // 构建通知参数
            RequestParam requestParam = new RequestParam.Builder()
                    .serialNumber(serial)
                    .nonce(nonce)
                    .signature(signature)
                    .timestamp(timestamp)
                    .body(body)
                    .build();
​
            // 解析通知内容
            Transaction transaction = notificationParser.parse(requestParam, Transaction.class);
​
            // 处理支付结果
            if ("SUCCESS".equals(transaction.getTradeState())) {
                String orderId = transaction.getOutTradeNo();
                log.info("支付成功,订单号:{}", orderId);
                
                // TODO: 更新订单状态为已支付
                // orderService.updateOrderStatus(orderId, OrderStatus.PAID);
            }
​
            // 返回成功响应
            result.put("code", "SUCCESS");
            result.put("message", "处理成功");
        } catch (Exception e) {
            log.error("处理微信支付通知失败", e);
            result.put("code", "FAIL");
            result.put("message", e.getMessage());
        }
        return result;
    }
​
    /**
     * 查询订单状态
     */
    @GetMapping("/query/{orderId}")
    public Map<String, Object> queryOrder(@PathVariable String orderId) {
        Map<String, Object> result = new HashMap<>();
        try {
            Transaction transaction = wechatPayService.queryOrder(orderId);
            result.put("code", 200);
            result.put("data", transaction);
            result.put("message", "查询成功");
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", e.getMessage());
        }
        return result;
    }
​
    /**
     * 读取请求体内容
     */
    private String readRequestBody(HttpServletRequest request) {
        try (BufferedReader reader = request.getReader()) {
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            return sb.toString();
        } catch (Exception e) {
            log.error("读取请求体失败", e);
            return "";
        }
    }
}

二、支付宝支付接入

2.1 申请支付宝开放平台账号

申请条件
  • 企业支付宝账号

  • 企业营业执照

  • 法人身份证

申请流程
  1. 注册企业支付宝账号

  2. 入驻开放平台

    • 访问 支付宝开放平台

    • 使用企业支付宝账号登录

    • 完成开发者入驻(需要企业认证)

  3. 创建应用

    • 进入开放平台 → 控制台 → 创建应用

    • 选择"网页/移动应用"

    • 填写应用名称、图标等信息

  4. 添加支付能力

    • 在应用详情页 → 产品绑定

    • 添加"电脑网站支付"或"手机网站支付"

    • 等待审核(通常1-3个工作日)

  5. 配置密钥

    • 使用支付宝开放平台密钥工具生成密钥对

    • 在应用详情页 → 开发设置 → 接口加签方式

    • 上传应用公钥,保存支付宝公钥

获取关键参数
应用ID(appId):在应用详情页获取
应用私钥(privateKey):本地生成的私钥
支付宝公钥(alipayPublicKey):上传公钥后获取的支付宝公钥

2.2 配置支付宝沙箱环境

支付宝提供了完整的沙箱环境用于开发测试。

获取沙箱参数
  1. 访问 支付宝开放平台

  2. 进入:开发中心 → 沙箱环境

  3. 获取以下信息:

    沙箱应用ID
    沙箱网关地址
    沙箱RSA2密钥(需自己生成)
沙箱环境地址
沙箱网关:https://openapi-sandbox.dl.alipaydev.com/gateway.do
正式网关:https://openapi.alipay.com/gateway.do
沙箱测试账号

支付宝会提供沙箱买家账号和卖家账号,用于测试支付流程。


2.3 Java后端配置

2.3.1 添加Maven依赖
<dependencies>
    <!-- 支付宝SDK -->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.39.0.ALL</version>
    </dependency>
    
    <!-- 支付宝小程序/生活号SDK(可选) -->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java-duoyilang</artifactId>
        <version>4.39.0.ALL</version>
    </dependency>
</dependencies>
2.3.2 配置文件 application.yml
alipay:
  # 应用ID
  app-id: "2021000000000000"
  # 应用私钥
  private-key: "MIIEvgIBADANBgkqhkiG9w0BAQEFAASC..."
  # 支付宝公钥
  alipay-public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ..."
  # 支付网关(沙箱环境使用沙箱网关)
  gateway-url: "https://openapi-sandbox.dl.alipaydev.com/gateway.do"
  # 正式环境网关
  # gateway-url: "https://openapi.alipay.com/gateway.do"
  # 签名类型
  sign-type: "RSA2"
  # 异步通知地址
  notify-url: "https://your-domain.com/api/pay/alipay/notify"
  # 同步跳转地址
  return-url: "https://your-domain.com/api/pay/alipay/return"
  # 编码格式
  charset: "UTF-8"
  # 格式
  format: "json"
2.3.3 支付宝支付配置类
package com.example.config;
​
import com.alipay.api.AlipayClient;
import com.alipay.api.AlipayConfig;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
​
/**
 * 支付宝支付配置类
 */
@Configuration
public class AlipayConfig {
​
    @Value("${alipay.app-id}")
    private String appId;
​
    @Value("${alipay.private-key}")
    private String privateKey;
​
    @Value("${alipay.alipay-public-key}")
    private String alipayPublicKey;
​
    @Value("${alipay.gateway-url}")
    private String gatewayUrl;
​
    @Value("${alipay.sign-type}")
    private String signType;
​
    @Value("${alipay.charset}")
    private String charset;
​
    @Value("${alipay.format}")
    private String format;
​
    @Value("${alipay.notify-url}")
    private String notifyUrl;
​
    @Value("${alipay.return-url}")
    private String returnUrl;
​
    /**
     * 配置支付宝客户端
     */
    @Bean
    public AlipayClient alipayClient() throws Exception {
        AlipayConfig alipayConfig = new AlipayConfig();
        alipayConfig.setServerUrl(gatewayUrl);
        alipayConfig.setAppId(appId);
        alipayConfig.setPrivateKey(privateKey);
        alipayConfig.setAlipayPublicKey(alipayPublicKey);
        alipayConfig.setSignType(signType);
        alipayConfig.setCharset(charset);
        alipayConfig.setFormat(format);
​
        return new DefaultAlipayClient(alipayConfig);
    }
​
    // Getter方法,供其他类使用
    public String getNotifyUrl() {
        return notifyUrl;
    }
​
    public String getReturnUrl() {
        return returnUrl;
    }
​
    public String getAppId() {
        return appId;
    }
}
2.3.4 支付宝支付Service类
package com.example.service;
​
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.example.config.AlipayConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
​
import java.util.HashMap;
import java.util.Map;
​
/**
 * 支付宝支付Service
 */
@Service
public class AlipayService {
​
    private static final Logger log = LoggerFactory.getLogger(AlipayService.class);
​
    @Autowired
    private AlipayClient alipayClient;
​
    @Autowired
    private AlipayConfig alipayConfig;
​
    /**
     * 创建电脑网站支付订单
     * 
     * @param orderId     商户订单号
     * @param amount      支付金额(单位:元)
     * @param subject     订单标题
     * @param body        订单描述
     * @return 支付表单HTML
     */
    public String createPagePayOrder(String orderId, String amount, String subject, String body) {
        try {
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            request.setNotifyUrl(alipayConfig.getNotifyUrl());
            request.setReturnUrl(alipayConfig.getReturnUrl());
​
            AlipayTradePagePayModel model = new AlipayTradePagePayModel();
            model.setOutTradeNo(orderId);
            model.setTotalAmount(amount);
            model.setSubject(subject);
            model.setBody(body);
            model.setProductCode("FAST_INSTANT_TRADE_PAY");
            request.setBizModel(model);
​
            AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                log.info("电脑网站支付订单创建成功,订单号:{}", orderId);
                return response.getBody();
            } else {
                log.error("电脑网站支付订单创建失败,订单号:{},错误:{}", orderId, response.getSubMsg());
                throw new RuntimeException("创建支付订单失败:" + response.getSubMsg());
            }
        } catch (AlipayApiException e) {
            log.error("电脑网站支付订单创建异常,订单号:{}", orderId, e);
            throw new RuntimeException("创建支付订单异常", e);
        }
    }
​
    /**
     * 创建手机网站支付订单
     * 
     * @param orderId     商户订单号
     * @param amount      支付金额(单位:元)
     * @param subject     订单标题
     * @param body        订单描述
     * @return 支付链接
     */
    public String createWapPayOrder(String orderId, String amount, String subject, String body) {
        try {
            AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
            request.setNotifyUrl(alipayConfig.getNotifyUrl());
            request.setReturnUrl(alipayConfig.getReturnUrl());
​
            AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
            model.setOutTradeNo(orderId);
            model.setTotalAmount(amount);
            model.setSubject(subject);
            model.setBody(body);
            model.setProductCode("QUICK_WAP_WAY");
            request.setBizModel(model);
​
            AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
            if (response.isSuccess()) {
                log.info("手机网站支付订单创建成功,订单号:{}", orderId);
                return response.getBody();
            } else {
                log.error("手机网站支付订单创建失败,订单号:{},错误:{}", orderId, response.getSubMsg());
                throw new RuntimeException("创建支付订单失败:" + response.getSubMsg());
            }
        } catch (AlipayApiException e) {
            log.error("手机网站支付订单创建异常,订单号:{}", orderId, e);
            throw new RuntimeException("创建支付订单异常", e);
        }
    }
​
    /**
     * 查询订单状态
     * 
     * @param orderId 商户订单号
     * @return 订单信息
     */
    public AlipayTradeQueryResponse queryOrder(String orderId) {
        try {
            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
            AlipayTradeQueryModel model = new AlipayTradeQueryModel();
            model.setOutTradeNo(orderId);
            request.setBizModel(model);
​
            AlipayTradeQueryResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("订单查询成功,订单号:{},状态:{}", orderId, response.getTradeStatus());
                return response;
            } else {
                log.error("订单查询失败,订单号:{},错误:{}", orderId, response.getSubMsg());
                throw new RuntimeException("查询订单失败:" + response.getSubMsg());
            }
        } catch (AlipayApiException e) {
            log.error("订单查询异常,订单号:{}", orderId, e);
            throw new RuntimeException("查询订单异常", e);
        }
    }
​
    /**
     * 关闭订单
     * 
     * @param orderId 商户订单号
     */
    public void closeOrder(String orderId) {
        try {
            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
            AlipayTradeCloseModel model = new AlipayTradeCloseModel();
            model.setOutTradeNo(orderId);
            request.setBizModel(model);
​
            AlipayTradeCloseResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("订单关闭成功,订单号:{}", orderId);
            } else {
                log.error("订单关闭失败,订单号:{},错误:{}", orderId, response.getSubMsg());
                throw new RuntimeException("关闭订单失败:" + response.getSubMsg());
            }
        } catch (AlipayApiException e) {
            log.error("订单关闭异常,订单号:{}", orderId, e);
            throw new RuntimeException("关闭订单异常", e);
        }
    }
​
    /**
     * 申请退款
     * 
     * @param orderId      商户订单号
     * @param refundId     退款订单号
     * @param refundAmount 退款金额
     * @param refundReason 退款原因
     * @return 退款结果
     */
    public AlipayTradeRefundResponse refund(String orderId, String refundId, 
                                            String refundAmount, String refundReason) {
        try {
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            AlipayTradeRefundModel model = new AlipayTradeRefundModel();
            model.setOutTradeNo(orderId);
            model.setOutRequestNo(refundId);
            model.setRefundAmount(refundAmount);
            model.setRefundReason(refundReason);
            request.setBizModel(model);
​
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {
                log.info("退款申请成功,订单号:{},退款号:{}", orderId, refundId);
                return response;
            } else {
                log.error("退款申请失败,订单号:{},错误:{}", orderId, response.getSubMsg());
                throw new RuntimeException("退款申请失败:" + response.getSubMsg());
            }
        } catch (AlipayApiException e) {
            log.error("退款申请异常,订单号:{}", orderId, e);
            throw new RuntimeException("退款申请异常", e);
        }
    }
​
    /**
     * 验证支付宝异步通知签名
     * 
     * @param params 通知参数
     * @return 验证结果
     */
    public boolean verifyNotify(Map<String, String> params) {
        try {
            return AlipaySignature.rsaCheckV1(params, 
                    alipayConfig.getAlipayPublicKey(), 
                    "UTF-8", 
                    "RSA2");
        } catch (AlipayApiException e) {
            log.error("支付宝通知签名验证失败", e);
            return false;
        }
    }
}
2.3.5 支付宝支付Controller类
package com.example.controller;
​
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.example.service.AlipayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
​
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
​
/**
 * 支付宝支付Controller
 */
@RestController
@RequestMapping("/api/pay/alipay")
public class AlipayController {
​
    private static final Logger log = LoggerFactory.getLogger(AlipayController.class);
​
    @Autowired
    private AlipayService alipayService;
​
    /**
     * 创建电脑网站支付订单
     */
    @PostMapping("/page/create")
    public void createPagePayOrder(@RequestParam String orderId,
                                    @RequestParam String amount,
                                    @RequestParam String subject,
                                    @RequestParam String body,
                                    HttpServletResponse response) {
        try {
            String formHtml = alipayService.createPagePayOrder(orderId, amount, subject, body);
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write(formHtml);
            response.getWriter().flush();
        } catch (IOException e) {
            log.error("返回支付表单失败", e);
            throw new RuntimeException("创建支付订单失败", e);
        }
    }
​
    /**
     * 创建手机网站支付订单
     */
    @PostMapping("/wap/create")
    public void createWapPayOrder(@RequestParam String orderId,
                                   @RequestParam String amount,
                                   @RequestParam String subject,
                                   @RequestParam String body,
                                   HttpServletResponse response) {
        try {
            String formHtml = alipayService.createWapPayOrder(orderId, amount, subject, body);
            response.setContentType("text/html;charset=UTF-8");
            response.getWriter().write(formHtml);
            response.getWriter().flush();
        } catch (IOException e) {
            log.error("返回支付表单失败", e);
            throw new RuntimeException("创建支付订单失败", e);
        }
    }
​
    /**
     * 支付宝异步通知回调
     */
    @PostMapping("/notify")
    public String handleNotify(HttpServletRequest request) {
        try {
            // 获取支付宝POST过来的信息
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
                String name = iter.next();
                String[] values = requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
                }
                params.put(name, valueStr);
            }
​
            // 验证签名
            boolean signVerified = alipayService.verifyNotify(params);
            if (signVerified) {
                // 获取关键参数
                String orderId = params.get("out_trade_no");
                String tradeNo = params.get("trade_no");
                String tradeStatus = params.get("trade_status");
                String totalAmount = params.get("total_amount");
​
                log.info("支付宝异步通知,订单号:{},交易号:{},状态:{}", orderId, tradeNo, tradeStatus);
​
                // 处理支付结果
                if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
                    // TODO: 验证金额是否正确
                    // TODO: 更新订单状态为已支付
                    // orderService.updateOrderStatus(orderId, OrderStatus.PAID);
                    return "success";
                }
                return "success";
            } else {
                log.error("支付宝异步通知签名验证失败");
                return "failure";
            }
        } catch (Exception e) {
            log.error("处理支付宝通知异常", e);
            return "failure";
        }
    }
​
    /**
     * 支付宝同步跳转(支付成功后跳转)
     */
    @GetMapping("/return")
    public String handleReturn(HttpServletRequest request) {
        // 重定向到前端支付成功页面
        return "redirect:/pay/success";
    }
​
    /**
     * 查询订单状态
     */
    @GetMapping("/query/{orderId}")
    public Map<String, Object> queryOrder(@PathVariable String orderId) {
        Map<String, Object> result = new HashMap<>();
        try {
            AlipayTradeQueryResponse response = alipayService.queryOrder(orderId);
            result.put("code", 200);
            result.put("data", response);
            result.put("message", "查询成功");
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", e.getMessage());
        }
        return result;
    }
​
    /**
     * 申请退款
     */
    @PostMapping("/refund")
    public Map<String, Object> refund(@RequestParam String orderId,
                                       @RequestParam String refundId,
                                       @RequestParam String refundAmount,
                                       @RequestParam String refundReason) {
        Map<String, Object> result = new HashMap<>();
        try {
            AlipayTradeRefundResponse response = alipayService.refund(
                    orderId, refundId, refundAmount, refundReason);
            result.put("code", 200);
            result.put("data", response);
            result.put("message", "退款成功");
        } catch (Exception e) {
            result.put("code", 500);
            result.put("message", e.getMessage());
        }
        return result;
    }
}

三、支付流程对比

特性 微信支付 支付宝支付
申请难度 需要认证公众号,审核严格 企业支付宝入驻,审核较快
沙箱环境 功能有限,建议小额真实测试 功能完善,有测试账号
支付方式 Native(扫码)、JSAPI、小程序、H5 电脑网站、手机网站、APP
金额单位 分(Integer) 元(String,2位小数)
通知方式 异步通知(POST JSON) 异步通知(POST Form)
签名方式 V3使用RSA,证书方式 RSA2,公钥私钥方式

四、常见问题及解决方案

4.1 微信支付常见问题

Q1: 调用接口返回"签名错误"

  • 检查API密钥是否正确

  • 检查商户证书是否过期

  • 确认请求参数的编码格式

Q2: 支付回调通知接收不到

  • 确认通知地址是公网可访问的HTTPS地址

  • 检查服务器防火墙是否放行

  • 查看微信商户平台的回调日志

Q3: 沙箱环境测试报错

  • 微信沙箱环境功能有限,建议使用真实商户号小额测试

  • 确保使用正确的沙箱API地址

4.2 支付宝支付常见问题

Q1: 调用接口返回"签名错误"

  • 检查应用私钥和支付宝公钥是否匹配

  • 确认签名方式(RSA2)与配置一致

  • 使用支付宝密钥工具重新生成密钥对

Q2: 异步通知验签失败

  • 确认使用的是支付宝公钥(非应用公钥)

  • 检查公钥是否完整(包含BEGIN和END标记)

  • 确认签名方式与配置一致

Q3: 沙箱环境无法调起支付

  • 确认使用沙箱网关地址

  • 使用沙箱提供的测试账号登录

  • 检查沙箱应用是否添加了支付能力


五、安全建议

5.1 密钥管理

/**
 * 密钥配置最佳实践
 * 
 * 1. 私钥不要硬编码在代码中
 * 2. 使用配置中心或环境变量管理密钥
 * 3. 生产环境使用密钥管理服务(KMS)
 * 4. 定期轮换密钥
 */

5.2 金额校验

/**
 * 支付回调金额校验示例
 * 
 * 必须在回调中校验金额,防止恶意篡改
 */
public boolean verifyAmount(String orderId, String notifyAmount) {
    // 从数据库查询订单金额
    Order order = orderService.getByOrderId(orderId);
    BigDecimal orderAmount = order.getTotalAmount();
    BigDecimal payAmount = new BigDecimal(notifyAmount);
    
    // 校验金额是否一致
    return orderAmount.compareTo(payAmount) == 0;
}

5.3 幂等性处理

/**
 * 支付回调幂等性处理示例
 * 
 * 防止重复通知导致重复处理
 */
public void handlePaymentSuccess(String orderId) {
    // 使用分布式锁或数据库乐观锁保证幂等
    boolean locked = redisLock.tryLock("pay:" + orderId, 10, TimeUnit.SECONDS);
    if (locked) {
        try {
            Order order = orderService.getByOrderId(orderId);
            if (order.getStatus() != OrderStatus.PAID) {
                // 更新订单状态
                orderService.updateOrderStatus(orderId, OrderStatus.PAID);
                // 其他业务处理
            }
        } finally {
            redisLock.unlock("pay:" + orderId);
        }
    }
}

六、总结

本文详细介绍了Java后端接入微信支付和支付宝支付的完整流程,包括:

  1. 商户账号申请:两个平台都需要企业资质,申请流程相对规范

  2. 沙箱环境配置:支付宝沙箱更完善,微信建议小额真实测试

  3. Java配置类编写:使用官方SDK,配置类清晰易维护

  4. 支付流程实现:创建订单、异步通知、查询退款等核心功能

在实际开发中,建议:

  • 优先使用官方SDK,避免自己封装

  • 做好异常处理和日志记录

  • 重视安全性,特别是密钥管理和金额校验

  • 充分测试后再上线


参考资料

更多推荐