feat: ( payment ) add changing payment for booking

This commit is contained in:
Abubeker Yasin
2026-07-08 23:05:42 +03:00
parent 78bee6dbe3
commit 01e24e1aae
2 changed files with 29 additions and 3 deletions

View File

@@ -338,7 +338,14 @@ export class PaymentsService {
};
return this.prisma.paymentIntent.upsert({
where: { bookingId },
update: data,
// amountMinor/currency are refreshed on update too: a cross-currency method switch
// (e.g. Waafi/USD → Telebirr/ETB) re-initiates over the same row, and the projection
// must reflect the currency the new provider actually charges — not the first one's.
update: {
...data,
amountMinor: snapshot.amountMinor,
currency: snapshot.currency,
},
create: {
bookingId,
amountMinor: snapshot.amountMinor,

View File

@@ -77,9 +77,28 @@ export class IntentsService {
request.referenceId,
);
if (existing) {
// Payer switched method (e.g. Waafi → Telebirr) on an uncharged session: retire the
// open intent and fall through to open a fresh one for the new provider. Only safe
// while REQUIRES_ACTION — PROCESSING/SUCCEEDED intents may have money in flight, so
// they keep the reuse path (the switch is silently refused until they resolve).
const switchingProvider =
existing.provider !== request.provider &&
existing.status === ProviderPaymentStatus.REQUIRES_ACTION;
if (switchingProvider) {
await this.intentsRepository.update(existing.id, {
status: ProviderPaymentStatus.CANCELLED,
failureCode: "METHOD_CHANGED",
failureMessage: `Payer switched from ${existing.provider} to ${request.provider}`,
});
this.logger.log(
`intent ${existing.id} retired (METHOD_CHANGED ${existing.provider}${request.provider}) for ` +
`${request.service}/${request.referenceType}/${request.referenceId}`,
);
} else {
const reusable = await this.reuseOrRetire(existing);
if (reusable) return this.toSnapshot(reusable);
}
}
const provider = this.providers.get(request.provider);
if (!provider) {