@siglume/direct-request-payment 0.4.22 → 0.4.23
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 +12 -0
- package/bin/siglume-sdrp.mjs +58 -10
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/pricing.md +1 -1
- package/docs/sandbox.md +7 -0
- package/examples/hosted-checkout-python/pyproject.toml +1 -1
- package/package.json +2 -2
- package/templates/express/siglume-order-store.sql.ts +58 -8
|
@@ -324,18 +324,26 @@ class SqlSiglumeOrderStore implements SiglumeSdrpOrderStore {
|
|
|
324
324
|
|
|
325
325
|
async processWebhookEventOnce(eventId: string, handler: () => Promise<void>): Promise<"processed" | "duplicate"> {
|
|
326
326
|
const cleanEventId = requireText(eventId, "event_id");
|
|
327
|
+
if (!this.options.executor.transaction && !this.tx.getStore()) {
|
|
328
|
+
return this.processWebhookEventOnceWithoutTransaction(cleanEventId, handler);
|
|
329
|
+
}
|
|
327
330
|
return this.withTransaction(async (executor) => {
|
|
328
331
|
const parts = sqlParts(this.options);
|
|
329
|
-
const existing = await executor.query(
|
|
330
|
-
`SELECT
|
|
332
|
+
const existing = await executor.query<{ status?: unknown }>(
|
|
333
|
+
`SELECT status FROM ${parts.qEvents} WHERE event_id = ${this.p(1)} LIMIT 1`,
|
|
331
334
|
[cleanEventId],
|
|
332
335
|
);
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
336
|
+
const existingStatus = existing.length ? String(existing[0]?.status || "") : "";
|
|
337
|
+
if (existingStatus === "processed" || existingStatus === "processing") return "duplicate";
|
|
338
|
+
if (existingStatus === "failed") {
|
|
339
|
+
await executor.execute(
|
|
340
|
+
`UPDATE ${parts.qEvents} SET status = ${this.p(1)}, error_message = NULL, processed_at = NULL WHERE event_id = ${this.p(2)}`,
|
|
341
|
+
["processing", cleanEventId],
|
|
342
|
+
);
|
|
343
|
+
} else {
|
|
344
|
+
const inserted = affectedRows(await executor.execute(insertWebhookEventSql(this.options, cleanEventId), [cleanEventId, "processing"]));
|
|
345
|
+
if (inserted === 0) return "duplicate";
|
|
346
|
+
}
|
|
339
347
|
await this.tx.run(executor, handler);
|
|
340
348
|
await executor.execute(
|
|
341
349
|
`UPDATE ${parts.qEvents} SET status = ${this.p(1)}, processed_at = CURRENT_TIMESTAMP WHERE event_id = ${this.p(2)}`,
|
|
@@ -354,6 +362,43 @@ class SqlSiglumeOrderStore implements SiglumeSdrpOrderStore {
|
|
|
354
362
|
return rows[0] ?? null;
|
|
355
363
|
}
|
|
356
364
|
|
|
365
|
+
private async processWebhookEventOnceWithoutTransaction(
|
|
366
|
+
cleanEventId: string,
|
|
367
|
+
handler: () => Promise<void>,
|
|
368
|
+
): Promise<"processed" | "duplicate"> {
|
|
369
|
+
const parts = sqlParts(this.options);
|
|
370
|
+
const existing = await this.options.executor.query<{ status?: unknown }>(
|
|
371
|
+
`SELECT status FROM ${parts.qEvents} WHERE event_id = ${this.p(1)} LIMIT 1`,
|
|
372
|
+
[cleanEventId],
|
|
373
|
+
);
|
|
374
|
+
const existingStatus = existing.length ? String(existing[0]?.status || "") : "";
|
|
375
|
+
if (existingStatus === "processed" || existingStatus === "processing") return "duplicate";
|
|
376
|
+
if (existingStatus === "failed") {
|
|
377
|
+
await this.options.executor.execute(
|
|
378
|
+
`UPDATE ${parts.qEvents} SET status = ${this.p(1)}, error_message = NULL, processed_at = NULL WHERE event_id = ${this.p(2)}`,
|
|
379
|
+
["processing", cleanEventId],
|
|
380
|
+
);
|
|
381
|
+
} else {
|
|
382
|
+
const inserted = await this.executeChanged(insertWebhookEventSql(this.options, cleanEventId), [cleanEventId, "processing"]);
|
|
383
|
+
if (inserted === 0) return "duplicate";
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
await handler();
|
|
388
|
+
await this.options.executor.execute(
|
|
389
|
+
`UPDATE ${parts.qEvents} SET status = ${this.p(1)}, error_message = NULL, processed_at = CURRENT_TIMESTAMP WHERE event_id = ${this.p(2)}`,
|
|
390
|
+
["processed", cleanEventId],
|
|
391
|
+
);
|
|
392
|
+
return "processed";
|
|
393
|
+
} catch (error) {
|
|
394
|
+
await this.options.executor.execute(
|
|
395
|
+
`UPDATE ${parts.qEvents} SET status = ${this.p(1)}, error_message = ${this.p(2)}, processed_at = NULL WHERE event_id = ${this.p(3)}`,
|
|
396
|
+
["failed", errorMessage(error), cleanEventId],
|
|
397
|
+
);
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
357
402
|
async markOrderPaidOnce(input: {
|
|
358
403
|
order_id: string;
|
|
359
404
|
requirement_id: string;
|
|
@@ -536,6 +581,11 @@ function textOrNull(value: unknown): string | null {
|
|
|
536
581
|
return typeof value === "string" && value ? value : null;
|
|
537
582
|
}
|
|
538
583
|
|
|
584
|
+
function errorMessage(error: unknown): string {
|
|
585
|
+
if (error instanceof Error) return error.message.slice(0, 1000);
|
|
586
|
+
return String(error || "webhook handler failed").slice(0, 1000);
|
|
587
|
+
}
|
|
588
|
+
|
|
539
589
|
function normalizeRows(value: unknown): Record<string, unknown>[] {
|
|
540
590
|
if (Array.isArray(value)) return value as Record<string, unknown>[];
|
|
541
591
|
if (value && typeof value === "object" && Array.isArray((value as { rows?: unknown[] }).rows)) {
|