@open-mercato/checkout 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4559.1.839e136509
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-039-null-gateway-edit.spec.js +118 -0
- package/dist/modules/checkout/__integration__/TC-CHKT-039-null-gateway-edit.spec.js.map +7 -0
- package/dist/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.js +66 -0
- package/dist/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.js.map +7 -0
- package/dist/modules/checkout/commands/links.js +13 -0
- package/dist/modules/checkout/commands/links.js.map +2 -2
- package/dist/modules/checkout/commands/templates.js +13 -0
- package/dist/modules/checkout/commands/templates.js.map +2 -2
- package/dist/modules/checkout/components/LinkTemplateForm.js +27 -7
- package/dist/modules/checkout/components/LinkTemplateForm.js.map +2 -2
- package/dist/modules/checkout/data/validators.js +11 -2
- package/dist/modules/checkout/data/validators.js.map +2 -2
- package/package.json +5 -5
- package/src/modules/checkout/__integration__/TC-CHKT-039-null-gateway-edit.spec.ts +134 -0
- package/src/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.ts +98 -0
- package/src/modules/checkout/commands/__tests__/optimistic-lock.test.ts +261 -0
- package/src/modules/checkout/commands/links.ts +13 -0
- package/src/modules/checkout/commands/templates.ts +13 -0
- package/src/modules/checkout/components/LinkTemplateForm.tsx +27 -7
- package/src/modules/checkout/data/__tests__/validators.test.ts +59 -1
- package/src/modules/checkout/data/validators.ts +11 -2
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
[build:checkout] found
|
|
1
|
+
[build:checkout] found 149 entry points
|
|
2
2
|
[build:checkout] built successfully
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { getAuthToken } from "@open-mercato/core/modules/core/__integration__/helpers/api";
|
|
3
|
+
import { readJsonSafe } from "@open-mercato/core/modules/core/__integration__/helpers/generalFixtures";
|
|
4
|
+
import {
|
|
5
|
+
createFixedTemplateInput,
|
|
6
|
+
createLinkFixture,
|
|
7
|
+
createTemplateFixture,
|
|
8
|
+
deleteCheckoutEntityIfExists,
|
|
9
|
+
readLink,
|
|
10
|
+
readTemplate,
|
|
11
|
+
updateLink,
|
|
12
|
+
updateTemplate
|
|
13
|
+
} from "./helpers/fixtures.js";
|
|
14
|
+
test.describe("TC-CHKT-039: Draft template/pay-link edits tolerate a null gateway provider (issue #2505)", () => {
|
|
15
|
+
test("template: clearing the gateway provider on a draft saves and round-trips as null", async ({ request }) => {
|
|
16
|
+
let token = null;
|
|
17
|
+
let templateId = null;
|
|
18
|
+
try {
|
|
19
|
+
token = await getAuthToken(request);
|
|
20
|
+
templateId = await createTemplateFixture(request, token, createFixedTemplateInput({ status: "draft" }));
|
|
21
|
+
const clearResponse = await updateTemplate(request, token, templateId, {
|
|
22
|
+
name: "Consulting Fee (no gateway)",
|
|
23
|
+
pricingMode: "fixed",
|
|
24
|
+
fixedPriceAmount: 49.99,
|
|
25
|
+
fixedPriceCurrencyCode: "USD",
|
|
26
|
+
status: "draft",
|
|
27
|
+
gatewayProviderKey: null
|
|
28
|
+
});
|
|
29
|
+
expect(
|
|
30
|
+
clearResponse.ok(),
|
|
31
|
+
`Clearing the gateway on a draft template should succeed: ${clearResponse.status()} ${JSON.stringify(await readJsonSafe(clearResponse))}`
|
|
32
|
+
).toBeTruthy();
|
|
33
|
+
const cleared = await readTemplate(request, token, templateId);
|
|
34
|
+
expect(cleared.gatewayProviderKey ?? null).toBeNull();
|
|
35
|
+
expect(cleared.name).toBe("Consulting Fee (no gateway)");
|
|
36
|
+
const renameResponse = await updateTemplate(request, token, templateId, {
|
|
37
|
+
name: "Consulting Fee renamed",
|
|
38
|
+
gatewayProviderKey: null
|
|
39
|
+
});
|
|
40
|
+
expect(
|
|
41
|
+
renameResponse.ok(),
|
|
42
|
+
`Editing a field on a gateway-less draft template should succeed: ${renameResponse.status()} ${JSON.stringify(await readJsonSafe(renameResponse))}`
|
|
43
|
+
).toBeTruthy();
|
|
44
|
+
const renamed = await readTemplate(request, token, templateId);
|
|
45
|
+
expect(renamed.gatewayProviderKey ?? null).toBeNull();
|
|
46
|
+
expect(renamed.name).toBe("Consulting Fee renamed");
|
|
47
|
+
const blankResponse = await updateTemplate(request, token, templateId, {
|
|
48
|
+
gatewayProviderKey: " "
|
|
49
|
+
});
|
|
50
|
+
expect(
|
|
51
|
+
blankResponse.ok(),
|
|
52
|
+
`A blank gateway should normalize to null on a draft template: ${blankResponse.status()} ${JSON.stringify(await readJsonSafe(blankResponse))}`
|
|
53
|
+
).toBeTruthy();
|
|
54
|
+
const blanked = await readTemplate(request, token, templateId);
|
|
55
|
+
expect(blanked.gatewayProviderKey ?? null).toBeNull();
|
|
56
|
+
} finally {
|
|
57
|
+
await deleteCheckoutEntityIfExists(request, token, "templates", templateId);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
test("pay-link: clearing the gateway provider on a draft saves and round-trips as null", async ({ request }) => {
|
|
61
|
+
let token = null;
|
|
62
|
+
let linkId = null;
|
|
63
|
+
try {
|
|
64
|
+
token = await getAuthToken(request);
|
|
65
|
+
const link = await createLinkFixture(request, token, createFixedTemplateInput({ status: "draft" }));
|
|
66
|
+
linkId = link.id;
|
|
67
|
+
const clearResponse = await updateLink(request, token, link.id, {
|
|
68
|
+
name: "Pay link (no gateway)",
|
|
69
|
+
pricingMode: "fixed",
|
|
70
|
+
fixedPriceAmount: 49.99,
|
|
71
|
+
fixedPriceCurrencyCode: "USD",
|
|
72
|
+
status: "draft",
|
|
73
|
+
gatewayProviderKey: null
|
|
74
|
+
});
|
|
75
|
+
expect(
|
|
76
|
+
clearResponse.ok(),
|
|
77
|
+
`Clearing the gateway on a draft pay-link should succeed: ${clearResponse.status()} ${JSON.stringify(await readJsonSafe(clearResponse))}`
|
|
78
|
+
).toBeTruthy();
|
|
79
|
+
const cleared = await readLink(request, token, link.id);
|
|
80
|
+
expect(cleared.gatewayProviderKey ?? null).toBeNull();
|
|
81
|
+
expect(cleared.name).toBe("Pay link (no gateway)");
|
|
82
|
+
const renameResponse = await updateLink(request, token, link.id, {
|
|
83
|
+
name: "Pay link renamed",
|
|
84
|
+
gatewayProviderKey: null
|
|
85
|
+
});
|
|
86
|
+
expect(
|
|
87
|
+
renameResponse.ok(),
|
|
88
|
+
`Editing a field on a gateway-less draft pay-link should succeed: ${renameResponse.status()} ${JSON.stringify(await readJsonSafe(renameResponse))}`
|
|
89
|
+
).toBeTruthy();
|
|
90
|
+
const renamed = await readLink(request, token, link.id);
|
|
91
|
+
expect(renamed.gatewayProviderKey ?? null).toBeNull();
|
|
92
|
+
expect(renamed.name).toBe("Pay link renamed");
|
|
93
|
+
} finally {
|
|
94
|
+
await deleteCheckoutEntityIfExists(request, token, "links", linkId);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
test("pay-link: publishing still requires a gateway provider", async ({ request }) => {
|
|
98
|
+
let token = null;
|
|
99
|
+
let linkId = null;
|
|
100
|
+
try {
|
|
101
|
+
token = await getAuthToken(request);
|
|
102
|
+
const link = await createLinkFixture(request, token, createFixedTemplateInput({ status: "draft" }));
|
|
103
|
+
linkId = link.id;
|
|
104
|
+
const publishResponse = await updateLink(request, token, link.id, {
|
|
105
|
+
status: "active",
|
|
106
|
+
gatewayProviderKey: null
|
|
107
|
+
});
|
|
108
|
+
expect([400, 422]).toContain(publishResponse.status());
|
|
109
|
+
const body = await readJsonSafe(publishResponse);
|
|
110
|
+
const fieldError = typeof body?.fieldErrors?.gatewayProviderKey === "string" ? body.fieldErrors.gatewayProviderKey : "";
|
|
111
|
+
const errorMessage = typeof body?.error === "string" ? body.error : "";
|
|
112
|
+
expect(`${fieldError} ${errorMessage}`.toLowerCase()).toContain("gateway");
|
|
113
|
+
} finally {
|
|
114
|
+
await deleteCheckoutEntityIfExists(request, token, "links", linkId);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=TC-CHKT-039-null-gateway-edit.spec.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/checkout/__integration__/TC-CHKT-039-null-gateway-edit.spec.ts"],
|
|
4
|
+
"sourcesContent": ["import { expect, test } from '@playwright/test'\nimport { getAuthToken } from '@open-mercato/core/modules/core/__integration__/helpers/api'\nimport { readJsonSafe } from '@open-mercato/core/modules/core/__integration__/helpers/generalFixtures'\nimport {\n createFixedTemplateInput,\n createLinkFixture,\n createTemplateFixture,\n deleteCheckoutEntityIfExists,\n readLink,\n readTemplate,\n updateLink,\n updateTemplate,\n} from './helpers/fixtures'\n\ntest.describe('TC-CHKT-039: Draft template/pay-link edits tolerate a null gateway provider (issue #2505)', () => {\n test('template: clearing the gateway provider on a draft saves and round-trips as null', async ({ request }) => {\n let token: string | null = null\n let templateId: string | null = null\n\n try {\n token = await getAuthToken(request)\n templateId = await createTemplateFixture(request, token, createFixedTemplateInput({ status: 'draft' }))\n\n const clearResponse = await updateTemplate(request, token, templateId, {\n name: 'Consulting Fee (no gateway)',\n pricingMode: 'fixed',\n fixedPriceAmount: 49.99,\n fixedPriceCurrencyCode: 'USD',\n status: 'draft',\n gatewayProviderKey: null,\n })\n expect(\n clearResponse.ok(),\n `Clearing the gateway on a draft template should succeed: ${clearResponse.status()} ${JSON.stringify(await readJsonSafe(clearResponse))}`,\n ).toBeTruthy()\n\n const cleared = await readTemplate(request, token, templateId)\n expect(cleared.gatewayProviderKey ?? null).toBeNull()\n expect(cleared.name).toBe('Consulting Fee (no gateway)')\n\n const renameResponse = await updateTemplate(request, token, templateId, {\n name: 'Consulting Fee renamed',\n gatewayProviderKey: null,\n })\n expect(\n renameResponse.ok(),\n `Editing a field on a gateway-less draft template should succeed: ${renameResponse.status()} ${JSON.stringify(await readJsonSafe(renameResponse))}`,\n ).toBeTruthy()\n\n const renamed = await readTemplate(request, token, templateId)\n expect(renamed.gatewayProviderKey ?? null).toBeNull()\n expect(renamed.name).toBe('Consulting Fee renamed')\n\n const blankResponse = await updateTemplate(request, token, templateId, {\n gatewayProviderKey: ' ',\n })\n expect(\n blankResponse.ok(),\n `A blank gateway should normalize to null on a draft template: ${blankResponse.status()} ${JSON.stringify(await readJsonSafe(blankResponse))}`,\n ).toBeTruthy()\n\n const blanked = await readTemplate(request, token, templateId)\n expect(blanked.gatewayProviderKey ?? null).toBeNull()\n } finally {\n await deleteCheckoutEntityIfExists(request, token, 'templates', templateId)\n }\n })\n\n test('pay-link: clearing the gateway provider on a draft saves and round-trips as null', async ({ request }) => {\n let token: string | null = null\n let linkId: string | null = null\n\n try {\n token = await getAuthToken(request)\n const link = await createLinkFixture(request, token, createFixedTemplateInput({ status: 'draft' }))\n linkId = link.id\n\n const clearResponse = await updateLink(request, token, link.id, {\n name: 'Pay link (no gateway)',\n pricingMode: 'fixed',\n fixedPriceAmount: 49.99,\n fixedPriceCurrencyCode: 'USD',\n status: 'draft',\n gatewayProviderKey: null,\n })\n expect(\n clearResponse.ok(),\n `Clearing the gateway on a draft pay-link should succeed: ${clearResponse.status()} ${JSON.stringify(await readJsonSafe(clearResponse))}`,\n ).toBeTruthy()\n\n const cleared = await readLink(request, token, link.id)\n expect(cleared.gatewayProviderKey ?? null).toBeNull()\n expect(cleared.name).toBe('Pay link (no gateway)')\n\n const renameResponse = await updateLink(request, token, link.id, {\n name: 'Pay link renamed',\n gatewayProviderKey: null,\n })\n expect(\n renameResponse.ok(),\n `Editing a field on a gateway-less draft pay-link should succeed: ${renameResponse.status()} ${JSON.stringify(await readJsonSafe(renameResponse))}`,\n ).toBeTruthy()\n\n const renamed = await readLink(request, token, link.id)\n expect(renamed.gatewayProviderKey ?? null).toBeNull()\n expect(renamed.name).toBe('Pay link renamed')\n } finally {\n await deleteCheckoutEntityIfExists(request, token, 'links', linkId)\n }\n })\n\n test('pay-link: publishing still requires a gateway provider', async ({ request }) => {\n let token: string | null = null\n let linkId: string | null = null\n\n try {\n token = await getAuthToken(request)\n const link = await createLinkFixture(request, token, createFixedTemplateInput({ status: 'draft' }))\n linkId = link.id\n\n const publishResponse = await updateLink(request, token, link.id, {\n status: 'active',\n gatewayProviderKey: null,\n })\n expect([400, 422]).toContain(publishResponse.status())\n const body = await readJsonSafe<{ error?: string; fieldErrors?: { gatewayProviderKey?: string } }>(publishResponse)\n const fieldError = typeof body?.fieldErrors?.gatewayProviderKey === 'string' ? body.fieldErrors.gatewayProviderKey : ''\n const errorMessage = typeof body?.error === 'string' ? body.error : ''\n expect(`${fieldError} ${errorMessage}`.toLowerCase()).toContain('gateway')\n } finally {\n await deleteCheckoutEntityIfExists(request, token, 'links', linkId)\n }\n })\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { expect, test } from "@playwright/test";
|
|
2
|
+
import { getAuthToken } from "@open-mercato/core/modules/core/__integration__/helpers/api";
|
|
3
|
+
import { createFixedTemplateInput, createLinkFixture, readLink } from "./helpers/fixtures.js";
|
|
4
|
+
const OPTIMISTIC_LOCK_HEADER = "x-om-ext-optimistic-lock-expected-updated-at";
|
|
5
|
+
function readUpdatedAt(record) {
|
|
6
|
+
const raw = record.updatedAt ?? record.updated_at;
|
|
7
|
+
expect(typeof raw, "link detail should expose updatedAt as a string").toBe("string");
|
|
8
|
+
return new Date(Date.parse(raw)).toISOString();
|
|
9
|
+
}
|
|
10
|
+
test.describe("TC-CHKT-039: pay-link stale DELETE optimistic-lock guard", () => {
|
|
11
|
+
test("stale DELETE returns 409; fresh DELETE succeeds; header-less stays backward-compatible", async ({ request }) => {
|
|
12
|
+
const token = await getAuthToken(request);
|
|
13
|
+
const guarded = await createLinkFixture(request, token, createFixedTemplateInput());
|
|
14
|
+
const additive = await createLinkFixture(request, token, createFixedTemplateInput());
|
|
15
|
+
let guardedDeleted = false;
|
|
16
|
+
let additiveDeleted = false;
|
|
17
|
+
try {
|
|
18
|
+
const detail = await readLink(request, token, guarded.id);
|
|
19
|
+
const t0 = readUpdatedAt(detail);
|
|
20
|
+
const bump = await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {
|
|
21
|
+
method: "PUT",
|
|
22
|
+
headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
|
|
23
|
+
data: { subtitle: "bumped for stale-delete test" }
|
|
24
|
+
});
|
|
25
|
+
expect(bump.status(), "PUT bump should succeed").toBeLessThan(300);
|
|
26
|
+
const staleDelete = await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {
|
|
27
|
+
method: "DELETE",
|
|
28
|
+
headers: { Authorization: `Bearer ${token}`, [OPTIMISTIC_LOCK_HEADER]: t0 }
|
|
29
|
+
});
|
|
30
|
+
expect(staleDelete.status(), "DELETE with a stale header should return 409").toBe(409);
|
|
31
|
+
expect(await staleDelete.json()).toMatchObject({
|
|
32
|
+
code: "optimistic_lock_conflict",
|
|
33
|
+
expectedUpdatedAt: t0
|
|
34
|
+
});
|
|
35
|
+
const stillThere = await readLink(request, token, guarded.id);
|
|
36
|
+
const t1 = readUpdatedAt(stillThere);
|
|
37
|
+
expect(t1, "stale delete must NOT have removed the record").not.toBe(t0);
|
|
38
|
+
const freshDelete = await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {
|
|
39
|
+
method: "DELETE",
|
|
40
|
+
headers: { Authorization: `Bearer ${token}`, [OPTIMISTIC_LOCK_HEADER]: t1 }
|
|
41
|
+
});
|
|
42
|
+
expect(freshDelete.status(), "DELETE with a fresh header should succeed").toBeLessThan(300);
|
|
43
|
+
guardedDeleted = true;
|
|
44
|
+
const nohdrDelete = await request.fetch(`/api/checkout/links/${encodeURIComponent(additive.id)}`, {
|
|
45
|
+
method: "DELETE",
|
|
46
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
47
|
+
});
|
|
48
|
+
expect(nohdrDelete.status(), "DELETE without a header should succeed").toBeLessThan(300);
|
|
49
|
+
additiveDeleted = true;
|
|
50
|
+
} finally {
|
|
51
|
+
if (!guardedDeleted) {
|
|
52
|
+
await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {
|
|
53
|
+
method: "DELETE",
|
|
54
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
55
|
+
}).catch(() => void 0);
|
|
56
|
+
}
|
|
57
|
+
if (!additiveDeleted) {
|
|
58
|
+
await request.fetch(`/api/checkout/links/${encodeURIComponent(additive.id)}`, {
|
|
59
|
+
method: "DELETE",
|
|
60
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
61
|
+
}).catch(() => void 0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=TC-CHKT-039-stale-delete-lock.spec.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/checkout/__integration__/TC-CHKT-039-stale-delete-lock.spec.ts"],
|
|
4
|
+
"sourcesContent": ["import { expect, test } from '@playwright/test'\nimport { getAuthToken } from '@open-mercato/core/modules/core/__integration__/helpers/api'\nimport { createFixedTemplateInput, createLinkFixture, readLink } from './helpers/fixtures'\n\n/**\n * TC-CHKT-039: OSS optimistic locking now guards the pay-link/template DELETE.\n *\n * QA round-6 (PR #2055): after a save conflict surfaced the unified conflict\n * bar, clicking Delete still removed the stale record. The pay-link/template\n * delete path was unguarded \u2014 the client sent no version header and the\n * `checkout.link.delete` / `checkout.template.delete` commands never called\n * `enforceCommandOptimisticLock` (only the *.update commands did).\n *\n * This spec proves the server contract the UI relies on:\n * - GET detail exposes `updatedAt`.\n * - DELETE without the header succeeds (strictly additive).\n * - DELETE with a stale header returns 409 with the structured conflict body.\n * - DELETE with a fresh header deletes the record.\n *\n * Requires `OM_OPTIMISTIC_LOCK=all` (CI default).\n */\nconst OPTIMISTIC_LOCK_HEADER = 'x-om-ext-optimistic-lock-expected-updated-at'\n\nfunction readUpdatedAt(record: Record<string, unknown>): string {\n const raw = record.updatedAt ?? record.updated_at\n expect(typeof raw, 'link detail should expose updatedAt as a string').toBe('string')\n return new Date(Date.parse(raw as string)).toISOString()\n}\n\ntest.describe('TC-CHKT-039: pay-link stale DELETE optimistic-lock guard', () => {\n test('stale DELETE returns 409; fresh DELETE succeeds; header-less stays backward-compatible', async ({ request }) => {\n const token = await getAuthToken(request)\n\n // Two links: one to prove the stale\u2192409\u2192fresh path, one to prove the\n // strictly-additive header-less delete still works.\n const guarded = await createLinkFixture(request, token, createFixedTemplateInput())\n const additive = await createLinkFixture(request, token, createFixedTemplateInput())\n let guardedDeleted = false\n let additiveDeleted = false\n\n try {\n const detail = await readLink(request, token, guarded.id)\n const t0 = readUpdatedAt(detail as Record<string, unknown>)\n\n // A save advances the version, making t0 stale.\n const bump = await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {\n method: 'PUT',\n headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },\n data: { subtitle: 'bumped for stale-delete test' },\n })\n expect(bump.status(), 'PUT bump should succeed').toBeLessThan(300)\n\n // Stale DELETE \u2192 409 with the structured conflict body; record survives.\n const staleDelete = await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}`, [OPTIMISTIC_LOCK_HEADER]: t0 },\n })\n expect(staleDelete.status(), 'DELETE with a stale header should return 409').toBe(409)\n expect((await staleDelete.json()) as Record<string, unknown>).toMatchObject({\n code: 'optimistic_lock_conflict',\n expectedUpdatedAt: t0,\n })\n\n const stillThere = await readLink(request, token, guarded.id)\n const t1 = readUpdatedAt(stillThere as Record<string, unknown>)\n expect(t1, 'stale delete must NOT have removed the record').not.toBe(t0)\n\n // Fresh DELETE \u2192 succeeds.\n const freshDelete = await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}`, [OPTIMISTIC_LOCK_HEADER]: t1 },\n })\n expect(freshDelete.status(), 'DELETE with a fresh header should succeed').toBeLessThan(300)\n guardedDeleted = true\n\n // Header-less DELETE still works (strictly additive).\n const nohdrDelete = await request.fetch(`/api/checkout/links/${encodeURIComponent(additive.id)}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n })\n expect(nohdrDelete.status(), 'DELETE without a header should succeed').toBeLessThan(300)\n additiveDeleted = true\n } finally {\n if (!guardedDeleted) {\n await request.fetch(`/api/checkout/links/${encodeURIComponent(guarded.id)}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n }).catch(() => undefined)\n }\n if (!additiveDeleted) {\n await request.fetch(`/api/checkout/links/${encodeURIComponent(additive.id)}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${token}` },\n }).catch(() => undefined)\n }\n }\n })\n})\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -3,6 +3,7 @@ import { buildCustomFieldResetMap, loadCustomFieldSnapshot } from "@open-mercato
|
|
|
3
3
|
import { setCustomFieldsIfAny } from "@open-mercato/shared/lib/commands/helpers";
|
|
4
4
|
import { loadCustomFieldValues } from "@open-mercato/shared/lib/crud/custom-fields";
|
|
5
5
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
6
|
+
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
6
7
|
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
7
8
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
8
9
|
import { CheckoutLink, CheckoutLinkTemplate, CheckoutTransaction } from "../data/entities.js";
|
|
@@ -215,6 +216,12 @@ const updateLinkCommand = {
|
|
|
215
216
|
deletedAt: null
|
|
216
217
|
}, void 0, scope);
|
|
217
218
|
if (!link) throw new CrudHttpError(404, { error: "Link not found" });
|
|
219
|
+
enforceCommandOptimisticLock({
|
|
220
|
+
resourceKind: "checkout.link",
|
|
221
|
+
resourceId: link.id,
|
|
222
|
+
current: link.updatedAt ?? null,
|
|
223
|
+
request: ctx.request ?? null
|
|
224
|
+
});
|
|
218
225
|
if (link.isLocked) {
|
|
219
226
|
throw new CrudHttpError(422, { error: "This link has active transactions and cannot be edited" });
|
|
220
227
|
}
|
|
@@ -358,6 +365,12 @@ const deleteLinkCommand = {
|
|
|
358
365
|
deletedAt: null
|
|
359
366
|
}, void 0, scope);
|
|
360
367
|
if (!link) throw new CrudHttpError(404, { error: "Link not found" });
|
|
368
|
+
enforceCommandOptimisticLock({
|
|
369
|
+
resourceKind: "checkout.link",
|
|
370
|
+
resourceId: link.id,
|
|
371
|
+
current: link.updatedAt ?? null,
|
|
372
|
+
request: ctx.request ?? null
|
|
373
|
+
});
|
|
361
374
|
const activeCount = await em.count(CheckoutTransaction, {
|
|
362
375
|
linkId: link.id,
|
|
363
376
|
organizationId: scope.organizationId,
|
|
@@ -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 { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\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}\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 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 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 { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLock } 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}\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 enforceCommandOptimisticLock({\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 enforceCommandOptimisticLock({\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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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,6 +2,7 @@ import { registerCommand } from "@open-mercato/shared/lib/commands";
|
|
|
2
2
|
import { buildCustomFieldResetMap, loadCustomFieldSnapshot } from "@open-mercato/shared/lib/commands/customFieldSnapshots";
|
|
3
3
|
import { setCustomFieldsIfAny } from "@open-mercato/shared/lib/commands/helpers";
|
|
4
4
|
import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
5
|
+
import { enforceCommandOptimisticLock } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
|
|
5
6
|
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
6
7
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
7
8
|
import { CheckoutLink, CheckoutLinkTemplate } from "../data/entities.js";
|
|
@@ -202,6 +203,12 @@ const updateTemplateCommand = {
|
|
|
202
203
|
deletedAt: null
|
|
203
204
|
}, void 0, scope);
|
|
204
205
|
if (!template) throw new CrudHttpError(404, { error: "Template not found" });
|
|
206
|
+
enforceCommandOptimisticLock({
|
|
207
|
+
resourceKind: "checkout.template",
|
|
208
|
+
resourceId: template.id,
|
|
209
|
+
current: template.updatedAt ?? null,
|
|
210
|
+
request: ctx.request ?? null
|
|
211
|
+
});
|
|
205
212
|
const beforeCustom = await loadCustomFieldSnapshot(em, {
|
|
206
213
|
entityId: CHECKOUT_ENTITY_IDS.template,
|
|
207
214
|
recordId: template.id,
|
|
@@ -349,6 +356,12 @@ const deleteTemplateCommand = {
|
|
|
349
356
|
deletedAt: null
|
|
350
357
|
}, void 0, scope);
|
|
351
358
|
if (!template) throw new CrudHttpError(404, { error: "Template not found" });
|
|
359
|
+
enforceCommandOptimisticLock({
|
|
360
|
+
resourceKind: "checkout.template",
|
|
361
|
+
resourceId: template.id,
|
|
362
|
+
current: template.updatedAt ?? null,
|
|
363
|
+
request: ctx.request ?? null
|
|
364
|
+
});
|
|
352
365
|
template.deletedAt = /* @__PURE__ */ new Date();
|
|
353
366
|
await em.flush();
|
|
354
367
|
await emitCheckoutEvent("checkout.template.deleted", {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/checkout/commands/templates.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 { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CheckoutLink, CheckoutLinkTemplate } from '../data/entities'\nimport { createTemplateSchema, updateTemplateSchema } 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 hashCheckoutPassword,\n parseCheckoutInput,\n serializeTemplateOrLink,\n toMoneyString,\n validateDescriptorCurrencies,\n} from '../lib/utils'\nimport {\n buildSelectiveLinkedCustomFieldUpdates,\n buildSelectiveLinkedLinkSnapshot,\n captureTemplateSnapshot,\n createTemplateFromSnapshot,\n extractUndoPayload,\n captureLinkSnapshot,\n readCommandId,\n resolveCommandScope,\n restoreLinkFromSnapshot,\n restoreTemplateFromSnapshot,\n toCheckoutAuditSnapshot,\n type CheckoutTemplateSnapshot,\n} from './shared'\n\ntype CheckoutTemplateUndoPayload = {\n before?: CheckoutTemplateSnapshot | null\n after?: CheckoutTemplateSnapshot | null\n}\n\nasync function syncLinkedLinksWithTemplateSnapshot(params: {\n em: EntityManager\n dataEngine: DataEngine\n scope: { organizationId: string; tenantId: string }\n templateId: string\n before: CheckoutTemplateSnapshot\n after: CheckoutTemplateSnapshot\n}) {\n const { em, dataEngine, scope, templateId, before, after } = params\n const linkedLinks = await findWithDecryption(\n em,\n CheckoutLink,\n {\n templateId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n isLocked: false,\n },\n undefined,\n scope,\n )\n\n let changedLinks = false\n\n for (const link of linkedLinks) {\n const currentLinkSnapshot = captureLinkSnapshot(link)\n const nextLinkSnapshot = buildSelectiveLinkedLinkSnapshot(currentLinkSnapshot, before, after)\n if (nextLinkSnapshot.changed) {\n restoreLinkFromSnapshot(link, nextLinkSnapshot.snapshot)\n changedLinks = true\n }\n\n const currentCustom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n const customFieldUpdates = buildSelectiveLinkedCustomFieldUpdates(currentCustom, before.custom, after.custom)\n if (Object.keys(customFieldUpdates).length > 0) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n values: customFieldUpdates,\n })\n }\n }\n\n if (changedLinks) {\n await em.flush()\n }\n}\n\nconst createTemplateCommand: CommandHandler<Record<string, unknown>, { id: string }> = {\n id: 'checkout.template.create',\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, createTemplateSchema.parse)\n const scope = resolveCommandScope(ctx)\n validateDescriptorCurrencies(parsed.gatewayProviderKey, deriveConfiguredCurrencies(parsed))\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(parsed.gatewayProviderKey, descriptorService, scope)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const template = em.create(CheckoutLinkTemplate, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n ...parsed,\n fixedPriceAmount: toMoneyString(parsed.fixedPriceAmount),\n fixedPriceOriginalAmount: toMoneyString(parsed.fixedPriceOriginalAmount),\n customAmountMin: toMoneyString(parsed.customAmountMin),\n customAmountMax: toMoneyString(parsed.customAmountMax),\n passwordHash: await hashCheckoutPassword(parsed.password),\n } as any)\n em.persist(template)\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: customFields,\n })\n await emitCheckoutEvent('checkout.template.created', {\n id: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n return { id: template.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, { id: result.id })\n if (!template) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return captureTemplateSnapshot(template, custom)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as CheckoutTemplateSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.templates.create', 'Create pay-link template'),\n resourceKind: 'checkout.template',\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 CheckoutTemplateUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const after = extractUndoPayload<CheckoutTemplateUndoPayload>(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.template,\n recordId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n values: reset,\n notify: false,\n })\n }\n const template = await em.findOne(CheckoutLinkTemplate, { id: after.id })\n if (!template) return\n template.deletedAt = new Date()\n await em.flush()\n },\n}\n\nconst updateTemplateCommand: CommandHandler<Record<string, unknown>, { ok: true }> = {\n id: 'checkout.template.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseCheckoutInput(rawInput, updateTemplateSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: parsed.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!template) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return { before: captureTemplateSnapshot(template, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, updateTemplateSchema.parse)\n const scope = resolveCommandScope(ctx)\n validateDescriptorCurrencies(parsed.gatewayProviderKey ?? null, deriveConfiguredCurrencies(parsed))\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(parsed.gatewayProviderKey ?? null, descriptorService, scope)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: parsed.id,\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 const beforeCustom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n const beforeSnapshot = captureTemplateSnapshot(template, beforeCustom)\n const passwordHash = parsed.password !== undefined\n ? await hashCheckoutPassword(parsed.password)\n : template.passwordHash\n Object.assign(template, {\n ...parsed,\n fixedPriceAmount: parsed.fixedPriceAmount !== undefined ? toMoneyString(parsed.fixedPriceAmount) : template.fixedPriceAmount,\n fixedPriceOriginalAmount: parsed.fixedPriceOriginalAmount !== undefined ? toMoneyString(parsed.fixedPriceOriginalAmount) : template.fixedPriceOriginalAmount,\n customAmountMin: parsed.customAmountMin !== undefined ? toMoneyString(parsed.customAmountMin) : template.customAmountMin,\n customAmountMax: parsed.customAmountMax !== undefined ? toMoneyString(parsed.customAmountMax) : template.customAmountMax,\n passwordHash,\n })\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: customFields,\n })\n const afterCustom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n const afterSnapshot = captureTemplateSnapshot(template, afterCustom)\n await syncLinkedLinksWithTemplateSnapshot({\n em,\n dataEngine,\n scope,\n templateId: template.id,\n before: beforeSnapshot,\n after: afterSnapshot,\n })\n await emitCheckoutEvent('checkout.template.updated', {\n id: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n return { ok: true }\n },\n captureAfter: async (input, _result, ctx) => {\n const { parsed } = parseCheckoutInput(input, updateTemplateSchema.parse)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, { id: parsed.id, deletedAt: null })\n if (!template) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return captureTemplateSnapshot(template, custom)\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as CheckoutTemplateSnapshot | null | undefined\n const after = snapshots.after as CheckoutTemplateSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.templates.update', 'Update pay-link template'),\n resourceKind: 'checkout.template',\n resourceId: after?.id ?? before?.id ?? readCommandId(input, 'Template id is required'),\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 CheckoutTemplateUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<CheckoutTemplateUndoPayload>(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 template = await em.findOne(CheckoutLinkTemplate, { id: before.id, deletedAt: null })\n if (!template) return\n restoreTemplateFromSnapshot(template, 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.template,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n if (after) {\n await syncLinkedLinksWithTemplateSnapshot({\n em,\n dataEngine,\n scope: { organizationId: before.organizationId, tenantId: before.tenantId },\n templateId: before.id,\n before: after,\n after: before,\n })\n }\n },\n}\n\nconst deleteTemplateCommand: CommandHandler<Record<string, unknown>, { ok: true }> = {\n id: 'checkout.template.delete',\n async prepare(rawInput, ctx) {\n const templateId = readCommandId(rawInput, 'Template id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: templateId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!template) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return { before: captureTemplateSnapshot(template, custom) }\n },\n async execute(rawInput, ctx) {\n const templateId = readCommandId(rawInput, 'Template id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: 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 template.deletedAt = new Date()\n await em.flush()\n await emitCheckoutEvent('checkout.template.deleted', {\n id: template.id,\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 CheckoutTemplateSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.templates.delete', 'Delete pay-link template'),\n resourceKind: 'checkout.template',\n resourceId: before?.id ?? readCommandId(input, 'Template 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 CheckoutTemplateUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<CheckoutTemplateUndoPayload>(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 template = await em.findOne(CheckoutLinkTemplate, { id: before.id })\n if (template) {\n restoreTemplateFromSnapshot(template, before)\n } else {\n template = em.create(CheckoutLinkTemplate, createTemplateFromSnapshot(before))\n em.persist(template)\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.template,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n },\n}\n\nregisterCommand(createTemplateCommand)\nregisterCommand(updateTemplateCommand)\nregisterCommand(deleteTemplateCommand)\n\nexport function serializeTemplateRecord(record: CheckoutLinkTemplate) {\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 { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { CheckoutLink, CheckoutLinkTemplate } from '../data/entities'\nimport { createTemplateSchema, updateTemplateSchema } 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 hashCheckoutPassword,\n parseCheckoutInput,\n serializeTemplateOrLink,\n toMoneyString,\n validateDescriptorCurrencies,\n} from '../lib/utils'\nimport {\n buildSelectiveLinkedCustomFieldUpdates,\n buildSelectiveLinkedLinkSnapshot,\n captureTemplateSnapshot,\n createTemplateFromSnapshot,\n extractUndoPayload,\n captureLinkSnapshot,\n readCommandId,\n resolveCommandScope,\n restoreLinkFromSnapshot,\n restoreTemplateFromSnapshot,\n toCheckoutAuditSnapshot,\n type CheckoutTemplateSnapshot,\n} from './shared'\n\ntype CheckoutTemplateUndoPayload = {\n before?: CheckoutTemplateSnapshot | null\n after?: CheckoutTemplateSnapshot | null\n}\n\nasync function syncLinkedLinksWithTemplateSnapshot(params: {\n em: EntityManager\n dataEngine: DataEngine\n scope: { organizationId: string; tenantId: string }\n templateId: string\n before: CheckoutTemplateSnapshot\n after: CheckoutTemplateSnapshot\n}) {\n const { em, dataEngine, scope, templateId, before, after } = params\n const linkedLinks = await findWithDecryption(\n em,\n CheckoutLink,\n {\n templateId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n isLocked: false,\n },\n undefined,\n scope,\n )\n\n let changedLinks = false\n\n for (const link of linkedLinks) {\n const currentLinkSnapshot = captureLinkSnapshot(link)\n const nextLinkSnapshot = buildSelectiveLinkedLinkSnapshot(currentLinkSnapshot, before, after)\n if (nextLinkSnapshot.changed) {\n restoreLinkFromSnapshot(link, nextLinkSnapshot.snapshot)\n changedLinks = true\n }\n\n const currentCustom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n })\n const customFieldUpdates = buildSelectiveLinkedCustomFieldUpdates(currentCustom, before.custom, after.custom)\n if (Object.keys(customFieldUpdates).length > 0) {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.link,\n recordId: link.id,\n tenantId: link.tenantId,\n organizationId: link.organizationId,\n values: customFieldUpdates,\n })\n }\n }\n\n if (changedLinks) {\n await em.flush()\n }\n}\n\nconst createTemplateCommand: CommandHandler<Record<string, unknown>, { id: string }> = {\n id: 'checkout.template.create',\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, createTemplateSchema.parse)\n const scope = resolveCommandScope(ctx)\n validateDescriptorCurrencies(parsed.gatewayProviderKey, deriveConfiguredCurrencies(parsed))\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(parsed.gatewayProviderKey, descriptorService, scope)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const template = em.create(CheckoutLinkTemplate, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n ...parsed,\n fixedPriceAmount: toMoneyString(parsed.fixedPriceAmount),\n fixedPriceOriginalAmount: toMoneyString(parsed.fixedPriceOriginalAmount),\n customAmountMin: toMoneyString(parsed.customAmountMin),\n customAmountMax: toMoneyString(parsed.customAmountMax),\n passwordHash: await hashCheckoutPassword(parsed.password),\n } as any)\n em.persist(template)\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: customFields,\n })\n await emitCheckoutEvent('checkout.template.created', {\n id: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n return { id: template.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, { id: result.id })\n if (!template) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return captureTemplateSnapshot(template, custom)\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as CheckoutTemplateSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.templates.create', 'Create pay-link template'),\n resourceKind: 'checkout.template',\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 CheckoutTemplateUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const after = extractUndoPayload<CheckoutTemplateUndoPayload>(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.template,\n recordId: after.id,\n tenantId: after.tenantId,\n organizationId: after.organizationId,\n values: reset,\n notify: false,\n })\n }\n const template = await em.findOne(CheckoutLinkTemplate, { id: after.id })\n if (!template) return\n template.deletedAt = new Date()\n await em.flush()\n },\n}\n\nconst updateTemplateCommand: CommandHandler<Record<string, unknown>, { ok: true }> = {\n id: 'checkout.template.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseCheckoutInput(rawInput, updateTemplateSchema.parse)\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: parsed.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!template) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return { before: captureTemplateSnapshot(template, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, customFields } = parseCheckoutInput(rawInput, updateTemplateSchema.parse)\n const scope = resolveCommandScope(ctx)\n validateDescriptorCurrencies(parsed.gatewayProviderKey ?? null, deriveConfiguredCurrencies(parsed))\n const descriptorService = ctx.container.resolve('paymentGatewayDescriptorService') as PaymentGatewayDescriptorService\n await ensureGatewayProviderConfigured(parsed.gatewayProviderKey ?? null, descriptorService, scope)\n const em = ctx.container.resolve('em') as EntityManager\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: parsed.id,\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 enforceCommandOptimisticLock({\n resourceKind: 'checkout.template',\n resourceId: template.id,\n current: template.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n const beforeCustom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n const beforeSnapshot = captureTemplateSnapshot(template, beforeCustom)\n const passwordHash = parsed.password !== undefined\n ? await hashCheckoutPassword(parsed.password)\n : template.passwordHash\n Object.assign(template, {\n ...parsed,\n fixedPriceAmount: parsed.fixedPriceAmount !== undefined ? toMoneyString(parsed.fixedPriceAmount) : template.fixedPriceAmount,\n fixedPriceOriginalAmount: parsed.fixedPriceOriginalAmount !== undefined ? toMoneyString(parsed.fixedPriceOriginalAmount) : template.fixedPriceOriginalAmount,\n customAmountMin: parsed.customAmountMin !== undefined ? toMoneyString(parsed.customAmountMin) : template.customAmountMin,\n customAmountMax: parsed.customAmountMax !== undefined ? toMoneyString(parsed.customAmountMax) : template.customAmountMax,\n passwordHash,\n })\n await em.flush()\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n values: customFields,\n })\n const afterCustom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n const afterSnapshot = captureTemplateSnapshot(template, afterCustom)\n await syncLinkedLinksWithTemplateSnapshot({\n em,\n dataEngine,\n scope,\n templateId: template.id,\n before: beforeSnapshot,\n after: afterSnapshot,\n })\n await emitCheckoutEvent('checkout.template.updated', {\n id: template.id,\n tenantId: scope.tenantId,\n organizationId: scope.organizationId,\n }).catch(() => undefined)\n return { ok: true }\n },\n captureAfter: async (input, _result, ctx) => {\n const { parsed } = parseCheckoutInput(input, updateTemplateSchema.parse)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, { id: parsed.id, deletedAt: null })\n if (!template) return null\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return captureTemplateSnapshot(template, custom)\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as CheckoutTemplateSnapshot | null | undefined\n const after = snapshots.after as CheckoutTemplateSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.templates.update', 'Update pay-link template'),\n resourceKind: 'checkout.template',\n resourceId: after?.id ?? before?.id ?? readCommandId(input, 'Template id is required'),\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 CheckoutTemplateUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<CheckoutTemplateUndoPayload>(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 template = await em.findOne(CheckoutLinkTemplate, { id: before.id, deletedAt: null })\n if (!template) return\n restoreTemplateFromSnapshot(template, 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.template,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n if (after) {\n await syncLinkedLinksWithTemplateSnapshot({\n em,\n dataEngine,\n scope: { organizationId: before.organizationId, tenantId: before.tenantId },\n templateId: before.id,\n before: after,\n after: before,\n })\n }\n },\n}\n\nconst deleteTemplateCommand: CommandHandler<Record<string, unknown>, { ok: true }> = {\n id: 'checkout.template.delete',\n async prepare(rawInput, ctx) {\n const templateId = readCommandId(rawInput, 'Template id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: templateId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, undefined, scope)\n if (!template) return {}\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: CHECKOUT_ENTITY_IDS.template,\n recordId: template.id,\n tenantId: template.tenantId,\n organizationId: template.organizationId,\n })\n return { before: captureTemplateSnapshot(template, custom) }\n },\n async execute(rawInput, ctx) {\n const templateId = readCommandId(rawInput, 'Template id is required')\n const scope = resolveCommandScope(ctx)\n const em = ctx.container.resolve('em') as EntityManager\n const template = await findOneWithDecryption(em, CheckoutLinkTemplate, {\n id: 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 enforceCommandOptimisticLock({\n resourceKind: 'checkout.template',\n resourceId: template.id,\n current: template.updatedAt ?? null,\n request: ctx.request ?? null,\n })\n template.deletedAt = new Date()\n await em.flush()\n await emitCheckoutEvent('checkout.template.deleted', {\n id: template.id,\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 CheckoutTemplateSnapshot | null | undefined\n return {\n actionLabel: translate('checkout.audit.templates.delete', 'Delete pay-link template'),\n resourceKind: 'checkout.template',\n resourceId: before?.id ?? readCommandId(input, 'Template 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 CheckoutTemplateUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<CheckoutTemplateUndoPayload>(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 template = await em.findOne(CheckoutLinkTemplate, { id: before.id })\n if (template) {\n restoreTemplateFromSnapshot(template, before)\n } else {\n template = em.create(CheckoutLinkTemplate, createTemplateFromSnapshot(before))\n em.persist(template)\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.template,\n recordId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n values: reset,\n notify: false,\n })\n }\n },\n}\n\nregisterCommand(createTemplateCommand)\nregisterCommand(updateTemplateCommand)\nregisterCommand(deleteTemplateCommand)\n\nexport function serializeTemplateRecord(record: CheckoutLinkTemplate) {\n return serializeTemplateOrLink(record)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|