Welcome to Xendit’s latest documentation. For legacy content, access the previous version here.

Guest checkout (one-off payment)

Prev Next

Guest checkout, also known as a one-off payment, is the simplest way for customers to complete a transaction without needing to create an account. During this process, customers enter their card details, authenticate themselves, and finalize their purchase.

Guest checkout flow

1. Create a payment session

Request - POST /sessions

{
    "reference_id": "YOUR_PAYMENT_REFERENCE_ID",
    "session_type": "PAY",
    "mode": "CARDS_SESSION_JS",
    "amount": 100000,
    "currency": "IDR",
    "country": "ID",
    "customer": {
        "reference_id": "YOUR_CUSTOMER_REFERENCE",
        "type": "INDIVIDUAL",
        "email": "test@yourdomain.com",
        "mobile_number": "+6212345678",
        "individual_detail": {
            "given_names": "Jaap",
            "surname": "Stam"
        }
    },
    "cards_session_js": {
        "success_return_url": "https://yourcompany.com/success",
        "failure_return_url": "https://yourcompany.com/failure"
    }
}

Response - POST /sessions

{
    "payment_session_id": "ps-6746c1006b7752b4d91725af",
    "created": "2024-11-27T06:49:36.535Z",
    "updated": "2024-11-27T06:49:36.535Z",
    "status": "ACTIVE",
    "reference_id": "YOUR_PAYMENT_REFERENCE_ID",
    "currency": "IDR",
    "amount": 10000,
    "country": "ID",
    "customer_id": "XENDIT_GENERATED_CUSTOMER_ID",
    "expires_at": "2024-11-27T07:19:36.434Z",
    "session_type": "PAY",
    "mode": "CARDS_SESSION_JS",
    "locale": "en",
    "business_id": "YOUR_BUSINESS_ID", 
    "cards_session_js": {
        "success_return_url": "https://yourcompany.com/success",
        "failure_return_url": "https://yourcompany.com/failure"
    }
}

2. Collect card information

Implement card_session.js on your page. Collect the card information using Xendit.payment.collectCardData(reqData, xenditResponseHandler). This will collect the card information and send it to Xendit using  card_session.js.

Request - to card_session_js

{
    "card_number": "4000000000001091",
    "expiry_month": "12",
    "expiry_year": "2040",
    "cvn": "123",
    "cardholder_first_name": "Firstname",
    "cardholder_last_name": "Last",
    "cardholder_email": "shopper@emailaddress.com",
    "cardholder_phone_number": "+61111111111",
    "payment_session_id": "YOUR_PAYMENT_SESSION_ID"
}

Response - from card_session_js

{ 
    "message": "Status updated. Wait for a callback or get the status using the Get API.", 
    "payment_request_id": "PAYMENT_REQUEST_ID", 
    "action_url": "AUTHENTICATION_PAGE_URL" 
}

Important: Store the payment_request_id. You'll need it to retrieve the transaction's status and it will be included in the payment webhook for status updates.

3. Redirect to the authentication page

Redirect your customer to the authentication page provided by the action_url from the response object. This is where the cardholder completes the 3D Secure authentication.

4. Customer completes authentication

After successfully authenticating, your customer will be redirected to your success_return_url. If authentication fails, they will be redirected to your failure_return_url.

5. Receive the webhook

Xendit will send a payment webhook to your configured webhook endpoint, indicating the final status of the transaction. You can match this webhook with the payment_request_id you stored earlier.

Example payment.capture webhook:

{
    "created": "2024-12-18T05:46:35.109Z",
    "business_id": "62440e322008e87fb29c1fd0",
    "event": "payment.capture",
    "data": {
        "type": "PAY",
        "status": "SUCCEEDED",
        "country": "ID",
        "created": "2024-12-18T05:46:08.192Z",
        "updated": "2024-12-18T05:46:30.627Z",
        "captures": [
            {
                "capture_id": "cptr-08f17fa3-e80c-4d8e-8c34-17aa3400bc1c",
                "capture_amount": 10000,
                "capture_timestamp": "2024-12-18T05:46:34.234Z"
            }
        ],
        "currency": "IDR",
        "payment_id": "py-3f57d678-2448-4c9f-a433-8468d366fb5c",
        "business_id": "62440e322008e87fb29c1fd0",
        "customer_id": "cust-7de9a9b4-37e8-40ad-b665-d97f42e538c5",
        "channel_code": "CARDS",
        "reference_id": "97ba0a32-b996-4abf-8a7b-6184a6644676_b8d18f2f-3",
        "capture_method": "AUTOMATIC",
        "request_amount": 10000,
        "payment_details": {
            "authorization_data": {
                "reconciliation_id": "7345007929096981703954",
                "authorization_code": "831000",
                "acquirer_merchant_id": "xendit_ctv_agg",
                "network_response_code": "00",
                "network_transaction_id": "016153570198200",
                "cvn_verification_result": "M",
                "retrieval_reference_number": "435205253972",
                "address_verification_result": "M",
                "network_response_code_descriptor": "Approved and completed sucessfully"
            },
            "authentication_data": {
                "flow": "CHALLENGE",
                "a_res": {
                    "eci": "05",
                    "message_version": "2.1.0",
                    "authentication_value": "AAIBBYNoEwAAACcKhAJkdQAAAAA=","directory_server_trans_id": "e537f539-d59f-4ebe-8d56-7fdc31a8e9b4"
                }
            }
        },
        "payment_request_id": "pr-5593127f-8c7b-4d2f-b487-c785ffc21e2f"
    },
    "api_version": "v3"
}

It's recommended to save the payment_id and payment_details from the webhook, correlated with the payment_request_id, as proof of payment.

Guest checkout without authentication (one-off payment)

We highly recommend performing authentication when processing card transactions. Authentication does not only provide a liability shift (protecting you from certain fraud disputes), it additionally ensures users are who they say they are.

However, upon request and after a risk-based assessment, Xendit can activate guest checkout without mandating authentication. We’ll perform a risk-based assessment, before allowing you to accept transactions without authentication.

Guest checkout flow without authentication

How to implement

1. Create a payment session

Request - POST /sessions

{
    "reference_id": "YOUR_PAYMENT_REFERENCE_ID",
    "session_type": "PAY",
    "mode": "CARDS_SESSION_JS",
    "amount": 10000,
    "currency": "IDR",
    "channel_properties": {
        "cards": {
            "skip_three_ds": true
        }
    },
    "country": "ID",
    "customer": {
        "reference_id": "YOUR_CUSTOMER_REFERENCE",
        "type": "INDIVIDUAL",
        "email": "test@yourdomain.com",
        "mobile_number": "+6212345678",
        "individual_detail": {
            "given_names": "Lorem",
            "surname": "Ipsum"
        }
    },
    "cards_session_js": {
        "success_return_url": "https://yourcompany.com/success",
        "failure_return_url": "https://yourcompany.com/failure"
    }
}

Response - POST /sessions

{
    "payment_session_id": "UNIQUE_PAYMENT_SESSION_ID",
    "created": "2024-11-28T05:07:14.585Z",
    "updated": "2024-11-28T05:07:14.585Z",
    "status": "ACTIVE",
    "reference_id": "new-uuid8",
    "currency": "IDR",
    "amount": 10000,
    "country": "ID",
    "customer_id": "cust-f945d691-2101-4582-890a-12afd211ea12",
    "expires_at": "2024-11-28T05:37:14.390Z",
    "session_type": "PAY",
    "mode": "CARDS_SESSION_JS",
    "locale": "en",
    "business_id": "62440e322008e87fb29c1fd0",
    "channel_properties": {
        "cards": {
            "skip_three_ds": true
        }
    },
    "cards_session_js": {
        "success_return_url": "https://yourcompany.com/success",
        "failure_return_url": "https://yourcompany.com/failure"
    }
}

2. Gather the card information

Similar to the authenticated flow, Initiate card_session.js on your page and use Xendit.payment.collectCardData(reqData, xenditResponseHandler) to collect the card details. This will collect the card information and send it to Xendit using  card_session.js.

Request - to card_session_js

{
    "card_number": "4000000000001091",
    "expiry_month": "12",
    "expiry_year": "2040",
    "cvn": "123",
    "cardholder_first_name": "firstName",
    "cardholder_last_name": "lastName",
    "cardholder_email": "shopper@emailaddress.com",
    "cardholder_phone_number": "+61111111111",
    "payment_session_id": "YOUR_PAYMENT_SESSION_ID"
}

Response - from card_session_js

{ 
    "message": "Status updated. Wait for a callback or get the status using the Get API.", 
    "payment_request_id": "PAYMENT_REQUEST_ID" 
}

Important: Make sure to store the payment_request_id as you’ll need it to retrieve the state of the transaction at a later stage.

3. Receive the webhook

Xendit will immediately send a payment webhook to your webhook endpoint with the final transaction status. You can match the payment webhook with the payment_request_id.

Example payment.capture webhook

{
    "created": "2024-12-18T05:46:35.109Z",
    "business_id": "62440e322008e87fb29c1fd0",
    "event": "payment.capture",
    "data": {
        "type": "PAY",
        "status": "SUCCEEDED",
        "country": "ID",
        "created": "2024-12-18T05:46:08.192Z",
        "updated": "2024-12-18T05:46:30.627Z",
        "captures": [
            {
                "capture_id": "cptr-08f17fa3-e80c-4d8e-8c34-17aa3400bc1c",
                "capture_amount": 10000,
                "capture_timestamp": "2024-12-18T05:46:34.234Z"
            }
        ],
        "currency": "IDR",
        "payment_id": "py-3f57d678-2448-4c9f-a433-8468d366fb5c",
        "business_id": "62440e322008e87fb29c1fd0",
        "customer_id": "cust-7de9a9b4-37e8-40ad-b665-d97f42e538c5",
        "channel_code": "CARDS",
        "reference_id": "97ba0a32-b996-4abf-8a7b-6184a6644676_b8d18f2f-3",
        "capture_method": "AUTOMATIC",
        "request_amount": 10000,
        "payment_details": {
            "authorization_data": {
                "reconciliation_id": "7345007929096981703954",
                "authorization_code": "831000",
                "acquirer_merchant_id": "xendit_ctv_agg",
                "network_response_code": "00",
                "network_transaction_id": "016153570198200",
                "cvn_verification_result": "M",
                "retrieval_reference_number": "435205253972",
                "address_verification_result": "M",
                "network_response_code_descriptor": "Approved and completed sucessfully"
            },
            "authentication_data": {
                "flow": "CHALLENGE",
                "a_res": {
                    "eci": "05",
                    "message_version": "2.1.0",
                    "authentication_value": "AAIBBYNoEwAAACcKhAJkdQAAAAA=","directory_server_trans_id": "e537f539-d59f-4ebe-8d56-7fdc31a8e9b4"
                }
            }
        },
        "payment_request_id": "pr-5593127f-8c7b-4d2f-b487-c785ffc21e2f"
    },
    "api_version": "v3"
}

As with the authenticated flow, you can save the payment_id and payment_details correlated with the payment_request_id as proof of payment.

4. Get the status of the transaction

If you prefer to poll for the transaction status rather than relying solely on webhooks, you can use the GET Payment Request endpoint with your payment_request_id.

Request: GET https://api.xendit.co/v3/payment_requests/YOUR_PAYMENT_REQUEST_ID

This will return the status of the transaction.

Response - from payment_requests

{
    "payment_request_id": "YOUR_PAYMENT_REQUEST_ID",
    "country": "ID",
    "request_amount": 10000,
    "currency": "IDR",
    "business_id": "62440e322008e87fb29c1fd0",
    "reference_id": "new-uuid8_0c2d367b-a",
    "customer_id": "cust-f945d691-2101-4582-890a-12afd211ea12",
    "created": "2024-11-28T05:07:26.741104Z",
    "updated": "2024-11-28T05:07:29.804025Z",
    "status": "SUCCEEDED",
    "actions": [],
    "channel_properties": {
        "skip_three_ds": true,
        "success_return_url": "https://yourcompany.com/success",
        "failure_return_url": "https://yourcompany.com/failure",
        "card_details": {
            "masked_card_number": "400000XXXXXX1091",
            "cardholder_first_name": "firstName",
            "cardholder_last_name": "lastName",
            "cardholder_email": "your_customers_email_address",
            "cardholder_phone_number": "+61111111111",
            "expiry_month": "12",
            "expiry_year": "2040",
            "fingerprint": "635a0be115cf90001ae83752",
            "type": "CREDIT",
            "network": "VISA",
            "country": "ID",
            "issuer": "PT BANK RAKYAT INDONESIA TBK"
        },
        "card_on_file_type": "CUSTOMER_UNSCHEDULED",
        "billing_information": {
            "city": null,
            "country": "",
            "postal_code": null,
            "province_state": null,
            "street_line1": null,
            "street_line2": null
        }
    },
    "channel_code": "CARDS",
    "type": "PAY",
    "capture_method": "AUTOMATIC",
    "latest_payment_id": "py-29fee9cc-bec8-489d-a1ca-7caec0aae2c9"
}