Create a Checkout Page via API

In this quickstart guide, you will learn how to create and pay into an Invoice created through the Invoice API. Use the Invoice API to quickly create a Checkout page without having to code your own. This guide is useful for use cases such as building an E-Commerce website with a checkout option.

This example is in Test mode and will not collect an actual payment.

Before you begin

  1. Register for a Xendit account by visiting our dashboard
  2. Generate a Secret API Key for development in Test mode here or by visiting Settings > Developers > API Keys on the dashboard
  3. Make sure to set "Write" permissions to your API Key for Money-In products. For more details, please visit this guide

1. Create an Invoice by making a POST request to the Invoice API

This example uses the Axios library to perform a POST request to Xendit in JavaScript by defining a createInvoice() function.

In this example, the customer has made a fast food purchase with delivery. Each item the customer has ordered together with the delivery fees is specified in this request. This is an ideal user experience since Xendit can display these items on the Checkout page right before your customer completes payment.

This example also specifies the customer's email and phone number to allow notifications to be sent to both these channels upon successful payment.

import axios from 'axios';

const authToken = Buffer.from('xnd_development_xxxx:').toString('base64');

async function createInvoice() {
    try {
        const { data, status } = await axios.post('https://api.xendit.co/v2/invoices',
            {
                external_id: 'xendit_test_id_1',
                amount: 110000,
                currency: 'IDR',
                customer: {
                    given_names: 'Ahmad',
                    surname: 'Gunawan',
                    email: 'ahmad_gunawan@example.com',
                    mobile_number: '+6287774441111',
                },
                customer_notification_preference: {
                    invoice_paid: ['email', 'whatsapp']
                },
                success_redirect_url: 'example.com/success',
                failure_redirect_url: 'example.com/failure',
                items: [
                    {
                        name: 'Double Cheeseburger',
                        quantity: 1,
                        price: 7000,
                        category: 'Fast Food'
                    },
                    {
                        name: 'Chocolate Sundae',
                        quantity: 1,
                        price: 3000,
                        category: 'Fast Food'
                    }
                ],
                fees: [
                    {
                        type: "Delivery",
                        value: 10000
                    }
                ]
            },
            {
                headers: {
                    'Authorization': `Basic ${authToken}`
                }
            }
        )
}

The table below gives an explanation of each parameter in the request and the reason for specifying it. Make sure to replace the secret key in the example with your own secret key.

ParameterRequiredDescription
external_idYesA unique ID defined by you that you can use to identify each Invoice
amountYesThe total amount that will be accepted as payment by Xendit
currencyYesCurrency of the payment to be accepted. In this case, we will use IDR
customer.given_namesNoThe given name of the customer. Use this field if you would like to send notifications to the customer
customer.surnameNoThe surname of the customer
customer.emailNoThe email to which the customer will receive a confirmation of their payment
customer.mobile_numberNoThe phone number to which the customer will receive a confirmation of their payment
customer_notification_preference.invoice_paidNoChannels to which notifications will be sent to the customer. In this example, we will send both Email and WhatsApp notifications
success_redirect_urlNoA page to redirect to after successful payment. This page should be built and hosted by you
failure_redirect_urlNoA page to redirect to after failed payment. This page should be built and hosted by you
items.nameNoName of the item the customer has purchased. This will appear on the checkout page.
items.quantityNoQuantity of the item the customer has purchased. This will appear on the checkout screen.
items.priceNoPrice of the item the customer has purchased. This, together with the value of the fees, should add up to the Invoice amount value.
items.categoryNoCategory, defined by you, of which the item belongs to. In this example, we have defined this as fast food.
fees.typeNoType of fees to be collected as part of this invoice. Use this if you would like to collect an additional shipping or admin fee.
fees.valueNoAmount of the fees to be charged to the customer

After successful Invoice creation, Xendit responds to you with multiple parameters one of which is the Invoice URL. To view this, you can log the url parameter in the console.

console.log(`Response returned with a status of ${status}`);

const { invoice_url } = data;

console.log(`Invoice created! Visit ${invoice_url} to complete payment`)

For an E-Commerce use case, you should redirect the user to this page after Invoice creation. Putting it all together, your final script should look like this:

import axios from 'axios';

const authToken = Buffer.from('xnd_development_xxxx:').toString('base64');

async function createInvoice() {
    try {
        const { data, status } = await axios.post('https://api.xendit.co/v2/invoices',
            {
                external_id: 'xendit_test_id_1',
                amount: 110000,
                currency: 'IDR',
                customer: {
                    given_names: 'Ahmad',
                    surname: 'Gunawan',
                    email: 'ahmad_gunawan@example.com',
                    mobile_number: '+6287774441111',
                },
                customer_notification_preference: {
                    invoice_paid: ['email', 'whatsapp']
                },
                success_redirect_url: 'example.com/success',
                failure_redirect_url: 'example.com/failure',
                items: [
                    {
                        name: 'Double Cheeseburger',
                        quantity: 1,
                        price: 7000,
                        category: 'Fast Food'
                    },
                    {
                        name: 'Chocolate Sundae',
                        quantity: 1,
                        price: 3000,
                        category: 'Fast Food'
                    }
                ],
                fees: [
                    {
                        type: "Shipping",
                        value: 10000
                    }
                ]
            },
            {
                headers: {
                    'Authorization': `Basic ${authToken}`
                }
            }
        )

        console.log(`Response returned with a status of ${status}`);
        
        const { invoice_url } = data;

        console.log(`Invoice created! Visit ${invoice_url} to complete payment`)
    } catch (error) {
        console.log("Request failed")
    }
}

createInvoice()

After running this code (you may use NodeJS or Express to do this), you should see the Checkout page's URL being logged after successful Invoice creation.

> Response returned with a status of 200
> Invoice created! Visit https://checkout-staging.xendit.co/web/XXXXc3844c0c9828d96006a to complete payment

2. Handle the Callback for Successful Payment

Once the Invoice is paid by the customer, Xendit will send you a callback notifying successful payment. In this step, you will define a function to handle this callback sent from Xendit.

In the script below, a simple express server serves a POST endpoint /receive_callback. This is the endpoint Xendit will send the callback to.

app.post('/receive_callback', async(req, res) => {
    const { body } = req;
    if (body.status === 'PAID') {
        console.log(`Invoice successfully paid with status ${body.status} and id ${body.id}`)
    }
    res.sendStatus(200).end()
})
ParameterAlways PresentDescription
statusYesStatus of the payment. Will equal "PAID" when payment is successful
idYesID of the Invoice. Can be used for Refunds via dashboard or to uniquely identify each invoice

For a detailed list of parameters provided in the Invoice callback, please visit our API reference page. This page also provides additional details on how to handle failed payment callbacks.

3. Set the Callback URL on the Xendit Dashboard

Now that you have defined your callback handler, it is time to set the callback URL on Xendit's Dashboard. Ideally, this URL would lead to a production deployment of your server. For this example, it is possible to use the free tool ngrok to publicly host a localhost server.

> ngrok http 3000

Session Status                online                                                                                                                                                                      
Account                       abc@gmail.com (Plan: Free)                                                                                                                                            
Update                        update available (version 3.3.0-beta, Ctrl-U to update)                                                                                                                     
Version                       3.0.4                                                                                                                                                                       
Region                        Asia Pacific (ap)                                                                                                                                                           
Latency                       22ms                                                                                                                                                                        
Web Interface                 http://127.0.0.1:4040                                                                                                                                                       
Forwarding                    https://XXXX-121-6-44-41.ngrok-free.app -> http://localhost:3000     

You can then set the callback URL (https://XXXX-121-6-44-41.ngrok-free.app in this case) on the Xendit Dashboard. Click here and navigate to Invoices > Invoices Paid to Test and Save this URL.

4. Simulate a payment made to your Invoice

Open the Invoice link you previously created. This will lead you to Xendit's checkout page.

In the staging environment, you're able to select any payment method and simulate payment by clicking the link in the red header.

Let's select OVO and simulate a payment by clicking the link in the red banner.

You've now successfully paid your invoice!

5. Verify that Callback was received

Now, if you go back to the server that you've been running (and perhaps hosting through ngrok), you should see a console log indicating a successful payment.

> Invoice successfully paid with status PAID and id 64759740b3adfe825417816a

6. Set success and failure URLs (Optional)

Xendit is able to redirect your customer to a success or failure page you've created on your website after successful payment or in the event of a failed payment. For this example, the following lines of code in Express will be a simple demonstration.

app.get('/success', async(req, res) => {
    res.status(200).send('Success!')
})

app.get('/failure', async(req, res) => {
    res.status(200).send('Failed!')
})

After this, you can provide these URLs in our Invoice creation POST request. Since this example is hosted through ngrok, the URLs have been updated to the ngrok domain that was assigned. Do note that only the relevant part of the request has been included in this example.

...
success_redirect_url: 'https://XXXX-121-6-44-41.ngrok-free.app/success',
failure_redirect_url: 'https://XXXX-121-6-44-41.ngrok-free.app/failure',
...

Wrapping Up

You've managed to successfully integrate to Xendit by creating a Checkout page and simulating payment. You've also managed to verify successful payment by verifying a successful callback sent by Xendit.

Feel free to explore the following for further reading,

Last Updated on 2023-06-16