PHPackages                             kejubayer/redx-api-integration - 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. [API Development](/categories/api)
4. /
5. kejubayer/redx-api-integration

ActiveLibrary[API Development](/categories/api)

kejubayer/redx-api-integration
==============================

Laravel package for integrating the RedX API and storing RedX webhook requests.

1.0.5(3w ago)011↓100%MITPHPPHP ^8.1

Since May 12Pushed 3w agoCompare

[ Source](https://github.com/kejubayer/redx-api-integration)[ Packagist](https://packagist.org/packages/kejubayer/redx-api-integration)[ RSS](/packages/kejubayer-redx-api-integration/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (5)Versions (7)Used By (0)

Laravel RedX API Integration Package
====================================

[](#laravel-redx-api-integration-package)

`kejubayer/redx-api-integration` is a Laravel package for RedX courier API integration in Bangladesh. It provides a simple RedX HTTP client, configurable API endpoints, a webhook route, and automatic storage of RedX webhook payloads in a database table.

Use this package to create RedX parcels, track parcels, cancel parcels, call configured RedX API endpoints, and receive RedX delivery status webhook updates in your Laravel application.

Features
--------

[](#features)

- Laravel auto-discovery support
- RedX API client using Laravel HTTP client
- Configurable RedX base URL, API token header, timeout, and endpoints
- Easy named methods for common RedX endpoints
- Generic endpoint-name helpers for all custom RedX endpoints
- Built-in RedX webhook route
- Stores every webhook request in `redx_webhook_requests`
- Extracts searchable webhook fields like tracking number, status, invoice number, and delivery type
- Optional webhook shared-secret protection
- Event dispatch after every stored webhook request
- Supports Laravel 10, Laravel 11, and Laravel 12

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- Laravel 10, 11, or 12
- Composer

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

[](#installation)

Install the package with Composer:

```
composer require kejubayer/redx-api-integration
```

Publish the configuration file:

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

Publish the migration:

```
php artisan vendor:publish --tag=redx-migrations
```

Run the migration:

```
php artisan migrate
```

Environment Variables
---------------------

[](#environment-variables)

Add your RedX API credentials to `.env`:

```
REDX_BASE_URL=https://openapi.redx.com.bd/v1.0.0-beta
REDX_API_TOKEN="Bearer your-redx-jwt-token"
REDX_TOKEN_HEADER=API-ACCESS-TOKEN
REDX_TIMEOUT=30

REDX_WEBHOOK_ENABLED=true
REDX_WEBHOOK_PATH=redx/webhook
```

Optional webhook secret protection:

```
REDX_WEBHOOK_SECRET=your-shared-secret
REDX_WEBHOOK_SECRET_HEADER=X-Redx-Webhook-Secret
```

RedX expects the token in this HTTP header:

```
API-ACCESS-TOKEN: Bearer your-redx-jwt-token
```

So `REDX_TOKEN_HEADER` is the header name, and `REDX_API_TOKEN` is the full header value.

Configuration
-------------

[](#configuration)

After publishing, the config file is available at:

```
config/redx-api-integration.php

```

Default endpoint configuration:

```
'endpoints' => [
    'create_parcel' => env('REDX_CREATE_PARCEL_ENDPOINT', '/parcel'),
    'track_parcel' => env('REDX_TRACK_PARCEL_ENDPOINT', '/parcel/track/{tracking_id}'),
    'parcel_details' => env('REDX_PARCEL_DETAILS_ENDPOINT', '/parcel/info/{tracking_id}'),
    'update_parcel' => env('REDX_UPDATE_PARCEL_ENDPOINT', '/parcels'),
    'areas' => env('REDX_AREAS_ENDPOINT', '/areas'),
    'create_pickup_store' => env('REDX_CREATE_PICKUP_STORE_ENDPOINT', '/pickup/store'),
    'pickup_stores' => env('REDX_PICKUP_STORES_ENDPOINT', '/pickup/stores'),
    'pickup_store_details' => env('REDX_PICKUP_STORE_DETAILS_ENDPOINT', '/pickup/store/info/{pickup_store_id}'),
    'charge_calculator' => env('REDX_CHARGE_CALCULATOR_ENDPOINT', '/charge/charge_calculator'),
],
```

If your RedX merchant account uses different endpoint paths, update the config or set these `.env` values:

```
REDX_CREATE_PARCEL_ENDPOINT=/parcel
REDX_TRACK_PARCEL_ENDPOINT=/parcel/track/{tracking_id}
REDX_PARCEL_DETAILS_ENDPOINT=/parcel/info/{tracking_id}
REDX_UPDATE_PARCEL_ENDPOINT=/parcels
REDX_AREAS_ENDPOINT=/areas
REDX_CREATE_PICKUP_STORE_ENDPOINT=/pickup/store
REDX_PICKUP_STORES_ENDPOINT=/pickup/stores
REDX_PICKUP_STORE_DETAILS_ENDPOINT=/pickup/store/info/{pickup_store_id}
REDX_CHARGE_CALCULATOR_ENDPOINT=/charge/charge_calculator
```

Basic Usage
-----------

[](#basic-usage)

Use the facade:

```
use Kejubayer\RedxApiIntegration\Facades\Redx;

$data = Redx::createParcel([
    'customer_name' => 'Customer Name',
    'customer_phone' => '01700000000',
    'delivery_area' => 'Dhaka',
    'delivery_area_id' => 1,
    'customer_address' => 'Dhaka, Bangladesh',
    'cash_collection_amount' => 1000,
    'parcel_weight' => 1,
    'value' => 1000,
]);
```

Use dependency injection:

```
use Kejubayer\RedxApiIntegration\RedxApiIntegration;

class ParcelController
{
    public function store(RedxApiIntegration $redx)
    {
        return $redx->createParcel([
            'customer_name' => 'Customer Name',
            'customer_phone' => '01700000000',
            'delivery_area' => 'Dhaka',
            'delivery_area_id' => 1,
            'customer_address' => 'Dhaka, Bangladesh',
            'cash_collection_amount' => 1000,
            'parcel_weight' => 1,
            'value' => 1000,
        ]);
    }
}
```

Available Methods
-----------------

[](#available-methods)

Quick method list:

MethodPurpose`Redx::createParcel($payload)`Create a RedX parcel`Redx::trackParcel($trackingId)`Track parcel by tracking number`Redx::parcelDetails($trackingId)`Get parcel details by tracking ID`Redx::updateParcel($trackingId, $propertyName, $newValue, $reason)`Update a parcel property`Redx::updateParcelRaw($payload)`Update a parcel with the full RedX payload`Redx::cancelParcel($trackingId, $reason)`Cancel parcel by tracking ID`Redx::areas($query)`Get RedX areas`Redx::areasByPostCode($postCode)`Get areas by postal code`Redx::areasByDistrictName($districtName)`Get areas by district name`Redx::createPickupStore($payload)`Create pickup store`Redx::pickupStores($query)`Get pickup stores`Redx::pickupStoreDetails($pickupStoreId)`Get pickup store details`Redx::calculateCharge($query)`Calculate parcel charge`Redx::getEndpoint($name, $replacements, $query)`Call a configured GET endpoint`Redx::postEndpoint($name, $payload, $replacements)`Call a configured POST endpoint`Redx::putEndpoint($name, $payload, $replacements)`Call a configured PUT endpoint`Redx::patchEndpoint($name, $payload, $replacements)`Call a configured PATCH endpoint`Redx::deleteEndpoint($name, $payload, $replacements)`Call a configured DELETE endpoint`Redx::callEndpoint($method, $name, $payload, $replacements)`Call any configured endpoint dynamically`Redx::endpoint($name, $replacements)`Resolve a configured endpoint path`Redx::get($uri, $query)`Raw GET request`Redx::post($uri, $payload)`Raw POST request`Redx::put($uri, $payload)`Raw PUT request`Redx::patch($uri, $payload)`Raw PATCH request`Redx::delete($uri, $payload)`Raw DELETE request`Redx::request()`Get the configured Laravel HTTP client`Redx::getResponse($uri, $query)`Raw GET request with Laravel response object`Redx::postResponse($uri, $payload)`Raw POST request with Laravel response object`Redx::putResponse($uri, $payload)`Raw PUT request with Laravel response object`Redx::patchResponse($uri, $payload)`Raw PATCH request with Laravel response object`Redx::deleteResponse($uri, $payload)`Raw DELETE request with Laravel response object### createParcel

[](#createparcel)

Create a new RedX parcel.

```
use Kejubayer\RedxApiIntegration\Facades\Redx;

$parcel = Redx::createParcel([
    'customer_name' => 'Customer Name',
    'customer_phone' => '01700000000',
    'delivery_area' => 'Dhaka',
    'delivery_area_id' => 1,
    'customer_address' => 'House 1, Road 2, Dhaka',
    'merchant_invoice_id' => 'INV-1001',
    'cash_collection_amount' => '1500',
    'parcel_weight' => 1,
    'instruction' => 'Handle with care',
    'value' => 1500,
    'is_closed_box' => false,
    'pickup_store_id' => 1,
    'parcel_details_json' => [
        [
            'name' => 'Product name',
            'category' => 'Product category',
            'value' => 1500,
        ],
    ],
]);
```

Required RedX create parcel fields usually include:

```
customer_name, customer_phone, delivery_area, delivery_area_id,
customer_address, cash_collection_amount, parcel_weight, value

```

Use `merchant_invoice_id` for your invoice number when creating a parcel. Webhook payloads may return that value as `invoice_number`.

### parcelDetails

[](#parceldetails)

Get RedX parcel details by tracking ID.

```
$parcel = Redx::parcelDetails('21A427TU4BN3R');
```

### trackParcel

[](#trackparcel)

Track a parcel by RedX tracking number.

```
$tracking = Redx::trackParcel('25A223SU17V6CH');
```

### updateParcel

[](#updateparcel)

Update a RedX parcel property.

```
$result = Redx::updateParcel(
    trackingId: '21A427TU4BN3R',
    propertyName: 'status',
    newValue: 'cancelled',
    reason: 'Customer cancelled the order'
);
```

Send the exact RedX update payload:

```
$result = Redx::updateParcelRaw([
    'entity_type' => 'parcel-tracking-id',
    'entity_id' => '21A427TU4BN3R',
    'update_details' => [
        'property_name' => 'status',
        'new_value' => 'cancelled',
        'reason' => 'Customer cancelled the order',
    ],
]);
```

### cancelParcel

[](#cancelparcel)

Cancel a parcel by tracking ID. This is a shortcut for `updateParcel($trackingId, 'status', 'cancelled', $reason)`.

```
$result = Redx::cancelParcel('21A427TU4BN3R', 'Customer cancelled the order');
```

### areas

[](#areas)

Get RedX delivery areas.

```
$areas = Redx::areas();
```

With query parameters:

```
$areas = Redx::areas([
    'district_name' => 'Dhaka',
]);
```

Get areas by post code:

```
$areas = Redx::areasByPostCode(1206);
```

Get areas by district name:

```
$areas = Redx::areasByDistrictName('Dhaka');
```

### createPickupStore

[](#createpickupstore)

Create a RedX pickup store.

```
$store = Redx::createPickupStore([
    'name' => 'Test Pickup Store',
    'phone' => '8801898000999',
    'address' => 'Test Address',
    'area_id' => 1,
]);
```

### pickupStores

[](#pickupstores)

Get RedX pickup stores.

```
$stores = Redx::pickupStores();
```

### pickupStoreDetails

[](#pickupstoredetails)

Get pickup store details.

```
$store = Redx::pickupStoreDetails(1);
```

### calculateCharge

[](#calculatecharge)

Calculate RedX parcel charge.

```
$charge = Redx::calculateCharge([
    'delivery_area_id' => 12,
    'pickup_area_id' => 1,
    'cash_collection_amount' => 1500,
    'weight' => 500,
]);
```

Easy Use For All Endpoints
--------------------------

[](#easy-use-for-all-endpoints)

You can add any RedX API endpoint to `config/redx-api-integration.php` and call it by name.

Example config:

```
'endpoints' => [
    'create_parcel' => '/parcel',
    'track_parcel' => '/parcel/track/{tracking_id}',
    'parcel_details' => '/parcel/info/{tracking_id}',
    'update_parcel' => '/parcels',
    'my_custom_endpoint' => '/merchant/custom/{id}',
],
```

Call a configured GET endpoint:

```
$data = Redx::getEndpoint(
    name: 'my_custom_endpoint',
    replacements: ['id' => 123],
    query: ['page' => 1]
);
```

Call a configured POST endpoint:

```
$data = Redx::postEndpoint(
    name: 'create_parcel',
    payload: [
        'customer_name' => 'Customer Name',
        'customer_phone' => '01700000000',
    ]
);
```

Call a configured PUT endpoint:

```
$data = Redx::putEndpoint(
    name: 'my_custom_endpoint',
    payload: ['status' => 'updated'],
    replacements: ['id' => 123]
);
```

Call a configured PATCH endpoint:

```
$data = Redx::patchEndpoint(
    name: 'my_custom_endpoint',
    payload: ['status' => 'updated'],
    replacements: ['id' => 123]
);
```

Call a configured DELETE endpoint:

```
$data = Redx::deleteEndpoint(
    name: 'my_custom_endpoint',
    payload: ['reason' => 'Not needed'],
    replacements: ['id' => 123]
);
```

Call any configured endpoint dynamically:

```
$data = Redx::callEndpoint(
    method: 'post',
    name: 'my_custom_endpoint',
    payload: ['key' => 'value'],
    replacements: ['id' => 123]
);
```

Resolve only the endpoint path:

```
$uri = Redx::endpoint('parcel_details', [
    'tracking_id' => '21A427TU4BN3R',
]);

// Result: /parcel/info/21A427TU4BN3R
```

### get

[](#get)

Call any RedX GET endpoint.

```
$data = Redx::get('/parcel/track/25A223SU17V6CH');
```

### post

[](#post)

Call any RedX POST endpoint.

```
$data = Redx::post('/parcel', [
    'customer_name' => 'Customer Name',
    'customer_phone' => '01700000000',
]);
```

### put

[](#put)

Call any RedX PUT endpoint.

```
$data = Redx::put('/parcel/12345', [
    'customer_address' => 'Updated address, Dhaka',
]);
```

### patch

[](#patch)

Call any RedX PATCH endpoint.

```
$data = Redx::patch('/parcel/12345', [
    'customer_address' => 'Updated address, Dhaka',
]);
```

### delete

[](#delete)

Call any RedX DELETE endpoint.

```
$data = Redx::delete('/parcel/12345');
```

With a request body:

```
$data = Redx::delete('/parcel/12345', [
    'reason' => 'Duplicate order',
]);
```

### request

[](#request)

Get the configured Laravel HTTP pending request instance.

```
$response = Redx::request()
    ->withHeaders(['X-Custom-Header' => 'value'])
    ->post('/custom-endpoint', [
        'key' => 'value',
    ]);
```

Webhook Route
-------------

[](#webhook-route)

The package registers this route by default:

```
POST /redx/webhook

```

You can change the route path:

```
REDX_WEBHOOK_PATH=api/redx/webhook
```

You can disable the route:

```
REDX_WEBHOOK_ENABLED=false
```

RedX Webhook Payload Structure
------------------------------

[](#redx-webhook-payload-structure)

Expected RedX webhook JSON payload:

```
{
    "tracking_number": "",
    "timestamp": "",
    "status": "",
    "message_en": "",
    "message_bn": "",
    "invoice_number": "",
    "delivery_type": ""
}
```

Example payload:

```
{
    "tracking_number": "25A223SU17V6CH",
    "timestamp": "2026-05-12 10:30:00",
    "status": "delivered",
    "message_en": "Parcel has been delivered",
    "message_bn": "Bangla delivery status message",
    "invoice_number": "INV-1001",
    "delivery_type": "regular"
}
```

Webhook Database Structure
--------------------------

[](#webhook-database-structure)

Webhook requests are stored in the `redx_webhook_requests` table.

ColumnTypeDescription`id`integerPrimary key`tracking_number`string, nullableRedX tracking number`redx_timestamp`timestamp, nullableTimestamp sent by RedX`status`string, nullableParcel status`message_en`string, nullableEnglish status message`message_bn`string, nullableBangla status message`invoice_number`string, nullableMerchant invoice number`delivery_type`string, nullableRedX delivery type`payload`jsonFull webhook payload`headers`json, nullableRequest headers`ip_address`string, nullableSender IP address`user_agent`string, nullableRequest user agent`signature`string, nullableSignature header value, when available`processed_at`timestamp, nullableProcessing timestamp for your application`created_at`timestampCreated timestamp`updated_at`timestampUpdated timestampWebhook Model
-------------

[](#webhook-model)

The default model is:

```
Kejubayer\RedxApiIntegration\Models\RedxWebhookRequest
```

Query stored webhooks:

```
use Kejubayer\RedxApiIntegration\Models\RedxWebhookRequest;

$latest = RedxWebhookRequest::query()
    ->latest()
    ->take(10)
    ->get();
```

Find webhook requests by tracking number:

```
$requests = RedxWebhookRequest::query()
    ->where('tracking_number', '25A223SU17V6CH')
    ->get();
```

Find delivered parcels:

```
$delivered = RedxWebhookRequest::query()
    ->where('status', 'delivered')
    ->get();
```

Mark a webhook request as processed:

```
$webhookRequest->markAsProcessed();
```

Use your own model:

```
'webhook_model' => App\Models\RedxWebhookRequest::class,
```

Your custom model should extend the package model or provide the same fillable columns.

Webhook Event
-------------

[](#webhook-event)

After storing a webhook request, the package dispatches:

```
Kejubayer\RedxApiIntegration\Events\RedxWebhookReceived
```

Listen for the event:

```
use Illuminate\Support\Facades\Event;
use Kejubayer\RedxApiIntegration\Events\RedxWebhookReceived;

Event::listen(RedxWebhookReceived::class, function (RedxWebhookReceived $event) {
    $webhookRequest = $event->webhookRequest;

    if ($webhookRequest->status === 'delivered') {
        // Update your order status here.
    }
});
```

Create a listener:

```
php artisan make:listener ProcessRedxWebhook
```

Example listener:

```
namespace App\Listeners;

use Kejubayer\RedxApiIntegration\Events\RedxWebhookReceived;

class ProcessRedxWebhook
{
    public function handle(RedxWebhookReceived $event): void
    {
        $webhookRequest = $event->webhookRequest;

        // Match your order by invoice number or tracking number.
        $invoiceNumber = $webhookRequest->invoice_number;
        $status = $webhookRequest->status;

        $webhookRequest->markAsProcessed();
    }
}
```

Secure Webhooks
---------------

[](#secure-webhooks)

Set a shared secret in your `.env`:

```
REDX_WEBHOOK_SECRET=your-shared-secret
REDX_WEBHOOK_SECRET_HEADER=X-Redx-Webhook-Secret
```

RedX must send the same secret value in the configured header. If the secret does not match, the package returns `403 Forbidden`.

Testing Webhook Locally
-----------------------

[](#testing-webhook-locally)

You can test the webhook route with curl:

```
curl -X POST "https://your-app.test/redx/webhook" \
  -H "Content-Type: application/json" \
  -d '{
    "tracking_number": "25A223SU17V6CH",
    "timestamp": "2026-05-12 10:30:00",
    "status": "delivered",
    "message_en": "Parcel has been delivered",
    "message_bn": "Bangla delivery status message",
    "invoice_number": "INV-1001",
    "delivery_type": "regular"
  }'
```

With webhook secret:

```
curl -X POST "https://your-app.test/redx/webhook" \
  -H "Content-Type: application/json" \
  -H "X-Redx-Webhook-Secret: your-shared-secret" \
  -d '{
    "tracking_number": "25A223SU17V6CH",
    "timestamp": "2026-05-12 10:30:00",
    "status": "delivered",
    "message_en": "Parcel has been delivered",
    "message_bn": "Bangla delivery status message",
    "invoice_number": "INV-1001",
    "delivery_type": "regular"
  }'
```

Raw Response Usage
------------------

[](#raw-response-usage)

Normal package methods return PHP arrays directly. If you need the raw Laravel `Illuminate\Http\Client\Response` object, use the `*Response` methods or `request()`.

```
$response = Redx::getResponse('/parcel/track/25A223SU17V6CH');

if ($response->successful()) {
    return $response->json();
}

if ($response->failed()) {
    report($response->body());
}

$response->throw();
```

Error Handling
--------------

[](#error-handling)

Because normal methods return arrays, RedX validation errors are available directly:

```
$data = Redx::createParcel($payload);

if (isset($data['validation_errors'])) {
    return $data['validation_errors'];
}
```

License
-------

[](#license)

The MIT License.

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance94

Actively maintained with recent releases

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

6

Last Release

27d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/6655d8864b90508fae52cbbe858c707383d8e63541ca477ba9d1790e63c729d6?d=identicon)[kejubayer](/maintainers/kejubayer)

---

Top Contributors

[![kejubayer](https://avatars.githubusercontent.com/u/47379243?v=4)](https://github.com/kejubayer "kejubayer (8 commits)")

### Embed Badge

![Health badge](/badges/kejubayer-redx-api-integration/health.svg)

```
[![Health](https://phpackages.com/badges/kejubayer-redx-api-integration/health.svg)](https://phpackages.com/packages/kejubayer-redx-api-integration)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k51.0M7.4k](/packages/larastan-larastan)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76318.2M110](/packages/laravel-mcp)[api-platform/laravel

API Platform support for Laravel

59156.3k10](/packages/api-platform-laravel)[simplestats-io/laravel-client

Analytics for Laravel. Track visitors, registrations, and payments. Discover which channels actually drive revenue, not just traffic. Server-side, GDPR compliant, ad-blocker proof.

5019.3k](/packages/simplestats-io-laravel-client)[fleetbase/core-api

Core Framework and Resources for Fleetbase API

1232.2k16](/packages/fleetbase-core-api)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
