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.
@@ -67,6 +67,9 @@ declare enum PathaoWebhookEvent {
67
67
  ORDER_PAID = "order.paid",
68
68
  ORDER_PAID_RETURN = "order.paid-return",
69
69
  ORDER_EXCHANGED = "order.exchanged",
70
+ ORDER_RETURN_ID_CREATED = "order.return-id-created",
71
+ ORDER_RETURN_IN_TRANSIT = "order.return-in-transit",
72
+ ORDER_RETURNED_TO_MERCHANT = "order.returned-to-merchant",
70
73
  STORE_CREATED = "store.created",
71
74
  STORE_UPDATED = "store.updated"
72
75
  }
@@ -166,6 +169,25 @@ interface OrderExchangedPayload extends OrderWebhookPayload {
166
169
  collected_amount: number;
167
170
  reason?: string;
168
171
  }
172
+ /** Shared fields for the three return-journey events */
173
+ interface ReturnOrderWebhookPayload extends BaseWebhookPayload {
174
+ consignment_id: string;
175
+ return_consignment_id: string;
176
+ merchant_order_id?: string;
177
+ store_id: number;
178
+ collected_amount: number;
179
+ return_type: 'return' | 'paid-return' | 'exchange' | 'partial-delivery';
180
+ reason?: string;
181
+ }
182
+ interface OrderReturnIdCreatedPayload extends ReturnOrderWebhookPayload {
183
+ event: PathaoWebhookEvent.ORDER_RETURN_ID_CREATED;
184
+ }
185
+ interface OrderReturnInTransitPayload extends ReturnOrderWebhookPayload {
186
+ event: PathaoWebhookEvent.ORDER_RETURN_IN_TRANSIT;
187
+ }
188
+ interface OrderReturnedToMerchantPayload extends ReturnOrderWebhookPayload {
189
+ event: PathaoWebhookEvent.ORDER_RETURNED_TO_MERCHANT;
190
+ }
169
191
  interface StoreCreatedPayload extends StoreWebhookPayload {
170
192
  event: PathaoWebhookEvent.STORE_CREATED;
171
193
  }
@@ -173,7 +195,7 @@ interface StoreUpdatedPayload extends StoreWebhookPayload {
173
195
  event: PathaoWebhookEvent.STORE_UPDATED;
174
196
  }
175
197
  /** Union of all possible webhook payloads */
176
- type PathaoWebhookPayload = WebhookIntegrationPayload | OrderCreatedPayload | OrderUpdatedPayload | OrderPickupRequestedPayload | OrderAssignedForPickupPayload | OrderPickedPayload | OrderPickupFailedPayload | OrderPickupCancelledPayload | OrderAtSortingHubPayload | OrderInTransitPayload | OrderAtLastMileHubPayload | OrderAssignedForDeliveryPayload | OrderDeliveredPayload | OrderPartialDeliveryPayload | OrderReturnedPayload | OrderDeliveryFailedPayload | OrderOnHoldPayload | OrderPaidPayload | OrderPaidReturnPayload | OrderExchangedPayload | StoreCreatedPayload | StoreUpdatedPayload;
198
+ type PathaoWebhookPayload = WebhookIntegrationPayload | OrderCreatedPayload | OrderUpdatedPayload | OrderPickupRequestedPayload | OrderAssignedForPickupPayload | OrderPickedPayload | OrderPickupFailedPayload | OrderPickupCancelledPayload | OrderAtSortingHubPayload | OrderInTransitPayload | OrderAtLastMileHubPayload | OrderAssignedForDeliveryPayload | OrderDeliveredPayload | OrderPartialDeliveryPayload | OrderReturnedPayload | OrderDeliveryFailedPayload | OrderOnHoldPayload | OrderPaidPayload | OrderPaidReturnPayload | OrderExchangedPayload | OrderReturnIdCreatedPayload | OrderReturnInTransitPayload | OrderReturnedToMerchantPayload | StoreCreatedPayload | StoreUpdatedPayload;
177
199
  /** Maps each \`PathaoWebhookEvent\` to its specific payload type */
178
200
  interface WebhookEventPayloadMap {
179
201
  [PathaoWebhookEvent.WEBHOOK_INTEGRATION]: WebhookIntegrationPayload;
@@ -196,6 +218,9 @@ interface WebhookEventPayloadMap {
196
218
  [PathaoWebhookEvent.ORDER_PAID]: OrderPaidPayload;
197
219
  [PathaoWebhookEvent.ORDER_PAID_RETURN]: OrderPaidReturnPayload;
198
220
  [PathaoWebhookEvent.ORDER_EXCHANGED]: OrderExchangedPayload;
221
+ [PathaoWebhookEvent.ORDER_RETURN_ID_CREATED]: OrderReturnIdCreatedPayload;
222
+ [PathaoWebhookEvent.ORDER_RETURN_IN_TRANSIT]: OrderReturnInTransitPayload;
223
+ [PathaoWebhookEvent.ORDER_RETURNED_TO_MERCHANT]: OrderReturnedToMerchantPayload;
199
224
  [PathaoWebhookEvent.STORE_CREATED]: StoreCreatedPayload;
200
225
  [PathaoWebhookEvent.STORE_UPDATED]: StoreUpdatedPayload;
201
226
  }
@@ -309,4 +334,4 @@ declare class PathaoWebhookHandler extends EventEmitter {
309
334
  middleware(): (rawBody: Buffer | string | object) => Promise<WebhookResponseInstructions>;
310
335
  }
311
336
 
312
- export { type BaseWebhookPayload, type OrderAssignedForDeliveryPayload, type OrderAssignedForPickupPayload, type OrderAtLastMileHubPayload, type OrderAtSortingHubPayload, type OrderCreatedPayload, type OrderDeliveredPayload, type OrderDeliveryFailedPayload, type OrderExchangedPayload, type OrderInTransitPayload, type OrderOnHoldPayload, type OrderPaidPayload, type OrderPaidReturnPayload, type OrderPartialDeliveryPayload, type OrderPickedPayload, type OrderPickupCancelledPayload, type OrderPickupFailedPayload, type OrderPickupRequestedPayload, type OrderReturnedPayload, type OrderUpdatedPayload, type OrderWebhookPayload, PATHAO_SECRET_HEADER, PathaoWebhookError, PathaoWebhookEvent, PathaoWebhookHandler, type PathaoWebhookPayload, type StoreCreatedPayload, type StoreUpdatedPayload, type StoreWebhookPayload, type WebhookEventPayloadMap, type WebhookIntegrationPayload, type WebhookResponseInstructions, constructEvent };
337
+ export { type BaseWebhookPayload, type OrderAssignedForDeliveryPayload, type OrderAssignedForPickupPayload, type OrderAtLastMileHubPayload, type OrderAtSortingHubPayload, type OrderCreatedPayload, type OrderDeliveredPayload, type OrderDeliveryFailedPayload, type OrderExchangedPayload, type OrderInTransitPayload, type OrderOnHoldPayload, type OrderPaidPayload, type OrderPaidReturnPayload, type OrderPartialDeliveryPayload, type OrderPickedPayload, type OrderPickupCancelledPayload, type OrderPickupFailedPayload, type OrderPickupRequestedPayload, type OrderReturnIdCreatedPayload, type OrderReturnInTransitPayload, type OrderReturnedPayload, type OrderReturnedToMerchantPayload, type OrderUpdatedPayload, type OrderWebhookPayload, PATHAO_SECRET_HEADER, PathaoWebhookError, PathaoWebhookEvent, PathaoWebhookHandler, type PathaoWebhookPayload, type ReturnOrderWebhookPayload, type StoreCreatedPayload, type StoreUpdatedPayload, type StoreWebhookPayload, type WebhookEventPayloadMap, type WebhookIntegrationPayload, type WebhookResponseInstructions, constructEvent };
package/dist/webhooks.js CHANGED
@@ -30,6 +30,9 @@ var PathaoWebhookEvent = /* @__PURE__ */ ((PathaoWebhookEvent2) => {
30
30
  PathaoWebhookEvent2["ORDER_PAID"] = "order.paid";
31
31
  PathaoWebhookEvent2["ORDER_PAID_RETURN"] = "order.paid-return";
32
32
  PathaoWebhookEvent2["ORDER_EXCHANGED"] = "order.exchanged";
33
+ PathaoWebhookEvent2["ORDER_RETURN_ID_CREATED"] = "order.return-id-created";
34
+ PathaoWebhookEvent2["ORDER_RETURN_IN_TRANSIT"] = "order.return-in-transit";
35
+ PathaoWebhookEvent2["ORDER_RETURNED_TO_MERCHANT"] = "order.returned-to-merchant";
33
36
  PathaoWebhookEvent2["STORE_CREATED"] = "store.created";
34
37
  PathaoWebhookEvent2["STORE_UPDATED"] = "store.updated";
35
38
  return PathaoWebhookEvent2;
@@ -49,7 +52,7 @@ function constructEvent(rawBody) {
49
52
  }
50
53
  if (!parsed || typeof parsed !== "object" || !("event" in parsed)) {
51
54
  throw new PathaoWebhookError(
52
- "Webhook payload is missing the required `event` field."
55
+ "Webhook payload is missing the required 'event' field."
53
56
  );
54
57
  }
55
58
  return parsed;
@@ -86,7 +89,9 @@ var PathaoWebhookHandler = class extends events.EventEmitter {
86
89
  try {
87
90
  payload = constructEvent(rawBody);
88
91
  } catch (err) {
89
- const webhookErr = err instanceof PathaoWebhookError ? err : new PathaoWebhookError(err instanceof Error ? err.message : "Unknown error");
92
+ const webhookErr = err instanceof PathaoWebhookError ? err : new PathaoWebhookError(
93
+ err instanceof Error ? err.message : "Unknown error"
94
+ );
90
95
  if (this.listenerCount("error") > 0) {
91
96
  this.emit("error", webhookErr);
92
97
  }
@@ -138,21 +143,21 @@ var PathaoWebhookHandler = class extends events.EventEmitter {
138
143
  * \`\`\`typescript
139
144
  * const handle = handler.middleware();
140
145
  * const instructions = await handle(request.body);
141
- *
146
+ *
142
147
  * // Apply the required headers (the secret header)
143
148
  * for (const [key, value] of Object.entries(instructions.headers)) {
144
149
  * reply.header(key, value);
145
150
  * }
146
- *
151
+ *
147
152
  * if (instructions.error) {
148
153
  * return reply.status(instructions.statusCode).send({ error: instructions.error.message });
149
154
  * }
150
- *
155
+ *
151
156
  * // If it was the handshake, we should just return 202 as instructed
152
157
  * if (instructions.payload?.event === 'webhook_integration') {
153
158
  * return reply.status(instructions.statusCode).send();
154
159
  * }
155
- *
160
+ *
156
161
  * // Process your real webhook
157
162
  * return reply.status(instructions.statusCode).send({ received: true });
158
163
  * \`\`\`
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhooks.ts"],"names":["PathaoWebhookEvent","EventEmitter"],"mappings":"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","file":"webhooks.js","sourcesContent":["/**\n * Pathao Webhook Support\n *\n * Handles incoming webhook events from Pathao.\n *\n * IMPORTANT INTEGRATION DETAILS:\n * - Pathao does NOT sign incoming requests.\n * - Instead, Pathao requires you to prove ownership by echoing your webhook secret\n * in the \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header of EVERY response.\n * - This SDK automatically handles the \\`webhook_integration\\` handshake event, which\n * expects a 202 status code and the secret header.\n *\n * @example — Express\n * \\`\\`\\`typescript\n * import express from 'express';\n * import {\n * PathaoWebhookHandler,\n * PathaoWebhookEvent,\n * } from 'pathao-merchant-sdk/webhooks';\n *\n * const app = express();\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => { ... });\n *\n * // Mount express.json() BEFORE the webhook middleware\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => {\n * // The middleware already sets the required secret header.\n * // You just need to return a 200 OK for standard events.\n * res.status(200).send('OK');\n * }\n * );\n * \\`\\`\\`\n */\n\nimport { EventEmitter } from 'events';\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nexport class PathaoWebhookError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PathaoWebhookError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Event types\n// ---------------------------------------------------------------------------\n\n/**\n * All webhook event types emitted by Pathao.\n * Values are the exact strings that appear in the \\`event\\` field of each payload.\n */\nexport enum PathaoWebhookEvent {\n WEBHOOK_INTEGRATION = 'webhook_integration',\n ORDER_CREATED = 'order.created',\n ORDER_UPDATED = 'order.updated',\n ORDER_PICKUP_REQUESTED = 'order.pickup-requested',\n ORDER_ASSIGNED_FOR_PICKUP = 'order.assigned-for-pickup',\n ORDER_PICKED = 'order.picked',\n ORDER_PICKUP_FAILED = 'order.pickup-failed',\n ORDER_PICKUP_CANCELLED = 'order.pickup-cancelled',\n ORDER_AT_THE_SORTING_HUB = 'order.at-the-sorting-hub',\n ORDER_IN_TRANSIT = 'order.in-transit',\n ORDER_RECEIVED_AT_LAST_MILE_HUB = 'order.received-at-last-mile-hub',\n ORDER_ASSIGNED_FOR_DELIVERY = 'order.assigned-for-delivery',\n ORDER_DELIVERED = 'order.delivered',\n ORDER_PARTIAL_DELIVERY = 'order.partial-delivery',\n ORDER_RETURNED = 'order.returned',\n ORDER_DELIVERY_FAILED = 'order.delivery-failed',\n ORDER_ON_HOLD = 'order.on-hold',\n ORDER_PAID = 'order.paid',\n ORDER_PAID_RETURN = 'order.paid-return',\n ORDER_EXCHANGED = 'order.exchanged',\n STORE_CREATED = 'store.created',\n STORE_UPDATED = 'store.updated',\n}\n\n// ---------------------------------------------------------------------------\n// Payload types\n// ---------------------------------------------------------------------------\n\nexport interface WebhookIntegrationPayload {\n event: PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n}\n\n/** Fields present on every normal webhook payload */\nexport interface BaseWebhookPayload {\n event: string;\n /** Format: MySQL datetime YYYY-MM-DD HH:MM:SS (no timezone indicator) */\n updated_at: string;\n /** Format: ISO 8601 timestamp */\n timestamp: string;\n}\n\n/** Fields shared by all order-related events */\nexport interface OrderWebhookPayload extends BaseWebhookPayload {\n consignment_id: string;\n merchant_order_id?: string;\n store_id: number;\n delivery_fee?: number;\n}\n\n/** Fields shared by all store-related events */\nexport interface StoreWebhookPayload extends BaseWebhookPayload {\n store_id: number;\n store_name: string;\n store_address: string;\n is_active: 0 | 1;\n}\n\n// Per-event payload types ────────────────────────────────────────────────────\n\nexport interface OrderCreatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_CREATED;\n delivery_fee: number;\n}\n\nexport interface OrderUpdatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_UPDATED;\n delivery_fee: number;\n}\n\nexport interface OrderPickupRequestedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_REQUESTED;\n delivery_fee: number;\n}\n\nexport interface OrderAssignedForPickupPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP;\n}\n\nexport interface OrderPickedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKED;\n}\n\nexport interface OrderPickupFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_FAILED;\n}\n\nexport interface OrderPickupCancelledPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_CANCELLED;\n}\n\nexport interface OrderAtSortingHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB;\n}\n\nexport interface OrderInTransitPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_IN_TRANSIT;\n}\n\nexport interface OrderAtLastMileHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB;\n}\n\nexport interface OrderAssignedForDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY;\n}\n\nexport interface OrderDeliveredPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERED;\n collected_amount: number;\n}\n\nexport interface OrderPartialDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderReturnedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURNED;\n reason?: string;\n}\n\nexport interface OrderDeliveryFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERY_FAILED;\n reason?: string;\n}\n\nexport interface OrderOnHoldPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ON_HOLD;\n reason?: string;\n}\n\nexport interface OrderPaidPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID;\n invoice_id: string;\n}\n\nexport interface OrderPaidReturnPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID_RETURN;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderExchangedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_EXCHANGED;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface StoreCreatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_CREATED;\n}\n\nexport interface StoreUpdatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_UPDATED;\n}\n\n/** Union of all possible webhook payloads */\nexport type PathaoWebhookPayload =\n | WebhookIntegrationPayload\n | OrderCreatedPayload\n | OrderUpdatedPayload\n | OrderPickupRequestedPayload\n | OrderAssignedForPickupPayload\n | OrderPickedPayload\n | OrderPickupFailedPayload\n | OrderPickupCancelledPayload\n | OrderAtSortingHubPayload\n | OrderInTransitPayload\n | OrderAtLastMileHubPayload\n | OrderAssignedForDeliveryPayload\n | OrderDeliveredPayload\n | OrderPartialDeliveryPayload\n | OrderReturnedPayload\n | OrderDeliveryFailedPayload\n | OrderOnHoldPayload\n | OrderPaidPayload\n | OrderPaidReturnPayload\n | OrderExchangedPayload\n | StoreCreatedPayload\n | StoreUpdatedPayload;\n\n/** Maps each \\`PathaoWebhookEvent\\` to its specific payload type */\nexport interface WebhookEventPayloadMap {\n [PathaoWebhookEvent.WEBHOOK_INTEGRATION]: WebhookIntegrationPayload;\n [PathaoWebhookEvent.ORDER_CREATED]: OrderCreatedPayload;\n [PathaoWebhookEvent.ORDER_UPDATED]: OrderUpdatedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_REQUESTED]: OrderPickupRequestedPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP]: OrderAssignedForPickupPayload;\n [PathaoWebhookEvent.ORDER_PICKED]: OrderPickedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_FAILED]: OrderPickupFailedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_CANCELLED]: OrderPickupCancelledPayload;\n [PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB]: OrderAtSortingHubPayload;\n [PathaoWebhookEvent.ORDER_IN_TRANSIT]: OrderInTransitPayload;\n [PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB]: OrderAtLastMileHubPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY]: OrderAssignedForDeliveryPayload;\n [PathaoWebhookEvent.ORDER_DELIVERED]: OrderDeliveredPayload;\n [PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY]: OrderPartialDeliveryPayload;\n [PathaoWebhookEvent.ORDER_RETURNED]: OrderReturnedPayload;\n [PathaoWebhookEvent.ORDER_DELIVERY_FAILED]: OrderDeliveryFailedPayload;\n [PathaoWebhookEvent.ORDER_ON_HOLD]: OrderOnHoldPayload;\n [PathaoWebhookEvent.ORDER_PAID]: OrderPaidPayload;\n [PathaoWebhookEvent.ORDER_PAID_RETURN]: OrderPaidReturnPayload;\n [PathaoWebhookEvent.ORDER_EXCHANGED]: OrderExchangedPayload;\n [PathaoWebhookEvent.STORE_CREATED]: StoreCreatedPayload;\n [PathaoWebhookEvent.STORE_UPDATED]: StoreUpdatedPayload;\n}\n\n// ---------------------------------------------------------------------------\n// Core functions\n// ---------------------------------------------------------------------------\n\n/** Required response header used to authorize your endpoint with Pathao */\nexport const PATHAO_SECRET_HEADER = 'x-pathao-merchant-webhook-integration-secret';\n\n/**\n * Parses the raw body into a webhook payload.\n * Pathao does not sign inbound requests, so this just ensures it is valid JSON with an event field.\n *\n * Throws \\`PathaoWebhookError\\` on malformed JSON or missing event.\n *\n * @param rawBody Raw request body or parsed object\n */\nexport function constructEvent(\n rawBody: Buffer | string | object,\n): PathaoWebhookPayload {\n let parsed: unknown;\n if (typeof rawBody === 'object' && !Buffer.isBuffer(rawBody)) {\n parsed = rawBody;\n } else {\n const bodyStr = Buffer.isBuffer(rawBody) ? rawBody.toString('utf8') : rawBody;\n try {\n parsed = JSON.parse(bodyStr);\n } catch {\n throw new PathaoWebhookError('Webhook payload is not valid JSON.');\n }\n }\n\n if (\n !parsed ||\n typeof parsed !== 'object' ||\n !('event' in parsed)\n ) {\n throw new PathaoWebhookError(\n 'Webhook payload is missing the required \\`event\\` field.',\n );\n }\n\n return parsed as PathaoWebhookPayload;\n}\n\n// ---------------------------------------------------------------------------\n// PathaoWebhookHandler\n// ---------------------------------------------------------------------------\n\nexport type WebhookResponseInstructions = {\n statusCode: number;\n headers: Record<string, string>;\n payload: PathaoWebhookPayload | null;\n error: PathaoWebhookError | null;\n};\n\n/**\n * Stateful webhook handler that parses payloads, provides response instructions,\n * and dispatches events to typed listeners.\n *\n * @example\n * \\`\\`\\`typescript\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => {\n * // payload is fully typed as OrderDeliveredPayload\n * console.log(payload.consignment_id, payload.collected_amount);\n * });\n * \\`\\`\\`\n */\nexport class PathaoWebhookHandler extends EventEmitter {\n private readonly webhookSecret: string;\n\n constructor(webhookSecret: string) {\n super();\n if (!webhookSecret) {\n throw new PathaoWebhookError(\n 'webhookSecret is required to create a PathaoWebhookHandler.',\n );\n }\n this.webhookSecret = webhookSecret;\n }\n\n // Typed on() overloads ──────────────────────────────────────────────────\n\n /** Listen for a specific Pathao event with a fully-typed payload callback. */\n on<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n /** Fires for every successfully parsed event regardless of type. */\n on(event: 'webhook', listener: (payload: PathaoWebhookPayload) => void): this;\n /** Fires when parsing fails. */\n on(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n on(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /** Listen once for a specific Pathao event. */\n once<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n once(event: 'webhook', listener: (payload: PathaoWebhookPayload) => void): this;\n once(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n once(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.once(event, listener);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Parse the body and dispatch the event.\n *\n * Throws \\`PathaoWebhookError\\` on failure and also emits \\`'error'\\` so\n * listeners can respond without a try/catch.\n *\n * @param rawBody Raw request body or matched json object\n */\n process(rawBody: Buffer | string | object): PathaoWebhookPayload {\n let payload: PathaoWebhookPayload;\n\n try {\n payload = constructEvent(rawBody);\n } catch (err) {\n const webhookErr = err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(err instanceof Error ? err.message : 'Unknown error');\n if (this.listenerCount('error') > 0) {\n this.emit('error', webhookErr);\n }\n throw webhookErr;\n }\n\n this.emit(payload.event, payload);\n this.emit('webhook', payload);\n\n return payload;\n }\n\n /**\n * Returns an Express-compatible middleware function.\n *\n * Automatically sets the required \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header.\n * Automatically responds with 202 for the \\`webhook_integration\\` handshake.\n * For standard events, attaches the payload to \\`req.pathaoWebhook\\` and calls \\`next()\\`.\n * On error, calls \\`next(err)\\`.\n *\n * \\`\\`\\`typescript\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => res.sendStatus(200) // You must send 200 for other events\n * );\n * \\`\\`\\`\n */\n expressMiddleware() {\n return (\n req: {\n body: Buffer | string | object;\n pathaoWebhook?: PathaoWebhookPayload;\n },\n res: {\n setHeader: (name: string, value: string) => void;\n status: (code: number) => { send: () => void };\n },\n next: (err?: unknown) => void,\n ): void => {\n try {\n res.setHeader(PATHAO_SECRET_HEADER, this.webhookSecret);\n \n const payload = this.process(req.body);\n req.pathaoWebhook = payload;\n \n if (payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION) {\n res.status(202).send();\n return;\n }\n\n next();\n } catch (err) {\n next(err);\n }\n };\n }\n\n /**\n * Returns response instructions for any framework (Fastify, Hono, etc.).\n *\n * Never throws — always resolves with a \\`WebhookResponseInstructions\\` object\n * that tells you which status code and headers to return, along with the payload/error.\n *\n * \\`\\`\\`typescript\n * const handle = handler.middleware();\n * const instructions = await handle(request.body);\n * \n * // Apply the required headers (the secret header)\n * for (const [key, value] of Object.entries(instructions.headers)) {\n * reply.header(key, value);\n * }\n * \n * if (instructions.error) {\n * return reply.status(instructions.statusCode).send({ error: instructions.error.message });\n * }\n * \n * // If it was the handshake, we should just return 202 as instructed\n * if (instructions.payload?.event === 'webhook_integration') {\n * return reply.status(instructions.statusCode).send();\n * }\n * \n * // Process your real webhook\n * return reply.status(instructions.statusCode).send({ received: true });\n * \\`\\`\\`\n */\n middleware() {\n return async (\n rawBody: Buffer | string | object,\n ): Promise<WebhookResponseInstructions> => {\n const headers = { [PATHAO_SECRET_HEADER]: this.webhookSecret };\n \n try {\n const payload = this.process(rawBody);\n const isHandshake = payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n return {\n statusCode: isHandshake ? 202 : 200,\n headers,\n payload,\n error: null\n };\n } catch (err) {\n const webhookErr = err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(\n err instanceof Error ? err.message : 'Unknown error',\n );\n return {\n statusCode: 400,\n headers,\n payload: null,\n error: webhookErr\n };\n }\n };\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/webhooks.ts"],"names":["PathaoWebhookEvent","EventEmitter"],"mappings":"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","file":"webhooks.js","sourcesContent":["/**\n * Pathao Webhook Support\n *\n * Handles incoming webhook events from Pathao.\n *\n * IMPORTANT INTEGRATION DETAILS:\n * - Pathao does NOT sign incoming requests.\n * - Instead, Pathao requires you to prove ownership by echoing your webhook secret\n * in the \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header of EVERY response.\n * - This SDK automatically handles the \\`webhook_integration\\` handshake event, which\n * expects a 202 status code and the secret header.\n *\n * @example — Express\n * \\`\\`\\`typescript\n * import express from 'express';\n * import {\n * PathaoWebhookHandler,\n * PathaoWebhookEvent,\n * } from 'pathao-merchant-sdk/webhooks';\n *\n * const app = express();\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => { ... });\n *\n * // Mount express.json() BEFORE the webhook middleware\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => {\n * // The middleware already sets the required secret header.\n * // You just need to return a 200 OK for standard events.\n * res.status(200).send('OK');\n * }\n * );\n * \\`\\`\\`\n */\n\nimport { EventEmitter } from 'events';\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nexport class PathaoWebhookError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PathaoWebhookError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Event types\n// ---------------------------------------------------------------------------\n\n/**\n * All webhook event types emitted by Pathao.\n * Values are the exact strings that appear in the \\`event\\` field of each payload.\n */\nexport enum PathaoWebhookEvent {\n WEBHOOK_INTEGRATION = 'webhook_integration',\n ORDER_CREATED = 'order.created',\n ORDER_UPDATED = 'order.updated',\n ORDER_PICKUP_REQUESTED = 'order.pickup-requested',\n ORDER_ASSIGNED_FOR_PICKUP = 'order.assigned-for-pickup',\n ORDER_PICKED = 'order.picked',\n ORDER_PICKUP_FAILED = 'order.pickup-failed',\n ORDER_PICKUP_CANCELLED = 'order.pickup-cancelled',\n ORDER_AT_THE_SORTING_HUB = 'order.at-the-sorting-hub',\n ORDER_IN_TRANSIT = 'order.in-transit',\n ORDER_RECEIVED_AT_LAST_MILE_HUB = 'order.received-at-last-mile-hub',\n ORDER_ASSIGNED_FOR_DELIVERY = 'order.assigned-for-delivery',\n ORDER_DELIVERED = 'order.delivered',\n ORDER_PARTIAL_DELIVERY = 'order.partial-delivery',\n ORDER_RETURNED = 'order.returned',\n ORDER_DELIVERY_FAILED = 'order.delivery-failed',\n ORDER_ON_HOLD = 'order.on-hold',\n ORDER_PAID = 'order.paid',\n ORDER_PAID_RETURN = 'order.paid-return',\n ORDER_EXCHANGED = 'order.exchanged',\n ORDER_RETURN_ID_CREATED = 'order.return-id-created',\n ORDER_RETURN_IN_TRANSIT = 'order.return-in-transit',\n ORDER_RETURNED_TO_MERCHANT = 'order.returned-to-merchant',\n STORE_CREATED = 'store.created',\n STORE_UPDATED = 'store.updated',\n}\n\n// ---------------------------------------------------------------------------\n// Payload types\n// ---------------------------------------------------------------------------\n\nexport interface WebhookIntegrationPayload {\n event: PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n}\n\n/** Fields present on every normal webhook payload */\nexport interface BaseWebhookPayload {\n event: string;\n /** Format: MySQL datetime YYYY-MM-DD HH:MM:SS (no timezone indicator) */\n updated_at: string;\n /** Format: ISO 8601 timestamp */\n timestamp: string;\n}\n\n/** Fields shared by all order-related events */\nexport interface OrderWebhookPayload extends BaseWebhookPayload {\n consignment_id: string;\n merchant_order_id?: string;\n store_id: number;\n delivery_fee?: number;\n}\n\n/** Fields shared by all store-related events */\nexport interface StoreWebhookPayload extends BaseWebhookPayload {\n store_id: number;\n store_name: string;\n store_address: string;\n is_active: 0 | 1;\n}\n\n// Per-event payload types ────────────────────────────────────────────────────\n\nexport interface OrderCreatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_CREATED;\n delivery_fee: number;\n}\n\nexport interface OrderUpdatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_UPDATED;\n delivery_fee: number;\n}\n\nexport interface OrderPickupRequestedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_REQUESTED;\n delivery_fee: number;\n}\n\nexport interface OrderAssignedForPickupPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP;\n}\n\nexport interface OrderPickedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKED;\n}\n\nexport interface OrderPickupFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_FAILED;\n}\n\nexport interface OrderPickupCancelledPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_CANCELLED;\n}\n\nexport interface OrderAtSortingHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB;\n}\n\nexport interface OrderInTransitPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_IN_TRANSIT;\n}\n\nexport interface OrderAtLastMileHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB;\n}\n\nexport interface OrderAssignedForDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY;\n}\n\nexport interface OrderDeliveredPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERED;\n collected_amount: number;\n}\n\nexport interface OrderPartialDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderReturnedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURNED;\n reason?: string;\n}\n\nexport interface OrderDeliveryFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERY_FAILED;\n reason?: string;\n}\n\nexport interface OrderOnHoldPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ON_HOLD;\n reason?: string;\n}\n\nexport interface OrderPaidPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID;\n invoice_id: string;\n}\n\nexport interface OrderPaidReturnPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID_RETURN;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderExchangedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_EXCHANGED;\n collected_amount: number;\n reason?: string;\n}\n\n/** Shared fields for the three return-journey events */\nexport interface ReturnOrderWebhookPayload extends BaseWebhookPayload {\n consignment_id: string;\n return_consignment_id: string;\n merchant_order_id?: string;\n store_id: number;\n collected_amount: number;\n return_type: 'return' | 'paid-return' | 'exchange' | 'partial-delivery';\n reason?: string;\n}\n\nexport interface OrderReturnIdCreatedPayload extends ReturnOrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURN_ID_CREATED;\n}\n\nexport interface OrderReturnInTransitPayload extends ReturnOrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURN_IN_TRANSIT;\n}\n\nexport interface OrderReturnedToMerchantPayload extends ReturnOrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURNED_TO_MERCHANT;\n}\n\nexport interface StoreCreatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_CREATED;\n}\n\nexport interface StoreUpdatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_UPDATED;\n}\n\n/** Union of all possible webhook payloads */\nexport type PathaoWebhookPayload =\n | WebhookIntegrationPayload\n | OrderCreatedPayload\n | OrderUpdatedPayload\n | OrderPickupRequestedPayload\n | OrderAssignedForPickupPayload\n | OrderPickedPayload\n | OrderPickupFailedPayload\n | OrderPickupCancelledPayload\n | OrderAtSortingHubPayload\n | OrderInTransitPayload\n | OrderAtLastMileHubPayload\n | OrderAssignedForDeliveryPayload\n | OrderDeliveredPayload\n | OrderPartialDeliveryPayload\n | OrderReturnedPayload\n | OrderDeliveryFailedPayload\n | OrderOnHoldPayload\n | OrderPaidPayload\n | OrderPaidReturnPayload\n | OrderExchangedPayload\n | OrderReturnIdCreatedPayload\n | OrderReturnInTransitPayload\n | OrderReturnedToMerchantPayload\n | StoreCreatedPayload\n | StoreUpdatedPayload;\n\n/** Maps each \\`PathaoWebhookEvent\\` to its specific payload type */\nexport interface WebhookEventPayloadMap {\n [PathaoWebhookEvent.WEBHOOK_INTEGRATION]: WebhookIntegrationPayload;\n [PathaoWebhookEvent.ORDER_CREATED]: OrderCreatedPayload;\n [PathaoWebhookEvent.ORDER_UPDATED]: OrderUpdatedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_REQUESTED]: OrderPickupRequestedPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP]: OrderAssignedForPickupPayload;\n [PathaoWebhookEvent.ORDER_PICKED]: OrderPickedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_FAILED]: OrderPickupFailedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_CANCELLED]: OrderPickupCancelledPayload;\n [PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB]: OrderAtSortingHubPayload;\n [PathaoWebhookEvent.ORDER_IN_TRANSIT]: OrderInTransitPayload;\n [PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB]: OrderAtLastMileHubPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY]: OrderAssignedForDeliveryPayload;\n [PathaoWebhookEvent.ORDER_DELIVERED]: OrderDeliveredPayload;\n [PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY]: OrderPartialDeliveryPayload;\n [PathaoWebhookEvent.ORDER_RETURNED]: OrderReturnedPayload;\n [PathaoWebhookEvent.ORDER_DELIVERY_FAILED]: OrderDeliveryFailedPayload;\n [PathaoWebhookEvent.ORDER_ON_HOLD]: OrderOnHoldPayload;\n [PathaoWebhookEvent.ORDER_PAID]: OrderPaidPayload;\n [PathaoWebhookEvent.ORDER_PAID_RETURN]: OrderPaidReturnPayload;\n [PathaoWebhookEvent.ORDER_EXCHANGED]: OrderExchangedPayload;\n [PathaoWebhookEvent.ORDER_RETURN_ID_CREATED]: OrderReturnIdCreatedPayload;\n [PathaoWebhookEvent.ORDER_RETURN_IN_TRANSIT]: OrderReturnInTransitPayload;\n [PathaoWebhookEvent.ORDER_RETURNED_TO_MERCHANT]: OrderReturnedToMerchantPayload;\n [PathaoWebhookEvent.STORE_CREATED]: StoreCreatedPayload;\n [PathaoWebhookEvent.STORE_UPDATED]: StoreUpdatedPayload;\n}\n\n// ---------------------------------------------------------------------------\n// Core functions\n// ---------------------------------------------------------------------------\n\n/** Required response header used to authorize your endpoint with Pathao */\nexport const PATHAO_SECRET_HEADER =\n 'x-pathao-merchant-webhook-integration-secret';\n\n/**\n * Parses the raw body into a webhook payload.\n * Pathao does not sign inbound requests, so this just ensures it is valid JSON with an event field.\n *\n * Throws \\`PathaoWebhookError\\` on malformed JSON or missing event.\n *\n * @param rawBody Raw request body or parsed object\n */\nexport function constructEvent(\n rawBody: Buffer | string | object,\n): PathaoWebhookPayload {\n let parsed: unknown;\n if (typeof rawBody === 'object' && !Buffer.isBuffer(rawBody)) {\n parsed = rawBody;\n } else {\n const bodyStr = Buffer.isBuffer(rawBody)\n ? rawBody.toString('utf8')\n : rawBody;\n try {\n parsed = JSON.parse(bodyStr);\n } catch {\n throw new PathaoWebhookError('Webhook payload is not valid JSON.');\n }\n }\n\n if (!parsed || typeof parsed !== 'object' || !('event' in parsed)) {\n throw new PathaoWebhookError(\n \"Webhook payload is missing the required 'event' field.\",\n );\n }\n\n return parsed as PathaoWebhookPayload;\n}\n\n// ---------------------------------------------------------------------------\n// PathaoWebhookHandler\n// ---------------------------------------------------------------------------\n\nexport type WebhookResponseInstructions = {\n statusCode: number;\n headers: Record<string, string>;\n payload: PathaoWebhookPayload | null;\n error: PathaoWebhookError | null;\n};\n\n/**\n * Stateful webhook handler that parses payloads, provides response instructions,\n * and dispatches events to typed listeners.\n *\n * @example\n * \\`\\`\\`typescript\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => {\n * // payload is fully typed as OrderDeliveredPayload\n * console.log(payload.consignment_id, payload.collected_amount);\n * });\n * \\`\\`\\`\n */\nexport class PathaoWebhookHandler extends EventEmitter {\n private readonly webhookSecret: string;\n\n constructor(webhookSecret: string) {\n super();\n if (!webhookSecret) {\n throw new PathaoWebhookError(\n 'webhookSecret is required to create a PathaoWebhookHandler.',\n );\n }\n this.webhookSecret = webhookSecret;\n }\n\n // Typed on() overloads ──────────────────────────────────────────────────\n\n /** Listen for a specific Pathao event with a fully-typed payload callback. */\n on<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n /** Fires for every successfully parsed event regardless of type. */\n on(event: 'webhook', listener: (payload: PathaoWebhookPayload) => void): this;\n /** Fires when parsing fails. */\n on(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n on(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /** Listen once for a specific Pathao event. */\n once<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n once(\n event: 'webhook',\n listener: (payload: PathaoWebhookPayload) => void,\n ): this;\n once(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n once(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.once(event, listener);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Parse the body and dispatch the event.\n *\n * Throws \\`PathaoWebhookError\\` on failure and also emits \\`'error'\\` so\n * listeners can respond without a try/catch.\n *\n * @param rawBody Raw request body or matched json object\n */\n process(rawBody: Buffer | string | object): PathaoWebhookPayload {\n let payload: PathaoWebhookPayload;\n\n try {\n payload = constructEvent(rawBody);\n } catch (err) {\n const webhookErr =\n err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(\n err instanceof Error ? err.message : 'Unknown error',\n );\n if (this.listenerCount('error') > 0) {\n this.emit('error', webhookErr);\n }\n throw webhookErr;\n }\n\n this.emit(payload.event, payload);\n this.emit('webhook', payload);\n\n return payload;\n }\n\n /**\n * Returns an Express-compatible middleware function.\n *\n * Automatically sets the required \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header.\n * Automatically responds with 202 for the \\`webhook_integration\\` handshake.\n * For standard events, attaches the payload to \\`req.pathaoWebhook\\` and calls \\`next()\\`.\n * On error, calls \\`next(err)\\`.\n *\n * \\`\\`\\`typescript\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => res.sendStatus(200) // You must send 200 for other events\n * );\n * \\`\\`\\`\n */\n expressMiddleware() {\n return (\n req: {\n body: Buffer | string | object;\n pathaoWebhook?: PathaoWebhookPayload;\n },\n res: {\n setHeader: (name: string, value: string) => void;\n status: (code: number) => { send: () => void };\n },\n next: (err?: unknown) => void,\n ): void => {\n try {\n res.setHeader(PATHAO_SECRET_HEADER, this.webhookSecret);\n\n const payload = this.process(req.body);\n req.pathaoWebhook = payload;\n\n if (payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION) {\n res.status(202).send();\n return;\n }\n\n next();\n } catch (err) {\n next(err);\n }\n };\n }\n\n /**\n * Returns response instructions for any framework (Fastify, Hono, etc.).\n *\n * Never throws — always resolves with a \\`WebhookResponseInstructions\\` object\n * that tells you which status code and headers to return, along with the payload/error.\n *\n * \\`\\`\\`typescript\n * const handle = handler.middleware();\n * const instructions = await handle(request.body);\n *\n * // Apply the required headers (the secret header)\n * for (const [key, value] of Object.entries(instructions.headers)) {\n * reply.header(key, value);\n * }\n *\n * if (instructions.error) {\n * return reply.status(instructions.statusCode).send({ error: instructions.error.message });\n * }\n *\n * // If it was the handshake, we should just return 202 as instructed\n * if (instructions.payload?.event === 'webhook_integration') {\n * return reply.status(instructions.statusCode).send();\n * }\n *\n * // Process your real webhook\n * return reply.status(instructions.statusCode).send({ received: true });\n * \\`\\`\\`\n */\n middleware() {\n return async (\n rawBody: Buffer | string | object,\n ): Promise<WebhookResponseInstructions> => {\n const headers = { [PATHAO_SECRET_HEADER]: this.webhookSecret };\n\n try {\n const payload = this.process(rawBody);\n const isHandshake =\n payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n return {\n statusCode: isHandshake ? 202 : 200,\n headers,\n payload,\n error: null,\n };\n } catch (err) {\n const webhookErr =\n err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(\n err instanceof Error ? err.message : 'Unknown error',\n );\n return {\n statusCode: 400,\n headers,\n payload: null,\n error: webhookErr,\n };\n }\n };\n }\n}\n"]}
package/dist/webhooks.mjs CHANGED
@@ -28,6 +28,9 @@ var PathaoWebhookEvent = /* @__PURE__ */ ((PathaoWebhookEvent2) => {
28
28
  PathaoWebhookEvent2["ORDER_PAID"] = "order.paid";
29
29
  PathaoWebhookEvent2["ORDER_PAID_RETURN"] = "order.paid-return";
30
30
  PathaoWebhookEvent2["ORDER_EXCHANGED"] = "order.exchanged";
31
+ PathaoWebhookEvent2["ORDER_RETURN_ID_CREATED"] = "order.return-id-created";
32
+ PathaoWebhookEvent2["ORDER_RETURN_IN_TRANSIT"] = "order.return-in-transit";
33
+ PathaoWebhookEvent2["ORDER_RETURNED_TO_MERCHANT"] = "order.returned-to-merchant";
31
34
  PathaoWebhookEvent2["STORE_CREATED"] = "store.created";
32
35
  PathaoWebhookEvent2["STORE_UPDATED"] = "store.updated";
33
36
  return PathaoWebhookEvent2;
@@ -47,7 +50,7 @@ function constructEvent(rawBody) {
47
50
  }
48
51
  if (!parsed || typeof parsed !== "object" || !("event" in parsed)) {
49
52
  throw new PathaoWebhookError(
50
- "Webhook payload is missing the required `event` field."
53
+ "Webhook payload is missing the required 'event' field."
51
54
  );
52
55
  }
53
56
  return parsed;
@@ -84,7 +87,9 @@ var PathaoWebhookHandler = class extends EventEmitter {
84
87
  try {
85
88
  payload = constructEvent(rawBody);
86
89
  } catch (err) {
87
- const webhookErr = err instanceof PathaoWebhookError ? err : new PathaoWebhookError(err instanceof Error ? err.message : "Unknown error");
90
+ const webhookErr = err instanceof PathaoWebhookError ? err : new PathaoWebhookError(
91
+ err instanceof Error ? err.message : "Unknown error"
92
+ );
88
93
  if (this.listenerCount("error") > 0) {
89
94
  this.emit("error", webhookErr);
90
95
  }
@@ -136,21 +141,21 @@ var PathaoWebhookHandler = class extends EventEmitter {
136
141
  * \`\`\`typescript
137
142
  * const handle = handler.middleware();
138
143
  * const instructions = await handle(request.body);
139
- *
144
+ *
140
145
  * // Apply the required headers (the secret header)
141
146
  * for (const [key, value] of Object.entries(instructions.headers)) {
142
147
  * reply.header(key, value);
143
148
  * }
144
- *
149
+ *
145
150
  * if (instructions.error) {
146
151
  * return reply.status(instructions.statusCode).send({ error: instructions.error.message });
147
152
  * }
148
- *
153
+ *
149
154
  * // If it was the handshake, we should just return 202 as instructed
150
155
  * if (instructions.payload?.event === 'webhook_integration') {
151
156
  * return reply.status(instructions.statusCode).send();
152
157
  * }
153
- *
158
+ *
154
159
  * // Process your real webhook
155
160
  * return reply.status(instructions.statusCode).send({ received: true });
156
161
  * \`\`\`
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/webhooks.ts"],"names":["PathaoWebhookEvent"],"mappings":"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","file":"webhooks.mjs","sourcesContent":["/**\n * Pathao Webhook Support\n *\n * Handles incoming webhook events from Pathao.\n *\n * IMPORTANT INTEGRATION DETAILS:\n * - Pathao does NOT sign incoming requests.\n * - Instead, Pathao requires you to prove ownership by echoing your webhook secret\n * in the \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header of EVERY response.\n * - This SDK automatically handles the \\`webhook_integration\\` handshake event, which\n * expects a 202 status code and the secret header.\n *\n * @example — Express\n * \\`\\`\\`typescript\n * import express from 'express';\n * import {\n * PathaoWebhookHandler,\n * PathaoWebhookEvent,\n * } from 'pathao-merchant-sdk/webhooks';\n *\n * const app = express();\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => { ... });\n *\n * // Mount express.json() BEFORE the webhook middleware\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => {\n * // The middleware already sets the required secret header.\n * // You just need to return a 200 OK for standard events.\n * res.status(200).send('OK');\n * }\n * );\n * \\`\\`\\`\n */\n\nimport { EventEmitter } from 'events';\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nexport class PathaoWebhookError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PathaoWebhookError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Event types\n// ---------------------------------------------------------------------------\n\n/**\n * All webhook event types emitted by Pathao.\n * Values are the exact strings that appear in the \\`event\\` field of each payload.\n */\nexport enum PathaoWebhookEvent {\n WEBHOOK_INTEGRATION = 'webhook_integration',\n ORDER_CREATED = 'order.created',\n ORDER_UPDATED = 'order.updated',\n ORDER_PICKUP_REQUESTED = 'order.pickup-requested',\n ORDER_ASSIGNED_FOR_PICKUP = 'order.assigned-for-pickup',\n ORDER_PICKED = 'order.picked',\n ORDER_PICKUP_FAILED = 'order.pickup-failed',\n ORDER_PICKUP_CANCELLED = 'order.pickup-cancelled',\n ORDER_AT_THE_SORTING_HUB = 'order.at-the-sorting-hub',\n ORDER_IN_TRANSIT = 'order.in-transit',\n ORDER_RECEIVED_AT_LAST_MILE_HUB = 'order.received-at-last-mile-hub',\n ORDER_ASSIGNED_FOR_DELIVERY = 'order.assigned-for-delivery',\n ORDER_DELIVERED = 'order.delivered',\n ORDER_PARTIAL_DELIVERY = 'order.partial-delivery',\n ORDER_RETURNED = 'order.returned',\n ORDER_DELIVERY_FAILED = 'order.delivery-failed',\n ORDER_ON_HOLD = 'order.on-hold',\n ORDER_PAID = 'order.paid',\n ORDER_PAID_RETURN = 'order.paid-return',\n ORDER_EXCHANGED = 'order.exchanged',\n STORE_CREATED = 'store.created',\n STORE_UPDATED = 'store.updated',\n}\n\n// ---------------------------------------------------------------------------\n// Payload types\n// ---------------------------------------------------------------------------\n\nexport interface WebhookIntegrationPayload {\n event: PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n}\n\n/** Fields present on every normal webhook payload */\nexport interface BaseWebhookPayload {\n event: string;\n /** Format: MySQL datetime YYYY-MM-DD HH:MM:SS (no timezone indicator) */\n updated_at: string;\n /** Format: ISO 8601 timestamp */\n timestamp: string;\n}\n\n/** Fields shared by all order-related events */\nexport interface OrderWebhookPayload extends BaseWebhookPayload {\n consignment_id: string;\n merchant_order_id?: string;\n store_id: number;\n delivery_fee?: number;\n}\n\n/** Fields shared by all store-related events */\nexport interface StoreWebhookPayload extends BaseWebhookPayload {\n store_id: number;\n store_name: string;\n store_address: string;\n is_active: 0 | 1;\n}\n\n// Per-event payload types ────────────────────────────────────────────────────\n\nexport interface OrderCreatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_CREATED;\n delivery_fee: number;\n}\n\nexport interface OrderUpdatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_UPDATED;\n delivery_fee: number;\n}\n\nexport interface OrderPickupRequestedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_REQUESTED;\n delivery_fee: number;\n}\n\nexport interface OrderAssignedForPickupPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP;\n}\n\nexport interface OrderPickedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKED;\n}\n\nexport interface OrderPickupFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_FAILED;\n}\n\nexport interface OrderPickupCancelledPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_CANCELLED;\n}\n\nexport interface OrderAtSortingHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB;\n}\n\nexport interface OrderInTransitPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_IN_TRANSIT;\n}\n\nexport interface OrderAtLastMileHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB;\n}\n\nexport interface OrderAssignedForDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY;\n}\n\nexport interface OrderDeliveredPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERED;\n collected_amount: number;\n}\n\nexport interface OrderPartialDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderReturnedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURNED;\n reason?: string;\n}\n\nexport interface OrderDeliveryFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERY_FAILED;\n reason?: string;\n}\n\nexport interface OrderOnHoldPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ON_HOLD;\n reason?: string;\n}\n\nexport interface OrderPaidPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID;\n invoice_id: string;\n}\n\nexport interface OrderPaidReturnPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID_RETURN;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderExchangedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_EXCHANGED;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface StoreCreatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_CREATED;\n}\n\nexport interface StoreUpdatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_UPDATED;\n}\n\n/** Union of all possible webhook payloads */\nexport type PathaoWebhookPayload =\n | WebhookIntegrationPayload\n | OrderCreatedPayload\n | OrderUpdatedPayload\n | OrderPickupRequestedPayload\n | OrderAssignedForPickupPayload\n | OrderPickedPayload\n | OrderPickupFailedPayload\n | OrderPickupCancelledPayload\n | OrderAtSortingHubPayload\n | OrderInTransitPayload\n | OrderAtLastMileHubPayload\n | OrderAssignedForDeliveryPayload\n | OrderDeliveredPayload\n | OrderPartialDeliveryPayload\n | OrderReturnedPayload\n | OrderDeliveryFailedPayload\n | OrderOnHoldPayload\n | OrderPaidPayload\n | OrderPaidReturnPayload\n | OrderExchangedPayload\n | StoreCreatedPayload\n | StoreUpdatedPayload;\n\n/** Maps each \\`PathaoWebhookEvent\\` to its specific payload type */\nexport interface WebhookEventPayloadMap {\n [PathaoWebhookEvent.WEBHOOK_INTEGRATION]: WebhookIntegrationPayload;\n [PathaoWebhookEvent.ORDER_CREATED]: OrderCreatedPayload;\n [PathaoWebhookEvent.ORDER_UPDATED]: OrderUpdatedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_REQUESTED]: OrderPickupRequestedPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP]: OrderAssignedForPickupPayload;\n [PathaoWebhookEvent.ORDER_PICKED]: OrderPickedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_FAILED]: OrderPickupFailedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_CANCELLED]: OrderPickupCancelledPayload;\n [PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB]: OrderAtSortingHubPayload;\n [PathaoWebhookEvent.ORDER_IN_TRANSIT]: OrderInTransitPayload;\n [PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB]: OrderAtLastMileHubPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY]: OrderAssignedForDeliveryPayload;\n [PathaoWebhookEvent.ORDER_DELIVERED]: OrderDeliveredPayload;\n [PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY]: OrderPartialDeliveryPayload;\n [PathaoWebhookEvent.ORDER_RETURNED]: OrderReturnedPayload;\n [PathaoWebhookEvent.ORDER_DELIVERY_FAILED]: OrderDeliveryFailedPayload;\n [PathaoWebhookEvent.ORDER_ON_HOLD]: OrderOnHoldPayload;\n [PathaoWebhookEvent.ORDER_PAID]: OrderPaidPayload;\n [PathaoWebhookEvent.ORDER_PAID_RETURN]: OrderPaidReturnPayload;\n [PathaoWebhookEvent.ORDER_EXCHANGED]: OrderExchangedPayload;\n [PathaoWebhookEvent.STORE_CREATED]: StoreCreatedPayload;\n [PathaoWebhookEvent.STORE_UPDATED]: StoreUpdatedPayload;\n}\n\n// ---------------------------------------------------------------------------\n// Core functions\n// ---------------------------------------------------------------------------\n\n/** Required response header used to authorize your endpoint with Pathao */\nexport const PATHAO_SECRET_HEADER = 'x-pathao-merchant-webhook-integration-secret';\n\n/**\n * Parses the raw body into a webhook payload.\n * Pathao does not sign inbound requests, so this just ensures it is valid JSON with an event field.\n *\n * Throws \\`PathaoWebhookError\\` on malformed JSON or missing event.\n *\n * @param rawBody Raw request body or parsed object\n */\nexport function constructEvent(\n rawBody: Buffer | string | object,\n): PathaoWebhookPayload {\n let parsed: unknown;\n if (typeof rawBody === 'object' && !Buffer.isBuffer(rawBody)) {\n parsed = rawBody;\n } else {\n const bodyStr = Buffer.isBuffer(rawBody) ? rawBody.toString('utf8') : rawBody;\n try {\n parsed = JSON.parse(bodyStr);\n } catch {\n throw new PathaoWebhookError('Webhook payload is not valid JSON.');\n }\n }\n\n if (\n !parsed ||\n typeof parsed !== 'object' ||\n !('event' in parsed)\n ) {\n throw new PathaoWebhookError(\n 'Webhook payload is missing the required \\`event\\` field.',\n );\n }\n\n return parsed as PathaoWebhookPayload;\n}\n\n// ---------------------------------------------------------------------------\n// PathaoWebhookHandler\n// ---------------------------------------------------------------------------\n\nexport type WebhookResponseInstructions = {\n statusCode: number;\n headers: Record<string, string>;\n payload: PathaoWebhookPayload | null;\n error: PathaoWebhookError | null;\n};\n\n/**\n * Stateful webhook handler that parses payloads, provides response instructions,\n * and dispatches events to typed listeners.\n *\n * @example\n * \\`\\`\\`typescript\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => {\n * // payload is fully typed as OrderDeliveredPayload\n * console.log(payload.consignment_id, payload.collected_amount);\n * });\n * \\`\\`\\`\n */\nexport class PathaoWebhookHandler extends EventEmitter {\n private readonly webhookSecret: string;\n\n constructor(webhookSecret: string) {\n super();\n if (!webhookSecret) {\n throw new PathaoWebhookError(\n 'webhookSecret is required to create a PathaoWebhookHandler.',\n );\n }\n this.webhookSecret = webhookSecret;\n }\n\n // Typed on() overloads ──────────────────────────────────────────────────\n\n /** Listen for a specific Pathao event with a fully-typed payload callback. */\n on<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n /** Fires for every successfully parsed event regardless of type. */\n on(event: 'webhook', listener: (payload: PathaoWebhookPayload) => void): this;\n /** Fires when parsing fails. */\n on(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n on(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /** Listen once for a specific Pathao event. */\n once<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n once(event: 'webhook', listener: (payload: PathaoWebhookPayload) => void): this;\n once(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n once(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.once(event, listener);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Parse the body and dispatch the event.\n *\n * Throws \\`PathaoWebhookError\\` on failure and also emits \\`'error'\\` so\n * listeners can respond without a try/catch.\n *\n * @param rawBody Raw request body or matched json object\n */\n process(rawBody: Buffer | string | object): PathaoWebhookPayload {\n let payload: PathaoWebhookPayload;\n\n try {\n payload = constructEvent(rawBody);\n } catch (err) {\n const webhookErr = err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(err instanceof Error ? err.message : 'Unknown error');\n if (this.listenerCount('error') > 0) {\n this.emit('error', webhookErr);\n }\n throw webhookErr;\n }\n\n this.emit(payload.event, payload);\n this.emit('webhook', payload);\n\n return payload;\n }\n\n /**\n * Returns an Express-compatible middleware function.\n *\n * Automatically sets the required \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header.\n * Automatically responds with 202 for the \\`webhook_integration\\` handshake.\n * For standard events, attaches the payload to \\`req.pathaoWebhook\\` and calls \\`next()\\`.\n * On error, calls \\`next(err)\\`.\n *\n * \\`\\`\\`typescript\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => res.sendStatus(200) // You must send 200 for other events\n * );\n * \\`\\`\\`\n */\n expressMiddleware() {\n return (\n req: {\n body: Buffer | string | object;\n pathaoWebhook?: PathaoWebhookPayload;\n },\n res: {\n setHeader: (name: string, value: string) => void;\n status: (code: number) => { send: () => void };\n },\n next: (err?: unknown) => void,\n ): void => {\n try {\n res.setHeader(PATHAO_SECRET_HEADER, this.webhookSecret);\n \n const payload = this.process(req.body);\n req.pathaoWebhook = payload;\n \n if (payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION) {\n res.status(202).send();\n return;\n }\n\n next();\n } catch (err) {\n next(err);\n }\n };\n }\n\n /**\n * Returns response instructions for any framework (Fastify, Hono, etc.).\n *\n * Never throws — always resolves with a \\`WebhookResponseInstructions\\` object\n * that tells you which status code and headers to return, along with the payload/error.\n *\n * \\`\\`\\`typescript\n * const handle = handler.middleware();\n * const instructions = await handle(request.body);\n * \n * // Apply the required headers (the secret header)\n * for (const [key, value] of Object.entries(instructions.headers)) {\n * reply.header(key, value);\n * }\n * \n * if (instructions.error) {\n * return reply.status(instructions.statusCode).send({ error: instructions.error.message });\n * }\n * \n * // If it was the handshake, we should just return 202 as instructed\n * if (instructions.payload?.event === 'webhook_integration') {\n * return reply.status(instructions.statusCode).send();\n * }\n * \n * // Process your real webhook\n * return reply.status(instructions.statusCode).send({ received: true });\n * \\`\\`\\`\n */\n middleware() {\n return async (\n rawBody: Buffer | string | object,\n ): Promise<WebhookResponseInstructions> => {\n const headers = { [PATHAO_SECRET_HEADER]: this.webhookSecret };\n \n try {\n const payload = this.process(rawBody);\n const isHandshake = payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n return {\n statusCode: isHandshake ? 202 : 200,\n headers,\n payload,\n error: null\n };\n } catch (err) {\n const webhookErr = err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(\n err instanceof Error ? err.message : 'Unknown error',\n );\n return {\n statusCode: 400,\n headers,\n payload: null,\n error: webhookErr\n };\n }\n };\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/webhooks.ts"],"names":["PathaoWebhookEvent"],"mappings":"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","file":"webhooks.mjs","sourcesContent":["/**\n * Pathao Webhook Support\n *\n * Handles incoming webhook events from Pathao.\n *\n * IMPORTANT INTEGRATION DETAILS:\n * - Pathao does NOT sign incoming requests.\n * - Instead, Pathao requires you to prove ownership by echoing your webhook secret\n * in the \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header of EVERY response.\n * - This SDK automatically handles the \\`webhook_integration\\` handshake event, which\n * expects a 202 status code and the secret header.\n *\n * @example — Express\n * \\`\\`\\`typescript\n * import express from 'express';\n * import {\n * PathaoWebhookHandler,\n * PathaoWebhookEvent,\n * } from 'pathao-merchant-sdk/webhooks';\n *\n * const app = express();\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => { ... });\n *\n * // Mount express.json() BEFORE the webhook middleware\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => {\n * // The middleware already sets the required secret header.\n * // You just need to return a 200 OK for standard events.\n * res.status(200).send('OK');\n * }\n * );\n * \\`\\`\\`\n */\n\nimport { EventEmitter } from 'events';\n\n// ---------------------------------------------------------------------------\n// Error\n// ---------------------------------------------------------------------------\n\nexport class PathaoWebhookError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'PathaoWebhookError';\n }\n}\n\n// ---------------------------------------------------------------------------\n// Event types\n// ---------------------------------------------------------------------------\n\n/**\n * All webhook event types emitted by Pathao.\n * Values are the exact strings that appear in the \\`event\\` field of each payload.\n */\nexport enum PathaoWebhookEvent {\n WEBHOOK_INTEGRATION = 'webhook_integration',\n ORDER_CREATED = 'order.created',\n ORDER_UPDATED = 'order.updated',\n ORDER_PICKUP_REQUESTED = 'order.pickup-requested',\n ORDER_ASSIGNED_FOR_PICKUP = 'order.assigned-for-pickup',\n ORDER_PICKED = 'order.picked',\n ORDER_PICKUP_FAILED = 'order.pickup-failed',\n ORDER_PICKUP_CANCELLED = 'order.pickup-cancelled',\n ORDER_AT_THE_SORTING_HUB = 'order.at-the-sorting-hub',\n ORDER_IN_TRANSIT = 'order.in-transit',\n ORDER_RECEIVED_AT_LAST_MILE_HUB = 'order.received-at-last-mile-hub',\n ORDER_ASSIGNED_FOR_DELIVERY = 'order.assigned-for-delivery',\n ORDER_DELIVERED = 'order.delivered',\n ORDER_PARTIAL_DELIVERY = 'order.partial-delivery',\n ORDER_RETURNED = 'order.returned',\n ORDER_DELIVERY_FAILED = 'order.delivery-failed',\n ORDER_ON_HOLD = 'order.on-hold',\n ORDER_PAID = 'order.paid',\n ORDER_PAID_RETURN = 'order.paid-return',\n ORDER_EXCHANGED = 'order.exchanged',\n ORDER_RETURN_ID_CREATED = 'order.return-id-created',\n ORDER_RETURN_IN_TRANSIT = 'order.return-in-transit',\n ORDER_RETURNED_TO_MERCHANT = 'order.returned-to-merchant',\n STORE_CREATED = 'store.created',\n STORE_UPDATED = 'store.updated',\n}\n\n// ---------------------------------------------------------------------------\n// Payload types\n// ---------------------------------------------------------------------------\n\nexport interface WebhookIntegrationPayload {\n event: PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n}\n\n/** Fields present on every normal webhook payload */\nexport interface BaseWebhookPayload {\n event: string;\n /** Format: MySQL datetime YYYY-MM-DD HH:MM:SS (no timezone indicator) */\n updated_at: string;\n /** Format: ISO 8601 timestamp */\n timestamp: string;\n}\n\n/** Fields shared by all order-related events */\nexport interface OrderWebhookPayload extends BaseWebhookPayload {\n consignment_id: string;\n merchant_order_id?: string;\n store_id: number;\n delivery_fee?: number;\n}\n\n/** Fields shared by all store-related events */\nexport interface StoreWebhookPayload extends BaseWebhookPayload {\n store_id: number;\n store_name: string;\n store_address: string;\n is_active: 0 | 1;\n}\n\n// Per-event payload types ────────────────────────────────────────────────────\n\nexport interface OrderCreatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_CREATED;\n delivery_fee: number;\n}\n\nexport interface OrderUpdatedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_UPDATED;\n delivery_fee: number;\n}\n\nexport interface OrderPickupRequestedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_REQUESTED;\n delivery_fee: number;\n}\n\nexport interface OrderAssignedForPickupPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP;\n}\n\nexport interface OrderPickedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKED;\n}\n\nexport interface OrderPickupFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_FAILED;\n}\n\nexport interface OrderPickupCancelledPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PICKUP_CANCELLED;\n}\n\nexport interface OrderAtSortingHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB;\n}\n\nexport interface OrderInTransitPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_IN_TRANSIT;\n}\n\nexport interface OrderAtLastMileHubPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB;\n}\n\nexport interface OrderAssignedForDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY;\n}\n\nexport interface OrderDeliveredPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERED;\n collected_amount: number;\n}\n\nexport interface OrderPartialDeliveryPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderReturnedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURNED;\n reason?: string;\n}\n\nexport interface OrderDeliveryFailedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_DELIVERY_FAILED;\n reason?: string;\n}\n\nexport interface OrderOnHoldPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_ON_HOLD;\n reason?: string;\n}\n\nexport interface OrderPaidPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID;\n invoice_id: string;\n}\n\nexport interface OrderPaidReturnPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_PAID_RETURN;\n collected_amount: number;\n reason?: string;\n}\n\nexport interface OrderExchangedPayload extends OrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_EXCHANGED;\n collected_amount: number;\n reason?: string;\n}\n\n/** Shared fields for the three return-journey events */\nexport interface ReturnOrderWebhookPayload extends BaseWebhookPayload {\n consignment_id: string;\n return_consignment_id: string;\n merchant_order_id?: string;\n store_id: number;\n collected_amount: number;\n return_type: 'return' | 'paid-return' | 'exchange' | 'partial-delivery';\n reason?: string;\n}\n\nexport interface OrderReturnIdCreatedPayload extends ReturnOrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURN_ID_CREATED;\n}\n\nexport interface OrderReturnInTransitPayload extends ReturnOrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURN_IN_TRANSIT;\n}\n\nexport interface OrderReturnedToMerchantPayload extends ReturnOrderWebhookPayload {\n event: PathaoWebhookEvent.ORDER_RETURNED_TO_MERCHANT;\n}\n\nexport interface StoreCreatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_CREATED;\n}\n\nexport interface StoreUpdatedPayload extends StoreWebhookPayload {\n event: PathaoWebhookEvent.STORE_UPDATED;\n}\n\n/** Union of all possible webhook payloads */\nexport type PathaoWebhookPayload =\n | WebhookIntegrationPayload\n | OrderCreatedPayload\n | OrderUpdatedPayload\n | OrderPickupRequestedPayload\n | OrderAssignedForPickupPayload\n | OrderPickedPayload\n | OrderPickupFailedPayload\n | OrderPickupCancelledPayload\n | OrderAtSortingHubPayload\n | OrderInTransitPayload\n | OrderAtLastMileHubPayload\n | OrderAssignedForDeliveryPayload\n | OrderDeliveredPayload\n | OrderPartialDeliveryPayload\n | OrderReturnedPayload\n | OrderDeliveryFailedPayload\n | OrderOnHoldPayload\n | OrderPaidPayload\n | OrderPaidReturnPayload\n | OrderExchangedPayload\n | OrderReturnIdCreatedPayload\n | OrderReturnInTransitPayload\n | OrderReturnedToMerchantPayload\n | StoreCreatedPayload\n | StoreUpdatedPayload;\n\n/** Maps each \\`PathaoWebhookEvent\\` to its specific payload type */\nexport interface WebhookEventPayloadMap {\n [PathaoWebhookEvent.WEBHOOK_INTEGRATION]: WebhookIntegrationPayload;\n [PathaoWebhookEvent.ORDER_CREATED]: OrderCreatedPayload;\n [PathaoWebhookEvent.ORDER_UPDATED]: OrderUpdatedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_REQUESTED]: OrderPickupRequestedPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_PICKUP]: OrderAssignedForPickupPayload;\n [PathaoWebhookEvent.ORDER_PICKED]: OrderPickedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_FAILED]: OrderPickupFailedPayload;\n [PathaoWebhookEvent.ORDER_PICKUP_CANCELLED]: OrderPickupCancelledPayload;\n [PathaoWebhookEvent.ORDER_AT_THE_SORTING_HUB]: OrderAtSortingHubPayload;\n [PathaoWebhookEvent.ORDER_IN_TRANSIT]: OrderInTransitPayload;\n [PathaoWebhookEvent.ORDER_RECEIVED_AT_LAST_MILE_HUB]: OrderAtLastMileHubPayload;\n [PathaoWebhookEvent.ORDER_ASSIGNED_FOR_DELIVERY]: OrderAssignedForDeliveryPayload;\n [PathaoWebhookEvent.ORDER_DELIVERED]: OrderDeliveredPayload;\n [PathaoWebhookEvent.ORDER_PARTIAL_DELIVERY]: OrderPartialDeliveryPayload;\n [PathaoWebhookEvent.ORDER_RETURNED]: OrderReturnedPayload;\n [PathaoWebhookEvent.ORDER_DELIVERY_FAILED]: OrderDeliveryFailedPayload;\n [PathaoWebhookEvent.ORDER_ON_HOLD]: OrderOnHoldPayload;\n [PathaoWebhookEvent.ORDER_PAID]: OrderPaidPayload;\n [PathaoWebhookEvent.ORDER_PAID_RETURN]: OrderPaidReturnPayload;\n [PathaoWebhookEvent.ORDER_EXCHANGED]: OrderExchangedPayload;\n [PathaoWebhookEvent.ORDER_RETURN_ID_CREATED]: OrderReturnIdCreatedPayload;\n [PathaoWebhookEvent.ORDER_RETURN_IN_TRANSIT]: OrderReturnInTransitPayload;\n [PathaoWebhookEvent.ORDER_RETURNED_TO_MERCHANT]: OrderReturnedToMerchantPayload;\n [PathaoWebhookEvent.STORE_CREATED]: StoreCreatedPayload;\n [PathaoWebhookEvent.STORE_UPDATED]: StoreUpdatedPayload;\n}\n\n// ---------------------------------------------------------------------------\n// Core functions\n// ---------------------------------------------------------------------------\n\n/** Required response header used to authorize your endpoint with Pathao */\nexport const PATHAO_SECRET_HEADER =\n 'x-pathao-merchant-webhook-integration-secret';\n\n/**\n * Parses the raw body into a webhook payload.\n * Pathao does not sign inbound requests, so this just ensures it is valid JSON with an event field.\n *\n * Throws \\`PathaoWebhookError\\` on malformed JSON or missing event.\n *\n * @param rawBody Raw request body or parsed object\n */\nexport function constructEvent(\n rawBody: Buffer | string | object,\n): PathaoWebhookPayload {\n let parsed: unknown;\n if (typeof rawBody === 'object' && !Buffer.isBuffer(rawBody)) {\n parsed = rawBody;\n } else {\n const bodyStr = Buffer.isBuffer(rawBody)\n ? rawBody.toString('utf8')\n : rawBody;\n try {\n parsed = JSON.parse(bodyStr);\n } catch {\n throw new PathaoWebhookError('Webhook payload is not valid JSON.');\n }\n }\n\n if (!parsed || typeof parsed !== 'object' || !('event' in parsed)) {\n throw new PathaoWebhookError(\n \"Webhook payload is missing the required 'event' field.\",\n );\n }\n\n return parsed as PathaoWebhookPayload;\n}\n\n// ---------------------------------------------------------------------------\n// PathaoWebhookHandler\n// ---------------------------------------------------------------------------\n\nexport type WebhookResponseInstructions = {\n statusCode: number;\n headers: Record<string, string>;\n payload: PathaoWebhookPayload | null;\n error: PathaoWebhookError | null;\n};\n\n/**\n * Stateful webhook handler that parses payloads, provides response instructions,\n * and dispatches events to typed listeners.\n *\n * @example\n * \\`\\`\\`typescript\n * const handler = new PathaoWebhookHandler(process.env.PATHAO_WEBHOOK_SECRET!);\n *\n * handler.on(PathaoWebhookEvent.ORDER_DELIVERED, (payload) => {\n * // payload is fully typed as OrderDeliveredPayload\n * console.log(payload.consignment_id, payload.collected_amount);\n * });\n * \\`\\`\\`\n */\nexport class PathaoWebhookHandler extends EventEmitter {\n private readonly webhookSecret: string;\n\n constructor(webhookSecret: string) {\n super();\n if (!webhookSecret) {\n throw new PathaoWebhookError(\n 'webhookSecret is required to create a PathaoWebhookHandler.',\n );\n }\n this.webhookSecret = webhookSecret;\n }\n\n // Typed on() overloads ──────────────────────────────────────────────────\n\n /** Listen for a specific Pathao event with a fully-typed payload callback. */\n on<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n /** Fires for every successfully parsed event regardless of type. */\n on(event: 'webhook', listener: (payload: PathaoWebhookPayload) => void): this;\n /** Fires when parsing fails. */\n on(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n on(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.on(event, listener);\n }\n\n /** Listen once for a specific Pathao event. */\n once<E extends PathaoWebhookEvent>(\n event: E,\n listener: (payload: WebhookEventPayloadMap[E]) => void,\n ): this;\n once(\n event: 'webhook',\n listener: (payload: PathaoWebhookPayload) => void,\n ): this;\n once(event: 'error', listener: (error: PathaoWebhookError) => void): this;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n once(event: string | symbol, listener: (...args: any[]) => void): this {\n return super.once(event, listener);\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Parse the body and dispatch the event.\n *\n * Throws \\`PathaoWebhookError\\` on failure and also emits \\`'error'\\` so\n * listeners can respond without a try/catch.\n *\n * @param rawBody Raw request body or matched json object\n */\n process(rawBody: Buffer | string | object): PathaoWebhookPayload {\n let payload: PathaoWebhookPayload;\n\n try {\n payload = constructEvent(rawBody);\n } catch (err) {\n const webhookErr =\n err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(\n err instanceof Error ? err.message : 'Unknown error',\n );\n if (this.listenerCount('error') > 0) {\n this.emit('error', webhookErr);\n }\n throw webhookErr;\n }\n\n this.emit(payload.event, payload);\n this.emit('webhook', payload);\n\n return payload;\n }\n\n /**\n * Returns an Express-compatible middleware function.\n *\n * Automatically sets the required \\`X-Pathao-Merchant-Webhook-Integration-Secret\\` header.\n * Automatically responds with 202 for the \\`webhook_integration\\` handshake.\n * For standard events, attaches the payload to \\`req.pathaoWebhook\\` and calls \\`next()\\`.\n * On error, calls \\`next(err)\\`.\n *\n * \\`\\`\\`typescript\n * app.post(\n * '/webhooks/pathao',\n * express.json(),\n * handler.expressMiddleware(),\n * (req, res) => res.sendStatus(200) // You must send 200 for other events\n * );\n * \\`\\`\\`\n */\n expressMiddleware() {\n return (\n req: {\n body: Buffer | string | object;\n pathaoWebhook?: PathaoWebhookPayload;\n },\n res: {\n setHeader: (name: string, value: string) => void;\n status: (code: number) => { send: () => void };\n },\n next: (err?: unknown) => void,\n ): void => {\n try {\n res.setHeader(PATHAO_SECRET_HEADER, this.webhookSecret);\n\n const payload = this.process(req.body);\n req.pathaoWebhook = payload;\n\n if (payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION) {\n res.status(202).send();\n return;\n }\n\n next();\n } catch (err) {\n next(err);\n }\n };\n }\n\n /**\n * Returns response instructions for any framework (Fastify, Hono, etc.).\n *\n * Never throws — always resolves with a \\`WebhookResponseInstructions\\` object\n * that tells you which status code and headers to return, along with the payload/error.\n *\n * \\`\\`\\`typescript\n * const handle = handler.middleware();\n * const instructions = await handle(request.body);\n *\n * // Apply the required headers (the secret header)\n * for (const [key, value] of Object.entries(instructions.headers)) {\n * reply.header(key, value);\n * }\n *\n * if (instructions.error) {\n * return reply.status(instructions.statusCode).send({ error: instructions.error.message });\n * }\n *\n * // If it was the handshake, we should just return 202 as instructed\n * if (instructions.payload?.event === 'webhook_integration') {\n * return reply.status(instructions.statusCode).send();\n * }\n *\n * // Process your real webhook\n * return reply.status(instructions.statusCode).send({ received: true });\n * \\`\\`\\`\n */\n middleware() {\n return async (\n rawBody: Buffer | string | object,\n ): Promise<WebhookResponseInstructions> => {\n const headers = { [PATHAO_SECRET_HEADER]: this.webhookSecret };\n\n try {\n const payload = this.process(rawBody);\n const isHandshake =\n payload.event === PathaoWebhookEvent.WEBHOOK_INTEGRATION;\n return {\n statusCode: isHandshake ? 202 : 200,\n headers,\n payload,\n error: null,\n };\n } catch (err) {\n const webhookErr =\n err instanceof PathaoWebhookError\n ? err\n : new PathaoWebhookError(\n err instanceof Error ? err.message : 'Unknown error',\n );\n return {\n statusCode: 400,\n headers,\n payload: null,\n error: webhookErr,\n };\n }\n };\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pathao-merchant-sdk",
3
- "version": "2.2.0",
3
+ "version": "2.3.1",
4
4
  "description": "Unofficial TypeScript SDK for the Pathao Merchant API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -25,22 +25,6 @@
25
25
  "CHANGELOG.md",
26
26
  "CONTRIBUTING.md"
27
27
  ],
28
- "scripts": {
29
- "build": "tsup",
30
- "dev": "tsup --watch",
31
- "test": "jest",
32
- "test:watch": "jest --watch",
33
- "lint": "eslint src/**/*.ts",
34
- "lint:fix": "eslint src/**/*.ts --fix",
35
- "type-check": "tsc --noEmit",
36
- "prepublishOnly": "pnpm run build && pnpm run test",
37
- "release": "node scripts/release.js",
38
- "release:dry-run": "node scripts/release.js --dry-run",
39
- "changelog": "node scripts/release.js --changelog-only",
40
- "version:patch": "npm version patch --no-git-tag-version",
41
- "version:minor": "npm version minor --no-git-tag-version",
42
- "version:major": "npm version major --no-git-tag-version"
43
- },
44
28
  "keywords": [
45
29
  "pathao",
46
30
  "merchant",
@@ -68,7 +52,7 @@
68
52
  "node": ">=18.0.0"
69
53
  },
70
54
  "dependencies": {
71
- "axios": "^1.12.0"
55
+ "axios": "^1.20.0"
72
56
  },
73
57
  "devDependencies": {
74
58
  "@types/jest": "^29.5.0",
@@ -84,5 +68,15 @@
84
68
  },
85
69
  "peerDependencies": {
86
70
  "typescript": ">=4.9.0"
71
+ },
72
+ "scripts": {
73
+ "build": "tsup",
74
+ "dev": "tsup --watch",
75
+ "test": "jest",
76
+ "test:watch": "jest --watch",
77
+ "lint": "eslint src/**/*.ts",
78
+ "lint:fix": "eslint src/**/*.ts --fix",
79
+ "type-check": "tsc --noEmit",
80
+ "audit:security": "osv-scanner --lockfile=pnpm-lock.yaml"
87
81
  }
88
- }
82
+ }