Merge remote-tracking branch 'origin/staging' into freight/fix/pay

# Conflicts:
#	apps/edr-freight-api/src/modules/billing/billing.service.ts
#	apps/edr-freight-web/portal/src/pages/billing/InvoiceDetailPage.tsx
#	apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/PaymentMethodModal.tsx
#	pnpm-lock.yaml
This commit is contained in:
ghost2023
2026-08-01 13:09:14 +03:00
57 changed files with 2365 additions and 108 deletions

View File

@@ -34,9 +34,17 @@ function apiMessage(err: unknown, fallback: string): string {
* charge through a different endpoint (warehouse fee invoices); OTP
* confirmation always goes through billing, which owns the intent either way.
*/
/** CBE bill payment: no redirect — the payer takes this reference to any CBE channel. */
interface BillAction {
billReference: string;
instructions?: string;
expiresAt?: string;
}
export function useInvoicePayment(initiate: InitiateFn = payViaBilling) {
const [otpInvoiceId, setOtpInvoiceId] = useState<string | null>(null);
const [otpMessage, setOtpMessage] = useState<string | undefined>();
const [billAction, setBillAction] = useState<BillAction | null>(null);
const payMutation = useMutation({
mutationFn: (vars: {
@@ -52,6 +60,16 @@ export function useInvoicePayment(initiate: InitiateFn = payViaBilling) {
setOtpInvoiceId(vars.invoiceId);
return;
}
// CBE_BILL settles asynchronously via CBE, not the browser — show the
// bill reference instead of redirecting to a (nonexistent) checkout page.
if (data?.clientAction?.type === "SHOW_BILL_REFERENCE") {
setBillAction({
billReference: data.clientAction.billReference ?? "",
instructions: data.clientAction.instructions,
expiresAt: data.clientAction.expiresAt,
});
return;
}
window.location.href =
data?.clientAction?.type === "REDIRECT" && data.clientAction.url
? data.clientAction.url
@@ -76,6 +94,7 @@ export function useInvoicePayment(initiate: InitiateFn = payViaBilling) {
payMutation.reset();
otpMutation.reset();
setOtpInvoiceId(null);
setBillAction(null);
};
return {
@@ -109,6 +128,14 @@ export function useInvoicePayment(initiate: InitiateFn = payViaBilling) {
setOtpInvoiceId(null);
},
},
/** Drives the modal's "pay at CBE" step; `open` only for CBE_BILL. */
bill: {
open: billAction !== null,
billReference: billAction?.billReference,
instructions: billAction?.instructions,
expiresAt: billAction?.expiresAt,
close: () => setBillAction(null),
},
};
}