PHPackages                             glennraya/xendivel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. glennraya/xendivel

ActiveLibrary[Payment Processing](/categories/payments)

glennraya/xendivel
==================

A Laravel package to easily integrate Xendit payment gateway. It supports credit and debit cards, and e-wallet payments and custom invoices, queued notifications, webhook listeners and more.

v2.1.1(5mo ago)412.3k↓35.7%12MITBladePHP ~8.2.0|~8.3.0|~8.4.0

Since Jan 15Pushed 3mo ago2 watchersCompare

[ Source](https://github.com/glennraya/xendivel)[ Packagist](https://packagist.org/packages/glennraya/xendivel)[ RSS](/packages/glennraya-xendivel/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (6)Dependencies (6)Versions (9)Used By (0)

[![Project Logo](artwork/xendivel.jpg)](artwork/xendivel.jpg)

Xendivel — A Laravel package for Xendit payment gateway
=======================================================

[](#xendivel--a-laravel-package-for-xendit-payment-gateway)

A Laravel package designed for the seamless integration of the [Xendit](https://xendit.co/) payment gateway into your Laravel-powered applications or websites. It facilitates payments through credit cards, debit cards, and eWallets. Additionally, the package provides support for custom invoicing, queued invoice or refund email notifications, webhook event listeners and verification.

Video Demo
----------

[](#video-demo)

I've created a short video demo to showcase the package's features. You can find it [here](https://youtu.be/oxdE3Y6viAE).

Roadmap
-------

[](#roadmap)

The following features, while not currently supported by the Xendivel, are planned for inclusion in upcoming updates.

- Direct Bank Debit
- Promotions (coupon/discount codes)
- Subscription services
- Real-time push notifications for payment status (Laravel Reverb)
- Disbursement APIs (for mass payment processing like employee payroll)
- PayLater
- QR Code payments

Table of Contents
-----------------

[](#table-of-contents)

1. [Features](#features)
2. [Pre-requisites](#pre-requisites)
3. [Installation](#installation)
4. [Initial Setup](#initial-setup)
    - [Xendit API keys](#xendit-api-keys)
    - [Configure Mail (Optional)](#configure-mail-optional)
    - [Queues (Optional)](#queues-optional)
    - [Publish Config and Assets](#publish-config-and-assets)
        - [Publish Individual Assets](#publish-individual-assets)
5. [Checkout Templates](#checkout-templates)
6. [Usage](#usage)
    - [Card Payments](#card-payments)
        - [Card Details Tokenization](#card-details-tokenization)
        - [Charge Credit Or Debit Cards](#charge-credit-or-debit-cards)
            - [External ID](#external-id)
        - [Get Card Charge Transaction](#get-card-charge-transaction)
        - [Multi-Use Card Token](#multi-use-card-token)
    - [eWallet Payments](#ewallet-payments)
        - [Charge eWallet](#charge-ewallet)
        - [Get eWallet Charge](#get-ewallet-charge)
        - [Void eWallet Charge](#void-ewallet-charge)
    - [PDF Invoicing](#pdf-invoicing)
        - [Generate PDF Invoice](#generate-pdf-invoice)
        - [Download PDF Invoice](#download-pdf-invoice)
        - [Paper Size](#invoice-paper-size)
        - [Change Invoice Paper Size](#change-invoice-paper-size)
        - [Change Invoice Orientation](#change-invoice-orientation)
        - [Invoice Filenaming](#invoice-filename)
        - [Customizing PDF Invoice](#customizing-pdf-invoice-template)
        - [Sending PDF Invoice As Email Attachment](#sending-pdf-invoice-as-email-attachment)
            - [Sending PDF Invoice for Card Payments](#sending-pdf-invoice-for-card-payments)
            - [Sending PDF Invoice for eWallet Payments](#sending-pdf-invoice-for-ewallet-payments)
        - [Queue Invoice Email](#queue-invoice-email)
    - [Refunds](#refunds)
        - [Refund for Card Payments](#refund-for-card-payments)
        - [Refund for ewallet Payments](#refund-for-ewallet-payments)
        - [Get Refund Details](#get-refund-details)
        - [List All eWallet Refunds](#list-all-ewallet-refunds)
        - [Email Refund Confirmation](#email-refund-confirmation)
    - [Webhook](#webhook)
        - [Listen to Webhook Event](#listen-to-webhook-event)
        - [Webhook Verification](#webhook-verification)
7. [Deploying to Production](#deploying-to-production)
8. [Tests](#tests)

Features
--------

[](#features)

- **Credit/Debit Cards** - Easily process payments through major credit or debit cards.
- **eWallet Payments** - Accepts a diverse range of eWallet payments based on your region (GCash, ShopeePay, PayMaya, GrabPay, etc.).
- **Custom Invoicing** - Provides built-in, highly customizable, and professional-looking invoice templates.
- **Queued Email Notifications** - Enables the use of markdown email templates and the option to schedule email notifications for background processing.
- **Webhooks** - Comes with built-in webhook event listeners from Xendit and ensures secure webhook verification.

### Pre-requisites

[](#pre-requisites)

- PHP 8.0 or higher
- Laravel 9 or higher
- Node 18
- NPM or Yarn

Installation
------------

[](#installation)

**Composer**

Xendivel utilizes Composer's package auto-discovery. All you need to do is to install Xendivel via composer and it will automatically register itself.

```
composer require glennraya/xendivel
```

**Installing Puppeteer**

Xendivel depends on Puppeteer for generating PDF invoices from HTML or Blade templates.

```
npm install puppeteer
```

Or, you could also install it globally:

```
npm install puppeteer --location=global
```

Initial Setup
-------------

[](#initial-setup)

### Xendit API Keys

[](#xendit-api-keys)

Prior to using Xendivel, it's essential to have a Xendit account with properly configured API keys. Activation of your Xendit account for production is not necessary to test Xendivel's features. Test mode will be automatically enabled upon signing up for a Xendit account. Obtain your API keys from the following URLs:

- Secret Key/Public Key:
- Webhook Verification Token:

Generate `Money-In` `secret key` with `read` and `write` permissions from your dashboard API keys section.

After you acquired all these keys, please make sure you include them to your Laravel's `.env` file:

```
XENDIT_SECRET_KEY=your-secret-key
XENDIT_PUBLIC_KEY=your-public-key
XENDIT_WEBHOOK_VERIFICATION_TOKEN=your-webhook-verification-token
```

### Configure Mail (Optional)

[](#configure-mail-optional)

Xendivel can send invoices to your customers as email attachments. To utilize this feature, ensure your [Laravel Mail](https://laravel.com/docs/10.x/mail#main-content) is correctly set up. Before Xendivel dispatches invoice or refund email notifications, ensure your mail credentials are filled in your `.env` file.

```
MAIL_MAILER=smtp
MAIL_HOST=your-mailer-host
MAIL_PORT=your-mailer-port
MAIL_USERNAME=your-mailer-username
MAIL_PASSWORD=your-mailer-password
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS="fromaddress@example.com"
MAIL_FROM_NAME="${APP_NAME}"
```

### Queues (Optional)

[](#queues-optional)

Xendivel facilitates the queueing of email processes for background execution. If you intend to employ queued emails for tasks such as invoicing or refund notifications, ensure that you have properly configured [Laravel Queues](https://laravel.com/docs/10.x/queues#main-content).

Then, make sure you have a queue worker running:

```
php artisan queue:work
```

Finally, please ensure that `queue_email` is set to `true` from your `.env` file:

```
'queue_email' => true,
```

Once you have successfully configured Laravel's queues and enabled `queue_email` to `true`, Xendivel is now capable of dispatching invoice or refund emails to the queue for background execution, enabling your app to respond to other requests or do other tasks without waiting for the jobs to finish. **This will improve overall user experience!**

### Publish Config and Assets

[](#publish-config-and-assets)

All assets and configuration file must be published to its proper directory for Xendivel to function properly:

```
php artisan vendor:publish --tag=xendivel
```

Executing this command will publish Xendivel's assets to the following directories:

- Config file - `config` directory.
- Invoice template - `resources/views/vendor/xendivel` directory.
- Email templates - `resources/views/vendor/xendivel/emails` directory.
- Blade checkout template - `resources/views/vendor/xendivel` directory.
- Webhook Event and Listener - `app/Events` and `app/Listeners` directory respectively.

#### Publish Individual Assets

[](#publish-individual-assets)

##### Configuration File

[](#configuration-file)

```
php artisan vendor:publish --tag=xendivel-config
```

##### Invoice Template

[](#invoice-template)

```
php artisan vendor:publish --tag=xendivel-invoice
```

##### Checkout (Blade)

[](#checkout-blade)

```
php artisan vendor:publish --tag=xendivel-checkout-blade
```

##### Checkout (ReactJS)

[](#checkout-reactjs)

```
php artisan vendor:publish --tag=xendivel-checkout-react
```

##### Checkout (ReactJS + TypeScript)

[](#checkout-reactjs--typescript)

```
php artisan vendor:publish --tag=xendivel-checkout-react-typescript
```

##### Webhook Event Listener

[](#webhook-event-listener)

```
php artisan vendor:publish --tag=xendivel-webhook-listener
```

Checkout Templates
------------------

[](#checkout-templates)

[![Checkout Template](docs/image_assets/checkout-template.png)](docs/image_assets/checkout-template.png)

Xendivel ships with a complete, fully working checkout template for cards and eWallet payments. The template include various variants such as **ReactJS component**, **ReactJS+TypeScript** component, and a regular **Blade** template and **VanillaJS**.

You can choose between the currently available template variants, you can even create your own.

### Blade Template

[](#blade-template)

We offer a standard Blade template for the checkout example, using VanillaJS. There's a built-in route allowing you to test this template at `/xendivel/checkout/blade`. You can access it through a URL like `https://your-domain.test/xendivel/checkout/blade`.

Note

When you run the command `php artisan vendor:publish --tag=xendivel` the checkout blade template will be on your `/resources/views/vendor/xendivel/checkout.blade.php` directory.

### ReactJS + TypeScript component

[](#reactjs--typescript-component)

Xendivel also have a checkout template component for **ReactJS** or **React+TypeScript** for those who are using front-end frameworks like React instead of regular Blade template.

```
php artisan vendor:publish --tag=xendivel-checkout-react-typescript

php artisan vendor:publish --tag=xendivel-checkout-react
```

These will be published under `/resources/js/vendor/xendivel/Checkout.tsx` for React+TypeScript or `/resources/js/vendor/xendivel/Checkout.jsx` for plain ReactJS.

Important

After publishing either one of these templates, please make sure you filled up the `public key` section on these React templates. Since this is a public key, it's perfectly safe to publish it directly on your templates.

```
// Set your 'public' key here.
Xendit.setPublishableKey(
    'your-public-key',
)
```

These templates demonstrate card tokenization, credit/debit card, and eWallet payments. They serve to guide your payment collection process for implementation in your front-end stack. Alternatively, use them as fully functional standalone templates if you wish.

Usage
-----

[](#usage)

### Card Payments

[](#card-payments)

#### Card Details Tokenization

[](#card-details-tokenization)

Xendit uses tokenization to securely handle credit or debit card details. This process involves using Xendit's JavaScript library to convert sensitive card information like the number, expiry date, and CVV into secure tokens before they are sent to your back-end. This method ensures that the actual card details are not transmitted, enhancing the safety and confidentiality of your customer's card information.

For more details, refer to Xendit's documentation below:

Xendivel offers easy-to-use templates in **ReactJS, React+TypeScript, and Blade**, providing ready-to-use checkout components for card/eWallet transactions. These templates offer a robust foundation for payment processing. For further information, see the [Checkout templates](#checkout-templates) section.

#### Charge Credit Or Debit Cards

[](#charge-credit-or-debit-cards)

The `Xendivel::payWithCard` function accepts the incoming request payload with the `token_id`, `amount`, and `authentication_id`:

Example Front-end POST Request Using Axios

```
axios.post('/pay-with-card', {
    amount: 1200,
    token_id: 'card-token', // From card tokenization process.
    authentication_id: 'auth-id', // From authentication process.
    // Additional optional parameters:

    // external_id: 'your-custom-external-id',

    // descriptor: "Merchant Business Name",

    // currency: 'PHP',

    // metadata: {
    //     store_owner: 'Glenn Raya',
    //     nationality: 'Filipino',
    //     product: 'MacBook Pro 16" M3 Pro',
    //     other_details: {
    //         purpose: 'Work laptop',
    //         issuer: 'Xendivel LTD',
    //         manufacturer: 'Apple',
    //         color: 'Silver'
    //     }
    // }

    // billing_details: {
    //     given_names: 'Glenn',
    //     surname: 'Raya',
    //     email: 'glenn@example.com',
    //     mobile_number: '+639171234567',
    //     phone_number: '+63476221234',
    //     address:{
    //         street_line1: 'Ivory St. Greenfield Subd.',
    //         street_line2: 'Brgy. Coastal Ridge',
    //         city: 'Balanga City',
    //         province_state: 'Bataan',
    //         postal_code: '2100',
    //         country: 'PH'
    //     }
    // },
})
// ...
```

Then, in your Laravel route/controller

`POST` Request:

```
use GlennRaya\Xendivel\Xendivel;

Route::post('/pay-with-card', function (Request $request) {
    $payment = Xendivel::payWithCard($request)
        ->getResponse();

    return $payment;
});
```

The `getResponse()` function ensures that you get a JSON response:

```
{
  "status": "CAPTURED",
  "authorized_amount": 5198,
  "capture_amount": 5198,
  "currency": "PHP",
  "metadata": {},
  "credit_card_token_id": "656ed874edab5300169c3092",
  "business_id": "6551f678273a62fd8d86e25a",
  "merchant_id": "104019905",
  "merchant_reference_code": "656ed874edab5300169c3091",
  "external_id": "43565633-dd58-47ae-bbe6-648f78d6652c",
  "eci": "02",
  "charge_type": "SINGLE_USE_TOKEN",
  "masked_card_number": "520000XXXXXX1005",
  "card_brand": "MASTERCARD",
  "card_type": "CREDIT",
  "ucaf": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
  "descriptor": "XDT*JSON FAKERY",
  "authorization_id": "656ed87c23f3c20015e2fb95",
  "bank_reconciliation_id": "7017631974056110603955",
  "issuing_bank_name": "PT BANK NEGARA INDONESIA TBK",
  "cvn_code": "M",
  "approval_code": "831000",
  "created": "2023-12-05T07:59:58.453Z",
  "id": "656ed87e23f3c20015e2fb96",
  "card_fingerprint": "61d6ed632aa321002350e0b2"
}
```

Xendit accepts optional parameters such as **`billing_details`**, **`metadata`**, **`external_id`**, **`currency`**, and **`descriptor`** as demonstrated in the Axios request above. Please refer to Xendit's documentation to learn more about these parameters:

> You can also forward an invoice in PDF format as an email attachment to your customer's email address. Details about this process are covered in the [PDF Invoicing](#pdf-invoicing) section.

##### External ID

[](#external-id)

Xendit requires the inclusion of an `external_id` parameter in each credit/debit card charge. By default, Xendivel simplifies this process by generating a unique external ID using Ordered UUID v4 automatically for you.

Nevertheless, if you choose to create your own `external_id` for some reason, you can achieve this by setting the `auto_id` option in the `xendivel.php` config file to `false`.

Config file: `config/xendivel.php`

```
 'auto_id' => false,
```

Subsequently, ensure that you manually provide your custom `external_id` for each card charge request.

```
axios.post('/pay-with-card', {
    amount: 1200,
    token_id: 'card-token', // From card tokenization process.
    authentication_id: 'auth-id', // From authentication process.
+   external_id: 'your-custom-external-id', // Provide your own external id.
})
```

#### Get Card Charge Transaction

[](#get-card-charge-transaction)

To retrieve the details of the card charge object, you must provide the `id` of the card charge (which should come from your database or your Xendit dashboard) as the first parameter, and the string `card` as the second parameter.

`GET` Request:

```
use GlennRaya\Xendivel\Xendivel;

Route::get('/payment', function () {
    // card charge id example: 659518586a863f003659b718
    $response = Xendivel::getPayment('card-charge-id', 'card')
        ->getResponse();

    return $response;
});
```

This endpoint will return a JSON response that shows important details like the `status` of the card charge, `charge_type`, `card_type`, `card_brand`, etc.

```
{
  "created": "2020-01-08T04:49:08.815Z",
  "status": "CAPTURED",
  "business_id": "5848fdf860053555135587e7",
  "authorized_amount": 10000,
  "external_id": "test-pre-auth",
  "merchant_id": "xendit",
  "merchant_reference_code": "598942aabb91a4ec309e9a35",
  "card_type": "CREDIT",
  "masked_card_number": "400000XXXXXX0002",
  "charge_type": "SINGLE_USE_TOKEN",
  "card_brand": "VISA",
  "bank_reconciliation_id": "5132390610356134503009",
  "capture_amount": 9900,
  "descriptor": "My new store",
  "id": "659518586a863f003659b718"
}
```

#### Multi-Use Card Token

[](#multi-use-card-token)

It's a common practice in e-commerce platforms to offer customers the convenience of saving their credit/debit card details for future use, eliminating the need for repetitive data entry during subsequent payments.

This functionality is achieved through the card tokenization process. If you've examined the [checkout templates](#checkout-templates) included with Xendivel, you'll find that this process has already been implemented for you.

Example JSON response for multi-use card token:

```
{
  "status": "CAPTURED",
  "authorized_amount": 5198,
  "capture_amount": 5198,
  "currency": "PHP",
  "metadata": {},
  "credit_card_token_id": "65715e52689dc6001715bc57",
  "business_id": "6551f678273a62fd8d86e25a",
  "merchant_id": "104019905",
  "merchant_reference_code": "65715e530e502a00161aa2d9",
  "external_id": "f4270ddb-650d-4973-8786-1f5b4c048c76",
  "eci": "02",
  "charge_type": "MULTIPLE_USE_TOKEN",
  "masked_card_number": "520000XXXXXX1005",
  "card_brand": "MASTERCARD",
  "card_type": "CREDIT",
  "ucaf": "AJkBBkhgQQAAAE4gSEJydQAAAAA=",
  "descriptor": "XDT*JSON FAKERY",
  "authorization_id": "65715e5d689dc6001715bc5b",
  "bank_reconciliation_id": "7019285426096226603954",
  "issuing_bank_name": "PT BANK NEGARA INDONESIA TBK",
  "cvn_code": "M",
  "approval_code": "831000",
  "created": "2023-12-07T05:55:43.603Z",
  "id": "65715e5f689dc6001715bc60",
  "card_fingerprint": "61d6ed632aa321002350e0b2"
}
```

> \[!IMPORTANT \] When `charge_type` is `MULTIPLE_USE_TOKEN`, you should make sure that you save the `credit_card_token_id` to your database. You will use this token to charge the card again in the future without re-entering the card details again using the same endpoint use the initially charge the card.

### eWallet Payments

[](#ewallet-payments)

Xendivel is compatible with all eWallet payment channels supported by Xendit. For further details, refer to the documentation at , and explore Xendit's API reference at .

#### Charge eWallet

[](#charge-ewallet)

Example Axios post request:

```
axios
    .post('/pay-via-ewallet', {
        // You can test different failure scenarios by using the 'magic amount' from Xendit.
        amount: parseInt(amount),
        currency: 'PHP',
        checkout_method: 'ONE_TIME_PAYMENT',
        channel_code: 'PH_GCASH',
        channel_properties: {
            success_redirect_url:
                'https://your-domain.test/ewallet/success',
            failure_redirect_url: 'https://your-domain.test/ewallet/failed',
        },
    })
    .then(response => {
        // Upon successful request, you will be redirected to the eWallet's checkout url.
        console.log(response.data)
        window.location.href =
            response.data.actions.desktop_web_checkout_url
    })
    /// ...
```

Then, on your Laravel route or controller:

`POST` Request:

```
use GlennRaya\Xendivel\Xendivel;

Route::post('/pay-via-ewallet', function (Request $request) {
    $response = Xendivel::payWithEwallet($request)
        ->getResponse();

    return $response;
});
```

In the example Axios request above you will be redirected to the eWallet payment provider's checkout page to complete the payment authorization there. If you are on development mode, you will see something like this:

[![eWallet Payment Authorization Page](docs/image_assets/ewallet-authorization.png)](docs/image_assets/ewallet-authorization.png)

The resulting JSON response would look like this:

```
{
    "created": "2023-12-09T07:51:17.926Z",
    "business_id": "6551f678273a62fd8d86e25a",
    "event": "ewallet.capture",
    "data": {
        "id": "ewc_5b2ad2c6-11a3-410a-b5ab-b41d16e39879",
        "basket": null,
        "status": "SUCCEEDED",
        "actions": {
            "qr_checkout_string": null,
            "mobile_web_checkout_url": "https://ewallet-mock-connector.xendit.co/v1/ewallet_connector/checkouts?token=clq1oqg032dn7a8hko1g",
            "desktop_web_checkout_url": "https://ewallet-mock-connector.xendit.co/v1/ewallet_connector/checkouts?token=clq1oqg032dn7a8hko1g",
            "mobile_deeplink_checkout_url": null
        },
        "created": "2023-12-09T07:51:06.63582Z",
        "updated": "2023-12-09T07:51:17.780894Z",
        "currency": "PHP",
        "customer": null,
        "metadata": null,
        "voided_at": null,
        "capture_now": true,
        "customer_id": null,
        "void_status": null,
        "callback_url": "https://pktuw9nrxn.sharedwithexpose.com/xendit/webhook",
        "channel_code": "PH_GCASH",
        "failure_code": null,
        "reference_id": "90c0c5f5-c6f0-4f2e-bf6c-f23763911f8a",
        "charge_amount": 1000,
        "capture_amount": 1000,
        "checkout_method": "ONE_TIME_PAYMENT",
        "refunded_amount": null,
        "payment_method_id": null,
        "channel_properties": {
            "failure_redirect_url": "https://package.test/ewallet/failed",
            "success_redirect_url": "https://package.test/ewallet/success"
        },
        "is_redirect_required": true,
        "payer_charged_amount": null,
        "shipping_information": null,
        "payer_charged_currency": null
    },
    "api_version": null
}
```

Upon the successful completion of the payment, you will be redirected to the designated success or failure page URL as specified in your axios request parameters (`success_redirect_url` or `failure_redirect_url`).

#### eWallet Charge Reference ID

[](#ewallet-charge-reference-id)

Like the card charge, Xendit requires the inclusion of `reference_id` on the eWallet charge payload. Xendivel also handles this for you automatically by including Ordered UUID V4 on each payload upon request.

If you wish to add your own implementation for `reference_id`, like in the card payment, set the `auto_id` to `false` from your config file:

Config file: `config/xendivel.php`

```
 'auto_id' => false,
```

And make sure you provide your own `reference_id` for every eWallet charge request:

```
axios
    .post('/pay-via-ewallet', {
        // You can test different failure scenarios by using the 'magic amount' from Xendit.
        reference_id: 'your-own-reference-id',
        amount: parseInt(amount),
        currency: 'PHP',
        // Other params...
    })
    .then(response => {
        // Upon successful request, you will be redirected to the eWallet's checkout url.
        console.log(response.data)
        window.location.href =
            response.data.actions.desktop_web_checkout_url
    })
    /// ...
```

#### Responding to eWallet Charge Webhook Event

[](#responding-to-ewallet-charge-webhook-event)

Before your app can receive webhook callbacks from Xendit. Please make sure that you properly setup a webhook endpoint from your Xendit's dashboard under **eWallet Payment Status**:

This is required for both development and production. For this purpose you can use tools like [Ngrok](https://ngrok.com) or [Expose](https://expose.dev) so your local project (`localhost`) can receive webhook callbacks from Xendit.

By default, Xendivel will listen to `xendit/webhook` URL for callbacks as defined in Xendivel's config file whenever you make an eWallet charge, refund, or void transactions. You have the option to change the default webhook URL if you prefer:

`config/xendivel.php`

```
'webhook_url' => '/xendit/webhook', // You can change this to whatever you like.
```

Then, after you published Xendivel's webhook event listeners from [here](#publish-config-and-assets), you should register the events and listener to your event service provider located in `app\Providers\EventServiceProvider.php`:

```
use App\Events\eWalletEvents;
use App\Listeners\eWalletWebhookListener;

protected $listen = [
    // ...

    eWalletEvents::class => [
        eWalletWebhookListener::class,
    ],
];
```

After this, you can now respond to the callback event from Xendit after a successful eWallet charge from the webhook listener located in `app/Listener/eWalletWebhookListener.php`:

```
public function handle(eWalletEvents $event)
{
    // You can inspect the returned data from the webhoook in your logs file
    // storage/logs/laravel.log
    logger('Webhook data received: ', $event->webhook_data);

    // if($event->webhook_data['data']['status'] === 'SUCCEEDED') {
    //     $invoice_data = [
    //         // Invoice data...
    //     ];

    //     $email_invoice = new Xendivel();
    //     $email_invoice->emailInvoiceTo('glenn@example.com', $invoice_data)
    //         ->send();
    // }
}
```

You can now perform other tasks based on the payload of the callback such as interacting with your database, call other APIs, send an email, etc.

Important

Xendit will send a webhook event everytime you perform an eWallet charge, refund, or void transaction to the same webhook endpoint.

#### Exclude Xendit's Webhook Callback from CSRF Protection

[](#exclude-xendits-webhook-callback-from-csrf-protection)

You should also make sure you allow Xendit's callback from your CSRF protection, so any webhook callback Xendit sends to your application will be accepted by your routes. You can exclude the routes by adding their URIs to the `$except` property of the `VerifyCsrfToken` middleware:

```
