@waffo/pancake-ts 0.2.1 → 0.2.2

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/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to `@waffo/pancake-ts` will be documented in this file.
4
4
 
5
5
  Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.2.2] - 2026-04-03
8
+
9
+ ### Fixed
10
+
11
+ - **Checkout idempotency** — Checkout methods (`anonymous.create()`, `authenticated.create()`, `createSession()`) now use time-windowed idempotency keys (60-second window) instead of fully deterministic keys. Previously, identical checkout params always produced the same `X-Idempotency-Key`, causing the gateway to return cached (and potentially expired) sessions. Now, same params within the same minute are still deduped (protects against network retries), but a new key is generated after the window elapses.
12
+ - **Timestamp consistency** — `Date.now()` is now called once per request and shared between signature timestamp and idempotency key calculation, eliminating a theoretical edge case where the two could land in different seconds.
13
+
14
+ ### Internal
15
+
16
+ - **`PostOptions` interface** — Extracted inline `{ idempotencyWindow?: number }` into a named type in `types.ts` (not publicly exported).
17
+
7
18
  ## [0.2.1] - 2026-04-02
8
19
 
9
20
  ### Added
package/dist/index.cjs CHANGED
@@ -222,18 +222,26 @@ var HttpClient = class {
222
222
  *
223
223
  * Behavior:
224
224
  * - Generates a deterministic `X-Idempotency-Key` from `merchantId + path + body` (same request produces same key)
225
+ * - When `idempotencyWindow` is set, a floored timestamp is mixed into the key so identical params produce
226
+ * a new key after the window elapses (useful for checkout where repeated creation is intentional)
225
227
  * - Auto-builds RSA-SHA256 signature (`X-Merchant-Id` / `X-Timestamp` / `X-Signature`)
226
228
  * - Unwraps the response envelope: returns `data` on success, throws `WaffoPancakeError` on failure
227
229
  *
228
230
  * @param path - API path (e.g. `/v1/actions/store/create-store`)
229
231
  * @param body - Request body object
232
+ * @param options - Optional settings
233
+ * @param options.idempotencyWindow - Time window in seconds for idempotency key rotation (e.g. 60 = per-minute dedup)
230
234
  * @returns Parsed `data` field from the response
231
235
  * @throws {WaffoPancakeError} When the API returns errors
232
236
  */
233
- async post(path, body) {
237
+ async post(path, body, options) {
234
238
  const bodyStr = JSON.stringify(body);
235
- const timestamp = Math.floor(Date.now() / 1e3).toString();
239
+ const now = Date.now();
240
+ const timestampSec = Math.floor(now / 1e3);
241
+ const timestamp = timestampSec.toString();
236
242
  const signature = signRequest("POST", path, timestamp, bodyStr, this.privateKey);
243
+ const idempotencyBase = `${this.merchantId}:${path}:${bodyStr}`;
244
+ const idempotencyInput = options?.idempotencyWindow ? `${idempotencyBase}:${Math.floor(timestampSec / options.idempotencyWindow)}` : idempotencyBase;
237
245
  const response = await this._fetch(`${this.baseUrl}${path}`, {
238
246
  method: "POST",
239
247
  headers: {
@@ -241,7 +249,7 @@ var HttpClient = class {
241
249
  "X-Merchant-Id": this.merchantId,
242
250
  "X-Timestamp": timestamp,
243
251
  "X-Signature": signature,
244
- "X-Idempotency-Key": (0, import_node_crypto2.createHash)("sha256").update(`${this.merchantId}:${path}:${bodyStr}`).digest("hex")
252
+ "X-Idempotency-Key": (0, import_node_crypto2.createHash)("sha256").update(idempotencyInput).digest("hex")
245
253
  },
246
254
  body: bodyStr
247
255
  });
@@ -528,7 +536,8 @@ var CheckoutAnonymousResource = class {
528
536
  validateCheckoutCommon(params);
529
537
  return this.http.post(
530
538
  "/v1/actions/checkout/create-session",
531
- params
539
+ params,
540
+ { idempotencyWindow: 60 }
532
541
  );
533
542
  }
534
543
  };
@@ -568,11 +577,11 @@ var CheckoutAuthenticatedResource = class {
568
577
  this.http.post("/v1/actions/auth/issue-session-token", {
569
578
  storeId: params.storeId,
570
579
  buyerIdentity
571
- }),
580
+ }, { idempotencyWindow: 60 }),
572
581
  this.http.post("/v1/actions/checkout/create-session", {
573
582
  ...sessionFields,
574
583
  buyerEmail: buyerEmail ?? buyerIdentity
575
- })
584
+ }, { idempotencyWindow: 60 })
576
585
  ]);
577
586
  return {
578
587
  sessionId: sessionResult.sessionId,
@@ -615,7 +624,7 @@ var CheckoutResource = class {
615
624
  * // Redirect to session.checkoutUrl
616
625
  */
617
626
  async createSession(params) {
618
- return this.http.post("/v1/actions/checkout/create-session", params);
627
+ return this.http.post("/v1/actions/checkout/create-session", params, { idempotencyWindow: 60 });
619
628
  }
620
629
  };
621
630