paperfly_courier 0.1.0
paperfly_courier: ^0.1.0 copied to clipboard
Official Dart/Flutter client for the Paperfly (Bangladesh) merchant courier API — create parcels, exchange orders, track shipments, cancel orders, and parse webhook events.
paperfly_courier #
Official Dart/Flutter client for the Paperfly (Bangladesh) merchant courier API. Create parcels and exchange orders, track shipments, cancel orders, and parse webhook events — all with typed requests, typed responses, and typed errors.
Works in any Dart or Flutter app (mobile, web, desktop, server/CLI); it has no Flutter SDK dependency, so it's just as usable from a Dart backend.
Features #
createOrder— create a parcel, or an exchange order viaPaperflyOrderRequest.exchange.trackOrder— look up delivery status, with a ready-made chronologicaltimeline.cancelOrder— cancel a parcel.PaperflyWebhookEvent— parse inbound webhook payloads for all 14 documented event types.PaperflyWebhookVerifier— check a webhook's shared secret.- Typed exceptions (
PaperflyAuthException,PaperflyApiException,PaperflyNetworkException,PaperflyDecodeException) instead of raw HTTP responses to parse yourself.
Getting started #
Add the package:
flutter pub add paperfly_courier
or, for a plain Dart project:
dart pub add paperfly_courier
You'll need three values from your Paperfly merchant dashboard (https://go.paperfly.com.bd/merchant/dashboard → Developer & Plugins → API Docs):
- Your merchant panel User Name
- Your merchant panel Password
- The
paperflykeyheader value shown on that page
Security note: never hard-code these in a public client app, and never embed them in an app you distribute. Treat them like any other API secret — load them from secure configuration and, where possible, make Paperfly calls from your own backend rather than directly from a mobile or web client.
Usage #
Create a parcel #
import 'package:paperfly_courier/paperfly_courier.dart';
final client = PaperflyClient(
merchantUsername: 'YOUR_MERCHANT_USERNAME',
merchantPassword: 'YOUR_MERCHANT_PASSWORD',
paperflyKey: 'YOUR_PAPERFLY_KEY',
);
try {
final order = await client.createOrder(
PaperflyOrderRequest(
merchantOrderReference: 'ORDER-1001', // must be unique per order
storeName: 'My Shop',
productBrief: 'Blue T-Shirt (L)',
packagePrice: 590,
maxWeight: 0.5,
customerName: 'Jane Doe',
customerAddress: 'House 12, Road 5, Banani, Dhaka',
customerPhone: '01700000000',
),
);
print(order.trackingNumber); // e.g. "Z-051125-63821-A3-A1"
print(order.trackingBarcode);
} on PaperflyAuthException catch (e) {
// Bad username/password/paperflykey.
} on PaperflyApiException catch (e) {
// Paperfly rejected the request — see e.message / e.responseBody.
} on PaperflyNetworkException catch (e) {
// Couldn't reach Paperfly at all (offline, timeout, etc).
}
Create an exchange order #
Paperfly uses the same endpoint for exchange orders — just set exchange:
final order = await client.createOrder(
PaperflyOrderRequest(
merchantOrderReference: 'ORDER-1002',
storeName: 'My Shop',
productBrief: 'Blue T-Shirt (L)',
packagePrice: 590,
maxWeight: 0.5,
customerName: 'Jane Doe',
customerAddress: 'House 12, Road 5, Banani, Dhaka',
customerPhone: '01700000000',
exchange: PaperflyExchangeDetails(
description: 'Red T-Shirt (M) — exchanged for blue L',
price: 590,
weight: 0.5,
),
),
);
Track a parcel #
final tracking = await client.trackOrder('ORDER-1001');
for (final milestone in tracking.latest?.timeline ?? const []) {
print('${milestone.stage}: ${milestone.time}');
}
print(tracking.latest?.isDelivered); // true / false
Cancel a parcel #
final cancelled = await client.cancelOrder('ORDER-1001');
print(cancelled.message);
Handling webhooks #
Configure a webhook URL and secret under Developer & Plugins → Webhooks
in the dashboard, then parse incoming requests on your server with
PaperflyWebhookEvent:
import 'dart:convert';
import 'package:paperfly_courier/paperfly_courier.dart';
// Inside your webhook handler, given the raw request body:
void handleWebhook(String requestBody) {
final event = PaperflyWebhookEvent.fromJson(jsonDecode(requestBody));
switch (event.type) {
case PaperflyWebhookEventType.parcelDelivered:
// Mark the order delivered in your own system.
break;
case PaperflyWebhookEventType.parcelCancelled:
// ...
break;
default:
// See PaperflyWebhookEventType for the full list of 14 events.
break;
}
print(event.data.orderNumber);
print(event.data.merchantOrderReference);
print(event.data.recipient.name);
}
Paperfly's dashboard states that the secret you configure "will be sent in
the header for verification", without documenting the exact header name or
signing scheme. Compare whatever header your server receives against your
configured secret with PaperflyWebhookVerifier.isValid:
final isGenuine = PaperflyWebhookVerifier.isValid(
request.headers['x-paperfly-secret'], // adjust to the header you observe
configuredSecret,
);
Webhooks arrive on your server, not inside the Flutter app itself — wire this up in whatever backend (Cloud Function, Node/PHP/Dart server, etc.) receives the webhook, not in client-side app code.
Full example #
See example/lib/main.dart for a small Flutter
app that creates, tracks, and cancels a parcel, and
example/paperfly_courier_example.dart
for a plain-Dart version.
API coverage #
This package covers every endpoint documented on the merchant dashboard's Developer & Plugins → API Docs page as of writing:
| Operation | Endpoint |
|---|---|
| Create order | POST /merchant/api/service/new_order_v2.php |
| Create exchange | POST /merchant/api/service/new_order_v2.php (with exchange fields) |
| Track order | POST /API-Order-Tracking |
| Cancel order | POST /api/v1/cancel-order |
All requests are authenticated with HTTP Basic Auth (your merchant panel
username/password) plus a paperflykey header.
Error handling #
Every failure surfaces as a PaperflyException subtype instead of a raw
http.Response:
PaperflyAuthException— HTTP 401/403, or bad credentials.PaperflyApiException— any other API-reported failure (non-2xx status, or a 200 whose body reports failure). CarriesstatusCode,responseCode, and the rawresponseBody.PaperflyNetworkException— the request couldn't be sent at all (offline, DNS failure, timeout, etc). Carries the originalcause.PaperflyDecodeException— Paperfly's response wasn't valid JSON.
Contributing #
Issues and pull requests are welcome. Please run dart format .,
dart analyze, and dart test before submitting.
License #
MIT — see LICENSE.