@open-mercato/checkout 0.6.6-develop.6465.1.019f0fb26f → 0.6.6-develop.6471.1.9ab5bdd79a

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.
Files changed (28) hide show
  1. package/dist/modules/checkout/api/helpers.js +3 -1
  2. package/dist/modules/checkout/api/helpers.js.map +2 -2
  3. package/dist/modules/checkout/api/pay/[slug]/submit/route.js +4 -2
  4. package/dist/modules/checkout/api/pay/[slug]/submit/route.js.map +2 -2
  5. package/dist/modules/checkout/components/PayPage.js +5 -3
  6. package/dist/modules/checkout/components/PayPage.js.map +2 -2
  7. package/dist/modules/checkout/seed/examples.js +3 -1
  8. package/dist/modules/checkout/seed/examples.js.map +2 -2
  9. package/dist/modules/checkout/subscribers/session-started-email.js +3 -1
  10. package/dist/modules/checkout/subscribers/session-started-email.js.map +2 -2
  11. package/dist/modules/checkout/subscribers/transaction-completed-notify.js +4 -2
  12. package/dist/modules/checkout/subscribers/transaction-completed-notify.js.map +2 -2
  13. package/dist/modules/checkout/subscribers/transaction-failed-notify.js +4 -2
  14. package/dist/modules/checkout/subscribers/transaction-failed-notify.js.map +2 -2
  15. package/dist/modules/checkout/subscribers/usage-limit-reached-notify.js +3 -1
  16. package/dist/modules/checkout/subscribers/usage-limit-reached-notify.js.map +2 -2
  17. package/dist/modules/checkout/workers/transaction-expiry.worker.js +3 -1
  18. package/dist/modules/checkout/workers/transaction-expiry.worker.js.map +2 -2
  19. package/package.json +5 -5
  20. package/src/modules/checkout/api/helpers.ts +4 -1
  21. package/src/modules/checkout/api/pay/[slug]/submit/route.ts +5 -2
  22. package/src/modules/checkout/components/PayPage.tsx +6 -3
  23. package/src/modules/checkout/seed/examples.ts +4 -1
  24. package/src/modules/checkout/subscribers/session-started-email.ts +4 -1
  25. package/src/modules/checkout/subscribers/transaction-completed-notify.ts +5 -2
  26. package/src/modules/checkout/subscribers/transaction-failed-notify.ts +5 -2
  27. package/src/modules/checkout/subscribers/usage-limit-reached-notify.ts +4 -1
  28. package/src/modules/checkout/workers/transaction-expiry.worker.ts +4 -1
@@ -4,6 +4,8 @@ import { CHECKOUT_ENTITY_IDS } from "../lib/constants.js";
4
4
  import { DEFAULT_CHECKOUT_CUSTOMER_FIELDS } from "../lib/defaults.js";
5
5
  import { ensureUniqueSlug, toMoneyString, toTemplateOrLinkMutationInput } from "../lib/utils.js";
6
6
  import { ensureCheckoutFieldsetsAndDefinitions } from "./customFields.js";
7
+ import { createLogger } from "@open-mercato/shared/lib/logger";
8
+ const logger = createLogger("checkout").child({ component: "seed" });
7
9
  const TEMPLATE_SEEDS = [
8
10
  {
9
11
  name: "Consulting Fee",
@@ -277,7 +279,7 @@ async function assignCustomFields(args) {
277
279
  values: args.values
278
280
  });
279
281
  } catch (error) {
280
- console.warn("[checkout.seed] Failed to set example custom field values", error);
282
+ logger.warn("Failed to set example custom field values", { err: error });
281
283
  }
282
284
  }
283
285
  async function seedCheckoutExamples({ em, container, tenantId, organizationId }) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/seed/examples.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { InitSetupContext } from '@open-mercato/shared/modules/setup'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { CheckoutLink, CheckoutLinkTemplate } from '../data/entities'\nimport { CHECKOUT_ENTITY_IDS } from '../lib/constants'\nimport { DEFAULT_CHECKOUT_CUSTOMER_FIELDS } from '../lib/defaults'\nimport { ensureUniqueSlug, toMoneyString, toTemplateOrLinkMutationInput } from '../lib/utils'\nimport { ensureCheckoutFieldsetsAndDefinitions } from './customFields'\n\ntype TemplateSeed = {\n name: string\n title: string\n subtitle: string\n description: string\n pricingMode: CheckoutLinkTemplate['pricingMode']\n fixedPriceAmount?: number | null\n fixedPriceCurrencyCode?: string | null\n customAmountMin?: number | null\n customAmountMax?: number | null\n customAmountCurrencyCode?: string | null\n priceListItems?: Array<{ id: string; description: string; amount: number; currencyCode: string }>\n maxCompletions?: number | null\n customFieldsetCode?: string | null\n displayCustomFieldsOnPage?: boolean\n successTitle?: string\n successMessage?: string\n cancelTitle?: string\n cancelMessage?: string\n errorTitle?: string\n errorMessage?: string\n startEmailSubject?: string\n startEmailBody?: string\n successEmailSubject?: string\n successEmailBody?: string\n errorEmailSubject?: string\n errorEmailBody?: string\n collectCustomerDetails?: boolean\n customFields?: Record<string, unknown>\n}\n\ntype LinkSeed = {\n templateName: string\n name: string\n title: string\n subtitle: string\n description: string\n slug: string\n customFields?: Record<string, unknown>\n}\n\nconst TEMPLATE_SEEDS: TemplateSeed[] = [\n {\n name: 'Consulting Fee',\n title: 'Consulting session payment',\n subtitle: 'One-hour consulting session',\n description: '## Included\\n\\n- 60 minute strategy call\\n- Summary and next steps\\n- Single-use payment request',\n pricingMode: 'fixed',\n fixedPriceAmount: 150,\n fixedPriceCurrencyCode: 'USD',\n maxCompletions: 1,\n customFieldsetCode: 'service_package',\n displayCustomFieldsOnPage: true,\n successTitle: 'Booking confirmed',\n successMessage: '## Payment received\\n\\nThank you for booking your consulting session. We will send the meeting invite and prep notes within one business day.',\n cancelTitle: 'Payment cancelled',\n cancelMessage: 'No worries. Your consulting slot has not been confirmed yet. You can return to this link when you are ready.',\n errorTitle: 'Payment needs another try',\n errorMessage: 'We could not finish the payment for your consulting session. Please retry or contact us if the issue continues.',\n startEmailSubject: 'We received your consulting payment request',\n startEmailBody: 'Hi {{firstName}},\\n\\nWe started processing your payment for **{{linkTitle}}** in the amount of **{{amount}} {{currencyCode}}**.',\n successEmailSubject: 'Your consulting session is confirmed',\n successEmailBody: 'Hi {{firstName}},\\n\\nYour payment for **{{linkTitle}}** was completed successfully.\\n\\nTransaction reference: `{{transactionId}}`.\\n\\nWe will follow up with scheduling details shortly.',\n errorEmailSubject: 'We could not complete your payment',\n errorEmailBody: 'Hi {{firstName}},\\n\\nYour payment for **{{linkTitle}}** could not be completed. {{errorMessage}}\\n\\nYou can retry using the same link.',\n collectCustomerDetails: true,\n customFields: {\n service_deliverables: '- 60 minute strategy call\\n- Written summary with next steps\\n- Follow-up recommendations by email',\n delivery_timeline: 'We send the meeting invite and prep notes within 1 business day after payment.',\n session_format: 'Remote video call',\n support_contact: 'ops@open-mercato.com',\n },\n },\n {\n name: 'Donation',\n title: 'Support our community work',\n subtitle: 'Choose any amount that feels right',\n description: '## Thank you\\n\\nYour contribution helps fund future programs, scholarships, and open events.',\n pricingMode: 'custom_amount',\n customAmountMin: 5,\n customAmountMax: 500,\n customAmountCurrencyCode: 'USD',\n maxCompletions: null,\n customFieldsetCode: 'donation_campaign',\n displayCustomFieldsOnPage: true,\n successTitle: 'Thank you for your support',\n successMessage: '## Donation received\\n\\nThank you for supporting our community work. Your contribution helps us fund new workshops, scholarships, and open events.',\n cancelTitle: 'Donation not completed',\n cancelMessage: 'Your donation was not completed. You can come back to this page any time and try again.',\n errorTitle: 'Donation payment failed',\n errorMessage: 'We could not complete the donation payment. Please retry in a moment or use another payment method if available.',\n startEmailSubject: 'Your donation payment is being processed',\n startEmailBody: 'Hi {{firstName}},\\n\\nWe started processing your donation for **{{linkTitle}}** in the amount of **{{amount}} {{currencyCode}}**.',\n successEmailSubject: 'Thank you for your donation',\n successEmailBody: 'Hi {{firstName}},\\n\\nYour donation for **{{linkTitle}}** was completed successfully.\\n\\nReference: `{{transactionId}}`.\\n\\nThank you for helping us continue this work.',\n errorEmailSubject: 'Your donation payment needs another try',\n errorEmailBody: 'Hi {{firstName}},\\n\\nWe could not complete your donation payment. {{errorMessage}}\\n\\nYou can use the same link to try again.',\n collectCustomerDetails: false,\n customFields: {\n impact_summary: 'Your donation helps fund free community workshops and scholarship places for new participants.',\n donation_usage: 'Funds support programming, venue costs, and materials.',\n tax_receipt_note: 'A donation confirmation email is sent after successful payment.',\n support_contact: 'donations@open-mercato.com',\n },\n },\n {\n name: 'Event Ticket',\n title: 'Spring Gala ticket',\n subtitle: 'Select the right ticket tier',\n description: '## Ticket tiers\\n\\nChoose between General, VIP, and Premium access for the gala event.',\n pricingMode: 'price_list',\n priceListItems: [\n { id: 'general', description: 'General admission', amount: 25, currencyCode: 'USD' },\n { id: 'vip', description: 'VIP ticket', amount: 75, currencyCode: 'USD' },\n { id: 'premium', description: 'Premium table seat', amount: 150, currencyCode: 'USD' },\n ],\n maxCompletions: 100,\n customFieldsetCode: 'event_ticket',\n displayCustomFieldsOnPage: true,\n successTitle: 'Your seat is reserved',\n successMessage: '## Ticket purchased\\n\\nThank you for purchasing your Spring Gala ticket. Please keep the confirmation email for check-in details.',\n cancelTitle: 'Ticket payment cancelled',\n cancelMessage: 'Your ticket was not reserved. You can return to this page to complete the purchase later.',\n errorTitle: 'Ticket payment failed',\n errorMessage: 'We could not complete the ticket payment. Please retry or contact the events team for assistance.',\n startEmailSubject: 'Your event ticket payment is being processed',\n startEmailBody: 'Hi {{firstName}},\\n\\nWe started processing your payment for **{{linkTitle}}** in the amount of **{{amount}} {{currencyCode}}**.',\n successEmailSubject: 'Your Spring Gala ticket is confirmed',\n successEmailBody: 'Hi {{firstName}},\\n\\nYour payment for **{{linkTitle}}** was successful.\\n\\nTicket reference: `{{transactionId}}`.\\n\\nWe look forward to seeing you at the event.',\n errorEmailSubject: 'We could not confirm your event ticket',\n errorEmailBody: 'Hi {{firstName}},\\n\\nWe could not complete your ticket payment. {{errorMessage}}\\n\\nPlease use the same link to try again.',\n collectCustomerDetails: true,\n customFields: {\n event_date: 'May 14, 2026 at 7:00 PM',\n event_location: 'Mercato Hall, 12 River Street, New York',\n ticket_includes: '- Entry to the gala\\n- Welcome drink\\n- Networking session',\n support_contact: 'events@open-mercato.com',\n },\n },\n]\n\nconst LINK_SEEDS: LinkSeed[] = [\n {\n templateName: 'Consulting Fee',\n name: 'January Consulting Session',\n title: 'January consulting session',\n subtitle: 'Invoice for a one-hour advisory meeting',\n description: '## January consulting session\\n\\nThis pay link covers the agreed one-hour consulting engagement.',\n slug: 'january-consulting',\n customFields: {\n service_deliverables: '- 60 minute planning call\\n- Action summary tailored to the January brief\\n- Follow-up notes by email',\n delivery_timeline: 'We send the calendar invite within 4 business hours after payment.',\n session_format: 'Remote video call',\n support_contact: 'ops@open-mercato.com',\n },\n },\n {\n templateName: 'Donation',\n name: 'Community Donation',\n title: 'Community donation',\n subtitle: 'Support the initiative with a custom amount',\n description: '## Support the mission\\n\\nA simple pay link for collecting donations without a long customer form.',\n slug: 'donate',\n customFields: {\n impact_summary: 'Your support keeps the community program accessible and free to attend.',\n donation_usage: 'Funds go directly to programming, speakers, and participant materials.',\n tax_receipt_note: 'A donation confirmation will be emailed right after payment.',\n support_contact: 'donations@open-mercato.com',\n },\n },\n {\n templateName: 'Event Ticket',\n name: 'Spring Gala 2026',\n title: 'Spring Gala 2026',\n subtitle: 'Choose a ticket tier and reserve your seat',\n description: '## Spring Gala 2026\\n\\nPick your ticket tier and complete the payment to confirm attendance.',\n slug: 'spring-gala-2026',\n customFields: {\n event_date: 'May 14, 2026 at 7:00 PM',\n event_location: 'Mercato Hall, 12 River Street, New York',\n ticket_includes: '- Gala entry\\n- Welcome drink\\n- Access to the networking lounge',\n support_contact: 'events@open-mercato.com',\n },\n },\n]\n\nfunction cloneCustomerFields() {\n return DEFAULT_CHECKOUT_CUSTOMER_FIELDS.map((field) => ({ ...field }))\n}\n\nfunction buildScopedSeedSlug(seedSlug: string, tenantId: string) {\n const suffix = tenantId.split('-')[0] || tenantId.slice(0, 8)\n return `${seedSlug}-${suffix}`\n}\n\nasync function ensureTemplate(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n seed: TemplateSeed,\n): Promise<{ template: CheckoutLinkTemplate; created: boolean }> {\n const existing = await em.findOne(CheckoutLinkTemplate, {\n name: seed.name,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existing) {\n return { template: existing, created: false }\n }\n\n const template = em.create(CheckoutLinkTemplate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: seed.name,\n title: seed.title,\n subtitle: seed.subtitle,\n description: seed.description,\n themeMode: 'auto',\n pricingMode: seed.pricingMode,\n fixedPriceAmount: toMoneyString(seed.fixedPriceAmount ?? null),\n fixedPriceCurrencyCode: seed.fixedPriceCurrencyCode ?? null,\n fixedPriceIncludesTax: true,\n fixedPriceOriginalAmount: null,\n customAmountMin: toMoneyString(seed.customAmountMin ?? null),\n customAmountMax: toMoneyString(seed.customAmountMax ?? null),\n customAmountCurrencyCode: seed.customAmountCurrencyCode ?? null,\n priceListItems: seed.priceListItems?.map((item) => ({ ...item })) ?? null,\n gatewayProviderKey: null,\n gatewaySettings: {},\n customFieldsetCode: seed.customFieldsetCode ?? null,\n collectCustomerDetails: seed.collectCustomerDetails ?? true,\n customerFieldsSchema: cloneCustomerFields(),\n legalDocuments: {},\n displayCustomFieldsOnPage: seed.displayCustomFieldsOnPage ?? false,\n successTitle: seed.successTitle ?? null,\n successMessage: seed.successMessage ?? null,\n cancelTitle: seed.cancelTitle ?? null,\n cancelMessage: seed.cancelMessage ?? null,\n errorTitle: seed.errorTitle ?? null,\n errorMessage: seed.errorMessage ?? null,\n startEmailSubject: seed.startEmailSubject ?? null,\n startEmailBody: seed.startEmailBody ?? null,\n successEmailSubject: seed.successEmailSubject ?? null,\n successEmailBody: seed.successEmailBody ?? null,\n errorEmailSubject: seed.errorEmailSubject ?? null,\n errorEmailBody: seed.errorEmailBody ?? null,\n sendStartEmail: true,\n sendSuccessEmail: true,\n sendErrorEmail: true,\n maxCompletions: seed.maxCompletions ?? null,\n status: 'draft',\n checkoutType: 'pay_link',\n })\n em.persist(template)\n return { template, created: true }\n}\n\nasync function ensureLink(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n template: CheckoutLinkTemplate,\n seed: LinkSeed,\n): Promise<{ link: CheckoutLink; created: boolean }> {\n const scopedSlug = buildScopedSeedSlug(seed.slug, scope.tenantId)\n\n const existingByScopedSlug = await em.findOne(CheckoutLink, {\n slug: scopedSlug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existingByScopedSlug) {\n return { link: existingByScopedSlug, created: false }\n }\n\n const existingByLegacySlug = await em.findOne(CheckoutLink, {\n slug: seed.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existingByLegacySlug) {\n return { link: existingByLegacySlug, created: false }\n }\n\n const existingByName = await em.findOne(CheckoutLink, {\n name: seed.name,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existingByName) {\n return { link: existingByName, created: false }\n }\n\n const uniqueSlug = await ensureUniqueSlug(em, scope, scopedSlug, `${seed.name}-${scope.tenantId.slice(0, 8)}`)\n\n const values = toTemplateOrLinkMutationInput(template, {\n name: seed.name,\n title: seed.title,\n subtitle: seed.subtitle,\n description: seed.description,\n slug: uniqueSlug,\n templateId: template.id,\n status: 'draft',\n })\n\n const link = em.create(CheckoutLink, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n completionCount: 0,\n activeReservationCount: 0,\n isLocked: false,\n slug: uniqueSlug,\n fixedPriceAmount: toMoneyString(values.fixedPriceAmount ?? null),\n fixedPriceOriginalAmount: toMoneyString(values.fixedPriceOriginalAmount ?? null),\n customAmountMin: toMoneyString(values.customAmountMin ?? null),\n customAmountMax: toMoneyString(values.customAmountMax ?? null),\n ...values,\n } as any)\n em.persist(link)\n return { link, created: true }\n}\n\nasync function assignCustomFields(args: {\n dataEngine: DataEngine\n entityId: string\n recordId: string\n tenantId: string\n organizationId: string\n values?: Record<string, unknown>\n}) {\n if (!args.values || Object.keys(args.values).length === 0) return\n try {\n await setCustomFieldsIfAny({\n dataEngine: args.dataEngine,\n entityId: args.entityId,\n recordId: args.recordId,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n values: args.values,\n })\n } catch (error) {\n console.warn('[checkout.seed] Failed to set example custom field values', error)\n }\n}\n\nexport async function seedCheckoutExamples({ em, container, tenantId, organizationId }: InitSetupContext) {\n const scope = { tenantId, organizationId }\n const dataEngine = container.resolve('dataEngine') as DataEngine\n await ensureCheckoutFieldsetsAndDefinitions(em, scope)\n const templateAssignments: Array<() => Promise<void>> = []\n const linkAssignments: Array<() => Promise<void>> = []\n const templatesByName = new Map<string, CheckoutLinkTemplate>()\n\n for (const seed of TEMPLATE_SEEDS) {\n const { template, created } = await ensureTemplate(em, scope, seed)\n templatesByName.set(seed.name, template)\n if (created) {\n templateAssignments.push(() => assignCustomFields({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId,\n organizationId,\n values: seed.customFields,\n }))\n }\n }\n\n await em.flush()\n\n for (const assign of templateAssignments) {\n await assign()\n }\n\n for (const seed of LINK_SEEDS) {\n const template = templatesByName.get(seed.templateName)\n if (!template) continue\n const { link, created } = await ensureLink(em, scope, template, seed)\n if (created) {\n linkAssignments.push(() => assignCustomFields({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId,\n organizationId,\n values: seed.customFields,\n }))\n }\n }\n\n await em.flush()\n\n for (const assign of linkAssignments) {\n await assign()\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { InitSetupContext } from '@open-mercato/shared/modules/setup'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { CheckoutLink, CheckoutLinkTemplate } from '../data/entities'\nimport { CHECKOUT_ENTITY_IDS } from '../lib/constants'\nimport { DEFAULT_CHECKOUT_CUSTOMER_FIELDS } from '../lib/defaults'\nimport { ensureUniqueSlug, toMoneyString, toTemplateOrLinkMutationInput } from '../lib/utils'\nimport { ensureCheckoutFieldsetsAndDefinitions } from './customFields'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout').child({ component: 'seed' })\n\ntype TemplateSeed = {\n name: string\n title: string\n subtitle: string\n description: string\n pricingMode: CheckoutLinkTemplate['pricingMode']\n fixedPriceAmount?: number | null\n fixedPriceCurrencyCode?: string | null\n customAmountMin?: number | null\n customAmountMax?: number | null\n customAmountCurrencyCode?: string | null\n priceListItems?: Array<{ id: string; description: string; amount: number; currencyCode: string }>\n maxCompletions?: number | null\n customFieldsetCode?: string | null\n displayCustomFieldsOnPage?: boolean\n successTitle?: string\n successMessage?: string\n cancelTitle?: string\n cancelMessage?: string\n errorTitle?: string\n errorMessage?: string\n startEmailSubject?: string\n startEmailBody?: string\n successEmailSubject?: string\n successEmailBody?: string\n errorEmailSubject?: string\n errorEmailBody?: string\n collectCustomerDetails?: boolean\n customFields?: Record<string, unknown>\n}\n\ntype LinkSeed = {\n templateName: string\n name: string\n title: string\n subtitle: string\n description: string\n slug: string\n customFields?: Record<string, unknown>\n}\n\nconst TEMPLATE_SEEDS: TemplateSeed[] = [\n {\n name: 'Consulting Fee',\n title: 'Consulting session payment',\n subtitle: 'One-hour consulting session',\n description: '## Included\\n\\n- 60 minute strategy call\\n- Summary and next steps\\n- Single-use payment request',\n pricingMode: 'fixed',\n fixedPriceAmount: 150,\n fixedPriceCurrencyCode: 'USD',\n maxCompletions: 1,\n customFieldsetCode: 'service_package',\n displayCustomFieldsOnPage: true,\n successTitle: 'Booking confirmed',\n successMessage: '## Payment received\\n\\nThank you for booking your consulting session. We will send the meeting invite and prep notes within one business day.',\n cancelTitle: 'Payment cancelled',\n cancelMessage: 'No worries. Your consulting slot has not been confirmed yet. You can return to this link when you are ready.',\n errorTitle: 'Payment needs another try',\n errorMessage: 'We could not finish the payment for your consulting session. Please retry or contact us if the issue continues.',\n startEmailSubject: 'We received your consulting payment request',\n startEmailBody: 'Hi {{firstName}},\\n\\nWe started processing your payment for **{{linkTitle}}** in the amount of **{{amount}} {{currencyCode}}**.',\n successEmailSubject: 'Your consulting session is confirmed',\n successEmailBody: 'Hi {{firstName}},\\n\\nYour payment for **{{linkTitle}}** was completed successfully.\\n\\nTransaction reference: `{{transactionId}}`.\\n\\nWe will follow up with scheduling details shortly.',\n errorEmailSubject: 'We could not complete your payment',\n errorEmailBody: 'Hi {{firstName}},\\n\\nYour payment for **{{linkTitle}}** could not be completed. {{errorMessage}}\\n\\nYou can retry using the same link.',\n collectCustomerDetails: true,\n customFields: {\n service_deliverables: '- 60 minute strategy call\\n- Written summary with next steps\\n- Follow-up recommendations by email',\n delivery_timeline: 'We send the meeting invite and prep notes within 1 business day after payment.',\n session_format: 'Remote video call',\n support_contact: 'ops@open-mercato.com',\n },\n },\n {\n name: 'Donation',\n title: 'Support our community work',\n subtitle: 'Choose any amount that feels right',\n description: '## Thank you\\n\\nYour contribution helps fund future programs, scholarships, and open events.',\n pricingMode: 'custom_amount',\n customAmountMin: 5,\n customAmountMax: 500,\n customAmountCurrencyCode: 'USD',\n maxCompletions: null,\n customFieldsetCode: 'donation_campaign',\n displayCustomFieldsOnPage: true,\n successTitle: 'Thank you for your support',\n successMessage: '## Donation received\\n\\nThank you for supporting our community work. Your contribution helps us fund new workshops, scholarships, and open events.',\n cancelTitle: 'Donation not completed',\n cancelMessage: 'Your donation was not completed. You can come back to this page any time and try again.',\n errorTitle: 'Donation payment failed',\n errorMessage: 'We could not complete the donation payment. Please retry in a moment or use another payment method if available.',\n startEmailSubject: 'Your donation payment is being processed',\n startEmailBody: 'Hi {{firstName}},\\n\\nWe started processing your donation for **{{linkTitle}}** in the amount of **{{amount}} {{currencyCode}}**.',\n successEmailSubject: 'Thank you for your donation',\n successEmailBody: 'Hi {{firstName}},\\n\\nYour donation for **{{linkTitle}}** was completed successfully.\\n\\nReference: `{{transactionId}}`.\\n\\nThank you for helping us continue this work.',\n errorEmailSubject: 'Your donation payment needs another try',\n errorEmailBody: 'Hi {{firstName}},\\n\\nWe could not complete your donation payment. {{errorMessage}}\\n\\nYou can use the same link to try again.',\n collectCustomerDetails: false,\n customFields: {\n impact_summary: 'Your donation helps fund free community workshops and scholarship places for new participants.',\n donation_usage: 'Funds support programming, venue costs, and materials.',\n tax_receipt_note: 'A donation confirmation email is sent after successful payment.',\n support_contact: 'donations@open-mercato.com',\n },\n },\n {\n name: 'Event Ticket',\n title: 'Spring Gala ticket',\n subtitle: 'Select the right ticket tier',\n description: '## Ticket tiers\\n\\nChoose between General, VIP, and Premium access for the gala event.',\n pricingMode: 'price_list',\n priceListItems: [\n { id: 'general', description: 'General admission', amount: 25, currencyCode: 'USD' },\n { id: 'vip', description: 'VIP ticket', amount: 75, currencyCode: 'USD' },\n { id: 'premium', description: 'Premium table seat', amount: 150, currencyCode: 'USD' },\n ],\n maxCompletions: 100,\n customFieldsetCode: 'event_ticket',\n displayCustomFieldsOnPage: true,\n successTitle: 'Your seat is reserved',\n successMessage: '## Ticket purchased\\n\\nThank you for purchasing your Spring Gala ticket. Please keep the confirmation email for check-in details.',\n cancelTitle: 'Ticket payment cancelled',\n cancelMessage: 'Your ticket was not reserved. You can return to this page to complete the purchase later.',\n errorTitle: 'Ticket payment failed',\n errorMessage: 'We could not complete the ticket payment. Please retry or contact the events team for assistance.',\n startEmailSubject: 'Your event ticket payment is being processed',\n startEmailBody: 'Hi {{firstName}},\\n\\nWe started processing your payment for **{{linkTitle}}** in the amount of **{{amount}} {{currencyCode}}**.',\n successEmailSubject: 'Your Spring Gala ticket is confirmed',\n successEmailBody: 'Hi {{firstName}},\\n\\nYour payment for **{{linkTitle}}** was successful.\\n\\nTicket reference: `{{transactionId}}`.\\n\\nWe look forward to seeing you at the event.',\n errorEmailSubject: 'We could not confirm your event ticket',\n errorEmailBody: 'Hi {{firstName}},\\n\\nWe could not complete your ticket payment. {{errorMessage}}\\n\\nPlease use the same link to try again.',\n collectCustomerDetails: true,\n customFields: {\n event_date: 'May 14, 2026 at 7:00 PM',\n event_location: 'Mercato Hall, 12 River Street, New York',\n ticket_includes: '- Entry to the gala\\n- Welcome drink\\n- Networking session',\n support_contact: 'events@open-mercato.com',\n },\n },\n]\n\nconst LINK_SEEDS: LinkSeed[] = [\n {\n templateName: 'Consulting Fee',\n name: 'January Consulting Session',\n title: 'January consulting session',\n subtitle: 'Invoice for a one-hour advisory meeting',\n description: '## January consulting session\\n\\nThis pay link covers the agreed one-hour consulting engagement.',\n slug: 'january-consulting',\n customFields: {\n service_deliverables: '- 60 minute planning call\\n- Action summary tailored to the January brief\\n- Follow-up notes by email',\n delivery_timeline: 'We send the calendar invite within 4 business hours after payment.',\n session_format: 'Remote video call',\n support_contact: 'ops@open-mercato.com',\n },\n },\n {\n templateName: 'Donation',\n name: 'Community Donation',\n title: 'Community donation',\n subtitle: 'Support the initiative with a custom amount',\n description: '## Support the mission\\n\\nA simple pay link for collecting donations without a long customer form.',\n slug: 'donate',\n customFields: {\n impact_summary: 'Your support keeps the community program accessible and free to attend.',\n donation_usage: 'Funds go directly to programming, speakers, and participant materials.',\n tax_receipt_note: 'A donation confirmation will be emailed right after payment.',\n support_contact: 'donations@open-mercato.com',\n },\n },\n {\n templateName: 'Event Ticket',\n name: 'Spring Gala 2026',\n title: 'Spring Gala 2026',\n subtitle: 'Choose a ticket tier and reserve your seat',\n description: '## Spring Gala 2026\\n\\nPick your ticket tier and complete the payment to confirm attendance.',\n slug: 'spring-gala-2026',\n customFields: {\n event_date: 'May 14, 2026 at 7:00 PM',\n event_location: 'Mercato Hall, 12 River Street, New York',\n ticket_includes: '- Gala entry\\n- Welcome drink\\n- Access to the networking lounge',\n support_contact: 'events@open-mercato.com',\n },\n },\n]\n\nfunction cloneCustomerFields() {\n return DEFAULT_CHECKOUT_CUSTOMER_FIELDS.map((field) => ({ ...field }))\n}\n\nfunction buildScopedSeedSlug(seedSlug: string, tenantId: string) {\n const suffix = tenantId.split('-')[0] || tenantId.slice(0, 8)\n return `${seedSlug}-${suffix}`\n}\n\nasync function ensureTemplate(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n seed: TemplateSeed,\n): Promise<{ template: CheckoutLinkTemplate; created: boolean }> {\n const existing = await em.findOne(CheckoutLinkTemplate, {\n name: seed.name,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existing) {\n return { template: existing, created: false }\n }\n\n const template = em.create(CheckoutLinkTemplate, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n name: seed.name,\n title: seed.title,\n subtitle: seed.subtitle,\n description: seed.description,\n themeMode: 'auto',\n pricingMode: seed.pricingMode,\n fixedPriceAmount: toMoneyString(seed.fixedPriceAmount ?? null),\n fixedPriceCurrencyCode: seed.fixedPriceCurrencyCode ?? null,\n fixedPriceIncludesTax: true,\n fixedPriceOriginalAmount: null,\n customAmountMin: toMoneyString(seed.customAmountMin ?? null),\n customAmountMax: toMoneyString(seed.customAmountMax ?? null),\n customAmountCurrencyCode: seed.customAmountCurrencyCode ?? null,\n priceListItems: seed.priceListItems?.map((item) => ({ ...item })) ?? null,\n gatewayProviderKey: null,\n gatewaySettings: {},\n customFieldsetCode: seed.customFieldsetCode ?? null,\n collectCustomerDetails: seed.collectCustomerDetails ?? true,\n customerFieldsSchema: cloneCustomerFields(),\n legalDocuments: {},\n displayCustomFieldsOnPage: seed.displayCustomFieldsOnPage ?? false,\n successTitle: seed.successTitle ?? null,\n successMessage: seed.successMessage ?? null,\n cancelTitle: seed.cancelTitle ?? null,\n cancelMessage: seed.cancelMessage ?? null,\n errorTitle: seed.errorTitle ?? null,\n errorMessage: seed.errorMessage ?? null,\n startEmailSubject: seed.startEmailSubject ?? null,\n startEmailBody: seed.startEmailBody ?? null,\n successEmailSubject: seed.successEmailSubject ?? null,\n successEmailBody: seed.successEmailBody ?? null,\n errorEmailSubject: seed.errorEmailSubject ?? null,\n errorEmailBody: seed.errorEmailBody ?? null,\n sendStartEmail: true,\n sendSuccessEmail: true,\n sendErrorEmail: true,\n maxCompletions: seed.maxCompletions ?? null,\n status: 'draft',\n checkoutType: 'pay_link',\n })\n em.persist(template)\n return { template, created: true }\n}\n\nasync function ensureLink(\n em: EntityManager,\n scope: { tenantId: string; organizationId: string },\n template: CheckoutLinkTemplate,\n seed: LinkSeed,\n): Promise<{ link: CheckoutLink; created: boolean }> {\n const scopedSlug = buildScopedSeedSlug(seed.slug, scope.tenantId)\n\n const existingByScopedSlug = await em.findOne(CheckoutLink, {\n slug: scopedSlug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existingByScopedSlug) {\n return { link: existingByScopedSlug, created: false }\n }\n\n const existingByLegacySlug = await em.findOne(CheckoutLink, {\n slug: seed.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existingByLegacySlug) {\n return { link: existingByLegacySlug, created: false }\n }\n\n const existingByName = await em.findOne(CheckoutLink, {\n name: seed.name,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n deletedAt: null,\n })\n if (existingByName) {\n return { link: existingByName, created: false }\n }\n\n const uniqueSlug = await ensureUniqueSlug(em, scope, scopedSlug, `${seed.name}-${scope.tenantId.slice(0, 8)}`)\n\n const values = toTemplateOrLinkMutationInput(template, {\n name: seed.name,\n title: seed.title,\n subtitle: seed.subtitle,\n description: seed.description,\n slug: uniqueSlug,\n templateId: template.id,\n status: 'draft',\n })\n\n const link = em.create(CheckoutLink, {\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n completionCount: 0,\n activeReservationCount: 0,\n isLocked: false,\n slug: uniqueSlug,\n fixedPriceAmount: toMoneyString(values.fixedPriceAmount ?? null),\n fixedPriceOriginalAmount: toMoneyString(values.fixedPriceOriginalAmount ?? null),\n customAmountMin: toMoneyString(values.customAmountMin ?? null),\n customAmountMax: toMoneyString(values.customAmountMax ?? null),\n ...values,\n } as any)\n em.persist(link)\n return { link, created: true }\n}\n\nasync function assignCustomFields(args: {\n dataEngine: DataEngine\n entityId: string\n recordId: string\n tenantId: string\n organizationId: string\n values?: Record<string, unknown>\n}) {\n if (!args.values || Object.keys(args.values).length === 0) return\n try {\n await setCustomFieldsIfAny({\n dataEngine: args.dataEngine,\n entityId: args.entityId,\n recordId: args.recordId,\n tenantId: args.tenantId,\n organizationId: args.organizationId,\n values: args.values,\n })\n } catch (error) {\n logger.warn('Failed to set example custom field values', { err: error })\n }\n}\n\nexport async function seedCheckoutExamples({ em, container, tenantId, organizationId }: InitSetupContext) {\n const scope = { tenantId, organizationId }\n const dataEngine = container.resolve('dataEngine') as DataEngine\n await ensureCheckoutFieldsetsAndDefinitions(em, scope)\n const templateAssignments: Array<() => Promise<void>> = []\n const linkAssignments: Array<() => Promise<void>> = []\n const templatesByName = new Map<string, CheckoutLinkTemplate>()\n\n for (const seed of TEMPLATE_SEEDS) {\n const { template, created } = await ensureTemplate(em, scope, seed)\n templatesByName.set(seed.name, template)\n if (created) {\n templateAssignments.push(() => assignCustomFields({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId,\n organizationId,\n values: seed.customFields,\n }))\n }\n }\n\n await em.flush()\n\n for (const assign of templateAssignments) {\n await assign()\n }\n\n for (const seed of LINK_SEEDS) {\n const template = templatesByName.get(seed.templateName)\n if (!template) continue\n const { link, created } = await ensureLink(em, scope, template, seed)\n if (created) {\n linkAssignments.push(() => assignCustomFields({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId,\n organizationId,\n values: seed.customFields,\n }))\n }\n }\n\n await em.flush()\n\n for (const assign of linkAssignments) {\n await assign()\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,6 @@
1
1
  import { dispatchCheckoutEmailJob } from "../lib/emailQueue.js";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ const logger = createLogger("checkout").child({ component: "session-started-email" });
2
4
  const metadata = {
3
5
  event: "checkout.transaction.sessionStarted",
4
6
  persistent: true,
@@ -14,7 +16,7 @@ async function handle(payload) {
14
16
  organizationId: payload.organizationId
15
17
  });
16
18
  } catch (err) {
17
- console.error("[checkout:session-started-email] email enqueue failed:", err);
19
+ logger.error("email enqueue failed", { transactionId: payload.transactionId, err });
18
20
  }
19
21
  }
20
22
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/subscribers/session-started-email.ts"],
4
- "sourcesContent": ["import { dispatchCheckoutEmailJob } from '../lib/emailQueue'\n\nexport const metadata = {\n event: 'checkout.transaction.sessionStarted',\n persistent: true,\n id: 'checkout:session-started-email',\n}\n\ntype SessionStartedPayload = {\n transactionId: string\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: SessionStartedPayload) {\n if (!payload.transactionId || !payload.tenantId || !payload.organizationId) return\n\n try {\n await dispatchCheckoutEmailJob({\n type: 'start',\n transactionId: payload.transactionId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n console.error('[checkout:session-started-email] email enqueue failed:', err)\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,gCAAgC;AAElC,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAQA,eAAO,OAA8B,SAAgC;AACnE,MAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAE5E,MAAI;AACF,UAAM,yBAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,0DAA0D,GAAG;AAAA,EAC7E;AACF;",
4
+ "sourcesContent": ["import { dispatchCheckoutEmailJob } from '../lib/emailQueue'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout').child({ component: 'session-started-email' })\n\nexport const metadata = {\n event: 'checkout.transaction.sessionStarted',\n persistent: true,\n id: 'checkout:session-started-email',\n}\n\ntype SessionStartedPayload = {\n transactionId: string\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: SessionStartedPayload) {\n if (!payload.transactionId || !payload.tenantId || !payload.organizationId) return\n\n try {\n await dispatchCheckoutEmailJob({\n type: 'start',\n transactionId: payload.transactionId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n logger.error('email enqueue failed', { transactionId: payload.transactionId, err })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,wBAAwB,CAAC;AAE7E,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAQA,eAAO,OAA8B,SAAgC;AACnE,MAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAE5E,MAAI;AACF,UAAM,yBAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,wBAAwB,EAAE,eAAe,QAAQ,eAAe,IAAI,CAAC;AAAA,EACpF;AACF;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,8 @@ import { buildFeatureNotificationFromType } from "@open-mercato/core/modules/not
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { notificationTypes } from "../notifications.js";
5
5
  import { dispatchCheckoutEmailJob } from "../lib/emailQueue.js";
6
+ import { createLogger } from "@open-mercato/shared/lib/logger";
7
+ const logger = createLogger("checkout").child({ component: "transaction-completed-notify" });
6
8
  const metadata = {
7
9
  event: "checkout.transaction.completed",
8
10
  persistent: true,
@@ -31,7 +33,7 @@ async function handle(payload) {
31
33
  });
32
34
  }
33
35
  } catch (err) {
34
- console.error("[checkout:transaction-completed-notify] notification failed:", err);
36
+ logger.error("notification failed", { transactionId: payload.transactionId, err });
35
37
  }
36
38
  try {
37
39
  await dispatchCheckoutEmailJob({
@@ -41,7 +43,7 @@ async function handle(payload) {
41
43
  organizationId: payload.organizationId
42
44
  });
43
45
  } catch (err) {
44
- console.error("[checkout:transaction-completed-notify] email enqueue failed:", err);
46
+ logger.error("email enqueue failed", { transactionId: payload.transactionId, err });
45
47
  }
46
48
  }
47
49
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/subscribers/transaction-completed-notify.ts"],
4
- "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { notificationTypes } from '../notifications'\nimport { dispatchCheckoutEmailJob } from '../lib/emailQueue'\n\nexport const metadata = {\n event: 'checkout.transaction.completed',\n persistent: true,\n id: 'checkout:transaction-completed-notify',\n}\n\ntype CompletedPayload = {\n transactionId: string\n linkId: string\n slug?: string | null\n status: string\n amount?: number | null\n currency?: string | null\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: CompletedPayload) {\n if (!payload.transactionId || !payload.tenantId || !payload.organizationId) return\n\n try {\n const container = await createRequestContainer()\n const notificationService = resolveNotificationService(container)\n const typeDef = notificationTypes.find((n) => n.type === 'checkout.transaction.completed')\n\n if (typeDef) {\n const input = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: 'checkout.view',\n bodyVariables: {\n amount: payload.amount != null ? String(payload.amount) : '',\n currency: payload.currency ?? '',\n },\n sourceEntityType: 'checkout:checkout_transaction',\n sourceEntityId: payload.transactionId,\n linkHref: `/backend/checkout/transactions/${payload.transactionId}`,\n })\n await notificationService.createForFeature(input, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n }\n } catch (err) {\n console.error('[checkout:transaction-completed-notify] notification failed:', err)\n }\n\n try {\n await dispatchCheckoutEmailJob({\n type: 'success',\n transactionId: payload.transactionId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n console.error('[checkout:transaction-completed-notify] email enqueue failed:', err)\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAClC,SAAS,gCAAgC;AAElC,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAaA,eAAO,OAA8B,SAA2B;AAC9D,MAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAE5E,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,sBAAsB,2BAA2B,SAAS;AAChE,UAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,gCAAgC;AAEzF,QAAI,SAAS;AACX,YAAM,QAAQ,iCAAiC,SAAS;AAAA,QACtD,iBAAiB;AAAA,QACjB,eAAe;AAAA,UACb,QAAQ,QAAQ,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AAAA,UAC1D,UAAU,QAAQ,YAAY;AAAA,QAChC;AAAA,QACA,kBAAkB;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,UAAU,kCAAkC,QAAQ,aAAa;AAAA,MACnE,CAAC;AACD,YAAM,oBAAoB,iBAAiB,OAAO;AAAA,QAChD,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,gEAAgE,GAAG;AAAA,EACnF;AAEA,MAAI;AACF,UAAM,yBAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,iEAAiE,GAAG;AAAA,EACpF;AACF;",
4
+ "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { notificationTypes } from '../notifications'\nimport { dispatchCheckoutEmailJob } from '../lib/emailQueue'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout').child({ component: 'transaction-completed-notify' })\n\nexport const metadata = {\n event: 'checkout.transaction.completed',\n persistent: true,\n id: 'checkout:transaction-completed-notify',\n}\n\ntype CompletedPayload = {\n transactionId: string\n linkId: string\n slug?: string | null\n status: string\n amount?: number | null\n currency?: string | null\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: CompletedPayload) {\n if (!payload.transactionId || !payload.tenantId || !payload.organizationId) return\n\n try {\n const container = await createRequestContainer()\n const notificationService = resolveNotificationService(container)\n const typeDef = notificationTypes.find((n) => n.type === 'checkout.transaction.completed')\n\n if (typeDef) {\n const input = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: 'checkout.view',\n bodyVariables: {\n amount: payload.amount != null ? String(payload.amount) : '',\n currency: payload.currency ?? '',\n },\n sourceEntityType: 'checkout:checkout_transaction',\n sourceEntityId: payload.transactionId,\n linkHref: `/backend/checkout/transactions/${payload.transactionId}`,\n })\n await notificationService.createForFeature(input, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n }\n } catch (err) {\n logger.error('notification failed', { transactionId: payload.transactionId, err })\n }\n\n try {\n await dispatchCheckoutEmailJob({\n type: 'success',\n transactionId: payload.transactionId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n logger.error('email enqueue failed', { transactionId: payload.transactionId, err })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAClC,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,+BAA+B,CAAC;AAEpF,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAaA,eAAO,OAA8B,SAA2B;AAC9D,MAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAE5E,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,sBAAsB,2BAA2B,SAAS;AAChE,UAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,gCAAgC;AAEzF,QAAI,SAAS;AACX,YAAM,QAAQ,iCAAiC,SAAS;AAAA,QACtD,iBAAiB;AAAA,QACjB,eAAe;AAAA,UACb,QAAQ,QAAQ,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AAAA,UAC1D,UAAU,QAAQ,YAAY;AAAA,QAChC;AAAA,QACA,kBAAkB;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,UAAU,kCAAkC,QAAQ,aAAa;AAAA,MACnE,CAAC;AACD,YAAM,oBAAoB,iBAAiB,OAAO;AAAA,QAChD,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,uBAAuB,EAAE,eAAe,QAAQ,eAAe,IAAI,CAAC;AAAA,EACnF;AAEA,MAAI;AACF,UAAM,yBAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,wBAAwB,EAAE,eAAe,QAAQ,eAAe,IAAI,CAAC;AAAA,EACpF;AACF;",
6
6
  "names": []
7
7
  }
@@ -3,6 +3,8 @@ import { buildFeatureNotificationFromType } from "@open-mercato/core/modules/not
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { notificationTypes } from "../notifications.js";
5
5
  import { dispatchCheckoutEmailJob } from "../lib/emailQueue.js";
6
+ import { createLogger } from "@open-mercato/shared/lib/logger";
7
+ const logger = createLogger("checkout").child({ component: "transaction-failed-notify" });
6
8
  const metadata = {
7
9
  event: "checkout.transaction.failed",
8
10
  persistent: true,
@@ -31,7 +33,7 @@ async function handle(payload) {
31
33
  });
32
34
  }
33
35
  } catch (err) {
34
- console.error("[checkout:transaction-failed-notify] notification failed:", err);
36
+ logger.error("notification failed", { transactionId: payload.transactionId, err });
35
37
  }
36
38
  try {
37
39
  await dispatchCheckoutEmailJob({
@@ -41,7 +43,7 @@ async function handle(payload) {
41
43
  organizationId: payload.organizationId
42
44
  });
43
45
  } catch (err) {
44
- console.error("[checkout:transaction-failed-notify] email enqueue failed:", err);
46
+ logger.error("email enqueue failed", { transactionId: payload.transactionId, err });
45
47
  }
46
48
  }
47
49
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/subscribers/transaction-failed-notify.ts"],
4
- "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { notificationTypes } from '../notifications'\nimport { dispatchCheckoutEmailJob } from '../lib/emailQueue'\n\nexport const metadata = {\n event: 'checkout.transaction.failed',\n persistent: true,\n id: 'checkout:transaction-failed-notify',\n}\n\ntype FailedPayload = {\n transactionId: string\n linkId: string\n slug?: string | null\n status: string\n amount?: number | null\n currency?: string | null\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: FailedPayload) {\n if (!payload.transactionId || !payload.tenantId || !payload.organizationId) return\n\n try {\n const container = await createRequestContainer()\n const notificationService = resolveNotificationService(container)\n const typeDef = notificationTypes.find((n) => n.type === 'checkout.transaction.failed')\n\n if (typeDef) {\n const input = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: 'checkout.view',\n bodyVariables: {\n amount: payload.amount != null ? String(payload.amount) : '',\n currency: payload.currency ?? '',\n },\n sourceEntityType: 'checkout:checkout_transaction',\n sourceEntityId: payload.transactionId,\n linkHref: `/backend/checkout/transactions/${payload.transactionId}`,\n })\n await notificationService.createForFeature(input, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n }\n } catch (err) {\n console.error('[checkout:transaction-failed-notify] notification failed:', err)\n }\n\n try {\n await dispatchCheckoutEmailJob({\n type: 'error',\n transactionId: payload.transactionId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n console.error('[checkout:transaction-failed-notify] email enqueue failed:', err)\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAClC,SAAS,gCAAgC;AAElC,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAaA,eAAO,OAA8B,SAAwB;AAC3D,MAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAE5E,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,sBAAsB,2BAA2B,SAAS;AAChE,UAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,6BAA6B;AAEtF,QAAI,SAAS;AACX,YAAM,QAAQ,iCAAiC,SAAS;AAAA,QACtD,iBAAiB;AAAA,QACjB,eAAe;AAAA,UACb,QAAQ,QAAQ,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AAAA,UAC1D,UAAU,QAAQ,YAAY;AAAA,QAChC;AAAA,QACA,kBAAkB;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,UAAU,kCAAkC,QAAQ,aAAa;AAAA,MACnE,CAAC;AACD,YAAM,oBAAoB,iBAAiB,OAAO;AAAA,QAChD,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,YAAQ,MAAM,6DAA6D,GAAG;AAAA,EAChF;AAEA,MAAI;AACF,UAAM,yBAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,8DAA8D,GAAG;AAAA,EACjF;AACF;",
4
+ "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { notificationTypes } from '../notifications'\nimport { dispatchCheckoutEmailJob } from '../lib/emailQueue'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout').child({ component: 'transaction-failed-notify' })\n\nexport const metadata = {\n event: 'checkout.transaction.failed',\n persistent: true,\n id: 'checkout:transaction-failed-notify',\n}\n\ntype FailedPayload = {\n transactionId: string\n linkId: string\n slug?: string | null\n status: string\n amount?: number | null\n currency?: string | null\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: FailedPayload) {\n if (!payload.transactionId || !payload.tenantId || !payload.organizationId) return\n\n try {\n const container = await createRequestContainer()\n const notificationService = resolveNotificationService(container)\n const typeDef = notificationTypes.find((n) => n.type === 'checkout.transaction.failed')\n\n if (typeDef) {\n const input = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: 'checkout.view',\n bodyVariables: {\n amount: payload.amount != null ? String(payload.amount) : '',\n currency: payload.currency ?? '',\n },\n sourceEntityType: 'checkout:checkout_transaction',\n sourceEntityId: payload.transactionId,\n linkHref: `/backend/checkout/transactions/${payload.transactionId}`,\n })\n await notificationService.createForFeature(input, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n }\n } catch (err) {\n logger.error('notification failed', { transactionId: payload.transactionId, err })\n }\n\n try {\n await dispatchCheckoutEmailJob({\n type: 'error',\n transactionId: payload.transactionId,\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n logger.error('email enqueue failed', { transactionId: payload.transactionId, err })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAClC,SAAS,gCAAgC;AACzC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,4BAA4B,CAAC;AAEjF,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AAaA,eAAO,OAA8B,SAAwB;AAC3D,MAAI,CAAC,QAAQ,iBAAiB,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAE5E,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,sBAAsB,2BAA2B,SAAS;AAChE,UAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,6BAA6B;AAEtF,QAAI,SAAS;AACX,YAAM,QAAQ,iCAAiC,SAAS;AAAA,QACtD,iBAAiB;AAAA,QACjB,eAAe;AAAA,UACb,QAAQ,QAAQ,UAAU,OAAO,OAAO,QAAQ,MAAM,IAAI;AAAA,UAC1D,UAAU,QAAQ,YAAY;AAAA,QAChC;AAAA,QACA,kBAAkB;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,UAAU,kCAAkC,QAAQ,aAAa;AAAA,MACnE,CAAC;AACD,YAAM,oBAAoB,iBAAiB,OAAO;AAAA,QAChD,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH;AAAA,EACF,SAAS,KAAK;AACZ,WAAO,MAAM,uBAAuB,EAAE,eAAe,QAAQ,eAAe,IAAI,CAAC;AAAA,EACnF;AAEA,MAAI;AACF,UAAM,yBAAyB;AAAA,MAC7B,MAAM;AAAA,MACN,eAAe,QAAQ;AAAA,MACvB,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,wBAAwB,EAAE,eAAe,QAAQ,eAAe,IAAI,CAAC;AAAA,EACpF;AACF;",
6
6
  "names": []
7
7
  }
@@ -2,6 +2,8 @@ import { resolveNotificationService } from "@open-mercato/core/modules/notificat
2
2
  import { buildFeatureNotificationFromType } from "@open-mercato/core/modules/notifications/lib/notificationBuilder";
3
3
  import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
4
  import { notificationTypes } from "../notifications.js";
5
+ import { createLogger } from "@open-mercato/shared/lib/logger";
6
+ const logger = createLogger("checkout").child({ component: "usage-limit-reached-notify" });
5
7
  const metadata = {
6
8
  event: "checkout.link.usageLimitReached",
7
9
  persistent: true,
@@ -28,7 +30,7 @@ async function handle(payload) {
28
30
  organizationId: payload.organizationId
29
31
  });
30
32
  } catch (err) {
31
- console.error("[checkout:usage-limit-reached-notify] notification failed:", err);
33
+ logger.error("notification failed", { err });
32
34
  }
33
35
  }
34
36
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/subscribers/usage-limit-reached-notify.ts"],
4
- "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { notificationTypes } from '../notifications'\n\nexport const metadata = {\n event: 'checkout.link.usageLimitReached',\n persistent: true,\n id: 'checkout:usage-limit-reached-notify',\n}\n\ntype UsageLimitPayload = {\n id: string\n slug?: string | null\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: UsageLimitPayload) {\n if (!payload.id || !payload.tenantId || !payload.organizationId) return\n\n try {\n const container = await createRequestContainer()\n const notificationService = resolveNotificationService(container)\n const typeDef = notificationTypes.find((n) => n.type === 'checkout.link.usageLimitReached')\n if (!typeDef) return\n\n const input = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: 'checkout.edit',\n bodyVariables: {\n linkName: payload.slug ?? payload.id,\n },\n sourceEntityType: 'checkout:checkout_link',\n sourceEntityId: payload.id,\n linkHref: `/backend/checkout/pay-links/${payload.id}`,\n })\n await notificationService.createForFeature(input, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n console.error('[checkout:usage-limit-reached-notify] notification failed:', err)\n }\n}\n"],
5
- "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAE3B,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AASA,eAAO,OAA8B,SAA4B;AAC/D,MAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAEjE,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,sBAAsB,2BAA2B,SAAS;AAChE,UAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,iCAAiC;AAC1F,QAAI,CAAC,QAAS;AAEd,UAAM,QAAQ,iCAAiC,SAAS;AAAA,MACtD,iBAAiB;AAAA,MACjB,eAAe;AAAA,QACb,UAAU,QAAQ,QAAQ,QAAQ;AAAA,MACpC;AAAA,MACA,kBAAkB;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,UAAU,+BAA+B,QAAQ,EAAE;AAAA,IACrD,CAAC;AACD,UAAM,oBAAoB,iBAAiB,OAAO;AAAA,MAChD,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,YAAQ,MAAM,8DAA8D,GAAG;AAAA,EACjF;AACF;",
4
+ "sourcesContent": ["import { resolveNotificationService } from '@open-mercato/core/modules/notifications/lib/notificationService'\nimport { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { notificationTypes } from '../notifications'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout').child({ component: 'usage-limit-reached-notify' })\n\nexport const metadata = {\n event: 'checkout.link.usageLimitReached',\n persistent: true,\n id: 'checkout:usage-limit-reached-notify',\n}\n\ntype UsageLimitPayload = {\n id: string\n slug?: string | null\n tenantId: string\n organizationId: string\n}\n\nexport default async function handle(payload: UsageLimitPayload) {\n if (!payload.id || !payload.tenantId || !payload.organizationId) return\n\n try {\n const container = await createRequestContainer()\n const notificationService = resolveNotificationService(container)\n const typeDef = notificationTypes.find((n) => n.type === 'checkout.link.usageLimitReached')\n if (!typeDef) return\n\n const input = buildFeatureNotificationFromType(typeDef, {\n requiredFeature: 'checkout.edit',\n bodyVariables: {\n linkName: payload.slug ?? payload.id,\n },\n sourceEntityType: 'checkout:checkout_link',\n sourceEntityId: payload.id,\n linkHref: `/backend/checkout/pay-links/${payload.id}`,\n })\n await notificationService.createForFeature(input, {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n })\n } catch (err) {\n logger.error('notification failed', { err })\n }\n}\n"],
5
+ "mappings": "AAAA,SAAS,kCAAkC;AAC3C,SAAS,wCAAwC;AACjD,SAAS,8BAA8B;AACvC,SAAS,yBAAyB;AAClC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,6BAA6B,CAAC;AAElF,MAAM,WAAW;AAAA,EACtB,OAAO;AAAA,EACP,YAAY;AAAA,EACZ,IAAI;AACN;AASA,eAAO,OAA8B,SAA4B;AAC/D,MAAI,CAAC,QAAQ,MAAM,CAAC,QAAQ,YAAY,CAAC,QAAQ,eAAgB;AAEjE,MAAI;AACF,UAAM,YAAY,MAAM,uBAAuB;AAC/C,UAAM,sBAAsB,2BAA2B,SAAS;AAChE,UAAM,UAAU,kBAAkB,KAAK,CAAC,MAAM,EAAE,SAAS,iCAAiC;AAC1F,QAAI,CAAC,QAAS;AAEd,UAAM,QAAQ,iCAAiC,SAAS;AAAA,MACtD,iBAAiB;AAAA,MACjB,eAAe;AAAA,QACb,UAAU,QAAQ,QAAQ,QAAQ;AAAA,MACpC;AAAA,MACA,kBAAkB;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,UAAU,+BAA+B,QAAQ,EAAE;AAAA,IACrD,CAAC;AACD,UAAM,oBAAoB,iBAAiB,OAAO;AAAA,MAChD,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,WAAO,MAAM,uBAAuB,EAAE,IAAI,CAAC;AAAA,EAC7C;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,7 @@
1
1
  import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
2
2
  import { CheckoutTransaction } from "../data/entities.js";
3
+ import { createLogger } from "@open-mercato/shared/lib/logger";
4
+ const logger = createLogger("checkout").child({ component: "transaction-expiry" });
3
5
  const CHECKOUT_EXPIRY_QUEUE = "checkout-transaction-expiry";
4
6
  const EXPIRY_TIMEOUT_MS = 2 * 60 * 60 * 1e3;
5
7
  const metadata = {
@@ -41,7 +43,7 @@ async function handle(job, ctx) {
41
43
  ctx: commandCtx
42
44
  });
43
45
  } catch (error) {
44
- console.error("[checkout:transaction-expiry] Failed to expire transaction", transaction.id, error);
46
+ logger.error("Failed to expire transaction", { transactionId: transaction.id, err: error });
45
47
  }
46
48
  }
47
49
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/workers/transaction-expiry.worker.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands/types'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CheckoutTransaction } from '../data/entities'\n\nexport const CHECKOUT_EXPIRY_QUEUE = 'checkout-transaction-expiry'\n\nconst EXPIRY_TIMEOUT_MS = 2 * 60 * 60 * 1000 // 2 hours\n\nexport type CheckoutExpiryJob = {\n batchSize?: number\n}\n\nexport const metadata: WorkerMeta = {\n queue: CHECKOUT_EXPIRY_QUEUE,\n id: 'checkout:transaction-expiry',\n concurrency: 1,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport default async function handle(job: QueuedJob<CheckoutExpiryJob>, ctx: HandlerContext): Promise<void> {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const commandBus = ctx.resolve('commandBus') as CommandBus\n const batchSize = job.payload?.batchSize ?? 100\n const cutoff = new Date(Date.now() - EXPIRY_TIMEOUT_MS)\n\n const staleTransactions = await findWithDecryption(\n em,\n CheckoutTransaction,\n {\n status: 'processing',\n createdAt: { $lt: cutoff },\n },\n { limit: batchSize, orderBy: { createdAt: 'ASC' } },\n )\n\n for (const transaction of staleTransactions) {\n try {\n const commandCtx: CommandRuntimeContext = {\n container: { resolve: ctx.resolve } as unknown as CommandRuntimeContext['container'],\n auth: null,\n organizationScope: null,\n selectedOrganizationId: transaction.organizationId,\n organizationIds: [transaction.organizationId],\n }\n await commandBus.execute('checkout.transaction.updateStatus', {\n input: {\n id: transaction.id,\n status: 'expired',\n paymentStatus: 'expired',\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n },\n ctx: commandCtx,\n })\n } catch (error) {\n console.error('[checkout:transaction-expiry] Failed to expire transaction', transaction.id, error)\n }\n }\n}\n"],
5
- "mappings": "AAIA,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AAE7B,MAAM,wBAAwB;AAErC,MAAM,oBAAoB,IAAI,KAAK,KAAK;AAMjC,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAMA,eAAO,OAA8B,KAAmC,KAAoC;AAC1G,QAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,QAAM,aAAa,IAAI,QAAQ,YAAY;AAC3C,QAAM,YAAY,IAAI,SAAS,aAAa;AAC5C,QAAM,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,iBAAiB;AAEtD,QAAM,oBAAoB,MAAM;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,MACR,WAAW,EAAE,KAAK,OAAO;AAAA,IAC3B;AAAA,IACA,EAAE,OAAO,WAAW,SAAS,EAAE,WAAW,MAAM,EAAE;AAAA,EACpD;AAEA,aAAW,eAAe,mBAAmB;AAC3C,QAAI;AACF,YAAM,aAAoC;AAAA,QACxC,WAAW,EAAE,SAAS,IAAI,QAAQ;AAAA,QAClC,MAAM;AAAA,QACN,mBAAmB;AAAA,QACnB,wBAAwB,YAAY;AAAA,QACpC,iBAAiB,CAAC,YAAY,cAAc;AAAA,MAC9C;AACA,YAAM,WAAW,QAAQ,qCAAqC;AAAA,QAC5D,OAAO;AAAA,UACL,IAAI,YAAY;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,IACH,SAAS,OAAO;AACd,cAAQ,MAAM,8DAA8D,YAAY,IAAI,KAAK;AAAA,IACnG;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { JobContext, QueuedJob, WorkerMeta } from '@open-mercato/queue'\nimport type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'\nimport type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands/types'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { CheckoutTransaction } from '../data/entities'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('checkout').child({ component: 'transaction-expiry' })\n\nexport const CHECKOUT_EXPIRY_QUEUE = 'checkout-transaction-expiry'\n\nconst EXPIRY_TIMEOUT_MS = 2 * 60 * 60 * 1000 // 2 hours\n\nexport type CheckoutExpiryJob = {\n batchSize?: number\n}\n\nexport const metadata: WorkerMeta = {\n queue: CHECKOUT_EXPIRY_QUEUE,\n id: 'checkout:transaction-expiry',\n concurrency: 1,\n}\n\ntype HandlerContext = JobContext & {\n resolve: <T = unknown>(name: string) => T\n}\n\nexport default async function handle(job: QueuedJob<CheckoutExpiryJob>, ctx: HandlerContext): Promise<void> {\n const em = (ctx.resolve('em') as EntityManager).fork()\n const commandBus = ctx.resolve('commandBus') as CommandBus\n const batchSize = job.payload?.batchSize ?? 100\n const cutoff = new Date(Date.now() - EXPIRY_TIMEOUT_MS)\n\n const staleTransactions = await findWithDecryption(\n em,\n CheckoutTransaction,\n {\n status: 'processing',\n createdAt: { $lt: cutoff },\n },\n { limit: batchSize, orderBy: { createdAt: 'ASC' } },\n )\n\n for (const transaction of staleTransactions) {\n try {\n const commandCtx: CommandRuntimeContext = {\n container: { resolve: ctx.resolve } as unknown as CommandRuntimeContext['container'],\n auth: null,\n organizationScope: null,\n selectedOrganizationId: transaction.organizationId,\n organizationIds: [transaction.organizationId],\n }\n await commandBus.execute('checkout.transaction.updateStatus', {\n input: {\n id: transaction.id,\n status: 'expired',\n paymentStatus: 'expired',\n organizationId: transaction.organizationId,\n tenantId: transaction.tenantId,\n },\n ctx: commandCtx,\n })\n } catch (error) {\n logger.error('Failed to expire transaction', { transactionId: transaction.id, err: error })\n }\n }\n}\n"],
5
+ "mappings": "AAIA,SAAS,0BAA0B;AACnC,SAAS,2BAA2B;AACpC,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,UAAU,EAAE,MAAM,EAAE,WAAW,qBAAqB,CAAC;AAE1E,MAAM,wBAAwB;AAErC,MAAM,oBAAoB,IAAI,KAAK,KAAK;AAMjC,MAAM,WAAuB;AAAA,EAClC,OAAO;AAAA,EACP,IAAI;AAAA,EACJ,aAAa;AACf;AAMA,eAAO,OAA8B,KAAmC,KAAoC;AAC1G,QAAM,KAAM,IAAI,QAAQ,IAAI,EAAoB,KAAK;AACrD,QAAM,aAAa,IAAI,QAAQ,YAAY;AAC3C,QAAM,YAAY,IAAI,SAAS,aAAa;AAC5C,QAAM,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,iBAAiB;AAEtD,QAAM,oBAAoB,MAAM;AAAA,IAC9B;AAAA,IACA;AAAA,IACA;AAAA,MACE,QAAQ;AAAA,MACR,WAAW,EAAE,KAAK,OAAO;AAAA,IAC3B;AAAA,IACA,EAAE,OAAO,WAAW,SAAS,EAAE,WAAW,MAAM,EAAE;AAAA,EACpD;AAEA,aAAW,eAAe,mBAAmB;AAC3C,QAAI;AACF,YAAM,aAAoC;AAAA,QACxC,WAAW,EAAE,SAAS,IAAI,QAAQ;AAAA,QAClC,MAAM;AAAA,QACN,mBAAmB;AAAA,QACnB,wBAAwB,YAAY;AAAA,QACpC,iBAAiB,CAAC,YAAY,cAAc;AAAA,MAC9C;AACA,YAAM,WAAW,QAAQ,qCAAqC;AAAA,QAC5D,OAAO;AAAA,UACL,IAAI,YAAY;AAAA,UAChB,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,gBAAgB,YAAY;AAAA,UAC5B,UAAU,YAAY;AAAA,QACxB;AAAA,QACA,KAAK;AAAA,MACP,CAAC;AAAA,IACH,SAAS,OAAO;AACd,aAAO,MAAM,gCAAgC,EAAE,eAAe,YAAY,IAAI,KAAK,MAAM,CAAC;AAAA,IAC5F;AAAA,EACF;AACF;",
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.6465.1.019f0fb26f",
3
+ "version": "0.6.6-develop.6471.1.9ab5bdd79a",
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.6465.1.019f0fb26f",
66
- "@open-mercato/ui": "0.6.6-develop.6465.1.019f0fb26f",
65
+ "@open-mercato/core": "0.6.6-develop.6471.1.9ab5bdd79a",
66
+ "@open-mercato/ui": "0.6.6-develop.6471.1.9ab5bdd79a",
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.6465.1.019f0fb26f",
71
+ "@open-mercato/shared": "0.6.6-develop.6471.1.9ab5bdd79a",
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.6465.1.019f0fb26f",
76
+ "@open-mercato/shared": "0.6.6-develop.6471.1.9ab5bdd79a",
77
77
  "@types/jest": "^30.0.0",
78
78
  "@types/react": "^19.2.17",
79
79
  "@types/react-dom": "^19.2.3",
@@ -10,6 +10,9 @@ import type { RbacService } from '@open-mercato/core/modules/auth/services/rbacS
10
10
  import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
11
11
  import { CHECKOUT_PASSWORD_COOKIE } from '../lib/constants'
12
12
  import { verifyCheckoutAccessToken } from '../lib/utils'
13
+ import { createLogger } from '@open-mercato/shared/lib/logger'
14
+
15
+ const logger = createLogger('checkout')
13
16
 
14
17
  export async function requireAdminContext(req: Request): Promise<{
15
18
  auth: Exclude<AuthContext, null>
@@ -120,7 +123,7 @@ export function handleCheckoutRouteError(error: unknown) {
120
123
  )
121
124
  }
122
125
  if (error instanceof Error) {
123
- console.error('[checkout] Unhandled route error:', error.message)
126
+ logger.error('Unhandled route error', { err: error })
124
127
  }
125
128
  return NextResponse.json({ error: 'Internal server error' }, { status: 500 })
126
129
  }
@@ -24,6 +24,9 @@ import {
24
24
  import { validateCheckoutCustomerData } from '../../../../lib/customerDataValidation'
25
25
  import { checkoutSubmitRateLimitConfig } from '../../../../lib/rateLimiter'
26
26
  import { checkoutTag } from '../../../openapi'
27
+ import { createLogger } from '@open-mercato/shared/lib/logger'
28
+
29
+ const logger = createLogger('checkout')
27
30
 
28
31
  type CachedSubmitResponse = {
29
32
  transactionId: string
@@ -515,11 +518,11 @@ export async function POST(req: Request, { params }: { params: Promise<{ slug: s
515
518
  },
516
519
  ctx,
517
520
  }).catch(() => undefined)
518
- console.error('[checkout] Failed to create payment session', {
521
+ logger.error('Failed to create payment session', {
519
522
  linkId: link.id,
520
523
  transactionId: transaction.id,
521
524
  providerKey: link.gatewayProviderKey,
522
- error: error instanceof Error ? error.message : String(error),
525
+ err: error,
523
526
  })
524
527
  throw new CrudHttpError(502, { error: 'Unable to start the payment session' })
525
528
  }
@@ -37,6 +37,9 @@ import {
37
37
  getCheckoutCustomerFieldSemanticType,
38
38
  validateCheckoutCustomerData,
39
39
  } from '../lib/customerDataValidation'
40
+ import { createLogger } from '@open-mercato/shared/lib/logger'
41
+
42
+ const logger = createLogger('checkout').child({ component: 'PayPage' })
40
43
 
41
44
  type CustomerFieldOption = {
42
45
  value: string
@@ -1764,7 +1767,7 @@ export function PayPage({
1764
1767
  Object.entries(transformed as Record<string, unknown>).map(([fieldPath, value]) => [fieldPath, String(value)]),
1765
1768
  )
1766
1769
  } catch (error) {
1767
- console.error('[PayPage] Error in transformValidation:', error)
1770
+ logger.error('Error in transformValidation', { err: error })
1768
1771
  return nextErrors
1769
1772
  }
1770
1773
  }, [triggerPayPageFormEvent])
@@ -1799,7 +1802,7 @@ export function PayPage({
1799
1802
  setFieldErrors((current) => ({ ...current, [fieldPath]: nextMessage }))
1800
1803
  }
1801
1804
  } catch (error) {
1802
- console.error('[PayPage] Error in onFieldChange:', error)
1805
+ logger.error('Error in onFieldChange', { err: error })
1803
1806
  }
1804
1807
  })()
1805
1808
  }, [applySubmitDataToState, triggerPayPageFormEvent])
@@ -1888,7 +1891,7 @@ export function PayPage({
1888
1891
  }
1889
1892
  }
1890
1893
  } catch (error) {
1891
- console.error('[PayPage] Error in transformFormData:', error)
1894
+ logger.error('Error in transformFormData', { err: error })
1892
1895
  }
1893
1896
  }
1894
1897
 
@@ -7,6 +7,9 @@ import { CHECKOUT_ENTITY_IDS } from '../lib/constants'
7
7
  import { DEFAULT_CHECKOUT_CUSTOMER_FIELDS } from '../lib/defaults'
8
8
  import { ensureUniqueSlug, toMoneyString, toTemplateOrLinkMutationInput } from '../lib/utils'
9
9
  import { ensureCheckoutFieldsetsAndDefinitions } from './customFields'
10
+ import { createLogger } from '@open-mercato/shared/lib/logger'
11
+
12
+ const logger = createLogger('checkout').child({ component: 'seed' })
10
13
 
11
14
  type TemplateSeed = {
12
15
  name: string
@@ -351,7 +354,7 @@ async function assignCustomFields(args: {
351
354
  values: args.values,
352
355
  })
353
356
  } catch (error) {
354
- console.warn('[checkout.seed] Failed to set example custom field values', error)
357
+ logger.warn('Failed to set example custom field values', { err: error })
355
358
  }
356
359
  }
357
360
 
@@ -1,4 +1,7 @@
1
1
  import { dispatchCheckoutEmailJob } from '../lib/emailQueue'
2
+ import { createLogger } from '@open-mercato/shared/lib/logger'
3
+
4
+ const logger = createLogger('checkout').child({ component: 'session-started-email' })
2
5
 
3
6
  export const metadata = {
4
7
  event: 'checkout.transaction.sessionStarted',
@@ -23,6 +26,6 @@ export default async function handle(payload: SessionStartedPayload) {
23
26
  organizationId: payload.organizationId,
24
27
  })
25
28
  } catch (err) {
26
- console.error('[checkout:session-started-email] email enqueue failed:', err)
29
+ logger.error('email enqueue failed', { transactionId: payload.transactionId, err })
27
30
  }
28
31
  }
@@ -3,6 +3,9 @@ import { buildFeatureNotificationFromType } from '@open-mercato/core/modules/not
3
3
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import { notificationTypes } from '../notifications'
5
5
  import { dispatchCheckoutEmailJob } from '../lib/emailQueue'
6
+ import { createLogger } from '@open-mercato/shared/lib/logger'
7
+
8
+ const logger = createLogger('checkout').child({ component: 'transaction-completed-notify' })
6
9
 
7
10
  export const metadata = {
8
11
  event: 'checkout.transaction.completed',
@@ -46,7 +49,7 @@ export default async function handle(payload: CompletedPayload) {
46
49
  })
47
50
  }
48
51
  } catch (err) {
49
- console.error('[checkout:transaction-completed-notify] notification failed:', err)
52
+ logger.error('notification failed', { transactionId: payload.transactionId, err })
50
53
  }
51
54
 
52
55
  try {
@@ -57,6 +60,6 @@ export default async function handle(payload: CompletedPayload) {
57
60
  organizationId: payload.organizationId,
58
61
  })
59
62
  } catch (err) {
60
- console.error('[checkout:transaction-completed-notify] email enqueue failed:', err)
63
+ logger.error('email enqueue failed', { transactionId: payload.transactionId, err })
61
64
  }
62
65
  }
@@ -3,6 +3,9 @@ import { buildFeatureNotificationFromType } from '@open-mercato/core/modules/not
3
3
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import { notificationTypes } from '../notifications'
5
5
  import { dispatchCheckoutEmailJob } from '../lib/emailQueue'
6
+ import { createLogger } from '@open-mercato/shared/lib/logger'
7
+
8
+ const logger = createLogger('checkout').child({ component: 'transaction-failed-notify' })
6
9
 
7
10
  export const metadata = {
8
11
  event: 'checkout.transaction.failed',
@@ -46,7 +49,7 @@ export default async function handle(payload: FailedPayload) {
46
49
  })
47
50
  }
48
51
  } catch (err) {
49
- console.error('[checkout:transaction-failed-notify] notification failed:', err)
52
+ logger.error('notification failed', { transactionId: payload.transactionId, err })
50
53
  }
51
54
 
52
55
  try {
@@ -57,6 +60,6 @@ export default async function handle(payload: FailedPayload) {
57
60
  organizationId: payload.organizationId,
58
61
  })
59
62
  } catch (err) {
60
- console.error('[checkout:transaction-failed-notify] email enqueue failed:', err)
63
+ logger.error('email enqueue failed', { transactionId: payload.transactionId, err })
61
64
  }
62
65
  }
@@ -2,6 +2,9 @@ import { resolveNotificationService } from '@open-mercato/core/modules/notificat
2
2
  import { buildFeatureNotificationFromType } from '@open-mercato/core/modules/notifications/lib/notificationBuilder'
3
3
  import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
4
4
  import { notificationTypes } from '../notifications'
5
+ import { createLogger } from '@open-mercato/shared/lib/logger'
6
+
7
+ const logger = createLogger('checkout').child({ component: 'usage-limit-reached-notify' })
5
8
 
6
9
  export const metadata = {
7
10
  event: 'checkout.link.usageLimitReached',
@@ -39,6 +42,6 @@ export default async function handle(payload: UsageLimitPayload) {
39
42
  organizationId: payload.organizationId,
40
43
  })
41
44
  } catch (err) {
42
- console.error('[checkout:usage-limit-reached-notify] notification failed:', err)
45
+ logger.error('notification failed', { err })
43
46
  }
44
47
  }
@@ -4,6 +4,9 @@ import type { CommandBus } from '@open-mercato/shared/lib/commands/command-bus'
4
4
  import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands/types'
5
5
  import { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
6
6
  import { CheckoutTransaction } from '../data/entities'
7
+ import { createLogger } from '@open-mercato/shared/lib/logger'
8
+
9
+ const logger = createLogger('checkout').child({ component: 'transaction-expiry' })
7
10
 
8
11
  export const CHECKOUT_EXPIRY_QUEUE = 'checkout-transaction-expiry'
9
12
 
@@ -59,7 +62,7 @@ export default async function handle(job: QueuedJob<CheckoutExpiryJob>, ctx: Han
59
62
  ctx: commandCtx,
60
63
  })
61
64
  } catch (error) {
62
- console.error('[checkout:transaction-expiry] Failed to expire transaction', transaction.id, error)
65
+ logger.error('Failed to expire transaction', { transactionId: transaction.id, err: error })
63
66
  }
64
67
  }
65
68
  }