@open-mercato/checkout 0.6.4-develop.4382.1.6b4f656b77 → 0.6.4

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 (52) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/checkout/__integration__/TC-CHK-CRUDFORM-001.spec.js +125 -0
  3. package/dist/modules/checkout/__integration__/TC-CHK-CRUDFORM-001.spec.js.map +7 -0
  4. package/dist/modules/checkout/__integration__/TC-CHK-CRUDFORM-002.spec.js +139 -0
  5. package/dist/modules/checkout/__integration__/TC-CHK-CRUDFORM-002.spec.js.map +7 -0
  6. package/dist/modules/checkout/__integration__/TC-CHKT-008.spec.js +2 -0
  7. package/dist/modules/checkout/__integration__/TC-CHKT-008.spec.js.map +2 -2
  8. package/dist/modules/checkout/__integration__/TC-CHKT-039-null-gateway-edit.spec.js +115 -0
  9. package/dist/modules/checkout/__integration__/TC-CHKT-039-null-gateway-edit.spec.js.map +7 -0
  10. package/dist/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.js +66 -0
  11. package/dist/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.js.map +7 -0
  12. package/dist/modules/checkout/__integration__/TC-CHKT-040-gateway-settings-select.spec.js +52 -0
  13. package/dist/modules/checkout/__integration__/TC-CHKT-040-gateway-settings-select.spec.js.map +7 -0
  14. package/dist/modules/checkout/__integration__/TC-CHKT-041-template-stale-delete-conflict.spec.js +44 -0
  15. package/dist/modules/checkout/__integration__/TC-CHKT-041-template-stale-delete-conflict.spec.js.map +7 -0
  16. package/dist/modules/checkout/backend/checkout/pay-links/page.js +9 -0
  17. package/dist/modules/checkout/backend/checkout/pay-links/page.js.map +2 -2
  18. package/dist/modules/checkout/backend/checkout/templates/page.js +9 -0
  19. package/dist/modules/checkout/backend/checkout/templates/page.js.map +2 -2
  20. package/dist/modules/checkout/commands/links.js +67 -0
  21. package/dist/modules/checkout/commands/links.js.map +2 -2
  22. package/dist/modules/checkout/commands/templates.js +69 -2
  23. package/dist/modules/checkout/commands/templates.js.map +2 -2
  24. package/dist/modules/checkout/components/GatewaySettingsFields.js +32 -15
  25. package/dist/modules/checkout/components/GatewaySettingsFields.js.map +2 -2
  26. package/dist/modules/checkout/components/LinkTemplateForm.js +27 -7
  27. package/dist/modules/checkout/components/LinkTemplateForm.js.map +2 -2
  28. package/dist/modules/checkout/data/validators.js +18 -2
  29. package/dist/modules/checkout/data/validators.js.map +2 -2
  30. package/dist/modules/checkout/lib/utils.js +26 -5
  31. package/dist/modules/checkout/lib/utils.js.map +2 -2
  32. package/jest.config.cjs +2 -0
  33. package/package.json +7 -8
  34. package/src/modules/checkout/__integration__/TC-CHK-CRUDFORM-001.spec.ts +158 -0
  35. package/src/modules/checkout/__integration__/TC-CHK-CRUDFORM-002.spec.ts +171 -0
  36. package/src/modules/checkout/__integration__/TC-CHKT-008.spec.ts +4 -0
  37. package/src/modules/checkout/__integration__/TC-CHKT-039-null-gateway-edit.spec.ts +131 -0
  38. package/src/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.ts +98 -0
  39. package/src/modules/checkout/__integration__/TC-CHKT-040-gateway-settings-select.spec.ts +60 -0
  40. package/src/modules/checkout/__integration__/TC-CHKT-041-template-stale-delete-conflict.spec.ts +58 -0
  41. package/src/modules/checkout/backend/checkout/pay-links/page.tsx +8 -0
  42. package/src/modules/checkout/backend/checkout/templates/page.tsx +8 -0
  43. package/src/modules/checkout/commands/__tests__/optimistic-lock.test.ts +261 -0
  44. package/src/modules/checkout/commands/__tests__/redo-coverage.test.ts +21 -0
  45. package/src/modules/checkout/commands/links.ts +67 -0
  46. package/src/modules/checkout/commands/templates.ts +74 -2
  47. package/src/modules/checkout/components/GatewaySettingsFields.tsx +40 -18
  48. package/src/modules/checkout/components/LinkTemplateForm.tsx +27 -7
  49. package/src/modules/checkout/data/__tests__/validators.test.ts +66 -1
  50. package/src/modules/checkout/data/validators.ts +18 -2
  51. package/src/modules/checkout/lib/__tests__/utils.test.ts +112 -0
  52. package/src/modules/checkout/lib/utils.ts +41 -5
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/data/validators.ts"],
4
- "sourcesContent": ["import { z } from 'zod'\nimport { fieldsetCodeRegex } from '@open-mercato/shared/modules/entities/validators'\nimport { DEFAULT_CHECKOUT_CUSTOMER_FIELDS } from '../lib/defaults'\nimport { CHECKOUT_LINK_STATUSES } from '../lib/constants'\n\nfunction normalizeBlankString(value: unknown): unknown {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nfunction normalizeOptionalDocument(value: unknown): unknown {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return value\n const source = value as Record<string, unknown>\n const title = typeof source.title === 'string' ? source.title.trim() : ''\n const markdown = typeof source.markdown === 'string' ? source.markdown.trim() : ''\n const required = source.required === true\n if (!title && !markdown && !required) return undefined\n return value\n}\n\nfunction requiredTrimmedString(message: string) {\n return z.string().trim().min(1, { message })\n}\n\nconst hexColorSchema = z.string().regex(/^#([0-9a-fA-F]{6})$/, {\n message: 'checkout.validation.common.invalidColor',\n})\nconst currencyCodeSchema = z.string().trim().toUpperCase().regex(/^[A-Z]{3}$/, {\n message: 'checkout.validation.common.invalidCurrencyCode',\n})\nconst optionalTrimmedString = z.preprocess(\n normalizeBlankString,\n z.string().trim().min(1, { message: 'checkout.validation.common.required' }).optional().nullable(),\n)\nconst optionalUrlSchema = z.preprocess(\n normalizeBlankString,\n z.string().url('checkout.validation.common.invalidUrl').optional().nullable(),\n)\nconst optionalFieldsetCodeSchema = z.preprocess(\n normalizeBlankString,\n z.string().regex(fieldsetCodeRegex, {\n message: 'checkout.validation.common.invalidFieldsetCode',\n }).optional().nullable(),\n)\nconst positiveMoneySchema = z.coerce.number().finite('checkout.validation.common.invalidNumber').nonnegative('checkout.validation.common.nonNegativeNumber')\nconst linkStatusSchema = z.enum(CHECKOUT_LINK_STATUSES)\n\nexport const customerFieldOptionSchema = z.object({\n value: requiredTrimmedString('checkout.validation.common.required'),\n label: requiredTrimmedString('checkout.validation.common.required'),\n})\n\nexport const customerFieldDefinitionSchema = z.object({\n key: z.string().regex(/^[a-z][A-Za-z0-9]*$/, {\n message: 'checkout.validation.customerFields.key.invalid',\n }),\n label: requiredTrimmedString('checkout.validation.common.required'),\n kind: z.enum(['text', 'multiline', 'boolean', 'select', 'radio']),\n required: z.boolean(),\n fixed: z.boolean(),\n placeholder: optionalTrimmedString,\n options: z.array(customerFieldOptionSchema).optional(),\n sortOrder: z.coerce.number().int('checkout.validation.common.integer').min(0, {\n message: 'checkout.validation.common.nonNegativeInteger',\n }),\n})\n\nexport const legalDocumentSchema = z.preprocess(\n normalizeOptionalDocument,\n z.object({\n title: requiredTrimmedString('checkout.validation.common.required'),\n markdown: requiredTrimmedString('checkout.validation.common.required'),\n required: z.boolean().default(false),\n }).optional(),\n)\n\nexport const legalDocumentsSchema = z.object({\n terms: legalDocumentSchema.optional(),\n privacyPolicy: legalDocumentSchema.optional(),\n}).optional()\n\nexport const priceListItemSchema = z.object({\n id: requiredTrimmedString('checkout.validation.common.required'),\n description: requiredTrimmedString('checkout.validation.common.required'),\n amount: positiveMoneySchema,\n currencyCode: currencyCodeSchema,\n})\n\nexport const gatewaySettingsSchema = z.record(z.string(), z.unknown()).optional()\n\nconst checkoutContentSchema = z.object({\n name: requiredTrimmedString('checkout.validation.name.required'),\n title: optionalTrimmedString,\n subtitle: optionalTrimmedString,\n description: z.string().optional().nullable(),\n logoAttachmentId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n logoUrl: optionalUrlSchema,\n primaryColor: hexColorSchema.optional().nullable(),\n secondaryColor: hexColorSchema.optional().nullable(),\n backgroundColor: hexColorSchema.optional().nullable(),\n themeMode: z.enum(['light', 'dark', 'auto']).default('auto'),\n pricingMode: z.enum(['fixed', 'custom_amount', 'price_list']),\n fixedPriceAmount: positiveMoneySchema.optional().nullable(),\n fixedPriceCurrencyCode: currencyCodeSchema.optional().nullable(),\n fixedPriceIncludesTax: z.boolean().default(true),\n fixedPriceOriginalAmount: positiveMoneySchema.optional().nullable(),\n customAmountMin: positiveMoneySchema.optional().nullable(),\n customAmountMax: positiveMoneySchema.optional().nullable(),\n customAmountCurrencyCode: currencyCodeSchema.optional().nullable(),\n priceListItems: z.array(priceListItemSchema).optional().nullable(),\n gatewayProviderKey: requiredTrimmedString('checkout.validation.gatewayProviderKey.required'),\n gatewaySettings: gatewaySettingsSchema,\n customFieldsetCode: optionalFieldsetCodeSchema,\n collectCustomerDetails: z.boolean().default(true),\n customerFieldsSchema: z.array(customerFieldDefinitionSchema).default([...DEFAULT_CHECKOUT_CUSTOMER_FIELDS]),\n legalDocuments: legalDocumentsSchema,\n displayCustomFieldsOnPage: z.boolean().default(false),\n successTitle: optionalTrimmedString,\n successMessage: z.string().optional().nullable(),\n cancelTitle: optionalTrimmedString,\n cancelMessage: z.string().optional().nullable(),\n errorTitle: optionalTrimmedString,\n errorMessage: z.string().optional().nullable(),\n successEmailSubject: optionalTrimmedString,\n successEmailBody: z.string().optional().nullable(),\n sendSuccessEmail: z.boolean().default(true),\n errorEmailSubject: optionalTrimmedString,\n errorEmailBody: z.string().optional().nullable(),\n sendErrorEmail: z.boolean().default(true),\n startEmailSubject: optionalTrimmedString,\n startEmailBody: z.string().optional().nullable(),\n sendStartEmail: z.boolean().default(true),\n password: optionalTrimmedString,\n maxCompletions: z.coerce.number().int('checkout.validation.common.integer').positive('checkout.validation.common.positiveInteger').optional().nullable(),\n status: linkStatusSchema.default('draft'),\n checkoutType: z.enum(['pay_link', 'simple_checkout']).default('pay_link'),\n})\n\nfunction validatePricingConsistency<T extends z.infer<typeof checkoutContentSchema>>(value: T, ctx: z.RefinementCtx) {\n if (value.pricingMode === 'fixed') {\n if (value.fixedPriceAmount == null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.fixedPriceAmount.required', path: ['fixedPriceAmount'] })\n }\n if (!value.fixedPriceCurrencyCode) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.fixedPriceCurrencyCode.required', path: ['fixedPriceCurrencyCode'] })\n }\n }\n\n if (value.pricingMode === 'custom_amount') {\n if (value.customAmountMin == null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmountMin.required', path: ['customAmountMin'] })\n }\n if (value.customAmountMax == null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmountMax.required', path: ['customAmountMax'] })\n }\n if (!value.customAmountCurrencyCode) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmountCurrencyCode.required', path: ['customAmountCurrencyCode'] })\n }\n if (value.customAmountMin != null && value.customAmountMax != null && value.customAmountMin > value.customAmountMax) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmount.range', path: ['customAmountMax'] })\n }\n }\n\n if (value.pricingMode === 'price_list') {\n if (!Array.isArray(value.priceListItems) || value.priceListItems.length === 0) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.priceListItems.required', path: ['priceListItems'] })\n return\n }\n const currencies = new Set(value.priceListItems.map((item) => item.currencyCode))\n if (currencies.size > 1) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.priceListItems.singleCurrency', path: ['priceListItems'] })\n }\n }\n}\n\nexport const createTemplateSchema = checkoutContentSchema.superRefine(validatePricingConsistency)\n\nfunction applyPartialPricingConsistency(value: Record<string, unknown>, ctx: z.RefinementCtx) {\n if (!value.pricingMode) return\n validatePricingConsistency({\n ...checkoutContentSchema.parse({\n ...value,\n name: value.name ?? 'placeholder',\n pricingMode: value.pricingMode,\n customerFieldsSchema: value.customerFieldsSchema ?? [...DEFAULT_CHECKOUT_CUSTOMER_FIELDS],\n }),\n ...value,\n }, ctx)\n}\n\nexport const updateTemplateSchema = checkoutContentSchema.partial().extend({\n id: z.string().uuid('checkout.validation.common.invalidUuid'),\n password: optionalTrimmedString,\n customerFieldsSchema: z.array(customerFieldDefinitionSchema).optional(),\n}).superRefine((value, ctx) => {\n applyPartialPricingConsistency(value, ctx)\n})\n\nexport const createLinkSchema = createTemplateSchema.safeExtend({\n templateId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n slug: optionalTrimmedString,\n})\n\nexport const updateLinkSchema = checkoutContentSchema.partial().safeExtend({\n id: z.string().uuid('checkout.validation.common.invalidUuid'),\n templateId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n slug: optionalTrimmedString,\n password: optionalTrimmedString,\n customerFieldsSchema: z.array(customerFieldDefinitionSchema).optional(),\n}).superRefine((value, ctx) => {\n applyPartialPricingConsistency(value, ctx)\n})\n\nexport const transactionStatusSchema = z.enum(['pending', 'processing', 'completed', 'failed', 'cancelled', 'expired'])\n\nexport const transactionCreateSchema = z.object({\n linkId: z.string().uuid('checkout.validation.common.invalidUuid'),\n amount: positiveMoneySchema,\n currencyCode: currencyCodeSchema,\n idempotencyKey: requiredTrimmedString('checkout.validation.common.required'),\n customerData: z.record(z.string(), z.unknown()).default({}),\n firstName: optionalTrimmedString,\n lastName: optionalTrimmedString,\n email: optionalTrimmedString,\n phone: optionalTrimmedString,\n gatewayTransactionId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n paymentStatus: optionalTrimmedString,\n selectedPriceItemId: optionalTrimmedString,\n acceptedLegalConsents: z.record(z.string(), z.unknown()).optional().nullable(),\n ipAddress: optionalTrimmedString,\n userAgent: z.string().optional().nullable(),\n tenantId: z.string().uuid('checkout.validation.common.invalidUuid'),\n organizationId: z.string().uuid('checkout.validation.common.invalidUuid'),\n})\n\nexport const transactionUpdateStatusSchema = z.object({\n id: z.string().uuid('checkout.validation.common.invalidUuid'),\n status: transactionStatusSchema,\n paymentStatus: optionalTrimmedString,\n gatewayTransactionId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n tenantId: z.string().uuid('checkout.validation.common.invalidUuid'),\n organizationId: z.string().uuid('checkout.validation.common.invalidUuid'),\n})\n\nexport const publicPasswordVerifySchema = z.object({\n password: z.string().min(1, { message: 'checkout.validation.common.required' }),\n})\n\nexport const publicSubmitSchema = z.object({\n customerData: z.record(z.string(), z.unknown()),\n acceptedLegalConsents: z.object({\n terms: z.boolean().optional(),\n privacyPolicy: z.boolean().optional(),\n }).default({}),\n amount: z.coerce.number().finite().nonnegative().optional(),\n selectedPriceItemId: optionalTrimmedString,\n})\n\nexport type CustomerFieldDefinitionInput = z.infer<typeof customerFieldDefinitionSchema>\nexport type PriceListItemInput = z.infer<typeof priceListItemSchema>\nexport type CreateTemplateInput = z.infer<typeof createTemplateSchema>\nexport type UpdateTemplateInput = z.infer<typeof updateTemplateSchema>\nexport type CreateLinkInput = z.infer<typeof createLinkSchema>\nexport type UpdateLinkInput = z.infer<typeof updateLinkSchema>\nexport type CreateTransactionInput = z.infer<typeof transactionCreateSchema>\nexport type UpdateTransactionStatusInput = z.infer<typeof transactionUpdateStatusSchema>\nexport type PublicSubmitInput = z.infer<typeof publicSubmitSchema>\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { z } from 'zod'\nimport { fieldsetCodeRegex } from '@open-mercato/shared/modules/entities/validators'\nimport { DEFAULT_CHECKOUT_CUSTOMER_FIELDS } from '../lib/defaults'\nimport { CHECKOUT_LINK_STATUSES } from '../lib/constants'\n\nfunction normalizeBlankString(value: unknown): unknown {\n if (typeof value !== 'string') return value\n const trimmed = value.trim()\n return trimmed.length > 0 ? trimmed : null\n}\n\nfunction normalizeOptionalDocument(value: unknown): unknown {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return value\n const source = value as Record<string, unknown>\n const title = typeof source.title === 'string' ? source.title.trim() : ''\n const markdown = typeof source.markdown === 'string' ? source.markdown.trim() : ''\n const required = source.required === true\n if (!title && !markdown && !required) return undefined\n return value\n}\n\nfunction requiredTrimmedString(message: string) {\n return z.string().trim().min(1, { message })\n}\n\nconst hexColorSchema = z.string().regex(/^#([0-9a-fA-F]{6})$/, {\n message: 'checkout.validation.common.invalidColor',\n})\nconst currencyCodeSchema = z.string().trim().toUpperCase().regex(/^[A-Z]{3}$/, {\n message: 'checkout.validation.common.invalidCurrencyCode',\n})\nconst optionalTrimmedString = z.preprocess(\n normalizeBlankString,\n z.string().trim().min(1, { message: 'checkout.validation.common.required' }).optional().nullable(),\n)\nconst optionalUrlSchema = z.preprocess(\n normalizeBlankString,\n z.string().url('checkout.validation.common.invalidUrl').optional().nullable(),\n)\nconst optionalFieldsetCodeSchema = z.preprocess(\n normalizeBlankString,\n z.string().regex(fieldsetCodeRegex, {\n message: 'checkout.validation.common.invalidFieldsetCode',\n }).optional().nullable(),\n)\nconst positiveMoneySchema = z.coerce.number().finite('checkout.validation.common.invalidNumber').nonnegative('checkout.validation.common.nonNegativeNumber')\nconst linkStatusSchema = z.enum(CHECKOUT_LINK_STATUSES)\n\nexport const customerFieldOptionSchema = z.object({\n value: requiredTrimmedString('checkout.validation.common.required'),\n label: requiredTrimmedString('checkout.validation.common.required'),\n})\n\nexport const customerFieldDefinitionSchema = z.object({\n key: z.string().regex(/^[a-z][A-Za-z0-9]*$/, {\n message: 'checkout.validation.customerFields.key.invalid',\n }),\n label: requiredTrimmedString('checkout.validation.common.required'),\n kind: z.enum(['text', 'multiline', 'boolean', 'select', 'radio']),\n required: z.boolean(),\n fixed: z.boolean(),\n placeholder: optionalTrimmedString,\n options: z.array(customerFieldOptionSchema).optional(),\n sortOrder: z.coerce.number().int('checkout.validation.common.integer').min(0, {\n message: 'checkout.validation.common.nonNegativeInteger',\n }),\n})\n\nexport const legalDocumentSchema = z.preprocess(\n normalizeOptionalDocument,\n z.object({\n title: requiredTrimmedString('checkout.validation.common.required'),\n markdown: requiredTrimmedString('checkout.validation.common.required'),\n required: z.boolean().default(false),\n }).optional(),\n)\n\nexport const legalDocumentsSchema = z.object({\n terms: legalDocumentSchema.optional(),\n privacyPolicy: legalDocumentSchema.optional(),\n}).optional()\n\nexport const priceListItemSchema = z.object({\n id: requiredTrimmedString('checkout.validation.common.required'),\n description: requiredTrimmedString('checkout.validation.common.required'),\n amount: positiveMoneySchema,\n currencyCode: currencyCodeSchema,\n})\n\nexport const gatewaySettingsSchema = z.record(z.string(), z.unknown()).optional()\n\nconst checkoutContentSchema = z.object({\n name: requiredTrimmedString('checkout.validation.name.required'),\n title: optionalTrimmedString,\n subtitle: optionalTrimmedString,\n description: z.string().optional().nullable(),\n logoAttachmentId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n logoUrl: optionalUrlSchema,\n primaryColor: hexColorSchema.optional().nullable(),\n secondaryColor: hexColorSchema.optional().nullable(),\n backgroundColor: hexColorSchema.optional().nullable(),\n themeMode: z.enum(['light', 'dark', 'auto']).default('auto'),\n pricingMode: z.enum(['fixed', 'custom_amount', 'price_list']),\n fixedPriceAmount: positiveMoneySchema.optional().nullable(),\n fixedPriceCurrencyCode: currencyCodeSchema.optional().nullable(),\n fixedPriceIncludesTax: z.boolean().default(true),\n fixedPriceOriginalAmount: positiveMoneySchema.optional().nullable(),\n customAmountMin: positiveMoneySchema.optional().nullable(),\n customAmountMax: positiveMoneySchema.optional().nullable(),\n customAmountCurrencyCode: currencyCodeSchema.optional().nullable(),\n priceListItems: z.array(priceListItemSchema).optional().nullable(),\n gatewayProviderKey: optionalTrimmedString,\n gatewaySettings: gatewaySettingsSchema,\n customFieldsetCode: optionalFieldsetCodeSchema,\n collectCustomerDetails: z.boolean().default(true),\n customerFieldsSchema: z.array(customerFieldDefinitionSchema).default([...DEFAULT_CHECKOUT_CUSTOMER_FIELDS]),\n legalDocuments: legalDocumentsSchema,\n displayCustomFieldsOnPage: z.boolean().default(false),\n successTitle: optionalTrimmedString,\n successMessage: z.string().optional().nullable(),\n cancelTitle: optionalTrimmedString,\n cancelMessage: z.string().optional().nullable(),\n errorTitle: optionalTrimmedString,\n errorMessage: z.string().optional().nullable(),\n successEmailSubject: optionalTrimmedString,\n successEmailBody: z.string().optional().nullable(),\n sendSuccessEmail: z.boolean().default(true),\n errorEmailSubject: optionalTrimmedString,\n errorEmailBody: z.string().optional().nullable(),\n sendErrorEmail: z.boolean().default(true),\n startEmailSubject: optionalTrimmedString,\n startEmailBody: z.string().optional().nullable(),\n sendStartEmail: z.boolean().default(true),\n password: optionalTrimmedString,\n maxCompletions: z.coerce.number().int('checkout.validation.common.integer').positive('checkout.validation.common.positiveInteger').optional().nullable(),\n status: linkStatusSchema.default('draft'),\n checkoutType: z.enum(['pay_link', 'simple_checkout']).default('pay_link'),\n})\n\nfunction validatePricingConsistency<T extends z.infer<typeof checkoutContentSchema>>(value: T, ctx: z.RefinementCtx) {\n if (value.pricingMode === 'fixed') {\n if (value.fixedPriceAmount == null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.fixedPriceAmount.required', path: ['fixedPriceAmount'] })\n }\n if (!value.fixedPriceCurrencyCode) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.fixedPriceCurrencyCode.required', path: ['fixedPriceCurrencyCode'] })\n }\n }\n\n if (value.pricingMode === 'custom_amount') {\n if (value.customAmountMin == null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmountMin.required', path: ['customAmountMin'] })\n }\n if (value.customAmountMax == null) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmountMax.required', path: ['customAmountMax'] })\n }\n if (!value.customAmountCurrencyCode) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmountCurrencyCode.required', path: ['customAmountCurrencyCode'] })\n }\n if (value.customAmountMin != null && value.customAmountMax != null && value.customAmountMin > value.customAmountMax) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.customAmount.range', path: ['customAmountMax'] })\n }\n }\n\n if (value.pricingMode === 'price_list') {\n if (!Array.isArray(value.priceListItems) || value.priceListItems.length === 0) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.priceListItems.required', path: ['priceListItems'] })\n return\n }\n const currencies = new Set(value.priceListItems.map((item) => item.currencyCode))\n if (currencies.size > 1) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'checkout.validation.priceListItems.singleCurrency', path: ['priceListItems'] })\n }\n }\n}\n\nexport const createTemplateSchema = checkoutContentSchema.superRefine((value, ctx) => {\n validatePricingConsistency(value, ctx)\n if (!value.gatewayProviderKey) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'checkout.validation.gatewayProviderKey.required',\n path: ['gatewayProviderKey'],\n })\n }\n})\n\nfunction applyPartialPricingConsistency(value: Record<string, unknown>, ctx: z.RefinementCtx) {\n if (!value.pricingMode) return\n validatePricingConsistency({\n ...checkoutContentSchema.parse({\n ...value,\n name: value.name ?? 'placeholder',\n pricingMode: value.pricingMode,\n customerFieldsSchema: value.customerFieldsSchema ?? [...DEFAULT_CHECKOUT_CUSTOMER_FIELDS],\n }),\n ...value,\n }, ctx)\n}\n\nexport const updateTemplateSchema = checkoutContentSchema.partial().extend({\n id: z.string().uuid('checkout.validation.common.invalidUuid'),\n password: optionalTrimmedString,\n customerFieldsSchema: z.array(customerFieldDefinitionSchema).optional(),\n}).superRefine((value, ctx) => {\n applyPartialPricingConsistency(value, ctx)\n if (Object.prototype.hasOwnProperty.call(value, 'gatewayProviderKey') && !value.gatewayProviderKey) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: 'checkout.validation.gatewayProviderKey.required',\n path: ['gatewayProviderKey'],\n })\n }\n})\n\nexport const createLinkSchema = createTemplateSchema.safeExtend({\n templateId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n slug: optionalTrimmedString,\n})\n\nexport const updateLinkSchema = checkoutContentSchema.partial().safeExtend({\n id: z.string().uuid('checkout.validation.common.invalidUuid'),\n templateId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n slug: optionalTrimmedString,\n password: optionalTrimmedString,\n customerFieldsSchema: z.array(customerFieldDefinitionSchema).optional(),\n}).superRefine((value, ctx) => {\n applyPartialPricingConsistency(value, ctx)\n})\n\nexport const transactionStatusSchema = z.enum(['pending', 'processing', 'completed', 'failed', 'cancelled', 'expired'])\n\nexport const transactionCreateSchema = z.object({\n linkId: z.string().uuid('checkout.validation.common.invalidUuid'),\n amount: positiveMoneySchema,\n currencyCode: currencyCodeSchema,\n idempotencyKey: requiredTrimmedString('checkout.validation.common.required'),\n customerData: z.record(z.string(), z.unknown()).default({}),\n firstName: optionalTrimmedString,\n lastName: optionalTrimmedString,\n email: optionalTrimmedString,\n phone: optionalTrimmedString,\n gatewayTransactionId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n paymentStatus: optionalTrimmedString,\n selectedPriceItemId: optionalTrimmedString,\n acceptedLegalConsents: z.record(z.string(), z.unknown()).optional().nullable(),\n ipAddress: optionalTrimmedString,\n userAgent: z.string().optional().nullable(),\n tenantId: z.string().uuid('checkout.validation.common.invalidUuid'),\n organizationId: z.string().uuid('checkout.validation.common.invalidUuid'),\n})\n\nexport const transactionUpdateStatusSchema = z.object({\n id: z.string().uuid('checkout.validation.common.invalidUuid'),\n status: transactionStatusSchema,\n paymentStatus: optionalTrimmedString,\n gatewayTransactionId: z.string().uuid('checkout.validation.common.invalidUuid').optional().nullable(),\n tenantId: z.string().uuid('checkout.validation.common.invalidUuid'),\n organizationId: z.string().uuid('checkout.validation.common.invalidUuid'),\n})\n\nexport const publicPasswordVerifySchema = z.object({\n password: z.string().min(1, { message: 'checkout.validation.common.required' }),\n})\n\nexport const publicSubmitSchema = z.object({\n customerData: z.record(z.string(), z.unknown()),\n acceptedLegalConsents: z.object({\n terms: z.boolean().optional(),\n privacyPolicy: z.boolean().optional(),\n }).default({}),\n amount: z.coerce.number().finite().nonnegative().optional(),\n selectedPriceItemId: optionalTrimmedString,\n})\n\nexport type CustomerFieldDefinitionInput = z.infer<typeof customerFieldDefinitionSchema>\nexport type PriceListItemInput = z.infer<typeof priceListItemSchema>\nexport type CreateTemplateInput = z.infer<typeof createTemplateSchema>\nexport type UpdateTemplateInput = z.infer<typeof updateTemplateSchema>\nexport type CreateLinkInput = z.infer<typeof createLinkSchema>\nexport type UpdateLinkInput = z.infer<typeof updateLinkSchema>\nexport type CreateTransactionInput = z.infer<typeof transactionCreateSchema>\nexport type UpdateTransactionStatusInput = z.infer<typeof transactionUpdateStatusSchema>\nexport type PublicSubmitInput = z.infer<typeof publicSubmitSchema>\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -71,6 +71,17 @@ function toMoneyString(value) {
71
71
  return numeric == null ? null : numeric.toFixed(2);
72
72
  }
73
73
  import { normalizeOptionalString as normalizeOptionalString2, buildCheckoutAttachmentPreviewUrl as buildCheckoutAttachmentPreviewUrl2 } from "./client-utils.js";
74
+ function normalizeJsonRecord(value) {
75
+ if (!value) return {};
76
+ if (typeof value === "object" && !Array.isArray(value)) return value;
77
+ if (typeof value !== "string") return {};
78
+ try {
79
+ const parsed = JSON.parse(value);
80
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
81
+ } catch {
82
+ return {};
83
+ }
84
+ }
74
85
  function deriveConfiguredCurrencies(input) {
75
86
  const currencies = /* @__PURE__ */ new Set();
76
87
  if (input.pricingMode === "fixed" && input.fixedPriceCurrencyCode) currencies.add(input.fixedPriceCurrencyCode);
@@ -102,7 +113,7 @@ function toTemplateOrLinkMutationInput(record, overrides = {}) {
102
113
  customAmountCurrencyCode: record.customAmountCurrencyCode ?? null,
103
114
  priceListItems: record.priceListItems ?? null,
104
115
  gatewayProviderKey: record.gatewayProviderKey ?? "",
105
- gatewaySettings: record.gatewaySettings ?? {},
116
+ gatewaySettings: normalizeJsonRecord(record.gatewaySettings),
106
117
  customFieldsetCode: record.customFieldsetCode ?? null,
107
118
  collectCustomerDetails: record.collectCustomerDetails,
108
119
  customerFieldsSchema: record.customerFieldsSchema ?? [],
@@ -180,11 +191,16 @@ function normalizeCheckoutAccessSessionVersion(value) {
180
191
  if (!value) return null;
181
192
  return value instanceof Date ? value.toISOString() : value;
182
193
  }
194
+ function deriveCheckoutAccessSessionVersion(value) {
195
+ const normalized = normalizeCheckoutAccessSessionVersion(value);
196
+ if (normalized == null) return null;
197
+ return createHmac("sha256", getCheckoutAccessTokenSecret()).update(normalized).digest("base64url");
198
+ }
183
199
  function signCheckoutAccessToken(slug, options) {
184
200
  const payload = Buffer.from(JSON.stringify({
185
201
  slug,
186
202
  linkId: options?.linkId ?? null,
187
- sessionVersion: normalizeCheckoutAccessSessionVersion(options?.sessionVersion),
203
+ sessionVersion: deriveCheckoutAccessSessionVersion(options?.sessionVersion),
188
204
  exp: Date.now() + 60 * 60 * 1e3
189
205
  }), "utf-8").toString("base64url");
190
206
  const signature = createHmac("sha256", getCheckoutAccessTokenSecret()).update(payload).digest("base64url");
@@ -202,7 +218,7 @@ function verifyCheckoutAccessToken(token, slug, options) {
202
218
  if (parsed.slug !== slug || typeof parsed.exp !== "number" || parsed.exp <= Date.now()) return false;
203
219
  if (options?.linkId && parsed.linkId !== options.linkId) return false;
204
220
  if (options?.sessionVersion) {
205
- return parsed.sessionVersion === normalizeCheckoutAccessSessionVersion(options.sessionVersion);
221
+ return parsed.sessionVersion === deriveCheckoutAccessSessionVersion(options.sessionVersion);
206
222
  }
207
223
  return true;
208
224
  } catch {
@@ -232,6 +248,10 @@ function applyTerminalTransactionState(link, status) {
232
248
  usageLimitReached: status === "completed" && link.maxCompletions != null && link.completionCount >= link.maxCompletions
233
249
  };
234
250
  }
251
+ function computeConsentMarkdownHash(documentKey, markdown) {
252
+ return createHmac("sha256", getCheckoutAccessTokenSecret()).update(`${documentKey}
253
+ ${markdown}`).digest("hex");
254
+ }
235
255
  function buildConsentProof(link, acceptedLegalConsents) {
236
256
  const proof = {};
237
257
  const legalDocuments = link.legalDocuments && typeof link.legalDocuments === "object" ? link.legalDocuments : {};
@@ -244,7 +264,7 @@ function buildConsentProof(link, acceptedLegalConsents) {
244
264
  title: document.title ?? key,
245
265
  required: document.required === true,
246
266
  acceptedAt: (/* @__PURE__ */ new Date()).toISOString(),
247
- markdownHash: createHmac("sha256", key).update(document.markdown).digest("hex")
267
+ markdownHash: computeConsentMarkdownHash(key, document.markdown)
248
268
  };
249
269
  }
250
270
  return proof;
@@ -319,7 +339,7 @@ function serializeTemplateOrLink(record) {
319
339
  customAmountCurrencyCode: record.customAmountCurrencyCode ?? null,
320
340
  priceListItems: record.priceListItems ?? [],
321
341
  gatewayProviderKey: record.gatewayProviderKey ?? null,
322
- gatewaySettings: record.gatewaySettings ?? {},
342
+ gatewaySettings: normalizeJsonRecord(record.gatewaySettings),
323
343
  customFieldsetCode: record.customFieldsetCode ?? null,
324
344
  collectCustomerDetails: record.collectCustomerDetails,
325
345
  customerFieldsSchema: record.customerFieldsSchema ?? [],
@@ -382,6 +402,7 @@ export {
382
402
  applyTerminalTransactionState,
383
403
  buildCheckoutAttachmentPreviewUrl2 as buildCheckoutAttachmentPreviewUrl,
384
404
  buildConsentProof,
405
+ computeConsentMarkdownHash,
385
406
  deriveConfiguredCurrencies,
386
407
  ensureUniqueSlug,
387
408
  getCheckoutCustomerFieldSemanticType,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/checkout/lib/utils.ts"],
4
- "sourcesContent": ["import { createHmac, timingSafeEqual } from 'crypto'\nimport bcrypt from 'bcryptjs'\nimport { slugify } from '@open-mercato/shared/lib/slugify'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { normalizeCustomFieldResponse } from '@open-mercato/shared/lib/custom-fields/normalize'\nimport { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getPaymentGatewayDescriptor } from '@open-mercato/shared/modules/payment_gateways/types'\nimport { CheckoutLink, CheckoutLinkTemplate, CheckoutTransaction } from '../data/entities'\nimport { buildCheckoutAttachmentPreviewUrl, normalizeOptionalString } from './client-utils'\nimport type {\n CreateLinkInput,\n CreateTemplateInput,\n PublicSubmitInput,\n UpdateLinkInput,\n UpdateTemplateInput,\n} from '../data/validators'\nimport { CHECKOUT_TERMINAL_STATUSES } from './constants'\nexport {\n getCheckoutCustomerFieldSemanticType,\n isValidCheckoutEmail,\n isValidCheckoutPhone,\n validateCheckoutCustomerData,\n} from './customerDataValidation'\n\nexport type CheckoutScope = {\n organizationId: string\n tenantId: string\n}\n\nexport type CheckoutLinkStatus = 'draft' | 'active' | 'inactive'\n\nexport type CheckoutPayloadWithCustomFields<TInput> = {\n parsed: TInput\n customFields: Record<string, unknown>\n}\n\ntype TemplateOrLinkInput =\n | CreateTemplateInput\n | UpdateTemplateInput\n | CreateLinkInput\n | UpdateLinkInput\n\ntype TemplateOrLinkMutationInput = Omit<CreateLinkInput, 'password'> & {\n password?: string | null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction isCheckoutLinkRecord(record: CheckoutLinkTemplate | CheckoutLink): record is CheckoutLink {\n return typeof (record as { slug?: unknown }).slug === 'string'\n}\n\nfunction normalizeMaybeStringifiedJsonObject(value: unknown): Record<string, unknown> {\n if (isRecord(value)) return value\n if (typeof value !== 'string') return {}\n\n const parsed = parseDecryptedFieldValue(value)\n return isRecord(parsed) ? parsed : {}\n}\n\nexport function pickExplicitParsedOverrides<TInput extends Record<string, unknown>>(\n rawInput: unknown,\n parsed: TInput,\n): Partial<TInput> {\n if (!isRecord(rawInput)) return {}\n\n const overrides: Partial<TInput> = {}\n for (const key of Object.keys(parsed) as Array<keyof TInput>) {\n if (!Object.prototype.hasOwnProperty.call(rawInput, key)) continue\n overrides[key] = parsed[key]\n }\n\n return overrides\n}\n\nexport function requireCheckoutScope(input: { auth?: { orgId?: string | null; tenantId?: string | null } | null }): CheckoutScope {\n const organizationId = input.auth?.orgId ?? null\n const tenantId = input.auth?.tenantId ?? null\n if (!organizationId || !tenantId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n return { organizationId, tenantId }\n}\n\nexport function parseCheckoutInput<TInput>(raw: unknown, parser: (value: unknown) => TInput): CheckoutPayloadWithCustomFields<TInput> {\n const source = isRecord(raw) ? { ...raw } : {}\n const customFields = isRecord(source.customFields) ? source.customFields : {}\n delete source.customFields\n return {\n parsed: parser(source),\n customFields,\n }\n}\n\nexport function resolveLoadedCheckoutCustomFields(\n values: Record<string, unknown> | null | undefined,\n): Record<string, unknown> {\n return normalizeCustomFieldResponse(values) ?? {}\n}\n\nexport function toIsoString(value: unknown): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const parsed = new Date(value as string | number)\n return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString()\n}\n\nexport function toMoneyNumber(value: string | number | null | undefined): number | null {\n if (value == null) return null\n const numeric = Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nexport function toMoneyString(value: string | number | null | undefined): string | null {\n const numeric = toMoneyNumber(value)\n return numeric == null ? null : numeric.toFixed(2)\n}\n\nexport { normalizeOptionalString, buildCheckoutAttachmentPreviewUrl } from './client-utils'\n\nexport function deriveConfiguredCurrencies(input: TemplateOrLinkInput): string[] {\n const currencies = new Set<string>()\n if (input.pricingMode === 'fixed' && input.fixedPriceCurrencyCode) currencies.add(input.fixedPriceCurrencyCode)\n if (input.pricingMode === 'custom_amount' && input.customAmountCurrencyCode) currencies.add(input.customAmountCurrencyCode)\n if (input.pricingMode === 'price_list') {\n for (const item of input.priceListItems ?? []) currencies.add(item.currencyCode)\n }\n return Array.from(currencies)\n}\n\nexport function toTemplateOrLinkMutationInput(\n record: CheckoutLinkTemplate | CheckoutLink,\n overrides: Partial<TemplateOrLinkMutationInput> = {},\n): TemplateOrLinkMutationInput {\n return {\n name: record.name,\n title: record.title ?? null,\n subtitle: record.subtitle ?? null,\n description: record.description ?? null,\n logoAttachmentId: record.logoAttachmentId ?? null,\n logoUrl: record.logoUrl ?? null,\n primaryColor: record.primaryColor ?? null,\n secondaryColor: record.secondaryColor ?? null,\n backgroundColor: record.backgroundColor ?? null,\n themeMode: record.themeMode,\n pricingMode: record.pricingMode,\n fixedPriceAmount: toMoneyNumber(record.fixedPriceAmount),\n fixedPriceCurrencyCode: record.fixedPriceCurrencyCode ?? null,\n fixedPriceIncludesTax: record.fixedPriceIncludesTax,\n fixedPriceOriginalAmount: toMoneyNumber(record.fixedPriceOriginalAmount),\n customAmountMin: toMoneyNumber(record.customAmountMin),\n customAmountMax: toMoneyNumber(record.customAmountMax),\n customAmountCurrencyCode: record.customAmountCurrencyCode ?? null,\n priceListItems: record.priceListItems ?? null,\n gatewayProviderKey: record.gatewayProviderKey ?? '',\n gatewaySettings: record.gatewaySettings ?? {},\n customFieldsetCode: record.customFieldsetCode ?? null,\n collectCustomerDetails: record.collectCustomerDetails,\n customerFieldsSchema: (record.customerFieldsSchema ?? []) as CreateTemplateInput['customerFieldsSchema'],\n legalDocuments: (record.legalDocuments ?? undefined) as CreateTemplateInput['legalDocuments'],\n displayCustomFieldsOnPage: record.displayCustomFieldsOnPage,\n successTitle: record.successTitle ?? null,\n successMessage: record.successMessage ?? null,\n cancelTitle: record.cancelTitle ?? null,\n cancelMessage: record.cancelMessage ?? null,\n errorTitle: record.errorTitle ?? null,\n errorMessage: record.errorMessage ?? null,\n successEmailSubject: record.successEmailSubject ?? null,\n successEmailBody: record.successEmailBody ?? null,\n sendSuccessEmail: record.sendSuccessEmail,\n errorEmailSubject: record.errorEmailSubject ?? null,\n errorEmailBody: record.errorEmailBody ?? null,\n sendErrorEmail: record.sendErrorEmail,\n startEmailSubject: record.startEmailSubject ?? null,\n startEmailBody: record.startEmailBody ?? null,\n sendStartEmail: record.sendStartEmail,\n password: undefined,\n maxCompletions: record.maxCompletions ?? null,\n status: record.status,\n checkoutType: record.checkoutType,\n ...(isCheckoutLinkRecord(record) ? { slug: record.slug, templateId: record.templateId ?? null } : {}),\n ...overrides,\n }\n}\n\nexport function validateDescriptorCurrencies(providerKey: string | null | undefined, currencies: string[]): void {\n if (!providerKey || currencies.length === 0) return\n const descriptor = getPaymentGatewayDescriptor(providerKey)\n const supported = descriptor?.sessionConfig?.supportedCurrencies\n if (!descriptor || !supported || supported === '*') return\n const unsupported = currencies.filter((currency) => !supported.includes(currency))\n if (unsupported.length > 0) {\n throw new CrudHttpError(422, {\n error: `Unsupported currency for provider ${providerKey}: ${unsupported.join(', ')}`,\n })\n }\n}\n\nexport async function ensureUniqueSlug(\n em: EntityManager,\n _scope: CheckoutScope,\n requestedSlug: string | null | undefined,\n fallbackText: string,\n excludeId?: string | null,\n): Promise<string> {\n const base = slugify(requestedSlug || fallbackText || 'pay-link') || 'pay-link'\n let candidate = base\n let counter = 1\n while (true) {\n const existing = await em.findOne(CheckoutLink, {\n slug: candidate,\n deletedAt: null,\n ...(excludeId ? { id: { $ne: excludeId } } : {}),\n })\n if (!existing) return candidate\n counter += 1\n candidate = `${base}-${counter}`\n }\n}\n\nexport async function hashCheckoutPassword(password: string | null | undefined): Promise<string | null> {\n const normalized = normalizeOptionalString(password)\n if (!normalized) return null\n return bcrypt.hash(normalized, 10)\n}\n\nexport async function verifyCheckoutPassword(password: string, passwordHash: string | null | undefined): Promise<boolean> {\n if (!passwordHash) return false\n return bcrypt.compare(password, passwordHash)\n}\n\nfunction getCheckoutAccessTokenSecret(): string {\n const secret = process.env.AUTH_SECRET\n || process.env.NEXTAUTH_SECRET\n || process.env.JWT_SECRET\n || process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY\n if (!secret) {\n throw new Error(\n 'Checkout password sessions require AUTH_SECRET, NEXTAUTH_SECRET, JWT_SECRET, or TENANT_DATA_ENCRYPTION_FALLBACK_KEY',\n )\n }\n return secret\n}\n\nfunction normalizeCheckoutAccessSessionVersion(value: Date | string | null | undefined): string | null {\n if (!value) return null\n return value instanceof Date ? value.toISOString() : value\n}\n\nexport function signCheckoutAccessToken(\n slug: string,\n options?: { linkId?: string | null; sessionVersion?: Date | string | null },\n): string {\n const payload = Buffer.from(JSON.stringify({\n slug,\n linkId: options?.linkId ?? null,\n sessionVersion: normalizeCheckoutAccessSessionVersion(options?.sessionVersion),\n exp: Date.now() + (60 * 60 * 1000),\n }), 'utf-8').toString('base64url')\n const signature = createHmac('sha256', getCheckoutAccessTokenSecret()).update(payload).digest('base64url')\n return `${payload}.${signature}`\n}\n\nexport function verifyCheckoutAccessToken(\n token: string | null | undefined,\n slug: string,\n options?: { linkId?: string | null; sessionVersion?: Date | string | null },\n): boolean {\n if (!token) return false\n const [payload, signature] = token.split('.')\n if (!payload || !signature) return false\n const expected = createHmac('sha256', getCheckoutAccessTokenSecret()).update(payload).digest()\n const actual = Buffer.from(signature, 'base64url')\n if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) return false\n try {\n const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf-8')) as {\n slug?: string\n linkId?: string | null\n sessionVersion?: string | null\n exp?: number\n }\n if (parsed.slug !== slug || typeof parsed.exp !== 'number' || parsed.exp <= Date.now()) return false\n if (options?.linkId && parsed.linkId !== options.linkId) return false\n if (options?.sessionVersion) {\n return parsed.sessionVersion === normalizeCheckoutAccessSessionVersion(options.sessionVersion)\n }\n return true\n } catch {\n return false\n }\n}\n\nexport function mapGatewayStatusToCheckoutStatus(status: string | null | undefined): CheckoutTransaction['status'] {\n if (status === 'captured' || status === 'authorized') return 'completed'\n if (status === 'cancelled') return 'cancelled'\n if (status === 'expired') return 'expired'\n if (status === 'failed') return 'failed'\n return 'processing'\n}\n\nexport function isTerminalCheckoutStatus(status: string | null | undefined): boolean {\n return typeof status === 'string' && CHECKOUT_TERMINAL_STATUSES.has(status)\n}\n\nexport function isCheckoutLinkPublic(status: CheckoutLinkStatus | string | null | undefined): boolean {\n return status === 'active'\n}\n\nexport function applyTerminalTransactionState(\n link: Pick<CheckoutLink, 'activeReservationCount' | 'completionCount' | 'isLocked' | 'maxCompletions'>,\n status: CheckoutTransaction['status'],\n): { usageLimitReached: boolean } {\n link.activeReservationCount = Math.max(0, link.activeReservationCount - 1)\n if (status === 'completed') {\n link.completionCount += 1\n }\n link.isLocked = link.activeReservationCount > 0\n return {\n usageLimitReached: status === 'completed'\n && link.maxCompletions != null\n && link.completionCount >= link.maxCompletions,\n }\n}\n\nexport function buildConsentProof(link: CheckoutLink, acceptedLegalConsents: PublicSubmitInput['acceptedLegalConsents']) {\n const proof: Record<string, unknown> = {}\n const legalDocuments = link.legalDocuments && typeof link.legalDocuments === 'object'\n ? link.legalDocuments as Record<string, { title?: string; markdown?: string; required?: boolean }>\n : {}\n for (const key of ['terms', 'privacyPolicy']) {\n const document = legalDocuments[key]\n if (!document?.markdown) continue\n const accepted = acceptedLegalConsents?.[key as keyof PublicSubmitInput['acceptedLegalConsents']] === true\n if (!accepted) continue\n proof[key] = {\n title: document.title ?? key,\n required: document.required === true,\n acceptedAt: new Date().toISOString(),\n markdownHash: createHmac('sha256', key).update(document.markdown).digest('hex'),\n }\n }\n return proof\n}\n\nexport function resolveSubmittedAmount(link: CheckoutLink, input: PublicSubmitInput): { amount: number; currencyCode: string; selectedPriceItemId: string | null } {\n if (link.pricingMode === 'fixed') {\n const expected = toMoneyNumber(link.fixedPriceAmount)\n if (expected == null || !link.fixedPriceCurrencyCode) {\n throw new CrudHttpError(422, { error: 'checkout.payPage.errors.submit' })\n }\n if (input.amount != null && Number(input.amount) !== expected) {\n throw new CrudHttpError(422, { error: 'checkout.payPage.errors.submit' })\n }\n return { amount: expected, currencyCode: link.fixedPriceCurrencyCode, selectedPriceItemId: null }\n }\n if (link.pricingMode === 'custom_amount') {\n if (input.amount == null || !link.customAmountCurrencyCode) {\n throw new CrudHttpError(422, {\n error: 'checkout.payPage.validation.fixErrors',\n fieldErrors: { amount: 'checkout.payPage.validation.amountRequired' },\n })\n }\n const min = toMoneyNumber(link.customAmountMin) ?? 0\n const max = toMoneyNumber(link.customAmountMax)\n const amount = Number(input.amount)\n if (amount < min || (max != null && amount > max)) {\n throw new CrudHttpError(422, {\n error: 'checkout.payPage.validation.fixErrors',\n fieldErrors: { amount: 'checkout.payPage.errors.submit' },\n })\n }\n return { amount, currencyCode: link.customAmountCurrencyCode, selectedPriceItemId: null }\n }\n const selectedPriceItem = (link.priceListItems ?? []).find((item) => item.id === input.selectedPriceItemId)\n if (!selectedPriceItem) {\n throw new CrudHttpError(422, {\n error: 'checkout.payPage.validation.fixErrors',\n fieldErrors: { selectedPriceItemId: 'checkout.payPage.validation.priceSelectionRequired' },\n })\n }\n if (input.amount != null && Number(input.amount) !== Number(selectedPriceItem.amount)) {\n throw new CrudHttpError(422, { error: 'checkout.payPage.errors.submit' })\n }\n return {\n amount: Number(selectedPriceItem.amount),\n currencyCode: selectedPriceItem.currencyCode,\n selectedPriceItemId: selectedPriceItem.id,\n }\n}\n\nexport function serializeTemplateOrLink(record: CheckoutLinkTemplate | CheckoutLink) {\n const logoPreviewUrl = buildCheckoutAttachmentPreviewUrl(record.logoAttachmentId) ?? record.logoUrl ?? null\n return {\n id: record.id,\n name: record.name,\n title: record.title ?? null,\n subtitle: record.subtitle ?? null,\n description: record.description ?? null,\n logoAttachmentId: record.logoAttachmentId ?? null,\n logoUrl: record.logoUrl ?? null,\n logoPreviewUrl,\n primaryColor: record.primaryColor ?? null,\n secondaryColor: record.secondaryColor ?? null,\n backgroundColor: record.backgroundColor ?? null,\n themeMode: record.themeMode,\n pricingMode: record.pricingMode,\n fixedPriceAmount: toMoneyNumber(record.fixedPriceAmount),\n fixedPriceCurrencyCode: record.fixedPriceCurrencyCode ?? null,\n fixedPriceIncludesTax: record.fixedPriceIncludesTax,\n fixedPriceOriginalAmount: toMoneyNumber(record.fixedPriceOriginalAmount),\n customAmountMin: toMoneyNumber(record.customAmountMin),\n customAmountMax: toMoneyNumber(record.customAmountMax),\n customAmountCurrencyCode: record.customAmountCurrencyCode ?? null,\n priceListItems: record.priceListItems ?? [],\n gatewayProviderKey: record.gatewayProviderKey ?? null,\n gatewaySettings: record.gatewaySettings ?? {},\n customFieldsetCode: record.customFieldsetCode ?? null,\n collectCustomerDetails: record.collectCustomerDetails,\n customerFieldsSchema: record.customerFieldsSchema ?? [],\n legalDocuments: record.legalDocuments ?? {},\n displayCustomFieldsOnPage: record.displayCustomFieldsOnPage,\n successTitle: record.successTitle ?? null,\n successMessage: record.successMessage ?? null,\n cancelTitle: record.cancelTitle ?? null,\n cancelMessage: record.cancelMessage ?? null,\n errorTitle: record.errorTitle ?? null,\n errorMessage: record.errorMessage ?? null,\n successEmailSubject: record.successEmailSubject ?? null,\n successEmailBody: record.successEmailBody ?? null,\n sendSuccessEmail: record.sendSuccessEmail,\n errorEmailSubject: record.errorEmailSubject ?? null,\n errorEmailBody: record.errorEmailBody ?? null,\n sendErrorEmail: record.sendErrorEmail,\n startEmailSubject: record.startEmailSubject ?? null,\n startEmailBody: record.startEmailBody ?? null,\n sendStartEmail: record.sendStartEmail,\n maxCompletions: record.maxCompletions ?? null,\n status: record.status,\n checkoutType: record.checkoutType,\n createdAt: toIsoString(record.createdAt),\n updatedAt: toIsoString(record.updatedAt),\n ...(isCheckoutLinkRecord(record) ? {\n slug: record.slug,\n templateId: record.templateId ?? null,\n completionCount: record.completionCount,\n activeReservationCount: record.activeReservationCount,\n isLocked: record.isLocked,\n } : {}),\n }\n}\n\nexport function serializeTransaction(record: CheckoutTransaction, link?: CheckoutLink | null, includePii = false) {\n return {\n id: record.id,\n linkId: record.linkId,\n linkName: link?.name ?? null,\n linkSlug: link?.slug ?? null,\n amount: toMoneyNumber(record.amount),\n currencyCode: record.currencyCode,\n status: record.status,\n paymentStatus: record.paymentStatus ?? null,\n gatewayTransactionId: record.gatewayTransactionId ?? null,\n selectedPriceItemId: record.selectedPriceItemId ?? null,\n acceptedLegalConsents: includePii ? normalizeMaybeStringifiedJsonObject(record.acceptedLegalConsents) : null,\n customerData: includePii ? normalizeMaybeStringifiedJsonObject(record.customerData) : null,\n firstName: includePii ? (record.firstName ?? null) : null,\n lastName: includePii ? (record.lastName ?? null) : null,\n email: includePii ? (record.email ?? null) : null,\n phone: includePii ? (record.phone ?? null) : null,\n ipAddress: includePii ? (record.ipAddress ?? null) : null,\n userAgent: includePii ? (record.userAgent ?? null) : null,\n createdAt: toIsoString(record.createdAt),\n updatedAt: toIsoString(record.updatedAt),\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { createHmac, timingSafeEqual } from 'crypto'\nimport bcrypt from 'bcryptjs'\nimport { slugify } from '@open-mercato/shared/lib/slugify'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { normalizeCustomFieldResponse } from '@open-mercato/shared/lib/custom-fields/normalize'\nimport { parseDecryptedFieldValue } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { getPaymentGatewayDescriptor } from '@open-mercato/shared/modules/payment_gateways/types'\nimport { CheckoutLink, CheckoutLinkTemplate, CheckoutTransaction } from '../data/entities'\nimport { buildCheckoutAttachmentPreviewUrl, normalizeOptionalString } from './client-utils'\nimport type {\n CreateLinkInput,\n CreateTemplateInput,\n PublicSubmitInput,\n UpdateLinkInput,\n UpdateTemplateInput,\n} from '../data/validators'\nimport { CHECKOUT_TERMINAL_STATUSES } from './constants'\nexport {\n getCheckoutCustomerFieldSemanticType,\n isValidCheckoutEmail,\n isValidCheckoutPhone,\n validateCheckoutCustomerData,\n} from './customerDataValidation'\n\nexport type CheckoutScope = {\n organizationId: string\n tenantId: string\n}\n\nexport type CheckoutLinkStatus = 'draft' | 'active' | 'inactive'\n\nexport type CheckoutPayloadWithCustomFields<TInput> = {\n parsed: TInput\n customFields: Record<string, unknown>\n}\n\ntype TemplateOrLinkInput =\n | CreateTemplateInput\n | UpdateTemplateInput\n | CreateLinkInput\n | UpdateLinkInput\n\ntype TemplateOrLinkMutationInput = Omit<CreateLinkInput, 'password'> & {\n password?: string | null\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === 'object' && !Array.isArray(value)\n}\n\nfunction isCheckoutLinkRecord(record: CheckoutLinkTemplate | CheckoutLink): record is CheckoutLink {\n return typeof (record as { slug?: unknown }).slug === 'string'\n}\n\nfunction normalizeMaybeStringifiedJsonObject(value: unknown): Record<string, unknown> {\n if (isRecord(value)) return value\n if (typeof value !== 'string') return {}\n\n const parsed = parseDecryptedFieldValue(value)\n return isRecord(parsed) ? parsed : {}\n}\n\nexport function pickExplicitParsedOverrides<TInput extends Record<string, unknown>>(\n rawInput: unknown,\n parsed: TInput,\n): Partial<TInput> {\n if (!isRecord(rawInput)) return {}\n\n const overrides: Partial<TInput> = {}\n for (const key of Object.keys(parsed) as Array<keyof TInput>) {\n if (!Object.prototype.hasOwnProperty.call(rawInput, key)) continue\n overrides[key] = parsed[key]\n }\n\n return overrides\n}\n\nexport function requireCheckoutScope(input: { auth?: { orgId?: string | null; tenantId?: string | null } | null }): CheckoutScope {\n const organizationId = input.auth?.orgId ?? null\n const tenantId = input.auth?.tenantId ?? null\n if (!organizationId || !tenantId) {\n throw new CrudHttpError(401, { error: 'Unauthorized' })\n }\n return { organizationId, tenantId }\n}\n\nexport function parseCheckoutInput<TInput>(raw: unknown, parser: (value: unknown) => TInput): CheckoutPayloadWithCustomFields<TInput> {\n const source = isRecord(raw) ? { ...raw } : {}\n const customFields = isRecord(source.customFields) ? source.customFields : {}\n delete source.customFields\n return {\n parsed: parser(source),\n customFields,\n }\n}\n\nexport function resolveLoadedCheckoutCustomFields(\n values: Record<string, unknown> | null | undefined,\n): Record<string, unknown> {\n return normalizeCustomFieldResponse(values) ?? {}\n}\n\nexport function toIsoString(value: unknown): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const parsed = new Date(value as string | number)\n return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString()\n}\n\nexport function toMoneyNumber(value: string | number | null | undefined): number | null {\n if (value == null) return null\n const numeric = Number(value)\n return Number.isFinite(numeric) ? numeric : null\n}\n\nexport function toMoneyString(value: string | number | null | undefined): string | null {\n const numeric = toMoneyNumber(value)\n return numeric == null ? null : numeric.toFixed(2)\n}\n\nexport { normalizeOptionalString, buildCheckoutAttachmentPreviewUrl } from './client-utils'\n\nfunction normalizeJsonRecord(value: unknown): Record<string, unknown> {\n if (!value) return {}\n if (typeof value === 'object' && !Array.isArray(value)) return value as Record<string, unknown>\n if (typeof value !== 'string') return {}\n try {\n const parsed = JSON.parse(value)\n return parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : {}\n } catch {\n return {}\n }\n}\n\nexport function deriveConfiguredCurrencies(input: TemplateOrLinkInput): string[] {\n const currencies = new Set<string>()\n if (input.pricingMode === 'fixed' && input.fixedPriceCurrencyCode) currencies.add(input.fixedPriceCurrencyCode)\n if (input.pricingMode === 'custom_amount' && input.customAmountCurrencyCode) currencies.add(input.customAmountCurrencyCode)\n if (input.pricingMode === 'price_list') {\n for (const item of input.priceListItems ?? []) currencies.add(item.currencyCode)\n }\n return Array.from(currencies)\n}\n\nexport function toTemplateOrLinkMutationInput(\n record: CheckoutLinkTemplate | CheckoutLink,\n overrides: Partial<TemplateOrLinkMutationInput> = {},\n): TemplateOrLinkMutationInput {\n return {\n name: record.name,\n title: record.title ?? null,\n subtitle: record.subtitle ?? null,\n description: record.description ?? null,\n logoAttachmentId: record.logoAttachmentId ?? null,\n logoUrl: record.logoUrl ?? null,\n primaryColor: record.primaryColor ?? null,\n secondaryColor: record.secondaryColor ?? null,\n backgroundColor: record.backgroundColor ?? null,\n themeMode: record.themeMode,\n pricingMode: record.pricingMode,\n fixedPriceAmount: toMoneyNumber(record.fixedPriceAmount),\n fixedPriceCurrencyCode: record.fixedPriceCurrencyCode ?? null,\n fixedPriceIncludesTax: record.fixedPriceIncludesTax,\n fixedPriceOriginalAmount: toMoneyNumber(record.fixedPriceOriginalAmount),\n customAmountMin: toMoneyNumber(record.customAmountMin),\n customAmountMax: toMoneyNumber(record.customAmountMax),\n customAmountCurrencyCode: record.customAmountCurrencyCode ?? null,\n priceListItems: record.priceListItems ?? null,\n gatewayProviderKey: record.gatewayProviderKey ?? '',\n gatewaySettings: normalizeJsonRecord(record.gatewaySettings),\n customFieldsetCode: record.customFieldsetCode ?? null,\n collectCustomerDetails: record.collectCustomerDetails,\n customerFieldsSchema: (record.customerFieldsSchema ?? []) as CreateTemplateInput['customerFieldsSchema'],\n legalDocuments: (record.legalDocuments ?? undefined) as CreateTemplateInput['legalDocuments'],\n displayCustomFieldsOnPage: record.displayCustomFieldsOnPage,\n successTitle: record.successTitle ?? null,\n successMessage: record.successMessage ?? null,\n cancelTitle: record.cancelTitle ?? null,\n cancelMessage: record.cancelMessage ?? null,\n errorTitle: record.errorTitle ?? null,\n errorMessage: record.errorMessage ?? null,\n successEmailSubject: record.successEmailSubject ?? null,\n successEmailBody: record.successEmailBody ?? null,\n sendSuccessEmail: record.sendSuccessEmail,\n errorEmailSubject: record.errorEmailSubject ?? null,\n errorEmailBody: record.errorEmailBody ?? null,\n sendErrorEmail: record.sendErrorEmail,\n startEmailSubject: record.startEmailSubject ?? null,\n startEmailBody: record.startEmailBody ?? null,\n sendStartEmail: record.sendStartEmail,\n password: undefined,\n maxCompletions: record.maxCompletions ?? null,\n status: record.status,\n checkoutType: record.checkoutType,\n ...(isCheckoutLinkRecord(record) ? { slug: record.slug, templateId: record.templateId ?? null } : {}),\n ...overrides,\n }\n}\n\nexport function validateDescriptorCurrencies(providerKey: string | null | undefined, currencies: string[]): void {\n if (!providerKey || currencies.length === 0) return\n const descriptor = getPaymentGatewayDescriptor(providerKey)\n const supported = descriptor?.sessionConfig?.supportedCurrencies\n if (!descriptor || !supported || supported === '*') return\n const unsupported = currencies.filter((currency) => !supported.includes(currency))\n if (unsupported.length > 0) {\n throw new CrudHttpError(422, {\n error: `Unsupported currency for provider ${providerKey}: ${unsupported.join(', ')}`,\n })\n }\n}\n\nexport async function ensureUniqueSlug(\n em: EntityManager,\n _scope: CheckoutScope,\n requestedSlug: string | null | undefined,\n fallbackText: string,\n excludeId?: string | null,\n): Promise<string> {\n const base = slugify(requestedSlug || fallbackText || 'pay-link') || 'pay-link'\n let candidate = base\n let counter = 1\n while (true) {\n const existing = await em.findOne(CheckoutLink, {\n slug: candidate,\n deletedAt: null,\n ...(excludeId ? { id: { $ne: excludeId } } : {}),\n })\n if (!existing) return candidate\n counter += 1\n candidate = `${base}-${counter}`\n }\n}\n\nexport async function hashCheckoutPassword(password: string | null | undefined): Promise<string | null> {\n const normalized = normalizeOptionalString(password)\n if (!normalized) return null\n return bcrypt.hash(normalized, 10)\n}\n\nexport async function verifyCheckoutPassword(password: string, passwordHash: string | null | undefined): Promise<boolean> {\n if (!passwordHash) return false\n return bcrypt.compare(password, passwordHash)\n}\n\nfunction getCheckoutAccessTokenSecret(): string {\n const secret = process.env.AUTH_SECRET\n || process.env.NEXTAUTH_SECRET\n || process.env.JWT_SECRET\n || process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY\n if (!secret) {\n throw new Error(\n 'Checkout password sessions require AUTH_SECRET, NEXTAUTH_SECRET, JWT_SECRET, or TENANT_DATA_ENCRYPTION_FALLBACK_KEY',\n )\n }\n return secret\n}\n\nfunction normalizeCheckoutAccessSessionVersion(value: Date | string | null | undefined): string | null {\n if (!value) return null\n return value instanceof Date ? value.toISOString() : value\n}\n\n// Derive a non-reversible cookie-safe representation of `sessionVersion`\n// so the embedded payload never exposes the raw input (typically the\n// checkout link's bcrypt passwordHash \u2014 see #2675). The HMAC keeps\n// rotation semantics intact: any change to the input produces a fresh\n// digest, invalidating outstanding cookies.\nfunction deriveCheckoutAccessSessionVersion(value: Date | string | null | undefined): string | null {\n const normalized = normalizeCheckoutAccessSessionVersion(value)\n if (normalized == null) return null\n return createHmac('sha256', getCheckoutAccessTokenSecret()).update(normalized).digest('base64url')\n}\n\nexport function signCheckoutAccessToken(\n slug: string,\n options?: { linkId?: string | null; sessionVersion?: Date | string | null },\n): string {\n const payload = Buffer.from(JSON.stringify({\n slug,\n linkId: options?.linkId ?? null,\n sessionVersion: deriveCheckoutAccessSessionVersion(options?.sessionVersion),\n exp: Date.now() + (60 * 60 * 1000),\n }), 'utf-8').toString('base64url')\n const signature = createHmac('sha256', getCheckoutAccessTokenSecret()).update(payload).digest('base64url')\n return `${payload}.${signature}`\n}\n\nexport function verifyCheckoutAccessToken(\n token: string | null | undefined,\n slug: string,\n options?: { linkId?: string | null; sessionVersion?: Date | string | null },\n): boolean {\n if (!token) return false\n const [payload, signature] = token.split('.')\n if (!payload || !signature) return false\n const expected = createHmac('sha256', getCheckoutAccessTokenSecret()).update(payload).digest()\n const actual = Buffer.from(signature, 'base64url')\n if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) return false\n try {\n const parsed = JSON.parse(Buffer.from(payload, 'base64url').toString('utf-8')) as {\n slug?: string\n linkId?: string | null\n sessionVersion?: string | null\n exp?: number\n }\n if (parsed.slug !== slug || typeof parsed.exp !== 'number' || parsed.exp <= Date.now()) return false\n if (options?.linkId && parsed.linkId !== options.linkId) return false\n if (options?.sessionVersion) {\n return parsed.sessionVersion === deriveCheckoutAccessSessionVersion(options.sessionVersion)\n }\n return true\n } catch {\n return false\n }\n}\n\nexport function mapGatewayStatusToCheckoutStatus(status: string | null | undefined): CheckoutTransaction['status'] {\n if (status === 'captured' || status === 'authorized') return 'completed'\n if (status === 'cancelled') return 'cancelled'\n if (status === 'expired') return 'expired'\n if (status === 'failed') return 'failed'\n return 'processing'\n}\n\nexport function isTerminalCheckoutStatus(status: string | null | undefined): boolean {\n return typeof status === 'string' && CHECKOUT_TERMINAL_STATUSES.has(status)\n}\n\nexport function isCheckoutLinkPublic(status: CheckoutLinkStatus | string | null | undefined): boolean {\n return status === 'active'\n}\n\nexport function applyTerminalTransactionState(\n link: Pick<CheckoutLink, 'activeReservationCount' | 'completionCount' | 'isLocked' | 'maxCompletions'>,\n status: CheckoutTransaction['status'],\n): { usageLimitReached: boolean } {\n link.activeReservationCount = Math.max(0, link.activeReservationCount - 1)\n if (status === 'completed') {\n link.completionCount += 1\n }\n link.isLocked = link.activeReservationCount > 0\n return {\n usageLimitReached: status === 'completed'\n && link.maxCompletions != null\n && link.completionCount >= link.maxCompletions,\n }\n}\n\n// Security model: the consent proof is a GDPR/consent audit trail, so `markdownHash`\n// must be tamper-evident. It is an HMAC keyed with the server-side checkout secret\n// (never a public constant), which an attacker who can edit a stored consent row\n// cannot recompute without the secret. The document key is folded into the HMAC\n// message to namespace each document while keeping a single server-held key.\nexport function computeConsentMarkdownHash(documentKey: string, markdown: string): string {\n return createHmac('sha256', getCheckoutAccessTokenSecret())\n .update(`${documentKey}\\n${markdown}`)\n .digest('hex')\n}\n\nexport function buildConsentProof(link: CheckoutLink, acceptedLegalConsents: PublicSubmitInput['acceptedLegalConsents']) {\n const proof: Record<string, unknown> = {}\n const legalDocuments = link.legalDocuments && typeof link.legalDocuments === 'object'\n ? link.legalDocuments as Record<string, { title?: string; markdown?: string; required?: boolean }>\n : {}\n for (const key of ['terms', 'privacyPolicy']) {\n const document = legalDocuments[key]\n if (!document?.markdown) continue\n const accepted = acceptedLegalConsents?.[key as keyof PublicSubmitInput['acceptedLegalConsents']] === true\n if (!accepted) continue\n proof[key] = {\n title: document.title ?? key,\n required: document.required === true,\n acceptedAt: new Date().toISOString(),\n markdownHash: computeConsentMarkdownHash(key, document.markdown),\n }\n }\n return proof\n}\n\nexport function resolveSubmittedAmount(link: CheckoutLink, input: PublicSubmitInput): { amount: number; currencyCode: string; selectedPriceItemId: string | null } {\n if (link.pricingMode === 'fixed') {\n const expected = toMoneyNumber(link.fixedPriceAmount)\n if (expected == null || !link.fixedPriceCurrencyCode) {\n throw new CrudHttpError(422, { error: 'checkout.payPage.errors.submit' })\n }\n if (input.amount != null && Number(input.amount) !== expected) {\n throw new CrudHttpError(422, { error: 'checkout.payPage.errors.submit' })\n }\n return { amount: expected, currencyCode: link.fixedPriceCurrencyCode, selectedPriceItemId: null }\n }\n if (link.pricingMode === 'custom_amount') {\n if (input.amount == null || !link.customAmountCurrencyCode) {\n throw new CrudHttpError(422, {\n error: 'checkout.payPage.validation.fixErrors',\n fieldErrors: { amount: 'checkout.payPage.validation.amountRequired' },\n })\n }\n const min = toMoneyNumber(link.customAmountMin) ?? 0\n const max = toMoneyNumber(link.customAmountMax)\n const amount = Number(input.amount)\n if (amount < min || (max != null && amount > max)) {\n throw new CrudHttpError(422, {\n error: 'checkout.payPage.validation.fixErrors',\n fieldErrors: { amount: 'checkout.payPage.errors.submit' },\n })\n }\n return { amount, currencyCode: link.customAmountCurrencyCode, selectedPriceItemId: null }\n }\n const selectedPriceItem = (link.priceListItems ?? []).find((item) => item.id === input.selectedPriceItemId)\n if (!selectedPriceItem) {\n throw new CrudHttpError(422, {\n error: 'checkout.payPage.validation.fixErrors',\n fieldErrors: { selectedPriceItemId: 'checkout.payPage.validation.priceSelectionRequired' },\n })\n }\n if (input.amount != null && Number(input.amount) !== Number(selectedPriceItem.amount)) {\n throw new CrudHttpError(422, { error: 'checkout.payPage.errors.submit' })\n }\n return {\n amount: Number(selectedPriceItem.amount),\n currencyCode: selectedPriceItem.currencyCode,\n selectedPriceItemId: selectedPriceItem.id,\n }\n}\n\nexport function serializeTemplateOrLink(record: CheckoutLinkTemplate | CheckoutLink) {\n const logoPreviewUrl = buildCheckoutAttachmentPreviewUrl(record.logoAttachmentId) ?? record.logoUrl ?? null\n return {\n id: record.id,\n name: record.name,\n title: record.title ?? null,\n subtitle: record.subtitle ?? null,\n description: record.description ?? null,\n logoAttachmentId: record.logoAttachmentId ?? null,\n logoUrl: record.logoUrl ?? null,\n logoPreviewUrl,\n primaryColor: record.primaryColor ?? null,\n secondaryColor: record.secondaryColor ?? null,\n backgroundColor: record.backgroundColor ?? null,\n themeMode: record.themeMode,\n pricingMode: record.pricingMode,\n fixedPriceAmount: toMoneyNumber(record.fixedPriceAmount),\n fixedPriceCurrencyCode: record.fixedPriceCurrencyCode ?? null,\n fixedPriceIncludesTax: record.fixedPriceIncludesTax,\n fixedPriceOriginalAmount: toMoneyNumber(record.fixedPriceOriginalAmount),\n customAmountMin: toMoneyNumber(record.customAmountMin),\n customAmountMax: toMoneyNumber(record.customAmountMax),\n customAmountCurrencyCode: record.customAmountCurrencyCode ?? null,\n priceListItems: record.priceListItems ?? [],\n gatewayProviderKey: record.gatewayProviderKey ?? null,\n gatewaySettings: normalizeJsonRecord(record.gatewaySettings),\n customFieldsetCode: record.customFieldsetCode ?? null,\n collectCustomerDetails: record.collectCustomerDetails,\n customerFieldsSchema: record.customerFieldsSchema ?? [],\n legalDocuments: record.legalDocuments ?? {},\n displayCustomFieldsOnPage: record.displayCustomFieldsOnPage,\n successTitle: record.successTitle ?? null,\n successMessage: record.successMessage ?? null,\n cancelTitle: record.cancelTitle ?? null,\n cancelMessage: record.cancelMessage ?? null,\n errorTitle: record.errorTitle ?? null,\n errorMessage: record.errorMessage ?? null,\n successEmailSubject: record.successEmailSubject ?? null,\n successEmailBody: record.successEmailBody ?? null,\n sendSuccessEmail: record.sendSuccessEmail,\n errorEmailSubject: record.errorEmailSubject ?? null,\n errorEmailBody: record.errorEmailBody ?? null,\n sendErrorEmail: record.sendErrorEmail,\n startEmailSubject: record.startEmailSubject ?? null,\n startEmailBody: record.startEmailBody ?? null,\n sendStartEmail: record.sendStartEmail,\n maxCompletions: record.maxCompletions ?? null,\n status: record.status,\n checkoutType: record.checkoutType,\n createdAt: toIsoString(record.createdAt),\n updatedAt: toIsoString(record.updatedAt),\n ...(isCheckoutLinkRecord(record) ? {\n slug: record.slug,\n templateId: record.templateId ?? null,\n completionCount: record.completionCount,\n activeReservationCount: record.activeReservationCount,\n isLocked: record.isLocked,\n } : {}),\n }\n}\n\nexport function serializeTransaction(record: CheckoutTransaction, link?: CheckoutLink | null, includePii = false) {\n return {\n id: record.id,\n linkId: record.linkId,\n linkName: link?.name ?? null,\n linkSlug: link?.slug ?? null,\n amount: toMoneyNumber(record.amount),\n currencyCode: record.currencyCode,\n status: record.status,\n paymentStatus: record.paymentStatus ?? null,\n gatewayTransactionId: record.gatewayTransactionId ?? null,\n selectedPriceItemId: record.selectedPriceItemId ?? null,\n acceptedLegalConsents: includePii ? normalizeMaybeStringifiedJsonObject(record.acceptedLegalConsents) : null,\n customerData: includePii ? normalizeMaybeStringifiedJsonObject(record.customerData) : null,\n firstName: includePii ? (record.firstName ?? null) : null,\n lastName: includePii ? (record.lastName ?? null) : null,\n email: includePii ? (record.email ?? null) : null,\n phone: includePii ? (record.phone ?? null) : null,\n ipAddress: includePii ? (record.ipAddress ?? null) : null,\n userAgent: includePii ? (record.userAgent ?? null) : null,\n createdAt: toIsoString(record.createdAt),\n updatedAt: toIsoString(record.updatedAt),\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["normalizeOptionalString", "buildCheckoutAttachmentPreviewUrl"]
7
7
  }
package/jest.config.cjs CHANGED
@@ -9,6 +9,8 @@ module.exports = {
9
9
  moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
10
10
  moduleNameMapper: {
11
11
  '^@open-mercato/checkout/(.*)$': '<rootDir>/src/$1',
12
+ '^@open-mercato/cache$': '<rootDir>/../cache/src/index.ts',
13
+ '^@open-mercato/cache/(.*)$': '<rootDir>/../cache/src/$1',
12
14
  '^@open-mercato/core/(.*)$': '<rootDir>/../core/src/$1',
13
15
  '^@open-mercato/shared/(.*)$': '<rootDir>/../shared/src/$1',
14
16
  '^@open-mercato/queue/(.*)$': '<rootDir>/../queue/src/$1',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/checkout",
3
- "version": "0.6.4-develop.4382.1.6b4f656b77",
3
+ "version": "0.6.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {
@@ -61,20 +61,20 @@
61
61
  }
62
62
  },
63
63
  "dependencies": {
64
- "@open-mercato/core": "0.6.4-develop.4382.1.6b4f656b77",
65
- "@open-mercato/ui": "0.6.4-develop.4382.1.6b4f656b77",
64
+ "@open-mercato/core": "0.6.4",
65
+ "@open-mercato/ui": "0.6.4",
66
66
  "bcryptjs": "^3.0.3"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "@mikro-orm/postgresql": "^7.0.14",
70
- "@open-mercato/shared": "0.6.4-develop.4382.1.6b4f656b77",
70
+ "@open-mercato/shared": "0.6.4",
71
71
  "react": "^19.0.0",
72
72
  "react-dom": "^19.0.0"
73
73
  },
74
74
  "devDependencies": {
75
- "@open-mercato/shared": "0.6.4-develop.4382.1.6b4f656b77",
75
+ "@open-mercato/shared": "0.6.4",
76
76
  "@types/jest": "^30.0.0",
77
- "@types/react": "^19.2.16",
77
+ "@types/react": "^19.2.17",
78
78
  "@types/react-dom": "^19.2.3",
79
79
  "esbuild": "^0.28.0",
80
80
  "glob": "^13.0.6",
@@ -90,6 +90,5 @@
90
90
  "type": "git",
91
91
  "url": "https://github.com/open-mercato/open-mercato",
92
92
  "directory": "packages/checkout"
93
- },
94
- "stableVersion": "0.6.3"
93
+ }
95
94
  }
@@ -0,0 +1,158 @@
1
+ import { expect, test } from '@playwright/test'
2
+ import { getAuthToken } from '@open-mercato/core/modules/core/__integration__/helpers/api'
3
+ import {
4
+ assertScalarFieldsPersisted,
5
+ skipIfCrudFormExtensionTestsDisabled,
6
+ } from '@open-mercato/core/helpers/integration/crudFormPersistence'
7
+ import {
8
+ createPriceListTemplateInput,
9
+ createTemplateFixture,
10
+ deleteCheckoutEntityIfExists,
11
+ readTemplate,
12
+ updateTemplate,
13
+ type CheckoutLinkInput,
14
+ } from './helpers/fixtures'
15
+
16
+ /**
17
+ * TC-CHK-CRUDFORM-001: pay-link template CrudForm persists scalars, a price-list array
18
+ * + custom fields (#2466 / #2566).
19
+ *
20
+ * The checkout template surface is hand-written (command bus + bespoke serializer), so it
21
+ * does NOT fit `runCrudFormRoundTrip` from the sweep harness:
22
+ * - writes go through the collection POST (`/api/checkout/templates`) but updates/deletes go
23
+ * through the RESTful detail route (`/api/checkout/templates/[id]`), not a `?id=` collection
24
+ * route;
25
+ * - the serializer returns camelCase fields (not the makeCrud snake_case shape);
26
+ * - custom fields come back as a top-level `customFields` object (not an array / `customValues`).
27
+ *
28
+ * This spec therefore drives the canonical create → read-back → assert → update → read-back →
29
+ * assert → delete cycle inline using the checkout integration fixtures, while reusing the sweep
30
+ * harness gate (`skipIfCrudFormExtensionTestsDisabled`) and scalar assertion helper. It proves
31
+ * every field type the template CrudForm edits round-trips: string scalars, enums (pricingMode,
32
+ * status), booleans, an integer, the `priceListItems` array, and default-seeded custom fields.
33
+ *
34
+ * Verified contract:
35
+ * - Read-back uses the detail GET (the list route does not filter by `?ids=`/`?id=`).
36
+ * - Custom fields submit as a `customFields` object and return under `record.customFields`.
37
+ * - PUT is a partial update — omitted custom fields are retained.
38
+ * - Self-contained: custom-field definitions are seeded by `seedDefaults`; the template is the
39
+ * only fixture and is deleted in `finally`.
40
+ *
41
+ * Gated by `OM_INTEGRATION_CRUDFORM_EXTENSION_TESTS_DISABLED` (default off → runs).
42
+ */
43
+ test.describe('TC-CHK-CRUDFORM-001: pay-link template CrudForm persists scalars, array + custom fields', () => {
44
+ test.beforeAll(() => {
45
+ skipIfCrudFormExtensionTestsDisabled()
46
+ })
47
+
48
+ test('round-trips scalars, priceListItems array, and custom fields on create and update', async ({ request }) => {
49
+ const token = await getAuthToken(request)
50
+ const stamp = Date.now()
51
+ let templateId: string | null = null
52
+
53
+ try {
54
+ const createInput: CheckoutLinkInput = createPriceListTemplateInput({
55
+ name: `QA CRUDFORM Template ${stamp}`,
56
+ title: `QA CRUDFORM Template Title ${stamp}`,
57
+ subtitle: 'Original template subtitle',
58
+ description: 'Original template description',
59
+ priceListItems: [
60
+ { id: 'tier-basic', description: 'Basic tier', amount: 19.99, currencyCode: 'USD' },
61
+ { id: 'tier-pro', description: 'Pro tier', amount: 49.99, currencyCode: 'USD' },
62
+ ],
63
+ collectCustomerDetails: true,
64
+ displayCustomFieldsOnPage: true,
65
+ customFieldsetCode: 'service_package',
66
+ maxCompletions: 25,
67
+ status: 'draft',
68
+ customFields: {
69
+ service_deliverables: 'Discovery workshop and implementation memo',
70
+ delivery_timeline: 'Within 5 business days',
71
+ support_contact: 'ops@example.test',
72
+ },
73
+ })
74
+ templateId = await createTemplateFixture(request, token, createInput)
75
+
76
+ const afterCreate = await readTemplate(request, token, templateId)
77
+ assertScalarFieldsPersisted(
78
+ afterCreate,
79
+ {
80
+ name: `QA CRUDFORM Template ${stamp}`,
81
+ title: `QA CRUDFORM Template Title ${stamp}`,
82
+ subtitle: 'Original template subtitle',
83
+ description: 'Original template description',
84
+ pricingMode: 'price_list',
85
+ priceListItems: [
86
+ { id: 'tier-basic', description: 'Basic tier', amount: 19.99, currencyCode: 'USD' },
87
+ { id: 'tier-pro', description: 'Pro tier', amount: 49.99, currencyCode: 'USD' },
88
+ ],
89
+ collectCustomerDetails: true,
90
+ displayCustomFieldsOnPage: true,
91
+ customFieldsetCode: 'service_package',
92
+ maxCompletions: 25,
93
+ status: 'draft',
94
+ },
95
+ 'after-create',
96
+ )
97
+ expect(afterCreate.customFields, 'after-create custom fields should persist').toMatchObject({
98
+ service_deliverables: 'Discovery workshop and implementation memo',
99
+ delivery_timeline: 'Within 5 business days',
100
+ support_contact: 'ops@example.test',
101
+ })
102
+
103
+ const updatePayload: Partial<CheckoutLinkInput> = {
104
+ name: `QA CRUDFORM Template ${stamp} EDITED`,
105
+ title: `QA CRUDFORM Template Title ${stamp} EDITED`,
106
+ subtitle: 'Updated template subtitle',
107
+ description: 'Updated template description',
108
+ pricingMode: 'price_list',
109
+ priceListItems: [
110
+ { id: 'tier-solo', description: 'Solo tier', amount: 99, currencyCode: 'USD' },
111
+ ],
112
+ gatewayProviderKey: 'mock',
113
+ collectCustomerDetails: false,
114
+ displayCustomFieldsOnPage: false,
115
+ customFieldsetCode: 'service_package',
116
+ maxCompletions: 50,
117
+ status: 'active',
118
+ customFields: {
119
+ delivery_timeline: 'Within 2 business days',
120
+ session_format: 'Remote video call',
121
+ },
122
+ }
123
+ const updateResponse = await updateTemplate(request, token, templateId, updatePayload)
124
+ expect(
125
+ updateResponse.ok(),
126
+ `update template failed: ${updateResponse.status()}`,
127
+ ).toBeTruthy()
128
+
129
+ const afterUpdate = await readTemplate(request, token, templateId)
130
+ assertScalarFieldsPersisted(
131
+ afterUpdate,
132
+ {
133
+ name: `QA CRUDFORM Template ${stamp} EDITED`,
134
+ title: `QA CRUDFORM Template Title ${stamp} EDITED`,
135
+ subtitle: 'Updated template subtitle',
136
+ description: 'Updated template description',
137
+ pricingMode: 'price_list',
138
+ priceListItems: [
139
+ { id: 'tier-solo', description: 'Solo tier', amount: 99, currencyCode: 'USD' },
140
+ ],
141
+ collectCustomerDetails: false,
142
+ displayCustomFieldsOnPage: false,
143
+ maxCompletions: 50,
144
+ status: 'active',
145
+ },
146
+ 'after-update',
147
+ )
148
+ expect(afterUpdate.customFields, 'after-update custom fields should persist + retain omitted keys').toMatchObject({
149
+ delivery_timeline: 'Within 2 business days',
150
+ session_format: 'Remote video call',
151
+ service_deliverables: 'Discovery workshop and implementation memo',
152
+ support_contact: 'ops@example.test',
153
+ })
154
+ } finally {
155
+ await deleteCheckoutEntityIfExists(request, token, 'templates', templateId)
156
+ }
157
+ })
158
+ })