@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.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/admin/api/payment-methods-client.d.ts +65 -0
- package/dist/admin/api/payment-methods-client.d.ts.map +1 -0
- package/dist/admin/api/payment-methods-client.js +39 -0
- package/dist/admin/api/payment-methods-client.js.map +1 -0
- package/dist/admin/index.d.ts +35 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +37 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/pages/PaymentMethodsPage.d.ts +9 -0
- package/dist/admin/pages/PaymentMethodsPage.d.ts.map +1 -0
- package/dist/admin/pages/PaymentMethodsPage.js +228 -0
- package/dist/admin/pages/PaymentMethodsPage.js.map +1 -0
- package/dist/admin/renderers/registry.d.ts +15 -0
- package/dist/admin/renderers/registry.d.ts.map +1 -0
- package/dist/admin/renderers/registry.js +18 -0
- package/dist/admin/renderers/registry.js.map +1 -0
- package/dist/backend/commands/payment-method.commands.d.ts +86 -0
- package/dist/backend/commands/payment-method.commands.d.ts.map +1 -0
- package/dist/backend/commands/payment-method.commands.js +168 -0
- package/dist/backend/commands/payment-method.commands.js.map +1 -0
- package/dist/backend/demo/reset.d.ts +13 -0
- package/dist/backend/demo/reset.d.ts.map +1 -0
- package/dist/backend/demo/reset.js +13 -0
- package/dist/backend/demo/reset.js.map +1 -0
- package/dist/backend/demo/rows.d.ts +31 -0
- package/dist/backend/demo/rows.d.ts.map +1 -0
- package/dist/backend/demo/rows.js +50 -0
- package/dist/backend/demo/rows.js.map +1 -0
- package/dist/backend/demo/seed.d.ts +17 -0
- package/dist/backend/demo/seed.d.ts.map +1 -0
- package/dist/backend/demo/seed.js +30 -0
- package/dist/backend/demo/seed.js.map +1 -0
- package/dist/backend/entities/payment-method.entity.d.ts +28 -0
- package/dist/backend/entities/payment-method.entity.d.ts.map +1 -0
- package/dist/backend/entities/payment-method.entity.js +93 -0
- package/dist/backend/entities/payment-method.entity.js.map +1 -0
- package/dist/backend/index.d.ts +103 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +151 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.d.ts +55 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +226 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/services/name-resolver.d.ts +12 -0
- package/dist/backend/services/name-resolver.d.ts.map +1 -0
- package/dist/backend/services/name-resolver.js +19 -0
- package/dist/backend/services/name-resolver.js.map +1 -0
- package/dist/backend/services/order-status-registry.port.d.ts +57 -0
- package/dist/backend/services/order-status-registry.port.d.ts.map +1 -0
- package/dist/backend/services/order-status-registry.port.js +53 -0
- package/dist/backend/services/order-status-registry.port.js.map +1 -0
- package/dist/backend/services/payment-adapter-registry.d.ts +79 -0
- package/dist/backend/services/payment-adapter-registry.d.ts.map +1 -0
- package/dist/backend/services/payment-adapter-registry.js +87 -0
- package/dist/backend/services/payment-adapter-registry.js.map +1 -0
- package/dist/backend/services/payment-method-eligibility.d.ts +24 -0
- package/dist/backend/services/payment-method-eligibility.d.ts.map +1 -0
- package/dist/backend/services/payment-method-eligibility.js +48 -0
- package/dist/backend/services/payment-method-eligibility.js.map +1 -0
- package/dist/backend/services/payment-method-read-port.d.ts +25 -0
- package/dist/backend/services/payment-method-read-port.d.ts.map +1 -0
- package/dist/backend/services/payment-method-read-port.js +57 -0
- package/dist/backend/services/payment-method-read-port.js.map +1 -0
- package/dist/backend/services/payment-method-reconciler.d.ts +97 -0
- package/dist/backend/services/payment-method-reconciler.d.ts.map +1 -0
- package/dist/backend/services/payment-method-reconciler.js +164 -0
- package/dist/backend/services/payment-method-reconciler.js.map +1 -0
- package/dist/backend/services/registry-singleton.d.ts +16 -0
- package/dist/backend/services/registry-singleton.d.ts.map +1 -0
- package/dist/backend/services/registry-singleton.js +17 -0
- package/dist/backend/services/registry-singleton.js.map +1 -0
- package/dist/install/index.d.ts +57 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +57 -0
- package/dist/install/index.js.map +1 -0
- package/dist/manifest.d.ts +169 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +145 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260611T140353_payment_methods_adapter.d.ts +32 -0
- package/dist/migrations/20260611T140353_payment_methods_adapter.d.ts.map +1 -0
- package/dist/migrations/20260611T140353_payment_methods_adapter.js +76 -0
- package/dist/migrations/20260611T140353_payment_methods_adapter.js.map +1 -0
- package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.d.ts +46 -0
- package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.d.ts.map +1 -0
- package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.js +54 -0
- package/dist/migrations/20260821T084920_payment_methods_failure_status_on_hold.js.map +1 -0
- package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.d.ts +27 -0
- package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.d.ts.map +1 -0
- package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.js +44 -0
- package/dist/migrations/20260912T094631_payment_methods_sales_channel_payment_methods.js.map +1 -0
- package/dist/migrations/index.d.ts +32 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +36 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/ports/index.d.ts +157 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +2 -0
- package/dist/ports/index.js.map +1 -0
- package/docs/payment_methods.md +146 -0
- package/i18n/en.json +5 -0
- package/i18n/pl.json +5 -0
- package/package.json +105 -0
- 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"}
|