@open-mercato/checkout 0.6.6-develop.6362.1.a3dd164018 → 0.6.6-develop.6363.1.03c5e3429a

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.
@@ -12,11 +12,11 @@ import {
12
12
  readGatewayTransaction,
13
13
  sendMockGatewayWebhook,
14
14
  submitPayLink,
15
- waitForCapturedExampleEvents,
16
15
  waitForCheckoutStatus
17
16
  } from "./helpers/fixtures.js";
18
17
  test.describe("TC-CHKT-033: External webhook subscription can receive checkout success/failure automation events with stable identifiers and no secrets", () => {
19
18
  test("emits webhook-safe completed and failed payloads with stable identifiers for both terminal states", async ({ request }) => {
19
+ test.setTimeout(6e4);
20
20
  let token = null;
21
21
  let templateId = null;
22
22
  let completedLinkId = null;
@@ -68,14 +68,21 @@ test.describe("TC-CHKT-033: External webhook subscription can receive checkout s
68
68
  { providerKey: "mock_processing" }
69
69
  );
70
70
  expect(webhookResponse.status()).toBe(202);
71
- await waitForCheckoutStatus(request, token, failedBody.transactionId, "failed");
72
- await waitForCapturedExampleEvents(request, token, [
73
- "checkout.transaction.completed",
74
- "checkout.transaction.failed"
75
- ]);
76
- const events = await listCapturedExampleEvents(request, token);
77
- const completedEvent = events.find((event) => event.event === "checkout.transaction.completed" && event.payload.transactionId === completedBody.transactionId);
78
- const failedEvent = events.find((event) => event.event === "checkout.transaction.failed" && event.payload.transactionId === failedBody.transactionId);
71
+ const failedTransaction = await waitForCheckoutStatus(request, token, failedBody.transactionId, "failed", {
72
+ attempts: 120,
73
+ intervalMs: 250
74
+ });
75
+ expect(failedTransaction.status).toBe("failed");
76
+ expect(failedTransaction.paymentStatus).toBe("failed");
77
+ let completedEvent;
78
+ let failedEvent;
79
+ for (let attempt = 0; attempt < 40; attempt += 1) {
80
+ const events = await listCapturedExampleEvents(request, token, { prefix: "checkout.transaction." });
81
+ completedEvent = events.find((event) => event.event === "checkout.transaction.completed" && event.payload.transactionId === completedBody.transactionId);
82
+ failedEvent = events.find((event) => event.event === "checkout.transaction.failed" && event.payload.transactionId === failedBody.transactionId);
83
+ if (completedEvent && failedEvent) break;
84
+ await new Promise((resolve) => setTimeout(resolve, 150));
85
+ }
79
86
  for (const [eventName, eventPayload, linkId, slug, gatewayProvider] of [
80
87
  ["checkout.transaction.completed", completedEvent?.payload, completedLink.id, completedLink.slug, "mock"],
81
88
  ["checkout.transaction.failed", failedEvent?.payload, failedLink.id, failedLink.slug, "mock_processing"]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/__integration__/TC-CHKT-033.spec.ts"],
4
- "sourcesContent": ["import { expect, test } from '@playwright/test'\nimport { getAuthToken } from '@open-mercato/core/modules/core/__integration__/helpers/api'\nimport {\n clearCapturedExampleEvents,\n createCustomerData,\n createFixedTemplateInput,\n createLinkFixture,\n createTemplateFixture,\n deleteCheckoutEntityIfExists,\n findGatewayTransactionIdForCheckout,\n listCapturedExampleEvents,\n readGatewayTransaction,\n sendMockGatewayWebhook,\n submitPayLink,\n waitForCapturedExampleEvents,\n waitForCheckoutStatus,\n} from './helpers/fixtures'\n\ntest.describe('TC-CHKT-033: External webhook subscription can receive checkout success/failure automation events with stable identifiers and no secrets', () => {\n test('emits webhook-safe completed and failed payloads with stable identifiers for both terminal states', async ({ request }) => {\n let token: string | null = null\n let templateId: string | null = null\n let completedLinkId: string | null = null\n let failedLinkId: string | null = null\n let failedTransactionId: string | null = null\n\n try {\n token = await getAuthToken(request)\n await clearCapturedExampleEvents(request, token)\n\n templateId = await createTemplateFixture(request, token, createFixedTemplateInput())\n\n const completedLink = await createLinkFixture(request, token, {\n ...createFixedTemplateInput({\n status: 'active',\n templateId,\n }),\n })\n completedLinkId = completedLink.id\n\n const completedSubmit = await submitPayLink(request, completedLink.slug, {\n customerData: createCustomerData(),\n acceptedLegalConsents: {},\n amount: 49.99,\n })\n expect(completedSubmit.status()).toBe(201)\n const completedBody = await completedSubmit.json()\n\n const failedLink = await createLinkFixture(request, token, {\n ...createFixedTemplateInput({\n status: 'active',\n templateId,\n gatewayProviderKey: 'mock_processing',\n }),\n })\n failedLinkId = failedLink.id\n\n const failedSubmit = await submitPayLink(request, failedLink.slug, {\n customerData: createCustomerData(),\n acceptedLegalConsents: {},\n amount: 49.99,\n })\n expect(failedSubmit.status()).toBe(201)\n const failedBody = await failedSubmit.json()\n failedTransactionId = failedBody.transactionId\n\n const gatewayTransactionId = await findGatewayTransactionIdForCheckout(request, token, failedBody.transactionId)\n const gatewayTransaction = await readGatewayTransaction(request, token, gatewayTransactionId)\n expect(gatewayTransaction.providerSessionId).toBeTruthy()\n\n const webhookResponse = await sendMockGatewayWebhook(\n request,\n token,\n gatewayTransaction.providerSessionId!,\n 'failed',\n 49.99,\n { providerKey: 'mock_processing' },\n )\n expect(webhookResponse.status()).toBe(202)\n await waitForCheckoutStatus(request, token, failedBody.transactionId, 'failed')\n\n await waitForCapturedExampleEvents(request, token, [\n 'checkout.transaction.completed',\n 'checkout.transaction.failed',\n ])\n const events = await listCapturedExampleEvents(request, token)\n const completedEvent = events.find((event) => event.event === 'checkout.transaction.completed' && event.payload.transactionId === completedBody.transactionId)\n const failedEvent = events.find((event) => event.event === 'checkout.transaction.failed' && event.payload.transactionId === failedBody.transactionId)\n\n for (const [eventName, eventPayload, linkId, slug, gatewayProvider] of [\n ['checkout.transaction.completed', completedEvent?.payload, completedLink.id, completedLink.slug, 'mock'],\n ['checkout.transaction.failed', failedEvent?.payload, failedLink.id, failedLink.slug, 'mock_processing'],\n ] as const) {\n expect(eventPayload, `${eventName} payload missing`).toBeTruthy()\n expect(eventPayload).toMatchObject({\n linkId,\n templateId,\n slug,\n gatewayProvider,\n amount: 49.99,\n currency: 'USD',\n })\n expect(typeof eventPayload?.transactionId).toBe('string')\n expect(typeof eventPayload?.gatewayTransactionId).toBe('string')\n expect(typeof eventPayload?.occurredAt).toBe('string')\n expect(eventPayload).not.toHaveProperty('customerData')\n expect(eventPayload).not.toHaveProperty('acceptedLegalConsents')\n expect(eventPayload).not.toHaveProperty('gatewaySettings')\n expect(eventPayload).not.toHaveProperty('passwordHash')\n }\n } finally {\n if (failedTransactionId && token) {\n await waitForCheckoutStatus(request, token, failedTransactionId, 'failed')\n }\n await deleteCheckoutEntityIfExists(request, token, 'links', completedLinkId)\n await deleteCheckoutEntityIfExists(request, token, 'links', failedLinkId)\n await deleteCheckoutEntityIfExists(request, token, 'templates', templateId)\n }\n })\n})\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { expect, test } from '@playwright/test'\nimport { getAuthToken } from '@open-mercato/core/modules/core/__integration__/helpers/api'\nimport {\n clearCapturedExampleEvents,\n createCustomerData,\n createFixedTemplateInput,\n createLinkFixture,\n createTemplateFixture,\n deleteCheckoutEntityIfExists,\n findGatewayTransactionIdForCheckout,\n listCapturedExampleEvents,\n readGatewayTransaction,\n sendMockGatewayWebhook,\n submitPayLink,\n waitForCheckoutStatus,\n} from './helpers/fixtures'\n\ntest.describe('TC-CHKT-033: External webhook subscription can receive checkout success/failure automation events with stable identifiers and no secrets', () => {\n test('emits webhook-safe completed and failed payloads with stable identifiers for both terminal states', async ({ request }) => {\n test.setTimeout(60_000)\n\n let token: string | null = null\n let templateId: string | null = null\n let completedLinkId: string | null = null\n let failedLinkId: string | null = null\n let failedTransactionId: string | null = null\n\n try {\n token = await getAuthToken(request)\n await clearCapturedExampleEvents(request, token)\n\n templateId = await createTemplateFixture(request, token, createFixedTemplateInput())\n\n const completedLink = await createLinkFixture(request, token, {\n ...createFixedTemplateInput({\n status: 'active',\n templateId,\n }),\n })\n completedLinkId = completedLink.id\n\n const completedSubmit = await submitPayLink(request, completedLink.slug, {\n customerData: createCustomerData(),\n acceptedLegalConsents: {},\n amount: 49.99,\n })\n expect(completedSubmit.status()).toBe(201)\n const completedBody = await completedSubmit.json()\n\n const failedLink = await createLinkFixture(request, token, {\n ...createFixedTemplateInput({\n status: 'active',\n templateId,\n gatewayProviderKey: 'mock_processing',\n }),\n })\n failedLinkId = failedLink.id\n\n const failedSubmit = await submitPayLink(request, failedLink.slug, {\n customerData: createCustomerData(),\n acceptedLegalConsents: {},\n amount: 49.99,\n })\n expect(failedSubmit.status()).toBe(201)\n const failedBody = await failedSubmit.json()\n failedTransactionId = failedBody.transactionId\n\n const gatewayTransactionId = await findGatewayTransactionIdForCheckout(request, token, failedBody.transactionId)\n const gatewayTransaction = await readGatewayTransaction(request, token, gatewayTransactionId)\n expect(gatewayTransaction.providerSessionId).toBeTruthy()\n\n const webhookResponse = await sendMockGatewayWebhook(\n request,\n token,\n gatewayTransaction.providerSessionId!,\n 'failed',\n 49.99,\n { providerKey: 'mock_processing' },\n )\n expect(webhookResponse.status()).toBe(202)\n const failedTransaction = await waitForCheckoutStatus(request, token, failedBody.transactionId, 'failed', {\n attempts: 120,\n intervalMs: 250,\n })\n expect(failedTransaction.status).toBe('failed')\n expect(failedTransaction.paymentStatus).toBe('failed')\n\n let completedEvent: Awaited<ReturnType<typeof listCapturedExampleEvents>>[number] | undefined\n let failedEvent: Awaited<ReturnType<typeof listCapturedExampleEvents>>[number] | undefined\n for (let attempt = 0; attempt < 40; attempt += 1) {\n const events = await listCapturedExampleEvents(request, token, { prefix: 'checkout.transaction.' })\n completedEvent = events.find((event) => event.event === 'checkout.transaction.completed' && event.payload.transactionId === completedBody.transactionId)\n failedEvent = events.find((event) => event.event === 'checkout.transaction.failed' && event.payload.transactionId === failedBody.transactionId)\n if (completedEvent && failedEvent) break\n await new Promise((resolve) => setTimeout(resolve, 150))\n }\n\n for (const [eventName, eventPayload, linkId, slug, gatewayProvider] of [\n ['checkout.transaction.completed', completedEvent?.payload, completedLink.id, completedLink.slug, 'mock'],\n ['checkout.transaction.failed', failedEvent?.payload, failedLink.id, failedLink.slug, 'mock_processing'],\n ] as const) {\n expect(eventPayload, `${eventName} payload missing`).toBeTruthy()\n expect(eventPayload).toMatchObject({\n linkId,\n templateId,\n slug,\n gatewayProvider,\n amount: 49.99,\n currency: 'USD',\n })\n expect(typeof eventPayload?.transactionId).toBe('string')\n expect(typeof eventPayload?.gatewayTransactionId).toBe('string')\n expect(typeof eventPayload?.occurredAt).toBe('string')\n expect(eventPayload).not.toHaveProperty('customerData')\n expect(eventPayload).not.toHaveProperty('acceptedLegalConsents')\n expect(eventPayload).not.toHaveProperty('gatewaySettings')\n expect(eventPayload).not.toHaveProperty('passwordHash')\n }\n } finally {\n if (failedTransactionId && token) {\n await waitForCheckoutStatus(request, token, failedTransactionId, 'failed')\n }\n await deleteCheckoutEntityIfExists(request, token, 'links', completedLinkId)\n await deleteCheckoutEntityIfExists(request, token, 'links', failedLinkId)\n await deleteCheckoutEntityIfExists(request, token, 'templates', templateId)\n }\n })\n})\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -210,11 +210,13 @@ async function readCheckoutStatus(request, slug, transactionId, options) {
210
210
  }
211
211
  );
212
212
  }
213
- async function waitForCheckoutStatus(request, token, transactionId, expectedStatus) {
214
- for (let attempt = 0; attempt < 20; attempt += 1) {
213
+ async function waitForCheckoutStatus(request, token, transactionId, expectedStatus, options) {
214
+ const attempts = options?.attempts ?? 20;
215
+ const intervalMs = options?.intervalMs ?? 150;
216
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
215
217
  const transaction = await readCheckoutTransaction(request, token, transactionId);
216
218
  if (transaction.status === expectedStatus) return transaction;
217
- await new Promise((resolve) => setTimeout(resolve, 150));
219
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
218
220
  }
219
221
  return readCheckoutTransaction(request, token, transactionId);
220
222
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/checkout/__integration__/helpers/fixtures.ts"],
4
- "sourcesContent": ["import { createHmac } from 'node:crypto'\nimport { expect, type APIRequestContext, type APIResponse, type Page } from '@playwright/test'\nimport { apiRequest } from '@open-mercato/core/modules/core/__integration__/helpers/api'\nimport {\n readJsonSafe,\n} from '@open-mercato/core/modules/core/__integration__/helpers/generalFixtures'\nimport type { PublicSubmitInput } from '../../data/validators'\n\nconst MOCK_GATEWAY_DEV_WEBHOOK_SECRET = 'open-mercato-mock-dev-webhook-secret'\nconst MOCK_GATEWAY_SIGNATURE_HEADER = 'x-mock-signature'\n\ntype JsonRecord = Record<string, unknown>\n\nlet forwardedIpSequence = Math.floor(Math.random() * 10_000)\n\nexport type CheckoutTemplateInput = {\n name: string\n title?: string | null\n subtitle?: string | null\n description?: string | null\n pricingMode: 'fixed' | 'custom_amount' | 'price_list'\n fixedPriceAmount?: number | null\n fixedPriceCurrencyCode?: string | null\n fixedPriceIncludesTax?: boolean\n fixedPriceOriginalAmount?: number | null\n customAmountMin?: number | null\n customAmountMax?: number | null\n customAmountCurrencyCode?: string | null\n priceListItems?: Array<{\n id: string\n description: string\n amount: number\n currencyCode: string\n }> | null\n gatewayProviderKey: string\n gatewaySettings?: Record<string, unknown>\n customFieldsetCode?: string | null\n collectCustomerDetails?: boolean\n legalDocuments?: {\n terms?: { title: string; markdown: string; required?: boolean }\n privacyPolicy?: { title: string; markdown: string; required?: boolean }\n }\n displayCustomFieldsOnPage?: boolean\n password?: string | null\n maxCompletions?: number | null\n status?: 'draft' | 'active' | 'inactive'\n customFields?: Record<string, unknown>\n}\n\nexport type CheckoutLinkInput = CheckoutTemplateInput & {\n templateId?: string | null\n slug?: string | null\n}\n\nexport type CheckoutLinkRecord = {\n id: string\n slug: string\n name?: string\n title?: string | null\n subtitle?: string | null\n status?: string\n templateId?: string | null\n completionCount?: number\n activeReservationCount?: number\n fixedPriceAmount?: number | null\n displayCustomFieldsOnPage?: boolean\n publicCustomFields?: Array<{ key?: string; label?: string; value?: unknown }>\n customerFieldsSchema?: Array<{ key: string; required?: boolean }>\n requiresPassword?: boolean\n available?: boolean\n remainingUses?: number | null\n gatewayProviderKey?: string | null\n customFields?: Record<string, unknown>\n}\n\nexport type CheckoutTransactionRecord = {\n id: string\n linkId?: string\n status: string\n paymentStatus?: string | null\n gatewayTransactionId?: string | null\n email?: string | null\n firstName?: string | null\n lastName?: string | null\n phone?: string | null\n acceptedLegalConsents?: Record<string, unknown> | null\n customerData?: Record<string, unknown> | null\n}\n\nexport type ExampleCapturedEvent = {\n event: string\n payload: Record<string, unknown>\n capturedAt: string\n}\n\nfunction uniqueLabel(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction createUniqueForwardedIp(): string {\n forwardedIpSequence += 1\n const thirdOctet = Math.floor(forwardedIpSequence / 250) % 250 + 1\n const fourthOctet = forwardedIpSequence % 250 + 1\n return `198.51.${thirdOctet}.${fourthOctet}`\n}\n\nexport function createCheckoutClientHeaders(overrides: Record<string, string> = {}): Record<string, string> {\n if (overrides['x-forwarded-for'] || overrides['x-real-ip']) {\n return overrides\n }\n return {\n 'x-forwarded-for': createUniqueForwardedIp(),\n ...overrides,\n }\n}\n\nexport function createFixedTemplateInput(overrides: Partial<CheckoutLinkInput> = {}): CheckoutLinkInput {\n const label = uniqueLabel('checkout-fixed')\n return {\n name: `QA ${label}`,\n title: `QA ${label}`,\n subtitle: 'QA checkout fixture',\n description: 'QA checkout fixture description',\n pricingMode: 'fixed',\n fixedPriceAmount: 49.99,\n fixedPriceCurrencyCode: 'USD',\n fixedPriceIncludesTax: true,\n fixedPriceOriginalAmount: 69.99,\n gatewayProviderKey: 'mock',\n status: 'draft',\n collectCustomerDetails: true,\n displayCustomFieldsOnPage: false,\n ...overrides,\n }\n}\n\nexport function createCustomAmountTemplateInput(overrides: Partial<CheckoutLinkInput> = {}): CheckoutLinkInput {\n const label = uniqueLabel('checkout-custom')\n return {\n name: `QA ${label}`,\n title: `QA ${label}`,\n subtitle: 'QA custom amount fixture',\n description: 'QA custom amount description',\n pricingMode: 'custom_amount',\n customAmountMin: 10,\n customAmountMax: 100,\n customAmountCurrencyCode: 'USD',\n gatewayProviderKey: 'mock',\n status: 'active',\n collectCustomerDetails: true,\n displayCustomFieldsOnPage: false,\n ...overrides,\n }\n}\n\nexport function createPriceListTemplateInput(overrides: Partial<CheckoutLinkInput> = {}): CheckoutLinkInput {\n const label = uniqueLabel('checkout-price-list')\n return {\n name: `QA ${label}`,\n title: `QA ${label}`,\n subtitle: 'QA price list fixture',\n description: 'QA price list description',\n pricingMode: 'price_list',\n priceListItems: [\n { id: 'basic', description: 'Basic', amount: 19.99, currencyCode: 'USD' },\n { id: 'plus', description: 'Plus', amount: 49.99, currencyCode: 'USD' },\n ],\n gatewayProviderKey: 'mock',\n status: 'active',\n collectCustomerDetails: true,\n displayCustomFieldsOnPage: false,\n ...overrides,\n }\n}\n\nexport function createCustomerData(overrides: Record<string, unknown> = {}): Record<string, unknown> {\n const label = uniqueLabel('customer')\n return {\n firstName: 'Quinn',\n lastName: 'Checkout',\n email: `${label}@example.test`,\n phone: '+15550001111',\n ...overrides,\n }\n}\n\nexport async function createTemplateFixture(\n request: APIRequestContext,\n token: string,\n input: CheckoutTemplateInput,\n): Promise<string> {\n const response = await apiRequest(request, 'POST', '/api/checkout/templates', {\n token,\n data: input,\n })\n const body = await readJsonSafe<{ id?: string }>(response)\n expect(\n response.ok(),\n `Failed to create template: ${response.status()} ${JSON.stringify(body)}`,\n ).toBeTruthy()\n expect(typeof body?.id === 'string' && body.id.length > 0, 'Template id is required').toBeTruthy()\n return body!.id!\n}\n\nexport async function createLinkFixture(\n request: APIRequestContext,\n token: string,\n input: CheckoutLinkInput,\n): Promise<{ id: string; slug: string }> {\n const response = await apiRequest(request, 'POST', '/api/checkout/links', {\n token,\n data: input,\n })\n const body = await readJsonSafe<{ id?: string; slug?: string }>(response)\n expect(\n response.ok(),\n `Failed to create link: ${response.status()} ${JSON.stringify(body)}`,\n ).toBeTruthy()\n expect(typeof body?.id === 'string' && body.id.length > 0, 'Link id is required').toBeTruthy()\n expect(typeof body?.slug === 'string' && body.slug.length > 0, 'Link slug is required').toBeTruthy()\n return { id: body!.id!, slug: body!.slug! }\n}\n\nexport async function readTemplate(\n request: APIRequestContext,\n token: string,\n templateId: string,\n): Promise<JsonRecord> {\n const response = await apiRequest(request, 'GET', `/api/checkout/templates/${encodeURIComponent(templateId)}`, { token })\n expect(response.ok(), `Failed to read template ${templateId}: ${response.status()}`).toBeTruthy()\n return (await readJsonSafe<JsonRecord>(response)) ?? {}\n}\n\nexport async function readLink(\n request: APIRequestContext,\n token: string,\n linkId: string,\n): Promise<CheckoutLinkRecord> {\n const response = await apiRequest(request, 'GET', `/api/checkout/links/${encodeURIComponent(linkId)}`, { token })\n expect(response.ok(), `Failed to read link ${linkId}: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<CheckoutLinkRecord>(response)) ?? {}) as CheckoutLinkRecord\n}\n\nexport async function updateTemplate(\n request: APIRequestContext,\n token: string,\n templateId: string,\n data: Record<string, unknown>,\n): Promise<APIResponse> {\n return apiRequest(request, 'PUT', `/api/checkout/templates/${encodeURIComponent(templateId)}`, {\n token,\n data,\n })\n}\n\nexport async function updateLink(\n request: APIRequestContext,\n token: string,\n linkId: string,\n data: Record<string, unknown>,\n): Promise<APIResponse> {\n return apiRequest(request, 'PUT', `/api/checkout/links/${encodeURIComponent(linkId)}`, {\n token,\n data,\n })\n}\n\nexport async function deleteTemplate(\n request: APIRequestContext,\n token: string,\n templateId: string,\n): Promise<APIResponse> {\n return apiRequest(request, 'DELETE', `/api/checkout/templates/${encodeURIComponent(templateId)}`, { token })\n}\n\nexport async function deleteLink(\n request: APIRequestContext,\n token: string,\n linkId: string,\n): Promise<APIResponse> {\n return apiRequest(request, 'DELETE', `/api/checkout/links/${encodeURIComponent(linkId)}`, { token })\n}\n\nexport async function listTemplates(\n request: APIRequestContext,\n token: string,\n query = '',\n): Promise<{ items: JsonRecord[]; total: number }> {\n const response = await apiRequest(request, 'GET', `/api/checkout/templates${query ? `?${query}` : ''}`, { token })\n expect(response.ok(), `Failed to list templates: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<{ items?: JsonRecord[]; total?: number }>(response)) ?? {\n items: [],\n total: 0,\n }) as { items: JsonRecord[]; total: number }\n}\n\nexport async function listLinks(\n request: APIRequestContext,\n token: string,\n query = '',\n): Promise<{ items: JsonRecord[]; total: number }> {\n const response = await apiRequest(request, 'GET', `/api/checkout/links${query ? `?${query}` : ''}`, { token })\n expect(response.ok(), `Failed to list links: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<{ items?: JsonRecord[]; total?: number }>(response)) ?? {\n items: [],\n total: 0,\n }) as { items: JsonRecord[]; total: number }\n}\n\nexport async function readPublicPayLink(\n request: APIRequestContext,\n slug: string,\n options?: { headers?: Record<string, string>; preview?: boolean },\n): Promise<APIResponse> {\n const query = options?.preview ? '?preview=true' : ''\n return request.fetch(`${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}${query}`, {\n method: 'GET',\n headers: createCheckoutClientHeaders(options?.headers),\n })\n}\n\nexport async function verifyPayLinkPassword(\n request: APIRequestContext,\n slug: string,\n password: string,\n): Promise<APIResponse> {\n return request.fetch(`${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}/verify-password`, {\n method: 'POST',\n headers: createCheckoutClientHeaders({ 'Content-Type': 'application/json' }),\n data: { password },\n })\n}\n\nexport async function submitPayLink(\n request: APIRequestContext,\n slug: string,\n input: PublicSubmitInput,\n options?: { idempotencyKey?: string; headers?: Record<string, string> },\n): Promise<APIResponse> {\n return request.fetch(`${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}/submit`, {\n method: 'POST',\n headers: createCheckoutClientHeaders({\n 'Content-Type': 'application/json',\n 'Idempotency-Key': options?.idempotencyKey ?? uniqueLabel('idempotency'),\n ...(options?.headers ?? {}),\n }),\n data: input,\n })\n}\n\nexport async function readCheckoutTransaction(\n request: APIRequestContext,\n token: string,\n transactionId: string,\n): Promise<CheckoutTransactionRecord> {\n const response = await apiRequest(request, 'GET', `/api/checkout/transactions/${encodeURIComponent(transactionId)}`, { token })\n expect(response.ok(), `Failed to read transaction ${transactionId}: ${response.status()}`).toBeTruthy()\n const body = (await readJsonSafe<{ transaction?: CheckoutTransactionRecord }>(response)) ?? {}\n return (body.transaction ?? {}) as CheckoutTransactionRecord\n}\n\nexport async function listCheckoutTransactions(\n request: APIRequestContext,\n token: string,\n query = '',\n): Promise<{ items: CheckoutTransactionRecord[]; total: number; canViewPii?: boolean }> {\n const response = await apiRequest(request, 'GET', `/api/checkout/transactions${query ? `?${query}` : ''}`, { token })\n expect(response.ok(), `Failed to list checkout transactions: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<{ items?: CheckoutTransactionRecord[]; total?: number; canViewPii?: boolean }>(response)) ?? {\n items: [],\n total: 0,\n }) as { items: CheckoutTransactionRecord[]; total: number; canViewPii?: boolean }\n}\n\nexport async function readCheckoutStatus(\n request: APIRequestContext,\n slug: string,\n transactionId: string,\n options?: { headers?: Record<string, string> },\n): Promise<APIResponse> {\n return request.fetch(\n `${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}/status/${encodeURIComponent(transactionId)}`,\n {\n method: 'GET',\n headers: createCheckoutClientHeaders(options?.headers),\n },\n )\n}\n\nexport async function waitForCheckoutStatus(\n request: APIRequestContext,\n token: string,\n transactionId: string,\n expectedStatus: string,\n): Promise<CheckoutTransactionRecord> {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n const transaction = await readCheckoutTransaction(request, token, transactionId)\n if (transaction.status === expectedStatus) return transaction\n await new Promise((resolve) => setTimeout(resolve, 150))\n }\n return readCheckoutTransaction(request, token, transactionId)\n}\n\nexport async function findGatewayTransactionIdForCheckout(\n request: APIRequestContext,\n token: string,\n checkoutTransactionId: string,\n): Promise<string> {\n const response = await apiRequest(request, 'GET', '/api/payment_gateways/transactions?page=1&pageSize=100', { token })\n expect(response.ok(), `Failed to list gateway transactions: ${response.status()}`).toBeTruthy()\n const body = (await readJsonSafe<{ items?: Array<{ id: string; paymentId: string }> }>(response)) ?? {}\n const match = body.items?.find((item) => item.paymentId === checkoutTransactionId)\n expect(match, `Gateway transaction for checkout ${checkoutTransactionId} was not found`).toBeTruthy()\n return match!.id\n}\n\nexport async function readGatewayTransaction(\n request: APIRequestContext,\n token: string,\n gatewayTransactionId: string,\n): Promise<{\n id: string\n providerSessionId: string | null\n unifiedStatus: string\n}> {\n const response = await apiRequest(request, 'GET', `/api/payment_gateways/transactions/${encodeURIComponent(gatewayTransactionId)}`, { token })\n expect(response.ok(), `Failed to read gateway transaction ${gatewayTransactionId}: ${response.status()}`).toBeTruthy()\n const body = (await readJsonSafe<{ transaction?: { id: string; providerSessionId?: string | null; unifiedStatus?: string } }>(response)) ?? {}\n const transaction = body.transaction\n expect(transaction?.id, 'Gateway transaction payload is required').toBeTruthy()\n return {\n id: transaction!.id,\n providerSessionId: transaction?.providerSessionId ?? null,\n unifiedStatus: transaction?.unifiedStatus ?? 'unknown',\n }\n}\n\nexport async function sendMockGatewayWebhook(\n request: APIRequestContext,\n token: string,\n providerSessionId: string,\n status: 'captured' | 'cancelled' | 'failed',\n amount: number,\n options?: { providerKey?: string },\n): Promise<APIResponse> {\n const provider = encodeURIComponent(options?.providerKey ?? 'mock')\n const payload = {\n type: `payment.${status}`,\n id: uniqueLabel(`mock-${status}`),\n data: {\n id: providerSessionId,\n status,\n amount,\n },\n }\n const rawBody = JSON.stringify(payload)\n const signature = createHmac('sha256', MOCK_GATEWAY_DEV_WEBHOOK_SECRET)\n .update(rawBody, 'utf-8')\n .digest('hex')\n const BASE_URL = process.env.BASE_URL?.trim() || ''\n return request.fetch(`${BASE_URL}/api/payment_gateways/webhook/${provider}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [MOCK_GATEWAY_SIGNATURE_HEADER]: signature,\n Authorization: `Bearer ${token}`,\n },\n data: rawBody,\n })\n}\n\nexport async function deleteCheckoutEntityIfExists(\n request: APIRequestContext,\n token: string | null,\n kind: 'templates' | 'links',\n id: string | null,\n): Promise<void> {\n if (!token || !id) return\n try {\n await apiRequest(request, 'DELETE', `/api/checkout/${kind}/${encodeURIComponent(id)}`, { token })\n } catch {\n return\n }\n}\n\nexport async function loginToBackendAndOpen(page: Page, path: string): Promise<void> {\n await page.goto('/login')\n await page.getByLabel('Email').fill('admin@acme.com')\n await page.getByLabel('Password', { exact: true }).fill('secret')\n await page.getByRole('button', { name: /sign in/i }).click()\n await page.waitForURL(/\\/backend(?:\\/.*)?$/)\n await page.goto(path)\n}\n\nexport async function clearCapturedExampleEvents(\n request: APIRequestContext,\n token: string,\n): Promise<void> {\n const response = await apiRequest(request, 'DELETE', '/api/example/qa-events', { token })\n expect(response.ok(), `Failed to clear captured example events: ${response.status()}`).toBeTruthy()\n}\n\nexport async function listCapturedExampleEvents(\n request: APIRequestContext,\n token: string,\n options?: { event?: string; prefix?: string },\n): Promise<ExampleCapturedEvent[]> {\n const query = new URLSearchParams()\n if (options?.event) query.set('event', options.event)\n if (options?.prefix) query.set('prefix', options.prefix)\n const response = await apiRequest(request, 'GET', `/api/example/qa-events${query.size > 0 ? `?${query.toString()}` : ''}`, {\n token,\n })\n expect(response.ok(), `Failed to list captured example events: ${response.status()}`).toBeTruthy()\n const body = await readJsonSafe<{ items?: ExampleCapturedEvent[] }>(response)\n return Array.isArray(body?.items) ? body.items : []\n}\n\nexport async function waitForCapturedExampleEvents(\n request: APIRequestContext,\n token: string,\n expectedEventNames: string[],\n): Promise<ExampleCapturedEvent[]> {\n for (let attempt = 0; attempt < 30; attempt += 1) {\n const events = await listCapturedExampleEvents(request, token)\n const seenNames = new Set(events.map((event) => event.event))\n if (expectedEventNames.every((eventName) => seenNames.has(eventName))) {\n return events\n }\n await new Promise((resolve) => setTimeout(resolve, 150))\n }\n return listCapturedExampleEvents(request, token)\n}\n"],
5
- "mappings": "AAAA,SAAS,kBAAkB;AAC3B,SAAS,cAAmE;AAC5E,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,OACK;AAGP,MAAM,kCAAkC;AACxC,MAAM,gCAAgC;AAItC,IAAI,sBAAsB,KAAK,MAAM,KAAK,OAAO,IAAI,GAAM;AAkF3D,SAAS,YAAY,QAAwB;AAC3C,SAAO,GAAG,MAAM,IAAI,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAC1E;AAEA,SAAS,0BAAkC;AACzC,yBAAuB;AACvB,QAAM,aAAa,KAAK,MAAM,sBAAsB,GAAG,IAAI,MAAM;AACjE,QAAM,cAAc,sBAAsB,MAAM;AAChD,SAAO,UAAU,UAAU,IAAI,WAAW;AAC5C;AAEO,SAAS,4BAA4B,YAAoC,CAAC,GAA2B;AAC1G,MAAI,UAAU,iBAAiB,KAAK,UAAU,WAAW,GAAG;AAC1D,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,mBAAmB,wBAAwB;AAAA,IAC3C,GAAG;AAAA,EACL;AACF;AAEO,SAAS,yBAAyB,YAAwC,CAAC,GAAsB;AACtG,QAAM,QAAQ,YAAY,gBAAgB;AAC1C,SAAO;AAAA,IACL,MAAM,MAAM,KAAK;AAAA,IACjB,OAAO,MAAM,KAAK;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,kBAAkB;AAAA,IAClB,wBAAwB;AAAA,IACxB,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,oBAAoB;AAAA,IACpB,QAAQ;AAAA,IACR,wBAAwB;AAAA,IACxB,2BAA2B;AAAA,IAC3B,GAAG;AAAA,EACL;AACF;AAEO,SAAS,gCAAgC,YAAwC,CAAC,GAAsB;AAC7G,QAAM,QAAQ,YAAY,iBAAiB;AAC3C,SAAO;AAAA,IACL,MAAM,MAAM,KAAK;AAAA,IACjB,OAAO,MAAM,KAAK;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,0BAA0B;AAAA,IAC1B,oBAAoB;AAAA,IACpB,QAAQ;AAAA,IACR,wBAAwB;AAAA,IACxB,2BAA2B;AAAA,IAC3B,GAAG;AAAA,EACL;AACF;AAEO,SAAS,6BAA6B,YAAwC,CAAC,GAAsB;AAC1G,QAAM,QAAQ,YAAY,qBAAqB;AAC/C,SAAO;AAAA,IACL,MAAM,MAAM,KAAK;AAAA,IACjB,OAAO,MAAM,KAAK;AAAA,IAClB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,gBAAgB;AAAA,MACd,EAAE,IAAI,SAAS,aAAa,SAAS,QAAQ,OAAO,cAAc,MAAM;AAAA,MACxE,EAAE,IAAI,QAAQ,aAAa,QAAQ,QAAQ,OAAO,cAAc,MAAM;AAAA,IACxE;AAAA,IACA,oBAAoB;AAAA,IACpB,QAAQ;AAAA,IACR,wBAAwB;AAAA,IACxB,2BAA2B;AAAA,IAC3B,GAAG;AAAA,EACL;AACF;AAEO,SAAS,mBAAmB,YAAqC,CAAC,GAA4B;AACnG,QAAM,QAAQ,YAAY,UAAU;AACpC,SAAO;AAAA,IACL,WAAW;AAAA,IACX,UAAU;AAAA,IACV,OAAO,GAAG,KAAK;AAAA,IACf,OAAO;AAAA,IACP,GAAG;AAAA,EACL;AACF;AAEA,eAAsB,sBACpB,SACA,OACA,OACiB;AACjB,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,2BAA2B;AAAA,IAC5E;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACD,QAAM,OAAO,MAAM,aAA8B,QAAQ;AACzD;AAAA,IACE,SAAS,GAAG;AAAA,IACZ,8BAA8B,SAAS,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,EACzE,EAAE,WAAW;AACb,SAAO,OAAO,MAAM,OAAO,YAAY,KAAK,GAAG,SAAS,GAAG,yBAAyB,EAAE,WAAW;AACjG,SAAO,KAAM;AACf;AAEA,eAAsB,kBACpB,SACA,OACA,OACuC;AACvC,QAAM,WAAW,MAAM,WAAW,SAAS,QAAQ,uBAAuB;AAAA,IACxE;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACD,QAAM,OAAO,MAAM,aAA6C,QAAQ;AACxE;AAAA,IACE,SAAS,GAAG;AAAA,IACZ,0BAA0B,SAAS,OAAO,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC;AAAA,EACrE,EAAE,WAAW;AACb,SAAO,OAAO,MAAM,OAAO,YAAY,KAAK,GAAG,SAAS,GAAG,qBAAqB,EAAE,WAAW;AAC7F,SAAO,OAAO,MAAM,SAAS,YAAY,KAAK,KAAK,SAAS,GAAG,uBAAuB,EAAE,WAAW;AACnG,SAAO,EAAE,IAAI,KAAM,IAAK,MAAM,KAAM,KAAM;AAC5C;AAEA,eAAsB,aACpB,SACA,OACA,YACqB;AACrB,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,2BAA2B,mBAAmB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;AACxH,SAAO,SAAS,GAAG,GAAG,2BAA2B,UAAU,KAAK,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AAChG,SAAQ,MAAM,aAAyB,QAAQ,KAAM,CAAC;AACxD;AAEA,eAAsB,SACpB,SACA,OACA,QAC6B;AAC7B,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,uBAAuB,mBAAmB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC;AAChH,SAAO,SAAS,GAAG,GAAG,uBAAuB,MAAM,KAAK,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AACxF,SAAS,MAAM,aAAiC,QAAQ,KAAM,CAAC;AACjE;AAEA,eAAsB,eACpB,SACA,OACA,YACA,MACsB;AACtB,SAAO,WAAW,SAAS,OAAO,2BAA2B,mBAAmB,UAAU,CAAC,IAAI;AAAA,IAC7F;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,WACpB,SACA,OACA,QACA,MACsB;AACtB,SAAO,WAAW,SAAS,OAAO,uBAAuB,mBAAmB,MAAM,CAAC,IAAI;AAAA,IACrF;AAAA,IACA;AAAA,EACF,CAAC;AACH;AAEA,eAAsB,eACpB,SACA,OACA,YACsB;AACtB,SAAO,WAAW,SAAS,UAAU,2BAA2B,mBAAmB,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;AAC7G;AAEA,eAAsB,WACpB,SACA,OACA,QACsB;AACtB,SAAO,WAAW,SAAS,UAAU,uBAAuB,mBAAmB,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC;AACrG;AAEA,eAAsB,cACpB,SACA,OACA,QAAQ,IACyC;AACjD,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,0BAA0B,QAAQ,IAAI,KAAK,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;AACjH,SAAO,SAAS,GAAG,GAAG,6BAA6B,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AACnF,SAAS,MAAM,aAAuD,QAAQ,KAAM;AAAA,IAClF,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,EACT;AACF;AAEA,eAAsB,UACpB,SACA,OACA,QAAQ,IACyC;AACjD,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,sBAAsB,QAAQ,IAAI,KAAK,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;AAC7G,SAAO,SAAS,GAAG,GAAG,yBAAyB,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AAC/E,SAAS,MAAM,aAAuD,QAAQ,KAAM;AAAA,IAClF,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,EACT;AACF;AAEA,eAAsB,kBACpB,SACA,MACA,SACsB;AACtB,QAAM,QAAQ,SAAS,UAAU,kBAAkB;AACnD,SAAO,QAAQ,MAAM,GAAG,QAAQ,IAAI,YAAY,uBAAuB,qBAAqB,mBAAmB,IAAI,CAAC,GAAG,KAAK,IAAI;AAAA,IAC9H,QAAQ;AAAA,IACR,SAAS,4BAA4B,SAAS,OAAO;AAAA,EACvD,CAAC;AACH;AAEA,eAAsB,sBACpB,SACA,MACA,UACsB;AACtB,SAAO,QAAQ,MAAM,GAAG,QAAQ,IAAI,YAAY,uBAAuB,qBAAqB,mBAAmB,IAAI,CAAC,oBAAoB;AAAA,IACtI,QAAQ;AAAA,IACR,SAAS,4BAA4B,EAAE,gBAAgB,mBAAmB,CAAC;AAAA,IAC3E,MAAM,EAAE,SAAS;AAAA,EACnB,CAAC;AACH;AAEA,eAAsB,cACpB,SACA,MACA,OACA,SACsB;AACtB,SAAO,QAAQ,MAAM,GAAG,QAAQ,IAAI,YAAY,uBAAuB,qBAAqB,mBAAmB,IAAI,CAAC,WAAW;AAAA,IAC7H,QAAQ;AAAA,IACR,SAAS,4BAA4B;AAAA,MACnC,gBAAgB;AAAA,MAChB,mBAAmB,SAAS,kBAAkB,YAAY,aAAa;AAAA,MACvE,GAAI,SAAS,WAAW,CAAC;AAAA,IAC3B,CAAC;AAAA,IACD,MAAM;AAAA,EACR,CAAC;AACH;AAEA,eAAsB,wBACpB,SACA,OACA,eACoC;AACpC,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,8BAA8B,mBAAmB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC;AAC9H,SAAO,SAAS,GAAG,GAAG,8BAA8B,aAAa,KAAK,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AACtG,QAAM,OAAQ,MAAM,aAA0D,QAAQ,KAAM,CAAC;AAC7F,SAAQ,KAAK,eAAe,CAAC;AAC/B;AAEA,eAAsB,yBACpB,SACA,OACA,QAAQ,IAC8E;AACtF,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,6BAA6B,QAAQ,IAAI,KAAK,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC;AACpH,SAAO,SAAS,GAAG,GAAG,yCAAyC,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AAC/F,SAAS,MAAM,aAA4F,QAAQ,KAAM;AAAA,IACvH,OAAO,CAAC;AAAA,IACR,OAAO;AAAA,EACT;AACF;AAEA,eAAsB,mBACpB,SACA,MACA,eACA,SACsB;AACtB,SAAO,QAAQ;AAAA,IACb,GAAG,QAAQ,IAAI,YAAY,uBAAuB,qBAAqB,mBAAmB,IAAI,CAAC,WAAW,mBAAmB,aAAa,CAAC;AAAA,IAC3I;AAAA,MACE,QAAQ;AAAA,MACR,SAAS,4BAA4B,SAAS,OAAO;AAAA,IACvD;AAAA,EACF;AACF;AAEA,eAAsB,sBACpB,SACA,OACA,eACA,gBACoC;AACpC,WAAS,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;AAChD,UAAM,cAAc,MAAM,wBAAwB,SAAS,OAAO,aAAa;AAC/E,QAAI,YAAY,WAAW,eAAgB,QAAO;AAClD,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AAAA,EACzD;AACA,SAAO,wBAAwB,SAAS,OAAO,aAAa;AAC9D;AAEA,eAAsB,oCACpB,SACA,OACA,uBACiB;AACjB,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,0DAA0D,EAAE,MAAM,CAAC;AACrH,SAAO,SAAS,GAAG,GAAG,wCAAwC,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AAC9F,QAAM,OAAQ,MAAM,aAAmE,QAAQ,KAAM,CAAC;AACtG,QAAM,QAAQ,KAAK,OAAO,KAAK,CAAC,SAAS,KAAK,cAAc,qBAAqB;AACjF,SAAO,OAAO,oCAAoC,qBAAqB,gBAAgB,EAAE,WAAW;AACpG,SAAO,MAAO;AAChB;AAEA,eAAsB,uBACpB,SACA,OACA,sBAKC;AACD,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,sCAAsC,mBAAmB,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC;AAC7I,SAAO,SAAS,GAAG,GAAG,sCAAsC,oBAAoB,KAAK,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AACrH,QAAM,OAAQ,MAAM,aAA0G,QAAQ,KAAM,CAAC;AAC7I,QAAM,cAAc,KAAK;AACzB,SAAO,aAAa,IAAI,yCAAyC,EAAE,WAAW;AAC9E,SAAO;AAAA,IACL,IAAI,YAAa;AAAA,IACjB,mBAAmB,aAAa,qBAAqB;AAAA,IACrD,eAAe,aAAa,iBAAiB;AAAA,EAC/C;AACF;AAEA,eAAsB,uBACpB,SACA,OACA,mBACA,QACA,QACA,SACsB;AACtB,QAAM,WAAW,mBAAmB,SAAS,eAAe,MAAM;AAClE,QAAM,UAAU;AAAA,IACd,MAAM,WAAW,MAAM;AAAA,IACvB,IAAI,YAAY,QAAQ,MAAM,EAAE;AAAA,IAChC,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,UAAU,KAAK,UAAU,OAAO;AACtC,QAAM,YAAY,WAAW,UAAU,+BAA+B,EACnE,OAAO,SAAS,OAAO,EACvB,OAAO,KAAK;AACf,QAAM,WAAW,QAAQ,IAAI,UAAU,KAAK,KAAK;AACjD,SAAO,QAAQ,MAAM,GAAG,QAAQ,iCAAiC,QAAQ,IAAI;AAAA,IAC3E,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,CAAC,6BAA6B,GAAG;AAAA,MACjC,eAAe,UAAU,KAAK;AAAA,IAChC;AAAA,IACA,MAAM;AAAA,EACR,CAAC;AACH;AAEA,eAAsB,6BACpB,SACA,OACA,MACA,IACe;AACf,MAAI,CAAC,SAAS,CAAC,GAAI;AACnB,MAAI;AACF,UAAM,WAAW,SAAS,UAAU,iBAAiB,IAAI,IAAI,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC;AAAA,EAClG,QAAQ;AACN;AAAA,EACF;AACF;AAEA,eAAsB,sBAAsB,MAAY,MAA6B;AACnF,QAAM,KAAK,KAAK,QAAQ;AACxB,QAAM,KAAK,WAAW,OAAO,EAAE,KAAK,gBAAgB;AACpD,QAAM,KAAK,WAAW,YAAY,EAAE,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;AAChE,QAAM,KAAK,UAAU,UAAU,EAAE,MAAM,WAAW,CAAC,EAAE,MAAM;AAC3D,QAAM,KAAK,WAAW,qBAAqB;AAC3C,QAAM,KAAK,KAAK,IAAI;AACtB;AAEA,eAAsB,2BACpB,SACA,OACe;AACf,QAAM,WAAW,MAAM,WAAW,SAAS,UAAU,0BAA0B,EAAE,MAAM,CAAC;AACxF,SAAO,SAAS,GAAG,GAAG,4CAA4C,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AACpG;AAEA,eAAsB,0BACpB,SACA,OACA,SACiC;AACjC,QAAM,QAAQ,IAAI,gBAAgB;AAClC,MAAI,SAAS,MAAO,OAAM,IAAI,SAAS,QAAQ,KAAK;AACpD,MAAI,SAAS,OAAQ,OAAM,IAAI,UAAU,QAAQ,MAAM;AACvD,QAAM,WAAW,MAAM,WAAW,SAAS,OAAO,yBAAyB,MAAM,OAAO,IAAI,IAAI,MAAM,SAAS,CAAC,KAAK,EAAE,IAAI;AAAA,IACzH;AAAA,EACF,CAAC;AACD,SAAO,SAAS,GAAG,GAAG,2CAA2C,SAAS,OAAO,CAAC,EAAE,EAAE,WAAW;AACjG,QAAM,OAAO,MAAM,aAAiD,QAAQ;AAC5E,SAAO,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,QAAQ,CAAC;AACpD;AAEA,eAAsB,6BACpB,SACA,OACA,oBACiC;AACjC,WAAS,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;AAChD,UAAM,SAAS,MAAM,0BAA0B,SAAS,KAAK;AAC7D,UAAM,YAAY,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC;AAC5D,QAAI,mBAAmB,MAAM,CAAC,cAAc,UAAU,IAAI,SAAS,CAAC,GAAG;AACrE,aAAO;AAAA,IACT;AACA,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,GAAG,CAAC;AAAA,EACzD;AACA,SAAO,0BAA0B,SAAS,KAAK;AACjD;",
4
+ "sourcesContent": ["import { createHmac } from 'node:crypto'\nimport { expect, type APIRequestContext, type APIResponse, type Page } from '@playwright/test'\nimport { apiRequest } from '@open-mercato/core/modules/core/__integration__/helpers/api'\nimport {\n readJsonSafe,\n} from '@open-mercato/core/modules/core/__integration__/helpers/generalFixtures'\nimport type { PublicSubmitInput } from '../../data/validators'\n\nconst MOCK_GATEWAY_DEV_WEBHOOK_SECRET = 'open-mercato-mock-dev-webhook-secret'\nconst MOCK_GATEWAY_SIGNATURE_HEADER = 'x-mock-signature'\n\ntype JsonRecord = Record<string, unknown>\n\nlet forwardedIpSequence = Math.floor(Math.random() * 10_000)\n\nexport type CheckoutTemplateInput = {\n name: string\n title?: string | null\n subtitle?: string | null\n description?: string | null\n pricingMode: 'fixed' | 'custom_amount' | 'price_list'\n fixedPriceAmount?: number | null\n fixedPriceCurrencyCode?: string | null\n fixedPriceIncludesTax?: boolean\n fixedPriceOriginalAmount?: number | null\n customAmountMin?: number | null\n customAmountMax?: number | null\n customAmountCurrencyCode?: string | null\n priceListItems?: Array<{\n id: string\n description: string\n amount: number\n currencyCode: string\n }> | null\n gatewayProviderKey: string\n gatewaySettings?: Record<string, unknown>\n customFieldsetCode?: string | null\n collectCustomerDetails?: boolean\n legalDocuments?: {\n terms?: { title: string; markdown: string; required?: boolean }\n privacyPolicy?: { title: string; markdown: string; required?: boolean }\n }\n displayCustomFieldsOnPage?: boolean\n password?: string | null\n maxCompletions?: number | null\n status?: 'draft' | 'active' | 'inactive'\n customFields?: Record<string, unknown>\n}\n\nexport type CheckoutLinkInput = CheckoutTemplateInput & {\n templateId?: string | null\n slug?: string | null\n}\n\nexport type CheckoutLinkRecord = {\n id: string\n slug: string\n name?: string\n title?: string | null\n subtitle?: string | null\n status?: string\n templateId?: string | null\n completionCount?: number\n activeReservationCount?: number\n fixedPriceAmount?: number | null\n displayCustomFieldsOnPage?: boolean\n publicCustomFields?: Array<{ key?: string; label?: string; value?: unknown }>\n customerFieldsSchema?: Array<{ key: string; required?: boolean }>\n requiresPassword?: boolean\n available?: boolean\n remainingUses?: number | null\n gatewayProviderKey?: string | null\n customFields?: Record<string, unknown>\n}\n\nexport type CheckoutTransactionRecord = {\n id: string\n linkId?: string\n status: string\n paymentStatus?: string | null\n gatewayTransactionId?: string | null\n email?: string | null\n firstName?: string | null\n lastName?: string | null\n phone?: string | null\n acceptedLegalConsents?: Record<string, unknown> | null\n customerData?: Record<string, unknown> | null\n}\n\nexport type ExampleCapturedEvent = {\n event: string\n payload: Record<string, unknown>\n capturedAt: string\n}\n\nfunction uniqueLabel(prefix: string): string {\n return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction createUniqueForwardedIp(): string {\n forwardedIpSequence += 1\n const thirdOctet = Math.floor(forwardedIpSequence / 250) % 250 + 1\n const fourthOctet = forwardedIpSequence % 250 + 1\n return `198.51.${thirdOctet}.${fourthOctet}`\n}\n\nexport function createCheckoutClientHeaders(overrides: Record<string, string> = {}): Record<string, string> {\n if (overrides['x-forwarded-for'] || overrides['x-real-ip']) {\n return overrides\n }\n return {\n 'x-forwarded-for': createUniqueForwardedIp(),\n ...overrides,\n }\n}\n\nexport function createFixedTemplateInput(overrides: Partial<CheckoutLinkInput> = {}): CheckoutLinkInput {\n const label = uniqueLabel('checkout-fixed')\n return {\n name: `QA ${label}`,\n title: `QA ${label}`,\n subtitle: 'QA checkout fixture',\n description: 'QA checkout fixture description',\n pricingMode: 'fixed',\n fixedPriceAmount: 49.99,\n fixedPriceCurrencyCode: 'USD',\n fixedPriceIncludesTax: true,\n fixedPriceOriginalAmount: 69.99,\n gatewayProviderKey: 'mock',\n status: 'draft',\n collectCustomerDetails: true,\n displayCustomFieldsOnPage: false,\n ...overrides,\n }\n}\n\nexport function createCustomAmountTemplateInput(overrides: Partial<CheckoutLinkInput> = {}): CheckoutLinkInput {\n const label = uniqueLabel('checkout-custom')\n return {\n name: `QA ${label}`,\n title: `QA ${label}`,\n subtitle: 'QA custom amount fixture',\n description: 'QA custom amount description',\n pricingMode: 'custom_amount',\n customAmountMin: 10,\n customAmountMax: 100,\n customAmountCurrencyCode: 'USD',\n gatewayProviderKey: 'mock',\n status: 'active',\n collectCustomerDetails: true,\n displayCustomFieldsOnPage: false,\n ...overrides,\n }\n}\n\nexport function createPriceListTemplateInput(overrides: Partial<CheckoutLinkInput> = {}): CheckoutLinkInput {\n const label = uniqueLabel('checkout-price-list')\n return {\n name: `QA ${label}`,\n title: `QA ${label}`,\n subtitle: 'QA price list fixture',\n description: 'QA price list description',\n pricingMode: 'price_list',\n priceListItems: [\n { id: 'basic', description: 'Basic', amount: 19.99, currencyCode: 'USD' },\n { id: 'plus', description: 'Plus', amount: 49.99, currencyCode: 'USD' },\n ],\n gatewayProviderKey: 'mock',\n status: 'active',\n collectCustomerDetails: true,\n displayCustomFieldsOnPage: false,\n ...overrides,\n }\n}\n\nexport function createCustomerData(overrides: Record<string, unknown> = {}): Record<string, unknown> {\n const label = uniqueLabel('customer')\n return {\n firstName: 'Quinn',\n lastName: 'Checkout',\n email: `${label}@example.test`,\n phone: '+15550001111',\n ...overrides,\n }\n}\n\nexport async function createTemplateFixture(\n request: APIRequestContext,\n token: string,\n input: CheckoutTemplateInput,\n): Promise<string> {\n const response = await apiRequest(request, 'POST', '/api/checkout/templates', {\n token,\n data: input,\n })\n const body = await readJsonSafe<{ id?: string }>(response)\n expect(\n response.ok(),\n `Failed to create template: ${response.status()} ${JSON.stringify(body)}`,\n ).toBeTruthy()\n expect(typeof body?.id === 'string' && body.id.length > 0, 'Template id is required').toBeTruthy()\n return body!.id!\n}\n\nexport async function createLinkFixture(\n request: APIRequestContext,\n token: string,\n input: CheckoutLinkInput,\n): Promise<{ id: string; slug: string }> {\n const response = await apiRequest(request, 'POST', '/api/checkout/links', {\n token,\n data: input,\n })\n const body = await readJsonSafe<{ id?: string; slug?: string }>(response)\n expect(\n response.ok(),\n `Failed to create link: ${response.status()} ${JSON.stringify(body)}`,\n ).toBeTruthy()\n expect(typeof body?.id === 'string' && body.id.length > 0, 'Link id is required').toBeTruthy()\n expect(typeof body?.slug === 'string' && body.slug.length > 0, 'Link slug is required').toBeTruthy()\n return { id: body!.id!, slug: body!.slug! }\n}\n\nexport async function readTemplate(\n request: APIRequestContext,\n token: string,\n templateId: string,\n): Promise<JsonRecord> {\n const response = await apiRequest(request, 'GET', `/api/checkout/templates/${encodeURIComponent(templateId)}`, { token })\n expect(response.ok(), `Failed to read template ${templateId}: ${response.status()}`).toBeTruthy()\n return (await readJsonSafe<JsonRecord>(response)) ?? {}\n}\n\nexport async function readLink(\n request: APIRequestContext,\n token: string,\n linkId: string,\n): Promise<CheckoutLinkRecord> {\n const response = await apiRequest(request, 'GET', `/api/checkout/links/${encodeURIComponent(linkId)}`, { token })\n expect(response.ok(), `Failed to read link ${linkId}: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<CheckoutLinkRecord>(response)) ?? {}) as CheckoutLinkRecord\n}\n\nexport async function updateTemplate(\n request: APIRequestContext,\n token: string,\n templateId: string,\n data: Record<string, unknown>,\n): Promise<APIResponse> {\n return apiRequest(request, 'PUT', `/api/checkout/templates/${encodeURIComponent(templateId)}`, {\n token,\n data,\n })\n}\n\nexport async function updateLink(\n request: APIRequestContext,\n token: string,\n linkId: string,\n data: Record<string, unknown>,\n): Promise<APIResponse> {\n return apiRequest(request, 'PUT', `/api/checkout/links/${encodeURIComponent(linkId)}`, {\n token,\n data,\n })\n}\n\nexport async function deleteTemplate(\n request: APIRequestContext,\n token: string,\n templateId: string,\n): Promise<APIResponse> {\n return apiRequest(request, 'DELETE', `/api/checkout/templates/${encodeURIComponent(templateId)}`, { token })\n}\n\nexport async function deleteLink(\n request: APIRequestContext,\n token: string,\n linkId: string,\n): Promise<APIResponse> {\n return apiRequest(request, 'DELETE', `/api/checkout/links/${encodeURIComponent(linkId)}`, { token })\n}\n\nexport async function listTemplates(\n request: APIRequestContext,\n token: string,\n query = '',\n): Promise<{ items: JsonRecord[]; total: number }> {\n const response = await apiRequest(request, 'GET', `/api/checkout/templates${query ? `?${query}` : ''}`, { token })\n expect(response.ok(), `Failed to list templates: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<{ items?: JsonRecord[]; total?: number }>(response)) ?? {\n items: [],\n total: 0,\n }) as { items: JsonRecord[]; total: number }\n}\n\nexport async function listLinks(\n request: APIRequestContext,\n token: string,\n query = '',\n): Promise<{ items: JsonRecord[]; total: number }> {\n const response = await apiRequest(request, 'GET', `/api/checkout/links${query ? `?${query}` : ''}`, { token })\n expect(response.ok(), `Failed to list links: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<{ items?: JsonRecord[]; total?: number }>(response)) ?? {\n items: [],\n total: 0,\n }) as { items: JsonRecord[]; total: number }\n}\n\nexport async function readPublicPayLink(\n request: APIRequestContext,\n slug: string,\n options?: { headers?: Record<string, string>; preview?: boolean },\n): Promise<APIResponse> {\n const query = options?.preview ? '?preview=true' : ''\n return request.fetch(`${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}${query}`, {\n method: 'GET',\n headers: createCheckoutClientHeaders(options?.headers),\n })\n}\n\nexport async function verifyPayLinkPassword(\n request: APIRequestContext,\n slug: string,\n password: string,\n): Promise<APIResponse> {\n return request.fetch(`${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}/verify-password`, {\n method: 'POST',\n headers: createCheckoutClientHeaders({ 'Content-Type': 'application/json' }),\n data: { password },\n })\n}\n\nexport async function submitPayLink(\n request: APIRequestContext,\n slug: string,\n input: PublicSubmitInput,\n options?: { idempotencyKey?: string; headers?: Record<string, string> },\n): Promise<APIResponse> {\n return request.fetch(`${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}/submit`, {\n method: 'POST',\n headers: createCheckoutClientHeaders({\n 'Content-Type': 'application/json',\n 'Idempotency-Key': options?.idempotencyKey ?? uniqueLabel('idempotency'),\n ...(options?.headers ?? {}),\n }),\n data: input,\n })\n}\n\nexport async function readCheckoutTransaction(\n request: APIRequestContext,\n token: string,\n transactionId: string,\n): Promise<CheckoutTransactionRecord> {\n const response = await apiRequest(request, 'GET', `/api/checkout/transactions/${encodeURIComponent(transactionId)}`, { token })\n expect(response.ok(), `Failed to read transaction ${transactionId}: ${response.status()}`).toBeTruthy()\n const body = (await readJsonSafe<{ transaction?: CheckoutTransactionRecord }>(response)) ?? {}\n return (body.transaction ?? {}) as CheckoutTransactionRecord\n}\n\nexport async function listCheckoutTransactions(\n request: APIRequestContext,\n token: string,\n query = '',\n): Promise<{ items: CheckoutTransactionRecord[]; total: number; canViewPii?: boolean }> {\n const response = await apiRequest(request, 'GET', `/api/checkout/transactions${query ? `?${query}` : ''}`, { token })\n expect(response.ok(), `Failed to list checkout transactions: ${response.status()}`).toBeTruthy()\n return ((await readJsonSafe<{ items?: CheckoutTransactionRecord[]; total?: number; canViewPii?: boolean }>(response)) ?? {\n items: [],\n total: 0,\n }) as { items: CheckoutTransactionRecord[]; total: number; canViewPii?: boolean }\n}\n\nexport async function readCheckoutStatus(\n request: APIRequestContext,\n slug: string,\n transactionId: string,\n options?: { headers?: Record<string, string> },\n): Promise<APIResponse> {\n return request.fetch(\n `${process.env.BASE_URL || 'http://localhost:3000'}/api/checkout/pay/${encodeURIComponent(slug)}/status/${encodeURIComponent(transactionId)}`,\n {\n method: 'GET',\n headers: createCheckoutClientHeaders(options?.headers),\n },\n )\n}\n\nexport async function waitForCheckoutStatus(\n request: APIRequestContext,\n token: string,\n transactionId: string,\n expectedStatus: string,\n options?: { attempts?: number; intervalMs?: number },\n): Promise<CheckoutTransactionRecord> {\n const attempts = options?.attempts ?? 20\n const intervalMs = options?.intervalMs ?? 150\n for (let attempt = 0; attempt < attempts; attempt += 1) {\n const transaction = await readCheckoutTransaction(request, token, transactionId)\n if (transaction.status === expectedStatus) return transaction\n await new Promise((resolve) => setTimeout(resolve, intervalMs))\n }\n return readCheckoutTransaction(request, token, transactionId)\n}\n\nexport async function findGatewayTransactionIdForCheckout(\n request: APIRequestContext,\n token: string,\n checkoutTransactionId: string,\n): Promise<string> {\n const response = await apiRequest(request, 'GET', '/api/payment_gateways/transactions?page=1&pageSize=100', { token })\n expect(response.ok(), `Failed to list gateway transactions: ${response.status()}`).toBeTruthy()\n const body = (await readJsonSafe<{ items?: Array<{ id: string; paymentId: string }> }>(response)) ?? {}\n const match = body.items?.find((item) => item.paymentId === checkoutTransactionId)\n expect(match, `Gateway transaction for checkout ${checkoutTransactionId} was not found`).toBeTruthy()\n return match!.id\n}\n\nexport async function readGatewayTransaction(\n request: APIRequestContext,\n token: string,\n gatewayTransactionId: string,\n): Promise<{\n id: string\n providerSessionId: string | null\n unifiedStatus: string\n}> {\n const response = await apiRequest(request, 'GET', `/api/payment_gateways/transactions/${encodeURIComponent(gatewayTransactionId)}`, { token })\n expect(response.ok(), `Failed to read gateway transaction ${gatewayTransactionId}: ${response.status()}`).toBeTruthy()\n const body = (await readJsonSafe<{ transaction?: { id: string; providerSessionId?: string | null; unifiedStatus?: string } }>(response)) ?? {}\n const transaction = body.transaction\n expect(transaction?.id, 'Gateway transaction payload is required').toBeTruthy()\n return {\n id: transaction!.id,\n providerSessionId: transaction?.providerSessionId ?? null,\n unifiedStatus: transaction?.unifiedStatus ?? 'unknown',\n }\n}\n\nexport async function sendMockGatewayWebhook(\n request: APIRequestContext,\n token: string,\n providerSessionId: string,\n status: 'captured' | 'cancelled' | 'failed',\n amount: number,\n options?: { providerKey?: string },\n): Promise<APIResponse> {\n const provider = encodeURIComponent(options?.providerKey ?? 'mock')\n const payload = {\n type: `payment.${status}`,\n id: uniqueLabel(`mock-${status}`),\n data: {\n id: providerSessionId,\n status,\n amount,\n },\n }\n const rawBody = JSON.stringify(payload)\n const signature = createHmac('sha256', MOCK_GATEWAY_DEV_WEBHOOK_SECRET)\n .update(rawBody, 'utf-8')\n .digest('hex')\n const BASE_URL = process.env.BASE_URL?.trim() || ''\n return request.fetch(`${BASE_URL}/api/payment_gateways/webhook/${provider}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n [MOCK_GATEWAY_SIGNATURE_HEADER]: signature,\n Authorization: `Bearer ${token}`,\n },\n data: rawBody,\n })\n}\n\nexport async function deleteCheckoutEntityIfExists(\n request: APIRequestContext,\n token: string | null,\n kind: 'templates' | 'links',\n id: string | null,\n): Promise<void> {\n if (!token || !id) return\n try {\n await apiRequest(request, 'DELETE', `/api/checkout/${kind}/${encodeURIComponent(id)}`, { token })\n } catch {\n return\n }\n}\n\nexport async function loginToBackendAndOpen(page: Page, path: string): Promise<void> {\n await page.goto('/login')\n await page.getByLabel('Email').fill('admin@acme.com')\n await page.getByLabel('Password', { exact: true }).fill('secret')\n await page.getByRole('button', { name: /sign in/i }).click()\n await page.waitForURL(/\\/backend(?:\\/.*)?$/)\n await page.goto(path)\n}\n\nexport async function clearCapturedExampleEvents(\n request: APIRequestContext,\n token: string,\n): Promise<void> {\n const response = await apiRequest(request, 'DELETE', '/api/example/qa-events', { token })\n expect(response.ok(), `Failed to clear captured example events: ${response.status()}`).toBeTruthy()\n}\n\nexport async function listCapturedExampleEvents(\n request: APIRequestContext,\n token: string,\n options?: { event?: string; prefix?: string },\n): Promise<ExampleCapturedEvent[]> {\n const query = new URLSearchParams()\n if (options?.event) query.set('event', options.event)\n if (options?.prefix) query.set('prefix', options.prefix)\n const response = await apiRequest(request, 'GET', `/api/example/qa-events${query.size > 0 ? `?${query.toString()}` : ''}`, {\n token,\n })\n expect(response.ok(), `Failed to list captured example events: ${response.status()}`).toBeTruthy()\n const body = await readJsonSafe<{ items?: ExampleCapturedEvent[] }>(response)\n return Array.isArray(body?.items) ? body.items : []\n}\n\nexport async function waitForCapturedExampleEvents(\n request: APIRequestContext,\n token: string,\n expectedEventNames: string[],\n): Promise<ExampleCapturedEvent[]> {\n for (let attempt = 0; attempt < 30; attempt += 1) {\n const events = await listCapturedExampleEvents(request, token)\n const seenNames = new Set(events.map((event) => event.event))\n if (expectedEventNames.every((eventName) => seenNames.has(eventName))) {\n return events\n }\n await new Promise((resolve) => setTimeout(resolve, 150))\n }\n return listCapturedExampleEvents(request, token)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/checkout",
3
- "version": "0.6.6-develop.6362.1.a3dd164018",
3
+ "version": "0.6.6-develop.6363.1.03c5e3429a",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -62,18 +62,18 @@
62
62
  }
63
63
  },
64
64
  "dependencies": {
65
- "@open-mercato/core": "0.6.6-develop.6362.1.a3dd164018",
66
- "@open-mercato/ui": "0.6.6-develop.6362.1.a3dd164018",
65
+ "@open-mercato/core": "0.6.6-develop.6363.1.03c5e3429a",
66
+ "@open-mercato/ui": "0.6.6-develop.6363.1.03c5e3429a",
67
67
  "bcryptjs": "^3.0.3"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@mikro-orm/postgresql": "^7.0.14",
71
- "@open-mercato/shared": "0.6.6-develop.6362.1.a3dd164018",
71
+ "@open-mercato/shared": "0.6.6-develop.6363.1.03c5e3429a",
72
72
  "react": "^19.0.0",
73
73
  "react-dom": "^19.0.0"
74
74
  },
75
75
  "devDependencies": {
76
- "@open-mercato/shared": "0.6.6-develop.6362.1.a3dd164018",
76
+ "@open-mercato/shared": "0.6.6-develop.6363.1.03c5e3429a",
77
77
  "@types/jest": "^30.0.0",
78
78
  "@types/react": "^19.2.17",
79
79
  "@types/react-dom": "^19.2.3",
@@ -12,12 +12,13 @@ import {
12
12
  readGatewayTransaction,
13
13
  sendMockGatewayWebhook,
14
14
  submitPayLink,
15
- waitForCapturedExampleEvents,
16
15
  waitForCheckoutStatus,
17
16
  } from './helpers/fixtures'
18
17
 
19
18
  test.describe('TC-CHKT-033: External webhook subscription can receive checkout success/failure automation events with stable identifiers and no secrets', () => {
20
19
  test('emits webhook-safe completed and failed payloads with stable identifiers for both terminal states', async ({ request }) => {
20
+ test.setTimeout(60_000)
21
+
21
22
  let token: string | null = null
22
23
  let templateId: string | null = null
23
24
  let completedLinkId: string | null = null
@@ -77,15 +78,22 @@ test.describe('TC-CHKT-033: External webhook subscription can receive checkout s
77
78
  { providerKey: 'mock_processing' },
78
79
  )
79
80
  expect(webhookResponse.status()).toBe(202)
80
- await waitForCheckoutStatus(request, token, failedBody.transactionId, 'failed')
81
+ const failedTransaction = await waitForCheckoutStatus(request, token, failedBody.transactionId, 'failed', {
82
+ attempts: 120,
83
+ intervalMs: 250,
84
+ })
85
+ expect(failedTransaction.status).toBe('failed')
86
+ expect(failedTransaction.paymentStatus).toBe('failed')
81
87
 
82
- await waitForCapturedExampleEvents(request, token, [
83
- 'checkout.transaction.completed',
84
- 'checkout.transaction.failed',
85
- ])
86
- const events = await listCapturedExampleEvents(request, token)
87
- const completedEvent = events.find((event) => event.event === 'checkout.transaction.completed' && event.payload.transactionId === completedBody.transactionId)
88
- const failedEvent = events.find((event) => event.event === 'checkout.transaction.failed' && event.payload.transactionId === failedBody.transactionId)
88
+ let completedEvent: Awaited<ReturnType<typeof listCapturedExampleEvents>>[number] | undefined
89
+ let failedEvent: Awaited<ReturnType<typeof listCapturedExampleEvents>>[number] | undefined
90
+ for (let attempt = 0; attempt < 40; attempt += 1) {
91
+ const events = await listCapturedExampleEvents(request, token, { prefix: 'checkout.transaction.' })
92
+ completedEvent = events.find((event) => event.event === 'checkout.transaction.completed' && event.payload.transactionId === completedBody.transactionId)
93
+ failedEvent = events.find((event) => event.event === 'checkout.transaction.failed' && event.payload.transactionId === failedBody.transactionId)
94
+ if (completedEvent && failedEvent) break
95
+ await new Promise((resolve) => setTimeout(resolve, 150))
96
+ }
89
97
 
90
98
  for (const [eventName, eventPayload, linkId, slug, gatewayProvider] of [
91
99
  ['checkout.transaction.completed', completedEvent?.payload, completedLink.id, completedLink.slug, 'mock'],
@@ -392,11 +392,14 @@ export async function waitForCheckoutStatus(
392
392
  token: string,
393
393
  transactionId: string,
394
394
  expectedStatus: string,
395
+ options?: { attempts?: number; intervalMs?: number },
395
396
  ): Promise<CheckoutTransactionRecord> {
396
- for (let attempt = 0; attempt < 20; attempt += 1) {
397
+ const attempts = options?.attempts ?? 20
398
+ const intervalMs = options?.intervalMs ?? 150
399
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
397
400
  const transaction = await readCheckoutTransaction(request, token, transactionId)
398
401
  if (transaction.status === expectedStatus) return transaction
399
- await new Promise((resolve) => setTimeout(resolve, 150))
402
+ await new Promise((resolve) => setTimeout(resolve, intervalMs))
400
403
  }
401
404
  return readCheckoutTransaction(request, token, transactionId)
402
405
  }