@waffo/pancake-ts 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +418 -0
- package/dist/index.cjs +757 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1258 -0
- package/dist/index.d.ts +1258 -0
- package/dist/index.js +713 -0
- package/dist/index.js.map +1 -0
- package/package.json +51 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
// src/http-client.ts
|
|
2
|
+
import { createHash as createHash2 } from "crypto";
|
|
3
|
+
|
|
4
|
+
// src/errors.ts
|
|
5
|
+
var WaffoPancakeError = class extends Error {
|
|
6
|
+
status;
|
|
7
|
+
errors;
|
|
8
|
+
constructor(status, errors) {
|
|
9
|
+
const rootCause = errors[0]?.message ?? "Unknown error";
|
|
10
|
+
super(rootCause);
|
|
11
|
+
this.name = "WaffoPancakeError";
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.errors = errors;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/signing.ts
|
|
18
|
+
import { createHash, createSign } from "crypto";
|
|
19
|
+
function signRequest(method, path, timestamp, body, privateKey) {
|
|
20
|
+
const bodyHash = createHash("sha256").update(body).digest("hex");
|
|
21
|
+
const canonicalRequest = `${method}
|
|
22
|
+
${path}
|
|
23
|
+
${timestamp}
|
|
24
|
+
${bodyHash}`;
|
|
25
|
+
const sign = createSign("sha256");
|
|
26
|
+
sign.update(canonicalRequest);
|
|
27
|
+
return sign.sign(privateKey, "base64");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/http-client.ts
|
|
31
|
+
var DEFAULT_BASE_URL = "https://waffo-pancake-auth-service.vercel.app";
|
|
32
|
+
var HttpClient = class {
|
|
33
|
+
merchantId;
|
|
34
|
+
privateKey;
|
|
35
|
+
baseUrl;
|
|
36
|
+
_fetch;
|
|
37
|
+
constructor(config) {
|
|
38
|
+
this.merchantId = config.merchantId;
|
|
39
|
+
this.privateKey = config.privateKey;
|
|
40
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
41
|
+
this._fetch = config.fetch ?? fetch;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Send a signed POST request and return the parsed `data` field.
|
|
45
|
+
*
|
|
46
|
+
* Behavior:
|
|
47
|
+
* - Generates a deterministic `X-Idempotency-Key` from `merchantId + path + body` (same request produces same key)
|
|
48
|
+
* - Auto-builds RSA-SHA256 signature (`X-Merchant-Id` / `X-Timestamp` / `X-Signature`)
|
|
49
|
+
* - Unwraps the response envelope: returns `data` on success, throws `WaffoPancakeError` on failure
|
|
50
|
+
*
|
|
51
|
+
* @param path - API path (e.g. `/v1/actions/store/create-store`)
|
|
52
|
+
* @param body - Request body object
|
|
53
|
+
* @returns Parsed `data` field from the response
|
|
54
|
+
* @throws {WaffoPancakeError} When the API returns errors
|
|
55
|
+
*/
|
|
56
|
+
async post(path, body) {
|
|
57
|
+
const bodyStr = JSON.stringify(body);
|
|
58
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
59
|
+
const signature = signRequest("POST", path, timestamp, bodyStr, this.privateKey);
|
|
60
|
+
const response = await this._fetch(`${this.baseUrl}${path}`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: {
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
"X-Merchant-Id": this.merchantId,
|
|
65
|
+
"X-Timestamp": timestamp,
|
|
66
|
+
"X-Signature": signature,
|
|
67
|
+
"X-Idempotency-Key": createHash2("sha256").update(`${this.merchantId}:${path}:${bodyStr}`).digest("hex")
|
|
68
|
+
},
|
|
69
|
+
body: bodyStr
|
|
70
|
+
});
|
|
71
|
+
const result = await response.json();
|
|
72
|
+
if ("errors" in result && result.errors) {
|
|
73
|
+
throw new WaffoPancakeError(response.status, result.errors);
|
|
74
|
+
}
|
|
75
|
+
return result.data;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/resources/auth.ts
|
|
80
|
+
var AuthResource = class {
|
|
81
|
+
constructor(http) {
|
|
82
|
+
this.http = http;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Issue a session token for a buyer.
|
|
86
|
+
*
|
|
87
|
+
* @param params - Token issuance parameters
|
|
88
|
+
* @returns Issued session token with expiration
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* const { token, expiresAt } = await client.auth.issueSessionToken({
|
|
92
|
+
* storeId: "store_xxx",
|
|
93
|
+
* buyerIdentity: "customer@example.com",
|
|
94
|
+
* });
|
|
95
|
+
*/
|
|
96
|
+
async issueSessionToken(params) {
|
|
97
|
+
return this.http.post("/v1/actions/auth/issue-session-token", params);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/resources/checkout.ts
|
|
102
|
+
var CheckoutResource = class {
|
|
103
|
+
constructor(http) {
|
|
104
|
+
this.http = http;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Create a checkout session. Returns a URL to redirect the customer to.
|
|
108
|
+
*
|
|
109
|
+
* @param params - Checkout session parameters
|
|
110
|
+
* @returns Session ID, checkout URL, and expiration
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* const session = await client.checkout.createSession({
|
|
114
|
+
* storeId: "store_xxx",
|
|
115
|
+
* productId: "prod_xxx",
|
|
116
|
+
* productType: "onetime",
|
|
117
|
+
* currency: "USD",
|
|
118
|
+
* buyerEmail: "customer@example.com",
|
|
119
|
+
* });
|
|
120
|
+
* // Redirect to session.checkoutUrl
|
|
121
|
+
*/
|
|
122
|
+
async createSession(params) {
|
|
123
|
+
return this.http.post("/v1/actions/checkout/create-session", params);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/resources/graphql.ts
|
|
128
|
+
var GraphQLResource = class {
|
|
129
|
+
constructor(http) {
|
|
130
|
+
this.http = http;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Execute a GraphQL query (Query only, no Mutations).
|
|
134
|
+
*
|
|
135
|
+
* @param params - GraphQL query and optional variables
|
|
136
|
+
* @returns GraphQL response with data and optional errors
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* const result = await client.graphql.query<{ stores: Array<{ id: string; name: string }> }>({
|
|
140
|
+
* query: `query { stores { id name status } }`,
|
|
141
|
+
* });
|
|
142
|
+
* console.log(result.data?.stores);
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* const result = await client.graphql.query({
|
|
146
|
+
* query: `query ($id: ID!) { onetimeProduct(id: $id) { id name prices } }`,
|
|
147
|
+
* variables: { id: "prod_xxx" },
|
|
148
|
+
* });
|
|
149
|
+
*/
|
|
150
|
+
async query(params) {
|
|
151
|
+
return this.http.post("/v1/graphql", params);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// src/resources/onetime-products.ts
|
|
156
|
+
var OnetimeProductsResource = class {
|
|
157
|
+
constructor(http) {
|
|
158
|
+
this.http = http;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create a one-time product with multi-currency pricing.
|
|
162
|
+
*
|
|
163
|
+
* @param params - Product creation parameters
|
|
164
|
+
* @returns Created product detail
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* const { product } = await client.onetimeProducts.create({
|
|
168
|
+
* storeId: "store_xxx",
|
|
169
|
+
* name: "E-Book",
|
|
170
|
+
* prices: { USD: { amount: 2900, taxIncluded: false, taxCategory: "digital_goods" } },
|
|
171
|
+
* });
|
|
172
|
+
*/
|
|
173
|
+
async create(params) {
|
|
174
|
+
return this.http.post("/v1/actions/onetime-product/create-product", params);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Update a one-time product. Creates a new version; skips if unchanged.
|
|
178
|
+
*
|
|
179
|
+
* @param params - Product update parameters (all content fields required)
|
|
180
|
+
* @returns Updated product detail
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* const { product } = await client.onetimeProducts.update({
|
|
184
|
+
* id: "prod_xxx",
|
|
185
|
+
* name: "E-Book v2",
|
|
186
|
+
* prices: { USD: { amount: 3900, taxIncluded: false, taxCategory: "digital_goods" } },
|
|
187
|
+
* });
|
|
188
|
+
*/
|
|
189
|
+
async update(params) {
|
|
190
|
+
return this.http.post("/v1/actions/onetime-product/update-product", params);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Publish a one-time product's test version to production.
|
|
194
|
+
*
|
|
195
|
+
* @param params - Product to publish
|
|
196
|
+
* @returns Published product detail
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* const { product } = await client.onetimeProducts.publish({ id: "prod_xxx" });
|
|
200
|
+
*/
|
|
201
|
+
async publish(params) {
|
|
202
|
+
return this.http.post("/v1/actions/onetime-product/publish-product", params);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Update a one-time product's status (active/inactive).
|
|
206
|
+
*
|
|
207
|
+
* @param params - Status update parameters
|
|
208
|
+
* @returns Updated product detail
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* const { product } = await client.onetimeProducts.updateStatus({
|
|
212
|
+
* id: "prod_xxx",
|
|
213
|
+
* status: ProductVersionStatus.Inactive,
|
|
214
|
+
* });
|
|
215
|
+
*/
|
|
216
|
+
async updateStatus(params) {
|
|
217
|
+
return this.http.post("/v1/actions/onetime-product/update-status", params);
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// src/resources/orders.ts
|
|
222
|
+
var OrdersResource = class {
|
|
223
|
+
constructor(http) {
|
|
224
|
+
this.http = http;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Cancel a subscription order.
|
|
228
|
+
*
|
|
229
|
+
* - pending -> canceled (immediate)
|
|
230
|
+
* - active/trialing -> canceling (PSP cancel, webhook updates later)
|
|
231
|
+
*
|
|
232
|
+
* @param params - Order to cancel
|
|
233
|
+
* @returns Order ID and resulting status
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* const { orderId, status } = await client.orders.cancelSubscription({
|
|
237
|
+
* orderId: "order_xxx",
|
|
238
|
+
* });
|
|
239
|
+
* // status: "canceled" or "canceling"
|
|
240
|
+
*/
|
|
241
|
+
async cancelSubscription(params) {
|
|
242
|
+
return this.http.post("/v1/actions/subscription-order/cancel-order", params);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
// src/resources/store-merchants.ts
|
|
247
|
+
var StoreMerchantsResource = class {
|
|
248
|
+
constructor(http) {
|
|
249
|
+
this.http = http;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Add a merchant to a store.
|
|
253
|
+
*
|
|
254
|
+
* @param params - Merchant addition parameters
|
|
255
|
+
* @returns Added merchant details
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* const result = await client.storeMerchants.add({
|
|
259
|
+
* storeId: "store_xxx",
|
|
260
|
+
* email: "member@example.com",
|
|
261
|
+
* role: "admin",
|
|
262
|
+
* });
|
|
263
|
+
*/
|
|
264
|
+
async add(params) {
|
|
265
|
+
return this.http.post("/v1/actions/store-merchant/add-merchant", params);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Remove a merchant from a store.
|
|
269
|
+
*
|
|
270
|
+
* @param params - Merchant removal parameters
|
|
271
|
+
* @returns Removal confirmation
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* const result = await client.storeMerchants.remove({
|
|
275
|
+
* storeId: "store_xxx",
|
|
276
|
+
* merchantId: "merchant_xxx",
|
|
277
|
+
* });
|
|
278
|
+
*/
|
|
279
|
+
async remove(params) {
|
|
280
|
+
return this.http.post("/v1/actions/store-merchant/remove-merchant", params);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Update a merchant's role in a store.
|
|
284
|
+
*
|
|
285
|
+
* @param params - Role update parameters
|
|
286
|
+
* @returns Updated role details
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* const result = await client.storeMerchants.updateRole({
|
|
290
|
+
* storeId: "store_xxx",
|
|
291
|
+
* merchantId: "merchant_xxx",
|
|
292
|
+
* role: "member",
|
|
293
|
+
* });
|
|
294
|
+
*/
|
|
295
|
+
async updateRole(params) {
|
|
296
|
+
return this.http.post("/v1/actions/store-merchant/update-role", params);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// src/resources/stores.ts
|
|
301
|
+
var StoresResource = class {
|
|
302
|
+
constructor(http) {
|
|
303
|
+
this.http = http;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Create a new store. Slug is auto-generated from the name.
|
|
307
|
+
*
|
|
308
|
+
* @param params - Store creation parameters
|
|
309
|
+
* @returns Created store entity
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* const { store } = await client.stores.create({ name: "My Store" });
|
|
313
|
+
*/
|
|
314
|
+
async create(params) {
|
|
315
|
+
return this.http.post("/v1/actions/store/create-store", params);
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Update an existing store's settings.
|
|
319
|
+
*
|
|
320
|
+
* @param params - Fields to update (only provided fields are changed)
|
|
321
|
+
* @returns Updated store entity
|
|
322
|
+
*
|
|
323
|
+
* @example
|
|
324
|
+
* const { store } = await client.stores.update({
|
|
325
|
+
* id: "store_xxx",
|
|
326
|
+
* name: "Updated Name",
|
|
327
|
+
* supportEmail: "help@example.com",
|
|
328
|
+
* });
|
|
329
|
+
*/
|
|
330
|
+
async update(params) {
|
|
331
|
+
return this.http.post("/v1/actions/store/update-store", params);
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Soft-delete a store. Only the owner can delete.
|
|
335
|
+
*
|
|
336
|
+
* @param params - Store to delete
|
|
337
|
+
* @returns Deleted store entity (with `deletedAt` set)
|
|
338
|
+
*
|
|
339
|
+
* @example
|
|
340
|
+
* const { store } = await client.stores.delete({ id: "store_xxx" });
|
|
341
|
+
*/
|
|
342
|
+
async delete(params) {
|
|
343
|
+
return this.http.post("/v1/actions/store/delete-store", params);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
// src/resources/subscription-product-groups.ts
|
|
348
|
+
var SubscriptionProductGroupsResource = class {
|
|
349
|
+
constructor(http) {
|
|
350
|
+
this.http = http;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Create a subscription product group for shared-trial or plan switching.
|
|
354
|
+
*
|
|
355
|
+
* @param params - Group creation parameters
|
|
356
|
+
* @returns Created group entity
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* const { group } = await client.subscriptionProductGroups.create({
|
|
360
|
+
* storeId: "store_xxx",
|
|
361
|
+
* name: "Pro Plans",
|
|
362
|
+
* rules: { sharedTrial: true },
|
|
363
|
+
* productIds: ["prod_aaa", "prod_bbb"],
|
|
364
|
+
* });
|
|
365
|
+
*/
|
|
366
|
+
async create(params) {
|
|
367
|
+
return this.http.post("/v1/actions/subscription-product-group/create-group", params);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Update a subscription product group. `productIds` is a full replacement.
|
|
371
|
+
*
|
|
372
|
+
* @param params - Group update parameters
|
|
373
|
+
* @returns Updated group entity
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* const { group } = await client.subscriptionProductGroups.update({
|
|
377
|
+
* id: "group_xxx",
|
|
378
|
+
* productIds: ["prod_aaa", "prod_bbb", "prod_ccc"],
|
|
379
|
+
* });
|
|
380
|
+
*/
|
|
381
|
+
async update(params) {
|
|
382
|
+
return this.http.post("/v1/actions/subscription-product-group/update-group", params);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Hard-delete a subscription product group.
|
|
386
|
+
*
|
|
387
|
+
* @param params - Group to delete
|
|
388
|
+
* @returns Deleted group entity
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* const { group } = await client.subscriptionProductGroups.delete({ id: "group_xxx" });
|
|
392
|
+
*/
|
|
393
|
+
async delete(params) {
|
|
394
|
+
return this.http.post("/v1/actions/subscription-product-group/delete-group", params);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Publish a test-environment group to production (upsert).
|
|
398
|
+
*
|
|
399
|
+
* @param params - Group to publish
|
|
400
|
+
* @returns Published group entity
|
|
401
|
+
*
|
|
402
|
+
* @example
|
|
403
|
+
* const { group } = await client.subscriptionProductGroups.publish({ id: "group_xxx" });
|
|
404
|
+
*/
|
|
405
|
+
async publish(params) {
|
|
406
|
+
return this.http.post("/v1/actions/subscription-product-group/publish-group", params);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// src/resources/subscription-products.ts
|
|
411
|
+
var SubscriptionProductsResource = class {
|
|
412
|
+
constructor(http) {
|
|
413
|
+
this.http = http;
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Create a subscription product with billing period and multi-currency pricing.
|
|
417
|
+
*
|
|
418
|
+
* @param params - Product creation parameters
|
|
419
|
+
* @returns Created product detail
|
|
420
|
+
*
|
|
421
|
+
* @example
|
|
422
|
+
* const { product } = await client.subscriptionProducts.create({
|
|
423
|
+
* storeId: "store_xxx",
|
|
424
|
+
* name: "Pro Plan",
|
|
425
|
+
* billingPeriod: "monthly",
|
|
426
|
+
* prices: { USD: { amount: 999, taxIncluded: false, taxCategory: "saas" } },
|
|
427
|
+
* });
|
|
428
|
+
*/
|
|
429
|
+
async create(params) {
|
|
430
|
+
return this.http.post("/v1/actions/subscription-product/create-product", params);
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Update a subscription product. Creates a new version; skips if unchanged.
|
|
434
|
+
*
|
|
435
|
+
* @param params - Product update parameters (all content fields required)
|
|
436
|
+
* @returns Updated product detail
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* const { product } = await client.subscriptionProducts.update({
|
|
440
|
+
* id: "prod_xxx",
|
|
441
|
+
* name: "Pro Plan v2",
|
|
442
|
+
* billingPeriod: "monthly",
|
|
443
|
+
* prices: { USD: { amount: 1499, taxIncluded: false, taxCategory: "saas" } },
|
|
444
|
+
* });
|
|
445
|
+
*/
|
|
446
|
+
async update(params) {
|
|
447
|
+
return this.http.post("/v1/actions/subscription-product/update-product", params);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Publish a subscription product's test version to production.
|
|
451
|
+
*
|
|
452
|
+
* @param params - Product to publish
|
|
453
|
+
* @returns Published product detail
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* const { product } = await client.subscriptionProducts.publish({ id: "prod_xxx" });
|
|
457
|
+
*/
|
|
458
|
+
async publish(params) {
|
|
459
|
+
return this.http.post("/v1/actions/subscription-product/publish-product", params);
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Update a subscription product's status (active/inactive).
|
|
463
|
+
*
|
|
464
|
+
* @param params - Status update parameters
|
|
465
|
+
* @returns Updated product detail
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* const { product } = await client.subscriptionProducts.updateStatus({
|
|
469
|
+
* id: "prod_xxx",
|
|
470
|
+
* status: ProductVersionStatus.Active,
|
|
471
|
+
* });
|
|
472
|
+
*/
|
|
473
|
+
async updateStatus(params) {
|
|
474
|
+
return this.http.post("/v1/actions/subscription-product/update-status", params);
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
// src/client.ts
|
|
479
|
+
var WaffoPancake = class {
|
|
480
|
+
http;
|
|
481
|
+
auth;
|
|
482
|
+
stores;
|
|
483
|
+
storeMerchants;
|
|
484
|
+
onetimeProducts;
|
|
485
|
+
subscriptionProducts;
|
|
486
|
+
subscriptionProductGroups;
|
|
487
|
+
orders;
|
|
488
|
+
checkout;
|
|
489
|
+
graphql;
|
|
490
|
+
constructor(config) {
|
|
491
|
+
this.http = new HttpClient(config);
|
|
492
|
+
this.auth = new AuthResource(this.http);
|
|
493
|
+
this.stores = new StoresResource(this.http);
|
|
494
|
+
this.storeMerchants = new StoreMerchantsResource(this.http);
|
|
495
|
+
this.onetimeProducts = new OnetimeProductsResource(this.http);
|
|
496
|
+
this.subscriptionProducts = new SubscriptionProductsResource(this.http);
|
|
497
|
+
this.subscriptionProductGroups = new SubscriptionProductGroupsResource(this.http);
|
|
498
|
+
this.orders = new OrdersResource(this.http);
|
|
499
|
+
this.checkout = new CheckoutResource(this.http);
|
|
500
|
+
this.graphql = new GraphQLResource(this.http);
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
// src/webhooks.ts
|
|
505
|
+
import { createVerify } from "crypto";
|
|
506
|
+
var DEFAULT_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
507
|
+
var TEST_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
508
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxnmRY6yMMA3lVqmAU6ZG
|
|
509
|
+
b1sjL/+r/z6E+ZjkXaDAKiqOhk9rpazni0bNsGXwmftTPk9jy2wn+j6JHODD/WH/
|
|
510
|
+
SCnSfvKkLIjy4Hk7BuCgB174C0ydan7J+KgXLkOwgCAxxB68t2tezldwo74ZpXgn
|
|
511
|
+
F49opzMvQ9prEwIAWOE+kV9iK6gx/AckSMtHIHpUesoPDkldpmFHlB2qpf1vsFTZ
|
|
512
|
+
5kD6DmGl+2GIVK01aChy2lk8pLv0yUMu18v44sLkO5M44TkGPJD9qG09wrvVG2wp
|
|
513
|
+
OTVCn1n5pP8P+HRLcgzbUB3OlZVfdFurn6EZwtyL4ZD9kdkQ4EZE/9inKcp3c1h4
|
|
514
|
+
xwIDAQAB
|
|
515
|
+
-----END PUBLIC KEY-----`;
|
|
516
|
+
var PROD_PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
|
|
517
|
+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+xApdTIb4ua+DgZKQ54
|
|
518
|
+
iBsD82ybyhGCLRETONW4Jgbb3A8DUM1LqBk6r/CmTOCHqLalTQHNigvP3R5zkDNX
|
|
519
|
+
iRJz6gA4MJ/+8K0+mnEE2RISQzN+Qu65TNd6svb+INm/kMaftY4uIXr6y6kchtTJ
|
|
520
|
+
dwnQhcKdAL2v7h7IFnkVelQsKxDdb2PqX8xX/qwd01iXvMcpCCaXovUwZsxH2QN5
|
|
521
|
+
ZKBTseJivbhUeyJCco4fdUyxOMHe2ybCVhyvim2uxAl1nkvL5L8RCWMCAV55LLo0
|
|
522
|
+
9OhmLahz/DYNu13YLVP6dvIT09ZFBYU6Owj1NxdinTynlJCFS9VYwBgmftosSE1U
|
|
523
|
+
dwIDAQAB
|
|
524
|
+
-----END PUBLIC KEY-----`;
|
|
525
|
+
function parseSignatureHeader(header) {
|
|
526
|
+
let t = "";
|
|
527
|
+
let v1 = "";
|
|
528
|
+
for (const pair of header.split(",")) {
|
|
529
|
+
const eqIdx = pair.indexOf("=");
|
|
530
|
+
if (eqIdx === -1) continue;
|
|
531
|
+
const key = pair.slice(0, eqIdx).trim();
|
|
532
|
+
const value = pair.slice(eqIdx + 1).trim();
|
|
533
|
+
if (key === "t") t = value;
|
|
534
|
+
else if (key === "v1") v1 = value;
|
|
535
|
+
}
|
|
536
|
+
return { t, v1 };
|
|
537
|
+
}
|
|
538
|
+
function rsaVerify(signatureInput, v1, publicKey) {
|
|
539
|
+
const verifier = createVerify("RSA-SHA256");
|
|
540
|
+
verifier.update(signatureInput);
|
|
541
|
+
return verifier.verify(publicKey, v1, "base64");
|
|
542
|
+
}
|
|
543
|
+
function verifyWebhook(payload, signatureHeader, options) {
|
|
544
|
+
if (!signatureHeader) {
|
|
545
|
+
throw new Error("Missing X-Waffo-Signature header");
|
|
546
|
+
}
|
|
547
|
+
const { t, v1 } = parseSignatureHeader(signatureHeader);
|
|
548
|
+
if (!t || !v1) {
|
|
549
|
+
throw new Error("Malformed X-Waffo-Signature header: missing t or v1");
|
|
550
|
+
}
|
|
551
|
+
const toleranceMs = options?.toleranceMs ?? DEFAULT_TOLERANCE_MS;
|
|
552
|
+
if (toleranceMs > 0) {
|
|
553
|
+
const timestampMs = Number(t);
|
|
554
|
+
if (Number.isNaN(timestampMs)) {
|
|
555
|
+
throw new Error("Invalid timestamp in X-Waffo-Signature header");
|
|
556
|
+
}
|
|
557
|
+
if (Math.abs(Date.now() - timestampMs) > toleranceMs) {
|
|
558
|
+
throw new Error("Webhook timestamp outside tolerance window (possible replay attack)");
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
const signatureInput = `${t}.${payload}`;
|
|
562
|
+
const env = options?.environment;
|
|
563
|
+
if (env === "test") {
|
|
564
|
+
if (!rsaVerify(signatureInput, v1, TEST_PUBLIC_KEY)) {
|
|
565
|
+
throw new Error("Invalid webhook signature (test key)");
|
|
566
|
+
}
|
|
567
|
+
} else if (env === "prod") {
|
|
568
|
+
if (!rsaVerify(signatureInput, v1, PROD_PUBLIC_KEY)) {
|
|
569
|
+
throw new Error("Invalid webhook signature (prod key)");
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
const prodValid = rsaVerify(signatureInput, v1, PROD_PUBLIC_KEY);
|
|
573
|
+
if (!prodValid) {
|
|
574
|
+
const testValid = rsaVerify(signatureInput, v1, TEST_PUBLIC_KEY);
|
|
575
|
+
if (!testValid) {
|
|
576
|
+
throw new Error("Invalid webhook signature (tried both prod and test keys)");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return JSON.parse(payload);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/types.ts
|
|
584
|
+
var Environment = /* @__PURE__ */ ((Environment2) => {
|
|
585
|
+
Environment2["Test"] = "test";
|
|
586
|
+
Environment2["Prod"] = "prod";
|
|
587
|
+
return Environment2;
|
|
588
|
+
})(Environment || {});
|
|
589
|
+
var TaxCategory = /* @__PURE__ */ ((TaxCategory2) => {
|
|
590
|
+
TaxCategory2["DigitalGoods"] = "digital_goods";
|
|
591
|
+
TaxCategory2["SaaS"] = "saas";
|
|
592
|
+
TaxCategory2["Software"] = "software";
|
|
593
|
+
TaxCategory2["Ebook"] = "ebook";
|
|
594
|
+
TaxCategory2["OnlineCourse"] = "online_course";
|
|
595
|
+
TaxCategory2["Consulting"] = "consulting";
|
|
596
|
+
TaxCategory2["ProfessionalService"] = "professional_service";
|
|
597
|
+
return TaxCategory2;
|
|
598
|
+
})(TaxCategory || {});
|
|
599
|
+
var BillingPeriod = /* @__PURE__ */ ((BillingPeriod2) => {
|
|
600
|
+
BillingPeriod2["Weekly"] = "weekly";
|
|
601
|
+
BillingPeriod2["Monthly"] = "monthly";
|
|
602
|
+
BillingPeriod2["Quarterly"] = "quarterly";
|
|
603
|
+
BillingPeriod2["Yearly"] = "yearly";
|
|
604
|
+
return BillingPeriod2;
|
|
605
|
+
})(BillingPeriod || {});
|
|
606
|
+
var ProductVersionStatus = /* @__PURE__ */ ((ProductVersionStatus2) => {
|
|
607
|
+
ProductVersionStatus2["Active"] = "active";
|
|
608
|
+
ProductVersionStatus2["Inactive"] = "inactive";
|
|
609
|
+
return ProductVersionStatus2;
|
|
610
|
+
})(ProductVersionStatus || {});
|
|
611
|
+
var EntityStatus = /* @__PURE__ */ ((EntityStatus2) => {
|
|
612
|
+
EntityStatus2["Active"] = "active";
|
|
613
|
+
EntityStatus2["Inactive"] = "inactive";
|
|
614
|
+
EntityStatus2["Suspended"] = "suspended";
|
|
615
|
+
return EntityStatus2;
|
|
616
|
+
})(EntityStatus || {});
|
|
617
|
+
var StoreRole = /* @__PURE__ */ ((StoreRole2) => {
|
|
618
|
+
StoreRole2["Owner"] = "owner";
|
|
619
|
+
StoreRole2["Admin"] = "admin";
|
|
620
|
+
StoreRole2["Member"] = "member";
|
|
621
|
+
return StoreRole2;
|
|
622
|
+
})(StoreRole || {});
|
|
623
|
+
var OnetimeOrderStatus = /* @__PURE__ */ ((OnetimeOrderStatus2) => {
|
|
624
|
+
OnetimeOrderStatus2["Pending"] = "pending";
|
|
625
|
+
OnetimeOrderStatus2["Completed"] = "completed";
|
|
626
|
+
OnetimeOrderStatus2["Canceled"] = "canceled";
|
|
627
|
+
return OnetimeOrderStatus2;
|
|
628
|
+
})(OnetimeOrderStatus || {});
|
|
629
|
+
var SubscriptionOrderStatus = /* @__PURE__ */ ((SubscriptionOrderStatus2) => {
|
|
630
|
+
SubscriptionOrderStatus2["Pending"] = "pending";
|
|
631
|
+
SubscriptionOrderStatus2["Active"] = "active";
|
|
632
|
+
SubscriptionOrderStatus2["Canceling"] = "canceling";
|
|
633
|
+
SubscriptionOrderStatus2["Canceled"] = "canceled";
|
|
634
|
+
SubscriptionOrderStatus2["PastDue"] = "past_due";
|
|
635
|
+
SubscriptionOrderStatus2["Expired"] = "expired";
|
|
636
|
+
return SubscriptionOrderStatus2;
|
|
637
|
+
})(SubscriptionOrderStatus || {});
|
|
638
|
+
var PaymentStatus = /* @__PURE__ */ ((PaymentStatus2) => {
|
|
639
|
+
PaymentStatus2["Pending"] = "pending";
|
|
640
|
+
PaymentStatus2["Succeeded"] = "succeeded";
|
|
641
|
+
PaymentStatus2["Failed"] = "failed";
|
|
642
|
+
PaymentStatus2["Canceled"] = "canceled";
|
|
643
|
+
return PaymentStatus2;
|
|
644
|
+
})(PaymentStatus || {});
|
|
645
|
+
var RefundTicketStatus = /* @__PURE__ */ ((RefundTicketStatus2) => {
|
|
646
|
+
RefundTicketStatus2["Pending"] = "pending";
|
|
647
|
+
RefundTicketStatus2["Approved"] = "approved";
|
|
648
|
+
RefundTicketStatus2["Rejected"] = "rejected";
|
|
649
|
+
RefundTicketStatus2["Processing"] = "processing";
|
|
650
|
+
RefundTicketStatus2["Succeeded"] = "succeeded";
|
|
651
|
+
RefundTicketStatus2["Failed"] = "failed";
|
|
652
|
+
return RefundTicketStatus2;
|
|
653
|
+
})(RefundTicketStatus || {});
|
|
654
|
+
var RefundStatus = /* @__PURE__ */ ((RefundStatus2) => {
|
|
655
|
+
RefundStatus2["Succeeded"] = "succeeded";
|
|
656
|
+
RefundStatus2["Failed"] = "failed";
|
|
657
|
+
return RefundStatus2;
|
|
658
|
+
})(RefundStatus || {});
|
|
659
|
+
var MediaType = /* @__PURE__ */ ((MediaType2) => {
|
|
660
|
+
MediaType2["Image"] = "image";
|
|
661
|
+
MediaType2["Video"] = "video";
|
|
662
|
+
return MediaType2;
|
|
663
|
+
})(MediaType || {});
|
|
664
|
+
var CheckoutSessionProductType = /* @__PURE__ */ ((CheckoutSessionProductType2) => {
|
|
665
|
+
CheckoutSessionProductType2["Onetime"] = "onetime";
|
|
666
|
+
CheckoutSessionProductType2["Subscription"] = "subscription";
|
|
667
|
+
return CheckoutSessionProductType2;
|
|
668
|
+
})(CheckoutSessionProductType || {});
|
|
669
|
+
var ErrorLayer = /* @__PURE__ */ ((ErrorLayer2) => {
|
|
670
|
+
ErrorLayer2["Gateway"] = "gateway";
|
|
671
|
+
ErrorLayer2["User"] = "user";
|
|
672
|
+
ErrorLayer2["Store"] = "store";
|
|
673
|
+
ErrorLayer2["Product"] = "product";
|
|
674
|
+
ErrorLayer2["Order"] = "order";
|
|
675
|
+
ErrorLayer2["GraphQL"] = "graphql";
|
|
676
|
+
ErrorLayer2["Resource"] = "resource";
|
|
677
|
+
ErrorLayer2["Email"] = "email";
|
|
678
|
+
return ErrorLayer2;
|
|
679
|
+
})(ErrorLayer || {});
|
|
680
|
+
var WebhookEventType = /* @__PURE__ */ ((WebhookEventType2) => {
|
|
681
|
+
WebhookEventType2["OrderCompleted"] = "order.completed";
|
|
682
|
+
WebhookEventType2["SubscriptionActivated"] = "subscription.activated";
|
|
683
|
+
WebhookEventType2["SubscriptionPaymentSucceeded"] = "subscription.payment_succeeded";
|
|
684
|
+
WebhookEventType2["SubscriptionCanceling"] = "subscription.canceling";
|
|
685
|
+
WebhookEventType2["SubscriptionUncanceled"] = "subscription.uncanceled";
|
|
686
|
+
WebhookEventType2["SubscriptionUpdated"] = "subscription.updated";
|
|
687
|
+
WebhookEventType2["SubscriptionCanceled"] = "subscription.canceled";
|
|
688
|
+
WebhookEventType2["SubscriptionPastDue"] = "subscription.past_due";
|
|
689
|
+
WebhookEventType2["RefundSucceeded"] = "refund.succeeded";
|
|
690
|
+
WebhookEventType2["RefundFailed"] = "refund.failed";
|
|
691
|
+
return WebhookEventType2;
|
|
692
|
+
})(WebhookEventType || {});
|
|
693
|
+
export {
|
|
694
|
+
BillingPeriod,
|
|
695
|
+
CheckoutSessionProductType,
|
|
696
|
+
EntityStatus,
|
|
697
|
+
Environment,
|
|
698
|
+
ErrorLayer,
|
|
699
|
+
MediaType,
|
|
700
|
+
OnetimeOrderStatus,
|
|
701
|
+
PaymentStatus,
|
|
702
|
+
ProductVersionStatus,
|
|
703
|
+
RefundStatus,
|
|
704
|
+
RefundTicketStatus,
|
|
705
|
+
StoreRole,
|
|
706
|
+
SubscriptionOrderStatus,
|
|
707
|
+
TaxCategory,
|
|
708
|
+
WaffoPancake,
|
|
709
|
+
WaffoPancakeError,
|
|
710
|
+
WebhookEventType,
|
|
711
|
+
verifyWebhook
|
|
712
|
+
};
|
|
713
|
+
//# sourceMappingURL=index.js.map
|