@open-mercato/checkout 0.6.8-develop.6903.1.0ec850a22b → 0.6.8-develop.6908.1.4792c7717e

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.
@@ -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\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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",
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\n/**\n * Generates a unique checkout link slug.\n *\n * NOTE: The slug uniqueness check runs GLOBALLY against all checkout links, and the\n * `scope` parameter is intentionally ignored. This is required because the public-facing\n * checkout routes (e.g., `/pay/[slug]`) do not have any tenant/organization discriminator\n * in their URL path, resolving links solely by the slug. Consequently, duplicate slugs\n * cannot exist across different tenants.\n *\n * @param em MikroORM EntityManager\n * @param _scope Tenant and organization scope (ignored in database query)\n * @param requestedSlug The slug requested by the user\n * @param fallbackText The title/name used to generate the slug if requestedSlug is empty\n * @param excludeId Optional ID of the checkout link to exclude from the duplicate check\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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/checkout",
3
- "version": "0.6.8-develop.6903.1.0ec850a22b",
3
+ "version": "0.6.8-develop.6908.1.4792c7717e",
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.8-develop.6903.1.0ec850a22b",
66
- "@open-mercato/ui": "0.6.8-develop.6903.1.0ec850a22b",
65
+ "@open-mercato/core": "0.6.8-develop.6908.1.4792c7717e",
66
+ "@open-mercato/ui": "0.6.8-develop.6908.1.4792c7717e",
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.8-develop.6903.1.0ec850a22b",
71
+ "@open-mercato/shared": "0.6.8-develop.6908.1.4792c7717e",
72
72
  "react": "^19.0.0",
73
73
  "react-dom": "^19.0.0"
74
74
  },
75
75
  "devDependencies": {
76
- "@open-mercato/shared": "0.6.8-develop.6903.1.0ec850a22b",
76
+ "@open-mercato/shared": "0.6.8-develop.6908.1.4792c7717e",
77
77
  "@types/jest": "^30.0.0",
78
78
  "@types/react": "^19.2.17",
79
79
  "@types/react-dom": "^19.2.3",
@@ -213,6 +213,21 @@ export function validateDescriptorCurrencies(providerKey: string | null | undefi
213
213
  }
214
214
  }
215
215
 
216
+ /**
217
+ * Generates a unique checkout link slug.
218
+ *
219
+ * NOTE: The slug uniqueness check runs GLOBALLY against all checkout links, and the
220
+ * `scope` parameter is intentionally ignored. This is required because the public-facing
221
+ * checkout routes (e.g., `/pay/[slug]`) do not have any tenant/organization discriminator
222
+ * in their URL path, resolving links solely by the slug. Consequently, duplicate slugs
223
+ * cannot exist across different tenants.
224
+ *
225
+ * @param em MikroORM EntityManager
226
+ * @param _scope Tenant and organization scope (ignored in database query)
227
+ * @param requestedSlug The slug requested by the user
228
+ * @param fallbackText The title/name used to generate the slug if requestedSlug is empty
229
+ * @param excludeId Optional ID of the checkout link to exclude from the duplicate check
230
+ */
216
231
  export async function ensureUniqueSlug(
217
232
  em: EntityManager,
218
233
  _scope: CheckoutScope,