feat(payment): 添加PayPal支付配置和订单状态更新功能
- 配置PayPal沙箱环境的Client ID和密钥 - 新增updatePaymentStatus方法用于更新订单支付状态 - 新增updateOrderStatus方法用于更新订单状态 - 实现支付状态更新时同步更新订单状态逻辑 - 添加详细的日志记录和异常处理机制 - 集成MyBatis Plus查询更新订单数据
This commit is contained in:
46
PAYPAL_TEST_ACCOUNT.md
Normal file
46
PAYPAL_TEST_ACCOUNT.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# PayPal沙箱测试账号说明
|
||||
|
||||
## API凭证(已配置到 application-dev.yml)
|
||||
|
||||
- **Client ID(API密钥)**: `AdGYUZpvLuHR30dybOApvM-RNB1pVKtd74SVfh-6TK52xV-1JEBddHVMCWuDdyyHri4DXd4kABBi7Icb`
|
||||
- **Client Secret(密钥)**: `ENblspyRmwsOU_PWFurlhEYUF5Da6aYKl0pjK4ehm7p3R5aSqvbpaF_YsIIs8v0ty1c9WJu15XP-Fe_1`
|
||||
|
||||
这些凭证已自动配置到 `application-dev.yml` 中,无需手动配置。
|
||||
|
||||
## 沙箱测试账号(用于测试支付流程)
|
||||
|
||||
### 商家账号(Business Account)
|
||||
- **用户名**: `sb-vtcmz48304367@business.example.com`
|
||||
- **密码**: `iN)7:z#4`
|
||||
|
||||
### 买家账号(Personal Account)
|
||||
PayPal沙箱环境通常会提供多个测试账号,你可以在PayPal开发者控制台查看。
|
||||
|
||||
## 测试流程
|
||||
|
||||
1. **启动应用**:确保使用 `dev` 环境配置启动
|
||||
2. **创建订单**:在系统中创建测试订单
|
||||
3. **点击支付**:在订单确认页面点击"立即支付"
|
||||
4. **PayPal登录**:使用上述商家账号或买家账号登录PayPal沙箱
|
||||
5. **完成支付**:在PayPal页面完成支付流程
|
||||
6. **查看结果**:支付完成后会自动跳转回系统,查看订单状态
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **环境切换**:当前配置为沙箱环境(`mode: sandbox`)
|
||||
2. **生产环境**:上线前需要:
|
||||
- 在PayPal开发者控制台获取生产环境的Client ID和Secret
|
||||
- 修改 `application-prod.yml` 中的配置
|
||||
- 将 `mode` 改为 `production`
|
||||
3. **测试账号**:沙箱测试账号仅用于开发测试,不能用于真实交易
|
||||
|
||||
## PayPal沙箱控制台
|
||||
|
||||
访问地址:https://developer.paypal.com/dashboard/
|
||||
|
||||
在这里可以:
|
||||
- 查看API凭证
|
||||
- 管理测试账号
|
||||
- 查看交易记录
|
||||
- 配置Webhook等
|
||||
|
||||
170
PAYPAL_WEBHOOK_GUIDE.md
Normal file
170
PAYPAL_WEBHOOK_GUIDE.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# PayPal Webhook处理指南
|
||||
|
||||
## 概述
|
||||
|
||||
PayPal Webhook是PayPal向你的服务器发送事件通知的机制。当支付状态发生变化时,PayPal会主动推送事件到你的Webhook URL。
|
||||
|
||||
## Webhook URL配置
|
||||
|
||||
在你的PayPal开发者控制台中配置Webhook URL:
|
||||
- **开发环境**: `https://你的域名/api/paypal/webhook`
|
||||
- **生产环境**: `https://你的生产域名/api/paypal/webhook`
|
||||
|
||||
## 支持的事件类型
|
||||
|
||||
系统已实现以下事件类型的处理:
|
||||
|
||||
### 1. PAYMENT.CAPTURE.COMPLETED
|
||||
**触发时机**: 当PayPal成功捕获资金时触发
|
||||
|
||||
**处理逻辑**:
|
||||
- 提取capture信息(金额、货币、状态等)
|
||||
- 通过`supplementary_data.related_ids.order_id`或`custom_id`获取ERP订单号
|
||||
- 更新ERP订单状态为"已支付"(PAID)
|
||||
|
||||
### 2. PAYMENT.CAPTURE.DENIED
|
||||
**触发时机**: 当支付捕获被拒绝时触发
|
||||
|
||||
**处理逻辑**:
|
||||
- 记录支付失败信息
|
||||
- 更新ERP订单状态为"支付失败"
|
||||
|
||||
### 3. PAYMENT.CAPTURE.REFUNDED
|
||||
**触发时机**: 当支付被退款时触发
|
||||
|
||||
**处理逻辑**:
|
||||
- 记录退款信息
|
||||
- 更新ERP订单状态为"已退款"
|
||||
|
||||
### 4. CHECKOUT.ORDER.APPROVED
|
||||
**触发时机**: 当订单被顾客批准时触发
|
||||
|
||||
**处理逻辑**:
|
||||
- 记录订单批准信息
|
||||
- 可以触发自动捕获逻辑(如果需要)
|
||||
|
||||
### 5. CHECKOUT.ORDER.COMPLETED
|
||||
**触发时机**: 当订单完成时触发
|
||||
|
||||
**处理逻辑**:
|
||||
- 记录订单完成信息
|
||||
- 根据业务需求处理订单完成逻辑
|
||||
|
||||
### 6. CHECKOUT.ORDER.CANCELLED
|
||||
**触发时机**: 当订单被取消时触发
|
||||
|
||||
**处理逻辑**:
|
||||
- 记录订单取消信息
|
||||
- 更新ERP订单状态为"已取消"
|
||||
|
||||
## 签名验证
|
||||
|
||||
### 开发环境(沙箱)
|
||||
- 签名验证可以暂时跳过(已实现自动跳过)
|
||||
- 但建议在测试时也启用验证,确保代码正确
|
||||
|
||||
### 生产环境
|
||||
- **必须启用签名验证**
|
||||
- 需要在配置文件中设置`webhook-id`
|
||||
- 系统会自动验证每个Webhook请求的签名
|
||||
|
||||
## 配置Webhook ID
|
||||
|
||||
### 1. 获取Webhook ID
|
||||
|
||||
1. 登录PayPal开发者控制台:https://developer.paypal.com/dashboard/
|
||||
2. 进入你的应用设置
|
||||
3. 找到Webhook配置部分
|
||||
4. 创建或查看Webhook,获取Webhook ID
|
||||
|
||||
### 2. 配置到系统
|
||||
|
||||
在`application-dev.yml`或`application-prod.yml`中添加:
|
||||
|
||||
```yaml
|
||||
paypal:
|
||||
webhook-id: YOUR_WEBHOOK_ID
|
||||
```
|
||||
|
||||
## 订单号关联
|
||||
|
||||
PayPal Webhook事件中需要关联ERP订单号,系统会按以下顺序尝试获取:
|
||||
|
||||
1. **从`supplementary_data.related_ids.order_id`获取**
|
||||
- 这是PayPal自动填充的,包含原始订单ID
|
||||
- 然后通过查询PayPal订单详情获取`reference_id`(即ERP订单号)
|
||||
|
||||
2. **从`custom_id`获取**
|
||||
- 如果创建PayPal订单时设置了`custom_id`,可以直接获取
|
||||
|
||||
3. **通过`order_id`查询订单详情**
|
||||
- 如果上述方法都无法获取,会通过PayPal API查询订单详情
|
||||
- 从订单的`purchase_units[0].reference_id`获取ERP订单号
|
||||
|
||||
## 测试Webhook
|
||||
|
||||
### 使用PayPal Webhook模拟器
|
||||
|
||||
1. 登录PayPal开发者控制台
|
||||
2. 进入Webhook配置页面
|
||||
3. 使用"Send test webhook"功能
|
||||
4. 选择要测试的事件类型
|
||||
5. 系统会收到测试事件并处理
|
||||
|
||||
### 本地测试
|
||||
|
||||
如果需要在本地测试,可以使用以下工具:
|
||||
|
||||
1. **ngrok**(推荐):
|
||||
```bash
|
||||
ngrok http 8082
|
||||
```
|
||||
然后将ngrok提供的URL配置到PayPal Webhook URL
|
||||
|
||||
2. **PayPal Webhook模拟器**:
|
||||
使用Postman等工具模拟Webhook请求
|
||||
|
||||
## 日志查看
|
||||
|
||||
Webhook处理的所有步骤都会记录日志,包括:
|
||||
- 接收到的Webhook事件
|
||||
- 签名验证结果
|
||||
- 事件处理过程
|
||||
- 订单状态更新结果
|
||||
|
||||
查看日志位置:
|
||||
- 应用日志文件
|
||||
- 控制台输出
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 1. Webhook未收到事件
|
||||
- 检查Webhook URL是否正确配置
|
||||
- 确认服务器可以访问(防火墙、网络等)
|
||||
- 检查PayPal控制台中的Webhook状态
|
||||
|
||||
### 2. 签名验证失败
|
||||
- 确认Webhook ID配置正确
|
||||
- 检查请求头是否完整
|
||||
- 确认使用的是正确的环境(沙箱/生产)
|
||||
|
||||
### 3. 无法关联ERP订单
|
||||
- 确认创建PayPal订单时设置了`reference_id`
|
||||
- 检查订单号格式是否正确
|
||||
- 查看日志中的详细信息
|
||||
|
||||
## 安全建议
|
||||
|
||||
1. **生产环境必须启用签名验证**
|
||||
2. **使用HTTPS**(PayPal要求)
|
||||
3. **记录所有Webhook事件**(用于审计和调试)
|
||||
4. **实现幂等性处理**(防止重复处理同一事件)
|
||||
5. **设置超时和重试机制**
|
||||
|
||||
## 代码位置
|
||||
|
||||
- **Webhook控制器**: `PayPalController.webhook()`
|
||||
- **Webhook服务**: `PayPalWebhookService` 和 `PayPalWebhookServiceImpl`
|
||||
- **事件DTO**: `PayPalWebhookEventDTO`
|
||||
- **配置**: `PayPalProperties.webhookId`
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
package com.mtkj.mtpay.dto.request;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 创建PayPal订单请求DTO
|
||||
*/
|
||||
@Data
|
||||
public class CreatePayPalOrderRequestDTO implements Serializable {
|
||||
|
||||
/**
|
||||
* 订单意图:CAPTURE(立即捕获)或 AUTHORIZE(预授权)
|
||||
*/
|
||||
@NotBlank(message = "订单意图不能为空")
|
||||
private String intent = "CAPTURE";
|
||||
|
||||
/**
|
||||
* ERP订单号(用于关联PayPal订单和ERP订单)
|
||||
*/
|
||||
@NotBlank(message = "ERP订单号不能为空")
|
||||
private String referenceId;
|
||||
|
||||
/**
|
||||
* 订单金额
|
||||
*/
|
||||
@NotNull(message = "订单金额不能为空")
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 货币代码(如USD, CNY等)
|
||||
*/
|
||||
@NotBlank(message = "货币代码不能为空")
|
||||
private String currencyCode;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String itemName;
|
||||
|
||||
/**
|
||||
* 商品描述
|
||||
*/
|
||||
private String itemDescription;
|
||||
|
||||
/**
|
||||
* 商品SKU
|
||||
*/
|
||||
private String itemSku;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
private Integer itemQuantity = 1;
|
||||
|
||||
/**
|
||||
* 商品单价
|
||||
*/
|
||||
private BigDecimal itemUnitAmount;
|
||||
|
||||
/**
|
||||
* 成功回调URL(PayPal支付成功后跳转)
|
||||
*/
|
||||
@NotBlank(message = "成功回调URL不能为空")
|
||||
private String returnUrl;
|
||||
|
||||
/**
|
||||
* 取消回调URL(PayPal支付取消后跳转)
|
||||
*/
|
||||
@NotBlank(message = "取消回调URL不能为空")
|
||||
private String cancelUrl;
|
||||
|
||||
/**
|
||||
* 收货人姓名
|
||||
*/
|
||||
private String shippingName;
|
||||
|
||||
/**
|
||||
* 收货地址 - 街道地址
|
||||
*/
|
||||
private String shippingAddressLine1;
|
||||
|
||||
/**
|
||||
* 收货地址 - 详细地址
|
||||
*/
|
||||
private String shippingAddressLine2;
|
||||
|
||||
/**
|
||||
* 收货地址 - 城市
|
||||
*/
|
||||
private String shippingCity;
|
||||
|
||||
/**
|
||||
* 收货地址 - 州/省
|
||||
*/
|
||||
private String shippingState;
|
||||
|
||||
/**
|
||||
* 收货地址 - 邮编
|
||||
*/
|
||||
private String shippingPostalCode;
|
||||
|
||||
/**
|
||||
* 收货地址 - 国家代码(如US, CN等)
|
||||
*/
|
||||
private String shippingCountryCode;
|
||||
|
||||
/**
|
||||
* PayPal账户邮箱(可选,如果提供会预填充)
|
||||
*/
|
||||
private String emailAddress;
|
||||
}
|
||||
|
||||
13
mt-pay/src/main/resources/application-paypal-example.yml
Normal file
13
mt-pay/src/main/resources/application-paypal-example.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
# PayPal支付配置示例
|
||||
# 请将以下配置添加到你的application.yml或application-dev.yml中
|
||||
|
||||
paypal:
|
||||
# PayPal Client ID(从PayPal开发者控制台获取)
|
||||
client-id: YOUR_CLIENT_ID
|
||||
# PayPal Client Secret(从PayPal开发者控制台获取)
|
||||
client-secret: YOUR_CLIENT_SECRET
|
||||
# 环境模式:sandbox(沙箱)或 production(生产)
|
||||
mode: sandbox
|
||||
# 是否启用PayPal支付
|
||||
enabled: true
|
||||
|
||||
Reference in New Issue
Block a user