跳到主要内容

OmniCRM支付系统API指南

概述

OmniCRM支付系统提供了一个全面的、与供应商无关的支付处理基础设施。今天它支持Stripe和PayPal,但模块化架构允许与任何支付提供商(Square、Adyen、Braintree等)集成,而无需更改应用程序代码。

本文档涵盖了系统中所有可用的支付API和工作流程。

🔧 Ansible Playbook集成:有关在配置playbook中实现这些支付API的信息,请参见**从Playbooks进行收费和支付**


目录

  1. 模块化架构
  2. 核心概念
  3. 财务文件
  4. 支付方式API
  5. 支付流程API
  6. 钱包API
  7. API参考摘要
  8. 常见用例

模块化架构

为什么与供应商无关?

该系统使用抽象层将业务逻辑与支付供应商的具体细节分开。这意味着:

  • ✅ 添加新的支付提供商而无需触及应用程序代码
  • ✅ 切换供应商无需数据库迁移
  • ✅ 同时支持多个供应商
  • ✅ 无论后端提供商如何,API始终保持一致

架构层

添加新的支付供应商

要添加新的提供商(例如Square、Adyen、Braintree),请联系您的OmniCRM技术团队。

有关现有供应商(Stripe、PayPal)的配置详细信息,请参见**供应商配置**。

流程概述

步骤

  1. 实现处理器类,使用标准的PaymentVendorInterface(授权、捕获、收费、退款、释放)
  2. 在VendorFactory中注册处理器,使用供应商名称
  3. 在供应商的沙盒环境中测试集成
  4. 部署 - 所有现有API自动支持新供应商

结果:一旦部署,新供应商将无缝工作:

# 添加Square支付方式
curl -X POST /api/payments/methods \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"vendor": "square",
"payment_token": "sq_xxxxx"
}'

所有支付API自动支持新供应商,无需更改应用程序代码。

集成时间:通常需要1-2天来集成新的供应商处理器。


供应商配置

Stripe配置

Stripe是主要的支付供应商,提供带有3D安全支持的卡处理。

1. 获取Stripe API密钥

  • https://stripe.com 注册
  • 导航到开发者 → API密钥
  • 复制您的可发布密钥(pk_live_...或pk_test_...)
  • 复制您的秘密密钥(sk_live_...或sk_test_...)

重要

  • 在开发中使用测试密钥(pk_test_/sk_test_)
  • 仅在生产中使用实时密钥(pk_live_/sk_live_)
  • 绝不要将API密钥提交到版本控制中

2. 配置后端

添加到crm_config.yaml

payment_vendors:
stripe:
api_key: "sk_live_YOUR_SECRET_KEY_HERE"
publishable_key: "pk_live_YOUR_PUBLISHABLE_KEY_HERE"

或通过环境变量:

export STRIPE_SECRET_KEY="sk_live_YOUR_SECRET_KEY_HERE"
export STRIPE_PUBLISHABLE_KEY="pk_live_YOUR_PUBLISHABLE_KEY_HERE"

3. 配置前端

添加到.env

REACT_APP_STRIPE_PUBLISHABLE_KEY=pk_live_YOUR_PUBLISHABLE_KEY_HERE

安全注意:仅可发布密钥放在前端。秘密密钥绝不能暴露给浏览器。

PayPal配置

PayPal提供卡片保管和PayPal账户支付。

配置

添加到crm_config.yaml

payment_vendors:
paypal:
client_id: "AXx_YOUR_CLIENT_ID_HERE"
client_secret: "ELx_YOUR_CLIENT_SECRET_HERE"
mode: "live" # 或 "sandbox" 用于测试

PCI合规性

OmniCRM如何维护PCI合规性

  • 卡数据直接输入到供应商托管的iframe中(Stripe Elements,PayPal Card Fields)
  • OmniCRM从不查看或存储完整的卡号
  • 仅在数据库中存储令牌化的支付方式
  • 降低您业务的PCI合规性范围

支付处理指标

OmniCRM提供全面的指标来监控支付处理操作。有关Stripe支付指标的完整详细信息,包括API调用跟踪、支付量、失败率和响应时间,请参见监控与指标


数据库架构与供应商无关

数据库架构支持任何支付供应商,无需迁移:

示例 - 保存Square卡:

{
"vendor": "square",
"vendor_payment_method_id": "sq_card_abc123",
"payment_type": "card"
}

无需代码更改。无需迁移。直接工作。


核心概念

数据模型

PaymentMethod

与供应商无关的客户支付方式存储。

{
"payment_method_id": 789,
"customer_id": 123,
"vendor": "stripe", // 'stripe', 'paypal'或任何添加的供应商
"vendor_payment_method_id": "pm_xxx", // 供应商的内部ID
"payment_type": "card", // 'card', 'paypal', 'ach'等
"is_default": true,
"card_brand": "visa",
"card_last4": "4242",
"card_exp_month": 12,
"card_exp_year": 2025,
"card_nickname": "My Visa Card",
"status": "active"
}

PaymentAuthorization

两阶段提交授权记录(保留资金)。

{
"authorization_id": 301,
"customer_id": 123,
"payment_method_id": 789,
"vendor": "stripe", // 哪个供应商授权
"vendor_authorization_id": "auth_xxx", // 供应商的授权ID
"amount": 200.00,
"currency": "USD",
"status": "authorized", // 'authorized', 'captured', 'released'
"authorized_at": "2025-12-27T10:00:00Z",
"expires_at": "2026-01-03T10:00:00Z",
"meta": {}
}

PaymentCapture

已捕获/完成的支付。

{
"capture_id": 103,
"authorization_id": 301,
"customer_id": 123,
"payment_method_id": 789,
"vendor": "stripe",
"vendor_transaction_id": "ch_xxx", // 供应商的交易ID
"amount": 200.00,
"currency": "USD",
"status": "succeeded", // 'succeeded', 'failed', 'refunded'
"captured_at": "2025-12-27T10:30:00Z",
"vendor_response": {}, // 完整的供应商响应
"meta": {}
}

WalletAccount

客户钱包及余额跟踪(与客户一对一)。

{
"wallet_account_id": 456,
"customer_id": 123,
"balance": 150.50,
"currency": "USD",
"auto_recharge_enabled": true,
"auto_recharge_amount": 100.00,
"auto_recharge_threshold": 10.00,
"low_balance_warning_threshold": 10.00
}

WalletLedger

所有钱包交易的完整审计记录。

{
"ledger_id": 501,
"customer_id": 123,
"wallet_account_id": 456,
"transaction_type": "credit", // 'credit', 'debit', 'refund', 'adjustment'
"amount": 100.00,
"balance_before": 150.50,
"balance_after": 250.50,
"currency": "USD",
"description": "Card top-up",
"reference_type": "payment_capture", // 链接到相关对象
"reference_id": 103,
"meta": {},
"created_at": "2025-12-27T10:35:00Z"
}

财务文件

有关发票模板和自定义的信息,请参见**客户发票**。

发票

定义:发票是包含借记交易(收费)列表的文档。当调用API创建发票时,应提供借记交易ID的数组。通过设置它们的invoice_id字段,这些交易与发票“链接”。

有关交易管理的详细信息,请参见**交易**。

关键字段

{
"invoice_id": 12345,
"invoice_number": "INV-2025-000001", // 自动生成:INV-YYYY-NNNNNN
"customer_id": 123,
"title": "每月服务发票",
"paid": true, // 支付状态
"void": false, // 作废状态
"payment_reference": "ch_xxxxx", // 最后/主要支付ID
"payment_type": "stripe_capture", // 最后支付类型
"payment_time": "2025-12-27T10:30:00",
"start_date": "2025-12-01",
"end_date": "2025-12-31",
"due_date": "2026-01-15",
"retail_cost": 500.00, // 发票总金额
"wholesale_cost": 250.00
}

发票生成:发票编号以INV-YYYY-NNNNNN格式自动生成,在日历年内按顺序生成,每年1月1日重置。

发票上的支付

支付如何工作:一旦从借记交易创建了发票,可以通过创建与发票相关联的信用交易(负的retail_cost)来应用支付

支付应:

  • 清楚地列在发票上,标记为“支付”
  • 显示相关的支付日期(可以与发票创建日期不同)
  • 支持每张发票的多个支付
  • 净额与借记相抵消以计算发票余额

发票状态

  • 已支付:总支付(信用)等于或超过总借记
  • 部分支付:已应用一些支付,但余额仍然存在
  • 超额支付:当前不处理 - 需要信用拆分(未来功能)

复式记账:系统实现了适当的会计,每个收费都有相应的支付:

// 1. 借记交易(收费)
{
"transaction_id": 7001,
"invoice_id": 12345,
"retail_cost": 100.00, // 正数 = 客户欠款
"title": "服务费"
}

// 2. 信用交易(支付)
{
"transaction_id": 7002,
"invoice_id": 12345,
"retail_cost": -100.00, // 负数 = 收到支付
"title": "发票支付:服务费(12345)",
"payment_type": "stripe_capture",
"payment_reference": "ch_xxxxx"
}

// 净结果:$0余额 → 发票标记为已支付

补充发票元数据:除了信用交易外,发票还存储摘要字段(payment_referencepayment_typepayment_time)以便快速查找。然而:

  • 主要方法:通过invoice_id链接的信用交易(Hayden的规范)
  • 次要元数据:发票字段存储最后/主要支付的摘要
  • 对于多个支付:查询信用交易以获取完整历史

对账单

定义:对账单显示客户在指定期间内的所有借记和信用交易。这是唯一一种同时显示借记和信用作为行项目的文档类型,就像银行对账单一样。

信用票据

目的:仅适用于已经应用支付的发票。必须作为发票作废过程的一部分进行。

流程

  1. 发票被作废(连同其相关的借记交易)
  2. 任何已应用的支付与创建的信用票据关联
  3. 客户的余额按等额金额进入信用
  4. 信用票据余额可以:
    • 作为支付应用于另一张发票,或
    • 退款给客户

退款逻辑:如果选择退款,则根据相关的信用交易及其最初的支付方式(例如,如果信用交易类型为Stripe,则进行Stripe退款)进行调用。


支付方式API

所有端点使用基本URL:/api/payments/

有关详细的支付方式管理和卡处理,请参见**支付方式**。

添加支付方式

为客户保存新的支付方式。

端点POST /api/payments/methods

请求

curl -X POST https://your-domain.com/api/payments/methods \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"is_default": false,
"card_nickname": "My Visa Card"
}'

请求体

{
"customer_id": 123,
"vendor": "stripe", // 'stripe', 'paypal'或任何添加的供应商
"payment_token": "pm_xxxxx", // 前端SDK生成的一次性令牌
"is_default": false, // 设置为默认支付方式?
"card_nickname": "My Visa Card" // 可选的友好名称
}

响应(201已创建):

{
"success": true,
"message": "支付方式添加成功",
"data": {
"payment_method_id": 789,
"customer_id": 123,
"vendor": "stripe",
"payment_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"card_exp_month": 12,
"card_exp_year": 2025,
"card_nickname": "My Visa Card",
"is_default": false,
"status": "active"
}
}

获取支付方式

检索客户的所有支付方式。

端点GET /api/payments/methods?customer_id={id}

请求

curl -X GET "https://your-domain.com/api/payments/methods?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"

响应(200 OK):

{
"success": true,
"data": [
{
"payment_method_id": 789,
"customer_id": 123,
"vendor": "stripe",
"payment_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"is_default": true
},
{
"payment_method_id": 790,
"customer_id": 123,
"vendor": "paypal",
"payment_type": "paypal",
"paypal_email": "user@example.com",
"is_default": false
}
]
}

获取默认支付方式

获取客户的默认支付方式。

端点GET /api/payments/methods/default?customer_id={id}

请求

curl -X GET "https://your-domain.com/api/payments/methods/default?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"

响应(200 OK):

{
"success": true,
"data": {
"payment_method_id": 789,
"vendor": "stripe",
"payment_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"is_default": true
}
}

设置默认支付方式

将支付方式设置为默认。

端点PUT /api/payments/methods/set-default

请求

curl -X PUT https://your-domain.com/api/payments/methods/set-default \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"payment_method_id": 790
}'

响应(200 OK):

{
"success": true,
"message": "默认支付方式已更新",
"data": {
"payment_method_id": 790,
"is_default": true
}
}

删除支付方式

删除已保存的支付方式。

端点DELETE /api/payments/methods/{payment_method_id}?customer_id={id}

请求

curl -X DELETE "https://your-domain.com/api/payments/methods/789?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"

响应(200 OK):

{
"success": true,
"message": "支付方式删除成功"
}

支付流程API

该系统支持多种支付流程,具体取决于您的用例。

1. 直接支付(简单收费)

用例:简单的一步支付,无需服务提供。

端点POST /api/payments/charge

请求

curl -X POST https://your-domain.com/api/payments/charge \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 50.00,
"currency": "USD",
"payment_method_id": 789,
"metadata": {
"order_id": "12345"
}
}'

请求体

{
"customer_id": 123,
"amount": 50.00,
"currency": "USD", // 默认:USD
"payment_method_id": 789, // 可选 - 如果省略则使用默认
"vendor": "stripe", // 如果使用payment_token则必需
"payment_token": "pm_xxxxx", // 可选 - 一次性令牌
"save_method": false, // 保存支付方式以供将来使用?
"metadata": {
"order_id": "12345"
}
}

响应(200 OK):

{
"success": true,
"message": "支付成功",
"data": {
"transaction_id": "ch_xxxxx",
"capture_id": 101,
"amount": 50.00,
"currency": "USD",
"status": "succeeded"
}
}

2. 发票支付(优先使用钱包)

用例:使用钱包余额支付发票,剩余部分使用卡支付。

端点POST /api/payments/invoice

流程

  1. 检查钱包余额
  2. 首先使用钱包资金
  3. 对短缺部分收取卡���(如有)
  4. 用卡金额信用钱包
  5. 从钱包中借记全额发票金额

请求

curl -X POST https://your-domain.com/api/payments/invoice \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 200.00,
"payment_method_id": 789,
"metadata": {
"invoice_id": 12345,
"description": "发票支付"
}
}'

响应(200 OK):

{
"success": true,
"message": "发票支付已处理",
"data": {
"customer_id": 123,
"service_amount": 200.00,
"routing_mode": "hybrid",
"initial_balance": 150.00,
"wallet_portion_used": 150.00, // 钱包覆盖了这部分
"card_portion_used": 50.00, // 卡收取剩余部分
"charged_amount": 50.00,
"wallet_credited": 50.00,
"wallet_debited": 200.00,
"final_balance": 0.00,
"payment_method_used": true
}
}

3. 授权保留(保留资金)

用例:为后续捕获保留资金(例如,酒店预订、租赁)。

🔧 Playbook实现:有关两阶段支付流程的Ansible playbook示例,请参见两阶段提交模式

第1步:创建授权保留

端点POST /api/payments/authorize/hold

优先使用钱包流程

  1. 检查钱包余额
  2. 计算短缺(金额 - 钱包余额)
  3. 仅对短缺部分授权卡
  4. 钱包借记在捕获时发生,而不是在此时

请求

curl -X POST https://your-domain.com/api/payments/authorize/hold \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 200.00,
"payment_method_id": 789,
"use_wallet": true,
"metadata": {
"reservation_id": "RES-001"
}
}'

请求体

{
"customer_id": 123,
"amount": 200.00,
"currency": "USD",
"payment_method_id": 789, // 可选 - 如果省略则使用默认
"vendor": "stripe", // 如果使用payment_token则必需
"payment_token": "pm_xxxxx", // 可选 - 一次性令牌
"save_method": false,
"use_wallet": true, // 启用优先使用钱包路由(默认:true)
"metadata": {
"reservation_id": "RES-001"
}
}

响应(200 OK):

{
"success": true,
"message": "支付已授权(创建了保留)",
"data": {
"authorization_id": 301,
"vendor_authorization_id": "auth_xxxxx",
"amount": 200.00,
"currency": "USD",
"status": "authorized",
"wallet_balance": 150.00,
"wallet_to_use": 150.00, // 钱包将覆盖这部分
"card_amount": 50.00, // 卡授权这部分
"message": "卡授权$50(钱包充值)。在捕获时将发生$200的借记。"
}
}

第2步:捕获授权

端点POST /api/payments/capture/{authorization_id}

优先使用钱包捕获流程

  1. 捕获卡(如果已授权) - 顶替钱包
  2. 用捕获的卡金额信用钱包
  3. 从钱包中借记全额服务金额
  4. 创建发票/交易(如请求)

请求

curl -X POST https://your-domain.com/api/payments/capture/301 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"amount": 200.00,
"metadata": {
"invoice": true,
"title": "酒店预订",
"description": "3晚住宿",
"wholesale_cost": 100.00,
"contract_days": 3,
"send_email": true
}
}'

请求体

{
"amount": 200.00, // 可选 - 如果省略则捕获全额
"metadata": {
"invoice": true, // 创建发票和交易记录?
"create_transaction": true, // 创建交易记录?
"title": "酒店预订",
"description": "3晚住宿",
"wholesale_cost": 100.00,
"contract_days": 3,
"send_email": true // 发送发票电子邮件?
}
}

响应(200 OK):

{
"success": true,
"message": "授权已捕获",
"data": {
"capture_id": 103,
"transaction_id": "ch_xxxxx",
"authorization_id": 301,
"amount": 200.00,
"currency": "USD",
"status": "succeeded",
"wallet_credit": { // 卡充值钱包
"ledger_id": 401,
"amount": 50.00
},
"wallet_debit": { // 服务从钱包中收取
"ledger_id": 402,
"amount": 200.00
},
"transaction": { // 如果invoice=true则创建
"transaction_id": 7001
},
"invoice": { // 如果invoice=true则创建
"invoice_id": 12345,
"invoice_number": "INV-2025-000001"
}
}
}

第3步:释放授权(取消)

端点POST /api/payments/release/{authorization_id}

用例:取消预订、提供失败或客户改变主意。

请求

curl -X POST https://your-domain.com/api/payments/release/301 \
-H "Authorization: Bearer YOUR_API_KEY"

响应(200 OK):

{
"success": true,
"message": "授权已释放",
"data": {
"authorization_id": 301,
"vendor_authorization_id": "auth_xxxxx",
"status": "released",
"released_at": "2025-12-27T10:45:00Z"
}
}

注意:使用优先使用钱包流程时,无需钱包退款,因为钱包在捕获时才会借记。

4. 充值支付(两阶段与提供)

用例:处理需要提供的服务充值支付(例如,热点/加密狗激活)。如果提供失败,则释放授权。

端点POST /api/payments/topup

流程:授权 → 提供服务 → 捕获

请求

curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 30.00,
"payment_method_id": 789,
"service_uuid": "svc-uuid-123",
"imsi": "123456789012345",
"days": 30,
"metadata": {
"is_rental": false
}
}'

对于匿名租赁/热点(支付方式未保存):

curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 1,
"amount": 5.00,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"service_uuid": "hotspot-uuid",
"imsi": "123456789012345",
"days": 1,
"metadata": {
"is_rental": true,
"billing_email": "user@example.com"
}
}'

响应(200 OK):

{
"success": true,
"message": "充值支付处理成功",
"data": {
"transaction_id": "ch_xxxxx",
"authorization_id": 302,
"capture_id": 104,
"amount": 30.00,
"status": "succeeded",
"provision_result": {
"success": true,
"topup_result": {...},
"service_uuid": "svc-uuid-123",
"imsi": "123456789012345",
"days": 30
},
"payment_method_saved": false // 匿名/租赁为false
}
}

5. 租赁支付(第三方)

用例:向一位客户的卡收费以支付另一位客户的服务。

端点POST /api/payments/rental

请求

curl -X POST https://your-domain.com/api/payments/rental \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"beneficiary_customer_id": 456,
"charge_customer_id": 123,
"amount": 75.00,
"payment_method_id": 789,
"service_description": "租赁服务支付",
"metadata": {
"rental_agreement_id": "RA-001"
}
}'

对于匿名租赁(租赁者的卡未保存):

curl -X POST https://your-domain.com/api/payments/rental \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"beneficiary_customer_id": 456,
"amount": 75.00,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"service_description": "匿名租赁支付",
"metadata": {
"billing_email": "renter@example.com"
}
}'

响应(200 OK):

{
"success": true,
"message": "租赁支付处理成功",
"data": {
"transaction_id": "ch_xxxxx",
"amount": 75.00,
"payment_method_saved": false // 匿名:未保存方法
}
}

6. 退款支付

端点POST /api/payments/refund

请求

curl -X POST https://your-domain.com/api/payments/refund \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"transaction_id": "ch_xxxxx",
"vendor": "stripe",
"amount": 50.00,
"reason": "customer_request"
}'

请求体

{
"transaction_id": "ch_xxxxx", // 供应商交易ID
"vendor": "stripe", // 'stripe', 'paypal'等
"amount": 50.00, // 可选 - 如果省略则全额退款
"reason": "customer_request" // 可选退款原因
}

响应(200 OK):

{
"success": true,
"message": "退款处理成功",
"data": {
"refund_id": "re_xxxxx",
"amount": 50.00,
"status": "succeeded"
}
}

钱包API

所有钱包端点使用基本URL:/api/wallet/

获取钱包余额

端点GET /api/wallet/balance?customer_id={id}

请求

curl -X GET "https://your-domain.com/api/wallet/balance?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"

响应(200 OK):

{
"customer_id": 123,
"balance": 150.50,
"currency": "USD"
}

获取钱包信息

获取完整的钱包和信用信息,包括自动充值设置。

端点GET /api/wallet/info?customer_id={id}

请求

curl -X GET "https://your-domain.com/api/wallet/info?customer_id=123" \
-H "Authorization: Bearer YOUR_API_KEY"

响应(200 OK):

{
"customer_id": 123,
"wallet": {
"wallet_account_id": 456,
"balance": 150.50,
"currency": "USD",
"auto_recharge_enabled": true,
"auto_recharge_amount": 100.00,
"auto_recharge_threshold": 10.00,
"low_balance_warning_threshold": 10.00
}
}

钱包充值

通过收费支付方式为钱包充值。

端点POST /api/wallet/topup

流程:收费卡/PayPal → 信用钱包

请求

curl -X POST https://your-domain.com/api/wallet/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 100.00,
"payment_method_id": 789
}'

请求体

{
"customer_id": 123,
"amount": 100.00,
"currency": "USD", // 默认:USD
"payment_method_id": 789 // 可选 - 如果省略则使用默认
}

响应(200 OK):

{
"success": true,
"payment": {
"transaction_id": "ch_xxxxx",
"amount": 100.00
},
"wallet": {
"ledger_id": 503,
"balance_after": 250.50
},
"message": "钱包充值100.00美元成功"
}

获取钱包交易

获取钱包交易历史(账本)。

端点GET /api/wallet/transactions?customer_id={id}&limit={n}&offset={n}&type={type}

请求

curl -X GET "https://your-domain.com/api/wallet/transactions?customer_id=123&limit=50&offset=0&type=credit" \
-H "Authorization: Bearer YOUR_API_KEY"

查询参数

  • customer_id(必需):客户ID
  • limit(可选):记录数量(默认:50)
  • offset(可选):分页偏移(默认:0)
  • type(可选):按交易类型过滤('credit','debit','refund','adjustment')

响应(200 OK):

{
"customer_id": 123,
"count": 2,
"transactions": [
{
"ledger_id": 501,
"transaction_type": "credit",
"amount": 100.00,
"balance_before": 150.50,
"balance_after": 250.50,
"description": "卡充值",
"reference_type": "payment_capture",
"reference_id": 103,
"created_at": "2025-12-27T10:35:00Z"
},
{
"ledger_id": 502,
"transaction_type": "debit",
"amount": 50.00,
"balance_before": 250.50,
"balance_after": 200.50,
"description": "服务收费",
"reference_type": "service_charge",
"reference_id": 789,
"created_at": "2025-12-27T11:00:00Z"
}
]
}

API参考摘要

支付方式端点

方法端点描述
POST/api/payments/methods添加支付方式
GET/api/payments/methods?customer_id={id}获取所有支付方式
GET/api/payments/methods/default?customer_id={id}获取默认支付方式
PUT/api/payments/methods/set-default设置默认支付方式
DELETE/api/payments/methods/{id}?customer_id={id}删除支付方式

支付流程端点

方法端点描述
POST/api/payments/charge直接支付(一步)
POST/api/payments/invoice发票支付(优先使用钱包)
POST/api/payments/topup充值与提供
POST/api/payments/authorize/hold创建授权保留
POST/api/payments/capture/{id}捕获授权
POST/api/payments/release/{id}释放授权
POST/api/payments/rental租赁/第三方支付
POST/api/payments/refund退款支付

钱包端点

方法端点描述
GET/api/wallet/balance?customer_id={id}获取钱包余额
GET/api/wallet/info?customer_id={id}获取钱包信息 + 设置
POST/api/wallet/topup通过支付方式为钱包充值
GET/api/wallet/transactions?customer_id={id}获取交易历史

PayPal特定端点

���法端点描述
POST/api/payments/paypal/vault/setup-token为Card Fields SDK创建PayPal设置令牌
POST/api/payments/paypal/vault/finalize完成PayPal保管并保存支付方式
POST/api/payments/paypal/vault/update-setup-token更新3DS/SCA处理的设置令牌

常见用例

用例1:向客户收费(预付)

场景:客户使用优先使用钱包路由支付30天的服务。

# 发票支付(优先使用钱包,卡支付短缺部分)
curl -X POST https://your-domain.com/api/payments/invoice \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 99.99,
"metadata": {
"service_id": 456,
"contract_days": 30,
"description": "30天高级服务"
}
}'

响应

{
"success": true,
"message": "发票支付已处理",
"data": {
"service_amount": 99.99,
"wallet_portion_used": 50.00, // 钱包有$50
"card_portion_used": 49.99, // 卡收取$49.99
"final_balance": 0.00
}
}

用例2:两阶段支付与服务提供

场景:仅在支付被授权后提供服务。如果提供失败,则不收���费用。

# 充值支付与自动提供
curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 149.99,
"payment_method_id": 789,
"service_uuid": "fiber-svc-uuid",
"imsi": "123456789012345",
"days": 30,
"metadata": {
"service_type": "fiber_internet"
}
}'

流程

  1. 在卡上授权$149.99
  2. 提供光纤互联网服务
  3. 如果提供成功 → 捕获支付
  4. 如果提供失败 → 释放授权(不收费)

用例3:热点匿名支付

场景:匿名用户支付WiFi热点访问费用。没有客户记录,没有保存的支付方式。

# 匿名热点支付
curl -X POST https://your-domain.com/api/payments/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 1,
"amount": 5.00,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"service_uuid": "hotspot-downtown",
"imsi": "999999999999999",
"days": 1,
"metadata": {
"is_rental": true,
"billing_email": "user@example.com",
"hotspot_location": "市中心咖啡馆"
}
}'

结果

  • 支付处理成功 ✅
  • 热点已激活 ✅
  • 支付方式未保存 ✅
  • 未创建客户记录 ✅

用例4:酒店预订(授权保留 + 捕获)

场景:为酒店预订保留资金,入住时捕获,取消时释放。

# 第1步:入住 - 保留资金
curl -X POST https://your-domain.com/api/payments/authorize/hold \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 500.00,
"use_wallet": true,
"metadata": {
"reservation_id": "RES-2025-001"
}
}'

# 响应:{"authorization_id": 301, "status": "authorized"}

# 第2a步:客户入住 - 捕获
curl -X POST https://your-domain.com/api/payments/capture/301 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"metadata": {
"invoice": true,
"title": "酒店住宿 - 3晚",
"description": "302房间,12月27日至30日",
"send_email": true
}
}'

# 第2b步:客户取消 - 释放保留
curl -X POST https://your-domain.com/api/payments/release/301 \
-H "Authorization: Bearer YOUR_API_KEY"

用例5:添加支付方式(Stripe)

场景:客户向其账户添加新的Visa卡。

# 第1步:前端通过Stripe.js创建Stripe令牌
# const {token} = await stripe.createToken(cardElement);

# 第2步:后端保存支付方式
curl -X POST https://your-domain.com/api/payments/methods \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"vendor": "stripe",
"payment_token": "pm_xxxxx",
"is_default": true,
"card_nickname": "工作Visa"
}'

用例6:钱包充值

场景:客户使用保存的支付方式为其钱包充值$100。

curl -X POST https://your-domain.com/api/wallet/topup \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 100.00
}'

响应

{
"success": true,
"payment": {
"transaction_id": "ch_xxxxx",
"amount": 100.00
},
"wallet": {
"balance_after": 250.50
},
"message": "钱包充值100.00美元成功"
}

错误处理

常见错误响应

资金不足(400错误请求):

{
"error": "钱包余额不足。可用:50.00,所需:200.00"
}

支付失败(400错误请求):

{
"error": "支付失败。请再试一次。"
}

验证错误(400错误请求):

{
"error": "customer_id和amount是必需的"
}

未找到(404未找到):

{
"error": "授权999未找到"
}

服务器错误(500内部服务器错误):

{
"error": "处理您的支付时发生错误"
}

退款与优先使用钱包

退款选项

系统支持两种退款类型,具体取决于您的业务需求:

1. 退款到支付来源(Stripe/PayPal)

用例:客户请求因取消服务或缺陷产品而全额退款。

流程:资金返回到原始支付方式(卡、PayPal账户等)。

端点POST /api/payments/refund

示例

curl -X POST https://your-domain.com/api/payments/refund \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"transaction_id": "ch_xxxxx",
"vendor": "stripe",
"amount": 100.00,
"reason": "customer_request"
}'

结果

  • Stripe/PayPal处理退款到原始支付方式
  • 客户在5-10个工作日内看到其卡/PayPal账户的信用
  • 资金不进入钱包
  • PaymentCapture表中维护完整的审计记录

何时使用

  • ✅ 客户取消订单
  • ✅ 服务未交付
  • ✅ 计费错误
  • ✅ 客户明确请求退款到卡

2. 信用到钱包

用例:部分退款、服务信用或保留资金以供未来购买。

注意:钱包信用通常在错误场景中由系统内部处理。有关手动钱包信用的信息,请联系支持或使用管理工具。

结果

  • 资金立即可用于钱包
  • 无等待期
  • 可用于未来购买
  • WalletLedger表中维护完整的审计记录

何时使用

  • ✅ 服务信用(例如,因停机而补偿)
  • ✅ 客户将重新购买的部分退款
  • ✅ 错误补偿(提供失败)
  • ✅ 促销信用

混合退款策略

最佳实践:在支付流程中的错误场景下,系统自动将信用转入钱包,而不是退款到卡。

自动错误恢复

示例场景

  1. 客户支付$100以获取服务
  2. 卡成功收费
  3. 提供失败
  4. 不退款$100到卡(5-10天 + 退款费用):
    • 立即信用$100到钱包
    • 客户可以立即重试购买
    • 无退款���用
    • 更好的用户体验

优先使用钱包路由:优化卡收费

系统仅对短缺部分收费,而不是全额,当您有钱包余额时。

示例1:$1钱包余额 + $10购买

场景:您钱包中有$1,想购买$10的附加服务。

传统支付流程(不是该系统的工作方式):

OmniCRM优先使用钱包流程

API请求

curl -X POST https://your-domain.com/api/payments/invoice \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 10.00,
"metadata": {
"addon_id": 456
}
}'

API响应

{
"success": true,
"message": "发票支付已处理",
"data": {
"customer_id": 123,
"service_amount": 10.00,
"routing_mode": "hybrid",
"initial_balance": 1.00,
"wallet_portion_used": 1.00, // 使用现有的$1
"card_portion_used": 9.00, // 仅收费$9短缺
"charged_amount": 9.00, // ← 卡收费
"wallet_credited": 9.00, // ← 充值钱包
"wallet_debited": 10.00, // ← 服务费用
"final_balance": 0.00
}
}

示例2:$50钱包余额 + $30购买

场景:您钱包中有$50,购买费用为$30。

优先使用钱包流程

API响应

{
"success": true,
"data": {
"service_amount": 30.00,
"initial_balance": 50.00,
"charged_amount": 0, // ← 无卡收费
"wallet_debited": 30.00,
"final_balance": 20.00,
"payment_method_used": false // ← 未使用卡
}
}

示例3:授权保留与钱包余额

场景:酒店保留$500的授权,钱包余额为$150。

第1步:授权POST /api/payments/authorize/hold):

API响应

{
"authorization_id": 301,
"amount": 500.00,
"status": "authorized",
"wallet_balance": 150.00,
"wallet_to_use": 150.00,
"card_amount": 350.00,
"message": "卡授权$350(钱包充值)。在捕获时将发生$500的借记。"
}

第2步:捕获POST /api/payments/capture/301):

这很重要

  • ✅ 客户仅对其卡保留$350,而不是$500
  • ✅ 减少客户可用信用的影响
  • ✅ 更准确的授权金额
  • ✅ 更好的客户体验

实施细节

优先使用钱包路由逻辑

工作原理

  1. 系统检查当前钱包余额
  2. 计算短缺:金额 - 钱包余额
  3. 如果短缺 > 0,则仅对短缺部分收费
  4. 用卡支付金额信用钱包
  5. 借记钱包全额服务金额
  6. 结果:客户仅为钱包无法覆盖的部分收费

路由模式覆盖

如果需要,您可以覆盖优先使用钱包的行为:

绕过模式 - 即使钱包有资金也始终收费卡:

curl -X POST https://your-domain.com/api/payments/charge \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{
"customer_id": 123,
"amount": 100.00,
"metadata": {
"routing_mode": "bypass"
}
}'

结果:卡收费$100,钱包信用$100,钱包借记$100(净:$0)

用例:客户希望使用卡以获取奖励/积分,即使有钱包余额。


最佳实践

  1. 始终使用优先使用钱包路由进行客户支付,以启用预付功能
  2. 在提供服务时使用两阶段支付(充值端点),以避免在提供失败时收费
  3. 为所有支付设置元数据以维护审计记录
  4. 仅对真正匿名用户使用匿名支付(热点、租赁)
  5. 优雅地处理错误并提供清晰的用户反馈
  6. 测试支付流程以确保成功和失败场景
  7. 监控授权过期(通常为卡的7天)
  8. 实施自动充值以改善客户体验

Playbook特定最佳实践

在Ansible playbook中实现支付流程时:

  1. 始终使用块/救援模式 - 将提供包装在try/catch中以实现自动回滚
  2. 存储authorization_id - 保存以供捕获/释放操作
  3. 验证API响应 - 在继续之前断言成功
  4. 四舍五入货币值 - 始终使用2位小数
  5. 检查支付方式 - 在授权之前验证客户是否有默认支付方式

有关完整详细信息和示例,请参见Playbook最佳实践


身份验证

所有API端点都需要通过API密钥进行身份验证,密钥应放在Authorization头中:

Authorization: Bearer YOUR_API_KEY

请联系您的系统管理员以获取API密钥。


供应商支持

当前支持

  • Stripe - 完全支持(卡、ACH)
  • PayPal - 完全支持(PayPal账户、通过Card Fields SDK的卡)

添加新供应商

模块化架构使添加新的支付供应商变得简单。有关详细信息,请参见模块化架构部分。


相关文档

实施指南

特性特定指南

快速导航