mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 19:28:17 +00:00
cac payemnt integration and cbe rate exchange webscrabing
This commit is contained in:
@@ -6,12 +6,14 @@ import {
|
||||
NotFoundException,
|
||||
} from "@nestjs/common";
|
||||
import { DataSource, QueryFailedError } from "typeorm";
|
||||
import { createMerchantOrderId } from "@edr/payment-providers";
|
||||
import { createMerchantOrderId, CacBankProvider } from "@edr/payment-providers";
|
||||
import {
|
||||
ConfirmPaymentRequest,
|
||||
InitiatePaymentRequest,
|
||||
PaymentIntentSnapshot,
|
||||
PaymentReferenceType,
|
||||
PaymentService,
|
||||
ProviderMethod,
|
||||
ProviderPaymentStatus,
|
||||
ProviderStatus,
|
||||
} from "@edr/types";
|
||||
@@ -52,6 +54,7 @@ export class IntentsService {
|
||||
private readonly dataSource: DataSource,
|
||||
@Inject(PAYMENT_PROVIDER_MAP)
|
||||
private readonly providers: PaymentProviderMap,
|
||||
private readonly cacBankProvider: CacBankProvider,
|
||||
) {}
|
||||
|
||||
/* ------------------------------------------------------------------ initiate */
|
||||
@@ -85,6 +88,15 @@ export class IntentsService {
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
request.provider === ProviderMethod.CAC_BANK &&
|
||||
!request.payerAccount?.trim()
|
||||
) {
|
||||
throw new BadRequestException(
|
||||
"payerAccount (customer mobile number) is required for CAC_BANK",
|
||||
);
|
||||
}
|
||||
|
||||
const merchantOrderId = createMerchantOrderId();
|
||||
const result = await provider.initiate({
|
||||
merchantOrderId,
|
||||
@@ -134,6 +146,65 @@ export class IntentsService {
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------ confirm (OTP providers) */
|
||||
|
||||
async confirm(
|
||||
intentId: string,
|
||||
request: ConfirmPaymentRequest,
|
||||
): Promise<PaymentIntentSnapshot> {
|
||||
const intent = await this.intentsRepository.findById(intentId);
|
||||
if (!intent) throw new NotFoundException("PaymentIntent not found");
|
||||
|
||||
if (intent.provider !== ProviderMethod.CAC_BANK) {
|
||||
throw new BadRequestException(
|
||||
`Confirm is not supported for provider: ${intent.provider}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (intent.status !== ProviderPaymentStatus.REQUIRES_ACTION) {
|
||||
throw new BadRequestException(
|
||||
`Intent is not awaiting confirmation (status=${intent.status})`,
|
||||
);
|
||||
}
|
||||
|
||||
if (!intent.providerOrderId) {
|
||||
throw new BadRequestException("Intent has no provider order id");
|
||||
}
|
||||
|
||||
const confirmResult = await this.cacBankProvider.confirmPayment(
|
||||
intent.providerOrderId,
|
||||
request.otp,
|
||||
);
|
||||
|
||||
if (confirmResult.reference) {
|
||||
await this.intentsRepository.update(intent.id, {
|
||||
rawInitiation: {
|
||||
...(intent.rawInitiation ?? {}),
|
||||
reference: confirmResult.reference,
|
||||
confirmResponse: confirmResult.rawResponse,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (confirmResult.status === "SUCCEEDED") {
|
||||
await this.applyProviderResult(intent.id, {
|
||||
status: ProviderPaymentStatus.SUCCEEDED,
|
||||
providerTxnId: confirmResult.providerTxnId,
|
||||
paidAt: new Date(),
|
||||
});
|
||||
} else {
|
||||
await this.applyProviderResult(intent.id, {
|
||||
status: ProviderPaymentStatus.FAILED,
|
||||
failureCode: confirmResult.failureCode,
|
||||
failureMessage: confirmResult.failureMessage,
|
||||
});
|
||||
}
|
||||
|
||||
const updated = await this.intentsRepository.findById(intent.id);
|
||||
if (!updated) throw new NotFoundException("PaymentIntent not found");
|
||||
return this.toSnapshot(updated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decide whether an existing active intent can be returned as-is. An expired
|
||||
* REQUIRES_ACTION intent is retired (CANCELLED, no notification — nothing was paid)
|
||||
@@ -192,7 +263,7 @@ export class IntentsService {
|
||||
if (!refreshable || !stale || !provider) return intent;
|
||||
|
||||
try {
|
||||
const status = await provider.queryStatus(intent.merchantOrderId);
|
||||
const status = await this.queryProviderStatus(intent);
|
||||
await this.applyProviderResult(
|
||||
intent.id,
|
||||
this.fromProviderStatus(status),
|
||||
@@ -207,6 +278,26 @@ export class IntentsService {
|
||||
}
|
||||
}
|
||||
|
||||
private async queryProviderStatus(
|
||||
intent: PaymentIntent,
|
||||
): Promise<ProviderStatus> {
|
||||
const provider = this.providers.get(intent.provider);
|
||||
if (!provider) {
|
||||
throw new Error(`Unknown provider: ${intent.provider}`);
|
||||
}
|
||||
|
||||
if (intent.provider === ProviderMethod.CAC_BANK) {
|
||||
const reference = (intent.rawInitiation as { reference?: string })
|
||||
?.reference;
|
||||
return this.cacBankProvider.queryStatus(
|
||||
intent.merchantOrderId,
|
||||
reference,
|
||||
);
|
||||
}
|
||||
|
||||
return provider.queryStatus(intent.merchantOrderId);
|
||||
}
|
||||
|
||||
fromProviderStatus(status: ProviderStatus): ProviderResultInput {
|
||||
return {
|
||||
status: status.status,
|
||||
|
||||
Reference in New Issue
Block a user