mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 03:10:54 +00:00
feat(billing): add PAYMENT_PROCESSING invoice status on payment success redirect (all except CBE bill)
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Post,
|
||||
Query,
|
||||
Res,
|
||||
} from "@nestjs/common";
|
||||
@@ -84,6 +85,15 @@ export class PaymentController {
|
||||
return this.paymentService.getIntentByBookingId(bookingId);
|
||||
}
|
||||
|
||||
@Post("redirect-success/:bookingId")
|
||||
@ApiOperation({
|
||||
summary:
|
||||
"Success-redirect ack: mark payment processing + invoice PAYMENT_PROCESSING (webhook remains source of truth)",
|
||||
})
|
||||
acknowledgeSuccessRedirect(@Param("bookingId") bookingId: string) {
|
||||
return this.paymentService.acknowledgeSuccessRedirect(bookingId);
|
||||
}
|
||||
|
||||
@Get("receipt/:orderId")
|
||||
@Public()
|
||||
@ApiOperation({ summary: "Generate a payment receipt HTML page" })
|
||||
|
||||
@@ -253,8 +253,10 @@ export class PaymentService {
|
||||
payerAccount: input.payerAccount,
|
||||
payerName: input.payerName,
|
||||
expiresAt: input.expiresAt,
|
||||
// bookingId lets the success page ack the redirect (→ PAYMENT_PROCESSING).
|
||||
returnUrl:
|
||||
input.returnUrl ?? "https://edrfreight.triaplc.com/payment/success",
|
||||
input.returnUrl ??
|
||||
`https://edrfreight.triaplc.com/payment/success?bookingId=${encodeURIComponent(input.referenceId)}`,
|
||||
failureUrl:
|
||||
input.failureUrl ?? "https://edrfreight.triaplc.com/payment/failure",
|
||||
});
|
||||
@@ -492,6 +494,37 @@ export class PaymentService {
|
||||
return { alreadyFinalized: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Success-redirect ack from the portal: the customer finished provider
|
||||
* checkout, settlement webhook not (necessarily) in yet. Optimistic
|
||||
* intermediate only — the webhook stays the source of truth. Never
|
||||
* downgrades: only action-required → processing, and the invoice moves to
|
||||
* PAYMENT_PROCESSING only from an open unpaid status. CBE_BILL is excluded
|
||||
* (bank-counter flow, it has no redirect).
|
||||
*/
|
||||
async acknowledgeSuccessRedirect(
|
||||
referenceId: string,
|
||||
): Promise<{ acknowledged: boolean }> {
|
||||
const intent = await this.paymentRepo.findOneBy({ refId: referenceId });
|
||||
if (!intent || intent.method === "cbe-bill") {
|
||||
return { acknowledged: false };
|
||||
}
|
||||
|
||||
if (intent.status === "action-required") {
|
||||
await this.paymentRepo.update(
|
||||
{ id: intent.id, status: "action-required" },
|
||||
{ status: "processing" },
|
||||
);
|
||||
}
|
||||
// Even if the intent already advanced (e.g. webhook raced the redirect to
|
||||
// "processing"), the invoice ack is idempotent and status-guarded.
|
||||
if (intent.status === "action-required" || intent.status === "processing") {
|
||||
await this.billing.markInvoicePaymentProcessing(intent.id);
|
||||
return { acknowledged: true };
|
||||
}
|
||||
return { acknowledged: false };
|
||||
}
|
||||
|
||||
async markPaymentFailed(input: {
|
||||
intentId: string;
|
||||
failureCode?: string;
|
||||
@@ -510,7 +543,9 @@ export class PaymentService {
|
||||
},
|
||||
);
|
||||
|
||||
// Invoice stays open for retry — nothing to settle. Logged only.
|
||||
// Invoice stays open for retry — nothing to settle. A redirect-acked
|
||||
// PAYMENT_PROCESSING invoice is put back to PENDING so it reads payable.
|
||||
await this.billing.revertInvoicePaymentProcessing(intent.id);
|
||||
this.logger.warn(
|
||||
`Payment ${intent.id} failed for ${intent.refId}` +
|
||||
(input.failureMessage ? `: ${input.failureMessage}` : ""),
|
||||
|
||||
Reference in New Issue
Block a user