@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.
@@ -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 event_id FROM ${parts.qEvents} WHERE event_id = ${this.p(1)} LIMIT 1`,
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
- if (existing.length) return "duplicate";
334
- const inserted = await this.executeChanged(
335
- insertWebhookEventSql(this.options, cleanEventId),
336
- [cleanEventId, "processing"],
337
- );
338
- if (inserted === 0) return "duplicate";
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)) {