redx_courier 0.0.1
redx_courier: ^0.0.1 copied to clipboard
A Dart/Flutter client for the RedX (redx.com.bd) courier OpenAPI: create parcels, resolve delivery areas from a free-text address, and list your pickup stores (including the shop-ID vs pickup-store-ID [...]
redx_courier #
A Dart/Flutter client for the RedX courier OpenAPI
(https://openapi.redx.com.bd/v1.0.0-beta): create parcels, resolve a
delivery area from a free-text customer address, and list your pickup
stores.
Companion package to paperfly_courier.
Install #
dependencies:
redx_courier: ^0.0.1
Getting your access token #
Log in to your RedX merchant dashboard and grab your API access token from the Developer/API section. It's sent on every request as:
API-ACCESS-TOKEN: Bearer <your token>
Finding your pickup store ID (read this first) #
This is the single most common integration mistake with RedX, and it's why this package puts it front and center.
RedX has two different IDs that look interchangeable but aren't:
- Your shop ID - the number in your merchant dashboard URL, and also
the
shop_idfield baked into your access token's own JWT payload. - Your pickup store ID - a separate ID for each physical pickup
location registered under your shop, used for the
pickup_store_idfield when creating a parcel.
It's natural to assume they're the same thing, or to grab the shop ID out
of your JWT and use that as pickup_store_id - RedX's own docs don't call
this out. If you do, createParcel() will fail with:
{"message": "Cannot read property 'PHONE' of undefined"}
That error is RedX's backend trying to read a phone number off a pickup
store that doesn't exist under the ID you sent (because it was actually
your shop ID). Same symptom no matter what else you change in the
request - a different cash_collection_amount, phone formatting, or the
type field will not fix it.
The fix: always look up your real pickup store ID at runtime instead of hardcoding a number you found in a URL or a JWT:
final client = RedxClient(accessToken: 'YOUR_REDX_ACCESS_TOKEN');
final stores = await client.getPickupStores();
for (final store in stores) {
print('${store.name} -> id: ${store.id}, phone: ${store.phone}');
}
final pickupStoreId = stores.first.id; // use this, not your shop ID
You can also sanity-check a specific ID with getPickupStoreInfo() - it
returns null (instead of throwing) when the ID you passed turns out to be
a shop ID rather than a pickup store ID:
final store = await client.getPickupStoreInfo(1040634);
if (store == null) {
print('That ID is not a pickup store - probably your shop ID instead.');
}
Usage #
import 'package:redx_courier/redx_courier.dart';
Future<void> main() async {
final client = RedxClient(accessToken: 'YOUR_REDX_ACCESS_TOKEN');
final stores = await client.getPickupStores();
final pickupStoreId = stores.first.id;
// Resolves a free-text address to the right RedX delivery area, e.g.
// matching "House 5, Road 12, Gulshan 2" to an area named "Gulshan-2".
final area = await client.resolveAreaForAddress(
districtName: 'Dhaka',
customerAddress: 'House 5, Road 12, Gulshan 2, Dhaka',
);
if (area == null) {
// No confident match - fall back to client.getAreasByDistrict('Dhaka')
// and let the customer pick their area explicitly.
return;
}
final result = await client.createParcel(
RedxParcelRequest(
customerName: 'Test Customer',
customerPhone: normalizeBdPhoneForRedx('+8801712345678') ?? '',
customerAddress: 'House 5, Road 12, Gulshan 2, Dhaka',
areaId: area.id,
areaName: area.name,
merchantInvoiceId: 'INV-0001',
cashCollectionAmount: 500, // 0 for a prepaid order
parcelWeight: 1,
value: 500,
itemName: 'Sample Item',
pickupStoreId: pickupStoreId,
),
);
print('success: ${result.success}, tracking: ${result.trackingId}');
client.close();
}
See example/example.dart for the full runnable
version.
API #
RedxClient.getPickupStores()- list pickup stores under your shop.RedxClient.getPickupStoreInfo(int pickupStoreId)- look up one store;nullif the ID isn't a real pickup store.RedxClient.getAreasByDistrict(String districtName)- raw area lookup, with automatic retry under known alternate district spellings (Barisal/ Barishal, Bogra/Bogura, Chattogram/Chittagong, Comilla/Cumilla, Jashore/ Jessore).RedxClient.resolveAreaForAddress(...)- best-match area for a district- free-text address;
nullif nothing matches confidently.
- free-text address;
RedxClient.createParcel(RedxParcelRequest request)- create a parcel.RedxClient.trackParcel(String trackingId)- track a parcel.normalizeBdPhoneForRedx(String? phone)- normalizes a phone number to the local01XXXXXXXXXformat RedX expects (a+880/880-prefixed number sent as-is has also been observed to trigger the same "Cannot read property 'PHONE' of undefined" crash as the wrong pickup store ID, so normalize phone numbers too).
License #
MIT, see LICENSE.