关于 customer-management
面向使用 DodoPayments 的开发者,涵盖客户记录、托管自助门户、支付方式、钱包账本和权益查询。重点说明客户生命周期操作及钱包交易的幂等处理。
主要功能
- 01客户记录的增删改查
- 02创建限时自助门户会话
- 03列出和删除已保存支付方式
- 04查询钱包余额并创建幂等账目
- 05读取客户权益与积分余额
适用场景
- 01将应用用户关联到 Dodo 客户
- 02为客户提供账单和订阅自助管理
- 03记录钱包充值或退款交易
- 04检查客户的功能授权和信用余额
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dodopayments/ skills --skill customer-management安装后在 Claude Code 中输入/customer-management确认已加载。
详细介绍
客户管理
创建客户记录、托管式自助服务门户、已保存的支付方式和真实货币钱包账本。此技能涵盖完整的客户生命周期:CRUD 操作、支付方式管理、限时门户会话和幂等钱包交易。
何时使用此技能
- 创建、列出、检索或更新客户记录
- 为发票、订阅和支付方式构建自助服务客户门户
- 管理已保存的支付方式和客户支付历史记录
- 实现客户钱包余额以及用于充值或退款的账本条目
- 将应用的用户记录关联到 Dodo 客户
- 读取客户权益和额度余额
核心概念
客户 是 Dodo 中用于归集付款、订阅和门户访问权限的记录。每位客户都有一个 ID(前缀为 cus_)、电子邮件、姓名和可选的元数据。
客户门户 是限时托管会话,允许客户自行操作:查看发票、取消订阅、更改套餐、更新支付方式、恢复暂停中的订阅,以及下载许可证密钥。创建会话后,将客户重定向到会话链接。
支付方式 是关联到客户的已保存银行卡或其他支付工具。你可以列出和删除这些方式;它们是在结账过程中创建的。
客户钱包 存放客户可用于未来购买的真实货币余额(USD、INR)。它们与用量额度不同(参见 credit-based-billing 技能)。钱包账本条目通过 idempotency_key 实现幂等,以防止重复扣款。
权益 是功能或文件授权。listCreditEntitlements 返回额度余额;listEntitlements 返回功能或文件授权;listEntitlementGrants 列出带有撤销状态的各项授权。
客户 CRUD
创建、列出、检索和更新客户记录。
import DodoPayments from 'dodopayments';
const client = new DodoPayments({
bearerToken: process.env.DODO_PAYMENTS_API_KEY,
environment: 'test_mode',
});
// Create a customer
const customer = await client.customers.create({
email: '[email protected]',
name: 'Alice Chen',
metadata: {
userId: 'user_12345',
tier: 'premium',
},
});
console.log(customer.customer_id); // cus_...
// List customers
const customers = await client.customers.list({
page_size: 10,
});
// Retrieve a customer
const retrieved = await client.customers.retrieve('cus_abc123');
// Update a customer
await client.customers.update('cus_abc123', {
name: 'Alice Chen-Smith',
metadata: { tier: 'enterprise' },
});
客户门户
创建限时会话,将客户重定向到托管门户。在门户中,客户可以查看发票、取消订阅、更改套餐、更新支付方式、恢复暂停中的订阅,以及下载许可证密钥。
切勿在浏览器中调用 SDK:它会使用你的秘密 API 密钥。在服务器路由中创建会话,并且只返回链接。
// Server route: create a portal session
const session = await client.customers.customerPortal.create('cus_abc123', {
return_url: 'https://yourapp.com/dashboard',
// send_email: true, // optionally also email the link to the customer
});
return Response.json({ link: session.link });
// Browser: redirect the customer
const res = await fetch('/api/portal', { method: 'POST' });
const { link } = await res.json();
window.location.href = link;
门户会话链接有效期为 24 小时。传入 send_email: true,Dodo 也会将链接发送到客户的电子邮箱。客户无法:
- 创建新订阅(请使用结账)
- 自行退款
- 更改电子邮件或姓名(请联系支持人员)
- 查看发票以外的账单历史记录
如果你需要重新实现这些功能,就等于在重复构建门户。请直接使用门户。
已保存的支付方式
列出和删除已保存到客户账户的支付方式。
// List payment methods for a customer
const methods = await client.customers.retrievePaymentMethods('cus_abc123');
console.log(methods);
// {
// items: [{ payment_method_id: 'pm_xyz789', payment_method: 'card' }]
// }
// Delete a payment method
await client.customers.deletePaymentMethod('pm_xyz789', {
customer_id: 'cus_abc123',
});
客户在结账时选择保存银行卡后,系统会创建支付方式。你无法通过 API 直接创建支付方式。
客户钱包
钱包存放客户可用于未来购买的真实货币余额(USD、INR)。它们与用量额度不同。列出钱包余额并创建幂等账本条目。
// List wallets for a customer
const wallets = await client.customers.wallets.list('cus_abc123');
console.log(wallets);
// {
// items: [
// { currency: 'USD', balance: 5000, customer_id: 'cus_abc123', ... },
// { currency: 'INR', balance: 100000, customer_id: 'cus_abc123', ... }
// ],
// total_balance_usd: 17000
// }
// List ledger entries
const entries = await client.customers.wallets.ledgerEntries.list('cus_abc123', {
page_size: 20,
});
// Create a ledger entry (credit or debit)
// Amounts are in the smallest currency unit (cents for USD, paise for INR)
await client.customers.wallets.ledgerEntries.create('cus_abc123', {
amount: 1000, // $10.00 in USD
currency: 'USD',
entry_type: 'credit', // or 'debit'
reason: 'Refund for order #12345',
idempotency_key: 'refund_order_12345_v1',
});
幂等性至关重要。 如果创建账本条目后网络故障,使用相同的 idempotency_key 重试会返回现有条目,而不会创建重复扣款。每笔交易始终使用稳定且唯一的键。
钱包存放的是真实货币。请勿在没有幂等键的情况下创建条目,也不要未经验证就向客户端公开钱包余额。
读取客户权益
权益是功能或文件授权。有三种方法:
// List credit entitlements (credit balances)
const credits = await client.customers.listCreditEntitlements('cus_abc123');
console.log(credits);
// {
// items: [{
// credit_entitlement_id: 'cred_ent_1',
// balance: '5000',
// name: 'API Calls',
// overage: '0',
// unit: 'calls'
// }]
// }
// List feature/file entitlements
const features = await client.customers.listEntitlements('cus_abc123');
console.log(features);
// {
// items: [
// {
// grant_id: 'entg_feature_1',
// entitlement_id: 'ent_feature_1',
// entitlement_name: 'Pro Features',
// integration_type: 'feature_flag',
// status: 'delivered',
// created_at: '2026-01-01T00:00:00Z',
// updated_at: '2026-01-01T00:00:00Z'
// }
// ]
// }
// List individual grants (with revocation status)
const grants = await client.customers.listEntitlementGrants('cus_abc123', {
page_size: 50,
});
console.log(grants);
// {
// items: [{
// id: 'entg_feature_1',
// entitlement_id: 'ent_feature_1',
// status: 'Delivered',
// revoked_at: null
// }]
// }
将应用用户关联到 Dodo 客户
将 Dodo 客户 ID 存储在用户记录中,或使用元数据关联双方。
// Option 1: Store the Dodo customer ID in your database
const customer = await client.customers.create({
email: user.email,
name: user.name,
});
await db.users.update(user.id, { dodo_customer_id: customer.customer_id });
// Option 2: Use metadata to store your user ID
const customer = await client.customers.create({
email: user.email,
name: user.name,
metadata: {
app_user_id: user.id,
},
});
// Later, retrieve by email (not a direct API call, but common pattern)
// You must store the mapping yourself or query Dodo's list endpoint
const customers = await client.customers.list({ page_size: 100 });
const found = customers.items.find(c => c.email === user.email);
电子邮件匹配的注意事项: 如果用户在你的应用中更改了电子邮件,Dodo 客户的电子邮件不会自动更新。请始终通过 client.customers.update() 显式同步电子邮件变更。
常见错误
为每次结账创建重复的客户。 传入 customer: { email } 并不会每次都创建新客户:默认情况下,结账会使用邮箱查找现有客户,并将会话关联到该客户。重复客户通常是因为设置了 feature_flags.always_create_new_customer: true,或者在每次结账前都调用 customers.create。只创建一次客户(或让首次结账创建客户),保存其 customer_id,并在后续结账时传入该 ID。
// WRONG: forces a new customer record on every checkout
const session = await client.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_abc', quantity: 1 }],
customer: { email: user.email },
feature_flags: { always_create_new_customer: true },
});
// RIGHT: reuse the stored customer ID, or fall back to email (matched to an existing customer by default)
const session = await client.checkoutSessions.create({
product_cart: [{ product_id: 'pdt_abc', quantity: 1 }],
customer: user.dodo_customer_id
? { customer_id: user.dodo_customer_id }
: { email: user.email },
});
自行构建专属账单 UI,而不是使用门户。 门户可处理发票、取消、套餐变更、付款方式更新和暂停状态恢复。如果你重新构建这些功能,就是在重复劳动,还会遗漏边缘情况。
在没有幂等键的情况下修改钱包余额。 重试会创建重复条目,并向客户重复收费。
// WRONG
await client.customers.wallets.ledgerEntries.create('cus_abc123', {
amount: 1000,
currency: 'USD',
entry_type: 'credit',
reason: 'Refund',
// No idempotency_key
});
// RIGHT
await client.customers.wallets.ledgerEntries.create('cus_abc123', {
amount: 1000,
currency: 'USD',
entry_type: 'credit',
reason: 'Refund',
idempotency_key: 'refund_order_12345_v1',
});
公开门户链接。 门户会话有时效限制,并与客户 ID 绑定。不要记录这些链接,也不要在 URL 中分享;应在服务器端生成链接并立即重定向。
混淆钱包与额度。 钱包中存放真实货币(USD、INR)。额度是基于用量的权益。如需管理额度,请参阅 credit-based-billing 技能。