@waffo/pancake-ts 0.2.1 → 0.3.0
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 +31 -0
- package/README.md +0 -12
- package/dist/index.cjs +36 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -40
- package/dist/index.d.ts +34 -40
- package/dist/index.js +36 -23
- package/dist/index.js.map +1 -1
- package/docs/api-reference.md +14 -20
- package/docs/graphql-guide.md +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,37 @@ 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.3.0] - 2026-04-09
|
|
8
|
+
|
|
9
|
+
### Breaking Changes
|
|
10
|
+
|
|
11
|
+
- **Checkout params simplified** — `storeId` and `productType` removed from `CreateCheckoutSessionParams`, `AnonymousCheckoutParams`, and `AuthenticatedCheckoutParams`. The server now derives both from `productId` automatically. Only `productId` + `currency` are required.
|
|
12
|
+
- **`CheckoutSessionProductType` enum removed** — No longer exported. Product type is determined server-side.
|
|
13
|
+
- **`IssueSessionTokenParams.storeId` now optional** — Provide either `storeId` or `productId` (at least one required). When `productId` is given, the server derives the store from the product.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- **`IssueSessionTokenParams.productId`** — New optional field. When provided without `storeId`, the server derives the store from the product.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- **`checkout.authenticated.create()`** — Now sends `productId` (instead of `storeId`) to `issue-session-token` endpoint for parallel session token + checkout session creation.
|
|
22
|
+
|
|
23
|
+
### Documentation
|
|
24
|
+
|
|
25
|
+
- **GraphQL type difference warning** — Added warnings in `graphql-guide.md`, `api-reference.md`, and external docs (EN/ZH/JA) clarifying that SDK TypeScript types reflect the REST API shape and differ from GraphQL schema types (e.g. `prices` is `Record<string, PriceInfo>` in REST but `[CurrencyPrice!]!` in GraphQL). Users should always use introspection for GraphQL queries.
|
|
26
|
+
|
|
27
|
+
## [0.2.2] - 2026-04-03
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **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.
|
|
32
|
+
- **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.
|
|
33
|
+
|
|
34
|
+
### Internal
|
|
35
|
+
|
|
36
|
+
- **`PostOptions` interface** — Extracted inline `{ idempotencyWindow?: number }` into a named type in `types.ts` (not publicly exported).
|
|
37
|
+
|
|
7
38
|
## [0.2.1] - 2026-04-02
|
|
8
39
|
|
|
9
40
|
### Added
|
package/README.md
CHANGED
|
@@ -28,9 +28,7 @@ const client = new WaffoPancake({
|
|
|
28
28
|
|
|
29
29
|
// Create a checkout session — one call handles token + session + URL
|
|
30
30
|
const result = await client.checkout.authenticated.create({
|
|
31
|
-
storeId: "STO_xxx", // from Dashboard > Stores
|
|
32
31
|
productId: "PROD_xxx", // from Dashboard > Products
|
|
33
|
-
productType: "onetime",
|
|
34
32
|
currency: "USD",
|
|
35
33
|
buyerIdentity: req.user.email, // your user's identity
|
|
36
34
|
});
|
|
@@ -85,18 +83,14 @@ The merchant provides buyer identity — the SDK issues a session token, creates
|
|
|
85
83
|
```typescript
|
|
86
84
|
// Basic — buyer identity only
|
|
87
85
|
const result = await client.checkout.authenticated.create({
|
|
88
|
-
storeId: "STO_xxx",
|
|
89
86
|
productId: "PROD_xxx",
|
|
90
|
-
productType: "onetime",
|
|
91
87
|
currency: "USD",
|
|
92
88
|
buyerIdentity: "customer@example.com",
|
|
93
89
|
});
|
|
94
90
|
|
|
95
91
|
// With dynamic pricing — override stored price (e.g., coupon, volume discount)
|
|
96
92
|
const result = await client.checkout.authenticated.create({
|
|
97
|
-
storeId: "STO_xxx",
|
|
98
93
|
productId: "PROD_xxx",
|
|
99
|
-
productType: "onetime",
|
|
100
94
|
currency: "USD",
|
|
101
95
|
buyerIdentity: "customer@example.com",
|
|
102
96
|
priceSnapshot: { amount: "19.99", taxCategory: "digital_goods" },
|
|
@@ -104,9 +98,7 @@ const result = await client.checkout.authenticated.create({
|
|
|
104
98
|
|
|
105
99
|
// Subscription with trial control + billing detail pre-fill
|
|
106
100
|
const result = await client.checkout.authenticated.create({
|
|
107
|
-
storeId: "STO_xxx",
|
|
108
101
|
productId: "PROD_xxx",
|
|
109
|
-
productType: "subscription",
|
|
110
102
|
currency: "USD",
|
|
111
103
|
buyerIdentity: "customer@example.com",
|
|
112
104
|
withTrial: true, // force enable trial (false = skip, omit = default rules)
|
|
@@ -125,17 +117,13 @@ No buyer identity required — the buyer fills in billing details manually on th
|
|
|
125
117
|
|
|
126
118
|
```typescript
|
|
127
119
|
const result = await client.checkout.anonymous.create({
|
|
128
|
-
storeId: "STO_xxx",
|
|
129
120
|
productId: "PROD_xxx",
|
|
130
|
-
productType: "onetime",
|
|
131
121
|
currency: "USD",
|
|
132
122
|
});
|
|
133
123
|
|
|
134
124
|
// Also supports priceSnapshot and withTrial
|
|
135
125
|
const result = await client.checkout.anonymous.create({
|
|
136
|
-
storeId: "STO_xxx",
|
|
137
126
|
productId: "PROD_xxx",
|
|
138
|
-
productType: "subscription",
|
|
139
127
|
currency: "USD",
|
|
140
128
|
priceSnapshot: { amount: "4.99", taxCategory: "saas" },
|
|
141
129
|
withTrial: false, // skip trial for this session
|
package/dist/index.cjs
CHANGED
|
@@ -21,7 +21,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BillingPeriod: () => BillingPeriod,
|
|
24
|
-
CheckoutSessionProductType: () => CheckoutSessionProductType,
|
|
25
24
|
EntityStatus: () => EntityStatus,
|
|
26
25
|
Environment: () => Environment,
|
|
27
26
|
ErrorLayer: () => ErrorLayer,
|
|
@@ -222,18 +221,26 @@ var HttpClient = class {
|
|
|
222
221
|
*
|
|
223
222
|
* Behavior:
|
|
224
223
|
* - Generates a deterministic `X-Idempotency-Key` from `merchantId + path + body` (same request produces same key)
|
|
224
|
+
* - When `idempotencyWindow` is set, a floored timestamp is mixed into the key so identical params produce
|
|
225
|
+
* a new key after the window elapses (useful for checkout where repeated creation is intentional)
|
|
225
226
|
* - Auto-builds RSA-SHA256 signature (`X-Merchant-Id` / `X-Timestamp` / `X-Signature`)
|
|
226
227
|
* - Unwraps the response envelope: returns `data` on success, throws `WaffoPancakeError` on failure
|
|
227
228
|
*
|
|
228
229
|
* @param path - API path (e.g. `/v1/actions/store/create-store`)
|
|
229
230
|
* @param body - Request body object
|
|
231
|
+
* @param options - Optional settings
|
|
232
|
+
* @param options.idempotencyWindow - Time window in seconds for idempotency key rotation (e.g. 60 = per-minute dedup)
|
|
230
233
|
* @returns Parsed `data` field from the response
|
|
231
234
|
* @throws {WaffoPancakeError} When the API returns errors
|
|
232
235
|
*/
|
|
233
|
-
async post(path, body) {
|
|
236
|
+
async post(path, body, options) {
|
|
234
237
|
const bodyStr = JSON.stringify(body);
|
|
235
|
-
const
|
|
238
|
+
const now = Date.now();
|
|
239
|
+
const timestampSec = Math.floor(now / 1e3);
|
|
240
|
+
const timestamp = timestampSec.toString();
|
|
236
241
|
const signature = signRequest("POST", path, timestamp, bodyStr, this.privateKey);
|
|
242
|
+
const idempotencyBase = `${this.merchantId}:${path}:${bodyStr}`;
|
|
243
|
+
const idempotencyInput = options?.idempotencyWindow ? `${idempotencyBase}:${Math.floor(timestampSec / options.idempotencyWindow)}` : idempotencyBase;
|
|
237
244
|
const response = await this._fetch(`${this.baseUrl}${path}`, {
|
|
238
245
|
method: "POST",
|
|
239
246
|
headers: {
|
|
@@ -241,7 +248,7 @@ var HttpClient = class {
|
|
|
241
248
|
"X-Merchant-Id": this.merchantId,
|
|
242
249
|
"X-Timestamp": timestamp,
|
|
243
250
|
"X-Signature": signature,
|
|
244
|
-
"X-Idempotency-Key": (0, import_node_crypto2.createHash)("sha256").update(
|
|
251
|
+
"X-Idempotency-Key": (0, import_node_crypto2.createHash)("sha256").update(idempotencyInput).digest("hex")
|
|
245
252
|
},
|
|
246
253
|
body: bodyStr
|
|
247
254
|
});
|
|
@@ -336,9 +343,7 @@ function validateBillingDetail(detail) {
|
|
|
336
343
|
}
|
|
337
344
|
}
|
|
338
345
|
function validateCheckoutCommon(params) {
|
|
339
|
-
validateShortId("storeId", params.storeId, "STO");
|
|
340
346
|
validateShortId("productId", params.productId, "PROD");
|
|
341
|
-
validateEnum("productType", params.productType, ["onetime", "subscription"]);
|
|
342
347
|
validateCurrencyCode("currency", params.currency);
|
|
343
348
|
if (params.priceSnapshot) {
|
|
344
349
|
validateAmountString("priceSnapshot.amount", params.priceSnapshot.amount);
|
|
@@ -364,13 +369,31 @@ var AuthResource = class {
|
|
|
364
369
|
* @returns Issued session token with expiration
|
|
365
370
|
*
|
|
366
371
|
* @example
|
|
372
|
+
* // By store ID
|
|
367
373
|
* const { token, expiresAt } = await client.auth.issueSessionToken({
|
|
368
374
|
* storeId: "STO_xxx",
|
|
369
375
|
* buyerIdentity: "customer@example.com",
|
|
370
376
|
* });
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* // By product ID (store derived automatically)
|
|
380
|
+
* const { token, expiresAt } = await client.auth.issueSessionToken({
|
|
381
|
+
* productId: "PROD_xxx",
|
|
382
|
+
* buyerIdentity: "customer@example.com",
|
|
383
|
+
* });
|
|
371
384
|
*/
|
|
372
385
|
async issueSessionToken(params) {
|
|
373
|
-
|
|
386
|
+
if (!params.storeId && !params.productId) {
|
|
387
|
+
throw new WaffoPancakeError(400, [
|
|
388
|
+
{ message: "Missing required field: provide storeId or productId", layer: "sdk" }
|
|
389
|
+
]);
|
|
390
|
+
}
|
|
391
|
+
if (params.storeId) {
|
|
392
|
+
validateShortId("storeId", params.storeId, "STO");
|
|
393
|
+
}
|
|
394
|
+
if (params.productId) {
|
|
395
|
+
validateShortId("productId", params.productId, "PROD");
|
|
396
|
+
}
|
|
374
397
|
validateRequired("buyerIdentity", params.buyerIdentity);
|
|
375
398
|
return this.http.post("/v1/actions/auth/issue-session-token", params);
|
|
376
399
|
}
|
|
@@ -517,9 +540,7 @@ var CheckoutAnonymousResource = class {
|
|
|
517
540
|
*
|
|
518
541
|
* @example
|
|
519
542
|
* const result = await client.checkout.anonymous.create({
|
|
520
|
-
* storeId: "STO_xxx",
|
|
521
543
|
* productId: "PROD_xxx",
|
|
522
|
-
* productType: "onetime",
|
|
523
544
|
* currency: "USD",
|
|
524
545
|
* });
|
|
525
546
|
* // Redirect to result.checkoutUrl
|
|
@@ -528,7 +549,8 @@ var CheckoutAnonymousResource = class {
|
|
|
528
549
|
validateCheckoutCommon(params);
|
|
529
550
|
return this.http.post(
|
|
530
551
|
"/v1/actions/checkout/create-session",
|
|
531
|
-
params
|
|
552
|
+
params,
|
|
553
|
+
{ idempotencyWindow: 60 }
|
|
532
554
|
);
|
|
533
555
|
}
|
|
534
556
|
};
|
|
@@ -552,9 +574,7 @@ var CheckoutAuthenticatedResource = class {
|
|
|
552
574
|
*
|
|
553
575
|
* @example
|
|
554
576
|
* const result = await client.checkout.authenticated.create({
|
|
555
|
-
* storeId: "STO_xxx",
|
|
556
577
|
* productId: "PROD_xxx",
|
|
557
|
-
* productType: "onetime",
|
|
558
578
|
* currency: "USD",
|
|
559
579
|
* buyerIdentity: "customer@example.com",
|
|
560
580
|
* });
|
|
@@ -566,13 +586,13 @@ var CheckoutAuthenticatedResource = class {
|
|
|
566
586
|
const { buyerIdentity, buyerEmail, ...sessionFields } = params;
|
|
567
587
|
const [tokenResult, sessionResult] = await Promise.all([
|
|
568
588
|
this.http.post("/v1/actions/auth/issue-session-token", {
|
|
569
|
-
|
|
589
|
+
productId: params.productId,
|
|
570
590
|
buyerIdentity
|
|
571
|
-
}),
|
|
591
|
+
}, { idempotencyWindow: 60 }),
|
|
572
592
|
this.http.post("/v1/actions/checkout/create-session", {
|
|
573
593
|
...sessionFields,
|
|
574
594
|
buyerEmail: buyerEmail ?? buyerIdentity
|
|
575
|
-
})
|
|
595
|
+
}, { idempotencyWindow: 60 })
|
|
576
596
|
]);
|
|
577
597
|
return {
|
|
578
598
|
sessionId: sessionResult.sessionId,
|
|
@@ -606,16 +626,14 @@ var CheckoutResource = class {
|
|
|
606
626
|
*
|
|
607
627
|
* @example
|
|
608
628
|
* const session = await client.checkout.createSession({
|
|
609
|
-
* storeId: "STO_xxx",
|
|
610
629
|
* productId: "PROD_xxx",
|
|
611
|
-
* productType: "onetime",
|
|
612
630
|
* currency: "USD",
|
|
613
631
|
* buyerEmail: "customer@example.com",
|
|
614
632
|
* });
|
|
615
633
|
* // Redirect to session.checkoutUrl
|
|
616
634
|
*/
|
|
617
635
|
async createSession(params) {
|
|
618
|
-
return this.http.post("/v1/actions/checkout/create-session", params);
|
|
636
|
+
return this.http.post("/v1/actions/checkout/create-session", params, { idempotencyWindow: 60 });
|
|
619
637
|
}
|
|
620
638
|
};
|
|
621
639
|
|
|
@@ -1291,11 +1309,6 @@ var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
|
1291
1309
|
MediaType2["Video"] = "video";
|
|
1292
1310
|
return MediaType2;
|
|
1293
1311
|
})(MediaType || {});
|
|
1294
|
-
var CheckoutSessionProductType = /* @__PURE__ */ ((CheckoutSessionProductType2) => {
|
|
1295
|
-
CheckoutSessionProductType2["Onetime"] = "onetime";
|
|
1296
|
-
CheckoutSessionProductType2["Subscription"] = "subscription";
|
|
1297
|
-
return CheckoutSessionProductType2;
|
|
1298
|
-
})(CheckoutSessionProductType || {});
|
|
1299
1312
|
var ErrorLayer = /* @__PURE__ */ ((ErrorLayer2) => {
|
|
1300
1313
|
ErrorLayer2["Gateway"] = "gateway";
|
|
1301
1314
|
ErrorLayer2["User"] = "user";
|
|
@@ -1325,7 +1338,6 @@ var WebhookEventType = /* @__PURE__ */ ((WebhookEventType2) => {
|
|
|
1325
1338
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1326
1339
|
0 && (module.exports = {
|
|
1327
1340
|
BillingPeriod,
|
|
1328
|
-
CheckoutSessionProductType,
|
|
1329
1341
|
EntityStatus,
|
|
1330
1342
|
Environment,
|
|
1331
1343
|
ErrorLayer,
|