@open-mercato/checkout 0.6.7 → 0.6.8-develop.6874.1.982d6097d8
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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/checkout/__integration__/TC-CHKT-042.spec.js +39 -0
- package/dist/modules/checkout/__integration__/TC-CHKT-042.spec.js.map +7 -0
- package/dist/modules/checkout/__integration__/TC-CHKT-043-concurrent-status-update.spec.js +62 -0
- package/dist/modules/checkout/__integration__/TC-CHKT-043-concurrent-status-update.spec.js.map +7 -0
- package/dist/modules/checkout/api/pay/[slug]/submit/route.js +43 -28
- package/dist/modules/checkout/api/pay/[slug]/submit/route.js.map +2 -2
- package/dist/modules/checkout/api/transactions/route.js +23 -6
- package/dist/modules/checkout/api/transactions/route.js.map +2 -2
- package/dist/modules/checkout/backend/checkout/pay-links/page.js +2 -1
- package/dist/modules/checkout/backend/checkout/pay-links/page.js.map +2 -2
- package/dist/modules/checkout/backend/checkout/templates/page.js +2 -1
- package/dist/modules/checkout/backend/checkout/templates/page.js.map +2 -2
- package/dist/modules/checkout/backend/checkout/transactions/page.js +2 -1
- package/dist/modules/checkout/backend/checkout/transactions/page.js.map +2 -2
- package/dist/modules/checkout/commands/links.js +1 -1
- package/dist/modules/checkout/commands/links.js.map +2 -2
- package/dist/modules/checkout/commands/transactions.js +45 -13
- package/dist/modules/checkout/commands/transactions.js.map +2 -2
- package/dist/modules/checkout/components/CustomerFieldsEditor.js +2 -2
- package/dist/modules/checkout/components/CustomerFieldsEditor.js.map +2 -2
- package/dist/modules/checkout/components/GatewaySettingsFields.js +2 -2
- package/dist/modules/checkout/components/GatewaySettingsFields.js.map +2 -2
- package/dist/modules/checkout/components/LinkTemplateForm.js +8 -8
- package/dist/modules/checkout/components/LinkTemplateForm.js.map +2 -2
- package/dist/modules/checkout/components/LogoUploadField.js +1 -1
- package/dist/modules/checkout/components/LogoUploadField.js.map +2 -2
- package/dist/modules/checkout/components/PayPage.js +39 -38
- package/dist/modules/checkout/components/PayPage.js.map +2 -2
- package/dist/modules/checkout/extension-points.js +68 -0
- package/dist/modules/checkout/extension-points.js.map +7 -0
- package/dist/modules/checkout/lib/transaction-status-machine.js +31 -0
- package/dist/modules/checkout/lib/transaction-status-machine.js.map +7 -0
- package/dist/modules/checkout/search.js +44 -6
- package/dist/modules/checkout/search.js.map +2 -2
- package/dist/modules/checkout/setup.js +28 -0
- package/dist/modules/checkout/setup.js.map +2 -2
- package/dist/modules/checkout/subscribers/search-reindex-transaction-created.js +32 -0
- package/dist/modules/checkout/subscribers/search-reindex-transaction-created.js.map +7 -0
- package/dist/modules/checkout/workers/transaction-expiry.worker.js +9 -2
- package/dist/modules/checkout/workers/transaction-expiry.worker.js.map +2 -2
- package/package.json +10 -9
- package/src/modules/checkout/__integration__/TC-CHKT-042.spec.ts +41 -0
- package/src/modules/checkout/__integration__/TC-CHKT-043-concurrent-status-update.spec.ts +112 -0
- package/src/modules/checkout/__tests__/search.test.ts +21 -0
- package/src/modules/checkout/__tests__/setup-schedules.test.ts +99 -0
- package/src/modules/checkout/api/pay/[slug]/submit/__tests__/route.test.ts +77 -0
- package/src/modules/checkout/api/pay/[slug]/submit/route.ts +58 -28
- package/src/modules/checkout/api/transactions/__tests__/route.test.ts +113 -0
- package/src/modules/checkout/api/transactions/route.ts +30 -6
- package/src/modules/checkout/backend/checkout/pay-links/page.tsx +2 -1
- package/src/modules/checkout/backend/checkout/templates/page.tsx +2 -1
- package/src/modules/checkout/backend/checkout/transactions/page.tsx +2 -1
- package/src/modules/checkout/commands/__tests__/update-transaction-status.test.ts +363 -0
- package/src/modules/checkout/commands/links.ts +5 -1
- package/src/modules/checkout/commands/transactions.ts +68 -13
- package/src/modules/checkout/components/CustomerFieldsEditor.tsx +2 -2
- package/src/modules/checkout/components/GatewaySettingsFields.tsx +2 -2
- package/src/modules/checkout/components/LinkTemplateForm.tsx +8 -8
- package/src/modules/checkout/components/LogoUploadField.tsx +1 -1
- package/src/modules/checkout/components/PayPage.tsx +38 -37
- package/src/modules/checkout/extension-points.ts +67 -0
- package/src/modules/checkout/i18n/de.json +1 -0
- package/src/modules/checkout/i18n/en.json +1 -0
- package/src/modules/checkout/i18n/es.json +1 -0
- package/src/modules/checkout/i18n/ko.json +463 -0
- package/src/modules/checkout/i18n/pl.json +1 -0
- package/src/modules/checkout/lib/__tests__/transaction-status-machine.test.ts +152 -0
- package/src/modules/checkout/lib/transaction-status-machine.ts +71 -0
- package/src/modules/checkout/search.ts +44 -6
- package/src/modules/checkout/setup.ts +51 -0
- package/src/modules/checkout/subscribers/__tests__/search-reindex-transaction-created.test.ts +29 -0
- package/src/modules/checkout/subscribers/search-reindex-transaction-created.ts +39 -0
- package/src/modules/checkout/workers/__tests__/transaction-expiry.worker.test.ts +124 -0
- package/src/modules/checkout/workers/transaction-expiry.worker.ts +11 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/checkout/backend/checkout/pay-links/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Plus } from 'lucide-react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\n\ntype LinkRow = {\n id: string\n name: string\n slug: string\n pricingMode: 'fixed' | 'custom_amount' | 'price_list'\n fixedPriceAmount?: number | null\n fixedPriceCurrencyCode?: string | null\n customAmountMin?: number | null\n customAmountMax?: number | null\n customAmountCurrencyCode?: string | null\n status: 'draft' | 'active' | 'inactive'\n completionCount: number\n maxCompletions?: number | null\n createdAt?: string | null\n}\n\ntype ListResponse = {\n items: LinkRow[]\n total: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nexport default function CheckoutPayLinksPage() {\n const t = useT()\n const [rows, setRows] = React.useState<LinkRow[]>([])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const { runMutation } = useGuardedMutation<{\n entityType: string\n entityId?: string\n }>({\n contextId: 'checkout:pay-links-list',\n })\n\n const filterDefs = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('checkout.admin.payLinks.filters.status'),\n type: 'select',\n options: [\n { value: 'draft', label: t('checkout.common.status.draft') },\n { value: 'active', label: t('checkout.common.status.active') },\n { value: 'inactive', label: t('checkout.common.status.inactive') },\n ],\n },\n {\n id: 'pricingMode',\n label: t('checkout.admin.payLinks.filters.pricing'),\n type: 'select',\n options: [\n { value: 'fixed', label: t('checkout.linkTemplateForm.pricing.modes.fixed') },\n { value: 'custom_amount', label: t('checkout.linkTemplateForm.pricing.modes.customAmount') },\n { value: 'price_list', label: t('checkout.linkTemplateForm.pricing.modes.priceList') },\n ],\n },\n ], [t])\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n search,\n })\n if (typeof filters.status === 'string' && filters.status) params.set('status', filters.status)\n if (typeof filters.pricingMode === 'string' && filters.pricingMode) params.set('pricingMode', filters.pricingMode)\n const result = await readApiResultOrThrow<ListResponse>(`/api/checkout/links?${params.toString()}`)\n setRows(result.items ?? [])\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n setLoading(false)\n }, [filters.pricingMode, filters.status, page, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const runRowMutation = React.useCallback(async ({\n row,\n operation,\n successMessage,\n fallbackErrorMessage,\n mutationPayload,\n }: {\n row: LinkRow\n operation: () => Promise<void>\n successMessage: string\n fallbackErrorMessage: string\n mutationPayload?: Record<string, unknown>\n }) => {\n try {\n await runMutation({\n operation,\n mutationPayload,\n context: {\n entityType: 'checkout:checkout_link',\n entityId: row.id,\n },\n })\n flash(successMessage, 'success')\n void loadRows()\n } catch (error) {\n const { message } = normalizeCrudServerError(error)\n flash(message || fallbackErrorMessage, 'error')\n }\n }, [loadRows, runMutation])\n\n const columns = React.useMemo<ColumnDef<LinkRow>[]>(() => [\n { accessorKey: 'name', header: t('checkout.admin.payLinks.columns.name') },\n {\n accessorKey: 'slug',\n header: t('checkout.admin.payLinks.columns.slug'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">/pay/{row.original.slug}</span>,\n },\n {\n accessorKey: 'pricingMode',\n header: t('checkout.admin.payLinks.columns.pricing'),\n cell: ({ row }) => {\n if (row.original.pricingMode === 'fixed') {\n return t('checkout.admin.payLinks.pricing.fixed', {\n amount: row.original.fixedPriceAmount?.toFixed(2) ?? '0.00',\n currency: row.original.fixedPriceCurrencyCode ?? '',\n }).trim()\n }\n if (row.original.pricingMode === 'custom_amount') {\n return t('checkout.admin.payLinks.pricing.customAmount', {\n min: row.original.customAmountMin?.toFixed(2) ?? '0.00',\n max: row.original.customAmountMax?.toFixed(2) ?? '0.00',\n currency: row.original.customAmountCurrencyCode ?? '',\n }).trim()\n }\n return t('checkout.linkTemplateForm.pricing.modes.priceList')\n },\n },\n {\n accessorKey: 'status',\n header: t('checkout.admin.payLinks.columns.status'),\n cell: ({ row }) => (\n <Badge variant={row.original.status === 'active' ? 'default' : 'secondary'}>\n {t(`checkout.common.status.${row.original.status}`)}\n </Badge>\n ),\n },\n {\n accessorKey: 'completionCount',\n header: t('checkout.admin.payLinks.columns.uses'),\n cell: ({ row }) => `${row.original.completionCount} / ${row.original.maxCompletions ?? '\u221E'}`,\n },\n {\n accessorKey: 'createdAt',\n header: t('checkout.admin.payLinks.columns.created'),\n cell: ({ row }) => formatDate(row.original.createdAt),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('checkout.admin.payLinks.title')}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('checkout.admin.payLinks.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(next) => { setFilters(next); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n pagination={{ page, pageSize: 25, total, totalPages, onPageChange: setPage }}\n isLoading={loading}\n perspective={{ tableId: 'checkout-links' }}\n actions={(\n <Button asChild>\n <Link href=\"/backend/checkout/pay-links/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('checkout.admin.payLinks.actions.create')}\n </Link>\n </Button>\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('checkout.admin.payLinks.title')}\n createHref=\"/backend/checkout/pay-links/create\"\n createLabel={t('checkout.admin.payLinks.actions.create')}\n />\n )}\n rowActions={(row) => (\n <RowActions items={[\n { id: 'edit', label: t('checkout.common.actions.edit'), href: `/backend/checkout/pay-links/${encodeURIComponent(row.id)}` },\n {\n id: 'preview',\n label: t('checkout.common.actions.preview'),\n onSelect: () => window.open(`/pay/${encodeURIComponent(row.slug)}?preview=true`, '_blank', 'noopener,noreferrer'),\n },\n ...(row.status === 'active'\n ? [{\n id: 'view',\n label: t('checkout.admin.payLinks.actions.viewPayPage'),\n onSelect: () => window.open(`/pay/${encodeURIComponent(row.slug)}`, '_blank', 'noopener,noreferrer'),\n }]\n : []),\n {\n id: 'copy',\n label: t('checkout.admin.payLinks.actions.copyUrl'),\n onSelect: async () => {\n await navigator.clipboard.writeText(`${window.location.origin}/pay/${row.slug}`)\n },\n },\n { id: 'transactions', label: t('checkout.admin.payLinks.actions.showTransactions'), href: `/backend/checkout/transactions?linkId=${encodeURIComponent(row.id)}` },\n ...(row.status !== 'active'\n ? [{\n id: 'publish',\n label: t('checkout.admin.payLinks.actions.publish'),\n onSelect: async () => {\n await runRowMutation({\n row,\n successMessage: t('checkout.admin.payLinks.flash.published'),\n fallbackErrorMessage: t('checkout.admin.payLinks.flash.publishError'),\n mutationPayload: { status: 'active' },\n operation: async () => {\n await apiCallOrThrow(`/api/checkout/links/${encodeURIComponent(row.id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'active' }),\n })\n },\n })\n },\n }]\n : [{\n id: 'deactivate',\n label: t('checkout.admin.payLinks.actions.deactivate'),\n onSelect: async () => {\n await runRowMutation({\n row,\n successMessage: t('checkout.admin.payLinks.flash.deactivated'),\n fallbackErrorMessage: t('checkout.admin.payLinks.flash.deactivateError'),\n mutationPayload: { status: 'inactive' },\n operation: async () => {\n await apiCallOrThrow(`/api/checkout/links/${encodeURIComponent(row.id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'inactive' }),\n })\n },\n })\n },\n }]),\n {\n id: 'delete',\n label: t('checkout.common.actions.delete'),\n onSelect: async () => {\n await runRowMutation({\n row,\n successMessage: t('checkout.admin.payLinks.flash.deleted'),\n fallbackErrorMessage: t('checkout.admin.payLinks.flash.deleteError'),\n operation: async () => {\n await apiCallOrThrow(`/api/checkout/links/${encodeURIComponent(row.id)}`, { method: 'DELETE' })\n },\n })\n },\n },\n ]} />\n )}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/checkout/modules/checkout/extension-points'\nimport Link from 'next/link'\nimport { Plus } from 'lucide-react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { normalizeCrudServerError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\n\ntype LinkRow = {\n id: string\n name: string\n slug: string\n pricingMode: 'fixed' | 'custom_amount' | 'price_list'\n fixedPriceAmount?: number | null\n fixedPriceCurrencyCode?: string | null\n customAmountMin?: number | null\n customAmountMax?: number | null\n customAmountCurrencyCode?: string | null\n status: 'draft' | 'active' | 'inactive'\n completionCount: number\n maxCompletions?: number | null\n createdAt?: string | null\n}\n\ntype ListResponse = {\n items: LinkRow[]\n total: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nexport default function CheckoutPayLinksPage() {\n const t = useT()\n const [rows, setRows] = React.useState<LinkRow[]>([])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const { runMutation } = useGuardedMutation<{\n entityType: string\n entityId?: string\n }>({\n contextId: 'checkout:pay-links-list',\n })\n\n const filterDefs = React.useMemo<FilterDef[]>(() => [\n {\n id: 'status',\n label: t('checkout.admin.payLinks.filters.status'),\n type: 'select',\n options: [\n { value: 'draft', label: t('checkout.common.status.draft') },\n { value: 'active', label: t('checkout.common.status.active') },\n { value: 'inactive', label: t('checkout.common.status.inactive') },\n ],\n },\n {\n id: 'pricingMode',\n label: t('checkout.admin.payLinks.filters.pricing'),\n type: 'select',\n options: [\n { value: 'fixed', label: t('checkout.linkTemplateForm.pricing.modes.fixed') },\n { value: 'custom_amount', label: t('checkout.linkTemplateForm.pricing.modes.customAmount') },\n { value: 'price_list', label: t('checkout.linkTemplateForm.pricing.modes.priceList') },\n ],\n },\n ], [t])\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n search,\n })\n if (typeof filters.status === 'string' && filters.status) params.set('status', filters.status)\n if (typeof filters.pricingMode === 'string' && filters.pricingMode) params.set('pricingMode', filters.pricingMode)\n const result = await readApiResultOrThrow<ListResponse>(`/api/checkout/links?${params.toString()}`)\n setRows(result.items ?? [])\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n setLoading(false)\n }, [filters.pricingMode, filters.status, page, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const runRowMutation = React.useCallback(async ({\n row,\n operation,\n successMessage,\n fallbackErrorMessage,\n mutationPayload,\n }: {\n row: LinkRow\n operation: () => Promise<void>\n successMessage: string\n fallbackErrorMessage: string\n mutationPayload?: Record<string, unknown>\n }) => {\n try {\n await runMutation({\n operation,\n mutationPayload,\n context: {\n entityType: 'checkout:checkout_link',\n entityId: row.id,\n },\n })\n flash(successMessage, 'success')\n void loadRows()\n } catch (error) {\n const { message } = normalizeCrudServerError(error)\n flash(message || fallbackErrorMessage, 'error')\n }\n }, [loadRows, runMutation])\n\n const columns = React.useMemo<ColumnDef<LinkRow>[]>(() => [\n { accessorKey: 'name', header: t('checkout.admin.payLinks.columns.name') },\n {\n accessorKey: 'slug',\n header: t('checkout.admin.payLinks.columns.slug'),\n cell: ({ row }) => <span className=\"font-mono text-xs\">/pay/{row.original.slug}</span>,\n },\n {\n accessorKey: 'pricingMode',\n header: t('checkout.admin.payLinks.columns.pricing'),\n cell: ({ row }) => {\n if (row.original.pricingMode === 'fixed') {\n return t('checkout.admin.payLinks.pricing.fixed', {\n amount: row.original.fixedPriceAmount?.toFixed(2) ?? '0.00',\n currency: row.original.fixedPriceCurrencyCode ?? '',\n }).trim()\n }\n if (row.original.pricingMode === 'custom_amount') {\n return t('checkout.admin.payLinks.pricing.customAmount', {\n min: row.original.customAmountMin?.toFixed(2) ?? '0.00',\n max: row.original.customAmountMax?.toFixed(2) ?? '0.00',\n currency: row.original.customAmountCurrencyCode ?? '',\n }).trim()\n }\n return t('checkout.linkTemplateForm.pricing.modes.priceList')\n },\n },\n {\n accessorKey: 'status',\n header: t('checkout.admin.payLinks.columns.status'),\n cell: ({ row }) => (\n <Badge variant={row.original.status === 'active' ? 'default' : 'secondary'}>\n {t(`checkout.common.status.${row.original.status}`)}\n </Badge>\n ),\n },\n {\n accessorKey: 'completionCount',\n header: t('checkout.admin.payLinks.columns.uses'),\n cell: ({ row }) => `${row.original.completionCount} / ${row.original.maxCompletions ?? '\u221E'}`,\n },\n {\n accessorKey: 'createdAt',\n header: t('checkout.admin.payLinks.columns.created'),\n cell: ({ row }) => formatDate(row.original.createdAt),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('checkout.admin.payLinks.title')}\n columns={columns}\n data={rows}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('checkout.admin.payLinks.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(next) => { setFilters(next); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n pagination={{ page, pageSize: 25, total, totalPages, onPageChange: setPage }}\n isLoading={loading}\n perspective={{ tableId: extensionPoints.hosts.linksTable.tableId }}\n actions={(\n <Button asChild>\n <Link href=\"/backend/checkout/pay-links/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('checkout.admin.payLinks.actions.create')}\n </Link>\n </Button>\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('checkout.admin.payLinks.title')}\n createHref=\"/backend/checkout/pay-links/create\"\n createLabel={t('checkout.admin.payLinks.actions.create')}\n />\n )}\n rowActions={(row) => (\n <RowActions items={[\n { id: 'edit', label: t('checkout.common.actions.edit'), href: `/backend/checkout/pay-links/${encodeURIComponent(row.id)}` },\n {\n id: 'preview',\n label: t('checkout.common.actions.preview'),\n onSelect: () => window.open(`/pay/${encodeURIComponent(row.slug)}?preview=true`, '_blank', 'noopener,noreferrer'),\n },\n ...(row.status === 'active'\n ? [{\n id: 'view',\n label: t('checkout.admin.payLinks.actions.viewPayPage'),\n onSelect: () => window.open(`/pay/${encodeURIComponent(row.slug)}`, '_blank', 'noopener,noreferrer'),\n }]\n : []),\n {\n id: 'copy',\n label: t('checkout.admin.payLinks.actions.copyUrl'),\n onSelect: async () => {\n await navigator.clipboard.writeText(`${window.location.origin}/pay/${row.slug}`)\n },\n },\n { id: 'transactions', label: t('checkout.admin.payLinks.actions.showTransactions'), href: `/backend/checkout/transactions?linkId=${encodeURIComponent(row.id)}` },\n ...(row.status !== 'active'\n ? [{\n id: 'publish',\n label: t('checkout.admin.payLinks.actions.publish'),\n onSelect: async () => {\n await runRowMutation({\n row,\n successMessage: t('checkout.admin.payLinks.flash.published'),\n fallbackErrorMessage: t('checkout.admin.payLinks.flash.publishError'),\n mutationPayload: { status: 'active' },\n operation: async () => {\n await apiCallOrThrow(`/api/checkout/links/${encodeURIComponent(row.id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'active' }),\n })\n },\n })\n },\n }]\n : [{\n id: 'deactivate',\n label: t('checkout.admin.payLinks.actions.deactivate'),\n onSelect: async () => {\n await runRowMutation({\n row,\n successMessage: t('checkout.admin.payLinks.flash.deactivated'),\n fallbackErrorMessage: t('checkout.admin.payLinks.flash.deactivateError'),\n mutationPayload: { status: 'inactive' },\n operation: async () => {\n await apiCallOrThrow(`/api/checkout/links/${encodeURIComponent(row.id)}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'inactive' }),\n })\n },\n })\n },\n }]),\n {\n id: 'delete',\n label: t('checkout.common.actions.delete'),\n onSelect: async () => {\n await runRowMutation({\n row,\n successMessage: t('checkout.admin.payLinks.flash.deleted'),\n fallbackErrorMessage: t('checkout.admin.payLinks.flash.deleteError'),\n operation: async () => {\n await apiCallOrThrow(`/api/checkout/links/${encodeURIComponent(row.id)}`, { method: 'DELETE' })\n },\n })\n },\n },\n ]} />\n )}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { extensionPoints } from "@open-mercato/checkout/modules/checkout/extension-points";
|
|
4
5
|
import Link from "next/link";
|
|
5
6
|
import { Plus } from "lucide-react";
|
|
6
7
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
@@ -106,7 +107,7 @@ function CheckoutTemplatesPage() {
|
|
|
106
107
|
setPage(1);
|
|
107
108
|
},
|
|
108
109
|
pagination: { page, pageSize: 25, total, totalPages, onPageChange: setPage },
|
|
109
|
-
perspective: { tableId:
|
|
110
|
+
perspective: { tableId: extensionPoints.hosts.templatesTable.tableId },
|
|
110
111
|
actions: /* @__PURE__ */ jsx(Button, { asChild: true, children: /* @__PURE__ */ jsxs(Link, { href: "/backend/checkout/templates/create", children: [
|
|
111
112
|
/* @__PURE__ */ jsx(Plus, { className: "mr-2 h-4 w-4" }),
|
|
112
113
|
t("checkout.admin.templates.actions.create")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/checkout/backend/checkout/templates/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Plus } from 'lucide-react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\n\ntype TemplateRow = {\n id: string\n name: string\n pricingMode: string\n gatewayProviderKey?: string | null\n maxCompletions?: number | null\n createdAt?: string | null\n}\n\ntype ListResponse = {\n items: TemplateRow[]\n total: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nexport default function CheckoutTemplatesPage() {\n const t = useT()\n const [rows, setRows] = React.useState<TemplateRow[]>([])\n const [loading, setLoading] = React.useState(true)\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n\n const filterDefs = React.useMemo<FilterDef[]>(() => [\n {\n id: 'pricingMode',\n label: t('checkout.admin.templates.filters.pricingMode'),\n type: 'select',\n options: [\n { value: 'fixed', label: t('checkout.linkTemplateForm.pricing.modes.fixed') },\n { value: 'custom_amount', label: t('checkout.linkTemplateForm.pricing.modes.customAmount') },\n { value: 'price_list', label: t('checkout.linkTemplateForm.pricing.modes.priceList') },\n ],\n },\n ], [t])\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n search,\n })\n if (typeof filters.pricingMode === 'string' && filters.pricingMode) params.set('pricingMode', filters.pricingMode)\n const result = await readApiResultOrThrow<ListResponse>(`/api/checkout/templates?${params.toString()}`)\n setRows(result.items ?? [])\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n setLoading(false)\n }, [filters.pricingMode, page, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const columns = React.useMemo<ColumnDef<TemplateRow>[]>(() => [\n { accessorKey: 'name', header: t('checkout.admin.templates.columns.name') },\n {\n accessorKey: 'pricingMode',\n header: t('checkout.admin.templates.columns.pricingMode'),\n cell: ({ row }) => {\n const mode = row.original.pricingMode\n if (mode === 'fixed') return t('checkout.linkTemplateForm.pricing.modes.fixed')\n if (mode === 'custom_amount') return t('checkout.linkTemplateForm.pricing.modes.customAmount')\n if (mode === 'price_list') return t('checkout.linkTemplateForm.pricing.modes.priceList')\n return mode\n },\n },\n {\n accessorKey: 'gatewayProviderKey',\n header: t('checkout.admin.templates.columns.gateway'),\n cell: ({ row }) => row.original.gatewayProviderKey ?? t('checkout.common.emptyValue'),\n },\n {\n accessorKey: 'maxCompletions',\n header: t('checkout.admin.templates.columns.maxUses'),\n cell: ({ row }) => row.original.maxCompletions ?? t('checkout.admin.templates.unlimited'),\n },\n {\n accessorKey: 'createdAt',\n header: t('checkout.admin.templates.columns.created'),\n cell: ({ row }) => formatDate(row.original.createdAt),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('checkout.admin.templates.title')}\n columns={columns}\n data={rows}\n isLoading={loading}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('checkout.admin.templates.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(next) => { setFilters(next); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n pagination={{ page, pageSize: 25, total, totalPages, onPageChange: setPage }}\n perspective={{ tableId:
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/checkout/modules/checkout/extension-points'\nimport Link from 'next/link'\nimport { Plus } from 'lucide-react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { ListEmptyState } from '@open-mercato/ui/backend/filters/ListEmptyState'\n\ntype TemplateRow = {\n id: string\n name: string\n pricingMode: string\n gatewayProviderKey?: string | null\n maxCompletions?: number | null\n createdAt?: string | null\n}\n\ntype ListResponse = {\n items: TemplateRow[]\n total: number\n totalPages: number\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nexport default function CheckoutTemplatesPage() {\n const t = useT()\n const [rows, setRows] = React.useState<TemplateRow[]>([])\n const [loading, setLoading] = React.useState(true)\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>({})\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n\n const filterDefs = React.useMemo<FilterDef[]>(() => [\n {\n id: 'pricingMode',\n label: t('checkout.admin.templates.filters.pricingMode'),\n type: 'select',\n options: [\n { value: 'fixed', label: t('checkout.linkTemplateForm.pricing.modes.fixed') },\n { value: 'custom_amount', label: t('checkout.linkTemplateForm.pricing.modes.customAmount') },\n { value: 'price_list', label: t('checkout.linkTemplateForm.pricing.modes.priceList') },\n ],\n },\n ], [t])\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n search,\n })\n if (typeof filters.pricingMode === 'string' && filters.pricingMode) params.set('pricingMode', filters.pricingMode)\n const result = await readApiResultOrThrow<ListResponse>(`/api/checkout/templates?${params.toString()}`)\n setRows(result.items ?? [])\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n setLoading(false)\n }, [filters.pricingMode, page, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const columns = React.useMemo<ColumnDef<TemplateRow>[]>(() => [\n { accessorKey: 'name', header: t('checkout.admin.templates.columns.name') },\n {\n accessorKey: 'pricingMode',\n header: t('checkout.admin.templates.columns.pricingMode'),\n cell: ({ row }) => {\n const mode = row.original.pricingMode\n if (mode === 'fixed') return t('checkout.linkTemplateForm.pricing.modes.fixed')\n if (mode === 'custom_amount') return t('checkout.linkTemplateForm.pricing.modes.customAmount')\n if (mode === 'price_list') return t('checkout.linkTemplateForm.pricing.modes.priceList')\n return mode\n },\n },\n {\n accessorKey: 'gatewayProviderKey',\n header: t('checkout.admin.templates.columns.gateway'),\n cell: ({ row }) => row.original.gatewayProviderKey ?? t('checkout.common.emptyValue'),\n },\n {\n accessorKey: 'maxCompletions',\n header: t('checkout.admin.templates.columns.maxUses'),\n cell: ({ row }) => row.original.maxCompletions ?? t('checkout.admin.templates.unlimited'),\n },\n {\n accessorKey: 'createdAt',\n header: t('checkout.admin.templates.columns.created'),\n cell: ({ row }) => formatDate(row.original.createdAt),\n },\n ], [t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('checkout.admin.templates.title')}\n columns={columns}\n data={rows}\n isLoading={loading}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('checkout.admin.templates.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(next) => { setFilters(next); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n pagination={{ page, pageSize: 25, total, totalPages, onPageChange: setPage }}\n perspective={{ tableId: extensionPoints.hosts.templatesTable.tableId }}\n actions={(\n <Button asChild>\n <Link href=\"/backend/checkout/templates/create\">\n <Plus className=\"mr-2 h-4 w-4\" />\n {t('checkout.admin.templates.actions.create')}\n </Link>\n </Button>\n )}\n emptyState={(\n <ListEmptyState\n entityName={t('checkout.admin.templates.title')}\n createHref=\"/backend/checkout/templates/create\"\n createLabel={t('checkout.admin.templates.actions.create')}\n />\n )}\n rowActions={(row) => (\n <RowActions items={[\n { id: 'edit', label: t('checkout.common.actions.edit'), href: `/backend/checkout/templates/${encodeURIComponent(row.id)}` },\n {\n id: 'preview',\n label: t('checkout.common.actions.preview'),\n onSelect: () => window.open(`/backend/checkout/templates/${encodeURIComponent(row.id)}/preview`, '_blank', 'noopener,noreferrer'),\n },\n { id: 'create-link', label: t('checkout.admin.templates.actions.createLinkFromTemplate'), href: `/backend/checkout/pay-links/create?templateId=${encodeURIComponent(row.id)}` },\n {\n id: 'delete',\n label: t('checkout.common.actions.delete'),\n onSelect: async () => {\n await apiCallOrThrow(`/api/checkout/templates/${encodeURIComponent(row.id)}`, { method: 'DELETE' })\n void loadRows()\n },\n },\n ]} />\n )}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import { extensionPoints } from "@open-mercato/checkout/modules/checkout/extension-points";
|
|
4
5
|
import { useSearchParams } from "next/navigation";
|
|
5
6
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
6
7
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
@@ -197,7 +198,7 @@ function CheckoutTransactionsPage() {
|
|
|
197
198
|
setPage(1);
|
|
198
199
|
},
|
|
199
200
|
pagination: { page, pageSize: 25, total, totalPages, onPageChange: setPage },
|
|
200
|
-
perspective: { tableId:
|
|
201
|
+
perspective: { tableId: extensionPoints.hosts.transactionsTable.tableId },
|
|
201
202
|
rowClickActionIds: ["view"],
|
|
202
203
|
rowActions: (row) => /* @__PURE__ */ jsx(RowActions, { items: [{ id: "view", label: t("checkout.admin.transactions.actions.viewDetail"), href: `/backend/checkout/transactions/${encodeURIComponent(row.id)}` }] })
|
|
203
204
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/checkout/backend/checkout/transactions/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useSearchParams } from 'next/navigation'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\ntype TransactionRow = {\n id: string\n linkId: string\n linkName?: string | null\n firstName?: string | null\n lastName?: string | null\n email?: string | null\n amount?: number | null\n currencyCode: string\n status: string\n paymentStatus?: string | null\n createdAt?: string | null\n}\n\ntype TransactionsResponse = {\n items: TransactionRow[]\n total: number\n totalPages: number\n canViewPii?: boolean\n}\n\ntype LinkLookupResponse = {\n id: string\n name?: string | null\n}\n\ntype LinkListResponse = {\n items?: Array<Record<string, unknown>>\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nfunction formatAmount(amount: number | null | undefined, currencyCode: string): string {\n const resolved = typeof amount === 'number' ? amount : 0\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency: currencyCode }).format(resolved)\n } catch {\n return `${resolved.toFixed(2)} ${currencyCode}`\n }\n}\n\nexport default function CheckoutTransactionsPage() {\n const t = useT()\n const searchParams = useSearchParams()\n const initialLinkId = React.useMemo(() => {\n const raw = searchParams.get('linkId')\n return raw && raw.trim().length > 0 ? raw.trim() : ''\n }, [searchParams])\n const [rows, setRows] = React.useState<TransactionRow[]>([])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>(() => (initialLinkId ? { linkId: initialLinkId } : {}))\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const [canViewPii, setCanViewPii] = React.useState(false)\n const [linkLabels, setLinkLabels] = React.useState<Record<string, string>>({})\n\n const rememberLinkOptions = React.useCallback((options: Array<{ value: string; label: string }>) => {\n if (!options.length) return\n setLinkLabels((prev) => {\n let changed = false\n const next = { ...prev }\n for (const option of options) {\n if (!option.value || !option.label) continue\n if (next[option.value] === option.label) continue\n next[option.value] = option.label\n changed = true\n }\n return changed ? next : prev\n })\n }, [])\n\n const loadLinkOptions = React.useCallback(async (query?: string): Promise<Array<{ value: string; label: string }>> => {\n const params = new URLSearchParams({ page: '1', pageSize: '20' })\n if (query && query.trim().length > 0) params.set('search', query.trim())\n try {\n const call = await apiCall<LinkListResponse>(`/api/checkout/links?${params.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const options = items\n .map((item): { value: string; label: string } | null => {\n const id = typeof item.id === 'string' ? item.id : null\n const name = typeof item.name === 'string' ? item.name : null\n if (!id || !name) return null\n return { value: id, label: name }\n })\n .filter((option): option is { value: string; label: string } => option !== null)\n rememberLinkOptions(options)\n return options\n } catch {\n return []\n }\n }, [rememberLinkOptions])\n\n React.useEffect(() => {\n if (!initialLinkId || linkLabels[initialLinkId]) return\n let active = true\n void readApiResultOrThrow<LinkLookupResponse>(`/api/checkout/links/${encodeURIComponent(initialLinkId)}`)\n .then((result) => {\n if (!active) return\n const name = typeof result.name === 'string' ? result.name.trim() : ''\n if (!name) return\n rememberLinkOptions([{ value: initialLinkId, label: name }])\n })\n .catch(() => null)\n return () => {\n active = false\n }\n }, [initialLinkId, linkLabels, rememberLinkOptions])\n\n const filterDefs = React.useMemo<FilterDef[]>(() => [\n {\n id: 'linkId',\n label: t('checkout.admin.transactions.filters.link'),\n type: 'combobox',\n placeholder: t('checkout.admin.payLinks.searchPlaceholder'),\n loadOptions: loadLinkOptions,\n formatValue: (value) => linkLabels[value] ?? value,\n },\n {\n id: 'status',\n label: t('checkout.admin.transactions.filters.status'),\n type: 'select',\n options: [\n { value: 'pending', label: t('checkout.admin.transactions.status.pending') },\n { value: 'processing', label: t('checkout.admin.transactions.status.processing') },\n { value: 'completed', label: t('checkout.admin.transactions.status.completed') },\n { value: 'failed', label: t('checkout.admin.transactions.status.failed') },\n { value: 'cancelled', label: t('checkout.admin.transactions.status.cancelled') },\n { value: 'expired', label: t('checkout.admin.transactions.status.expired') },\n ],\n },\n ], [linkLabels, loadLinkOptions, t])\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n search,\n })\n if (typeof filters.status === 'string' && filters.status) params.set('status', filters.status)\n if (typeof filters.linkId === 'string' && filters.linkId) params.set('linkId', filters.linkId)\n const result = await readApiResultOrThrow<TransactionsResponse>(`/api/checkout/transactions?${params.toString()}`)\n setRows(result.items ?? [])\n rememberLinkOptions(\n (result.items ?? [])\n .map((item) => {\n if (!item.linkId || !item.linkName) return null\n return { value: item.linkId, label: item.linkName }\n })\n .filter((option): option is { value: string; label: string } => option !== null),\n )\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n setCanViewPii(result.canViewPii === true)\n setLoading(false)\n }, [filters.linkId, filters.status, page, rememberLinkOptions, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const columns = React.useMemo<ColumnDef<TransactionRow>[]>(() => {\n const baseColumns: ColumnDef<TransactionRow>[] = [\n { accessorKey: 'linkName', header: t('checkout.admin.transactions.columns.link') },\n ]\n if (canViewPii) {\n baseColumns.push(\n {\n id: 'customer',\n header: t('checkout.admin.transactions.columns.customer'),\n cell: ({ row }) => [row.original.firstName, row.original.lastName].filter(Boolean).join(' ') || t('checkout.common.emptyValue'),\n },\n {\n accessorKey: 'email',\n header: t('checkout.admin.transactions.columns.email'),\n cell: ({ row }) => row.original.email ?? t('checkout.common.emptyValue'),\n },\n )\n }\n baseColumns.push(\n {\n accessorKey: 'amount',\n header: t('checkout.admin.transactions.columns.amount'),\n cell: ({ row }) => formatAmount(row.original.amount, row.original.currencyCode),\n },\n {\n accessorKey: 'status',\n header: t('checkout.admin.transactions.columns.status'),\n cell: ({ row }) => <Badge variant=\"secondary\">{t(`checkout.admin.transactions.status.${row.original.status}`, row.original.status)}</Badge>,\n },\n {\n accessorKey: 'paymentStatus',\n header: t('checkout.admin.transactions.columns.paymentStatus'),\n cell: ({ row }) => row.original.paymentStatus ?? t('checkout.common.emptyValue'),\n },\n {\n accessorKey: 'createdAt',\n header: t('checkout.admin.transactions.columns.date'),\n cell: ({ row }) => formatDate(row.original.createdAt),\n },\n )\n return baseColumns\n }, [canViewPii, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('checkout.admin.transactions.title')}\n columns={columns}\n data={rows}\n isLoading={loading}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('checkout.admin.transactions.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(next) => { setFilters(next); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n pagination={{ page, pageSize: 25, total, totalPages, onPageChange: setPage }}\n perspective={{ tableId:
|
|
5
|
-
"mappings": ";AA
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { extensionPoints } from '@open-mercato/checkout/modules/checkout/extension-points'\nimport { useSearchParams } from 'next/navigation'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { DataTable } from '@open-mercato/ui/backend/DataTable'\nimport { RowActions } from '@open-mercato/ui/backend/RowActions'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\ntype TransactionRow = {\n id: string\n linkId: string\n linkName?: string | null\n firstName?: string | null\n lastName?: string | null\n email?: string | null\n amount?: number | null\n currencyCode: string\n status: string\n paymentStatus?: string | null\n createdAt?: string | null\n}\n\ntype TransactionsResponse = {\n items: TransactionRow[]\n total: number\n totalPages: number\n canViewPii?: boolean\n}\n\ntype LinkLookupResponse = {\n id: string\n name?: string | null\n}\n\ntype LinkListResponse = {\n items?: Array<Record<string, unknown>>\n}\n\nfunction formatDate(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const parsed = new Date(value)\n return Number.isNaN(parsed.getTime()) ? value : parsed.toLocaleDateString()\n}\n\nfunction formatAmount(amount: number | null | undefined, currencyCode: string): string {\n const resolved = typeof amount === 'number' ? amount : 0\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency: currencyCode }).format(resolved)\n } catch {\n return `${resolved.toFixed(2)} ${currencyCode}`\n }\n}\n\nexport default function CheckoutTransactionsPage() {\n const t = useT()\n const searchParams = useSearchParams()\n const initialLinkId = React.useMemo(() => {\n const raw = searchParams.get('linkId')\n return raw && raw.trim().length > 0 ? raw.trim() : ''\n }, [searchParams])\n const [rows, setRows] = React.useState<TransactionRow[]>([])\n const [page, setPage] = React.useState(1)\n const [search, setSearch] = React.useState('')\n const [filters, setFilters] = React.useState<FilterValues>(() => (initialLinkId ? { linkId: initialLinkId } : {}))\n const [total, setTotal] = React.useState(0)\n const [totalPages, setTotalPages] = React.useState(1)\n const [loading, setLoading] = React.useState(true)\n const [canViewPii, setCanViewPii] = React.useState(false)\n const [linkLabels, setLinkLabels] = React.useState<Record<string, string>>({})\n\n const rememberLinkOptions = React.useCallback((options: Array<{ value: string; label: string }>) => {\n if (!options.length) return\n setLinkLabels((prev) => {\n let changed = false\n const next = { ...prev }\n for (const option of options) {\n if (!option.value || !option.label) continue\n if (next[option.value] === option.label) continue\n next[option.value] = option.label\n changed = true\n }\n return changed ? next : prev\n })\n }, [])\n\n const loadLinkOptions = React.useCallback(async (query?: string): Promise<Array<{ value: string; label: string }>> => {\n const params = new URLSearchParams({ page: '1', pageSize: '20' })\n if (query && query.trim().length > 0) params.set('search', query.trim())\n try {\n const call = await apiCall<LinkListResponse>(`/api/checkout/links?${params.toString()}`)\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n const options = items\n .map((item): { value: string; label: string } | null => {\n const id = typeof item.id === 'string' ? item.id : null\n const name = typeof item.name === 'string' ? item.name : null\n if (!id || !name) return null\n return { value: id, label: name }\n })\n .filter((option): option is { value: string; label: string } => option !== null)\n rememberLinkOptions(options)\n return options\n } catch {\n return []\n }\n }, [rememberLinkOptions])\n\n React.useEffect(() => {\n if (!initialLinkId || linkLabels[initialLinkId]) return\n let active = true\n void readApiResultOrThrow<LinkLookupResponse>(`/api/checkout/links/${encodeURIComponent(initialLinkId)}`)\n .then((result) => {\n if (!active) return\n const name = typeof result.name === 'string' ? result.name.trim() : ''\n if (!name) return\n rememberLinkOptions([{ value: initialLinkId, label: name }])\n })\n .catch(() => null)\n return () => {\n active = false\n }\n }, [initialLinkId, linkLabels, rememberLinkOptions])\n\n const filterDefs = React.useMemo<FilterDef[]>(() => [\n {\n id: 'linkId',\n label: t('checkout.admin.transactions.filters.link'),\n type: 'combobox',\n placeholder: t('checkout.admin.payLinks.searchPlaceholder'),\n loadOptions: loadLinkOptions,\n formatValue: (value) => linkLabels[value] ?? value,\n },\n {\n id: 'status',\n label: t('checkout.admin.transactions.filters.status'),\n type: 'select',\n options: [\n { value: 'pending', label: t('checkout.admin.transactions.status.pending') },\n { value: 'processing', label: t('checkout.admin.transactions.status.processing') },\n { value: 'completed', label: t('checkout.admin.transactions.status.completed') },\n { value: 'failed', label: t('checkout.admin.transactions.status.failed') },\n { value: 'cancelled', label: t('checkout.admin.transactions.status.cancelled') },\n { value: 'expired', label: t('checkout.admin.transactions.status.expired') },\n ],\n },\n ], [linkLabels, loadLinkOptions, t])\n\n const loadRows = React.useCallback(async () => {\n setLoading(true)\n const params = new URLSearchParams({\n page: String(page),\n pageSize: '25',\n search,\n })\n if (typeof filters.status === 'string' && filters.status) params.set('status', filters.status)\n if (typeof filters.linkId === 'string' && filters.linkId) params.set('linkId', filters.linkId)\n const result = await readApiResultOrThrow<TransactionsResponse>(`/api/checkout/transactions?${params.toString()}`)\n setRows(result.items ?? [])\n rememberLinkOptions(\n (result.items ?? [])\n .map((item) => {\n if (!item.linkId || !item.linkName) return null\n return { value: item.linkId, label: item.linkName }\n })\n .filter((option): option is { value: string; label: string } => option !== null),\n )\n setTotal(result.total ?? 0)\n setTotalPages(result.totalPages ?? 1)\n setCanViewPii(result.canViewPii === true)\n setLoading(false)\n }, [filters.linkId, filters.status, page, rememberLinkOptions, search])\n\n React.useEffect(() => {\n void loadRows()\n }, [loadRows])\n\n const columns = React.useMemo<ColumnDef<TransactionRow>[]>(() => {\n const baseColumns: ColumnDef<TransactionRow>[] = [\n { accessorKey: 'linkName', header: t('checkout.admin.transactions.columns.link') },\n ]\n if (canViewPii) {\n baseColumns.push(\n {\n id: 'customer',\n header: t('checkout.admin.transactions.columns.customer'),\n cell: ({ row }) => [row.original.firstName, row.original.lastName].filter(Boolean).join(' ') || t('checkout.common.emptyValue'),\n },\n {\n accessorKey: 'email',\n header: t('checkout.admin.transactions.columns.email'),\n cell: ({ row }) => row.original.email ?? t('checkout.common.emptyValue'),\n },\n )\n }\n baseColumns.push(\n {\n accessorKey: 'amount',\n header: t('checkout.admin.transactions.columns.amount'),\n cell: ({ row }) => formatAmount(row.original.amount, row.original.currencyCode),\n },\n {\n accessorKey: 'status',\n header: t('checkout.admin.transactions.columns.status'),\n cell: ({ row }) => <Badge variant=\"secondary\">{t(`checkout.admin.transactions.status.${row.original.status}`, row.original.status)}</Badge>,\n },\n {\n accessorKey: 'paymentStatus',\n header: t('checkout.admin.transactions.columns.paymentStatus'),\n cell: ({ row }) => row.original.paymentStatus ?? t('checkout.common.emptyValue'),\n },\n {\n accessorKey: 'createdAt',\n header: t('checkout.admin.transactions.columns.date'),\n cell: ({ row }) => formatDate(row.original.createdAt),\n },\n )\n return baseColumns\n }, [canViewPii, t])\n\n return (\n <Page>\n <PageBody>\n <DataTable\n title={t('checkout.admin.transactions.title')}\n columns={columns}\n data={rows}\n isLoading={loading}\n searchValue={search}\n onSearchChange={(value) => { setSearch(value); setPage(1) }}\n searchPlaceholder={t('checkout.admin.transactions.searchPlaceholder')}\n filters={filterDefs}\n filterValues={filters}\n onFiltersApply={(next) => { setFilters(next); setPage(1) }}\n onFiltersClear={() => { setFilters({}); setPage(1) }}\n pagination={{ page, pageSize: 25, total, totalPages, onPageChange: setPage }}\n perspective={{ tableId: extensionPoints.hosts.transactionsTable.tableId }}\n rowClickActionIds={['view']}\n rowActions={(row) => <RowActions items={[{ id: 'view', label: t('checkout.admin.transactions.actions.viewDetail'), href: `/backend/checkout/transactions/${encodeURIComponent(row.id)}` }]} />}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAgN2B;AA/M3B,YAAY,WAAW;AACvB,SAAS,uBAAuB;AAChC,SAAS,uBAAuB;AAEhC,SAAS,YAAY;AAErB,SAAS,MAAM,gBAAgB;AAC/B,SAAS,iBAAiB;AAC1B,SAAS,kBAAkB;AAC3B,SAAS,SAAS,4BAA4B;AAC9C,SAAS,aAAa;AAgCtB,SAAS,WAAW,OAA0C;AAC5D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,IAAI,KAAK,KAAK;AAC7B,SAAO,OAAO,MAAM,OAAO,QAAQ,CAAC,IAAI,QAAQ,OAAO,mBAAmB;AAC5E;AAEA,SAAS,aAAa,QAAmC,cAA8B;AACrF,QAAM,WAAW,OAAO,WAAW,WAAW,SAAS;AACvD,MAAI;AACF,WAAO,IAAI,KAAK,aAAa,QAAW,EAAE,OAAO,YAAY,UAAU,aAAa,CAAC,EAAE,OAAO,QAAQ;AAAA,EACxG,QAAQ;AACN,WAAO,GAAG,SAAS,QAAQ,CAAC,CAAC,IAAI,YAAY;AAAA,EAC/C;AACF;AAEe,SAAR,2BAA4C;AACjD,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,gBAAgB;AACrC,QAAM,gBAAgB,MAAM,QAAQ,MAAM;AACxC,UAAM,MAAM,aAAa,IAAI,QAAQ;AACrC,WAAO,OAAO,IAAI,KAAK,EAAE,SAAS,IAAI,IAAI,KAAK,IAAI;AAAA,EACrD,GAAG,CAAC,YAAY,CAAC;AACjB,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAA2B,CAAC,CAAC;AAC3D,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAS,CAAC;AACxC,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAS,EAAE;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAuB,MAAO,gBAAgB,EAAE,QAAQ,cAAc,IAAI,CAAC,CAAE;AACjH,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAS,CAAC;AAC1C,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,CAAC;AACpD,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,IAAI;AACjD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAiC,CAAC,CAAC;AAE7E,QAAM,sBAAsB,MAAM,YAAY,CAAC,YAAqD;AAClG,QAAI,CAAC,QAAQ,OAAQ;AACrB,kBAAc,CAAC,SAAS;AACtB,UAAI,UAAU;AACd,YAAM,OAAO,EAAE,GAAG,KAAK;AACvB,iBAAW,UAAU,SAAS;AAC5B,YAAI,CAAC,OAAO,SAAS,CAAC,OAAO,MAAO;AACpC,YAAI,KAAK,OAAO,KAAK,MAAM,OAAO,MAAO;AACzC,aAAK,OAAO,KAAK,IAAI,OAAO;AAC5B,kBAAU;AAAA,MACZ;AACA,aAAO,UAAU,OAAO;AAAA,IAC1B,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,kBAAkB,MAAM,YAAY,OAAO,UAAqE;AACpH,UAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,KAAK,UAAU,KAAK,CAAC;AAChE,QAAI,SAAS,MAAM,KAAK,EAAE,SAAS,EAAG,QAAO,IAAI,UAAU,MAAM,KAAK,CAAC;AACvE,QAAI;AACF,YAAM,OAAO,MAAM,QAA0B,uBAAuB,OAAO,SAAS,CAAC,EAAE;AACvF,UAAI,CAAC,KAAK,GAAI,QAAO,CAAC;AACtB,YAAM,QAAQ,MAAM,QAAQ,KAAK,QAAQ,KAAK,IAAI,KAAK,OAAO,QAAQ,CAAC;AACvE,YAAM,UAAU,MACb,IAAI,CAAC,SAAkD;AACtD,cAAM,KAAK,OAAO,KAAK,OAAO,WAAW,KAAK,KAAK;AACnD,cAAM,OAAO,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;AACzD,YAAI,CAAC,MAAM,CAAC,KAAM,QAAO;AACzB,eAAO,EAAE,OAAO,IAAI,OAAO,KAAK;AAAA,MAClC,CAAC,EACA,OAAO,CAAC,WAAuD,WAAW,IAAI;AACjF,0BAAoB,OAAO;AAC3B,aAAO;AAAA,IACT,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF,GAAG,CAAC,mBAAmB,CAAC;AAExB,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,iBAAiB,WAAW,aAAa,EAAG;AACjD,QAAI,SAAS;AACb,SAAK,qBAAyC,uBAAuB,mBAAmB,aAAa,CAAC,EAAE,EACrG,KAAK,CAAC,WAAW;AAChB,UAAI,CAAC,OAAQ;AACb,YAAM,OAAO,OAAO,OAAO,SAAS,WAAW,OAAO,KAAK,KAAK,IAAI;AACpE,UAAI,CAAC,KAAM;AACX,0BAAoB,CAAC,EAAE,OAAO,eAAe,OAAO,KAAK,CAAC,CAAC;AAAA,IAC7D,CAAC,EACA,MAAM,MAAM,IAAI;AACnB,WAAO,MAAM;AACX,eAAS;AAAA,IACX;AAAA,EACF,GAAG,CAAC,eAAe,YAAY,mBAAmB,CAAC;AAEnD,QAAM,aAAa,MAAM,QAAqB,MAAM;AAAA,IAClD;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,EAAE,0CAA0C;AAAA,MACnD,MAAM;AAAA,MACN,aAAa,EAAE,2CAA2C;AAAA,MAC1D,aAAa;AAAA,MACb,aAAa,CAAC,UAAU,WAAW,KAAK,KAAK;AAAA,IAC/C;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,OAAO,EAAE,4CAA4C;AAAA,MACrD,MAAM;AAAA,MACN,SAAS;AAAA,QACP,EAAE,OAAO,WAAW,OAAO,EAAE,4CAA4C,EAAE;AAAA,QAC3E,EAAE,OAAO,cAAc,OAAO,EAAE,+CAA+C,EAAE;AAAA,QACjF,EAAE,OAAO,aAAa,OAAO,EAAE,8CAA8C,EAAE;AAAA,QAC/E,EAAE,OAAO,UAAU,OAAO,EAAE,2CAA2C,EAAE;AAAA,QACzE,EAAE,OAAO,aAAa,OAAO,EAAE,8CAA8C,EAAE;AAAA,QAC/E,EAAE,OAAO,WAAW,OAAO,EAAE,4CAA4C,EAAE;AAAA,MAC7E;AAAA,IACF;AAAA,EACF,GAAG,CAAC,YAAY,iBAAiB,CAAC,CAAC;AAEnC,QAAM,WAAW,MAAM,YAAY,YAAY;AAC7C,eAAW,IAAI;AACf,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC,MAAM,OAAO,IAAI;AAAA,MACjB,UAAU;AAAA,MACV;AAAA,IACF,CAAC;AACD,QAAI,OAAO,QAAQ,WAAW,YAAY,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AAC7F,QAAI,OAAO,QAAQ,WAAW,YAAY,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AAC7F,UAAM,SAAS,MAAM,qBAA2C,8BAA8B,OAAO,SAAS,CAAC,EAAE;AACjH,YAAQ,OAAO,SAAS,CAAC,CAAC;AAC1B;AAAA,OACG,OAAO,SAAS,CAAC,GACf,IAAI,CAAC,SAAS;AACb,YAAI,CAAC,KAAK,UAAU,CAAC,KAAK,SAAU,QAAO;AAC3C,eAAO,EAAE,OAAO,KAAK,QAAQ,OAAO,KAAK,SAAS;AAAA,MACpD,CAAC,EACA,OAAO,CAAC,WAAuD,WAAW,IAAI;AAAA,IACnF;AACA,aAAS,OAAO,SAAS,CAAC;AAC1B,kBAAc,OAAO,cAAc,CAAC;AACpC,kBAAc,OAAO,eAAe,IAAI;AACxC,eAAW,KAAK;AAAA,EAClB,GAAG,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,MAAM,qBAAqB,MAAM,CAAC;AAEtE,QAAM,UAAU,MAAM;AACpB,SAAK,SAAS;AAAA,EAChB,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,UAAU,MAAM,QAAqC,MAAM;AAC/D,UAAM,cAA2C;AAAA,MAC/C,EAAE,aAAa,YAAY,QAAQ,EAAE,0CAA0C,EAAE;AAAA,IACnF;AACA,QAAI,YAAY;AACd,kBAAY;AAAA,QACV;AAAA,UACE,IAAI;AAAA,UACJ,QAAQ,EAAE,8CAA8C;AAAA,UACxD,MAAM,CAAC,EAAE,IAAI,MAAM,CAAC,IAAI,SAAS,WAAW,IAAI,SAAS,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,KAAK,EAAE,4BAA4B;AAAA,QAChI;AAAA,QACA;AAAA,UACE,aAAa;AAAA,UACb,QAAQ,EAAE,2CAA2C;AAAA,UACrD,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,SAAS,EAAE,4BAA4B;AAAA,QACzE;AAAA,MACF;AAAA,IACF;AACA,gBAAY;AAAA,MACV;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,4CAA4C;AAAA,QACtD,MAAM,CAAC,EAAE,IAAI,MAAM,aAAa,IAAI,SAAS,QAAQ,IAAI,SAAS,YAAY;AAAA,MAChF;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,4CAA4C;AAAA,QACtD,MAAM,CAAC,EAAE,IAAI,MAAM,oBAAC,SAAM,SAAQ,aAAa,YAAE,sCAAsC,IAAI,SAAS,MAAM,IAAI,IAAI,SAAS,MAAM,GAAE;AAAA,MACrI;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,mDAAmD;AAAA,QAC7D,MAAM,CAAC,EAAE,IAAI,MAAM,IAAI,SAAS,iBAAiB,EAAE,4BAA4B;AAAA,MACjF;AAAA,MACA;AAAA,QACE,aAAa;AAAA,QACb,QAAQ,EAAE,0CAA0C;AAAA,QACpD,MAAM,CAAC,EAAE,IAAI,MAAM,WAAW,IAAI,SAAS,SAAS;AAAA,MACtD;AAAA,IACF;AACA,WAAO;AAAA,EACT,GAAG,CAAC,YAAY,CAAC,CAAC;AAElB,SACE,oBAAC,QACC,8BAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,mCAAmC;AAAA,MAC5C;AAAA,MACA,MAAM;AAAA,MACN,WAAW;AAAA,MACX,aAAa;AAAA,MACb,gBAAgB,CAAC,UAAU;AAAE,kBAAU,KAAK;AAAG,gBAAQ,CAAC;AAAA,MAAE;AAAA,MAC1D,mBAAmB,EAAE,+CAA+C;AAAA,MACpE,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB,CAAC,SAAS;AAAE,mBAAW,IAAI;AAAG,gBAAQ,CAAC;AAAA,MAAE;AAAA,MACzD,gBAAgB,MAAM;AAAE,mBAAW,CAAC,CAAC;AAAG,gBAAQ,CAAC;AAAA,MAAE;AAAA,MACnD,YAAY,EAAE,MAAM,UAAU,IAAI,OAAO,YAAY,cAAc,QAAQ;AAAA,MAC3E,aAAa,EAAE,SAAS,gBAAgB,MAAM,kBAAkB,QAAQ;AAAA,MACxE,mBAAmB,CAAC,MAAM;AAAA,MAC1B,YAAY,CAAC,QAAQ,oBAAC,cAAW,OAAO,CAAC,EAAE,IAAI,QAAQ,OAAO,EAAE,gDAAgD,GAAG,MAAM,kCAAkC,mBAAmB,IAAI,EAAE,CAAC,GAAG,CAAC,GAAG;AAAA;AAAA,EAC9L,GACF,GACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -496,7 +496,7 @@ registerCommand(createLinkCommand);
|
|
|
496
496
|
registerCommand(updateLinkCommand);
|
|
497
497
|
registerCommand(deleteLinkCommand);
|
|
498
498
|
function serializeLinkRecord(record) {
|
|
499
|
-
return serializeTemplateOrLink(record);
|
|
499
|
+
return { ...serializeTemplateOrLink(record), completionCount: record.completionCount ?? 0 };
|
|
500
500
|
}
|
|
501
501
|
export {
|
|
502
502
|
serializeLinkRecord
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/checkout/commands/links.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { buildCustomFieldResetMap, loadCustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CheckoutLink, CheckoutLinkTemplate, CheckoutTransaction } from '../data/entities'\nimport { createLinkSchema, updateLinkSchema } from '../data/validators'\nimport { CHECKOUT_ENTITY_IDS } from '../lib/constants'\nimport {\n ensureGatewayProviderConfigured,\n type PaymentGatewayDescriptorService,\n} from '../lib/gatewayProviderAvailability'\nimport { emitCheckoutEvent } from '../events'\nimport {\n deriveConfiguredCurrencies,\n ensureUniqueSlug,\n hashCheckoutPassword,\n isCheckoutLinkPublic,\n parseCheckoutInput,\n pickExplicitParsedOverrides,\n resolveLoadedCheckoutCustomFields,\n serializeTemplateOrLink,\n toMoneyString,\n toTemplateOrLinkMutationInput,\n validateDescriptorCurrencies,\n} from '../lib/utils'\nimport {\n captureLinkSnapshot,\n createLinkFromSnapshot,\n extractUndoPayload,\n readCommandId,\n resolveCommandScope,\n restoreLinkFromSnapshot,\n toCheckoutAuditSnapshot,\n type CheckoutLinkSnapshot,\n} from './shared'\n\nconst ACTIVE_TRANSACTION_STATUSES = ['pending', 'processing']\n\ntype CheckoutLinkUndoPayload = {\n before?: CheckoutLinkSnapshot | null\n after?: CheckoutLinkSnapshot | null\n}\n\nasync function resolveRestoredLinkSlug(\n em: EntityManager,\n snapshot: CheckoutLinkSnapshot,\n): Promise<string> {\n return ensureUniqueSlug(\n em,\n {\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n },\n snapshot.slug,\n snapshot.title ?? snapshot.name,\n snapshot.id,\n )\n}\n\nconst createLinkCommand: CommandHandler<Record<string, unknown>, { id: string; slug: string }> = {\n id: 'checkout.link.create',\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, createLinkSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n\n let sourceValues = parsed\n let templateCustomFields: Record<string, unknown> = {}\n if (parsed.templateId) {\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: parsed.templateId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!template) throw new CrudHttpError(404, { error: 'Template not found' })\n sourceValues = toTemplateOrLinkMutationInput(template, {\n ...pickExplicitParsedOverrides(rawInput, parsed),\n templateId: template.id,\n })\n const loaded = await loadCustomFieldValues({\n em,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordIds: [template.id],\n tenantIdByRecord: { [template.id]: scope.tenantId },\n organizationIdByRecord: { [template.id]: scope.organizationId },\n })\n templateCustomFields = resolveLoadedCheckoutCustomFields(loaded[template.id])\n }\n\n validateDescriptorCurrencies(sourceValues.gatewayProviderKey ?? null, deriveConfiguredCurrencies(sourceValues))\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(sourceValues.gatewayProviderKey ?? null, descriptorService, scope)\n if (isCheckoutLinkPublic(sourceValues.status) && !sourceValues.gatewayProviderKey) {\n throw new CrudHttpError(422, { error: 'A payment gateway must be configured before this link can be published' })\n }\n const slug = await ensureUniqueSlug(\n em,\n scope,\n sourceValues.slug ?? null,\n sourceValues.title ?? sourceValues.name,\n )\n const link = em.create(CheckoutLink, {\n ...sourceValues,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n completionCount: 0,\n activeReservationCount: 0,\n isLocked: false,\n fixedPriceAmount: toMoneyString(sourceValues.fixedPriceAmount),\n fixedPriceOriginalAmount: toMoneyString(sourceValues.fixedPriceOriginalAmount),\n customAmountMin: toMoneyString(sourceValues.customAmountMin),\n customAmountMax: toMoneyString(sourceValues.customAmountMax),\n slug,\n passwordHash: await hashCheckoutPassword(sourceValues.password),\n } as any)\n em.persist(link)\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: { ...templateCustomFields, ...customFields },\n })\n await emitCheckoutEvent('checkout.link.created', {\n id: link.id,\n slug: link.slug,\n status: link.status,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n if (link.status === 'active') {\n await emitCheckoutEvent('checkout.link.published', {\n id: link.id,\n slug: link.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n }\n return { id: link.id, slug: link.slug }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(em, CheckoutLink, { id: result.id })\n if (!link) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return captureLinkSnapshot(link, custom)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as CheckoutLinkSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.links.create', 'Create pay link'),\n resourceKind: 'checkout.link',\n resourceId: result.id,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ? toCheckoutAuditSnapshot(after) : null,\n payload: {\n undo: {\n after: after ?? null,\n } satisfies CheckoutLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const after = extractUndoPayload<CheckoutLinkUndoPayload>(logEntry)?.after\n if (!after) return\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const reset = buildCustomFieldResetMap(undefined, after.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n values: reset,\n notify: false,\n })\n }\n const link = await em.findOne(CheckoutLink, { id: after.id })\n if (!link) return\n link.deletedAt = new Date()\n await em.flush()\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<CheckoutLinkSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for checkout link create' })\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n let link = await findOneWithDecryption(\n em,\n CheckoutLink,\n { id: after.id },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (link) {\n restoreLinkFromSnapshot(link, after)\n link.deletedAt = null\n link.slug = await resolveRestoredLinkSlug(em, after)\n } else {\n link = em.create(CheckoutLink, {\n ...createLinkFromSnapshot(after),\n slug: await resolveRestoredLinkSlug(em, after),\n })\n em.persist(link)\n }\n await em.flush()\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n values: reset,\n notify: false,\n })\n }\n await emitCheckoutEvent('checkout.link.created', {\n id: link.id,\n slug: link.slug,\n status: link.status,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n }).catch(() => undefined)\n if (link.status === 'active') {\n await emitCheckoutEvent('checkout.link.published', {\n id: link.id,\n slug: link.slug,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n }).catch(() => undefined)\n }\n return { id: link.id, slug: link.slug }\n },\n}\n\nconst updateLinkCommand: CommandHandler<Record<string, unknown>, { ok: true; slug: string }> = {\n id: 'checkout.link.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseCheckoutInput(rawInput, updateLinkSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: parsed.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return { before: captureLinkSnapshot(link, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, updateLinkSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: parsed.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) throw new CrudHttpError(404, { error: 'Link not found' })\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'checkout.link',\n resourceId: link.id,\n current: link.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n if (link.isLocked) {\n throw new CrudHttpError(422, { error: 'This link has active transactions and cannot be edited' })\n }\n const nextValues = toTemplateOrLinkMutationInput(link, parsed)\n validateDescriptorCurrencies(\n parsed.gatewayProviderKey ?? link.gatewayProviderKey ?? null,\n deriveConfiguredCurrencies(nextValues),\n )\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(nextValues.gatewayProviderKey ?? null, descriptorService, scope)\n if (isCheckoutLinkPublic(nextValues.status) && !nextValues.gatewayProviderKey) {\n throw new CrudHttpError(422, { error: 'A payment gateway must be configured before this link can be published' })\n }\n const slug = parsed.slug !== undefined || parsed.name !== undefined || parsed.title !== undefined\n ? await ensureUniqueSlug(em, scope, parsed.slug ?? link.slug, parsed.title ?? parsed.name ?? link.title ?? link.name, link.id)\n : link.slug\n const passwordHash = parsed.password !== undefined ? await hashCheckoutPassword(parsed.password) : link.passwordHash\n const previousStatus = link.status\n Object.assign(link, {\n ...parsed,\n fixedPriceAmount: parsed.fixedPriceAmount !== undefined ? toMoneyString(parsed.fixedPriceAmount) : link.fixedPriceAmount,\n fixedPriceOriginalAmount: parsed.fixedPriceOriginalAmount !== undefined ? toMoneyString(parsed.fixedPriceOriginalAmount) : link.fixedPriceOriginalAmount,\n customAmountMin: parsed.customAmountMin !== undefined ? toMoneyString(parsed.customAmountMin) : link.customAmountMin,\n customAmountMax: parsed.customAmountMax !== undefined ? toMoneyString(parsed.customAmountMax) : link.customAmountMax,\n slug,\n passwordHash,\n })\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: customFields,\n })\n await emitCheckoutEvent('checkout.link.updated', {\n id: link.id,\n slug: link.slug,\n status: link.status,\n previousStatus,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n if (previousStatus !== 'active' && link.status === 'active') {\n await emitCheckoutEvent('checkout.link.published', {\n id: link.id,\n slug: link.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n }\n return { ok: true, slug: link.slug }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(em, CheckoutLink, { slug: result.slug, deletedAt: null })\n if (!link) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return captureLinkSnapshot(link, custom)\n },\n buildLog: async ({ snapshots, result }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as CheckoutLinkSnapshot | null | undefined\n const after = snapshots.after as CheckoutLinkSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.links.update', 'Update pay link'),\n resourceKind: 'checkout.link',\n resourceId: after?.id ?? before?.id ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ? toCheckoutAuditSnapshot(before) : null,\n snapshotAfter: after ? toCheckoutAuditSnapshot(after) : null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies CheckoutLinkUndoPayload,\n },\n context: result.slug ? { slug: result.slug } : null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<CheckoutLinkUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const link = await em.findOne(CheckoutLink, { id: before.id, deletedAt: null })\n if (!link) return\n restoreLinkFromSnapshot(link, before)\n link.slug = await resolveRestoredLinkSlug(em, before)\n await em.flush()\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n },\n}\n\nconst deleteLinkCommand: CommandHandler<Record<string, unknown>, { ok: true }> = {\n id: 'checkout.link.delete',\n async prepare(rawInput, ctx) {\n const linkId = readCommandId(rawInput, 'Link id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: linkId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return { before: captureLinkSnapshot(link, custom) }\n },\n async execute(rawInput, ctx) {\n const linkId = readCommandId(rawInput, 'Link id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: linkId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) throw new CrudHttpError(404, { error: 'Link not found' })\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'checkout.link',\n resourceId: link.id,\n current: link.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n const activeCount = await em.count(CheckoutTransaction, {\n linkId: link.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n status: { $in: ACTIVE_TRANSACTION_STATUSES as Array<CheckoutTransaction['status']> },\n })\n if (activeCount > 0) {\n throw new CrudHttpError(422, { error: 'This link has active transactions and cannot be deleted' })\n }\n link.deletedAt = new Date()\n await em.flush()\n await emitCheckoutEvent('checkout.link.deleted', {\n id: link.id,\n slug: link.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n return { ok: true }\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as CheckoutLinkSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.links.delete', 'Delete pay link'),\n resourceKind: 'checkout.link',\n resourceId: before?.id ?? readCommandId(input, 'Link id is required'),\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n snapshotBefore: before ? toCheckoutAuditSnapshot(before) : null,\n payload: {\n undo: {\n before: before ?? null,\n } satisfies CheckoutLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<CheckoutLinkUndoPayload>(logEntry)?.before\n if (!before) return\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n let link = await em.findOne(CheckoutLink, { id: before.id })\n if (link) {\n restoreLinkFromSnapshot(link, before)\n link.slug = await resolveRestoredLinkSlug(em, before)\n } else {\n link = em.create(CheckoutLink, {\n ...createLinkFromSnapshot(before),\n slug: await resolveRestoredLinkSlug(em, before),\n })\n em.persist(link)\n }\n await em.flush()\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n },\n}\n\nregisterCommand(createLinkCommand)\nregisterCommand(updateLinkCommand)\nregisterCommand(deleteLinkCommand)\n\nexport function serializeLinkRecord(record: CheckoutLink) {\n return serializeTemplateOrLink(record)\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { buildCustomFieldResetMap, loadCustomFieldSnapshot } from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CheckoutLink, CheckoutLinkTemplate, CheckoutTransaction } from '../data/entities'\nimport { createLinkSchema, updateLinkSchema } from '../data/validators'\nimport { CHECKOUT_ENTITY_IDS } from '../lib/constants'\nimport {\n ensureGatewayProviderConfigured,\n type PaymentGatewayDescriptorService,\n} from '../lib/gatewayProviderAvailability'\nimport { emitCheckoutEvent } from '../events'\nimport {\n deriveConfiguredCurrencies,\n ensureUniqueSlug,\n hashCheckoutPassword,\n isCheckoutLinkPublic,\n parseCheckoutInput,\n pickExplicitParsedOverrides,\n resolveLoadedCheckoutCustomFields,\n serializeTemplateOrLink,\n toMoneyString,\n toTemplateOrLinkMutationInput,\n validateDescriptorCurrencies,\n} from '../lib/utils'\nimport {\n captureLinkSnapshot,\n createLinkFromSnapshot,\n extractUndoPayload,\n readCommandId,\n resolveCommandScope,\n restoreLinkFromSnapshot,\n toCheckoutAuditSnapshot,\n type CheckoutLinkSnapshot,\n} from './shared'\n\nconst ACTIVE_TRANSACTION_STATUSES = ['pending', 'processing']\n\ntype CheckoutLinkUndoPayload = {\n before?: CheckoutLinkSnapshot | null\n after?: CheckoutLinkSnapshot | null\n}\n\nasync function resolveRestoredLinkSlug(\n em: EntityManager,\n snapshot: CheckoutLinkSnapshot,\n): Promise<string> {\n return ensureUniqueSlug(\n em,\n {\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n },\n snapshot.slug,\n snapshot.title ?? snapshot.name,\n snapshot.id,\n )\n}\n\nconst createLinkCommand: CommandHandler<Record<string, unknown>, { id: string; slug: string }> = {\n id: 'checkout.link.create',\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, createLinkSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n\n let sourceValues = parsed\n let templateCustomFields: Record<string, unknown> = {}\n if (parsed.templateId) {\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: parsed.templateId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!template) throw new CrudHttpError(404, { error: 'Template not found' })\n sourceValues = toTemplateOrLinkMutationInput(template, {\n ...pickExplicitParsedOverrides(rawInput, parsed),\n templateId: template.id,\n })\n const loaded = await loadCustomFieldValues({\n em,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordIds: [template.id],\n tenantIdByRecord: { [template.id]: scope.tenantId },\n organizationIdByRecord: { [template.id]: scope.organizationId },\n })\n templateCustomFields = resolveLoadedCheckoutCustomFields(loaded[template.id])\n }\n\n validateDescriptorCurrencies(sourceValues.gatewayProviderKey ?? null, deriveConfiguredCurrencies(sourceValues))\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(sourceValues.gatewayProviderKey ?? null, descriptorService, scope)\n if (isCheckoutLinkPublic(sourceValues.status) && !sourceValues.gatewayProviderKey) {\n throw new CrudHttpError(422, { error: 'A payment gateway must be configured before this link can be published' })\n }\n const slug = await ensureUniqueSlug(\n em,\n scope,\n sourceValues.slug ?? null,\n sourceValues.title ?? sourceValues.name,\n )\n const link = em.create(CheckoutLink, {\n ...sourceValues,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n completionCount: 0,\n activeReservationCount: 0,\n isLocked: false,\n fixedPriceAmount: toMoneyString(sourceValues.fixedPriceAmount),\n fixedPriceOriginalAmount: toMoneyString(sourceValues.fixedPriceOriginalAmount),\n customAmountMin: toMoneyString(sourceValues.customAmountMin),\n customAmountMax: toMoneyString(sourceValues.customAmountMax),\n slug,\n passwordHash: await hashCheckoutPassword(sourceValues.password),\n } as any)\n em.persist(link)\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: { ...templateCustomFields, ...customFields },\n })\n await emitCheckoutEvent('checkout.link.created', {\n id: link.id,\n slug: link.slug,\n status: link.status,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n if (link.status === 'active') {\n await emitCheckoutEvent('checkout.link.published', {\n id: link.id,\n slug: link.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n }\n return { id: link.id, slug: link.slug }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(em, CheckoutLink, { id: result.id })\n if (!link) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return captureLinkSnapshot(link, custom)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as CheckoutLinkSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.links.create', 'Create pay link'),\n resourceKind: 'checkout.link',\n resourceId: result.id,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ? toCheckoutAuditSnapshot(after) : null,\n payload: {\n undo: {\n after: after ?? null,\n } satisfies CheckoutLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const after = extractUndoPayload<CheckoutLinkUndoPayload>(logEntry)?.after\n if (!after) return\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const reset = buildCustomFieldResetMap(undefined, after.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n values: reset,\n notify: false,\n })\n }\n const link = await em.findOne(CheckoutLink, { id: after.id })\n if (!link) return\n link.deletedAt = new Date()\n await em.flush()\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<CheckoutLinkSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for checkout link create' })\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n let link = await findOneWithDecryption(\n em,\n CheckoutLink,\n { id: after.id },\n {},\n { tenantId: after.tenantId, organizationId: after.organizationId },\n )\n if (link) {\n restoreLinkFromSnapshot(link, after)\n link.deletedAt = null\n link.slug = await resolveRestoredLinkSlug(em, after)\n } else {\n link = em.create(CheckoutLink, {\n ...createLinkFromSnapshot(after),\n slug: await resolveRestoredLinkSlug(em, after),\n })\n em.persist(link)\n }\n await em.flush()\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n values: reset,\n notify: false,\n })\n }\n await emitCheckoutEvent('checkout.link.created', {\n id: link.id,\n slug: link.slug,\n status: link.status,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n }).catch(() => undefined)\n if (link.status === 'active') {\n await emitCheckoutEvent('checkout.link.published', {\n id: link.id,\n slug: link.slug,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n }).catch(() => undefined)\n }\n return { id: link.id, slug: link.slug }\n },\n}\n\nconst updateLinkCommand: CommandHandler<Record<string, unknown>, { ok: true; slug: string }> = {\n id: 'checkout.link.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseCheckoutInput(rawInput, updateLinkSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: parsed.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return { before: captureLinkSnapshot(link, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, updateLinkSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: parsed.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) throw new CrudHttpError(404, { error: 'Link not found' })\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'checkout.link',\n resourceId: link.id,\n current: link.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n if (link.isLocked) {\n throw new CrudHttpError(422, { error: 'This link has active transactions and cannot be edited' })\n }\n const nextValues = toTemplateOrLinkMutationInput(link, parsed)\n validateDescriptorCurrencies(\n parsed.gatewayProviderKey ?? link.gatewayProviderKey ?? null,\n deriveConfiguredCurrencies(nextValues),\n )\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(nextValues.gatewayProviderKey ?? null, descriptorService, scope)\n if (isCheckoutLinkPublic(nextValues.status) && !nextValues.gatewayProviderKey) {\n throw new CrudHttpError(422, { error: 'A payment gateway must be configured before this link can be published' })\n }\n const slug = parsed.slug !== undefined || parsed.name !== undefined || parsed.title !== undefined\n ? await ensureUniqueSlug(em, scope, parsed.slug ?? link.slug, parsed.title ?? parsed.name ?? link.title ?? link.name, link.id)\n : link.slug\n const passwordHash = parsed.password !== undefined ? await hashCheckoutPassword(parsed.password) : link.passwordHash\n const previousStatus = link.status\n Object.assign(link, {\n ...parsed,\n fixedPriceAmount: parsed.fixedPriceAmount !== undefined ? toMoneyString(parsed.fixedPriceAmount) : link.fixedPriceAmount,\n fixedPriceOriginalAmount: parsed.fixedPriceOriginalAmount !== undefined ? toMoneyString(parsed.fixedPriceOriginalAmount) : link.fixedPriceOriginalAmount,\n customAmountMin: parsed.customAmountMin !== undefined ? toMoneyString(parsed.customAmountMin) : link.customAmountMin,\n customAmountMax: parsed.customAmountMax !== undefined ? toMoneyString(parsed.customAmountMax) : link.customAmountMax,\n slug,\n passwordHash,\n })\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: customFields,\n })\n await emitCheckoutEvent('checkout.link.updated', {\n id: link.id,\n slug: link.slug,\n status: link.status,\n previousStatus,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n if (previousStatus !== 'active' && link.status === 'active') {\n await emitCheckoutEvent('checkout.link.published', {\n id: link.id,\n slug: link.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n }\n return { ok: true, slug: link.slug }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const link = await findOneWithDecryption(em, CheckoutLink, { slug: result.slug, deletedAt: null })\n if (!link) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return captureLinkSnapshot(link, custom)\n },\n buildLog: async ({ snapshots, result }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as CheckoutLinkSnapshot | null | undefined\n const after = snapshots.after as CheckoutLinkSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.links.update', 'Update pay link'),\n resourceKind: 'checkout.link',\n resourceId: after?.id ?? before?.id ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ? toCheckoutAuditSnapshot(before) : null,\n snapshotAfter: after ? toCheckoutAuditSnapshot(after) : null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies CheckoutLinkUndoPayload,\n },\n context: result.slug ? { slug: result.slug } : null,\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<CheckoutLinkUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const link = await em.findOne(CheckoutLink, { id: before.id, deletedAt: null })\n if (!link) return\n restoreLinkFromSnapshot(link, before)\n link.slug = await resolveRestoredLinkSlug(em, before)\n await em.flush()\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n },\n}\n\nconst deleteLinkCommand: CommandHandler<Record<string, unknown>, { ok: true }> = {\n id: 'checkout.link.delete',\n async prepare(rawInput, ctx) {\n const linkId = readCommandId(rawInput, 'Link id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: linkId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n return { before: captureLinkSnapshot(link, custom) }\n },\n async execute(rawInput, ctx) {\n const linkId = readCommandId(rawInput, 'Link id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const link = await findOneWithDecryption(em, CheckoutLink, {\n id: linkId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!link) throw new CrudHttpError(404, { error: 'Link not found' })\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: 'checkout.link',\n resourceId: link.id,\n current: link.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n const activeCount = await em.count(CheckoutTransaction, {\n linkId: link.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n status: { $in: ACTIVE_TRANSACTION_STATUSES as Array<CheckoutTransaction['status']> },\n })\n if (activeCount > 0) {\n throw new CrudHttpError(422, { error: 'This link has active transactions and cannot be deleted' })\n }\n link.deletedAt = new Date()\n await em.flush()\n await emitCheckoutEvent('checkout.link.deleted', {\n id: link.id,\n slug: link.slug,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n return { ok: true }\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as CheckoutLinkSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.links.delete', 'Delete pay link'),\n resourceKind: 'checkout.link',\n resourceId: before?.id ?? readCommandId(input, 'Link id is required'),\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n snapshotBefore: before ? toCheckoutAuditSnapshot(before) : null,\n payload: {\n undo: {\n before: before ?? null,\n } satisfies CheckoutLinkUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<CheckoutLinkUndoPayload>(logEntry)?.before\n if (!before) return\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n let link = await em.findOne(CheckoutLink, { id: before.id })\n if (link) {\n restoreLinkFromSnapshot(link, before)\n link.slug = await resolveRestoredLinkSlug(em, before)\n } else {\n link = em.create(CheckoutLink, {\n ...createLinkFromSnapshot(before),\n slug: await resolveRestoredLinkSlug(em, before),\n })\n em.persist(link)\n }\n await em.flush()\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n },\n}\n\nregisterCommand(createLinkCommand)\nregisterCommand(updateLinkCommand)\nregisterCommand(deleteLinkCommand)\n\nexport function serializeLinkRecord(record: CheckoutLink) {\n // completionCount is link-only state (templates have no completions), so it is\n // added here rather than in the shared template/link serializer. It is the\n // observable proof that a terminal transaction was applied exactly once, which\n // TC-CHKT-043 asserts and could not previously read from any API.\n return { ...serializeTemplateOrLink(record), completionCount: record.completionCount ?? 0 }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
parseCheckoutInput,
|
|
11
11
|
toMoneyString
|
|
12
12
|
} from "../lib/utils.js";
|
|
13
|
+
import { assertValidCheckoutStatusTransition } from "../lib/transaction-status-machine.js";
|
|
13
14
|
function resolveTransactionScope(input) {
|
|
14
15
|
if (!input.organizationId || !input.tenantId) {
|
|
15
16
|
throw new CrudHttpError(400, { error: "Transaction scope is required" });
|
|
@@ -145,10 +146,39 @@ const updateTransactionStatusCommand = {
|
|
|
145
146
|
const nextStatus = parsed.status;
|
|
146
147
|
const previousTerminal = isTerminalCheckoutStatus(previousStatus);
|
|
147
148
|
const nextTerminal = isTerminalCheckoutStatus(nextStatus);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
await tx.
|
|
149
|
+
assertValidCheckoutStatusTransition(previousStatus, nextStatus);
|
|
150
|
+
const newPaymentStatus = parsed.paymentStatus ?? transaction.paymentStatus ?? null;
|
|
151
|
+
const newGatewayTransactionId = parsed.gatewayTransactionId ?? transaction.gatewayTransactionId ?? null;
|
|
152
|
+
const affected = await tx.nativeUpdate(
|
|
153
|
+
CheckoutTransaction,
|
|
154
|
+
{
|
|
155
|
+
id: parsed.id,
|
|
156
|
+
organizationId: scope.organizationId,
|
|
157
|
+
tenantId: scope.tenantId,
|
|
158
|
+
status: previousStatus
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
status: nextStatus,
|
|
162
|
+
paymentStatus: newPaymentStatus,
|
|
163
|
+
gatewayTransactionId: newGatewayTransactionId,
|
|
164
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
if (affected === 0) {
|
|
168
|
+
const actualTx = await tx.findOne(
|
|
169
|
+
CheckoutTransaction,
|
|
170
|
+
{ id: parsed.id, organizationId: scope.organizationId, tenantId: scope.tenantId },
|
|
171
|
+
{ fields: ["status"], refresh: true }
|
|
172
|
+
);
|
|
173
|
+
throw new CrudHttpError(409, {
|
|
174
|
+
error: `[internal] Transaction status was already updated by a concurrent process (expected "${previousStatus}", actual "${actualTx?.status ?? "unknown"}")`,
|
|
175
|
+
code: "concurrent_status_update",
|
|
176
|
+
expectedStatus: previousStatus,
|
|
177
|
+
currentStatus: actualTx?.status ?? "unknown",
|
|
178
|
+
requestedStatus: nextStatus
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
await tx.refresh(transaction);
|
|
152
182
|
if (!previousTerminal && nextTerminal) {
|
|
153
183
|
const { usageLimitReached } = applyTerminalTransactionState(link, nextStatus);
|
|
154
184
|
await tx.flush();
|
|
@@ -158,7 +188,7 @@ const updateTransactionStatusCommand = {
|
|
|
158
188
|
usageLimitReachedLinkSlug = link.slug;
|
|
159
189
|
}
|
|
160
190
|
}
|
|
161
|
-
if (nextTerminal) {
|
|
191
|
+
if (nextTerminal && previousStatus !== nextStatus) {
|
|
162
192
|
terminalEventPayload = {
|
|
163
193
|
transactionId: transaction.id,
|
|
164
194
|
linkId: transaction.linkId,
|
|
@@ -176,14 +206,16 @@ const updateTransactionStatusCommand = {
|
|
|
176
206
|
};
|
|
177
207
|
}
|
|
178
208
|
});
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
209
|
+
if (terminalEventPayload !== null) {
|
|
210
|
+
if (parsed.status === "completed") {
|
|
211
|
+
await emitCheckoutEvent("checkout.transaction.completed", terminalEventPayload).catch(() => void 0);
|
|
212
|
+
} else if (parsed.status === "failed") {
|
|
213
|
+
await emitCheckoutEvent("checkout.transaction.failed", terminalEventPayload).catch(() => void 0);
|
|
214
|
+
} else if (parsed.status === "cancelled") {
|
|
215
|
+
await emitCheckoutEvent("checkout.transaction.cancelled", terminalEventPayload).catch(() => void 0);
|
|
216
|
+
} else if (parsed.status === "expired") {
|
|
217
|
+
await emitCheckoutEvent("checkout.transaction.expired", terminalEventPayload).catch(() => void 0);
|
|
218
|
+
}
|
|
187
219
|
}
|
|
188
220
|
if (emitUsageLimitReached && usageLimitReachedLinkId && usageLimitReachedLinkSlug) {
|
|
189
221
|
await emitCheckoutEvent("checkout.link.usageLimitReached", {
|