This document provides guidelines on how to migrate from direct per-Channel APIs to Payments API v3. If your current integration is using per-channel endpoint such as /credit_card_charges , /ewallets, /qr_codes, /callback_virtual_accounts, it means you are still using Xendit’s direct per-channel APIs.
Why you need to migrate
Unified Payment Architecture
Direct per-Channel APIs: Required separate logic and distinct API models for different payment methods (e.g., separate implementations for Virtual Accounts, Credit Cards, E-Wallets).
Payments API v3: Built on an unified architecture. By integrating once with unified resources (such as
payment_requestandpayment_token, you can enable various channels and use cases such as: one-time payments, saved payment methods, and recurring subscriptions without rewriting your integration for each channel. You build an integration which easy to scale from the start.
Access to All New Payment Channels & Regional Expansion
Direct per-Channel APIs: Restricted to older payment channels, legacy flow types, and limited regional availability.
Payments API v3: Automatically unlocks access to Xendits’ newest payment channels and new features
Overall changes
In the Direct per-Channel APIs, every payment method operated separately. If you wanted to accept Virtual Accounts, E-Wallets, Cards, and QR codes, your had to maintain 4 distinct endpoints (/callback_virtual_accounts, /ewallets/charges, /credit_card_charges, /qr_codes), each with different payload shapes, different header parameters, and separate status lifecycle conventions.
In Payments API v3, a Payment Request (/v3/payment_requests) acts as the single core engine for all payment channels. Instead of picking a channel-specific API route, you send every transaction to one unified endpoint and simply pass a channel_code and channel_properties. You instruct Xendit what you intend to do using the type parameter (PAY, PAY_AND_SAVE, or REUSABLE_PAYMENT_CODE), and tokenization or saving customer payment details only happens when you explicitly tell the API to do so.
Endpoint Mapping
Detailed Changes
eWallets
Legacy POST /ewallets was already fairly close in shape to v3 — this is generally the most straightforward channel to migrate.
// Legacy — POST https://api.xendit.co/ewallets
{
"reference_id": "order-id-123",
"currency": "IDR",
"amount": 25000,
"checkout_method": "ONE_TIME_PAYMENT",
"channel_code": "ID_DANA",
"channel_properties": {
"success_redirect_url": "https://redirect.me/payment"
}
}// v3 — POST https://api.xendit.co/v3/payment_requests
// header: api-version: 2024-11-11
{
"reference_id": "order-id-123",
"type": "PAY",
"country": "ID",
"currency": "IDR",
"request_amount": 25000,
"channel_code": "DANA",
"channel_properties": {
"success_return_url": "https://redirect.me/payment"
}
Notes:
checkout_method: ONE_TIME_PAYMENT→type: "PAY"(usePAY_AND_SAVEif you also want to save the wallet for reuse, replacing legacycheckout_method: TOKENIZED_PAYMENT
QR Codes
// Legacy — POST https://api.xendit.co/qr_codes
{
"reference_id": "testing_id_1669118631",
"type": "DYNAMIC",
"currency": "IDR",
"channel_code": "ID_DANA",
"amount": 1000,
"callback_url": "https://yourwebsite.com/callback"
}// v3 — POST https://api.xendit.co/v3/payment_requests
// header: api-version: 2024-11-11
{
"reference_id": "testing_id_1669118631",
"type": "PAY",
"country": "ID",
"currency": "IDR",
"request_amount": 1000,
"channel_code": "QRIS"
}Direct Debit
Direct Debit API integration was multi-step: link the account, save it as a Payment Method, then charge it. v3 keeps a similar shape but flattens the objects involved.
// Legacy step 1 — POST https://api.xendit.co/linked_account_tokens
{
"customer_id": "cust-239c16f4-866d-43e8-9341-7badafbc019f",
"channel_code": "DC_BRI",
"properties": { "account_mobile_number": "+62812345678", "card_last_four": "8888" }
}
// Legacy step 2 — POST https://api.xendit.co/payment_methods
{
"customer_id": "cust-239c16f4-866d-43e8-9341-7badafbc019f",
"type": "DEBIT_CARD",
"properties": { "id": "la-fac7e744-ab40-4100-a447-cbbb16f29ded" }
}
// Legacy step 3 — POST https://api.xendit.co/direct_debits
{
"reference_id": "direct-debit-ref-1594718940",
"payment_method_id": "pm-b6116aea-8c23-42d0-a1e6-33227b52fccd",
"currency": "IDR",
"amount": 60000,
"enable_otp": true
}// v3 — POST https://api.xendit.co/v3/payment_tokens (linking/saving)
// header: api-version: 2024-11-11
{
"reference_id": "token_bri_user_001",
"country": "ID",
"currency": "IDR",
"channel_code": "BRI_DIRECT_DEBIT",
"customer": {
"reference_id": "cust_user_001",
"type": "INDIVIDUAL",
"individual_detail": {
"given_names": "John",
"surname": "Doe"
},
"email": "johndoe@example.com",
"mobile_number": "+628123456789"
},
"channel_properties": {
"card_last_four_digits": "1234",
"email": "johndoe@example.com",
"mobile_number": "+628123456789",
"success_return_url": "https://yourwebsite.com/save-payment/success",
"failure_return_url": "https://yourwebsite.com/save-payment/failure"
}
}
// v3 — POST https://api.xendit.co/v3/payment_requests (charging)
// header: api-version: 2024-11-11
{
"reference_id": "req_bri_charge_002",
"type": "PAY",
"payment_token_id": "pt-12345678-abcd-1234-abcd-1234567890ab",
"country": "ID",
"currency": "IDR",
"amount": 100000,
"channel_properties": {
"account_mobile_number": "+628123456789",
"success_return_url": "https://yourwebsite.com/success",
"failure_return_url": "https://yourwebsite.com/failure"
}
}Virtual Accounts
// Legacy — POST https://api.xendit.co/callback_virtual_accounts
{
"external_id": "ORDER-2020-123",
"bank_code": "BCA",
"name": "John Doe",
"is_closed": true,
"expected_amount": 3000000,
"is_single_use": true
}// v3 — POST https://api.xendit.co/v3/payment_requests
// header: api-version: 2024-11-11
{
"reference_id": "order_bca_va_12345",
"type": "PAY",
"country": "ID",
"currency": "IDR",
"request_amount": 3000000,
"channel_code": "BCA_VIRTUAL_ACCOUNT",
"channel_properties": {
"expires_at": "2026-07-23T11:00:00Z",
"display_name": "John Doe",
"virtual_account_number": "886969696988",
"success_return_url": "https://xendit.co/success"
}
}Retail Outlets — Indonesia (OTC ID)
// Legacy — POST https://api.xendit.co/fixed_payment_code
{
"external_id": "FPC-1619132067",
"retail_outlet_name": "ALFAMART",
"name": "John Doe",
"expected_amount": 25000
}// v3 — POST https://api.xendit.co/v3/payment_requests
// header: api-version: 2024-11-11
{
"reference_id": "FPC-1619132067",
"type": "REUSABLE_PAYMENT_CODE",
"country": "ID",
"currency": "IDR",
"request_amount": 25000,
"channel_code": "ALFAMART",
"channel_properties": {
"customer_name": "John Doe"
}
}Notes
use
type: "REUSABLE_PAYMENT_CODE"since OTC codes accept payment at any point before expiry (matching legacy behavior even for is_single_use: true codes)the returned payment_code/prefix combination moves into actions[].value with a
PRESENT_TO_CUSTOMERaction.
Retail Outlets — Philippines (OTC PH)
// Legacy — POST https://api.xendit.co/payment_codes
{
"reference_id": "123",
"channel_code": "7ELEVEN",
"customer_name": "John Doe",
"amount": 50,
"currency": "PHP"
}// v3 — POST https://api.xendit.co/v3/payment_requests
// header: api-version: 2024-11-11
{
"reference_id": "123",
"type": "REUSABLE_PAYMENT_CODE",
"country": "PH",
"currency": "PHP",
"request_amount": 50,
"channel_code": "7ELEVEN_CLIQQ",
"channel_properties": {
"expires_at" : "2024-06-01T11:00:00Z",
"payer_name" : "John Doe",
"payment_code" : "A1B2C3"
}
}Credit Cards
The migration from Xendit.JS to Components is the most involved migration, previously handling tokenization, authorization, capture and charge on separate endpoints, now moving to 1 single endpoint
You can access Xendit Payment Components to see the guidelines to implement Xendit Payment Components
For the Xendit.JS Integration use case
Actions | Direct per-Channel API (Legacy) | Sessions |
|---|---|---|
Collecting card information | Use client-side integration (Xendit.js) to collect card details and perform tokenizations then sent the resulting token_id to merchant’s backend server to create a charge | Merchant can collect and capture payments using Xendit Payment Components Merchants’s server first creates a Payment Session (define the flow that you want either doing one time payment (PAY) or PAY_AND_SAVE), passes the components_sdk_key to the frontend, for front end to initialize the Xendit UI components |
Perform charge to cards | Use |
Full PAN Integrations (PCI-DSS Level 1 Compliant merchants)
Actions: Perform charge to cards
Merchant send POST https://api.xendit.co/credit_card_charges
{
"external_id": "order_001",
"amount": 250000,
"card_data": {
"account_number": "4000000000001091",
"exp_month": "12",
"exp_year": "2028",
"card_cvn": "123"
},
"capture": true,
"descriptor": "MERCHANT STORE"
}Merchant send POST https://api.xendit.co/v3/payment_requests
{
"reference_id": "order_001",
"type": "PAY",
"country": "ID",
"currency": "IDR",
"amount": 250000,
"capture_method": "AUTOMATIC",
"channel_code": "CARDS",
"channel_properties": {
"card_details": {
"card_number": "4000000000001091",
"expiry_month": "12",
"expiry_year": "2028",
"cvn": "123",
"cardholder_first_name": "John",
"cardholder_last_name": "Doe"
},
"success_return_url": "https://yourwebsite.com/checkout/success",
"failure_return_url": "https://yourwebsite.com/checkout/failure"
}
}Actions: Perform 0-auth
Merchant send POST https://api.xendit.co/credit_card_charges
{
"external_id": "order_001",
"amount": 0,
"card_data": {
"account_number": "4000000000001091",
"exp_month": "12",
"exp_year": "2028",
"card_cvn": "123"
},
"capture": true,
"descriptor": "MERCHANT STORE"
}Merchant send POST https://api.xendit.co/v3/payment_requests
{
"reference_id": "verify_card_cust_789",
"type": "VERIFY_PAYMENT_METHOD",
"country": "ID",
"currency": "IDR",
"customer_id": "cust-abc123",
"channel_code": "CARDS",
"channel_properties": {
"card_details": {
"cvn": "123",
"card_number": "4000000000001091",
"expiry_year": "2027",
"expiry_month": "09",
"cardholder_first_name": "Budi",
"cardholder_last_name": "Santoso",
"cardholder_email": "budi@example.com",
"cardholder_phone_number": "+628123456789"
},
"card_on_file_type": "MERCHANT_UNSCHEDULED"
"success_return_url": "https://merchant.co/verify/success",
"failure_return_url": "https://merchant.co/verify/failure"
},
"description": "Card verification — no charge"
}For further details, can access Payment Verify Method
Notes:
Existing card
token_idfrom legacy API needs to be migrated topayment_token_idin Payments API v3Contact our support help@xendit.co for migration assistance.
Field Mappings
eWallets
QR Codes
Direct Debit
Virtual Accounts
Retail Outlets — Indonesia (OTC ID)
Retail Outlets — Philippines (OTC PH)
Credit Cards
Reference Links
Create Payment Request (v3 API reference)
Create Payment Token (v3 API reference)