Java后端接入微信支付与支付宝支付完整指南(含沙箱环境配置)
前言
在开发电商、SaaS等需要在线支付的应用时,微信支付和支付宝是最常用的两种支付方式。本文将详细介绍如何在Java后端项目中接入这两种支付方式,包括:
-
如何申请微信支付和支付宝商户账号
-
如何配置沙箱环境进行测试
-
Java后端的配置类编写
-
完整的支付流程代码示例
适用版本:微信支付 V3 API、支付宝开放平台最新API
一、微信支付接入
1.1 申请微信支付商户号
申请条件
-
已认证的微信公众号(服务号)或小程序
-
企业营业执照
-
法人身份证
-
对公银行账户
申请流程
-
登录微信公众平台
-
访问 微信公众平台
-
使用公众号管理员账号登录
-
-
进入微信支付申请页面
-
左侧菜单:微信支付 → 开通
-
-
填写申请信息
商户简称:显示在用户账单中的名称 客服电话:用户可联系的电话 经营类目:根据实际业务选择 营业执照:上传清晰的营业执照照片 法人身份证:正反面照片
-
等待审核
-
审核时间:1-5个工作日
-
审核通过后会收到邮件通知
-
-
账户验证
-
使用对公账户向腾讯指定账户打款(随机金额)
-
验证通过后即可获得商户号
-
获取关键参数
申请成功后,在商户平台(微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式)获取以下信息:
商户号(mchId):如 1600000001 API密钥(apiKey):在API安全中设置,32位字符串 APIv3密钥(apiV3Key):V3接口使用 商户证书序列号(merchantCertificateSerial):在API安全中下载证书 商户私钥(merchantPrivateKey):证书中的私钥文件
1.2 配置微信支付沙箱环境
微信支付提供了沙箱环境用于开发测试。
获取沙箱参数
-
进入:开发配置 → 沙箱环境
-
获取沙箱商户号和沙箱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-3个工作日)
-
-
配置密钥
-
使用支付宝开放平台密钥工具生成密钥对
-
在应用详情页 → 开发设置 → 接口加签方式
-
上传应用公钥,保存支付宝公钥
-
获取关键参数
应用ID(appId):在应用详情页获取 应用私钥(privateKey):本地生成的私钥 支付宝公钥(alipayPublicKey):上传公钥后获取的支付宝公钥
2.2 配置支付宝沙箱环境
支付宝提供了完整的沙箱环境用于开发测试。
获取沙箱参数
-
访问 支付宝开放平台
-
进入:开发中心 → 沙箱环境
-
获取以下信息:
沙箱应用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后端接入微信支付和支付宝支付的完整流程,包括:
-
商户账号申请:两个平台都需要企业资质,申请流程相对规范
-
沙箱环境配置:支付宝沙箱更完善,微信建议小额真实测试
-
Java配置类编写:使用官方SDK,配置类清晰易维护
-
支付流程实现:创建订单、异步通知、查询退款等核心功能
在实际开发中,建议:
-
优先使用官方SDK,避免自己封装
-
做好异常处理和日志记录
-
重视安全性,特别是密钥管理和金额校验
-
充分测试后再上线
参考资料
更多推荐

所有评论(0)