引言:为什么支付系统是Java开发者的“硬技能”?
做Java开发多年,发现“支付系统开发”是区分“普通CRUD程序员”和“企业级开发工程师”的关键—— 新手一听到“支付对接”就怕:微信支付v3的签名验签、支付宝的异步回调、支付订单的幂等性、跨系统的分布式事务、资金对账……每一个点都容易踩坑,要么对接失败,要么出现“重复支付”“订单状态不一致”的致命问题。
这套教程完全对标企业真实支付场景,从 0到1搭建一套可落地的支付系统 :整合微信支付v3、支付宝支付两大主流渠道,覆盖“下单→生成支付链接→用户支付→支付回调→订单更新→退款→对账”全核心流程,每一行代码都讲清楚“为什么这么写”“怎么避坑”,哪怕你只懂SpringBoot基础,也能跟着做出一套符合企业标准的支付系统。
项目定位 :通用型企业级支付系统,支持微信H5支付、支付宝电脑网站支付,涵盖「订单管理、支付对接、回调处理、退款管理、简单对账」五大核心模块,完全符合支付行业开发规范。
一、 先搞懂:企业级支付系统的核心逻辑(避坑基石) 做支付系统前,必须先理清“钱怎么流转”,这是所有代码的核心: 1.1 支付核心流程(企业真实场景) 是否用户下单生成支付订单(本地)调用微信/支付宝统一下单接口生成支付链接/二维码用户扫码/点击支付支付成功?
支付平台异步回调我方接口验签通过后更新本地订单状态异步对账(可选)订单超时关闭 1.2 核心痛点&技术落地方向(新手必看) 核心痛点企业级解决方案技术落地方向 签名/验签失败严格按支付平台规范实现签名,统一工具类微信v3使用SHA256-RSA,支付宝使用RSA2回调重复通知幂等性处理(订单号+回调流水号唯一标识)Redis分布式锁+数据库唯一索引订单状态不一致分布式事务+状态机控制本地消息表+RabbitMQ异步确认支付超时定时任务关闭超时未支付订单MyBatis-Plus+定时线程池(或XXL-Job)资金对账差异每日拉取支付平台账单,对比本地订单微信/支付宝账单接口+Excel/数据库对账 1.3 技术栈选型(企业级稳定优先,新手友好) 技术领域选型方案选型理由(实战+新手友好) 核心框架SpringBoot 2.7.122.x稳定版,文档最全,避坑成本最低ORM框架MyBatis-Plus 3.5.3.1简化CRUD,分页、条件查询开箱即用数据库MySQL 8.0主流关系型数据库,事务支持完善缓存/分布式锁Redis 6.2缓存支付订单、实现幂等性、分布式锁消息队列RabbitMQ 3.9.x异步处理回调、对账,解耦核心流程微信支付微信支付v3 SDK官方最新版,安全性更高(推荐企业使用)支付宝支付支付宝开放平台SDK 4.38企业主流版本,文档详细,新手易对接加密/签名BCrypt、RSA2密码加密+支付签名,符合支付安全规范构建工具Maven 3.8.xIDEA集成度高,一键导入依赖辅助工具Lombok、HuTool简化代码+通用工具(日期、加密、HTTP) 二、 0到1环境搭建(新手一次成功,避坑版) 2.1 支付平台开发者账号准备(关键!
必须先做) (1)微信支付开发者账号(新手用测试号) 注册地址:https://pay.weixin.qq.com/ 新手推荐:申请“微信支付测试号”(https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login),无需企业资质,直接获取appid、商户号、API密钥; 记录关键信息:
appid
、
mch_id
(商户号)、
api_v3_key
(v3密钥)、商户私钥/公钥(用于签名)。
(2)支付宝开发者账号(新手用沙箱环境) 注册地址:https://open.alipay.com/ 进入“开发者中心→沙箱环境”,获取:
app_id
、支付宝公钥、应用私钥(自己生成); 沙箱账号:可获取测试用的买家账号/密码,用于支付测试。
2.2 本地环境搭建(新手复制粘贴就行) (1)SpringBoot项目初始化(IDEA可视化操作) 打开IDEA→New→Project→Spring Initializr→Next; 填写信息: Group:
com.xxx
(新手填
com.pay
); Artifact:
payment-system
; Java:11; Spring Boot:2.7.12; 勾选核心依赖: Web→Spring Web; SQL→MySQL Driver、MyBatis Framework; NoSQL→Spring Data Redis; Messaging→Spring AMQP(RabbitMQ); Developer Tools→Lombok、Spring Boot DevTools; Finish,等待依赖加载完成。
(2)补充支付SDK依赖(pom.xml)
com.github.wechatpay-apiv3
wechatpay-java
0.2.10
com.alipay.sdk
alipay-sdk-java
4.38.0.ALL
cn.hutool
hutool-all
5.8.20
com.alibaba
fastjson
2.0.32
点击IDEA的Maven刷新按钮,等待依赖下载完成。
(3)核心配置(application.yml)
server:
port: 8080
servlet:
context-path: /pay
spring:
# 数据库配置(新手改密码)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/payment_system?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
# Redis配置
redis:
host: localhost
port: 6379
password: 123456
database: 0
# RabbitMQ配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath:mapper/**/*.xml
type-aliases-package: com.pay.entity
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 支付平台配置(新手替换为自己的测试号信息)
pay:
# 微信支付v3配置
wechat:
appid: wx1234567890abcdef # 测试号appid
mch_id: 1900009999 # 测试号商户号
api_v3_key: abcdef1234567890abcdef1234567890 # v3密钥
private_key_path: classpath:cert/wechat_private_key.pem # 商户私钥路径
notify_url: http://你的外网域名/pay/wechat/notify # 回调地址(必须外网可访问)
# 支付宝配置
alipay:
app_id: 2021000000000000 # 沙箱appid
private_key: 你的应用私钥 # 自己生成的应用私钥
public_key: 支付宝公钥 # 沙箱环境的支付宝公钥
gateway_url: https://openapi.alipaydev.com/gateway.do # 沙箱网关
notify_url: http://你的外网域名/pay/alipay/notify # 回调地址
(4)创建数据库(新手复制建表语句) 在MySQL中创建
payment_system
数据库,执行以下建表语句(核心表):
-- 支付订单表(核心)
CREATE TABLE pay_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '订单ID',
order_no VARCHAR(50) NOT NULL UNIQUE COMMENT '本地订单号',
out_trade_no VARCHAR(50) COMMENT '支付平台订单号',
pay_type TINYINT NOT NULL COMMENT '支付类型:1-微信 2-支付宝',
total_amount DECIMAL(10,2) NOT NULL COMMENT '支付金额(元)',
subject VARCHAR(100) NOT NULL COMMENT '订单标题',
status TINYINT NOT NULL DEFAULT 0 COMMENT '订单状态:0-待支付 1-支付成功 2-已取消 3-退款中 4-已退款',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
pay_time DATETIME COMMENT '支付时间',
notify_time DATETIME COMMENT '回调时间',
notify_id VARCHAR(100) COMMENT '回调ID(幂等性)'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='支付订单表';
-- 退款订单表
CREATE TABLE refund_order (
id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '退款ID',
order_no VARCHAR(50) NOT NULL COMMENT '关联本地订单号',
refund_no VARCHAR(50) NOT NULL UNIQUE COMMENT '退款单号',
refund_amount DECIMAL(10,2) NOT NULL COMMENT '退款金额',
refund_status TINYINT DEFAULT 0 COMMENT '退款状态:0-退款中 1-退款成功 2-退款失败',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
FOREIGN KEY (order_no) REFERENCES pay_order(order_no)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='退款订单表';
三、 核心代码实现(0到1,手把手敲) 先梳理项目结构(新手严格按这个建包):
com.pay
├── entity // 实体类(订单、退款)
├── mapper // Mapper层(MyBatis-Plus)
├── service // 服务层(核心业务)
│ └── impl // 服务实现类
├── controller // 控制器(下单、回调、退款)
├── config // 配置类(微信/支付宝、Redis、RabbitMQ)
├── util // 工具类(签名、订单号生成)
└── PaymentApplication.java // 启动类
3.1 通用工具类(支付核心:订单号生成+签名) (1)订单号生成工具(PayOrderNoUtil.java)
package com.pay.util;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
/**
* 支付订单号生成工具(企业级规范:日期+随机数+支付类型)
*/
public class PayOrderNoUtil {
/**
* 生成本地订单号
* @param payType 1-微信 2-支付宝
* @return 订单号(如:PAY202601061620301234561)
*/
public static String generateOrderNo(Integer payType) {
// 日期:yyyyMMddHHmmss
String dateStr = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
// 6位随机数
String randomStr = RandomUtil.randomNumbers(6);
// 拼接:PAY+日期+随机数+支付类型
return "PAY" + dateStr + randomStr + payType;
}
/**
* 生成退款单号
* @return 退款单号(如:REFUND20260106162030123456)
*/
public static String generateRefundNo() {
String dateStr = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
String randomStr = RandomUtil.randomNumbers(6);
return "REFUND" + dateStr + randomStr;
}
}
(2)微信支付v3签名工具(新手不用懂原理,直接用)
package com.pay.util;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Signer;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.security.PrivateKey;
/**
* 微信支付v3签名工具
*/
@Component
public class WeChatPaySignUtil {
@Value("${pay.wechat.mch_id}")
private String mchId;
@Value("${pay.wechat.private_key_path}")
private Resource privateKeyResource;
// 获取商户私钥
public PrivateKey getPrivateKey() throws IOException {
return PemUtil.loadPrivateKey(privateKeyResource.getInputStream());
}
// 获取签名器
public Signer getSigner() throws IOException {
return new PrivateKeySigner(mchId, getPrivateKey());
}
}
3.2 支付配置类(对接微信/支付宝) (1)微信支付配置(WeChatPayConfig.java)
package com.pay.config;
import com.pay.util.WeChatPaySignUtil;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
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.security.PrivateKey;
/**
* 微信支付v3配置(新手直接用,不用改)
*/
@Configuration
public class WeChatPayConfig {
@Value("${pay.wechat.appid}")
private String appid;
@Value("${pay.wechat.mch_id}")
private String mchId;
@Value("${pay.wechat.api_v3_key}")
private String apiV3Key;
@Bean
public AutoUpdateCertificatesVerifier verifier(WeChatPaySignUtil signUtil) throws IOException {
PrivateKey privateKey = signUtil.getPrivateKey();
return new AutoUpdateCertificatesVerifier(
new com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeyCredential(mchId, privateKey),
apiV3Key.getBytes());
}
@Bean
public org.apache.http.client.HttpClient weChatPayClient(AutoUpdateCertificatesVerifier verifier, WeChatPaySignUtil signUtil) throws IOException {
WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
.withMerchant(mchId, appid, signUtil.getPrivateKey())
.withValidator(new WechatPay2Validator(verifier));
return builder.build();
}
}
(2)支付宝支付配置(AlipayConfig.java)
package com.pay.config;
import com.alipay.api.AlipayClient;
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("${pay.alipay.app_id}")
private String appId;
@Value("${pay.alipay.private_key}")
private String privateKey;
@Value("${pay.alipay.public_key}")
private String publicKey;
@Value("${pay.alipay.gateway_url}")
private String gatewayUrl;
@Bean
public AlipayClient alipayClient() {
// 创建支付宝客户端(核心)
return new DefaultAlipayClient(
gatewayUrl,
appId,
privateKey,
"json",
"UTF-8",
publicKey,
"RSA2" // 加密方式:RSA2(企业标准)
);
}
}
3.3 核心业务:创建支付订单+生成支付链接 (1)支付订单实体类(PayOrder.java)
package com.pay.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Data
@TableName("pay_order")
public class PayOrder {
@TableId(type = IdType.AUTO)
private Long id;
private String orderNo; // 本地订单号
private String outTradeNo; // 支付平台订单号
private Integer payType; // 1-微信 2-支付宝
private BigDecimal totalAmount;// 支付金额
private String subject; // 订单标题
private Integer status; // 0-待支付 1-支付成功
private Date createTime;
private Date payTime;
private Date notifyTime;
private String notifyId; // 回调ID(幂等性)
}
(2)支付订单Service接口(PayOrderService.java)
package com.pay.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.pay.entity.PayOrder;
/**
* 支付订单服务接口
*/
public interface PayOrderService extends IService {
/**
* 创建支付订单并生成支付链接
* @param payType 1-微信 2-支付宝
* @param totalAmount 支付金额
* @param subject 订单标题
* @return 支付链接
*/
String createPayOrder(Integer payType, BigDecimal totalAmount, String subject);
}
(3)支付订单Service实现类(核心!
)
package com.pay.service.impl;
import com.alipay.api.AlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pay.entity.PayOrder;
import com.pay.mapper.PayOrderMapper;
import com.pay.service.PayOrderService;
import com.pay.util.PayOrderNoUtil;
import com.wechat.pay.contrib.apache.httpclient.util.JsonUtil;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.h5.H5Service;
import com.wechat.pay.java.service.payments.h5.model.CreateOrderRequest;
import com.wechat.pay.java.service.payments.h5.model.H5CreateOrderResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
/**
* 支付订单服务实现(整合微信+支付宝)
*/
@Service
public class PayOrderServiceImpl extends ServiceImpl implements PayOrderService {
@Value("${pay.wechat.appid}")
private String weChatAppId;
@Value("${pay.wechat.mch_id}")
private String weChatMchId;
@Value("${pay.wechat.api_v3_key}")
private String weChatApiV3Key;
@Value("${pay.wechat.notify_url}")
private String weChatNotifyUrl;
@Value("${pay.alipay.notify_url}")
private String alipayNotifyUrl;
@Resource
private AlipayClient alipayClient;
// 微信H5支付服务
private H5Service getWeChatH5Service() {
RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(weChatMchId)
.privateKeyFromPath("classpath:cert/wechat_private_key.pem")
.merchantSerialNumber("你的商户证书序列号")
.apiV3Key(weChatApiV3Key)
.build();
return new H5Service.Builder().config(config).build();
}
@Override
public String createPayOrder(Integer payType, BigDecimal totalAmount, String subject) {
// 1. 生成本地订单号
String orderNo = PayOrderNoUtil.generateOrderNo(payType);
// 2. 保存本地订单(待支付状态)
PayOrder payOrder = new PayOrder();
payOrder.setOrderNo(orderNo);
payOrder.setPayType(payType);
payOrder.setTotalAmount(totalAmount);
payOrder.setSubject(subject);
payOrder.setStatus(0); // 待支付
this.save(payOrder);
// 3. 根据支付类型生成支付链接
if (payType == 1) {
// 微信H5支付
return createWeChatH5Order(orderNo, totalAmount, subject);
} else if (payType == 2) {
// 支付宝电脑网站支付
return createAlipayPageOrder(orderNo, totalAmount, subject);
} else {
throw new RuntimeException("不支持的支付类型");
}
}
// 生成微信H5支付链接
private String createWeChatH5Order(String orderNo, BigDecimal totalAmount, String subject) {
CreateOrderRequest request = new CreateOrderRequest();
request.setAppid(weChatAppId);
request.setMchid(weChatMchId);
request.setDescription(subject);
request.setOutTradeNo(orderNo);
// 金额:分(微信支付单位是分,需转换)
request.setAmount(new CreateOrderRequest.Amount().setTotal(totalAmount.multiply(new BigDecimal(100)).intValue()));
request.setNotifyUrl(weChatNotifyUrl);
// 场景信息(H5支付必填)
CreateOrderRequest.SceneInfo sceneInfo = new CreateOrderRequest.SceneInfo();
sceneInfo.setPayerClientIp("127.0.0.1"); // 客户端IP
sceneInfo.setH5Info(new CreateOrderRequest.H5Info().setType("WAP"));
request.setSceneInfo(sceneInfo);
// 调用微信统一下单接口
H5Service h5Service = getWeChatH5Service();
H5CreateOrderResponse response = h5Service.createOrder(request);
// 返回支付链接
return response.getH5Url();
}
// 生成支付宝电脑网站支付链接
private String createAlipayPageOrder(String orderNo, BigDecimal totalAmount, String subject) {
try {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
// 同步回调(可选)
request.setReturnUrl("http://你的域名/pay/alipay/return");
// 异步回调
request.setNotifyUrl(alipayNotifyUrl);
// 业务参数
String bizContent = "{" +
"\"out_trade_no\":\"" + orderNo + "\"," +
"\"total_amount\":\"" + totalAmount + "\"," +
"\"subject\":\"" + subject + "\"," +
"\"product_code\":\"FAST_INSTANT_TRADE_PAY\"" +
"}";
request.setBizContent(bizContent);
// 生成支付链接(支付宝会返回HTML表单,直接跳转)
return alipayClient.pageExecute(request).getBody();
} catch (Exception e) {
throw new RuntimeException("支付宝下单失败:" + e.getMessage());
}
}
}
3.4 控制器层(下单接口+回调接口) (1)支付订单控制器(PayOrderController.java)
package com.pay.controller;
import com.pay.service.PayOrderService;
import com.pay.util.Result;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
/**
* 支付订单控制器(新手直接访问即可生成支付链接)
*/
@RestController
public class PayOrderController {
@Resource
private PayOrderService payOrderService;
/**
* 创建支付订单
* @param payType 1-微信 2-支付宝
* @param amount 支付金额(元)
* @param subject 订单标题
* @return 支付链接
*/
@GetMapping("/create")
public Result createPayOrder(
@RequestParam Integer payType,
@RequestParam BigDecimal amount,
@RequestParam String subject) {
String payUrl = payOrderService.createPayOrder(payType, amount, subject);
return Result.success(payUrl);
}
}
(2)微信支付回调控制器(核心!
幂等性处理)
package com.pay.controller;
import cn.hutool.core.util.StrUtil;
import com.pay.entity.PayOrder;
import com.pay.service.PayOrderService;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import com.wechat.pay.contrib.apache.httpclient.util.JsonUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 微信支付回调控制器(企业级幂等性处理)
*/
@RestController
public class WeChatPayNotifyController {
@Value("${pay.wechat.api_v3_key}")
private String apiV3Key;
@Resource
private PayOrderService payOrderService;
@Resource
private RedisTemplate redisTemplate;
// 回调成功返回(微信要求必须返回这个JSON)
private static final String SUCCESS_RESPONSE = "{\"code\":\"SUCCESS\",\"message\":\"成功\"}";
private static final String FAIL_RESPONSE = "{\"code\":\"FAIL\",\"message\":\"失败\"}";
@PostMapping("/wechat/notify")
public String weChatNotify(
@RequestHeader("Wechatpay-Serial") String serial,
@RequestHeader("Wechatpay-Signature") String signature,
@RequestHeader("Wechatpay-Timestamp") String timestamp,
@RequestHeader("Wechatpay-Nonce") String nonce,
@RequestBody String body) {
try {
// 1. 解密回调数据(微信v3回调数据是加密的)
AesUtil aesUtil = new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8));
Map notifyMap = JsonUtil.decodeJson(body, Map.class);
String ciphertext = (String) notifyMap.get("resource");
Map resourceMap = JsonUtil.decodeJson(ciphertext, Map.class);
String plainText = aesUtil.decryptToString(
(String) resourceMap.get("associated_data"),
(String) resourceMap.get("nonce"),
(String) resourceMap.get("ciphertext"));
Map resultMap = JsonUtil.decodeJson(plainText, Map.class);
// 2. 提取关键参数
String outTradeNo = (String) resultMap.get("out_trade_no"); // 本地订单号
String transactionId = (String) resultMap.get("transaction_id"); // 微信订单号
String notifyId = serial + "_" + nonce; // 回调唯一标识(幂等性)
// 3. 幂等性处理:Redis分布式锁+唯一标识
String lockKey = "pay:notify:wechat:" + outTradeNo;
Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, notifyId, 5, TimeUnit.MINUTES);
if (!lock) {
return SUCCESS_RESPONSE; // 重复回调,直接返回成功
}
// 4. 更新订单状态
PayOrder payOrder = payOrderService.getOne(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.lambda()
.eq(PayOrder::getOrderNo, outTradeNo));
if (payOrder == null) {
return FAIL_RESPONSE;
}
if (payOrder.getStatus() == 1) {
return SUCCESS_RESPONSE; // 已支付,无需处理
}
// 更新为支付成功
payOrder.setStatus(1);
payOrder.setOutTradeNo(transactionId);
payOrder.setPayTime(new java.util.Date());
payOrder.setNotifyTime(new java.util.Date());
payOrder.setNotifyId(notifyId);
payOrderService.updateById(payOrder);
return SUCCESS_RESPONSE;
} catch (Exception e) {
e.printStackTrace();
return FAIL_RESPONSE;
}
}
}
四、 实战踩坑复盘(企业级避坑指南) 坑1:微信支付v3签名失败 原因:商户私钥格式错误、商户证书序列号填错、api_v3_key长度不足32位; 解决:私钥用PEM格式,api_v3_key必须是32位字符串,证书序列号在微信支付商户平台查看。
坑2:支付宝回调验签失败 原因:应用私钥/支付宝公钥不匹配、回调参数被修改、编码不是UTF-8; 解决:重新生成RSA2密钥对,确保回调接口返回的参数未被篡改,统一用UTF-8编码。
坑3:回调重复通知导致订单重复更新 原因:支付平台会多次发送回调(网络问题); 解决:用“订单号+回调唯一标识”做幂等性,Redis锁+数据库唯一索引双重保障。
坑4:微信支付金额单位错误 原因:微信支付金额单位是“分”,直接传了“元”(比如1元传1,实际要传100); 解决:下单时将金额×100转换为分,回调时÷100转换回元。
五、 部署上线(企业级注意事项) 域名备案 :支付回调地址必须是备案的外网域名,不能用IP; HTTPS配置 :微信/支付宝回调要求接口是HTTPS,需配置SSL证书; 日志记录 :所有支付流程必须记录详细日志(下单、支付、回调、退款),便于对账和排查问题; 资金对账 :每日定时拉取微信/支付宝账单,对比本地订单,确保资金一致; 异常监控 :对接告警工具(钉钉/邮件),支付失败、回调失败时及时通知。
总结 关键点回顾 核心收获 :从0到1搭建了企业级支付系统,整合微信v3、支付宝两大支付渠道,掌握支付系统的核心流程(下单→支付→回调→更新订单); 实战重点 :学会了签名验签、幂等性处理、回调解密等支付开发核心技术,代码可直接落地到企业项目; 避坑要点 :解决了签名失败、重复回调、金额单位错误等支付开发高频问题。
这套支付系统的核心是“企业级落地”——没有花里胡哨的技术,聚焦支付的核心业务逻辑,每一步都符合行业规范,新手跟着做,既能掌握支付开发的核心技能,又能做出一套可上线的支付系统,这也是企业Java开发的核心竞争力之一。
