@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,103 @@
1
+ import type { FastifyRequest } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import type { ChannelMemberEntityType } from '@endora-commerce/contracts';
4
+ import { type ModuleContext } from '@endora-commerce/platform/kernel';
5
+ import type { CommandBus } from '@endora-commerce/platform/commands';
6
+ import type { RequireAdminAnyFactory, RequireAdminFactory } from '@endora-commerce/platform/kernel';
7
+ import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
8
+ import { EnumOrderStatusRegistry } from './services/order-status-registry.port.js';
9
+ import { PaymentMethodEligibilityService } from './services/payment-method-eligibility.js';
10
+ import { paymentAdapterRegistry } from './services/registry-singleton.js';
11
+ import { PaymentMethod } from './entities/payment-method.entity.js';
12
+ /**
13
+ * `payment_methods` — the module `orders` was mounting (feature 072, wave 1,
14
+ * T097).
15
+ *
16
+ * `orders/plugin.ts` imported both route files, seeded the built-in adapters,
17
+ * built the order-status registry and mounted the lot inside `commerceModule`.
18
+ * The same shape `audit_logs` had, with the same consequence: the payment-method
19
+ * surface could not exist without the orders module being composed, and the
20
+ * eligibility service that feature 034 wrote for it went unwired for two
21
+ * releases because the wiring lived in somebody else's file.
22
+ *
23
+ * **The adapter registry stays the process singleton, on purpose.** `stripe`,
24
+ * `autopay`, `tpay` and `payu` all `import { paymentAdapterRegistry }` and
25
+ * register into it, so building a second one here and registering *that* under
26
+ * the container name would give the platform two registries — precisely the
27
+ * multi-instance defect this wave has already fixed twice (`currencies`,
28
+ * `addresses`). Registering the existing instance as the container's value
29
+ * keeps exactly one and makes the container the single place that decides what
30
+ * it is, which is what turns the eventual replacement into a one-line change.
31
+ *
32
+ * (The singleton's own comment justifies itself with "the install-hook context
33
+ * cannot carry services". That is stale: all four providers register from their
34
+ * `plugin.ts`, at composition time, where the container is available. The
35
+ * replacement belongs to the wave that converts them.)
36
+ */
37
+ export interface PaymentMethodsCradle {
38
+ /**
39
+ * Feature 120 (FR-015) — the platform's channel-bridge registry, resolved by
40
+ * **container name** rather than imported. The shape is the narrow one this
41
+ * module needs, which is what `ctx.cradle<C>()` asks an author to declare;
42
+ * the registry itself is the kernel's and is published on no barrel.
43
+ */
44
+ readonly salesChannelBridgeRegistry: {
45
+ register(bridge: {
46
+ entityType: ChannelMemberEntityType;
47
+ table: string;
48
+ entityIdColumn: string;
49
+ }): void;
50
+ };
51
+ readonly emFactory: () => EntityManager;
52
+ readonly requireAdmin: RequireAdminFactory;
53
+ /**
54
+ * The any-of form of the same `auth` guard, for the one admin route two
55
+ * editors read (`GET /admin/order-statuses`); `routes.ts` says why.
56
+ */
57
+ readonly requireAdminAny: RequireAdminAnyFactory;
58
+ readonly commandBus: CommandBus;
59
+ readonly salesChannelMembershipPort: SalesChannelMembershipPort | undefined;
60
+ /**
61
+ * The two halves the allow-list is composed from (T138). This used to be one
62
+ * `organizationPaymentMethodAllowList` contribution point that a root filled
63
+ * with a closure fusing an actor read and an `organizations` table read.
64
+ * `organizations` could not fill it once converted — a module may not write a
65
+ * name another module owns — and the fusion was the defect anyway: one half
66
+ * is a deployment input, the other is a port.
67
+ */
68
+ readonly customerOrganizationIdResolver: (req: FastifyRequest) => string | null;
69
+ readonly organizationRestrictionPort: {
70
+ allowedIdsFor(organizationId: string, kind: 'paymentMethodIds' | 'deliveryMethodIds' | 'warehouseIds'): Promise<string[] | null>;
71
+ };
72
+ readonly paymentAdapterRegistry: typeof paymentAdapterRegistry;
73
+ readonly paymentMethodEligibility: PaymentMethodEligibilityService;
74
+ readonly paymentOrderStatusRegistry: EnumOrderStatusRegistry;
75
+ }
76
+ export declare function registerModule(ctx: ModuleContext): void;
77
+ /**
78
+ * The module's persisted entity classes, on the `./backend` subpath, as one
79
+ * array and **no named class export** (D-168).
80
+ *
81
+ * This is the shape the platform reads when the package is *installed*: the
82
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
83
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
84
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
85
+ * the package pass of `check-entity-tenant-classification`. A missing array is
86
+ * answered with `[]` — zero entities registered, no error anywhere.
87
+ */
88
+ export declare const entities: (typeof PaymentMethod)[];
89
+ /**
90
+ * The sales-channel membership bridge this module owns (feature 120, FR-015).
91
+ *
92
+ * Exported as well as registered because the owning module is the only honest
93
+ * source for it: a caller that composes no platform — the bare-database test
94
+ * harness, which constructs the kernel's membership service directly — declares
95
+ * these from here rather than from a list of nine it keeps itself, which is
96
+ * exactly the total map this feature deleted.
97
+ */
98
+ export declare const salesChannelBridges: ReadonlyArray<{
99
+ readonly entityType: ChannelMemberEntityType;
100
+ readonly table: string;
101
+ readonly entityIdColumn: string;
102
+ }>;
103
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AAE1E,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,kCAAkC,CAAC;AAEhF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AACrE,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AAKnF,OAAO,EAAE,uBAAuB,EAAE,MAAM,0CAA0C,CAAC;AACnF,OAAO,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAE3F,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,oBAAoB;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,0BAA0B,EAAE;QACnC,QAAQ,CAAC,MAAM,EAAE;YACf,UAAU,EAAE,uBAAuB,CAAC;YACpC,KAAK,EAAE,MAAM,CAAC;YACd,cAAc,EAAE,MAAM,CAAC;SACxB,GAAG,IAAI,CAAC;KACV,CAAC;IACF,QAAQ,CAAC,SAAS,EAAE,MAAM,aAAa,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,sBAAsB,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,0BAA0B,EAAE,0BAA0B,GAAG,SAAS,CAAC;IAC5E;;;;;;;OAOG;IACH,QAAQ,CAAC,8BAA8B,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,GAAG,IAAI,CAAC;IAChF,QAAQ,CAAC,2BAA2B,EAAE;QACpC,aAAa,CACX,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,kBAAkB,GAAG,mBAAmB,GAAG,cAAc,GAC9D,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;KAC7B,CAAC;IACF,QAAQ,CAAC,sBAAsB,EAAE,OAAO,sBAAsB,CAAC;IAC/D,QAAQ,CAAC,wBAAwB,EAAE,+BAA+B,CAAC;IACnE,QAAQ,CAAC,0BAA0B,EAAE,uBAAuB,CAAC;CAC9D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CA2IvD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,QAAQ,0BAEpB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,aAAa,CAAC;IAC9C,QAAQ,CAAC,UAAU,EAAE,uBAAuB,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC,CAEA,CAAC"}
@@ -0,0 +1,151 @@
1
+ import { lazyPort } from '@endora-commerce/platform/kernel';
2
+ import { effectiveState } from '@endora-commerce/platform/kernel';
3
+ import { registerPaymentMethodsAdminRoutes, registerPaymentMethodsPublicRoutes, } from './routes.js';
4
+ import { EnumOrderStatusRegistry } from './services/order-status-registry.port.js';
5
+ import { PaymentMethodEligibilityService } from './services/payment-method-eligibility.js';
6
+ import { PaymentMethodReadService } from './services/payment-method-read-port.js';
7
+ import { paymentAdapterRegistry } from './services/registry-singleton.js';
8
+ import { PaymentMethod } from './entities/payment-method.entity.js';
9
+ export function registerModule(ctx) {
10
+ /**
11
+ * Feature 120 (FR-015) — the sales-channel membership bridge this module owns.
12
+ *
13
+ * The platform used to hold a map **total** over
14
+ * `ChannelMemberEntityTypeSchema`, naming this module's bridge table from
15
+ * inside the kernel. That is D-52/D-53's prohibition expressed in a contract
16
+ * instead of an import, and it meant a membership call for 'payment-method' on an
17
+ * instance that never installed this module executed SQL against a relation
18
+ * that is not there (D-226). The module whose migration creates the table is
19
+ * the one that says where it is; a member nothing registers now refuses at
20
+ * the call instead.
21
+ *
22
+ * A **contribution** hook and deliberately **unprobed** (D-62/D-68): a
23
+ * registration is an inert statement about the schema rather than a
24
+ * capability, the rows outlive an operator switching this module off, and
25
+ * probing would make a runtime activation flip require a restart. It is
26
+ * declared first in this module so any later hook of its own that binds a
27
+ * membership finds the bridge already there.
28
+ */
29
+ ctx.onBoot(() => {
30
+ const { salesChannelBridgeRegistry } = ctx.cradle();
31
+ for (const bridge of salesChannelBridges)
32
+ salesChannelBridgeRegistry.register(bridge);
33
+ });
34
+ ctx.di.register({
35
+ // The built-in adapters are **not** seeded here: they live in `payments`
36
+ // (`payments/adapters/built-in-adapters.ts`), so seeding them from this
37
+ // module would be a reach into another module's internals to populate a
38
+ // registry this one merely holds. `payments` pushes them from its own boot
39
+ // hook (T143a), exactly as each gateway module registers its own — this
40
+ // module holds the table and never decides what is in it.
41
+ //
42
+ // And it stays a plain `di.register`. `PaymentAdapterRegistryPort` is
43
+ // published over this name, which makes it look like the odd one out beside
44
+ // the ports below; it is not. Five modules push into it from `ctx.onBoot`,
45
+ // where a `providePort` gate would throw `MODULE_DISABLED` during
46
+ // composition, and converting it would move every edge into it from
47
+ // `contributes` to `fails-closed` in the deactivation-consequence ledger —
48
+ // changing the sentence the operator's confirmation dialog renders
49
+ // (issue #192; port-publication.md §1.4). Presence is answered at
50
+ // enumeration instead, keyed on the module recorded with each entry.
51
+ paymentAdapterRegistry: ctx.asFunction(() => paymentAdapterRegistry).singleton(),
52
+ paymentMethodEligibility: ctx
53
+ .asFunction((cradle) => new PaymentMethodEligibilityService(cradle.paymentAdapterRegistry))
54
+ .singleton(),
55
+ paymentOrderStatusRegistry: ctx.asFunction(() => new EnumOrderStatusRegistry()).singleton(),
56
+ });
57
+ /**
58
+ * Feature 075, Phase P — the row-level read model.
59
+ *
60
+ * A **port**, unlike the two registrations above. The distinction is the one
61
+ * the deactivation-consequence ledger draws: the two registries are
62
+ * contribution seams whose absent-owner policy lives inside them and whose
63
+ * edges classify as `contributes`, so a gate over the registration would
64
+ * withdraw the table rather than one contributor's entry. A read of this
65
+ * module's own table is nothing of the kind — with `payment_methods` off, a
66
+ * caller asking which methods exist should be told the module is off.
67
+ */
68
+ ctx.di.providePort('paymentMethodReadPort', ctx
69
+ .asFunction(({ emFactory }) => new PaymentMethodReadService(emFactory))
70
+ .singleton());
71
+ ctx.routes(async (app) => {
72
+ const { emFactory, requireAdmin, requireAdminAny, paymentAdapterRegistry: registry, paymentMethodEligibility, paymentOrderStatusRegistry, } = ctx.cradle();
73
+ const membership = ctx.cradle().salesChannelMembershipPort;
74
+ /**
75
+ * Composed here from the two halves, and deliberately without a `catch`.
76
+ * The root closure this replaces wrapped the read in one, so a
77
+ * `ModuleDisabledError` from the port would have read as "no restriction" —
78
+ * fail-open on the one path whose whole job is to restrict. The single
79
+ * degrade that belongs here ("this caller has no Organization") is the
80
+ * resolver's own `null`, and the other ("the Organization is gone") is in
81
+ * `allowedIdsFor`'s return type.
82
+ */
83
+ const resolveAllowList = async (req) => {
84
+ const cradle = ctx.cradle();
85
+ const organizationId = cradle.customerOrganizationIdResolver(req);
86
+ if (organizationId === null)
87
+ return null;
88
+ return cradle.organizationRestrictionPort.allowedIdsFor(organizationId, 'paymentMethodIds');
89
+ };
90
+ await registerPaymentMethodsPublicRoutes(app, {
91
+ emFactory,
92
+ registry,
93
+ eligibility: paymentMethodEligibility,
94
+ resolveOrganizationPaymentMethodAllowList: resolveAllowList,
95
+ });
96
+ /**
97
+ * The delete-guard's one question for `payments`: how many attempts point
98
+ * at the method an operator is deleting (FR-003).
99
+ *
100
+ * Resolved per call and never captured — `payments` is deactivatable, so a
101
+ * port taken once at registration would go on answering after an operator
102
+ * switched it off. `null` while it is absent, which the Command turns into
103
+ * a 409 that says so; the alternative, deleting a method whose references
104
+ * nobody could count, is the orphan the guard exists to prevent. Declared
105
+ * in this module's manifest as `degrades-without` rather than in
106
+ * `dependencies`, because `payments` already declares this module and the
107
+ * reverse would close a cycle — and acknowledging it would keep the bind
108
+ * and make `payments.enabled` unusable.
109
+ */
110
+ const paymentRead = () => effectiveState.isPresent('payments')
111
+ ? lazyPort(ctx, 'paymentReadPort')
112
+ : null;
113
+ await registerPaymentMethodsAdminRoutes(app, {
114
+ emFactory,
115
+ requireAdmin,
116
+ requireAdminAny,
117
+ commandBus: ctx.cradle().commandBus,
118
+ registry,
119
+ orderStatusRegistry: paymentOrderStatusRegistry,
120
+ paymentRead,
121
+ ...(membership === undefined ? {} : { salesChannelMembership: membership }),
122
+ });
123
+ });
124
+ }
125
+ /**
126
+ * The module's persisted entity classes, on the `./backend` subpath, as one
127
+ * array and **no named class export** (D-168).
128
+ *
129
+ * This is the shape the platform reads when the package is *installed*: the
130
+ * boot-time loader (`src/packages/package-runtime.ts`, `exported['entities']`)
131
+ * and the static declaration reader (`scripts/lib/package-declarations.ts`),
132
+ * which is the third source of `check:module-boundary`'s `table→owner` map and
133
+ * the package pass of `check-entity-tenant-classification`. A missing array is
134
+ * answered with `[]` — zero entities registered, no error anywhere.
135
+ */
136
+ export const entities = [
137
+ PaymentMethod,
138
+ ];
139
+ /**
140
+ * The sales-channel membership bridge this module owns (feature 120, FR-015).
141
+ *
142
+ * Exported as well as registered because the owning module is the only honest
143
+ * source for it: a caller that composes no platform — the bare-database test
144
+ * harness, which constructs the kernel's membership service directly — declares
145
+ * these from here rather than from a list of nine it keeps itself, which is
146
+ * exactly the total map this feature deleted.
147
+ */
148
+ export const salesChannelBridges = [
149
+ { entityType: 'payment-method', table: 'sales_channel_payment_methods', entityIdColumn: 'payment_method_id' },
150
+ ];
151
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import type { FastifyInstance, FastifyRequest } from 'fastify';
2
+ import type { EntityManager } from '@mikro-orm/postgresql';
3
+ import { type PaymentReadPort } from '@endora-commerce/contracts';
4
+ import type { CommandBus } from '@endora-commerce/platform/commands';
5
+ import type { SalesChannelMembershipPort } from '@endora-commerce/platform/kernel';
6
+ import type { PaymentAdapterRegistry } from './services/payment-adapter-registry.js';
7
+ import type { PaymentMethodEligibilityService } from './services/payment-method-eligibility.js';
8
+ import { type OrderStatusRegistry } from './services/order-status-registry.port.js';
9
+ import type { RequireAdminAnyFactory, RequireAdminFactory } from '@endora-commerce/platform/kernel';
10
+ /**
11
+ * Public read endpoint: list active payment methods (storefront checkout).
12
+ * Admin mutation lives under `/api/v1/admin/payment-methods`. Eligibility
13
+ * validators (validateUseOnStorefront) are layered on in US2 via the
14
+ * eligibility service; this endpoint keeps the active ∩ org-allow-list
15
+ * filtering.
16
+ */
17
+ export interface PaymentMethodsPublicDeps {
18
+ emFactory: () => EntityManager;
19
+ /**
20
+ * Feature 026 US4 — per-Organization allow-list of payment-method IDs.
21
+ * Empty / null ⇒ platform defaults apply (every active method is offered).
22
+ */
23
+ resolveOrganizationPaymentMethodAllowList?: (req: FastifyRequest) => Promise<string[] | null>;
24
+ /** Feature 034 — resolves a method's storefront renderer key. */
25
+ registry?: PaymentAdapterRegistry;
26
+ /** Feature 034 — applies adapter `validateUseOnStorefront` + registered-adapter filtering. */
27
+ eligibility?: PaymentMethodEligibilityService;
28
+ }
29
+ export interface PaymentMethodsAdminDeps {
30
+ emFactory: () => EntityManager;
31
+ requireAdmin: RequireAdminFactory;
32
+ /**
33
+ * Only `GET /api/v1/admin/order-statuses` uses it, and only because that one
34
+ * route is read by two editors — see the comment on its registration below.
35
+ */
36
+ requireAdminAny: RequireAdminAnyFactory;
37
+ /** Issue #125 — the two admin writes run on the bus (Principle XIII). */
38
+ commandBus: CommandBus;
39
+ /** Feature 005 / T027b — new payment methods auto-bind to the system default. */
40
+ salesChannelMembership?: SalesChannelMembershipPort;
41
+ /** Feature 034 — validates `adapter` against the registered adapters. */
42
+ registry?: PaymentAdapterRegistry;
43
+ /** Feature 034 — validates `statusOn*` references + powers /admin/order-statuses. */
44
+ orderStatusRegistry?: OrderStatusRegistry;
45
+ /**
46
+ * Feature 075 — the delete-guard's read of `payments`, or `null` when that
47
+ * module is not effectively present. Required, unlike the optional deps
48
+ * above: a missing guard would silently delete a method attempts still point
49
+ * at, which is the one failure FR-003 exists to prevent.
50
+ */
51
+ paymentRead: () => PaymentReadPort | null;
52
+ }
53
+ export declare function registerPaymentMethodsPublicRoutes(app: FastifyInstance, deps: PaymentMethodsPublicDeps): Promise<void>;
54
+ export declare function registerPaymentMethodsAdminRoutes(app: FastifyInstance, deps: PaymentMethodsAdminDeps): Promise<void>;
55
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAWrE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0CAA0C,CAAC;AAChG,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EACV,sBAAsB,EACtB,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B;;;OAGG;IACH,yCAAyC,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9F,iEAAiE;IACjE,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,8FAA8F;IAC9F,WAAW,CAAC,EAAE,+BAA+B,CAAC;CAC/C;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,aAAa,CAAC;IAC/B,YAAY,EAAE,mBAAmB,CAAC;IAClC;;;OAGG;IACH,eAAe,EAAE,sBAAsB,CAAC;IACxC,yEAAyE;IACzE,UAAU,EAAE,UAAU,CAAC;IACvB,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,0BAA0B,CAAC;IACpD,yEAAyE;IACzE,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,qFAAqF;IACrF,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;;;OAKG;IACH,WAAW,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;CAC3C;AAMD,wBAAsB,kCAAkC,CACtD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA4Bf;AAED,wBAAsB,iCAAiC,CACrD,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA4Jf"}
@@ -0,0 +1,226 @@
1
+ import { ERROR_CODES, paymentMethodStatusPatchSchema, paymentMethodUpsertSchema, } from '@endora-commerce/contracts';
2
+ import { HttpError } from '@endora-commerce/platform/http';
3
+ import { makeDeletePaymentMethodCommand, makeSetPaymentMethodStatusCommand, makeUpsertPaymentMethodCommand, } from './commands/payment-method.commands.js';
4
+ import { effectiveState, toModulePresenceDto, } from '@endora-commerce/platform/kernel';
5
+ import { PaymentMethod } from './entities/payment-method.entity.js';
6
+ import { OrderStatusRegistryError, } from './services/order-status-registry.port.js';
7
+ function rendererKeyFor(deps, adapter) {
8
+ return deps.registry?.get(adapter)?.renderers?.storefront ?? null;
9
+ }
10
+ export async function registerPaymentMethodsPublicRoutes(app, deps) {
11
+ app.get('/api/v1/payment-methods', async (request) => {
12
+ const em = deps.emFactory();
13
+ let rows = await em.find(PaymentMethod, { status: 'active' }, { orderBy: { code: 'asc' } });
14
+ if (deps.resolveOrganizationPaymentMethodAllowList) {
15
+ const allowList = await deps.resolveOrganizationPaymentMethodAllowList(request);
16
+ if (allowList && allowList.length > 0) {
17
+ const allowSet = new Set(allowList);
18
+ rows = rows.filter((m) => allowSet.has(m.id));
19
+ }
20
+ }
21
+ // Feature 034 — adapter validateUseOnStorefront + registered-adapter filter.
22
+ // Sales-channel-assignment filtering is applied upstream once the storefront
23
+ // checkout passes the resolved channel; org/customer context is best-effort
24
+ // here (built-in validators do not depend on it).
25
+ if (deps.eligibility) {
26
+ rows = await deps.eligibility.filter(rows, {
27
+ salesChannelId: null,
28
+ organizationId: null,
29
+ customerAccountId: null,
30
+ surface: 'storefront',
31
+ });
32
+ }
33
+ return { data: rows.map((m) => serializePublic(m, deps)) };
34
+ });
35
+ }
36
+ export async function registerPaymentMethodsAdminRoutes(app, deps) {
37
+ const requireAdmin = deps.requireAdmin;
38
+ app.get('/api/v1/admin/payment-methods', { preHandler: requireAdmin('payment_methods:read') }, async () => {
39
+ const em = deps.emFactory();
40
+ const rows = await em.find(PaymentMethod, {}, { orderBy: { code: 'asc' } });
41
+ const data = await Promise.all(rows.map((m) => serializeAdmin(m, deps)));
42
+ return { data };
43
+ });
44
+ /**
45
+ * T017a — Order-status options for the admin status selectors (FR-009).
46
+ *
47
+ * The one route of this module's six that is **not** gated on
48
+ * `payment_methods:read` alone, and the exception is a fact about its
49
+ * consumers rather than about its data. It is registered once, here, and read
50
+ * by two editors: the payment-method screen and — through
51
+ * `admin/src/modules/delivery_methods/api/delivery-methods-client.ts` — the
52
+ * delivery-method one. Gating it on `payment_methods:read` alone would leave
53
+ * the delivery-method status selectors 403 for a role that holds only the
54
+ * delivery pair, which is every role that can open that screen and not this
55
+ * one.
56
+ *
57
+ * So it is an any-of over the two editors' read codes: holding either one is
58
+ * sufficient, which is what `requireAdminAny` means. The second member was
59
+ * `catalog:read` for as long as `delivery_methods` borrowed the catalogue's
60
+ * authority (MR !1078), and both members were asserted in
61
+ * `test/contract/payment_methods/permission-authority.test.ts` so that the
62
+ * merge request giving that module its own pair had to replace it visibly
63
+ * rather than widen the gate in silence. This is that replacement: the member
64
+ * is now `delivery_methods:read`, and no catalogue holder reaches the shared
65
+ * list any more, because no catalogue holder opens either editor.
66
+ *
67
+ * The edge does not bind: an operator may switch `delivery_methods` off, and
68
+ * when they do the code stops being grantable while `payment_methods:read`
69
+ * goes on opening the list for the only screen still asking for it.
70
+ */
71
+ app.get('/api/v1/admin/order-statuses', { preHandler: deps.requireAdminAny(['payment_methods:read', 'delivery_methods:read']) }, async () => {
72
+ const list = deps.orderStatusRegistry?.list() ?? [];
73
+ return { data: list };
74
+ });
75
+ // Registered payment adapters (feature 034) — powers the admin adapter picker
76
+ // so an admin can bind a payment method to a specific backend driver
77
+ // (e.g. `stripe`, `bank_transfer`) rather than defaulting to its `kind`.
78
+ app.get('/api/v1/admin/payment-methods/adapters', { preHandler: requireAdmin('payment_methods:read') }, async () => {
79
+ const list = deps.registry?.list() ?? [];
80
+ return { data: list };
81
+ });
82
+ app.put('/api/v1/admin/payment-methods/:code', {
83
+ preHandler: requireAdmin('payment_methods:write'),
84
+ schema: { body: paymentMethodUpsertSchema },
85
+ }, async (request) => {
86
+ const body = paymentMethodUpsertSchema.parse(request.body);
87
+ const em = deps.emFactory();
88
+ const adapter = body.adapter ?? body.kind;
89
+ if (deps.registry && !deps.registry.isRegistered(adapter)) {
90
+ throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, `Unknown payment adapter "${adapter}". Register an adapter module first.`);
91
+ }
92
+ // Validate any explicitly-provided Order-status references (FR-009).
93
+ if (deps.orderStatusRegistry) {
94
+ for (const ref of [body.statusOnPending, body.statusOnSuccess, body.statusOnFailure]) {
95
+ if (ref !== undefined)
96
+ assertValidStatus(deps.orderStatusRegistry, ref);
97
+ }
98
+ }
99
+ // Which methods a shop offers, and which order status each payment result
100
+ // moves an order to, is operator configuration. The read below only tells
101
+ // the Command whether this call creates or updates — the write itself
102
+ // happens inside the bus transaction (issue #125).
103
+ const existing = await em.findOne(PaymentMethod, { code: request.params.code });
104
+ const { method: row, created } = await deps.commandBus.run(makeUpsertPaymentMethodCommand({
105
+ code: request.params.code,
106
+ body,
107
+ adapter,
108
+ existingId: existing?.id ?? null,
109
+ }));
110
+ // Channel membership is the sales-channel bridge's own write, on its own
111
+ // EntityManager, so it stays outside the Command rather than pretending to
112
+ // share its transaction.
113
+ if (created && deps.salesChannelMembership) {
114
+ await deps.salesChannelMembership.bindToDefaultIfEmpty('payment-method', row.id);
115
+ }
116
+ // Replace sales-channel membership when an explicit (non-empty) set is given.
117
+ if (body.salesChannelIds && body.salesChannelIds.length > 0 && deps.salesChannelMembership) {
118
+ await replaceChannelMembership(deps.salesChannelMembership, row.id, body.salesChannelIds);
119
+ }
120
+ return { data: await serializeAdmin(row, deps) };
121
+ });
122
+ /**
123
+ * Availability — feature 076, D-82. The one write that decides whether a
124
+ * method is offered to buyers, wherever an operator arrives from: the four
125
+ * gateway screens link here rather than writing the column themselves.
126
+ *
127
+ * Keyed by **id**, like the `DELETE` beside it and unlike the `PUT` above:
128
+ * the `PUT` is an upsert, so it is keyed by the natural key it may create,
129
+ * while this operates on a row that must already exist. The 404 is inside the
130
+ * Command, on its own transaction.
131
+ */
132
+ app.patch('/api/v1/admin/payment-methods/:id/status', {
133
+ preHandler: requireAdmin('payment_methods:write'),
134
+ schema: { body: paymentMethodStatusPatchSchema },
135
+ }, async (request) => {
136
+ const body = paymentMethodStatusPatchSchema.parse(request.body);
137
+ const row = await deps.commandBus.run(makeSetPaymentMethodStatusCommand(request.params.id, body.status));
138
+ return { data: await serializeAdmin(row, deps) };
139
+ });
140
+ app.delete('/api/v1/admin/payment-methods/:id', { preHandler: requireAdmin('payment_methods:write') }, async (request, reply) => {
141
+ // The 404 and the T017b delete-guard (FR-003) live inside the Command.
142
+ // The guard's count runs on `paymentReadPort`, so it is `payments`
143
+ // answering for its own table rather than this module reading it; with
144
+ // that module switched off the guard has no answer and the delete is
145
+ // refused rather than taken on trust.
146
+ await deps.commandBus.run(makeDeletePaymentMethodCommand(request.params.id, { paymentRead: deps.paymentRead }));
147
+ return reply.status(204).send();
148
+ });
149
+ }
150
+ function assertValidStatus(registry, ref) {
151
+ try {
152
+ registry.assertValid(ref);
153
+ }
154
+ catch (err) {
155
+ if (err instanceof OrderStatusRegistryError) {
156
+ throw new HttpError(400, ERROR_CODES.VALIDATION_FAILED, `Unknown order status "${ref}".`);
157
+ }
158
+ throw err;
159
+ }
160
+ }
161
+ async function replaceChannelMembership(membership, methodId, desiredChannelIds) {
162
+ const current = await membership.listChannelsForEntity('payment-method', methodId);
163
+ const currentIds = new Set(current.map((c) => c.id));
164
+ const desired = new Set(desiredChannelIds);
165
+ // Add first so a later removal never transiently leaves zero channels.
166
+ for (const id of desired) {
167
+ if (!currentIds.has(id))
168
+ await membership.addToChannel(id, 'payment-method', methodId);
169
+ }
170
+ for (const id of currentIds) {
171
+ if (!desired.has(id))
172
+ await membership.removeFromChannel(id, 'payment-method', methodId);
173
+ }
174
+ }
175
+ function serializePublic(m, deps) {
176
+ return {
177
+ id: m.id,
178
+ code: m.code,
179
+ name: m.name,
180
+ kind: m.kind,
181
+ adapter: m.adapter,
182
+ status: m.status,
183
+ additionalPrice: Number(m.additionalPrice),
184
+ rendererKey: rendererKeyFor(deps, m.adapter),
185
+ };
186
+ }
187
+ /**
188
+ * Why this method is, or is not, offered to a buyer (issue #96).
189
+ *
190
+ * The admin keeps every row — switching a gateway off drops nothing — so the
191
+ * list carries the reason instead of the row. The reason is the owning module's
192
+ * presence, taken from the projection `/platform/modules` already renders, so
193
+ * no second vocabulary and no hard-coded module list on either frontend.
194
+ */
195
+ function availabilityOf(m, deps) {
196
+ const ownerModule = deps.registry?.ownerOf(m.adapter) ?? null;
197
+ const presence = ownerModule === null ? undefined : effectiveState.presence(ownerModule);
198
+ return {
199
+ ownerModule,
200
+ available: deps.registry?.isAvailable(m.adapter) ?? false,
201
+ ownerPresence: presence ? toModulePresenceDto(presence) : null,
202
+ };
203
+ }
204
+ async function serializeAdmin(m, deps) {
205
+ const channels = deps.salesChannelMembership
206
+ ? await deps.salesChannelMembership.listChannelsForEntity('payment-method', m.id)
207
+ : [];
208
+ return {
209
+ id: m.id,
210
+ code: m.code,
211
+ adapter: m.adapter,
212
+ kind: m.kind,
213
+ name: m.name,
214
+ status: m.status,
215
+ additionalPrice: Number(m.additionalPrice),
216
+ statusOnPending: m.statusOnPending,
217
+ statusOnSuccess: m.statusOnSuccess,
218
+ statusOnFailure: m.statusOnFailure,
219
+ salesChannelIds: channels.map((c) => c.id),
220
+ // Presence-blind: an admin screen goes on rendering a Stripe row the way
221
+ // Stripe renders it while Stripe is switched off.
222
+ rendererKey: deps.registry?.entry(m.adapter)?.adapter.renderers?.admin ?? null,
223
+ availability: availabilityOf(m, deps),
224
+ };
225
+ }
226
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/backend/routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resolve a payment-method display name (feature 034, FR-008).
3
+ *
4
+ * A method's `name` is a `lang → label` record. Resolution order:
5
+ * 1. exact language match (`name[lang]`),
6
+ * 2. the `default` key,
7
+ * 3. the first available value.
8
+ *
9
+ * Returns an empty string only for an empty record (which the schema rejects).
10
+ */
11
+ export declare function resolvePaymentMethodName(name: Record<string, string>, lang?: string | null): string;
12
+ //# sourceMappingURL=name-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-resolver.d.ts","sourceRoot":"","sources":["../../../src/backend/services/name-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC5B,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GACnB,MAAM,CAKR"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Resolve a payment-method display name (feature 034, FR-008).
3
+ *
4
+ * A method's `name` is a `lang → label` record. Resolution order:
5
+ * 1. exact language match (`name[lang]`),
6
+ * 2. the `default` key,
7
+ * 3. the first available value.
8
+ *
9
+ * Returns an empty string only for an empty record (which the schema rejects).
10
+ */
11
+ export function resolvePaymentMethodName(name, lang) {
12
+ if (lang && name[lang])
13
+ return name[lang];
14
+ if (name.default)
15
+ return name.default;
16
+ const first = Object.values(name)[0];
17
+ return first ?? '';
18
+ }
19
+ //# sourceMappingURL=name-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"name-resolver.js","sourceRoot":"","sources":["../../../src/backend/services/name-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,UAAU,wBAAwB,CACtC,IAA4B,EAC5B,IAAoB;IAEpB,IAAI,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC;IACtC,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,KAAK,IAAI,EAAE,CAAC;AACrB,CAAC"}