@endora-commerce/mod-payments 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 +59 -0
- package/dist/admin/index.d.ts +32 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +61 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/zones/OrderPaymentsTab.d.ts +42 -0
- package/dist/admin/zones/OrderPaymentsTab.d.ts.map +1 -0
- package/dist/admin/zones/OrderPaymentsTab.js +104 -0
- package/dist/admin/zones/OrderPaymentsTab.js.map +1 -0
- package/dist/backend/adapters/built-in-adapters.d.ts +58 -0
- package/dist/backend/adapters/built-in-adapters.d.ts.map +1 -0
- package/dist/backend/adapters/built-in-adapters.js +83 -0
- package/dist/backend/adapters/built-in-adapters.js.map +1 -0
- package/dist/backend/drivers/bank-transfer-driver.d.ts +28 -0
- package/dist/backend/drivers/bank-transfer-driver.d.ts.map +1 -0
- package/dist/backend/drivers/bank-transfer-driver.js +26 -0
- package/dist/backend/drivers/bank-transfer-driver.js.map +1 -0
- package/dist/backend/drivers/gateway-adapter-port.d.ts +71 -0
- package/dist/backend/drivers/gateway-adapter-port.d.ts.map +1 -0
- package/dist/backend/drivers/gateway-adapter-port.js +18 -0
- package/dist/backend/drivers/gateway-adapter-port.js.map +1 -0
- package/dist/backend/drivers/pickup-driver.d.ts +23 -0
- package/dist/backend/drivers/pickup-driver.d.ts.map +1 -0
- package/dist/backend/drivers/pickup-driver.js +19 -0
- package/dist/backend/drivers/pickup-driver.js.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts +6 -0
- package/dist/backend/email-templates/transactional-defaults.d.ts.map +1 -0
- package/dist/backend/email-templates/transactional-defaults.js +27 -0
- package/dist/backend/email-templates/transactional-defaults.js.map +1 -0
- package/dist/backend/entities/payment.entity.d.ts +26 -0
- package/dist/backend/entities/payment.entity.d.ts.map +1 -0
- package/dist/backend/entities/payment.entity.js +100 -0
- package/dist/backend/entities/payment.entity.js.map +1 -0
- package/dist/backend/index.d.ts +88 -0
- package/dist/backend/index.d.ts.map +1 -0
- package/dist/backend/index.js +318 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/routes.customer.d.ts +20 -0
- package/dist/backend/routes.customer.d.ts.map +1 -0
- package/dist/backend/routes.customer.js +24 -0
- package/dist/backend/routes.customer.js.map +1 -0
- package/dist/backend/routes.d.ts +36 -0
- package/dist/backend/routes.d.ts.map +1 -0
- package/dist/backend/routes.js +44 -0
- package/dist/backend/routes.js.map +1 -0
- package/dist/backend/services/gateway-refund-registry.d.ts +122 -0
- package/dist/backend/services/gateway-refund-registry.d.ts.map +1 -0
- package/dist/backend/services/gateway-refund-registry.js +106 -0
- package/dist/backend/services/gateway-refund-registry.js.map +1 -0
- package/dist/backend/services/payment-email-notifier.d.ts +71 -0
- package/dist/backend/services/payment-email-notifier.d.ts.map +1 -0
- package/dist/backend/services/payment-email-notifier.js +73 -0
- package/dist/backend/services/payment-email-notifier.js.map +1 -0
- package/dist/backend/services/payment-email-renderer.d.ts +22 -0
- package/dist/backend/services/payment-email-renderer.d.ts.map +1 -0
- package/dist/backend/services/payment-email-renderer.js +17 -0
- package/dist/backend/services/payment-email-renderer.js.map +1 -0
- package/dist/backend/services/payment-placement-apply-port.d.ts +33 -0
- package/dist/backend/services/payment-placement-apply-port.d.ts.map +1 -0
- package/dist/backend/services/payment-placement-apply-port.js +68 -0
- package/dist/backend/services/payment-placement-apply-port.js.map +1 -0
- package/dist/backend/services/payment-read-port.d.ts +33 -0
- package/dist/backend/services/payment-read-port.d.ts.map +1 -0
- package/dist/backend/services/payment-read-port.js +64 -0
- package/dist/backend/services/payment-read-port.js.map +1 -0
- package/dist/backend/services/payment-reference-port.d.ts +29 -0
- package/dist/backend/services/payment-reference-port.d.ts.map +1 -0
- package/dist/backend/services/payment-reference-port.js +70 -0
- package/dist/backend/services/payment-reference-port.js.map +1 -0
- package/dist/backend/services/payment-refund.d.ts +55 -0
- package/dist/backend/services/payment-refund.d.ts.map +1 -0
- package/dist/backend/services/payment-refund.js +101 -0
- package/dist/backend/services/payment-refund.js.map +1 -0
- package/dist/backend/services/payment-retry-service.d.ts +45 -0
- package/dist/backend/services/payment-retry-service.d.ts.map +1 -0
- package/dist/backend/services/payment-retry-service.js +187 -0
- package/dist/backend/services/payment-retry-service.js.map +1 -0
- package/dist/backend/services/payment-service.d.ts +62 -0
- package/dist/backend/services/payment-service.d.ts.map +1 -0
- package/dist/backend/services/payment-service.js +134 -0
- package/dist/backend/services/payment-service.js.map +1 -0
- package/dist/backend/services/receive-payment-handler.d.ts +260 -0
- package/dist/backend/services/receive-payment-handler.d.ts.map +1 -0
- package/dist/backend/services/receive-payment-handler.js +356 -0
- package/dist/backend/services/receive-payment-handler.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/manifest.d.ts +210 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +182 -0
- package/dist/manifest.js.map +1 -0
- package/dist/migrations/20260925T115728_payments_refunded_amount.d.ts +28 -0
- package/dist/migrations/20260925T115728_payments_refunded_amount.d.ts.map +1 -0
- package/dist/migrations/20260925T115728_payments_refunded_amount.js +32 -0
- package/dist/migrations/20260925T115728_payments_refunded_amount.js.map +1 -0
- package/dist/migrations/index.d.ts +27 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/index.js +29 -0
- package/dist/migrations/index.js.map +1 -0
- package/docs/payments.md +69 -0
- package/i18n/en.json +5 -0
- package/i18n/pl.json +5 -0
- package/package.json +96 -0
- package/tailwind.css +14 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { ERROR_CODES, ORDER_STATUS_ON_HOLD } from '@endora-commerce/contracts';
|
|
3
|
+
import { HttpError } from '@endora-commerce/platform/http';
|
|
4
|
+
import { Payment } from '../entities/payment.entity.js';
|
|
5
|
+
/**
|
|
6
|
+
* ReceivePaymentHandler (feature 034, FR-022/FR-023/FR-025).
|
|
7
|
+
*
|
|
8
|
+
* Resolves a Payment from the ingress payload, applies the outcome, and asks
|
|
9
|
+
* the order lifecycle for the move the method's `statusOnSuccess` /
|
|
10
|
+
* `statusOnFailure` names. Idempotent: a success after a terminal `paid` is a
|
|
11
|
+
* no-op; a failure after `paid` is rejected (no downgrade). Resolves a late
|
|
12
|
+
* event even when the adapter has since been de-registered (it keys on the
|
|
13
|
+
* persisted Payment, not the live registry).
|
|
14
|
+
*
|
|
15
|
+
* **The lifecycle answers; this handler does not decide** (feature 085 Phase
|
|
16
|
+
* D). It used to assign `order.status` after asking an `OrderStatusRegistry`
|
|
17
|
+
* whether the code existed, which is a different question from whether the
|
|
18
|
+
* order may go there — so the ingress wrote transitions the configured graph
|
|
19
|
+
* forbids, on the happy path, while an operator making the same move by hand
|
|
20
|
+
* was refused. Existence is now the port's `unknown_status` answer and
|
|
21
|
+
* reachability its `not_permitted` one, and both leave the order where it is.
|
|
22
|
+
*/
|
|
23
|
+
export class ReceivePaymentHandler {
|
|
24
|
+
emFactory;
|
|
25
|
+
paymentMethodRead;
|
|
26
|
+
orderTransition;
|
|
27
|
+
orderPaymentStatus;
|
|
28
|
+
log;
|
|
29
|
+
events;
|
|
30
|
+
constructor(emFactory,
|
|
31
|
+
/**
|
|
32
|
+
* The method behind the payment, as `payment_methods` publishes it. Read
|
|
33
|
+
* inside the settlement transaction, as `shipments` reads its delivery
|
|
34
|
+
* method: it is a read of a row this transaction never writes, so it is not
|
|
35
|
+
* held by `payments_order_fk` and carries none of the atomicity the `Order`
|
|
36
|
+
* import above does. When `payment_methods` is off it throws, inside the
|
|
37
|
+
* transaction, and the payment and the order roll back together.
|
|
38
|
+
*/
|
|
39
|
+
paymentMethodRead,
|
|
40
|
+
/**
|
|
41
|
+
* The lifecycle write (feature 085 Phase D). It validates against the
|
|
42
|
+
* configured graph, runs the veto guards, records the audit entry and
|
|
43
|
+
* applies the status's side-effects — the five things the assignment this
|
|
44
|
+
* replaced did none of, which is why a payment-driven cancellation used to
|
|
45
|
+
* leave the order's stock allocated forever and audited nowhere.
|
|
46
|
+
*
|
|
47
|
+
* **Called after this handler's transaction has committed**, never inside
|
|
48
|
+
* it: the port takes its own `EntityManager`, so a call from inside would
|
|
49
|
+
* write the order on a different pooled connection whose commit the
|
|
50
|
+
* enclosing rollback cannot reach (issue #200). The port's own doc block
|
|
51
|
+
* states the obligation.
|
|
52
|
+
*
|
|
53
|
+
* It announces the change too — `OrderTransitionService` emits the
|
|
54
|
+
* templated `order.status.*.after` events itself — so this handler no
|
|
55
|
+
* longer resolves `orderStatusAnnouncePort`. Doing both would emit every
|
|
56
|
+
* payment-driven status change twice.
|
|
57
|
+
*/
|
|
58
|
+
orderTransition,
|
|
59
|
+
/**
|
|
60
|
+
* The money axis of the order this payment settles (feature 080, T048).
|
|
61
|
+
*
|
|
62
|
+
* **Called inside this handler's transaction**, unlike `orderTransition`
|
|
63
|
+
* above, and the asymmetry is the whole reason there are two ports rather
|
|
64
|
+
* than one: `payments_order_fk` (`on delete restrict`) holds the payment row
|
|
65
|
+
* and `orders.payment_status` together, so that write may not commit
|
|
66
|
+
* separately from this one — while a *lifecycle* transition obtains its own
|
|
67
|
+
* `EntityManager` and may not run inside it. The interface takes the
|
|
68
|
+
* caller's `EntityManager` as a required parameter for exactly that reason
|
|
69
|
+
* (D-169), and the header above states the constraint in full.
|
|
70
|
+
*/
|
|
71
|
+
orderPaymentStatus,
|
|
72
|
+
/** Where a refused transition is recorded; see {@link SettlementLogger}. */
|
|
73
|
+
log, events) {
|
|
74
|
+
this.emFactory = emFactory;
|
|
75
|
+
this.paymentMethodRead = paymentMethodRead;
|
|
76
|
+
this.orderTransition = orderTransition;
|
|
77
|
+
this.orderPaymentStatus = orderPaymentStatus;
|
|
78
|
+
this.log = log;
|
|
79
|
+
this.events = events;
|
|
80
|
+
}
|
|
81
|
+
async receive(input) {
|
|
82
|
+
// command-coverage-ignore: provider payment-event ingestion — stamps the
|
|
83
|
+
// attempt result on the Payment row and the order's payment status, then
|
|
84
|
+
// asks `orderTransitionPort` for the lifecycle move, which records the
|
|
85
|
+
// `order.status_transition` audit entry co-transactionally with the status
|
|
86
|
+
// write it performs. The reason on this marker used to claim that audit
|
|
87
|
+
// while the handler assigned `order.status` itself and reached the orders
|
|
88
|
+
// flow at no point (feature 085, R3); Phase D made the claim true by
|
|
89
|
+
// routing the move through the seam that carries it.
|
|
90
|
+
const em = this.emFactory();
|
|
91
|
+
const result = await em.transactional(async (tx) => {
|
|
92
|
+
const payment = await this.resolvePayment(tx, input);
|
|
93
|
+
if (!payment) {
|
|
94
|
+
throw new HttpError(404, ERROR_CODES.NOT_FOUND, 'Payment not found for the given reference.');
|
|
95
|
+
}
|
|
96
|
+
// Idempotency / terminal-state guards (FR-025).
|
|
97
|
+
if (payment.status === 'paid') {
|
|
98
|
+
if (input.outcome === 'failure') {
|
|
99
|
+
throw new HttpError(409, ERROR_CODES.VALIDATION_FAILED, 'Payment is already settled; it cannot be marked failed.');
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
payment,
|
|
103
|
+
orderStatus: null,
|
|
104
|
+
idempotent: true,
|
|
105
|
+
emit: false,
|
|
106
|
+
transition: null,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const method = await this.paymentMethodRead.findById(payment.paymentMethodId);
|
|
110
|
+
let transition = null;
|
|
111
|
+
// The order this settlement moves, written through its owner's
|
|
112
|
+
// `EntityManager`-taking port on THIS transaction (see the header). It
|
|
113
|
+
// answers `null` for an order that is not there — the same state the
|
|
114
|
+
// `if (order)` guards this replaces described — and a published record
|
|
115
|
+
// otherwise, so the lifecycle status below is read rather than reachable.
|
|
116
|
+
let applied = null;
|
|
117
|
+
if (input.outcome === 'success') {
|
|
118
|
+
payment.status = 'paid';
|
|
119
|
+
payment.paidAt = new Date();
|
|
120
|
+
if (input.externalReference !== undefined) {
|
|
121
|
+
payment.externalReference = input.externalReference ?? null;
|
|
122
|
+
}
|
|
123
|
+
if (input.providerDetails)
|
|
124
|
+
payment.providerDetails = input.providerDetails;
|
|
125
|
+
applied = await this.orderPaymentStatus.applyPaymentStatus(tx, {
|
|
126
|
+
orderId: payment.orderId,
|
|
127
|
+
paymentStatus: 'paid',
|
|
128
|
+
});
|
|
129
|
+
if (method?.statusOnSuccess) {
|
|
130
|
+
transition = { to: method.statusOnSuccess, setting: 'status_on_success' };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
payment.status = 'failed';
|
|
135
|
+
payment.failureReason = input.failureReason ?? null;
|
|
136
|
+
if (input.providerDetails)
|
|
137
|
+
payment.providerDetails = input.providerDetails;
|
|
138
|
+
// Feature 085 (FR-001) — the decline is recorded on the money axis
|
|
139
|
+
// too. The lifecycle status alone could not say it: the method's
|
|
140
|
+
// `status_on_failure` is operator-configurable, so two orders sitting
|
|
141
|
+
// at the same status may have arrived there for opposite reasons, and
|
|
142
|
+
// the buyer's retry and the operator's list both need to know which.
|
|
143
|
+
applied = await this.orderPaymentStatus.applyPaymentStatus(tx, {
|
|
144
|
+
orderId: payment.orderId,
|
|
145
|
+
paymentStatus: 'failed',
|
|
146
|
+
});
|
|
147
|
+
if (method?.statusOnFailure) {
|
|
148
|
+
transition = { to: method.statusOnFailure, setting: 'status_on_failure' };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
await tx.flush();
|
|
152
|
+
return {
|
|
153
|
+
payment,
|
|
154
|
+
orderStatus: applied?.status ?? null,
|
|
155
|
+
idempotent: false,
|
|
156
|
+
emit: true,
|
|
157
|
+
adapter: method?.adapter ?? payment.paymentMethodId,
|
|
158
|
+
transition,
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
/**
|
|
162
|
+
* After the commit, never inside the transaction above (feature 085 R4).
|
|
163
|
+
*
|
|
164
|
+
* `OrderTransitionService` obtains its own `EntityManager`, so a call from
|
|
165
|
+
* inside `em.transactional` would write the order on a second pooled
|
|
166
|
+
* connection that commits on its own — the shape `check:transaction-context`
|
|
167
|
+
* refuses. The asymmetry of putting it here is deliberate and lands on the
|
|
168
|
+
* safe side: a crash between the commit and this line leaves the payment
|
|
169
|
+
* recorded and the lifecycle unmoved, which the buyer retries, where the
|
|
170
|
+
* opposite ordering would hold an order against a payment nobody recorded.
|
|
171
|
+
*/
|
|
172
|
+
const orderStatus = result.transition
|
|
173
|
+
? await this.moveOrder(result.payment.orderId, result.transition.to, result.transition.setting, result.orderStatus, input.outcome === 'failure' ? (input.failureReason ?? null) : null)
|
|
174
|
+
: result.orderStatus;
|
|
175
|
+
if (result.emit && this.events) {
|
|
176
|
+
const base = { eventId: randomUUID(), occurredAt: new Date().toISOString() };
|
|
177
|
+
if (result.payment.status === 'paid') {
|
|
178
|
+
this.events.emit('payment.received.v1', {
|
|
179
|
+
...base,
|
|
180
|
+
orderId: result.payment.orderId,
|
|
181
|
+
paymentId: result.payment.id,
|
|
182
|
+
adapter: result.adapter ?? '',
|
|
183
|
+
externalReference: result.payment.externalReference ?? null,
|
|
184
|
+
attemptNo: result.payment.attemptNo,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
this.events.emit('payment.failed.v1', {
|
|
189
|
+
...base,
|
|
190
|
+
orderId: result.payment.orderId,
|
|
191
|
+
paymentId: result.payment.id,
|
|
192
|
+
adapter: result.adapter ?? '',
|
|
193
|
+
failureReason: result.payment.failureReason ?? null,
|
|
194
|
+
attemptNo: result.payment.attemptNo,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// The templated `order.status.*.after` events (feature 038, T026) are no
|
|
198
|
+
// longer emitted from here: `OrderTransitionService` emits them itself,
|
|
199
|
+
// for the transition it applied, with the same actor and the order's
|
|
200
|
+
// business id besides. Announcing again would double every subscriber's
|
|
201
|
+
// reaction to a payment-driven status change.
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
paymentId: result.payment.id,
|
|
205
|
+
status: result.payment.status,
|
|
206
|
+
orderStatus,
|
|
207
|
+
idempotent: result.idempotent,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Ask the lifecycle for the move the configured setting names, and answer
|
|
212
|
+
* where the order ended up.
|
|
213
|
+
*
|
|
214
|
+
* Every refusal is a 200 for the provider (contract §"What the caller does
|
|
215
|
+
* with each outcome"): a PSP retries a non-2xx callback indefinitely, and an
|
|
216
|
+
* order an operator has already advanced past payment must not be dragged
|
|
217
|
+
* backwards because a delayed webhook arrived. So the refusal is recorded
|
|
218
|
+
* here and the settlement stands.
|
|
219
|
+
*
|
|
220
|
+
* There is deliberately no `catch`. A `ModuleDisabledError` out of the port's
|
|
221
|
+
* gate is not a refusal — it is the owner being absent — and swallowing it
|
|
222
|
+
* would turn fail-closed into fail-open.
|
|
223
|
+
*/
|
|
224
|
+
async moveOrder(orderId, to, setting, statusBefore, reason) {
|
|
225
|
+
const outcome = await this.orderTransition.applyStatus({
|
|
226
|
+
orderId,
|
|
227
|
+
to,
|
|
228
|
+
actor: { kind: 'system', source: 'payment' },
|
|
229
|
+
reason,
|
|
230
|
+
});
|
|
231
|
+
if (outcome.applied)
|
|
232
|
+
return outcome.to;
|
|
233
|
+
// A second declined attempt on an order already at the failure status: the
|
|
234
|
+
// attempt is recorded, nothing moved, and nothing needs saying (FR-006).
|
|
235
|
+
if (outcome.reason === 'already_there')
|
|
236
|
+
return outcome.from;
|
|
237
|
+
this.log.warn({
|
|
238
|
+
orderId,
|
|
239
|
+
from: outcome.from,
|
|
240
|
+
to,
|
|
241
|
+
setting,
|
|
242
|
+
refusal: outcome.reason,
|
|
243
|
+
detail: outcome.detail,
|
|
244
|
+
}, 'payments: the requested order status was not applied; the order keeps the one it has');
|
|
245
|
+
return outcome.from ?? statusBefore;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Reflect a gateway refund onto the Payment + Order (feature 049). Driven by
|
|
249
|
+
* the `charge.refunded` webhook for BOTH Dashboard- and platform-initiated
|
|
250
|
+
* refunds. `refundedAmount` is the gateway's cumulative total, so this is
|
|
251
|
+
* idempotent by construction (re-applying the same total is a no-op) and never
|
|
252
|
+
* calls the gateway back (no refund loop). A fully-refunded payment is never
|
|
253
|
+
* downgraded to partial.
|
|
254
|
+
*/
|
|
255
|
+
async reflectRefund(input) {
|
|
256
|
+
const em = this.emFactory();
|
|
257
|
+
const result = await em.transactional(async (tx) => {
|
|
258
|
+
const payment = await this.resolvePaymentForRefund(tx, input);
|
|
259
|
+
if (!payment)
|
|
260
|
+
return null;
|
|
261
|
+
const newRefunded = input.refundedAmount.toFixed(2);
|
|
262
|
+
const nextStatus = input.fullyRefunded
|
|
263
|
+
? 'refunded'
|
|
264
|
+
: input.refundedAmount > 0 && payment.status !== 'refunded'
|
|
265
|
+
? 'partially_refunded'
|
|
266
|
+
: payment.status;
|
|
267
|
+
const changed = payment.refundedAmount !== newRefunded || payment.status !== nextStatus;
|
|
268
|
+
payment.refundedAmount = newRefunded;
|
|
269
|
+
payment.status = nextStatus;
|
|
270
|
+
payment.providerDetails = {
|
|
271
|
+
...(payment.providerDetails ?? {}),
|
|
272
|
+
...(input.providerDetails ?? {}),
|
|
273
|
+
// Stamp the refund time only when the total actually moved, so a
|
|
274
|
+
// re-delivered webhook does not bump the recorded refund timestamp.
|
|
275
|
+
...(changed ? { refundedAt: new Date().toISOString() } : {}),
|
|
276
|
+
...(input.externalRefundId ? { refundReference: input.externalRefundId } : {}),
|
|
277
|
+
};
|
|
278
|
+
// Only a full refund flips the order's payment status; a partial refund is
|
|
279
|
+
// tracked on the payment while the order stays 'paid'. The port is
|
|
280
|
+
// therefore asked only on a full refund — which is also the only branch
|
|
281
|
+
// that ever needed the order: `Boolean(order) && input.fullyRefunded` was
|
|
282
|
+
// false for every partial one whether the order existed or not, so the
|
|
283
|
+
// read this replaces was doing no work on that path.
|
|
284
|
+
const refunded = input.fullyRefunded
|
|
285
|
+
? await this.orderPaymentStatus.applyPaymentStatus(tx, {
|
|
286
|
+
orderId: payment.orderId,
|
|
287
|
+
paymentStatus: 'refunded',
|
|
288
|
+
})
|
|
289
|
+
: null;
|
|
290
|
+
const holdsTheOrder = refunded !== null;
|
|
291
|
+
await tx.flush();
|
|
292
|
+
return { payment, changed, holdsTheOrder };
|
|
293
|
+
});
|
|
294
|
+
if (!result)
|
|
295
|
+
return null;
|
|
296
|
+
/**
|
|
297
|
+
* The hold goes through the lifecycle too, after the commit, for the same
|
|
298
|
+
* reason `receive()` does (feature 085 Phase D). This was the third of the
|
|
299
|
+
* three sites that assigned `order.status` directly, and it is the one an
|
|
300
|
+
* operator is most likely to meet: a fully refunded order held for review
|
|
301
|
+
* whose stock nothing had released.
|
|
302
|
+
*
|
|
303
|
+
* Asked on every full refund rather than only on a *changed* one, as the
|
|
304
|
+
* assignment it replaces was: a re-delivered webhook finds the order
|
|
305
|
+
* already on hold and the port answers `already_there`, which writes no
|
|
306
|
+
* second audit entry and emits no second event.
|
|
307
|
+
*/
|
|
308
|
+
if (result.holdsTheOrder) {
|
|
309
|
+
await this.moveOrder(result.payment.orderId, ORDER_STATUS_ON_HOLD, 'status_on_failure', null, 'Fully refunded; held for operator review.');
|
|
310
|
+
}
|
|
311
|
+
if (result.changed && this.events) {
|
|
312
|
+
this.events.emit('payment.refunded.v1', {
|
|
313
|
+
eventId: randomUUID(),
|
|
314
|
+
occurredAt: new Date().toISOString(),
|
|
315
|
+
orderId: result.payment.orderId,
|
|
316
|
+
paymentId: result.payment.id,
|
|
317
|
+
refundedAmount: Number(result.payment.refundedAmount),
|
|
318
|
+
currency: input.currency,
|
|
319
|
+
fullyRefunded: input.fullyRefunded,
|
|
320
|
+
externalRefundId: input.externalRefundId ?? null,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return { paymentId: result.payment.id, changed: result.changed };
|
|
324
|
+
}
|
|
325
|
+
async resolvePaymentForRefund(tx, input) {
|
|
326
|
+
if (input.paymentId)
|
|
327
|
+
return tx.findOne(Payment, { id: input.paymentId });
|
|
328
|
+
if (input.externalReference) {
|
|
329
|
+
const byRef = await tx.findOne(Payment, { externalReference: input.externalReference });
|
|
330
|
+
if (byRef)
|
|
331
|
+
return byRef;
|
|
332
|
+
}
|
|
333
|
+
if (input.orderId) {
|
|
334
|
+
// The settled payment for the order (most recent attempt).
|
|
335
|
+
return tx.findOne(Payment, { orderId: input.orderId, status: { $in: ['paid', 'partially_refunded', 'refunded'] } }, { orderBy: { attemptNo: 'desc' } });
|
|
336
|
+
}
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
async resolvePayment(tx, input) {
|
|
340
|
+
if (input.paymentId) {
|
|
341
|
+
return tx.findOne(Payment, { id: input.paymentId });
|
|
342
|
+
}
|
|
343
|
+
if (input.orderId && input.externalReference) {
|
|
344
|
+
const byRef = await tx.findOne(Payment, {
|
|
345
|
+
orderId: input.orderId,
|
|
346
|
+
externalReference: input.externalReference,
|
|
347
|
+
});
|
|
348
|
+
if (byRef)
|
|
349
|
+
return byRef;
|
|
350
|
+
// Fall back to the most recent open attempt for the order.
|
|
351
|
+
return tx.findOne(Payment, { orderId: input.orderId }, { orderBy: { attemptNo: 'desc' } });
|
|
352
|
+
}
|
|
353
|
+
return null;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=receive-payment-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receive-payment-handler.js","sourceRoot":"","sources":["../../../src/backend/services/receive-payment-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { GatewayRefundRegistry } from './gateway-refund-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* Process-wide GatewayRefundRegistry (feature 049).
|
|
4
|
+
*
|
|
5
|
+
* The four gateway modules import this instance directly and push their refund
|
|
6
|
+
* handlers into it from their `ctx.onBoot` hook, so the platform has exactly
|
|
7
|
+
* one table of handlers however many times it is composed. A module contributes
|
|
8
|
+
* only the handler it owns, and names itself when it does.
|
|
9
|
+
*
|
|
10
|
+
* The presence probe is wired here rather than in the class, for the reason its
|
|
11
|
+
* twin in `payment_methods` gives: this is the one instance that participates
|
|
12
|
+
* in the platform's lifecycle, and a registry a test builds for itself should
|
|
13
|
+
* keep answering about the handlers that test registered.
|
|
14
|
+
*/
|
|
15
|
+
export declare const gatewayRefundRegistry: GatewayRefundRegistry;
|
|
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,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,uBAEjC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { effectiveState } from '@endora-commerce/platform/kernel';
|
|
2
|
+
import { GatewayRefundRegistry } from './gateway-refund-registry.js';
|
|
3
|
+
/**
|
|
4
|
+
* Process-wide GatewayRefundRegistry (feature 049).
|
|
5
|
+
*
|
|
6
|
+
* The four gateway modules import this instance directly and push their refund
|
|
7
|
+
* handlers into it from their `ctx.onBoot` hook, so the platform has exactly
|
|
8
|
+
* one table of handlers however many times it is composed. A module contributes
|
|
9
|
+
* only the handler it owns, and names itself when it does.
|
|
10
|
+
*
|
|
11
|
+
* The presence probe is wired here rather than in the class, for the reason its
|
|
12
|
+
* twin in `payment_methods` gives: this is the one instance that participates
|
|
13
|
+
* in the platform's lifecycle, and a registry a test builds for itself should
|
|
14
|
+
* keep answering about the handlers that test registered.
|
|
15
|
+
*/
|
|
16
|
+
export const gatewayRefundRegistry = new GatewayRefundRegistry(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,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAErE;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,qBAAqB,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE,CACrF,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CACnC,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The error codes this module declares (D-182,
|
|
3
|
+
* `specs/090-module-owned-error-codes/`).
|
|
4
|
+
*
|
|
5
|
+
* Branded, so a typo at a raise site is a compile error rather than a code that
|
|
6
|
+
* travels the whole path and renders raw to whoever reads it. They live in this
|
|
7
|
+
* file because it is the one the package publishes at its root subpath: a
|
|
8
|
+
* sibling module under `src/` is reachable through no subpath at all, so the
|
|
9
|
+
* raise sites could name it and a consumer branching on the code could not.
|
|
10
|
+
*
|
|
11
|
+
* Declaring them in `errorCodes` below is a separate act and is what routes the
|
|
12
|
+
* sentence to this package's own `i18n/` bundle; the reasoning for the three is
|
|
13
|
+
* written there.
|
|
14
|
+
*/
|
|
15
|
+
export declare const paymentsErrorCodes: {
|
|
16
|
+
readonly PAYMENT_NOT_DUE: import("@endora-commerce/contracts").ModuleErrorCode;
|
|
17
|
+
readonly PAYMENT_ORDER_CLOSED: import("@endora-commerce/contracts").ModuleErrorCode;
|
|
18
|
+
readonly PAYMENT_ADAPTER_UNAVAILABLE: import("@endora-commerce/contracts").ModuleErrorCode;
|
|
19
|
+
};
|
|
20
|
+
export declare const paymentsSettingsManifest: {
|
|
21
|
+
moduleCode: string;
|
|
22
|
+
groups: {
|
|
23
|
+
code: string;
|
|
24
|
+
name: string;
|
|
25
|
+
salesChannelCodes?: string[] | undefined;
|
|
26
|
+
isSystemProtected?: boolean | undefined;
|
|
27
|
+
}[];
|
|
28
|
+
settings: {
|
|
29
|
+
code: string;
|
|
30
|
+
name: string;
|
|
31
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
32
|
+
defaultValue: unknown;
|
|
33
|
+
description?: string | undefined;
|
|
34
|
+
groupCode?: string | undefined;
|
|
35
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
36
|
+
salesChannelCodes?: string[] | undefined;
|
|
37
|
+
enumOptions?: string[] | undefined;
|
|
38
|
+
configurationType?: string | undefined;
|
|
39
|
+
hidden?: boolean | undefined;
|
|
40
|
+
}[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Payments module — manifest backfill (Module Lifecycle, feature 018).
|
|
44
|
+
*
|
|
45
|
+
* Predates the lifecycle system; this manifest is the static record
|
|
46
|
+
* required so the module participates in the registry. No install /
|
|
47
|
+
* uninstall hook today — the module's schema is owned by earlier
|
|
48
|
+
* platform-wide migrations.
|
|
49
|
+
*/
|
|
50
|
+
export declare const manifest: {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
version: string;
|
|
54
|
+
dependencies: string[];
|
|
55
|
+
description?: string | undefined;
|
|
56
|
+
acknowledgedDependencies?: {
|
|
57
|
+
moduleId: string;
|
|
58
|
+
port: string;
|
|
59
|
+
reason: string;
|
|
60
|
+
}[] | undefined;
|
|
61
|
+
nonBindingDependencies?: {
|
|
62
|
+
moduleId: string;
|
|
63
|
+
name: string;
|
|
64
|
+
kind: "contributes-to" | "degrades-without" | "refuses-without";
|
|
65
|
+
reason: string;
|
|
66
|
+
whenAbsent?: string | undefined;
|
|
67
|
+
}[] | undefined;
|
|
68
|
+
activation?: {
|
|
69
|
+
settingCode: string;
|
|
70
|
+
default: boolean;
|
|
71
|
+
} | {
|
|
72
|
+
nonDeactivatable: true;
|
|
73
|
+
reason: string;
|
|
74
|
+
} | undefined;
|
|
75
|
+
settings?: {
|
|
76
|
+
moduleCode: string;
|
|
77
|
+
groups: {
|
|
78
|
+
code: string;
|
|
79
|
+
name: string;
|
|
80
|
+
salesChannelCodes?: string[] | undefined;
|
|
81
|
+
isSystemProtected?: boolean | undefined;
|
|
82
|
+
}[];
|
|
83
|
+
settings: {
|
|
84
|
+
code: string;
|
|
85
|
+
name: string;
|
|
86
|
+
valueType: "string" | "number" | "boolean" | "json" | "string_list" | "secret" | "credential_ref";
|
|
87
|
+
defaultValue: unknown;
|
|
88
|
+
description?: string | undefined;
|
|
89
|
+
groupCode?: string | undefined;
|
|
90
|
+
previousDefaultValues?: unknown[] | undefined;
|
|
91
|
+
salesChannelCodes?: string[] | undefined;
|
|
92
|
+
enumOptions?: string[] | undefined;
|
|
93
|
+
configurationType?: string | undefined;
|
|
94
|
+
hidden?: boolean | undefined;
|
|
95
|
+
}[];
|
|
96
|
+
} | undefined;
|
|
97
|
+
i18n?: {
|
|
98
|
+
bundlesDir: string;
|
|
99
|
+
} | undefined;
|
|
100
|
+
docs?: false | {
|
|
101
|
+
dir: string;
|
|
102
|
+
} | undefined;
|
|
103
|
+
demo?: false | {
|
|
104
|
+
summary: string;
|
|
105
|
+
seed: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoSeedResult>;
|
|
106
|
+
reset: (context: import("@endora-commerce/contracts").ModuleDemoContext<never>) => Promise<import("@endora-commerce/contracts").DemoResetResult>;
|
|
107
|
+
after?: readonly string[] | undefined;
|
|
108
|
+
package?: string | undefined;
|
|
109
|
+
} | undefined;
|
|
110
|
+
actions?: {
|
|
111
|
+
id: string;
|
|
112
|
+
labelKey: string;
|
|
113
|
+
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";
|
|
114
|
+
targetRoute: string;
|
|
115
|
+
keywords: string[];
|
|
116
|
+
weight: number;
|
|
117
|
+
descriptionKey?: string | undefined;
|
|
118
|
+
requiredPermission?: string | undefined;
|
|
119
|
+
}[] | undefined;
|
|
120
|
+
permissions?: {
|
|
121
|
+
code: string;
|
|
122
|
+
label: string;
|
|
123
|
+
module?: string | undefined;
|
|
124
|
+
description?: string | undefined;
|
|
125
|
+
requires?: string[] | undefined;
|
|
126
|
+
}[] | undefined;
|
|
127
|
+
transactionalEmails?: {
|
|
128
|
+
code: string;
|
|
129
|
+
name: string;
|
|
130
|
+
variables: {
|
|
131
|
+
key: string;
|
|
132
|
+
label: string;
|
|
133
|
+
sampleValue?: string | undefined;
|
|
134
|
+
description?: string | undefined;
|
|
135
|
+
}[];
|
|
136
|
+
description?: string | undefined;
|
|
137
|
+
group?: string | undefined;
|
|
138
|
+
}[] | undefined;
|
|
139
|
+
capabilities?: string[] | undefined;
|
|
140
|
+
exclusiveCapabilities?: {
|
|
141
|
+
key: string;
|
|
142
|
+
errorCode: string;
|
|
143
|
+
}[] | undefined;
|
|
144
|
+
errorCodes?: {
|
|
145
|
+
code: string;
|
|
146
|
+
tokens?: string[] | undefined;
|
|
147
|
+
}[] | undefined;
|
|
148
|
+
blocks?: {
|
|
149
|
+
name: string;
|
|
150
|
+
labelKey: string;
|
|
151
|
+
category: string;
|
|
152
|
+
contexts: ("email" | "invoice" | "cms" | "newsletter")[];
|
|
153
|
+
fields: Record<string, {
|
|
154
|
+
type: "number" | "object" | "array" | "text" | "textarea" | "select" | "radio" | "external" | "uuid" | "richtext";
|
|
155
|
+
label?: string | undefined;
|
|
156
|
+
required?: boolean | undefined;
|
|
157
|
+
options?: {
|
|
158
|
+
label: string;
|
|
159
|
+
value: string | number;
|
|
160
|
+
}[] | undefined;
|
|
161
|
+
refKind?: string | undefined;
|
|
162
|
+
}>;
|
|
163
|
+
descriptionKey?: string | undefined;
|
|
164
|
+
defaultProps?: Record<string, unknown> | undefined;
|
|
165
|
+
responsiveFields?: string[] | undefined;
|
|
166
|
+
previewIcon?: string | undefined;
|
|
167
|
+
weight?: number | undefined;
|
|
168
|
+
}[] | undefined;
|
|
169
|
+
blockCategories?: {
|
|
170
|
+
key: string;
|
|
171
|
+
titleKey: string;
|
|
172
|
+
contexts: ("email" | "invoice" | "cms" | "newsletter")[];
|
|
173
|
+
weight?: number | undefined;
|
|
174
|
+
visible?: boolean | undefined;
|
|
175
|
+
}[] | undefined;
|
|
176
|
+
env?: {
|
|
177
|
+
name: string;
|
|
178
|
+
describes: {
|
|
179
|
+
en: string;
|
|
180
|
+
pl: string;
|
|
181
|
+
};
|
|
182
|
+
requirement: {
|
|
183
|
+
kind: "required";
|
|
184
|
+
} | {
|
|
185
|
+
kind: "requiredWhen";
|
|
186
|
+
input: string;
|
|
187
|
+
equals: string;
|
|
188
|
+
} | {
|
|
189
|
+
kind: "optional";
|
|
190
|
+
without: {
|
|
191
|
+
en: string;
|
|
192
|
+
pl: string;
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
secret: boolean;
|
|
196
|
+
generable: boolean;
|
|
197
|
+
owner: {
|
|
198
|
+
kind: "platform";
|
|
199
|
+
} | {
|
|
200
|
+
kind: "application";
|
|
201
|
+
application: "admin" | "backend" | "storefront";
|
|
202
|
+
} | {
|
|
203
|
+
kind: "module";
|
|
204
|
+
moduleId: string;
|
|
205
|
+
};
|
|
206
|
+
consumers: ("admin" | "backend" | "storefront")[];
|
|
207
|
+
addressOf: "admin" | "backend" | "storefront" | null;
|
|
208
|
+
}[] | undefined;
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,kBAAkB;;;;CAI7B,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;CAmBnC,CAAC;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsInB,CAAC"}
|