pathao-merchant-sdk 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,53 +1,43 @@
1
- # Pathao Merchant API SDK
1
+ # Pathao Merchant SDK
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/pathao-merchant-sdk.svg)](https://www.npmjs.com/package/pathao-merchant-sdk)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/pathao-merchant-sdk.svg)](https://www.npmjs.com/package/pathao-merchant-sdk)
5
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/pathao-merchant-sdk.svg)](https://bundlephobia.com/package/pathao-merchant-sdk)
6
+ [![GitHub stars](https://img.shields.io/github/stars/sifat07/pathao-merchant-sdk.svg)](https://github.com/sifat07/pathao-merchant-sdk/stargazers)
5
7
  [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
6
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
- [![Build Status](https://img.shields.io/github/actions/workflow/status/sifat07/pathao-merchant-sdk/ci.yml?branch=main)](https://github.com/sifat07/pathao-merchant-sdk/actions)
9
+ [![Build Status](https://img.shields.io/github/actions/workflow/status/sifat07/pathao-merchant-sdk/ci-cd.yml?branch=main)](https://github.com/sifat07/pathao-merchant-sdk/actions)
8
10
 
9
- An **unofficial** TypeScript SDK for integrating with the Pathao Merchant API. This community package provides a clean, type-safe interface for all Pathao Merchant API operations including order management, store management, price calculation, and more.
11
+ An **unofficial** TypeScript SDK for the [Pathao Courier Merchant API](https://merchant.pathao.com/developer). Provides a type-safe interface for order management, store management, price calculation, location lookup, and webhook handling.
10
12
 
11
- > **Disclaimer**: This is not an official package from Pathao. It's a community-maintained SDK based on the public Pathao Merchant API documentation.
13
+ > **Disclaimer:** This is a community-maintained package, not an official Pathao product. It is not affiliated with or endorsed by Pathao.
12
14
 
13
15
  ## Table of Contents
14
- - [Features](#features)
16
+
15
17
  - [Requirements](#requirements)
16
18
  - [Installation](#installation)
17
19
  - [Quick Start](#quick-start)
18
20
  - [Configuration](#configuration)
19
- - [Environment Variables](#environment-variables)
20
- - [What You Can Do](#what-you-can-do)
21
21
  - [API Reference](#api-reference)
22
- - [Examples](#examples)
22
+ - [Order Management](#order-management)
23
+ - [Bulk Orders](#bulk-orders)
24
+ - [Store Management](#store-management)
25
+ - [Price Calculation](#price-calculation)
26
+ - [Location Services](#location-services)
27
+ - [Validation Helpers](#validation-helpers)
23
28
  - [Error Handling](#error-handling)
24
29
  - [Webhooks](#webhooks)
25
- - [Authentication](#authentication)
26
- - [Official Documentation](#official-documentation)
30
+ - [TypeScript Types](#typescript-types)
27
31
  - [Contributing](#contributing)
28
- - [Development](#development)
29
32
  - [License](#license)
30
- - [Support](#support)
31
33
  - [Changelog](#changelog)
32
34
 
33
- ## Features
34
-
35
- - 🚀 **Full TypeScript Support** - Complete type definitions for all API responses
36
- - 🔐 **Automatic Authentication** - Handles OAuth2 token management and refresh
37
- - 📦 **Order Management** - Create, track, and manage delivery orders
38
- - 🏪 **Store Management** - Create and manage pickup/service points
39
- - 💰 **Price Calculation** - Get accurate delivery charges before creating orders
40
- - 🌍 **Location Services** - Access cities, zones, and areas data
41
- - 🔔 **Webhook Support** - Verify and handle inbound Pathao webhook events
42
- - ♻️ **Retry & Circuit Breaker** - Automatic retry with backoff, circuit breaker for resilience
43
- - ⚡ **Built with Axios** - Reliable HTTP client with request/response interceptors
44
- - 🛡️ **Error Handling** - Comprehensive error handling with detailed error messages
45
- - 📚 **Well Documented** - Extensive documentation and examples
35
+ ---
46
36
 
47
37
  ## Requirements
48
38
 
49
- - Node.js >= 18 (see `engines` in `package.json`)
50
- - TypeScript >= 4.9 (peer dependency; repository uses 5.x)
39
+ - Node.js >= 18
40
+ - TypeScript >= 4.9 (peer dependency)
51
41
 
52
42
  ## Installation
53
43
 
@@ -61,55 +51,70 @@ pnpm add pathao-merchant-sdk
61
51
 
62
52
  ## Quick Start
63
53
 
54
+ Use the sandbox credentials below to get started immediately. For production, get your credentials from the [Pathao Merchant Dashboard](https://merchant.pathao.com/developer) under **API Credentials**.
55
+
56
+ **Sandbox credentials (publicly provided by Pathao for testing):**
57
+
58
+ | Field | Value |
59
+ | -------------- | ------------------------------------------ |
60
+ | `baseURL` | `https://courier-api-sandbox.pathao.com` |
61
+ | `clientId` | `7N1aMJQbWm` |
62
+ | `clientSecret` | `wRcaibZkUdSNz2EI9ZyuXLlNrnAv0TdPUPXMnD39` |
63
+ | `username` | `test@pathao.com` |
64
+ | `password` | `lovePathao` |
65
+
64
66
  ```typescript
65
- import { PathaoApiService, DeliveryType, ItemType } from 'pathao-merchant-sdk';
66
-
67
- // Initialize the SDK
68
- const pathao = new PathaoApiService({
69
- baseURL: 'https://api-hermes.pathao.com', // or use PATHAO_BASE_URL env var
70
- clientId: 'your-client-id',
71
- clientSecret: 'your-client-secret',
72
- username: 'your-username',
73
- password: 'your-password'
67
+ import { PathaoApiService, DeliveryType, ItemType } from "pathao-merchant-sdk";
68
+
69
+ const pathao = PathaoApiService.fromConfig({
70
+ baseURL: "https://courier-api-sandbox.pathao.com",
71
+ clientId: "7N1aMJQbWm",
72
+ clientSecret: "wRcaibZkUdSNz2EI9ZyuXLlNrnAv0TdPUPXMnD39",
73
+ username: "test@pathao.com",
74
+ password: "lovePathao",
74
75
  });
75
76
 
76
- // Create a delivery order
77
77
  const order = await pathao.createOrder({
78
- store_id: 123,
79
- recipient_name: 'John Doe',
80
- recipient_phone: '01712345678',
81
- recipient_address: '123 Main Street, Dhanmondi, Dhaka',
78
+ store_id: 12345,
79
+ recipient_name: "John Doe",
80
+ recipient_phone: "01712345678",
81
+ recipient_address: "House 10, Road 5, Dhanmondi, Dhaka",
82
82
  delivery_type: DeliveryType.NORMAL,
83
83
  item_type: ItemType.PARCEL,
84
84
  item_quantity: 1,
85
- item_weight: 1.0,
86
- amount_to_collect: 500
85
+ item_weight: 0.5,
86
+ amount_to_collect: 500,
87
87
  });
88
88
 
89
- console.log('Order created:', order.data.consignment_id);
89
+ console.log("Consignment ID:", order.data.consignment_id);
90
90
  ```
91
91
 
92
- ## Environment Variables
92
+ ---
93
93
 
94
- Copy `env.example` to `.env` and fill in your credentials. If your runtime does not auto-load environment files, install and load `dotenv`:
94
+ ## Configuration
95
95
 
96
- ```bash
97
- cp env.example .env
98
- npm install dotenv --save-dev # or yarn add -D dotenv / pnpm add -D dotenv
99
- ```
96
+ ### Environments
100
97
 
101
- ```typescript
102
- import 'dotenv/config';
103
- ```
98
+ | | Sandbox | Production |
99
+ | ----------- | ---------------------------------------- | ------------------------------- |
100
+ | `baseURL` | `https://courier-api-sandbox.pathao.com` | `https://api-hermes.pathao.com` |
101
+ | Credentials | From merchant dashboard (sandbox tab) | From merchant dashboard |
102
+
103
+ Obtain your `client_id`, `client_secret`, username, and password from the **API Credentials** section of the [Pathao Merchant Dashboard](https://merchant.pathao.com/developer).
104
+
105
+ ### Environment Variables
104
106
 
105
- Required and optional variables:
107
+ Copy `.env.example` to `.env`:
108
+
109
+ ```bash
110
+ cp .env.example .env
111
+ ```
106
112
 
107
113
  ```env
108
- # Base URL (choose one)
109
- PATHAO_BASE_URL=https://courier-api-sandbox.pathao.com # sandbox
110
- # PATHAO_BASE_URL=https://api-hermes.pathao.com # production
114
+ # Choose one:
115
+ PATHAO_BASE_URL=https://courier-api-sandbox.pathao.com
116
+ # PATHAO_BASE_URL=https://api-hermes.pathao.com
111
117
 
112
- # Authentication (required)
113
118
  PATHAO_CLIENT_ID=your-client-id
114
119
  PATHAO_CLIENT_SECRET=your-client-secret
115
120
  PATHAO_USERNAME=your-username
@@ -119,103 +124,65 @@ PATHAO_PASSWORD=your-password
119
124
  PATHAO_TIMEOUT=30000
120
125
  ```
121
126
 
122
- ## What You Can Do
123
-
124
- - Create, price, and track delivery orders
125
- - Manage stores (pickup/service points)
126
- - Look up cities, zones, and areas
127
- - Validate phone, address, weight, and recipient inputs
128
- - Automatically handle OAuth2 authentication and token refresh
129
-
130
- ## API Reference
131
-
132
- ### Configuration
133
-
134
- ```typescript
135
- interface PathaoConfig {
136
- clientId: string; // Your Pathao API client ID
137
- clientSecret: string; // Your Pathao API client secret
138
- username: string; // Your Pathao API username
139
- password: string; // Your Pathao API password
140
- baseURL: string; // API base URL (required)
141
- timeout?: number; // Request timeout in ms (default: 30000)
142
- }
143
- ```
144
-
145
- #### Environment Variables
146
-
147
- The SDK automatically reads the variables listed in the [Environment Variables](#environment-variables) section when present, and falls back to the values you pass in the config.
127
+ If you use `dotenv`, load it before initializing the SDK:
148
128
 
149
129
  ```typescript
150
- // Option 1: All from environment variables
151
- const pathao = new PathaoApiService({});
152
-
153
- // Option 2: Mix of config and environment variables
154
- const pathao = new PathaoApiService({
155
- baseURL: 'https://api-hermes.pathao.com', // This overrides PATHAO_BASE_URL
156
- clientId: 'your-client-id', // This overrides PATHAO_CLIENT_ID
157
- // Other credentials will be taken from environment variables
158
- });
159
-
160
- // Option 3: All from config (environment variables as fallback)
161
- const pathao = new PathaoApiService({
162
- baseURL: process.env.PATHAO_BASE_URL || 'https://api-hermes.pathao.com',
163
- clientId: process.env.PATHAO_CLIENT_ID || 'your-client-id',
164
- clientSecret: process.env.PATHAO_CLIENT_SECRET || 'your-client-secret',
165
- username: process.env.PATHAO_USERNAME || 'your-username',
166
- password: process.env.PATHAO_PASSWORD || 'your-password',
167
- });
130
+ import "dotenv/config";
168
131
  ```
169
132
 
170
- #### Factory Methods
171
-
172
- The SDK provides convenient factory methods for common initialization patterns:
133
+ ### Factory Methods
173
134
 
174
135
  ```typescript
175
- // Create from environment variables
136
+ // From environment variables
176
137
  const pathao = PathaoApiService.fromEnv();
177
138
 
178
- // Create from environment with additional options
139
+ // From environment with options
179
140
  const pathao = PathaoApiService.fromEnv({
180
- debug: true, // Enable debug logging
181
- circuitBreaker: {
182
- threshold: 10, // Number of failures before opening circuit (default: 5)
183
- timeout: 120000 // Timeout before attempting to close circuit (default: 60000ms)
184
- }
141
+ debug: true,
142
+ circuitBreaker: { threshold: 10, timeout: 120_000 },
185
143
  });
186
144
 
187
- // Create from explicit config
145
+ // From explicit config
188
146
  const pathao = PathaoApiService.fromConfig({
189
- baseURL: 'https://api-hermes.pathao.com',
190
- clientId: 'client-id',
191
- clientSecret: 'client-secret',
192
- username: 'username',
193
- password: 'password',
194
- timeout: 5000
195
- }, {
196
- debug: true,
197
- circuitBreaker: { threshold: 8 }
147
+ baseURL: "https://api-hermes.pathao.com",
148
+ clientId: "your-client-id",
149
+ clientSecret: "your-client-secret",
150
+ username: "your-username",
151
+ password: "your-password",
152
+ });
153
+
154
+ // Named constructors (pre-fill the base URL)
155
+ const pathao = PathaoApiService.sandbox({
156
+ clientId,
157
+ clientSecret,
158
+ username,
159
+ password,
160
+ });
161
+ const pathao = PathaoApiService.production({
162
+ clientId,
163
+ clientSecret,
164
+ username,
165
+ password,
198
166
  });
199
167
  ```
200
168
 
201
- #### Advanced Options
169
+ ### Options
202
170
 
203
171
  ```typescript
204
172
  const pathao = new PathaoApiService(config, {
205
- debug: false, // Enable detailed debug logging (default: false)
173
+ debug: false, // Log all HTTP requests/responses (default: false)
206
174
  circuitBreaker: {
207
- threshold: 5, // Failures before opening circuit (default: 5)
208
- timeout: 60000 // Wait time before retry (default: 60000ms = 1 min)
209
- }
175
+ threshold: 5, // Failures before opening circuit (default: 5)
176
+ timeout: 60_000, // Ms before attempting to close circuit (default: 60000)
177
+ },
210
178
  });
211
179
  ```
212
180
 
213
- When `debug` is enabled, the SDK logs all HTTP requests and responses:
181
+ Configuration validation is **deferred** to the first API call — constructing the SDK never throws.
214
182
 
215
- ```
216
- [Pathao SDK] GET /aladdin/api/v1/stores { headers: {...}, data: {...} }
217
- [Pathao SDK] Response 200 { url: '...', data: {...} }
218
- ```
183
+ ---
184
+
185
+ ## API Reference
219
186
 
220
187
  ### Order Management
221
188
 
@@ -223,30 +190,74 @@ When `debug` is enabled, the SDK logs all HTTP requests and responses:
223
190
 
224
191
  ```typescript
225
192
  const order = await pathao.createOrder({
226
- store_id: 123,
227
- merchant_order_id: 'ORDER-123', // Optional: Your order tracking ID
228
- recipient_name: 'John Doe',
229
- recipient_phone: '01712345678',
230
- recipient_secondary_phone: '01712345679', // Optional
231
- recipient_address: '123 Main Street, Dhanmondi',
232
- recipient_city: 1, // Optional: Auto-detected if not provided
233
- recipient_zone: 1, // Optional: Auto-detected if not provided
234
- recipient_area: 1, // Optional: Auto-detected if not provided
235
- delivery_type: DeliveryType.NORMAL, // 48 for Normal, 12 for On Demand
236
- item_type: ItemType.PARCEL, // 1 for Document, 2 for Parcel
237
- special_instruction: 'Call before delivery', // Optional
238
- item_quantity: 1,
239
- item_weight: 1.0, // 0.5-10 kg
240
- item_description: 'Electronics', // Optional
241
- amount_to_collect: 500 // COD amount (0 for non-COD)
193
+ store_id: 12345, // Required — your store ID
194
+ merchant_order_id: "ORDER-001", // Optional — your internal tracking ID
195
+ recipient_name: "John Doe", // Required — 3–100 characters
196
+ recipient_phone: "01712345678", // Required — 11 digits, starts with 01
197
+ recipient_secondary_phone: "01812345678", // Optional
198
+ recipient_address: "House 10, Road 5, Dhanmondi, Dhaka", // Required — 10–220 chars
199
+ recipient_city: 1, // Optional — auto-detected if omitted
200
+ recipient_zone: 1, // Optional — auto-detected if omitted
201
+ recipient_area: 1, // Optional — auto-detected if omitted
202
+ delivery_type: DeliveryType.NORMAL, // Required — NORMAL (48) or ON_DEMAND (12)
203
+ item_type: ItemType.PARCEL, // Required — DOCUMENT (1) or PARCEL (2)
204
+ item_quantity: 1, // Required
205
+ item_weight: 0.5, // Required — 0.5–10 kg
206
+ item_description: "Cotton shirt", // Optional
207
+ special_instruction: "Call before delivery", // Optional
208
+ amount_to_collect: 500, // Required — COD amount; 0 for prepaid
242
209
  });
210
+
211
+ // Response
212
+ console.log(order.data.consignment_id); // Pathao tracking ID
213
+ console.log(order.data.merchant_order_id);
214
+ console.log(order.data.order_status); // "Pending"
215
+ console.log(order.data.delivery_fee); // number
243
216
  ```
244
217
 
245
218
  #### Get Order Status
246
219
 
247
220
  ```typescript
248
- const status = await pathao.getOrderStatus('consignment-id');
249
- console.log('Order status:', status.data.order_status);
221
+ const info = await pathao.getOrderStatus("DL121224VS8TTJ");
222
+
223
+ console.log(info.data.consignment_id);
224
+ console.log(info.data.order_status);
225
+ console.log(info.data.order_status_slug);
226
+ console.log(info.data.updated_at); // "YYYY-MM-DD HH:MM:SS"
227
+ console.log(info.data.invoice_id); // string | null
228
+ ```
229
+
230
+ ### Bulk Orders
231
+
232
+ ```typescript
233
+ const result = await pathao.createBulkOrder([
234
+ {
235
+ store_id: 12345,
236
+ recipient_name: "Alice",
237
+ recipient_phone: "01712345678",
238
+ recipient_address: "House 10, Road 5, Dhanmondi, Dhaka",
239
+ delivery_type: DeliveryType.NORMAL,
240
+ item_type: ItemType.PARCEL,
241
+ item_quantity: 1,
242
+ item_weight: 0.5,
243
+ amount_to_collect: 300,
244
+ },
245
+ {
246
+ store_id: 12345,
247
+ recipient_name: "Bob",
248
+ recipient_phone: "01812345678",
249
+ recipient_address: "House 3, Road 14, Gulshan, Dhaka",
250
+ delivery_type: DeliveryType.NORMAL,
251
+ item_type: ItemType.PARCEL,
252
+ item_quantity: 2,
253
+ item_weight: 1.0,
254
+ amount_to_collect: 800,
255
+ },
256
+ ]);
257
+
258
+ // Bulk order creation is asynchronous — response is HTTP 202
259
+ console.log(result.code); // 202
260
+ console.log(result.data); // true
250
261
  ```
251
262
 
252
263
  ### Store Management
@@ -255,453 +266,376 @@ console.log('Order status:', status.data.order_status);
255
266
 
256
267
  ```typescript
257
268
  const store = await pathao.createStore({
258
- name: 'My Store',
259
- contact_name: 'Store Manager',
260
- contact_number: '01712345678',
261
- address: '123 Store Street, Dhanmondi',
262
- city_id: 1,
263
- zone_id: 1,
264
- area_id: 1
269
+ name: "My Dhaka Store", // Required — 3–50 characters
270
+ contact_name: "Store Manager", // Required — 3–50 characters
271
+ contact_number: "01712345678", // Required — 11 digits, starts with 01
272
+ secondary_contact: "01812345678", // Optional
273
+ otp_number: "01712345678", // Optional — OTP delivery number
274
+ address: "House 10, Road 5, Dhanmondi, Dhaka", // Required — 15–120 chars
275
+ city_id: 1, // Required
276
+ zone_id: 1, // Required
277
+ area_id: 37, // Required
265
278
  });
279
+
280
+ // Store requires Pathao approval (~1 hour) before it can be used
281
+ console.log(store.data.store_name);
266
282
  ```
267
283
 
268
284
  #### Get Stores
269
285
 
270
286
  ```typescript
271
- const stores = await pathao.getStores();
272
- console.log('Available stores:', stores.data.data);
287
+ const stores = await pathao.getStores(); // first page
288
+ const stores = await pathao.getStores(2); // specific page
289
+
290
+ // Paginated response
291
+ stores.data.data.forEach((s) => {
292
+ console.log(s.store_id, s.store_name, s.is_active);
293
+ });
294
+
295
+ // Auto-fetch all pages
296
+ const allStores = await pathao.getStoresAll();
273
297
  ```
274
298
 
275
299
  ### Price Calculation
276
300
 
277
301
  ```typescript
278
302
  const price = await pathao.calculatePrice({
279
- store_id: 123,
303
+ store_id: 12345,
280
304
  item_type: ItemType.PARCEL,
281
- item_weight: 1.0,
282
305
  delivery_type: DeliveryType.NORMAL,
306
+ item_weight: 0.5,
283
307
  recipient_city: 1,
284
- recipient_zone: 1
308
+ recipient_zone: 1,
285
309
  });
286
310
 
287
- console.log('Price:', price.data.price);
288
- console.log('Final price:', price.data.final_price);
289
- console.log('COD percentage:', price.data.cod_percentage);
311
+ console.log(price.data.price); // base price
312
+ console.log(price.data.discount);
313
+ console.log(price.data.final_price); // price to display to customer
314
+ console.log(price.data.cod_enabled); // 0 | 1
315
+ console.log(price.data.cod_percentage); // e.g. 0.01
290
316
  ```
291
317
 
292
318
  ### Location Services
293
319
 
294
- #### Get Cities
295
-
296
320
  ```typescript
321
+ // Cities
297
322
  const cities = await pathao.getCities();
298
- console.log('Available cities:', cities.data);
299
- ```
323
+ // [{ city_id: 1, city_name: "Dhaka" }, ...]
300
324
 
301
- #### Get Areas
325
+ // Zones within a city
326
+ const zones = await pathao.getZones(1);
327
+ // [{ zone_id: 298, zone_name: "60 feet" }, ...]
302
328
 
303
- ```typescript
304
- const areas = await pathao.getAreas();
305
- console.log('Available areas:', areas.data);
329
+ // Areas within a zone
330
+ const areas = await pathao.getAreas(298);
331
+ // [{ area_id: 37, area_name: "Bonolota", home_delivery_available: true, pickup_available: true }, ...]
306
332
  ```
307
333
 
308
334
  ### Validation Helpers
309
335
 
310
- The SDK includes built-in validation helpers:
336
+ All helpers are static and can be used before constructing the SDK:
311
337
 
312
338
  ```typescript
313
- import { PathaoApiService } from 'pathao-merchant-sdk';
314
-
315
- // Validate phone number
316
- const isValidPhone = PathaoApiService.validatePhoneNumber('01712345678'); // true
317
-
318
- // Format phone number
319
- const formattedPhone = PathaoApiService.formatPhoneNumber('01712345678'); // '01712345678'
320
-
321
- // Validate address
322
- const isValidAddress = PathaoApiService.validateAddress('123 Main Street'); // true
323
-
324
- // Validate weight
325
- const isValidWeight = PathaoApiService.validateWeight(1.0); // true
339
+ import { PathaoApiService } from "pathao-merchant-sdk";
326
340
 
327
- // Validate recipient name
328
- const isValidName = PathaoApiService.validateRecipientName('John Doe'); // true
341
+ PathaoApiService.validatePhoneNumber("01712345678"); // true — 11 digits, starts with 01
342
+ PathaoApiService.validateContactNumber("01712345678"); // true — same rules
343
+ PathaoApiService.validateAddress("House 10, Road 5, Dhanmondi, Dhaka"); // true — 10–220 chars
344
+ PathaoApiService.validateStoreAddress("House 10, Road 5, Dhanmondi"); // true — 15–120 chars
345
+ PathaoApiService.validateWeight(0.5); // true — 0.5–10 kg
346
+ PathaoApiService.validateRecipientName("John Doe"); // true — 3–100 chars
347
+ PathaoApiService.validateStoreName("My Store"); // true — 3–50 chars
329
348
  ```
330
349
 
331
- ## Enums
332
-
333
- ### DeliveryType
334
-
335
- ```typescript
336
- enum DeliveryType {
337
- NORMAL = 48, // Normal delivery
338
- ON_DEMAND = 12 // On-demand delivery
339
- }
340
- ```
341
-
342
- ### ItemType
343
-
344
- ```typescript
345
- enum ItemType {
346
- DOCUMENT = 1, // Document
347
- PARCEL = 2 // Parcel
348
- }
349
- ```
350
+ ---
350
351
 
351
352
  ## Error Handling
352
353
 
353
- The SDK provides comprehensive error handling through the `PathaoApiError` class, which extends Error with additional properties for detailed error information:
354
+ All API errors are thrown as `PathaoApiError`:
354
355
 
355
356
  ```typescript
356
- import { PathaoApiService, PathaoApiError } from 'pathao-merchant-sdk';
357
-
358
- const pathao = new PathaoApiService({
359
- clientId: process.env.PATHAO_CLIENT_ID,
360
- clientSecret: process.env.PATHAO_CLIENT_SECRET,
361
- username: process.env.PATHAO_USERNAME,
362
- password: process.env.PATHAO_PASSWORD
363
- });
357
+ import { PathaoApiService, PathaoApiError } from "pathao-merchant-sdk";
364
358
 
365
359
  try {
366
360
  const order = await pathao.createOrder(orderData);
367
- console.log('Order created:', order.data.consignment_id);
368
- } catch (error) {
369
- // Check if it's a PathaoApiError (structured error from Pathao API)
370
- if (error instanceof PathaoApiError) {
371
- console.error('Pathao API Error:', {
372
- status: error.status, // HTTP status code
373
- code: error.code, // Pathao error code
374
- type: error.type, // Error type (e.g., 'ValidationException')
375
- message: error.message, // Error message
376
- errors: error.errors, // Field-level errors
377
- validation: error.validation // Validation errors
378
- });
379
- } else {
380
- console.error('Unexpected error:', error.message);
361
+ } catch (err) {
362
+ if (err instanceof PathaoApiError) {
363
+ console.error("HTTP status:", err.status); // e.g. 422
364
+ console.error("Pathao code:", err.code); // Pathao internal error code
365
+ console.error("Type:", err.type); // e.g. "ValidationException"
366
+ console.error("Message:", err.message);
367
+ console.error("Field errors:", err.errors); // { field: "message" }
368
+ console.error("Validation:", err.validation);
381
369
  }
382
370
  }
383
371
  ```
384
372
 
385
- ### Error Properties
386
-
387
- - **status**: HTTP status code (e.g., 400, 401, 422)
388
- - **code**: Pathao API error code for programmatic handling
389
- - **type**: Error type string (e.g., 'ValidationException', 'AuthenticationException')
390
- - **errors**: Object with field-level error messages
391
- - **validation**: Object with validation error messages
392
- - **message**: Human-readable error message
373
+ ### Common error scenarios
393
374
 
394
- ### Configuration Validation
375
+ | Status | Cause |
376
+ | ------ | ------------------------------------------------------------ |
377
+ | 400 | Bad request / missing required fields |
378
+ | 401 | Invalid or expired credentials |
379
+ | 422 | Validation failure — check `err.errors` for field details |
380
+ | 429 | Rate limited — SDK retries automatically after `Retry-After` |
381
+ | 503 | Circuit breaker open — too many consecutive failures |
395
382
 
396
- Configuration validation is deferred until the first API call to allow gradual setup:
383
+ ---
397
384
 
398
- ```typescript
399
- // This won't throw immediately
400
- const pathao = new PathaoApiService({});
385
+ ## Webhooks
401
386
 
402
- // This will throw PathaoApiError if credentials are missing
403
- try {
404
- await pathao.getStores();
405
- } catch (error) {
406
- if (error instanceof PathaoApiError) {
407
- console.error('Configuration error:', error.validation);
408
- }
409
- }
410
- ```
387
+ The webhooks module is a **separate entry point** with zero runtime dependencies (Node.js built-ins only).
411
388
 
412
- ## Webhooks
389
+ ### How Pathao webhooks work
413
390
 
414
- The `pathao-merchant-sdk/webhooks` sub-module handles inbound Pathao webhook events. It has **zero extra runtime dependencies** — only Node.js built-ins (`crypto`, `events`).
391
+ 1. Pathao sends a POST request with a JSON payload to your URL.
392
+ 2. The `X-PATHAO-Signature` header contains your configured webhook secret verbatim.
393
+ 3. Your endpoint must respond within 10 seconds with an `X-Pathao-Merchant-Webhook-Integration-Secret` header whose value equals your webhook secret.
394
+ 4. The HTTP status code should be 2xx.
415
395
 
416
- > **Note:** Pathao has no official webhook documentation. This implementation is based on reverse-engineering the [`pathao-courier`](https://www.npmjs.com/package/pathao-courier) package. The signature scheme is plain shared-secret equality (no HMAC) via constant-time comparison.
396
+ ### Setup requirements
417
397
 
418
- ### Installation
398
+ - Your URL must be publicly reachable over HTTPS with a valid SSL certificate.
399
+ - Configure your webhook URL and note the secret from the [Pathao Merchant Dashboard](https://merchant.pathao.com/developer).
400
+ - Store the secret in an environment variable (e.g. `PATHAO_WEBHOOK_SECRET`).
419
401
 
420
- The webhooks module is a separate entry point. Import it explicitly:
402
+ ### Import
421
403
 
422
404
  ```typescript
405
+ // ESM / TypeScript
423
406
  import {
424
407
  PathaoWebhookHandler,
425
- constructEvent,
426
- verifySignature,
427
408
  PathaoWebhookEvent,
428
- } from 'pathao-merchant-sdk/webhooks';
429
- ```
409
+ constructEvent,
410
+ PathaoWebhookError,
411
+ } from "pathao-merchant-sdk/webhooks";
430
412
 
431
- ```javascript
432
413
  // CommonJS
433
- const { PathaoWebhookHandler } = require('pathao-merchant-sdk/webhooks');
434
- ```
435
-
436
- ### Quick verification
437
-
438
- ```typescript
439
- import { verifySignature } from 'pathao-merchant-sdk/webhooks';
440
-
441
- const isValid = verifySignature(
442
- req.headers['x-pathao-signature'],
443
- process.env.PATHAO_WEBHOOK_SECRET!,
444
- );
445
- ```
446
-
447
- ### Verify + parse in one call
448
-
449
- ```typescript
450
- import { constructEvent, PathaoWebhookError } from 'pathao-merchant-sdk/webhooks';
451
-
452
- app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
453
- try {
454
- const payload = constructEvent(
455
- req.body, // raw Buffer — do NOT pre-parse
456
- req.headers,
457
- process.env.PATHAO_WEBHOOK_SECRET!,
458
- );
459
- console.log('Event:', payload.event, payload.data);
460
- res.sendStatus(200);
461
- } catch (err) {
462
- if (err instanceof PathaoWebhookError) {
463
- res.status(400).send(err.message);
464
- } else {
465
- res.sendStatus(500);
466
- }
467
- }
468
- });
414
+ const { PathaoWebhookHandler } = require("pathao-merchant-sdk/webhooks");
469
415
  ```
470
416
 
471
- ### Express middleware
417
+ ### Express integration
472
418
 
473
419
  ```typescript
474
- import { PathaoWebhookHandler, PathaoWebhookEvent } from 'pathao-merchant-sdk/webhooks';
420
+ import express from "express";
421
+ import {
422
+ PathaoWebhookHandler,
423
+ PathaoWebhookEvent,
424
+ } from "pathao-merchant-sdk/webhooks";
475
425
 
426
+ const app = express();
476
427
  const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);
477
428
 
478
- // Listen for specific events (fully typed payload)
479
429
  handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => {
480
- console.log('Delivered:', payload.consignment_id);
481
- });
482
-
483
- handler.on(PathaoWebhookEvent.ORDER_CANCELLED, (payload) => {
484
- console.log('Cancelled:', payload.consignment_id, payload.reason);
430
+ console.log(
431
+ "Delivered:",
432
+ payload.consignment_id,
433
+ "Collected:",
434
+ payload.collected_amount,
435
+ );
485
436
  });
486
437
 
487
- // Catch all events
488
- handler.on('webhook', (payload) => {
489
- console.log('Any event:', payload.event, payload.data);
438
+ handler.on(PathaoWebhookEvent.ORDER_PAID, (payload) => {
439
+ console.log("Invoice:", payload.invoice_id);
490
440
  });
491
441
 
492
- // Handle errors (invalid signature, bad JSON, etc.)
493
- handler.on('error', (err) => {
494
- console.error('Webhook error:', err.message);
442
+ handler.on("error", (err) => {
443
+ console.error("Webhook error:", err.message);
495
444
  });
496
445
 
497
- // Mount — must use express.raw() BEFORE this middleware
498
446
  app.post(
499
- '/webhook/pathao',
500
- express.raw({ type: 'application/json' }),
447
+ "/webhooks/pathao",
448
+ express.raw({ type: "application/json" }),
501
449
  handler.expressMiddleware(),
450
+ (req, res) => {
451
+ // expressMiddleware() sets the required secret header automatically.
452
+ // It also handles the handshake event internally (returns 202).
453
+ // For all other events the payload is on req.pathaoWebhook.
454
+ res.sendStatus(200);
455
+ },
502
456
  );
503
-
504
- // Access the parsed payload downstream
505
- app.post('/webhook/pathao', express.raw({ type: 'application/json' }), handler.expressMiddleware(), (req, res) => {
506
- console.log(req.pathaoWebhook); // typed PathaoWebhookPayload
507
- res.sendStatus(200);
508
- });
509
457
  ```
510
458
 
511
- ### Generic (non-Express) middleware
459
+ ### Framework-agnostic middleware
512
460
 
513
461
  ```typescript
514
462
  const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);
515
463
  const middleware = handler.middleware();
516
464
 
517
- // Returns { payload, error } — never rejects
518
- const { payload, error } = await middleware(rawBody, headers);
519
- if (error) {
520
- console.error('Bad webhook:', error.message);
521
- } else {
522
- console.log('Event:', payload!.event);
523
- }
524
- ```
525
-
526
- ### Supported event types
465
+ // In any async handler (Fastify, Hono, plain http, etc.)
466
+ const instructions = await middleware(rawBody);
527
467
 
528
- | Event constant | Event string |
529
- |---|---|
530
- | `ORDER_CREATED` | `order.created` |
531
- | `ORDER_ACCEPTED` | `order.accepted` |
532
- | `ORDER_PICKUP_REQUESTED` | `order.pickup_requested` |
533
- | `ORDER_PICKED` | `order.picked` |
534
- | `ORDER_IN_TRANSIT` | `order.in_transit` |
535
- | `ORDER_DELIVERED` | `order.delivered` |
536
- | `ORDER_CANCELLED` | `order.cancelled` |
537
- | `ORDER_HOLD` | `order.hold` |
538
- | `ORDER_RETURN_REQUESTED` | `order.return_requested` |
539
- | `ORDER_RETURN_PICKUP_REQUESTED` | `order.return_pickup_requested` |
540
- | `ORDER_RETURN_PICKED` | `order.return_picked` |
541
- | `ORDER_RETURN_IN_TRANSIT` | `order.return_in_transit` |
542
- | `ORDER_PARTIAL_DELIVERED` | `order.partial_delivered` |
543
- | `ORDER_DELIVERY_FAILED` | `order.delivery_failed` |
544
- | `ORDER_ON_HOLD` | `order.on_hold` |
545
- | `ORDER_PAID` | `order.paid` |
546
- | `ORDER_PAID_RETURN` | `order.paid_return` |
547
- | `ORDER_RETURNED` | `order.returned` |
548
- | `ORDER_EXCHANGED` | `order.exchanged` |
549
- | `STORE_CREATED` | `store.created` |
550
- | `STORE_UPDATED` | `store.updated` |
551
-
552
- ### TypeScript types
553
-
554
- All event payloads are fully typed. Access them via `WebhookEventPayloadMap`:
468
+ for (const [key, value] of Object.entries(instructions.headers)) {
469
+ reply.header(key, value); // always set — the secret header is required for every response
470
+ }
555
471
 
556
- ```typescript
557
- import type {
558
- WebhookEventPayloadMap,
559
- PathaoWebhookEvent,
560
- OrderDeliveredPayload,
561
- } from 'pathao-merchant-sdk/webhooks';
472
+ if (instructions.error) {
473
+ return reply.status(400).send({ error: instructions.error.message });
474
+ }
562
475
 
563
- // Explicit payload type
564
- const handler = (payload: OrderDeliveredPayload) => {
565
- console.log(payload.consignment_id, payload.amount_to_collect);
566
- };
476
+ if (instructions.payload?.event === "webhook_integration") {
477
+ return reply.status(202).send();
478
+ }
567
479
 
568
- // Via mapped type
569
- type DeliveredPayload = WebhookEventPayloadMap[PathaoWebhookEvent.ORDER_DELIVERED];
480
+ // instructions.payload is typed as PathaoWebhookPayload
481
+ console.log(instructions.payload?.event);
482
+ return reply.status(200).send({ received: true });
570
483
  ```
571
484
 
572
- ## Authentication
573
-
574
- The SDK automatically handles OAuth2 authentication and token refresh. You only need to provide your credentials once during initialization:
485
+ ### Parse and verify manually
575
486
 
576
487
  ```typescript
577
- const pathao = new PathaoApiService({
578
- clientId: process.env.PATHAO_CLIENT_ID,
579
- clientSecret: process.env.PATHAO_CLIENT_SECRET,
580
- username: process.env.PATHAO_USERNAME,
581
- password: process.env.PATHAO_PASSWORD
488
+ import {
489
+ constructEvent,
490
+ PathaoWebhookError,
491
+ } from "pathao-merchant-sdk/webhooks";
492
+
493
+ app.post("/webhooks/pathao", express.raw({ type: "application/json" }), (req, res) => {
494
+ res.setHeader(
495
+ "X-Pathao-Merchant-Webhook-Integration-Secret",
496
+ process.env.PATHAO_WEBHOOK_SECRET!,
497
+ );
498
+ try {
499
+ const payload = constructEvent(req.body);
500
+ console.log("Event:", payload.event);
501
+ res.sendStatus(200);
502
+ } catch (err) {
503
+ if (err instanceof PathaoWebhookError) {
504
+ res.status(400).send(err.message);
505
+ } else {
506
+ res.sendStatus(500);
507
+ }
508
+ }
582
509
  });
583
510
  ```
584
511
 
585
- ## Examples
512
+ ### Supported event types
586
513
 
587
- ### Complete Order Flow
514
+ All 24 event types from the Pathao dashboard:
515
+
516
+ | Enum constant | Event string | Key payload fields |
517
+ | --------------------------------- | --------------------------------- | ---------------------------------------------------------------------------- |
518
+ | `ORDER_CREATED` | `order.created` | `consignment_id`, `store_id`, `delivery_fee` |
519
+ | `ORDER_UPDATED` | `order.updated` | `consignment_id`, `store_id`, `delivery_fee` |
520
+ | `ORDER_PICKUP_REQUESTED` | `order.pickup-requested` | `consignment_id`, `store_id`, `delivery_fee` |
521
+ | `ORDER_ASSIGNED_FOR_PICKUP` | `order.assigned-for-pickup` | `consignment_id`, `store_id` |
522
+ | `ORDER_PICKED` | `order.picked` | `consignment_id`, `store_id` |
523
+ | `ORDER_PICKUP_FAILED` | `order.pickup-failed` | `consignment_id`, `store_id` |
524
+ | `ORDER_PICKUP_CANCELLED` | `order.pickup-cancelled` | `consignment_id`, `store_id` |
525
+ | `ORDER_AT_THE_SORTING_HUB` | `order.at-the-sorting-hub` | `consignment_id`, `store_id` |
526
+ | `ORDER_IN_TRANSIT` | `order.in-transit` | `consignment_id`, `store_id` |
527
+ | `ORDER_RECEIVED_AT_LAST_MILE_HUB` | `order.received-at-last-mile-hub` | `consignment_id`, `store_id` |
528
+ | `ORDER_ASSIGNED_FOR_DELIVERY` | `order.assigned-for-delivery` | `consignment_id`, `store_id` |
529
+ | `ORDER_DELIVERED` | `order.delivered` | `consignment_id`, `store_id`, `collected_amount` |
530
+ | `ORDER_PARTIAL_DELIVERY` | `order.partial-delivery` | `consignment_id`, `collected_amount`, `reason?` |
531
+ | `ORDER_RETURNED` | `order.returned` | `consignment_id`, `reason?` |
532
+ | `ORDER_DELIVERY_FAILED` | `order.delivery-failed` | `consignment_id`, `reason?` |
533
+ | `ORDER_ON_HOLD` | `order.on-hold` | `consignment_id`, `reason?` |
534
+ | `ORDER_PAID` | `order.paid` | `consignment_id`, `invoice_id` |
535
+ | `ORDER_PAID_RETURN` | `order.paid-return` | `consignment_id`, `collected_amount`, `reason?` |
536
+ | `ORDER_EXCHANGED` | `order.exchanged` | `consignment_id`, `collected_amount`, `reason?` |
537
+ | `ORDER_RETURN_ID_CREATED` | `order.return-id-created` | `consignment_id`, `return_consignment_id`, `return_type`, `collected_amount` |
538
+ | `ORDER_RETURN_IN_TRANSIT` | `order.return-in-transit` | `consignment_id`, `return_consignment_id`, `return_type`, `collected_amount` |
539
+ | `ORDER_RETURNED_TO_MERCHANT` | `order.returned-to-merchant` | `consignment_id`, `return_consignment_id`, `return_type`, `collected_amount` |
540
+ | `STORE_CREATED` | `store.created` | `store_id`, `store_name`, `store_address`, `is_active` |
541
+ | `STORE_UPDATED` | `store.updated` | `store_id`, `store_name`, `store_address`, `is_active` |
542
+
543
+ All payloads also include `updated_at` (MySQL datetime) and `timestamp` (ISO 8601).
544
+
545
+ ---
546
+
547
+ ## TypeScript Types
588
548
 
589
549
  ```typescript
590
- import { PathaoApiService, DeliveryType, ItemType } from 'pathao-merchant-sdk';
550
+ import type {
551
+ PathaoConfig,
552
+ PathaoOrderRequest,
553
+ PathaoOrderResponse,
554
+ PathaoStoreRequest,
555
+ PathaoStore,
556
+ PathaoPriceRequest,
557
+ PathaoPriceResponse,
558
+ PathaoOrderStatusResponse,
559
+ DeliveryType,
560
+ ItemType,
561
+ } from "pathao-merchant-sdk";
591
562
 
592
- async function createDeliveryOrder() {
593
- const pathao = new PathaoApiService({
594
- clientId: process.env.PATHAO_CLIENT_ID!,
595
- clientSecret: process.env.PATHAO_CLIENT_SECRET!,
596
- username: process.env.PATHAO_USERNAME!,
597
- password: process.env.PATHAO_PASSWORD!
598
- });
563
+ import type {
564
+ PathaoWebhookPayload,
565
+ WebhookEventPayloadMap,
566
+ OrderDeliveredPayload,
567
+ OrderReturnIdCreatedPayload,
568
+ PathaoWebhookEvent,
569
+ } from "pathao-merchant-sdk/webhooks";
599
570
 
600
- try {
601
- // 1. Calculate price first
602
- const price = await pathao.calculatePrice({
603
- store_id: 123,
604
- item_type: ItemType.PARCEL,
605
- item_weight: 1.0,
606
- delivery_type: DeliveryType.NORMAL,
607
- recipient_city: 1,
608
- recipient_zone: 1,
609
- recipient_area: 1
610
- });
611
-
612
- console.log('Estimated cost:', price.data.total_charge);
613
-
614
- // 2. Create the order
615
- const order = await pathao.createOrder({
616
- store_id: 123,
617
- merchant_order_id: `ORDER-${Date.now()}`,
618
- recipient_name: 'John Doe',
619
- recipient_phone: '01712345678',
620
- recipient_address: '123 Main Street, Dhanmondi, Dhaka',
621
- delivery_type: DeliveryType.NORMAL,
622
- item_type: ItemType.PARCEL,
623
- item_quantity: 1,
624
- item_weight: 1.0,
625
- amount_to_collect: 500
626
- });
627
-
628
- console.log('Order created successfully:', {
629
- consignmentId: order.data.consignment_id,
630
- invoiceId: order.data.invoice_id,
631
- status: order.data.status
632
- });
633
-
634
- // 3. Track the order
635
- const status = await pathao.getOrderStatus(order.data.consignment_id);
636
- console.log('Current status:', status.data.status);
637
-
638
- } catch (error) {
639
- console.error('Delivery order failed:', error.message);
640
- }
641
- }
571
+ // Access a specific payload type via the map
572
+ type PaidPayload = WebhookEventPayloadMap[PathaoWebhookEvent.ORDER_PAID];
642
573
  ```
643
574
 
644
- ## Official Documentation
645
-
646
- This SDK is based on the official Pathao Courier Merchant API documentation. For complete API reference and details:
647
-
648
- - **[Official API Documentation](./docs/official-pathao-api-documentation.md)** - Complete Pathao API guide
649
- - **[API Reference](./docs/pathao-api-reference.txt)** - Original API reference document
650
- - **[Pathao Merchant Portal](https://merchant.pathao.com)** - Official merchant dashboard
575
+ ---
651
576
 
652
577
  ## Contributing
653
578
 
654
- Contributions are welcome! Please feel free to submit a Pull Request.
579
+ Contributions are welcome. Please open an issue first for significant changes.
580
+
581
+ 1. Fork the repo
582
+ 2. Create a feature branch
583
+ 3. Run `pnpm test` and `pnpm run type-check` before submitting
655
584
 
656
585
  ## Development
657
586
 
658
- - Build: `npm run build`
659
- - Tests: `npm test`
660
- - Lint: `npm run lint`
661
- - Type check: `npm run type-check`
587
+ ```bash
588
+ pnpm install
589
+ pnpm run build # compile CJS + ESM + .d.ts
590
+ pnpm test # run Jest test suite
591
+ pnpm run type-check # tsc --noEmit
592
+ pnpm run lint # ESLint
593
+ ```
662
594
 
663
595
  ## License
664
596
 
665
- This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
597
+ MIT — see [LICENSE](LICENSE).
666
598
 
667
599
  ## Support
668
600
 
669
- For support, please open an issue on GitHub or contact me at sifatjasim@gmail.com.
670
-
671
- ## Disclaimer
601
+ Open an issue on [GitHub](https://github.com/sifat07/pathao-merchant-sdk/issues).
672
602
 
673
- This is an **unofficial** SDK and is not affiliated with or endorsed by Pathao. Use at your own risk. The maintainers are not responsible for any issues that may arise from using this package.
603
+ ---
674
604
 
675
605
  ## Changelog
676
606
 
607
+ ### 2.3.0 — 2026-04-16
608
+
609
+ - Replaced broken `pnpm audit` with OSV Scanner (scoped to production dependencies)
610
+ - Fixed release-please to read from manifest — prevents wrong version PRs
611
+ - Fixed CI/CD: pinned pnpm to v9, added `permissions` blocks, fixed `manual-release.yml` broken scripts and step ordering
612
+
613
+ ### 2.2.0 — 2026-04-16
614
+
615
+ - Added 3 missing webhook event types from official dashboard docs: `order.return-id-created`, `order.return-in-transit`, `order.returned-to-merchant` with full `ReturnOrderWebhookPayload` type
616
+ - Fixed tsconfig: added `node` and `jest` to `types` so `Buffer`/`EventEmitter` resolve correctly
617
+ - Fixed webhook header handling and improved event processing robustness
618
+
677
619
  ### 2.1.0
678
- - Added webhook support via `pathao-merchant-sdk/webhooks` sub-path
679
- - `PathaoWebhookHandler` — EventEmitter with typed `on()` overloads for all 21 event types
680
- - `constructEvent()` and `verifySignature()` standalone helpers
620
+
621
+ - Added `pathao-merchant-sdk/webhooks` sub-path entry point
622
+ - `PathaoWebhookHandler` — EventEmitter with typed `on()` overloads for all event types
623
+ - `constructEvent()` standalone helper
681
624
  - Express middleware and generic async middleware
682
- - Constant-time signature comparison via `crypto.timingSafeEqual`
683
- - Fixed case-insensitive header lookup for `X-PATHAO-Signature`
684
625
 
685
626
  ### 2.0.x
686
- - Added retry logic: 429 reads `Retry-After`, 5xx exponential backoff (max 2 retries)
687
- - Added circuit breaker: throws `PathaoApiError` (code 503) when open
688
- - Added `sandbox()` and `production()` named constructors
689
- - Deferred config validation to first API call
690
- - HTTPS-only enforcement in `validateConfiguration()`
691
- - Auth token redacted as `Bearer [REDACTED]` in debug logs
692
- - Added `User-Agent: pathao-merchant-sdk node/<version>` header
693
- - `getStores()` accepts optional `page` parameter
694
- - `validateContactNumber` requires `startsWith('01')`
627
+
628
+ - Factory methods: `fromEnv()`, `fromConfig()`, `sandbox()`, `production()`
629
+ - Debug logging (`debug` option) — `Authorization` header redacted
630
+ - Configurable circuit breaker (throws `PathaoApiError` code 503 when open)
631
+ - Retry logic: 429 reads `Retry-After`, 5xx exponential backoff (max 2 retries)
632
+ - Deferred config validation — constructor never throws
633
+ - HTTPS enforcement in `validateConfiguration()`
634
+ - `User-Agent: pathao-merchant-sdk node/<version>` header
635
+ - `getStores(page?)` pagination parameter
695
636
  - `is_active` and `cod_enabled` typed as `0 | 1`
696
- - Fixed shell injection in `scripts/release.js` (replaced `execSync` with `spawnSync`)
697
- - CI/CD: replaced deprecated actions, added version-existence check before publish
637
+ - Fixed shell injection in `scripts/release.js`
698
638
 
699
639
  ### 1.0.0
700
- - Initial release
701
- - Complete Pathao API integration
702
- - TypeScript support
703
- - Automatic authentication
704
- - Order management
705
- - Store management
706
- - Price calculation
707
- - Location services
640
+
641
+ - Initial release — order management, store management, price calculation, location services, automatic OAuth2