refatbd / bd-courier-fraud-checker
Check for fraudulent customers using Bangladeshi courier data.
Package info
github.com/refatbd/bd-courier-fraud-checker
pkg:composer/refatbd/bd-courier-fraud-checker
Requires
- php: ^7.4|^8.0|^8.1|^8.2|^8.3
- ext-openssl: *
README
Maintained with โค๏ธ by refatbd
A Laravel package that checks a Bangladeshi phone number against the data of four major couriers โ Steadfast, Pathao, RedX, and Carrybee โ and tells you, in one call, how risky that customer is before you confirm a Cash-on-Delivery order.
๐ 01XXXXXXXXX โโโโโโถ ๐ BdCourierFraudChecker โโโโโโถ ๐ Success rate ยท Fraud signals ยท Complaints
๐ Table of Contents
- โจ Features
- ๐ฆ Installation
- โ๏ธ Configuration
- ๐ Usage
- ๐งพ Response Format
- ๐ Supported Couriers
- ๐ง How the Fraud Signal Works
- โ Adding a New Courier
- โ FAQ
- ๐ Changelog
- ๐ License
โจ Features
| Feature | Description | |
|---|---|---|
| ๐ | One call, four couriers | A single check() queries Steadfast, Pathao, RedX & Carrybee. |
| ๐ | Delivery success rate | Delivered / cancelled / total + auto-calculated percentages. |
| ๐จ | Detailed complaints | Steadfast returns the full complaint list โ name, details, date & image. |
| ๐ท๏ธ | Fraud labels | Pathao rating, RedX segment, Carrybee complaint count. |
| โก | Smart caching | Auth tokens/cookies cached for ~50 min โ fewer logins, faster checks. |
| ๐ก๏ธ | Resilient | In-call re-auth on expired sessions, request timeouts, browser-like headers, graceful failures, BD phone validation. |
| ๐งฉ | Extensible | Drop in a new courier class and wire it up in minutes. |
๐ฆ Installation
composer require refatbd/bd-courier-fraud-checker
Publish the config file:
php artisan vendor:publish --tag=bdcourierfraudchecker-config
โ๏ธ Configuration
Add your courier merchant credentials to your .env file:
# ๐ฆ Steadfast STEADFAST_USER=your_email@example.com STEADFAST_PASSWORD=your_password # ๐ฅ Pathao PATHAO_USER=your_email@example.com PATHAO_PASSWORD=your_password # ๐ง RedX REDX_PHONE=01XXXXXXXXX REDX_PASSWORD=your_password # ๐จ Carrybee CARRYBEE_PHONE=01XXXXXXXXX CARRYBEE_PASSWORD=your_password
๐ก You only need to configure the couriers you actually use. A courier with missing credentials simply returns
status => falseinstead of breaking the whole check.
๐ Usage
use Refatbd\BdCourierFraudChecker\Facade\BdCourierFraudChecker; $result = BdCourierFraudChecker::check('01XXXXXXXXX');
That's it โ $result is an array keyed by courier. Loop over it, render it, or feed it into your own risk score.
๐งพ Response Format
[
'steadfast' => [
'status' => true,
'message' => 'Successful.',
'data' => [
'success' => 45,
'cancel' => 5,
'total' => 50,
'deliveredPercentage' => 90.0,
'returnPercentage' => 10.0,
'fraudReportCount' => 1,
'frauds' => [
[
'name' => 'Saiyan Ahammd Santo',
'phone' => '01893048178',
'details' => 'เฆชเฆพเฆฐเงเฆธเงเฆฒ เฆฐเฆฟเฆธเฆฟเฆญ เฆเฆฐเงเฆจเฆพเฅค',
'image' => null,
'consignment_id' => 124581452,
'created_at' => '2025-02-11T14:43:02.000000Z',
'created_at_human' => '1 year ago',
],
],
],
],
'pathao' => [
'status' => true,
'message' => 'Successful.',
'data' => [
// Pathao has moved to a rating-based model โ most accounts no longer
// receive numeric counts (showCount: false). See the note below.
'success' => null,
'cancel' => null,
'total' => null,
'deliveredPercentage' => null,
'returnPercentage' => null,
'customerRating' => 'excellent_customer', // Pathao's own label
'riskLevel' => 'low', // derived: low | medium | high
'showCount' => false, // did Pathao expose counts?
'countsAvailable' => false, // numeric data usable?
],
],
'redx' => [
'status' => true,
'message' => 'Successful.',
'data' => [
'success' => 30,
'cancel' => 5,
'total' => 35,
'deliveredPercentage' => 85.71,
'returnPercentage' => 14.29,
'customerSegment' => 'Normal Customer', // RedX's own rating label
],
],
'carrybee' => [
'status' => true,
'message' => 'Successful.',
'data' => [
'success' => 18,
'cancel' => 2,
'total' => 20,
'deliveredPercentage' => 90.0,
'returnPercentage' => 10.0,
'fraudCount' => 0, // Carrybee's own complaint counter
],
],
]
โ ๏ธ Always check
statusbefore readingdata. When a courier fails (auth error, no data, etc.) it returns['status' => false, 'message' => '...']with nodatakey.
๐ฅ Pathao counts may be
null. Pathao migrated to a rating-based model, so most merchant accounts receive no numeric delivery counts โshowCountandcountsAvailablearefalse, and the count fields arenull(not0, to avoid implying a customer with zero orders). The package still returns the full numeric breakdown for any account that is entitled to counts (countsAvailable: true). Guard oncountsAvailablebefore doing math on Pathao counts. Steadfast, RedX, and Carrybee continue to return real numeric counts.
๐ Supported Couriers
| Courier | Status | Delivery Stats | Fraud Signal |
|---|---|---|---|
| Steadfast | โ | โ | โ
Full complaint list โ frauds[] (name ยท details ยท date ยท image) |
| Pathao | โ | โ ๏ธ Rating-basedยน | ๐ท๏ธ Rating + risk โ customerRating, riskLevel |
| RedX | โ | โ | ๐ท๏ธ Segment label โ customerSegment |
| Carrybee | โ | โ | ๐ข Complaint count โ fraudCount |
ยน Pathao moved to a rating-based model โ most accounts get no numeric counts (countsAvailable: false). Numeric counts are still returned for entitled accounts.
More couriers can be added easily โ see Adding a New Courier.
๐ง How the Fraud Signal Works
Each courier exposes risk differently. The package normalizes the delivery stats for all of them, and surfaces each courier's native fraud signal on top:
| Courier | Field | Example values | Meaning |
|---|---|---|---|
| Steadfast | frauds[] + fraudReportCount |
complaint objects | Real merchant-submitted complaints with text, date & image |
| Pathao | customerRating + riskLevel |
excellent_customer โ low, fraud_customer โ high |
Pathao's internal rating, mapped to a coarse risk level |
| RedX | customerSegment |
Normal Customer, High Return Customer |
RedX's internal customer tier |
| Carrybee | fraudCount |
0, 3, โฆ |
How many complaints Carrybee holds for the number |
๐ Only Steadfast returns the full who / what / when complaint text. The others give a single label or count โ useful as a quick red flag, but without the details.
Pathao customerRating โ riskLevel mapping:
customerRating |
riskLevel |
|---|---|
excellent_customer, good_customer |
low |
regular_customer, new_customer |
medium |
fraud_customer |
high |
| unknown / missing | null |
โ Adding a New Courier
Click to expand the step-by-step guide
1. Create a new class in src/Courier/YourCourier.php:
<?php namespace Refatbd\BdCourierFraudChecker\Courier; use Refatbd\BdCourierFraudChecker\Traits\Helpers; class YourCourier { use Helpers; public function __construct() { $this->checkRequiredConfig(['your_courier_user', 'your_courier_password']); } public function check($phoneNumber) { $phoneNumber = $this->validateBDPhoneNumber($phoneNumber); // Add your API logic here return [ 'status' => true, 'message' => 'Successful.', 'data' => [], ]; } }
2. Add credentials to config/bdcourierfraudchecker.php:
"your_courier_user" => env("YOUR_COURIER_USER", ""), "your_courier_password" => env("YOUR_COURIER_PASSWORD", ""),
3. Inject it into CourierCheckerService and add it to the check() return array.
4. Bind it in BdCourierFraudCheckerServiceProvider.
โ FAQ
Do I need an account with every courier?
No. Configure only the couriers you use. Unconfigured couriers return
status => false and are skipped โ the rest still work.
Why are Pathao's delivery counts null?
Pathao migrated the customer-success endpoint to a rating-based model. Most merchant accounts now receive
show_count: false with no numeric delivery counts at all โ only a customer_rating. The package returns null for the count fields (and countsAvailable: false) rather than fake 0s, so you can tell "Pathao gave us no counts" apart from "a customer with zero orders". The customerRating / riskLevel is your reliable Pathao signal. If your account is entitled to numeric counts, the package returns them automatically with countsAvailable: true โ no code change needed. This is a Pathao server-side policy and can't be toggled from the API.
Is the data cached?
Only the auth tokens / session cookies are cached (~50 minutes) to avoid logging in on every request. The fraud lookup itself runs fresh on every call, so results are always current.
What phone formats are accepted?
Any valid Bangladeshi mobile number โ with or without the
+88/88 prefix. The package normalizes and validates it (01[3-9]XXXXXXXX) for you.
๐ Changelog
See CHANGELOG.md for the full version history.
Latest โ v1.2.0: connection hardening across all four couriers (browser-like headers, request timeouts, in-call re-authentication on stale sessions) and Pathao's rating-based response shape (riskLevel, showCount, countsAvailable; counts are null when Pathao doesn't expose them). โ ๏ธ Guard on countsAvailable before doing math on Pathao counts.
๐ License
Released under the MIT License โ free to use, modify, and distribute.
โญ If this package saved you from a fraud order, consider starring the repo!