add provider detail on the

This commit is contained in:
Abubeker Yasin
2026-07-16 12:05:47 +03:00
parent edbef5ccf2
commit c6ca5ed8e8
4 changed files with 34 additions and 0 deletions

View File

@@ -154,6 +154,13 @@ export class IntentStatusDto {
@ApiPropertyOptional() paidAt?: string;
@ApiPropertyOptional() failureCode?: string;
@ApiPropertyOptional() failureMessage?: string;
@ApiPropertyOptional({
type: "object",
additionalProperties: true,
description:
"Raw provider payload (initiation response merged with the latest status query) for inspection/debugging. Provider-specific shape; never trusted for state.",
})
providerResponse?: Record<string, unknown>;
}
export class BookingAmountResponseDto {

View File

@@ -432,6 +432,9 @@ export class PaymentsService {
expiresAt: snapshot.expiresAt ? new Date(snapshot.expiresAt) : null,
failureCode: snapshot.failureCode ?? null,
failureMessage: snapshot.failureMessage ?? null,
rawInitiation: snapshot.providerResponse
? (snapshot.providerResponse as unknown as Prisma.InputJsonValue)
: Prisma.DbNull,
};
return this.prisma.paymentIntent.upsert({
where: { bookingId },
@@ -589,6 +592,10 @@ export class PaymentsService {
paidAt: intent.paidAt?.toISOString(),
failureCode: intent.failureCode ?? undefined,
failureMessage: intent.failureMessage ?? undefined,
providerResponse:
intent.rawInitiation && typeof intent.rawInitiation === "object"
? (intent.rawInitiation as Record<string, unknown>)
: undefined,
};
}

View File

@@ -41,6 +41,8 @@ export interface ProviderResultInput {
confirmedAmountMinor?: number;
failureCode?: string;
failureMessage?: string;
/** Raw provider status-query body, merged into the intent's audit payload when present. */
rawResponse?: Record<string, unknown>;
}
@Injectable()
@@ -357,6 +359,7 @@ export class IntentsService {
providerTxnId: status.providerTxnId,
failureCode: status.failureCode,
failureMessage: status.failureMessage,
rawResponse: status.rawResponse,
};
}
@@ -388,6 +391,15 @@ export class IntentsService {
return { alreadyTerminal: true };
}
// Keep the audit payload current with the latest provider status body (surfaced as
// `providerResponse` in the snapshot). Merged so the initiation keys are preserved.
if (result.rawResponse) {
intent.rawInitiation = {
...(intent.rawInitiation ?? {}),
statusResponse: result.rawResponse,
};
}
if (result.status === ProviderPaymentStatus.SUCCEEDED) {
const paidAt = result.paidAt ?? new Date();
intent.status = ProviderPaymentStatus.SUCCEEDED;
@@ -482,6 +494,7 @@ export class IntentsService {
failureCode: intent.failureCode ?? undefined,
failureMessage: intent.failureMessage ?? undefined,
expiresAt: intent.expiresAt?.toISOString(),
providerResponse: intent.rawInitiation ?? undefined,
};
}
}

View File

@@ -153,6 +153,13 @@ export type PaymentIntentSnapshot ={
failureCode?: string;
failureMessage?: string;
expiresAt?: string;
/**
* Raw provider payload for inspection/debugging — the audit copy of the provider
* initiation response merged with the latest status-query response (secrets redacted
* upstream). Not a contract with the provider; shape is provider-specific. Never trusted
* for state decisions — the state machine drives `status`.
*/
providerResponse?: Record<string, unknown>;
}
export type PaymentEventType = "payment.succeeded" | "payment.failed";