steadfast-fraud
v1.0.2
Published
Check customer fraud/delivery stats on Steadfast Courier (Packzy) by phone number — handles login, session cookies, multi-account failover, and 24h auto-refresh automatically.
Maintainers
Readme
fraudjs
Check a customer's delivery and fraud history on Steadfast Courier (Packzy) by phone number. Drop in credentials and just call checkPhone() — login, cookies, and session refresh are handled automatically.
import { checkPhone } from 'steadfast-fraud';
const result = await checkPhone('01700000000');
// { delivered: 12, cancelled: 1, frauds: 0, consignment: [...] }Requirements
- Node.js ≥ 18 (uses built-in fetch and crypto)
- An active Steadfast merchant account
Install
npm install steadfast-fraudSetup
You need at least one Steadfast merchant credential. Two ways to add them:
Option A — .env (quickest)
[email protected]:yourpasswordMultiple accounts for failover (comma-separated, no spaces):
[email protected]:pass1,[email protected]:pass2Make sure your app loads .env (e.g. with dotenv) or export the variable in your shell.
Option B — addCredential() / CLI (persisted)
Credentials are encrypted with AES-256-GCM and stored in your OS config directory:
- Linux:
~/.config/fraudjs/credentials.json - macOS:
~/Library/Preferences/fraudjs/credentials.json - Windows:
%APPDATA%\fraudjs\credentials.json
npx fraudjs add-credential
# prompts for email + password (hidden input)Or from code:
import { addCredential } from 'steadfast-fraud';
await addCredential({ email: '[email protected]', password: 'yourpassword' });Encryption key
By default a random 32-byte key is generated and saved to ~/.config/fraudjs/master.key. It's machine-specific — credentials can't be decrypted on another machine unless you copy the key file.
For a portable/server setup, set FRAUDJS_SECRET to any string and the key will be derived from it instead:
# generate a good one:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
FRAUDJS_SECRET=<that value>API
checkPhone(phone)
Returns delivery and fraud stats for a phone number. Tries each configured credential in order; if one fails it moves to the next.
const { delivered, cancelled, frauds, consignment } = await checkPhone('01700000000');Returns:
{
delivered: number, // total successfully delivered orders
cancelled: number, // total cancelled orders
frauds: number, // flagged fraud orders
consignment: object[], // raw consignment objects from the Steadfast API
}Throws:
NoCredentialsError— nothing configuredAllCredentialsFailedError— every credential was tried and failed (has.failuresarray)NetworkError— DNS failure, timeout, etc.
addCredential({ email, password })
Encrypts and saves a credential. Replaces any existing entry for that email.
listCredentials()
Returns stored emails only — passwords are never exposed.
removeCredential(email)
Removes a stored credential. Throws if the email isn't found.
refresh()
Forces a cookie refresh for all configured accounts. You normally don't need this — sessions auto-refresh every 24 hours.
Error handling
All errors extend FraudJSError and have a .name matching the class name (useful for switch statements without instanceof):
import {
checkPhone,
NoCredentialsError,
AllCredentialsFailedError,
AuthError,
NetworkError,
} from 'steadfast-fraud';
try {
return await checkPhone(phone);
} catch (err) {
switch (err.name) {
case 'NoCredentialsError':
console.error('No credentials configured');
break;
case 'AllCredentialsFailedError':
for (const { email, error } of err.failures) {
console.error(`[${email}] ${error}`);
}
break;
case 'NetworkError':
console.error('Cannot reach Steadfast');
break;
default:
throw err;
}
}CLI
npx fraudjs check 01700000000
npx fraudjs add-credential
npx fraudjs list-credentials
npx fraudjs remove-credential [email protected]
npx fraudjs refreshcheck prints JSON to stdout. Exit code 1 + stderr message on failure.
How it works
Steadfast uses a standard Laravel session setup. The sequence on first use:
GET /login— pick up theXSRF-TOKENcookie and the_tokenCSRF value from the HTMLPOST /loginwithredirect: 'manual'— capture thesteadfast_courier_sessioncookie from the 302 before following it (if fetch auto-follows, those Set-Cookie headers are lost)- Follow the redirect to complete the session
GET /user/consignment/getbyphone/{phone}— send both cookies plusX-XSRF-TOKENset to the URL-decoded value of the XSRF cookie (Laravel writes it encoded but expects it decoded in the header)
Sessions are cached in memory. If a request returns HTML or a 401/419, the session is refreshed and the request is retried once before trying the next credential.
Using with Express / Next.js
The singleton client keeps sessions alive between requests, so only the first call per credential triggers a login:
// Express
app.get('/check/:phone', async (req, res) => {
try {
res.json(await checkPhone(req.params.phone));
} catch (err) {
res.status(502).json({ error: err.message, failures: err.failures });
}
});
// Next.js App Router (Node runtime)
export async function GET(req) {
const phone = new URL(req.url).searchParams.get('phone');
if (!phone) return Response.json({ error: 'phone required' }, { status: 400 });
try {
return Response.json(await checkPhone(phone));
} catch (err) {
return Response.json({ error: err.message }, { status: 502 });
}
}Troubleshooting
AllCredentialsFailedError: All N credential(s) failed
Check err.failures for per-account details. Common causes: wrong password, account locked, or rate limiting. Add more accounts with fraudjs add-credential.
AuthError: Login failed … — check email/password
Credentials were rejected. Verify by logging in to packzy.com manually.
Failed to decrypt credentials — wrong FRAUDJS_SECRET or corrupted store
The FRAUDJS_SECRET doesn't match the one used when the credentials were originally stored. Restore the original secret, or remove and re-add credentials.
Response always shows zeros
Phone number likely has no orders in Steadfast. Check result.consignment for the raw API payload.
Tests
npm testUses Node's built-in node:test — no extra dependencies. All tests mock globalThis.fetch, no network needed.
License
MIT
Note: This package automates access to the Steadfast merchant portal. You're responsible for complying with their terms. Use only accounts you own and authorise.
