@endora-commerce/mod-payment-methods 0.100.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/dist/admin/api/payment-methods-client.d.ts +65 -0
  4. package/dist/admin/api/payment-methods-client.d.ts.map +1 -0
  5. package/dist/admin/api/payment-methods-client.js +39 -0
  6. package/dist/admin/api/payment-methods-client.js.map +1 -0
  7. package/dist/admin/index.d.ts +35 -0
  8. package/dist/admin/index.d.ts.map +1 -0
  9. package/dist/admin/index.js +37 -0
  10. package/dist/admin/index.js.map +1 -0
  11. package/dist/admin/pages/PaymentMethodsPage.d.ts +9 -0
  12. package/dist/admin/pages/PaymentMethodsPage.d.ts.map +1 -0
  13. package/dist/admin/pages/PaymentMethodsPage.js +228 -0
  14. package/dist/admin/pages/PaymentMethodsPage.js.map +1 -0
  15. package/dist/admin/renderers/registry.d.ts +15 -0
  16. package/dist/admin/renderers/registry.d.ts.map +1 -0
  17. package/dist/admin/renderers/registry.js +18 -0
  18. package/dist/admin/renderers/registry.js.map +1 -0
  19. package/dist/backend/commands/payment-method.commands.d.ts +86 -0
  20. package/dist/backend/commands/payment-method.commands.d.ts.map +1 -0
  21. package/dist/backend/commands/payment-method.commands.js +168 -0
  22. package/dist/backend/commands/payment-method.commands.js.map +1 -0
  23. package/dist/backend/demo/reset.d.ts +13 -0
  24. package/dist/backend/demo/reset.d.ts.map +1 -0
  25. package/dist/backend/demo/reset.js +13 -0
  26. package/dist/backend/demo/reset.js.map +1 -0
  27. package/dist/backend/demo/rows.d.ts +31 -0
  28. package/dist/backend/demo/rows.d.ts.map +1 -0
  29. package/dist/backend/demo/rows.js +50 -0
  30. package/dist/backend/demo/rows.js.map +1 -0
  31. package/dist/backend/demo/seed.d.ts +17 -0
  32. package/dist/backend/demo/seed.d.ts.map +1 -0
  33. package/dist/backend/demo/seed.js +30 -0
  34. package/dist/backend/demo/seed.js.map +1 -0
  35. package/dist/backend/entities/payment-method.entity.d.ts +28 -0
  36. package/dist/backend/entities/payment-method.entity.d.ts.map +1 -0
  37. package/dist/backend/entities/payment-method.entity.js +93 -0
  38. package/dist/backend/entities/payment-method.entity.js.map +1 -0
  39. package/dist/backend/index.d.ts +103 -0
  40. package/dist/backend/index.d.ts.map +1 -0
  41. package/dist/backend/index.js +151 -0
  42. package/dist/backend/index.js.map +1 -0
  43. package/dist/backend/routes.d.ts +55 -0
  44. package/dist/backend/routes.d.ts.map +1 -0
  45. package/dist/backend/routes.js +226 -0
  46. package/dist/backend/routes.js.map +1 -0
  47. package/dist/backend/services/name-resolver.d.ts +12 -0
  48. package/dist/backend/services/name-resolver.d.ts.map +1 -0
  49. package/dist/backend/services/name-resolver.js +19 -0
  50. package/dist/backend/services/name-resolver.js.map +1 -0
  51. package/dist/backend/services/order-status-registry.port.d.ts +57 -0
  52. package/dist/backend/services/order-status-registry.port.d.ts.map +1 -0
  53. package/dist/backend/services/order-status-registry.port.js +53 -0
  54. package/dist/backend/services/order-status-registry.port.js.map +1 -0
  55. package/dist/backend/services/payment-adapter-registry.d.ts +79 -0
  56. package/dist/backend/services/payment-adapter-registry.d.ts.map +1 -0
  57. package/dist/backend/services/payment-adapter-registry.js +87 -0
  58. package/dist/backend/services/payment-adapter-registry.js.map +1 -0
  59. package/dist/backend/services/payment-method-eligibility.d.ts +24 -0
  60. package/dist/backend/services/payment-method-eligibility.d.ts.map +1 -0
  61. package/dist/backend/services/payment-method-eligibility.js +48 -0
  62. package/dist/backend/services/payment-method-eligibility.js.map +1 -0
  63. package/dist/backend/services/payment-method-read-port.d.ts +25 -0
  64. package/dist/backend/services/payment-method-read-port.d.ts.map +1 -0
  65. package/dist/backend/services/payment-method-read-port.js +57 -0
  66. package/dist/backend/services/payment-method-read-port.js.map +1 -0
  67. package/dist/backend/services/payment-method-reconciler.d.ts +97 -0
  68. package/dist/backend/services/payment-method-reconciler.d.ts.map +1 -0
  69. package/dist/backend/services/payment-method-reconciler.js +164 -0
  70. package/dist/backend/services/payment-method-reconciler.js.map +1 -0
  71. package/dist/backend/services/registry-singleton.d.ts +16 -0
  72. package/dist/backend/services/registry-singleton.d.ts.map +1 -0
  73. package/dist/backend/services/registry-singleton.js +17 -0
  74. package/dist/backend/services/registry-singleton.js.map +1 -0
  75. package/dist/install/index.d.ts +57 -0
  76. package/dist/install/index.d.ts.map +1 -0
  77. package/dist/install/index.js +57 -0
  78. package/dist/install/index.js.map +1 -0
  79. package/dist/manifest.d.ts +169 -0
  80. package/dist/manifest.d.ts.map +1 -0
  81. package/dist/manifest.js +145 -0
  82. package/dist/manifest.js.map +1 -0
  83. package/dist/migrations/20260611T140353_payment_methods_adapter.d.ts +32 -0
  84. package/dist/migrations/20260611T140353_payment_methods_adapter.d.ts.map +1 -0
  85. package/dist/migrations/20260611T140353_payment_methods_adapter.js +76 -0
  86. package/dist/migrations/20260611T140353_payment_methods_adapter.js.map +1 -0
  87. package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.d.ts +46 -0
  88. package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.d.ts.map +1 -0
  89. package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.js +54 -0
  90. package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.js.map +1 -0
  91. package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.d.ts +27 -0
  92. package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.d.ts.map +1 -0
  93. package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.js +44 -0
  94. package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.js.map +1 -0
  95. package/dist/migrations/index.d.ts +32 -0
  96. package/dist/migrations/index.d.ts.map +1 -0
  97. package/dist/migrations/index.js +36 -0
  98. package/dist/migrations/index.js.map +1 -0
  99. package/dist/ports/index.d.ts +157 -0
  100. package/dist/ports/index.d.ts.map +1 -0
  101. package/dist/ports/index.js +2 -0
  102. package/dist/ports/index.js.map +1 -0
  103. package/docs/payment_methods.md +146 -0
  104. package/i18n/en.json +5 -0
  105. package/i18n/pl.json +5 -0
  106. package/package.json +105 -0
  107. package/tailwind.css +14 -0
@@ -0,0 +1,164 @@
1
+ import { SalesChannel } from '@endora-commerce/platform/kernel';
2
+ import { PaymentMethod } from '../entities/payment-method.entity.js';
3
+ /**
4
+ * PaymentMethodReconciler (feature 034, FR-002) — the implementation behind
5
+ * `PaymentMethodSeedApi`, this module's published install surface
6
+ * (`../../ports/index.ts`, `createPaymentMethodSeeder` below).
7
+ *
8
+ * When a module contributes a payment adapter, it calls `ensureMethodForAdapter`
9
+ * from its **install hook** to create a configurable `payment_methods` row bound
10
+ * to that adapter — so installing a payment-method module surfaces an entry at
11
+ * `/payment-methods` with no core change. Idempotent and prune-safe: an existing
12
+ * row (matched by `code`) keeps its admin-edited configuration; the reconciler
13
+ * only fills a missing `adapter` link.
14
+ *
15
+ * The row and the adapter are contributed at **different moments, on purpose**.
16
+ * This row is durable state and is written once, at install; the adapter is an
17
+ * in-memory entry in a per-process table and is pushed from the contributing
18
+ * module's boot hook, on every composition (see `payment-adapter-registry.ts`).
19
+ * Neither waits for the other: a row whose `adapter` key nothing has contributed
20
+ * is simply not offered, and the registry is not read until a request reads it.
21
+ *
22
+ * **The `EntityManager` is a parameter of every method and is never held**
23
+ * (feature 134, FR-064; D-169). It used to be a constructor `emFactory`, which is
24
+ * the shape of a service resolved from a container — and the caller this class
25
+ * was written for has no container: `ModuleLifecycleContext` is
26
+ * `{ em, redis, log, module }` and `module:install` composes nothing (D-46). The
27
+ * hook's own `ctx.em` is therefore what every statement here runs on, handed in
28
+ * at the call, so the write lands in the transaction the orchestrator will
29
+ * commit or revert rather than on a fork of it.
30
+ *
31
+ * **It no longer touches sales-channel membership unconditionally (issue #96).**
32
+ * It used to call `bindToDefaultIfEmpty` for every row it created, and the four
33
+ * gateway modules called it from their plugin body on every composition — so a
34
+ * method an operator had deliberately unbound from every channel came back bound
35
+ * to Default at the next boot, and nothing said so. Binding belongs to the two
36
+ * seams that own the decision: the admin create path (a method an operator just
37
+ * created has to land somewhere) and the module's own seed (once, for the rows it
38
+ * creates). `bindToDefaultChannel` below is that second seam, and the caller
39
+ * guards it on `created === true`. "Unbound" is a state an operator is entitled
40
+ * to reach and to keep.
41
+ */
42
+ export class PaymentMethodReconciler {
43
+ async ensureMethodForAdapter(em, adapterKey, defaults) {
44
+ // command-coverage-ignore: idempotent reconciliation of adapter-backed payment
45
+ // methods — a system-invariant repair, not an operator-initiated write.
46
+ const existing = await em.findOne(PaymentMethod, { code: defaults.code });
47
+ if (existing) {
48
+ // Prune-safe: never clobber admin configuration. Only backfill a
49
+ // missing adapter link (e.g. a legacy row predating this feature).
50
+ if (!existing.adapter) {
51
+ existing.adapter = adapterKey;
52
+ await em.persistAndFlush(existing);
53
+ }
54
+ return { row: recordOf(existing), created: false };
55
+ }
56
+ const row = em.create(PaymentMethod, {
57
+ code: defaults.code,
58
+ name: defaults.name,
59
+ kind: defaults.type,
60
+ adapter: adapterKey,
61
+ status: defaults.status ?? 'active',
62
+ additionalPrice: defaults.additionalPrice ?? '0',
63
+ statusOnPending: defaults.statusOnPending ?? 'new',
64
+ statusOnSuccess: defaults.statusOnSuccess ?? 'paid',
65
+ // Feature 085 (FR-003) — see the twin default in
66
+ // `commands/payment-method.commands.ts`. It is also what retires the five
67
+ // `*_failure_status_on_hold` migrations with no replacement: a fresh
68
+ // install through this seam can no longer produce the `'cancelled'` they
69
+ // exist to correct (`contracts/foreign-write-repair.md` §4).
70
+ statusOnFailure: defaults.statusOnFailure ?? 'on_hold',
71
+ });
72
+ await em.persistAndFlush(row);
73
+ return { row: recordOf(row), created: true };
74
+ }
75
+ /**
76
+ * The seed's channel binding — one membership row in the system-default
77
+ * channel, written against **this module's own** bridge table.
78
+ *
79
+ * Not through `SalesChannelMembershipService`, and the reason is structural
80
+ * rather than a preference: that service needs the `EventBus`, the audit port
81
+ * and the channel-bridge registry, and the registry is contributed from a boot
82
+ * hook. An install composes nothing, so at this seam it holds no registration
83
+ * for `'payment-method'` and `bridges.require` would refuse (FR-017) before the
84
+ * database was touched. `sales_channel_payment_methods` is this module's since
85
+ * `specs/120-migration-closure-bridge-ownership/` Phase 2 (D-226), so the
86
+ * statement is the owner's own and crosses no boundary — which is what FR-064
87
+ * buys by putting the writer here instead of in the seeding module.
88
+ *
89
+ * The channel itself is read through the kernel's own `SalesChannel` entity
90
+ * rather than in SQL — `api_keys` reads it the same way — because a raw
91
+ * `select … from "sales_channels"` from a module is a `check:module-boundary`
92
+ * finding against a kernel-owned table, and correctly so: the table is not
93
+ * this module's and the entity is the platform's published name for it.
94
+ *
95
+ * `em.execute` for the insert rather than `em.getConnection().execute`, so the
96
+ * statement runs inside the caller's transaction (issue #200). The bridge has
97
+ * no entity class — the kernel's membership service writes it in SQL too.
98
+ *
99
+ * **No system-default channel answers `false` rather than raising**, exactly as
100
+ * the delivery twin does and for the same measured reason: the default channel
101
+ * is created by `DefaultChannelReconciler` at **boot**, from `composeApp`, and
102
+ * `module:install` composes nothing (D-46), so a database that has been migrated
103
+ * and never booted has none. The seed migration this replaced degraded the same
104
+ * way, silently — its `cross join "sales_channels" where "system_default"`
105
+ * produced no rows and inserted no membership — so answering `false` is what
106
+ * keeps a fresh install and an upgraded one at the same row state in that state
107
+ * too. Raising instead **aborts the install**, and the hook is not inside a
108
+ * database transaction.
109
+ */
110
+ async bindToDefaultChannel(em, paymentMethodId) {
111
+ // command-coverage-ignore: install-time seed membership for a row this seam
112
+ // just created — a system-invariant write with no request and no actor.
113
+ const defaultChannel = await em.findOne(SalesChannel, { systemDefault: true });
114
+ if (!defaultChannel)
115
+ return false;
116
+ const inserted = await em.execute('insert into "sales_channel_payment_methods" ("sales_channel_id", "payment_method_id") ' +
117
+ 'values (?, ?) on conflict ("sales_channel_id", "payment_method_id") do nothing ' +
118
+ 'returning "payment_method_id"', [defaultChannel.id, paymentMethodId]);
119
+ return inserted.length > 0;
120
+ }
121
+ /**
122
+ * The hard-uninstall half. Channel memberships go with the row, and so does
123
+ * every vendor-owned row keyed on it — each gateway's
124
+ * `*_payment_method_rules` and `*_payment_method_org_disables` declare
125
+ * `references "payment_methods" ("id") on delete cascade`.
126
+ */
127
+ async removeMethodForAdapter(em, code) {
128
+ // command-coverage-ignore: hard-uninstall removal of an adapter-backed
129
+ // payment method — no request, no actor and nothing to attribute an audit
130
+ // entry to; the operator path is `commands/payment-method.commands.ts`.
131
+ const removed = await em.nativeDelete(PaymentMethod, { code });
132
+ return removed > 0;
133
+ }
134
+ }
135
+ /**
136
+ * The published record, mapped field by field rather than by handing the entity
137
+ * back (D-168/D-77): a caller reads values off it and cannot persist through it.
138
+ */
139
+ function recordOf(row) {
140
+ return {
141
+ id: row.id,
142
+ code: row.code,
143
+ name: row.name,
144
+ kind: row.kind,
145
+ adapter: row.adapter,
146
+ status: row.status,
147
+ additionalPrice: row.additionalPrice,
148
+ statusOnPending: row.statusOnPending,
149
+ statusOnSuccess: row.statusOnSuccess,
150
+ statusOnFailure: row.statusOnFailure,
151
+ };
152
+ }
153
+ /**
154
+ * The runtime half of this module's install surface (feature 134, FR-064).
155
+ *
156
+ * A seeding module's `installHook` writes `createPaymentMethodSeeder()` and hands
157
+ * `ctx.em` to each call. The factory takes no arguments because the `em` belongs
158
+ * to the call and not to the seeder: one `em`, named at every statement, with no
159
+ * second source of truth for which transaction the write lands in.
160
+ */
161
+ export function createPaymentMethodSeeder() {
162
+ return new PaymentMethodReconciler();
163
+ }
164
+ //# sourceMappingURL=payment-method-reconciler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment-method-reconciler.js","sourceRoot":"","sources":["../../../src/backend/services/payment-method-reconciler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAC;AAOhE,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,OAAO,uBAAuB;IAClC,KAAK,CAAC,sBAAsB,CAC1B,EAAiB,EACjB,UAAkB,EAClB,QAAmC;QAEnC,+EAA+E;QAC/E,wEAAwE;QACxE,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QAC1E,IAAI,QAAQ,EAAE,CAAC;YACb,iEAAiE;YACjE,mEAAmE;YACnE,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;gBACtB,QAAQ,CAAC,OAAO,GAAG,UAAU,CAAC;gBAC9B,MAAM,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACrC,CAAC;YACD,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QACrD,CAAC;QAED,MAAM,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE;YACnC,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,OAAO,EAAE,UAAU;YACnB,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,QAAQ;YACnC,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,GAAG;YAChD,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,KAAK;YAClD,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,MAAM;YACnD,iDAAiD;YACjD,0EAA0E;YAC1E,qEAAqE;YACrE,yEAAyE;YACzE,6DAA6D;YAC7D,eAAe,EAAE,QAAQ,CAAC,eAAe,IAAI,SAAS;SACvD,CAAC,CAAC;QACH,MAAM,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;QAC9B,OAAO,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC/C,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAkCG;IACH,KAAK,CAAC,oBAAoB,CAAC,EAAiB,EAAE,eAAuB;QACnE,4EAA4E;QAC5E,wEAAwE;QACxE,MAAM,cAAc,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/E,IAAI,CAAC,cAAc;YAAE,OAAO,KAAK,CAAC;QAElC,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,OAAO,CAC/B,wFAAwF;YACtF,iFAAiF;YACjF,+BAA+B,EACjC,CAAC,cAAc,CAAC,EAAE,EAAE,eAAe,CAAC,CACrC,CAAC;QACF,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,sBAAsB,CAAC,EAAiB,EAAE,IAAY;QAC1D,uEAAuE;QACvE,0EAA0E;QAC1E,wEAAwE;QACxE,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,OAAO,OAAO,GAAG,CAAC,CAAC;IACrB,CAAC;CACF;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,GAAkB;IAClC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,eAAe,EAAE,GAAG,CAAC,eAAe;KACrC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,IAAI,uBAAuB,EAAE,CAAC;AACvC,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { PaymentAdapterRegistry } from './payment-adapter-registry.js';
2
+ /**
3
+ * Process-wide PaymentAdapterRegistry (feature 034).
4
+ *
5
+ * The four gateway modules and `payments` import this instance directly and
6
+ * push their adapters into it from their `ctx.onBoot` hook, so the platform has
7
+ * exactly one table of adapters however many times it is composed. A module
8
+ * contributes only adapters it owns, and names itself when it does.
9
+ *
10
+ * The presence probe is wired here rather than in the class: this is the one
11
+ * instance that participates in the platform's lifecycle, and a registry a
12
+ * test builds for itself should keep answering about the adapters that test
13
+ * registered (issue #96).
14
+ */
15
+ export declare const paymentAdapterRegistry: PaymentAdapterRegistry;
16
+ //# sourceMappingURL=registry-singleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-singleton.d.ts","sourceRoot":"","sources":["../../../src/backend/services/registry-singleton.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,wBAElC,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { effectiveState } from '@endora-commerce/platform/kernel';
2
+ import { PaymentAdapterRegistry } from './payment-adapter-registry.js';
3
+ /**
4
+ * Process-wide PaymentAdapterRegistry (feature 034).
5
+ *
6
+ * The four gateway modules and `payments` import this instance directly and
7
+ * push their adapters into it from their `ctx.onBoot` hook, so the platform has
8
+ * exactly one table of adapters however many times it is composed. A module
9
+ * contributes only adapters it owns, and names itself when it does.
10
+ *
11
+ * The presence probe is wired here rather than in the class: this is the one
12
+ * instance that participates in the platform's lifecycle, and a registry a
13
+ * test builds for itself should keep answering about the adapters that test
14
+ * registered (issue #96).
15
+ */
16
+ export const paymentAdapterRegistry = new PaymentAdapterRegistry(undefined, (moduleId) => effectiveState.isPresent(moduleId));
17
+ //# sourceMappingURL=registry-singleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry-singleton.js","sourceRoot":"","sources":["../../../src/backend/services/registry-singleton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAEvE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,sBAAsB,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE,CACvF,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CACnC,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The **install surface** this module publishes: one factory, on a subpath whose
3
+ * declared meaning is foreign consumption (feature 134, FR-064;
4
+ * `module-package-layout.md` R11, D-251).
5
+ *
6
+ * A module that ships its own payment gateway seeds its `payment_methods` rows
7
+ * from its `installHook` through `createPaymentMethodSeeder()`, handing `ctx.em`
8
+ * to every call; the interface it returns is published type-only on this
9
+ * package's `./ports`. `autopay`, `paypal`, `payu`, `stripe`, `tpay` and the
10
+ * example deployment's `payment_gateway_fixture` are its six consumers today,
11
+ * and the fixture is the one that stays once wave 2 extracts the five.
12
+ *
13
+ * ## Why this is a subpath of its own rather than a line on `./backend`
14
+ *
15
+ * The delivery twin — `packages/modules/delivery_methods/src/install/index.ts` —
16
+ * carries the argument in full, with the emitted-graph measurement that decided
17
+ * it. **Wave 2 reuses that decision rather than retaking it**
18
+ * (`contracts/foreign-write-repair.md` §2, §8 item 2h). In three lines:
19
+ *
20
+ * * **The npm edge coincides with a declared lifecycle dependency rather than
21
+ * substituting for one.** All five gateways already declare
22
+ * `dependencies: ['…', 'payment_methods', '…']` in their manifests, so the
23
+ * lifecycle, the migration order and the operator's switch all know about
24
+ * this edge; the npm declaration says the same thing a second way, in the
25
+ * only vocabulary a published tarball has. That is what puts this seam
26
+ * **inside** R4's reason rather than beside it.
27
+ * * **`./backend` is deliberately discriminating and must stay so.**
28
+ * `module-package-layout.md` R7: after D-168, *"every reach into
29
+ * `<pkg>/backend` really is a wiring reach"*. A factory there would make one
30
+ * subpath mean either "this consumer is wiring `payment_methods`", a real
31
+ * Principle I violation, or "this consumer seeds its own rows through the
32
+ * sanctioned surface" — two findings with opposite remedies behind one name,
33
+ * multiplied by five gateways and a fixture.
34
+ * * **The import is evaluated at every boot, and it is measured.**
35
+ * `manifest-index.generated.ts` imports every module's manifest statically and
36
+ * eagerly, in the server, the CLI and every worker — so a consumer's
37
+ * `installHook` is *called* at install and its `import` is *loaded* at boot.
38
+ * On the emitted artefact, walked the same way wave 1 walked the delivery
39
+ * twin: `dist/backend/index.js` pulls **9 emitted files and 7 external
40
+ * specifiers**, `@endora-commerce/platform/http` among them, where
41
+ * `dist/install/index.js` pulls **3 and 4, with no HTTP layer** — this file,
42
+ * the reconciler and the entity. `dist/ports/index.js` pulls **1 and 0**,
43
+ * being `export {};`. The delivery twin's install subpath measures 3 and 4 as
44
+ * well, which is what says the two seams cost the same.
45
+ *
46
+ * ## It is a pure re-export, and that is a rule
47
+ *
48
+ * No behaviour is added here and none may be. The install hook is **not** inside
49
+ * a database transaction — measured in wave 1: a hook that threw half-way left
50
+ * the first of two seeded rows behind and never wrote the second — so every
51
+ * behaviour placed on this path is a new partial-write surface. The write stays
52
+ * in `../backend/services/payment-method-reconciler.ts`, where its tests, its
53
+ * `command-coverage-ignore` classification and its Constitution XIII reasoning
54
+ * already live.
55
+ */
56
+ export { createPaymentMethodSeeder } from '../backend/services/payment-method-reconciler.js';
57
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/install/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kDAAkD,CAAC"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * The **install surface** this module publishes: one factory, on a subpath whose
3
+ * declared meaning is foreign consumption (feature 134, FR-064;
4
+ * `module-package-layout.md` R11, D-251).
5
+ *
6
+ * A module that ships its own payment gateway seeds its `payment_methods` rows
7
+ * from its `installHook` through `createPaymentMethodSeeder()`, handing `ctx.em`
8
+ * to every call; the interface it returns is published type-only on this
9
+ * package's `./ports`. `autopay`, `paypal`, `payu`, `stripe`, `tpay` and the
10
+ * example deployment's `payment_gateway_fixture` are its six consumers today,
11
+ * and the fixture is the one that stays once wave 2 extracts the five.
12
+ *
13
+ * ## Why this is a subpath of its own rather than a line on `./backend`
14
+ *
15
+ * The delivery twin — `packages/modules/delivery_methods/src/install/index.ts` —
16
+ * carries the argument in full, with the emitted-graph measurement that decided
17
+ * it. **Wave 2 reuses that decision rather than retaking it**
18
+ * (`contracts/foreign-write-repair.md` §2, §8 item 2h). In three lines:
19
+ *
20
+ * * **The npm edge coincides with a declared lifecycle dependency rather than
21
+ * substituting for one.** All five gateways already declare
22
+ * `dependencies: ['…', 'payment_methods', '…']` in their manifests, so the
23
+ * lifecycle, the migration order and the operator's switch all know about
24
+ * this edge; the npm declaration says the same thing a second way, in the
25
+ * only vocabulary a published tarball has. That is what puts this seam
26
+ * **inside** R4's reason rather than beside it.
27
+ * * **`./backend` is deliberately discriminating and must stay so.**
28
+ * `module-package-layout.md` R7: after D-168, *"every reach into
29
+ * `<pkg>/backend` really is a wiring reach"*. A factory there would make one
30
+ * subpath mean either "this consumer is wiring `payment_methods`", a real
31
+ * Principle I violation, or "this consumer seeds its own rows through the
32
+ * sanctioned surface" — two findings with opposite remedies behind one name,
33
+ * multiplied by five gateways and a fixture.
34
+ * * **The import is evaluated at every boot, and it is measured.**
35
+ * `manifest-index.generated.ts` imports every module's manifest statically and
36
+ * eagerly, in the server, the CLI and every worker — so a consumer's
37
+ * `installHook` is *called* at install and its `import` is *loaded* at boot.
38
+ * On the emitted artefact, walked the same way wave 1 walked the delivery
39
+ * twin: `dist/backend/index.js` pulls **9 emitted files and 7 external
40
+ * specifiers**, `@endora-commerce/platform/http` among them, where
41
+ * `dist/install/index.js` pulls **3 and 4, with no HTTP layer** — this file,
42
+ * the reconciler and the entity. `dist/ports/index.js` pulls **1 and 0**,
43
+ * being `export {};`. The delivery twin's install subpath measures 3 and 4 as
44
+ * well, which is what says the two seams cost the same.
45
+ *
46
+ * ## It is a pure re-export, and that is a rule
47
+ *
48
+ * No behaviour is added here and none may be. The install hook is **not** inside
49
+ * a database transaction — measured in wave 1: a hook that threw half-way left
50
+ * the first of two seeded rows behind and never wrote the second — so every
51
+ * behaviour placed on this path is a new partial-write surface. The write stays
52
+ * in `../backend/services/payment-method-reconciler.ts`, where its tests, its
53
+ * `command-coverage-ignore` classification and its Constitution XIII reasoning
54
+ * already live.
55
+ */
56
+ export { createPaymentMethodSeeder } from '../backend/services/payment-method-reconciler.js';
57
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/install/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,kDAAkD,CAAC"}
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Payment Methods module — manifest backfill (Module Lifecycle, feature 018).
3
+ *
4
+ * Predates the lifecycle system; this manifest is the static record
5
+ * required so the module participates in the registry. No install /
6
+ * uninstall hook today — the module's schema is owned by earlier
7
+ * platform-wide migrations.
8
+ */
9
+ export declare const manifest: {
10
+ id: string;
11
+ name: string;
12
+ version: string;
13
+ dependencies: string[];
14
+ description?: string | undefined;
15
+ acknowledgedDependencies?: {
16
+ moduleId: string;
17
+ port: string;
18
+ reason: string;
19
+ }[] | undefined;
20
+ nonBindingDependencies?: {
21
+ moduleId: string;
22
+ name: string;
23
+ kind: "contributes-to" | "degrades-without" | "refuses-without";
24
+ reason: string;
25
+ whenAbsent?: string | undefined;
26
+ }[] | undefined;
27
+ activation?: {
28
+ settingCode: string;
29
+ default: boolean;
30
+ } | {
31
+ nonDeactivatable: true;
32
+ reason: string;
33
+ } | undefined;
34
+ settings?: {
35
+ moduleCode: string;
36
+ groups: {
37
+ code: string;
38
+ name: string;
39
+ salesChannelCodes?: string[] | undefined;
40
+ isSystemProtected?: boolean | undefined;
41
+ }[];
42
+ settings: {
43
+ code: string;
44
+ name: string;
45
+ valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
46
+ defaultValue: unknown;
47
+ description?: string | undefined;
48
+ groupCode?: string | undefined;
49
+ previousDefaultValues?: unknown[] | undefined;
50
+ salesChannelCodes?: string[] | undefined;
51
+ enumOptions?: string[] | undefined;
52
+ configurationType?: string | undefined;
53
+ hidden?: boolean | undefined;
54
+ }[];
55
+ } | undefined;
56
+ i18n?: {
57
+ bundlesDir: string;
58
+ } | undefined;
59
+ docs?: false | {
60
+ dir: string;
61
+ } | undefined;
62
+ demo?: false | {
63
+ summary: string;
64
+ seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
65
+ reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
66
+ after?: readonly string[] | undefined;
67
+ package?: string | undefined;
68
+ } | undefined;
69
+ actions?: {
70
+ id: string;
71
+ labelKey: string;
72
+ icon: "Plus" | "Sparkles" | "Settings" | "Search" | "Boxes" | "Layers" | "Menu" | "PlusCircle" | "PlusSquare" | "FilePlus" | "FolderPlus" | "Upload" | "FileUp" | "CloudUpload" | "Download" | "FileDown" | "FileText" | "BookOpen" | "Rss" | "Package" | "Tag" | "ShoppingCart" | "Receipt" | "CreditCard" | "Users" | "UserPlus" | "Inbox" | "ListChecks" | "ClipboardList" | "Image" | "Video" | "LayoutDashboard" | "PanelLeft" | "KeyRound" | "ShieldCheck" | "Edit" | "Archive" | "Box" | "Truck" | "CircleDollarSign" | "Activity" | "LineChart" | "Smartphone" | "Webhook" | "Scale" | "PlugZap" | "PercentDiamond" | "Newspaper" | "Languages" | "Eraser" | "Warehouse" | "TrendingDown" | "Bell" | "PackageOpen" | "Building2" | "Store" | "ClipboardCheck";
73
+ targetRoute: string;
74
+ keywords: string[];
75
+ weight: number;
76
+ descriptionKey?: string | undefined;
77
+ requiredPermission?: string | undefined;
78
+ }[] | undefined;
79
+ permissions?: {
80
+ code: string;
81
+ label: string;
82
+ module?: string | undefined;
83
+ description?: string | undefined;
84
+ requires?: string[] | undefined;
85
+ }[] | undefined;
86
+ transactionalEmails?: {
87
+ code: string;
88
+ name: string;
89
+ variables: {
90
+ key: string;
91
+ label: string;
92
+ sampleValue?: string | undefined;
93
+ description?: string | undefined;
94
+ }[];
95
+ description?: string | undefined;
96
+ group?: string | undefined;
97
+ }[] | undefined;
98
+ capabilities?: string[] | undefined;
99
+ exclusiveCapabilities?: {
100
+ key: string;
101
+ errorCode: string;
102
+ }[] | undefined;
103
+ errorCodes?: {
104
+ code: string;
105
+ tokens?: string[] | undefined;
106
+ }[] | undefined;
107
+ blocks?: {
108
+ name: string;
109
+ labelKey: string;
110
+ category: string;
111
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
112
+ fields: Record<string, {
113
+ type: "number" | "object" | "array" | "uuid" | "text" | "textarea" | "select" | "radio" | "external" | "richtext";
114
+ label?: string | undefined;
115
+ required?: boolean | undefined;
116
+ options?: {
117
+ label: string;
118
+ value: string | number;
119
+ }[] | undefined;
120
+ refKind?: string | undefined;
121
+ }>;
122
+ descriptionKey?: string | undefined;
123
+ defaultProps?: Record<string, unknown> | undefined;
124
+ responsiveFields?: string[] | undefined;
125
+ previewIcon?: string | undefined;
126
+ weight?: number | undefined;
127
+ }[] | undefined;
128
+ blockCategories?: {
129
+ key: string;
130
+ titleKey: string;
131
+ contexts: ("invoice" | "email" | "cms" | "newsletter")[];
132
+ weight?: number | undefined;
133
+ visible?: boolean | undefined;
134
+ }[] | undefined;
135
+ env?: {
136
+ name: string;
137
+ describes: {
138
+ en: string;
139
+ pl: string;
140
+ };
141
+ requirement: {
142
+ kind: "required";
143
+ } | {
144
+ kind: "requiredWhen";
145
+ input: string;
146
+ equals: string;
147
+ } | {
148
+ kind: "optional";
149
+ without: {
150
+ en: string;
151
+ pl: string;
152
+ };
153
+ };
154
+ secret: boolean;
155
+ generable: boolean;
156
+ owner: {
157
+ kind: "platform";
158
+ } | {
159
+ kind: "application";
160
+ application: "admin" | "backend" | "storefront";
161
+ } | {
162
+ kind: "module";
163
+ moduleId: string;
164
+ };
165
+ consumers: ("admin" | "backend" | "storefront")[];
166
+ addressOf: "admin" | "backend" | "storefront" | null;
167
+ }[] | undefined;
168
+ };
169
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsHnB,CAAC"}
@@ -0,0 +1,145 @@
1
+ import { defineModuleManifest } from '@endora-commerce/contracts';
2
+ /**
3
+ * The demo data this module owns (feature 113, T220 — contract §1.3).
4
+ *
5
+ * A typed `const` rather than an inline object literal: declared inline the
6
+ * parameter infers from the schema and is `never`, so the author loses
7
+ * `context.ctx: ModuleContext`.
8
+ *
9
+ * Both bodies are reached by a **relative `await import()`** (§1.4), in
10
+ * `cliCommands.run`'s shape and for `cliCommands`' reason: a manifest is loaded
11
+ * by every process that composes the platform and by the check scripts that
12
+ * import the generated index, so a demo body imported at the top of this file
13
+ * would be a service graph pulled into all of them. It needs no `exports`
14
+ * subpath and no `files` entry (§1.5).
15
+ */
16
+ const demo = {
17
+ summary: 'A bank transfer and a credit-limit method, so the demo shop can be paid for.',
18
+ seed: async (context) => (await import('./backend/demo/seed.js')).seedDemo(context),
19
+ reset: async (context) => (await import('./backend/demo/reset.js')).resetDemo(context),
20
+ };
21
+ /**
22
+ * Payment Methods module — manifest backfill (Module Lifecycle, feature 018).
23
+ *
24
+ * Predates the lifecycle system; this manifest is the static record
25
+ * required so the module participates in the registry. No install /
26
+ * uninstall hook today — the module's schema is owned by earlier
27
+ * platform-wide migrations.
28
+ */
29
+ export const manifest = defineModuleManifest({
30
+ id: 'payment_methods',
31
+ name: 'Payment Methods',
32
+ description: 'Payment method definitions and per-channel availability.',
33
+ version: '1.0.0',
34
+ // `auth` owns the `requireAdmin` port the admin routes are gated by; the
35
+ // edge became real with the conversion (feature 072, T097).
36
+ // `organizations` since feature 072 (T138): the public method list is
37
+ // filtered by the caller's per-Organization allow-list, which this module
38
+ // reads through `organizationRestrictionPort`. The edge existed before as a
39
+ // root-supplied closure and was therefore invisible to the manifest.
40
+ dependencies: ['auth', 'organizations'],
41
+ /**
42
+ * D-44 — a real port edge deliberately kept out of `dependencies`, because
43
+ * the owner disappearing is a state this module handles.
44
+ */
45
+ nonBindingDependencies: [
46
+ {
47
+ moduleId: 'payments',
48
+ name: 'paymentReadPort',
49
+ kind: 'degrades-without',
50
+ whenAbsent: 'a payment method cannot be deleted, only set inactive',
51
+ reason: 'The delete-guard (feature 034, FR-003) asks how many payment attempts still point at ' +
52
+ 'the method an operator is deleting. Until feature 075 it asked in raw SQL against ' +
53
+ '`payments`\' own table, which named no import specifier and so crossed the boundary ' +
54
+ 'invisibly (D-87). `degrades-without` rather than `dependencies`: `payments` declares ' +
55
+ 'this module, so declaring it back would close a cycle, and acknowledging the edge ' +
56
+ 'would keep the bind and make `payments.enabled` unusable in every shop that takes ' +
57
+ 'money. `backend.ts` asks `effectiveState.isPresent` before resolving the port, and ' +
58
+ 'the Command refuses the delete with a 409 naming the reason rather than deleting a ' +
59
+ 'method whose references nobody could count.',
60
+ },
61
+ ],
62
+ /**
63
+ * The module's own authority (2026-08-28).
64
+ *
65
+ * All six admin routes used to enforce `catalog:read` / `catalog:write`, so
66
+ * whoever could edit a product could read the payment-method configuration
67
+ * and rewrite it — which methods a checkout offers, their surcharge, and
68
+ * which order status each payment outcome moves an order to. Both codes were
69
+ * real, declared and enforced, so the permission inventory's two directions
70
+ * were clean over the site and `check:action-route-permissions` found the
71
+ * palette action in perfect agreement with its target: they both said
72
+ * `catalog:read`.
73
+ *
74
+ * A pair and no third code, spelled `<module id>:<read|write>` like the five
75
+ * gateway modules, `returns` and `invoices`. A prefix that is not its owner's
76
+ * id is the mistake `PERMISSION_CATALOGUE` comments on twice
77
+ * (`integrations:manage`, `audit_log:read`), both frozen because they are
78
+ * persisted in role rows; getting it right on a code that does not exist yet
79
+ * is free. The codes stay here rather than in `PERMISSION_CATALOGUE`, which
80
+ * is for codes spanning modules — this module owns these outright.
81
+ *
82
+ * No data migration: see
83
+ * `test/contract/payment_methods/permission-authority.test.ts`.
84
+ */
85
+ permissions: [
86
+ { code: 'payment_methods:read', label: 'View payment methods' },
87
+ { code: 'payment_methods:write', label: 'Configure payment methods' },
88
+ ],
89
+ settings: {
90
+ moduleCode: 'payment_methods',
91
+ groups: [{ code: 'payment_methods', name: 'Payment methods' }],
92
+ settings: [
93
+ {
94
+ // Feature 073 — the operator's activation control. Platform-wide.
95
+ code: 'payment_methods.enabled',
96
+ name: 'Payment methods enabled',
97
+ // Same missing consequence as its delivery twin: with no method to
98
+ // pick, order placement answers "Payment method is not active" and no
99
+ // order can be completed at all. Switching this off is a different act
100
+ // from switching `payments` off — that one withdraws the adapters the
101
+ // methods are configured against — and both end the same way for a
102
+ // buyer, so both descriptions have to say so.
103
+ description: 'Switches the payment-method catalog on or off: the admin screens that define methods and their per-channel availability, and the public list a checkout picks from. With no method to pick, checkout cannot be completed and the shop stops taking orders. Nothing is dropped — every method, its channel bindings, its per-organization allow-list and the payments already taken against it stay in the database, and the catalog returns exactly as configured when you switch it back on.',
104
+ groupCode: 'payment_methods',
105
+ valueType: 'boolean',
106
+ defaultValue: true,
107
+ },
108
+ ],
109
+ },
110
+ // Feature 073, Amendment A1 (Constitution XVII) — deactivatable, because the
111
+ // reason it used to give does not hold. "The platform cannot take an order"
112
+ // rests on an edge that is not in the graph: `orders` declares `addresses`,
113
+ // `api_keys`, `carts`, `credit_limits`, `organizations`, `promotions`,
114
+ // `settings` and `transactional_emails`, and neither this module nor
115
+ // `delivery_methods`. The dependents that do declare it — `payments`,
116
+ // `quick_order`, and the four provider modules — fail closed when it is off,
117
+ // which is the intended meaning of switching a payment catalog off.
118
+ demo,
119
+ activation: { settingCode: 'payment_methods.enabled', default: true },
120
+ i18n: { bundlesDir: 'i18n' },
121
+ docs: { dir: 'docs' },
122
+ // Feature 076 (D-83 item 8) — a standing Principle XVI gap, closed here
123
+ // because this change is what starts sending operators to `/payment-methods`
124
+ // from four gateway screens. The module owned a real admin route and declared
125
+ // no palette action at all.
126
+ //
127
+ // `targetRoute` is the landing route **plain**: the action route regex rejects
128
+ // a query string, so the gateway screens' `?highlight=<code>` deep link is an
129
+ // in-page anchor rather than a second action. `payment_methods:read` is what
130
+ // gates `GET /api/v1/admin/payment-methods`, so the palette never advertises
131
+ // a 403.
132
+ actions: [
133
+ {
134
+ id: 'open-payment-methods',
135
+ labelKey: 'actions.openPaymentMethods.label',
136
+ descriptionKey: 'actions.openPaymentMethods.description',
137
+ icon: 'CreditCard',
138
+ targetRoute: '/payment-methods',
139
+ requiredPermission: 'payment_methods:read',
140
+ keywords: ['payment', 'method', 'availability', 'checkout', 'płatność', 'metoda'],
141
+ weight: 150,
142
+ },
143
+ ],
144
+ });
145
+ //# sourceMappingURL=manifest.js.map