mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 08:48:11 +00:00
fix(payments): reconcile with gateway before expiring unpaid holds
Replace the fixed 5-minute grace with a settlement check at expiry: expire() calls the payment API's reconcile endpoint — paid intents are kept and allocated via payment.succeeded, unverifiable results defer expiry to the next tick, only verifiably unpaid holds expire.
This commit is contained in:
@@ -31,6 +31,24 @@ export class PaymentClientService {
|
||||
return this.call("POST", "/payments/initiate", request);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST /payments/reconcile — settlement check for a domain order
|
||||
* (reconcile-before-cancel). Live-queries every non-failed intent at the
|
||||
* provider and registers any late capture found (flips it to SUCCEEDED and
|
||||
* emits payment.succeeded). `unverifiable: true` = could not confirm
|
||||
* "not paid" — the caller must NOT cancel/expire the order.
|
||||
*/
|
||||
async reconcileReference(
|
||||
referenceType: PaymentReferenceType,
|
||||
referenceId: string,
|
||||
): Promise<{ paid: boolean; unverifiable: boolean }> {
|
||||
return this.call("POST", "/payments/reconcile", {
|
||||
service: PaymentService.FREIGHT,
|
||||
referenceType,
|
||||
referenceId,
|
||||
});
|
||||
}
|
||||
|
||||
/** GET /payments/intents?… — active intent by domain reference; null when none exists. */
|
||||
async getIntentByReference(
|
||||
referenceType: PaymentReferenceType,
|
||||
|
||||
@@ -188,6 +188,30 @@ export class PaymentService {
|
||||
* marked paid WITHOUT emitting — the caller (billing) settles inline after it
|
||||
* has stored the intent id, avoiding a settle-before-correlation race.
|
||||
*/
|
||||
/**
|
||||
* Reconcile-before-cancel: ask the payment service whether ANY intent for
|
||||
* this shipment actually settled at the provider (bank/gateway). A late
|
||||
* capture found there is registered as SUCCEEDED and emits payment.succeeded,
|
||||
* which drives the normal paid flow. A network/provider error reports
|
||||
* `unverifiable` — the caller must not expire the order on unknown.
|
||||
*/
|
||||
async reconcileShipment(
|
||||
referenceId: string,
|
||||
): Promise<{ paid: boolean; unverifiable: boolean }> {
|
||||
try {
|
||||
const result = await this.paymentClient.reconcileReference(
|
||||
PaymentReferenceType.SHIPMENT,
|
||||
referenceId,
|
||||
);
|
||||
return { paid: result.paid, unverifiable: result.unverifiable };
|
||||
} catch (err) {
|
||||
this.logger.warn(
|
||||
`Reconcile for shipment ${referenceId} failed: ${(err as Error).message}`,
|
||||
);
|
||||
return { paid: false, unverifiable: true };
|
||||
}
|
||||
}
|
||||
|
||||
async initiate(input: InitiateIntentInput): Promise<InitiateIntentResult> {
|
||||
try {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user