Pay and withdraw from customer balances
Use available customer funds without creating another deposit address.
Internal payment
Create a Payment Intent with payment_method_types: ["customer_balance"], the exact customer,
chain, asset and amount. Set confirm: true for immediate automatic capture, or
capture_method: "manual" when the merchant must authorize first and capture later.
const payment = await paybytoken.paymentIntents.create(
{
amount: '25.00',
currency: 'USDC',
chain: 'base',
customer: customer.id,
payment_method_types: ['customer_balance'],
capture_method: 'automatic',
confirm: true,
metadata: { order_id: order.id },
},
{ idempotencyKey: `customer-balance-payment:${order.id}:v1` },
)The chain and asset must match one available customer balance row. Do not automatically fall back to another chain or stablecoin when funds are insufficient.
Manual authorization reserves the customer's funds. Capture only while the intent is
requires_capture; cancel or allow authorization expiry to release them. Fulfill only after the
canonical payment_intent.succeeded event.
const authorization = await paybytoken.paymentIntents.create(
{
amount: '25.00',
currency: 'USDC',
chain: 'base',
customer: customer.id,
payment_method_types: ['customer_balance'],
capture_method: 'manual',
confirm: true,
},
{ idempotencyKey: `authorization:${order.id}:v1` },
)
if (authorization.status === 'requires_capture') {
await paybytoken.paymentIntents.capture(authorization.id)
}Capture and cancellation are state-checked, but your order logic must also be idempotent. Never capture from a browser return or because an inventory system emitted the same callback twice.
External withdrawal
Read /api/v1/withdrawal-assets, display the fee and validate the exact network and destination.
Create the withdrawal with an idempotency key. The response can remain pending while Chain Service
funds and submits the transaction, so use customer_withdrawal.* webhooks or authenticated
retrieval for final state.
const withdrawal = await paybytoken.customerWithdrawals.create(
customer.id,
{
chain: 'base',
asset: 'USDC',
amount: '20.00',
destination_address: destination,
reference: withdrawalRequest.id,
},
{ idempotencyKey: `customer-withdrawal:${withdrawalRequest.id}:v1` },
)Show the configured fee and total debit before authorization. A transaction hash may appear before
your business considers the withdrawal final; use succeeded as the terminal success state and
route requires_attention to operations.
Refund
Create a Refund against the captured Payment Intent. A customer-balance refund returns to the same
merchant-scoped customer balance and never accepts an arbitrary address. Keep refund processing
separate from the original payment status and consume refund.* events idempotently.
const refund = await paybytoken.refunds.create(
{
payment_intent: payment.id,
amount: '10.00',
reason: 'requested_by_customer',
metadata: { support_case_id: supportCase.id },
},
{ idempotencyKey: `refund:${supportCase.id}:v1` },
)See the Customer withdrawals API and Refunds API for endpoint contracts.
Did this page answer your question?
Your feedback helps us improve the integration path.