diff --git a/apps/edr-freight-api/src/modules/bookings/booking-invoice.service.ts b/apps/edr-freight-api/src/modules/bookings/booking-invoice.service.ts index 1b8eddeca..21fef08ea 100644 --- a/apps/edr-freight-api/src/modules/bookings/booking-invoice.service.ts +++ b/apps/edr-freight-api/src/modules/bookings/booking-invoice.service.ts @@ -5,6 +5,7 @@ import { Injectable, Logger, } from "@nestjs/common"; +import { OnEvent } from "@nestjs/event-emitter"; import { Freight } from "@edr/types"; import { DataSource, EntityManager } from "typeorm"; @@ -94,6 +95,7 @@ export class BookingInvoiceService { * reactions live here (not in the payment process): each invoice type advances * the booking its own way. Only PREPAID exists today. */ + @OnEvent("booking.invoice.paid") async onBookingInvoicePaid(payload: InvoiceEventPayload): Promise { this.logger.log( `onBookingInvoicePaid trigger for ${payload.sourceId} from ${payload.invoiceId}`, diff --git a/apps/edr-freight-api/src/modules/payment/payment.service.ts b/apps/edr-freight-api/src/modules/payment/payment.service.ts index d92af7a3e..d773ebe1f 100644 --- a/apps/edr-freight-api/src/modules/payment/payment.service.ts +++ b/apps/edr-freight-api/src/modules/payment/payment.service.ts @@ -189,48 +189,53 @@ export class PaymentService { * has stored the intent id, avoiding a settle-before-correlation race. */ async initiate(input: InitiateIntentInput): Promise { - const snapshot = await this.paymentClient.initiate({ - service: PaymentServiceEnum.FREIGHT, - referenceType: PaymentReferenceType.SHIPMENT, - referenceId: input.referenceId, - orderRef: input.orderRef, - amountMinor: input.amountMinor, - currency: input.currency, - provider: input.method as ProviderMethod, - platform: input.platform, - payerAccount: input.payerAccount, - returnUrl: - input.returnUrl ?? "https://edrfreight.triaplc.com/payment/success", - failureUrl: - input.failureUrl ?? "https://edrfreight.triaplc.com/payment/failure", - }); + try { + const snapshot = await this.paymentClient.initiate({ + service: PaymentServiceEnum.FREIGHT, + referenceType: PaymentReferenceType.SHIPMENT, + referenceId: input.referenceId, + orderRef: input.orderRef, + amountMinor: input.amountMinor, + currency: input.currency, + provider: input.method as ProviderMethod, + platform: input.platform, + payerAccount: input.payerAccount, + returnUrl: + input.returnUrl ?? "https://edrfreight.triaplc.com/payment/success", + failureUrl: + input.failureUrl ?? "https://edrfreight.triaplc.com/payment/failure", + }); - const immediateSuccess = - snapshot.status === ProviderPaymentStatus.SUCCEEDED; - const paidAt = snapshot.paidAt ? new Date(snapshot.paidAt) : undefined; + const immediateSuccess = + snapshot.status === ProviderPaymentStatus.SUCCEEDED; + const paidAt = snapshot.paidAt ? new Date(snapshot.paidAt) : undefined; - const intent = await this.upsertIntent(input, snapshot); + const intent = await this.upsertIntent(input, snapshot); - if (immediateSuccess) { - // Settle the projection but DO NOT notify billing — billing settles - // inline once it has stored intentId on the invoice (see payInvoice), - // avoiding a settle-before-correlation race. - await this.markIntentSucceeded(intent.id, { + if (immediateSuccess) { + // Settle the projection but DO NOT notify billing — billing settles + // inline once it has stored intentId on the invoice (see payInvoice), + // avoiding a settle-before-correlation race. + await this.markIntentSucceeded(intent.id, { + providerTxnId: snapshot.providerTxnId, + paidAt, + notify: false, + }); + } + + return { + intentId: intent.id, + // `intent` still reflects the projection status ("processing" on immediate + // success — settlement is applied by the caller, not shown synchronously). + response: this.formatIntentResponse(intent), + immediateSuccess, providerTxnId: snapshot.providerTxnId, paidAt, - notify: false, - }); + }; + } catch (err) { + console.log(err); + throw err; } - - return { - intentId: intent.id, - // `intent` still reflects the projection status ("processing" on immediate - // success — settlement is applied by the caller, not shown synchronously). - response: this.formatIntentResponse(intent), - immediateSuccess, - providerTxnId: snapshot.providerTxnId, - paidAt, - }; } /** Create or update the local intent projection from a provider snapshot. */