当你在电商平台点击"立即支付"时,背后发生了什么?本文将深入解析电商下单支付全流程,让你彻底明白从商品到支付的完整旅程!
一、电商支付6大核心步骤
二、详细流程解析(含关键代码)
步骤1:用户提交订单(前端)
// 购物车页面点击"结算"
function submitOrder() {
// 收集用户选择
const orderData = {
items: [
{id: "P1001", qty: 2},
{id: "P2005", qty: 1}
],
addressId: "ADDR_2023",
couponId: "COUPON_50OFF"
};
// 发送创建订单请求
axios.post('/api/orders', orderData)
.then(response => {
// 获取到支付参数后调起支付
launchPayment(response.data.paymentParams);
});
}
步骤2:商家创建订单(后端)
// 订单服务
@Service
public class OrderService {
@Transactional
public Order createOrder(CreateOrderRequest request) {
// 1. 验证库存
inventoryService.checkStock(request.getItems());
// 2. 计算价格
BigDecimal total = calculateTotal(
request.getItems(),
request.getCouponId()
);
// 3. 创建订单
Order order = new Order();
order.setOrderNo(generateOrderNo()); // 生成唯一订单号
order.setTotalAmount(total);
order.setStatus(OrderStatus.WAIT_PAY);
// 4. 保存到数据库
orderRepository.save(order);
// 5. 锁定库存
inventoryService.lockStock(request.getItems());
return order;
}
}
步骤3:调用微信支付接口(关键!)
// 支付服务
@Service
public class PaymentService {
public PaymentParams preparePayment(String orderNo) {
Order order = orderRepository.findByOrderNo(orderNo);
// 构造微信请求参数
Map<String, String> params = new HashMap<>();
params.put("appid", WX_APP_ID);
params.put("mch_id", WX_MCH_ID);
params.put("out_trade_no", orderNo);
params.put("total_fee", order.getTotalAmount().multiply(100).intValue() + "");
params.put("body", "商品订单支付");
params.put("notify_url", "https://api.yourstore.com/pay/callback");
// 生成签名
String sign = generateSignature(params, API_KEY);
params.put("sign", sign);
// 调用微信接口
String response = HttpUtil.post(
"https://api.mch.weixin.qq.com/pay/unifiedorder",
XmlUtil.mapToXml(params)
);
// 解析返回结果
Map<String, String> result = XmlUtil.xmlToMap(response);
return new PaymentParams(
result.get("prepay_id"),
result.get("nonce_str")
);
}
}
步骤4:前端调起支付
// 调起微信支付
function launchPayment(params) {
wx.chooseWXPay({
timestamp: params.timestamp,
nonceStr: params.nonceStr,
package: `prepay_id=${params.prepayId}`,
signType: 'RSA',
paySign: params.paySign,
success: () => showPaymentSuccess(),
fail: () => showPaymentFailed()
});
}
// 支付成功页面
function showPaymentSuccess() {
// 轮询订单状态
const poll = setInterval(() => {
axios.get(`/api/orders/${orderId}/status`)
.then(res => {
if(res.data.status === 'PAID') {
clearInterval(poll);
redirectToOrderDetail();
}
})
}, 2000);
}
步骤5:微信支付处理流程
步骤6:支付回调处理(最重要!)
// 支付回调控制器
@RestController
@RequestMapping("/pay")
public class PaymentCallbackController {
@PostMapping("/callback")
public String handleCallback(@RequestBody String xmlData) {
try {
// 1. 验证签名
if (!verifySignature(xmlData)) {
log.error("签名验证失败: {}", xmlData);
return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
}
// 2. 解析XML
Map<String, String> data = XmlUtil.xmlToMap(xmlData);
// 3. 处理支付结果
if ("SUCCESS".equals(data.get("result_code"))) {
String orderNo = data.get("out_trade_no");
orderService.handlePaymentSuccess(orderNo);
}
// 4. 返回成功响应
return "<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>";
} catch (Exception e) {
log.error("支付回调处理异常", e);
return "<xml><return_code><![CDATA[FAIL]]></return_code></xml>";
}
}
}
// 订单服务
public void handlePaymentSuccess(String orderNo) {
// 1. 更新订单状态
orderRepository.updateStatus(orderNo, OrderStatus.PAID);
// 2. 扣减真实库存
inventoryService.reduceStock(orderNo);
// 3. 发送支付成功通知
notificationService.sendPaymentSuccess(orderNo);
// 4. 触发后续流程
orderProcessService.startFulfillment(orderNo);
}
三、电商支付特有流程解析
1. 库存管理双阶段
2. 优惠券处理流程
public void applyCoupon(String orderNo, String couponCode) {
// 1. 验证优惠券有效性
Coupon coupon = couponService.validate(couponCode);
// 2. 计算优惠金额
BigDecimal discount = calculateDiscount(orderNo, coupon);
// 3. 更新订单金额
orderService.updateOrderAmount(orderNo, discount);
// 4. 标记优惠券已使用
couponService.markUsed(couponCode);
}
3. 超时未支付处理
// 定时任务处理超时订单
@Scheduled(fixedRate = 60000) // 每分钟执行
public void cancelUnpaidOrders() {
List<Order> orders = orderRepo.findByStatusAndCreateTimeBefore(
OrderStatus.WAIT_PAY,
LocalDateTime.now().minusMinutes(30) // 30分钟未支付
);
for (Order order : orders) {
orderService.cancelOrder(order.getOrderNo(), "超时未支付");
inventoryService.unlockStock(order.getOrderNo());
}
}
四、支付安全防护措施
1. 防重复支付
@Transactional
public void processPayment(String orderNo, BigDecimal amount) {
Order order = orderRepo.findByOrderNo(orderNo);
// 检查订单状态
if (order.getStatus() != OrderStatus.WAIT_PAY) {
// 已支付订单处理退款
if (order.getStatus() == OrderStatus.PAID) {
refundService.createRefund(orderNo, amount);
}
throw new BusinessException("订单状态异常");
}
// 正常支付处理
// ...
}
2. 防羊毛党
public void checkRisk(String userId, BigDecimal amount) {
// 规则1:同一用户短时间多次下单
int orderCount = orderRepo.countByUserAndTimeRange(userId, Duration.ofMinutes(10));
if (orderCount > 5) {
throw new RiskControlException("操作过于频繁");
}
// 规则2:异常金额订单
if (amount.compareTo(BigDecimal.valueOf(100000)) > 0) {
riskService.verifyLargeOrder(userId);
}
}
3. 网络攻击防护
// 支付回调验证
private boolean verifySignature(String xmlData) {
// 1. 验证IP白名单
if (!IP_WHITELIST.contains(request.getRemoteAddr())) {
return false;
}
// 2. 验证时间戳
String timestamp = getXmlField(xmlData, "timestamp");
if (System.currentTimeMillis() - Long.parseLong(timestamp) > 300000) {
return false; // 超过5分钟视为无效
}
// 3. 验证签名
return signatureValidator.validate(xmlData);
}
五、电商支付最佳实践
1. 支付流程优化技巧
- 并行处理:库存锁定、优惠计算、订单创建并行执行
- 本地缓存:商品价格等不变数据使用缓存
- 预生成ID:提前生成订单号减少DB压力
2. 支付结果保障机制
3. 对账流程设计
// 每日对账任务
@Scheduled(cron = "0 0 3 * * ?") // 每天凌晨3点
public void dailyReconciliation() {
// 1. 获取微信账单
List<WxPaymentRecord> wxRecords = wxPayApi.downloadBill(LocalDate.now().minusDays(1));
// 2. 获取本地订单
List<Order> localOrders = orderRepo.findByDate(LocalDate.now().minusDays(1));
// 3. 对比差异
ReconciliationResult result = reconcile(wxRecords, localOrders);
// 4. 处理差异
handleDiscrepancies(result);
// 5. 发送报告
reportService.sendReconciliationReport(result);
}
六、常见问题解决方案
问题1:用户支付后订单状态未更新
解决方案:
- 检查回调地址是否可访问
- 验证签名逻辑是否正确
- 查看支付日志是否收到回调
- 通过微信接口主动查询订单状态
问题2:库存超卖
防护方案:
UPDATE inventory
SET stock = stock - 1
WHERE product_id = 'P1001' AND stock >= 1
问题3:支付掉单
处理流程:
七、微信支付调试技巧
1. 沙箱环境使用
// 启用沙箱环境
@Configuration
public class WxPayConfig {
@Bean
public WxPayService wxPayService() {
WxPayConfig config = new WxPayConfig();
config.setUseSandboxEnv(true); // 开启沙箱
return new WxPayServiceImpl(config);
}
}
2. 日志记录要点
// 记录完整支付流程
@Aspect
@Component
public class PaymentLogAspect {
@Around("execution(* com.yourapp.service.PaymentService.*(..))")
public Object logPayment(ProceedingJoinPoint joinPoint) throws Throwable {
// 记录请求参数
log.info("Payment Request: {}", Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
// 记录返回结果
log.info("Payment Response: {}", result);
return result;
}
}
3. 常用调试工具
工具名称 | 用途 | 链接 |
---|---|---|
微信支付调试器 | 模拟支付流程 | 下载 |
Ngrok | 本地回调穿透 | 官网 |
Postman | API调试 | 官网 |
开发口诀:
一验签名,二查状态,三看日志,四对账单
本地调试用沙箱,生产环境双验证
通过本文,你已掌握电商平台下单支付的完整流程。记住三大关键点:订单创建要幂等,支付回调需验证,状态管理要严谨。实际开发中,建议结合微信支付官方文档进行实现。