> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dodopayments.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 客户钱包

> 为客户持有预付资金。添加或扣除资金，将余额用于支付订阅费用，将退款作为钱包资金发放，并跟踪每次变更。

> **[Get Customer Wallets](https://docs.dodopayments.com/api-reference/customers/get-customer-wallets.md)**
>
> 获取客户在每种货币中的余额。

> **[Create Ledger Entry](https://docs.dodopayments.com/api-reference/customers/post-customer-wallets-ledger-entries.md)**
>
> 向钱包添加资金或从钱包扣除资金。

> **[List Ledger Entries](https://docs.dodopayments.com/api-reference/customers/get-customer-wallets-ledger-entries.md)**
>
> 分页查看所有钱包交易。

## 什么是客户钱包？

客户钱包为客户持有真实资金，每种货币对应一个余额。使用钱包可以：

* **存储预付资金**，用于未来支付订阅费用
* **处理退款**，将退款作为钱包余额，而不是退回银行卡
* **发放促销余额**，例如新客奖励或忠诚度奖励
* **在结算时自动将钱包资金用于支付订阅费用**
* **跟踪每笔交易**，记录交易前后的余额

> [!NOTE]
>
> 你无需自行创建钱包。首次在某种货币中添加账本条目时，系统会为客户创建该货币对应的钱包，初始余额为零。在此之前，[Get Customer Wallets](https://docs.dodopayments.com/api-reference/customers/get-customer-wallets.md) 不会返回该货币的条目。

> [!WARNING]
>
> **客户钱包 ≠ 基于信用的计费**
>
> 客户钱包持有 **真实资金**，Dodo Payments 会将其用于支付订阅费用。
>
> 如需跟踪 API 调用、令牌或计算小时数等虚拟使用量，请改用 [Credit-Based Billing](https://docs.dodopayments.com/features/credit-based-billing.md)。

![客户钱包](https://mintcdn.com/dodopayments/9oQrV7vsGpxeyDkL/images/customer/customer-wallet.png?fit=max\&auto=format\&n=9oQrV7vsGpxeyDkL\&q=85\&s=a4a7ff3c89f4074f37e19b8d99d49ef5)

## 工作原理

当订阅续订或套餐变更产生费用时，Dodo Payments 会先使用客户在该货币中的钱包余额，然后从支付方式中收取剩余金额。如果余额只能覆盖部分费用，则钱包会支付该部分。

### 自动设置

你无需进行任何设置。通过 API 添加账本条目，或在控制面板客户的 **Wallets** 标签页中点击 **Apply Credit/Debit**，该货币的钱包即可使用。

### 多币种支持

每种货币都有独立的余额。[Get Customer Wallets](https://docs.dodopayments.com/api-reference/customers/get-customer-wallets.md) 响应中的每个钱包都包含一个 `currency` 和一个 `balance`，数值以最小货币单位表示；响应还会添加 `total_balance_usd`，即将所有余额换算为 USD 后的总和。账本条目支持以下货币：

**USD Balance** `integer`

美元余额（以分为单位存储）

**EUR Balance** `integer`

欧元余额（以分存储）

**GBP Balance** `integer`

英镑余额（以便士存储）

> [!NOTE]
>
> 客户钱包支持 USD、EUR 和 GBP 余额。[Payout Structure](https://docs.dodopayments.com/features/payouts/payout-structure.md) 中所述的已停用 INR 原生钱包是你的商户收款钱包，而不是客户钱包。INR 仍支持作为 UPI、RuPay 和印度银行卡订阅的交易货币。

## 使用钱包

### 查看客户余额

获取客户在每种货币中的余额，例如在购买前确认余额，或在你的应用中显示余额。

> **[Get Customer Wallet Balances](https://docs.dodopayments.com/api-reference/customers/get-customer-wallets.md)**
>
> 获取客户所有货币的钱包余额。

### 增加或扣除资金

添加资金，例如新客奖励或退款余额；也可以扣除资金，例如手动收费。必须提供 `entry_type`、`currency` 和 `amount`。`amount` 是以最小货币单位表示的正整数。添加一个 `reason`（最多 500 个字符）作为审计记录，并添加 `idempotency_key` 以防止重复操作。响应会返回更新后的钱包。

> [!NOTE]
>
> `entry_type` 为 `'credit'` 时向钱包添加资金，为 `'debit'` 时从钱包扣除资金。超过余额的借记操作会因 `400` 而失败。

> **[Create Customer Wallet Ledger Entry](https://docs.dodopayments.com/api-reference/customers/post-customer-wallets-ledger-entries.md)**
>
> 向客户的钱包添加资金或从中扣除资金。

### 查看交易历史

列出客户的所有贷记和借记记录，支持分页以及可选的 `currency` 筛选条件。每条记录都有一个 `event_type`（`payment`、`payment_reversal`、`refund`、`refund_reversal`、`dispute`、`dispute_reversal` 或 `merchant_adjustment`）、`amount`，以及 `before_balance` 和 `after_balance`。使用账本对账，并回答客户的问题。

> **[List Customer Wallet Ledger Entries](https://docs.dodopayments.com/api-reference/customers/get-customer-wallets-ledger-entries.md)**
>
> 列出客户的所有钱包交易。

## 实际应用示例

示例使用 TypeScript SDK。`client` 是一个 `DodoPayments` 客户端，按照 [Integration Guide](https://docs.dodopayments.com/developer-resources/integration-guide.md) 中的方式创建。

### 退款到钱包

如需将退款作为钱包余额而不是退回客户的银行卡，请通过贷记条目添加相应金额。这些资金会留在你的业务账户中，供客户下次购买时使用。

```javascript theme={null}
async function refundToWallet(customerId, refundAmount, originalPaymentId) {
  await client.customers.wallets.ledgerEntries.create(customerId, {
    amount: refundAmount, // Amount in cents
    currency: 'USD',
    entry_type: 'credit',
    reason: `Refund for payment ${originalPaymentId}`,
    idempotency_key: `refund_${originalPaymentId}`
  });
}
```

> [!NOTE]
>
> 账本贷记不会退还原始付款。当你通过 Refunds API 退款时，Dodo Payments 会自动将其中由钱包支付的部分退回钱包。

### 新客奖励 / 促销余额

向新客户发放新客奖励，鼓励他们完成首次购买。

```javascript theme={null}
async function addWelcomeBonus(customerId) {
  await client.customers.wallets.ledgerEntries.create(customerId, {
    amount: 1000, // $10.00 promotional balance
    currency: 'USD',
    entry_type: 'credit',
    reason: 'Welcome bonus - $10 promotional balance',
    idempotency_key: `welcome_${customerId}`
  });
}
```

### 使用钱包支付订阅费用

从钱包中扣除资金，以支付由你自行收取的费用。续订无需执行此步骤，因为 Dodo Payments 会自动将余额用于续订。

```javascript theme={null}
async function deductForPurchase(customerId, purchaseAmount, purchaseId) {
  try {
    await client.customers.wallets.ledgerEntries.create(customerId, {
      amount: purchaseAmount,
      currency: 'USD',
      entry_type: 'debit',
      reason: `Manual charge for purchase ${purchaseId}`,
      idempotency_key: `charge_${purchaseId}`
    });
  } catch (error) {
    if (error.status === 400) {
      console.log('Insufficient wallet balance');
    }
  }
}
```

### 预付费计费系统

让客户预先为账户充值，然后随着时间推移逐步使用余额。`customerId`、`paymentId` 和 `purchaseId` 是你为客户、充值和购买设置的标识符。

**1. Add Initial Funds**

客户充值时，向其钱包添加资金。

```javascript theme={null}
await client.customers.wallets.ledgerEntries.create(customerId, {
  amount: 5000, // $50.00 deposit
  currency: 'USD',
  entry_type: 'credit',
  reason: 'Account funding - prepaid deposit',
  idempotency_key: `deposit_${paymentId}`
});
```

**2. Apply Balance to Purchases**

客户使用你的服务时，从余额中扣除相应金额。

```javascript theme={null}
await client.customers.wallets.ledgerEntries.create(customerId, {
  amount: 1500, // $15.00 charge
  currency: 'USD',
  entry_type: 'debit',
  reason: 'Service purchase - usage charge',
  idempotency_key: `purchase_${purchaseId}`
});
```

**3. Monitor Balances**

检查客户的资金是否即将用尽，并提示其充值。

```javascript theme={null}
const wallets = await client.customers.wallets.list(customerId);
const usdWallet = wallets.items.find(w => w.currency === 'USD');
const balance = usdWallet?.balance ?? 0; // no USD wallet yet means a zero balance

if (balance < 1000) { // Less than $10.00
  // sendLowBalanceNotification is your own function, for example an email
  await sendLowBalanceNotification(customerId, balance);
}
```

### 多货币支持

为不同地区的客户分别保留余额。

> **US Customers**
>
> 管理美国客户的 USD 资金。
>
> ```javascript theme={null}
> await client.customers.wallets.ledgerEntries.create(customerId, {
>   amount: 20000, // $200.00 in cents
>   currency: 'USD',
>   entry_type: 'credit',
>   reason: 'USD account funding',
>   idempotency_key: `usd_deposit_${paymentId}`
> });
> ```

> **European Customers**
>
> 管理欧洲客户的 EUR 资金。
>
> ```javascript theme={null}
> await client.customers.wallets.ledgerEntries.create(customerId, {
>   amount: 18000, // €180.00 in cents
>   currency: 'EUR',
>   entry_type: 'credit',
>   reason: 'EUR account funding',
>   idempotency_key: `eur_deposit_${paymentId}`
> });
> ```

> **UK Customers**
>
> 管理英国客户的 GBP 资金。
>
> ```javascript theme={null}
> await client.customers.wallets.ledgerEntries.create(customerId, {
>   amount: 15000, // £150.00 in pence
>   currency: 'GBP',
>   entry_type: 'credit',
>   reason: 'GBP account funding',
>   idempotency_key: `gbp_deposit_${paymentId}`
> });
> ```

## 最佳实践

### 防止重复交易

传入一个根据导致该条目产生的事件（例如订单或付款 ID）生成的 `idempotency_key`，以便重试时不会重复添加或扣除资金。如果使用相同的键重复发送请求，Dodo Payments 不会创建新条目，而是返回当前钱包。

```javascript theme={null}
async function addFundsSafely(customerId, amount, reason, eventId) {
  // Derive the key from the event, not from the time, so retries reuse it.
  const idempotencyKey = `credit_${customerId}_${eventId}`;

  try {
    const result = await client.customers.wallets.ledgerEntries.create(customerId, {
      amount: amount,
      currency: 'USD',
      entry_type: 'credit',
      reason: reason,
      idempotency_key: idempotencyKey
    });

    // Replaying the same idempotency_key creates no duplicate entry and
    // returns the wallet, so no special duplicate handling is required.
    return { success: true, wallet: result };
  } catch (error) {
    if (error.status === 400) {
      // e.g. insufficient balance on a debit
      return { success: false, reason: 'insufficient_balance' };
    }

    throw error;
  }
}
```

### 在收费前检查余额

从钱包中扣除大额金额前，请确认余额足以支付。

```javascript theme={null}
async function checkBalanceBeforeOperation(customerId, requiredAmount) {
  const wallets = await client.customers.wallets.list(customerId);
  const usdWallet = wallets.items.find(w => w.currency === 'USD');

  if (!usdWallet || usdWallet.balance < requiredAmount) {
    throw new Error('Insufficient funds for this operation');
  }

  return usdWallet.balance;
}
```

## 后续规划

客户钱包目前不支持以下功能：

* **余额过期**：钱包资金不会过期。
* **分析**：目前没有支出报告或余额趋势。请使用账本自行构建。
* **Webhooks**：余额发生变化时不会触发 webhook，也没有低余额提醒。如需跟踪变更，请列出账本条目。

> [!TIP]
>
> 先从基本的充值和扣款开始，随着业务增长，再逐步自动化更多计费流程。


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.