本文档介绍了如何在 PitchMaster.ai 项目中集成 Boxn API 的支付功能。集成包括创建订单、处理支付、管理退款以及处理 webhook 通知。
src/
├── api/
│ └── payment.ts # 支付 API 客户端
├── stores/
│ └── pay.ts # 支付状态管理 (Pinia store)
├── components/
│ └── PaymentForm.vue # 支付表单组件
└── utils/
└── paymentWebhook.ts # Webhook 处理工具
在项目根目录创建或更新 .env 文件:
# Boxn API 配置
VITE_BOXN_API_BASE_URL=https://boxn-api.onrender.com
VITE_BOXN_API_TOKEN=your_api_token_here
VITE_BOXN_WEBHOOK_SECRET=your_webhook_secret_here在应用启动时设置 Boxn API 认证令牌:
// 在 main.ts 或用户登录后
localStorage.setItem('boxn_token', 'your_api_token');import { usePaymentStore } from '@/stores/pay';
const paymentStore = usePaymentStore();
// 创建订单
const order = await paymentStore.createOrder({
product_id: 'premium_subscription',
quantity: 1,
currency: 'CNY'
});
// 检查订单状态
const status = await paymentStore.getOrderStatus(order.order_id);
// 加载支付历史
await paymentStore.loadPaymentHistory();POST /api/payments/orders请求参数:
interface CreateOrderRequest {
product_id: string; // 产品ID
quantity?: number; // 数量 (默认: 1)
currency?: string; // 货币 (默认: CNY)
metadata?: Record<string, unknown>; // 元数据
}响应:
interface CreateOrderResponse {
order_id: string; // 订单ID
payment_url: string; // 支付链接
amount: number; // 金额
currency: string; // 货币
status: string; // 状态
created_at: string; // 创建时间
expires_at: string; // 过期时间
}GET /api/payments/orders/{order_id}响应:
interface OrderStatusResponse {
order_id: string; // 订单ID
status: string; // 状态
amount: number; // 金额
currency: string; // 货币
payment_method?: string; // 支付方式
paid_at?: string; // 支付时间
created_at: string; // 创建时间
metadata?: Record<string, unknown>; // 元数据
}GET /api/payments/history?page=1&limit=10响应:
interface PaymentHistoryResponse {
orders: OrderStatusResponse[]; // 订单列表
total: number; // 总数
page: number; // 当前页
limit: number; // 每页数量
}POST /api/payments/refunds请求参数:
interface RefundRequest {
order_id: string; // 订单ID
amount?: number; // 退款金额 (默认全额)
reason?: string; // 退款原因
}<template>
<PaymentForm />
</template>
<script setup>
import PaymentForm from '@/components/PaymentForm.vue';
</script>该组件提供以下功能:
- 创建支付订单
- 显示当前订单状态
- 查看支付历史
- 申请退款
- 实时状态更新
在你的后端服务中设置 webhook 端点:
// 示例:Express.js 后端
app.post('/webhooks/payment', async (req, res) => {
const signature = req.headers['x-boxn-signature'];
const payload = JSON.stringify(req.body);
const isValid = await webhookUtils.processWebhookFromRequest(
payload,
signature,
process.env.BOXN_WEBHOOK_SECRET
);
if (isValid) {
res.status(200).json({ success: true });
} else {
res.status(400).json({ error: 'Invalid signature' });
}
});payment.completed- 支付完成payment.failed- 支付失败payment.refunded- 退款完成payment.cancelled- 支付取消
import { PaymentWebhookHandler } from '@/utils/paymentWebhook';
class CustomWebhookHandler extends PaymentWebhookHandler {
async handlePaymentCompleted(event) {
// 调用父类方法
await super.handlePaymentCompleted(event);
// 自定义业务逻辑
await this.sendConfirmationEmail(event);
await this.updateUserSubscription(event);
await this.triggerAnalytics(event);
}
private async sendConfirmationEmail(event) {
// 发送确认邮件
}
private async updateUserSubscription(event) {
// 更新用户订阅状态
}
private async triggerAnalytics(event) {
// 触发分析事件
}
}const paymentStore = usePaymentStore();
// 状态
paymentStore.currentOrder // 当前订单
paymentStore.orderHistory // 订单历史
paymentStore.paymentMethods // 支付方式
paymentStore.isLoading // 加载状态
paymentStore.error // 错误信息
paymentStore.pagination // 分页信息
// 计算属性
paymentStore.hasActiveOrder // 是否有活跃订单
paymentStore.totalSpent // 总消费金额
paymentStore.completedOrders // 已完成订单
paymentStore.pendingOrders // 待处理订单// 创建订单
await paymentStore.createOrder(orderData);
// 查询订单状态
await paymentStore.getOrderStatus(orderId);
// 加载支付历史
await paymentStore.loadPaymentHistory(page, limit);
// 申请退款
await paymentStore.requestRefund(refundData);
// 加载支付方式
await paymentStore.loadPaymentMethods();
// 清除当前订单
paymentStore.clearCurrentOrder();
// 清除错误
paymentStore.clearError();
// 重置状态
paymentStore.reset();-
认证失败
// 检查 token 是否有效 const token = localStorage.getItem('boxn_token'); if (!token) { // 重定向到登录页面 }
-
网络错误
try { await paymentStore.createOrder(orderData); } catch (error) { if (error.response?.status === 500) { // 服务器错误 } else if (error.code === 'NETWORK_ERROR') { // 网络错误 } }
-
订单状态错误
// 定期检查订单状态 const checkOrderStatus = async (orderId: string) => { try { await paymentStore.getOrderStatus(orderId); } catch (error) { console.error('Failed to check order status:', error); } };
- 不要在客户端代码中硬编码 API 令牌
- 使用环境变量存储敏感信息
- 定期轮换 API 令牌
- 始终验证 webhook 签名
- 使用 HTTPS 端点
- 实现幂等性处理
- 不要在前端暴露敏感错误信息
- 实现适当的重试机制
- 记录错误日志用于调试
// 验证订单数据
const validateOrderData = (data: CreateOrderRequest) => {
if (!data.product_id) {
throw new Error('Product ID is required');
}
if (data.quantity && data.quantity < 1) {
throw new Error('Quantity must be at least 1');
}
const validCurrencies = ['CNY', 'USD', 'EUR'];
if (data.currency && !validCurrencies.includes(data.currency)) {
throw new Error('Invalid currency');
}
};import { describe, it, expect, vi } from 'vitest';
import { usePaymentStore } from '@/stores/pay';
describe('Payment Store', () => {
it('should create order successfully', async () => {
const store = usePaymentStore();
const mockResponse = {
order_id: 'test_order_123',
payment_url: 'https://payment.example.com',
amount: 100,
currency: 'CNY',
status: 'pending',
created_at: '2024-01-01T00:00:00Z',
expires_at: '2024-01-01T01:00:00Z'
};
vi.spyOn(paymentApi, 'createOrder').mockResolvedValue(mockResponse);
const result = await store.createOrder({
product_id: 'test_product',
quantity: 1,
currency: 'CNY'
});
expect(result).toEqual(mockResponse);
expect(store.currentOrder).toEqual(mockResponse);
});
});// 测试完整的支付流程
describe('Payment Flow', () => {
it('should complete payment flow', async () => {
// 1. 创建订单
const order = await paymentStore.createOrder({
product_id: 'premium_subscription',
quantity: 1,
currency: 'CNY'
});
expect(order.status).toBe('pending');
// 2. 模拟支付完成
await paymentStore.getOrderStatus(order.order_id);
// 3. 验证状态更新
expect(paymentStore.currentOrder?.status).toBe('completed');
});
});确保在生产环境中正确设置所有必要的环境变量:
# 生产环境
VITE_BOXN_API_BASE_URL=https://boxn-api.onrender.com
VITE_BOXN_API_TOKEN=prod_token_here
VITE_BOXN_WEBHOOK_SECRET=prod_webhook_secret_here确保 Boxn API 允许你的域名访问:
// 在 Boxn API 控制台中配置允许的域名
const allowedOrigins = [
'https://yourdomain.com',
'https://www.yourdomain.com'
];- 设置错误监控 (如 Sentry)
- 记录支付相关日志
- 监控 webhook 处理状态
-
API 调用失败
- 检查网络连接
- 验证 API 令牌
- 确认 API 端点正确
-
Webhook 不工作
- 检查 webhook URL 是否正确
- 验证签名密钥
- 查看服务器日志
-
订单状态不同步
- 实现定期状态检查
- 使用 webhook 实时更新
- 添加手动刷新功能
// 启用调试模式
const DEBUG_PAYMENT = true;
if (DEBUG_PAYMENT) {
console.log('Payment API Request:', requestData);
console.log('Payment API Response:', responseData);
}- 初始版本
- 支持基本支付功能
- 集成 Boxn API
- 实现 webhook 处理
如有问题,请参考:
- Boxn API 文档
- 项目 GitHub Issues
- 技术支持邮箱