diff --git a/services/121-service/src/payments/fsp-integration/nedbank/README.md b/services/121-service/src/payments/fsp-integration/nedbank/README.md index bca30000bb..98b57314f8 100644 --- a/services/121-service/src/payments/fsp-integration/nedbank/README.md +++ b/services/121-service/src/payments/fsp-integration/nedbank/README.md @@ -20,3 +20,13 @@ To enable loading certificate files via environment variables in the Azure App S ```dotenv NEDBANK_CERTIFICATE_PATH=/var/ssl/private/.p12 ``` + +4. **Error Handling**: + + - When making a payment to Nedbank, if the certificate is not set up correctly, you will encounter transactions with the following error: + + ```json + { "errno": -71, "code": "EPROTO", "syscall": "write" } + ``` + + - Ensure that the certificate is correctly uploaded and the path is correctly set in the environment variables to avoid this error. diff --git a/services/121-service/src/payments/fsp-integration/nedbank/__snapshots__/nedbank.service.spec.ts.snap b/services/121-service/src/payments/fsp-integration/nedbank/__snapshots__/nedbank.service.spec.ts.snap index 1d3fbb6313..a354cd39b2 100644 --- a/services/121-service/src/payments/fsp-integration/nedbank/__snapshots__/nedbank.service.spec.ts.snap +++ b/services/121-service/src/payments/fsp-integration/nedbank/__snapshots__/nedbank.service.spec.ts.snap @@ -6,4 +6,6 @@ exports[`NedbankService createVoucher should throw an error if phone number does exports[`NedbankService createVoucher should throw an error if phone number length is not 11 1`] = `"Phone number must be 11 numbers long (including 27 as country code)"`; +exports[`NedbankService retrieveVoucherInfo should not return a voucher status and specific error code and message on a NBApimTooManyRequestsError api error 1`] = `"Too many requests"`; + exports[`NedbankService retrieveVoucherInfo should return a voucher status and specific error code and message on a NBApimResourceNotFound api error 1`] = `"Resource not found"`; diff --git a/services/121-service/src/payments/fsp-integration/nedbank/nedbank.service.spec.ts b/services/121-service/src/payments/fsp-integration/nedbank/nedbank.service.spec.ts index c48ee98281..f5bb164a01 100644 --- a/services/121-service/src/payments/fsp-integration/nedbank/nedbank.service.spec.ts +++ b/services/121-service/src/payments/fsp-integration/nedbank/nedbank.service.spec.ts @@ -167,6 +167,26 @@ describe('NedbankService', () => { ); }); + it('should not return a voucher status and specific error code and message on a NBApimTooManyRequestsError api error', async () => { + const error = new NedbankApiError('Too many requests'); + error.code = NedbankApiErrorCode.NBApimTooManyRequestsError; + + jest + .spyOn(apiService, 'getOrderByOrderCreateReference') + .mockRejectedValue(error); + + const result = await service.retrieveVoucherInfo(orderCreateReference); + + expect(result.errorCode).toBe( + NedbankErrorCode.tooManyRequestsForThisVoucher, + ); + expect(result.status).toBeUndefined(); + expect(result.errorMessage).toMatchSnapshot(); + expect(apiService.getOrderByOrderCreateReference).toHaveBeenCalledWith( + orderCreateReference, + ); + }); + it('should return the error message and a generic error code on a Nedbank Api error that is not specifically handled', async () => { const error = new NedbankApiError('General error'); error.code = 'SomeOtherErrorCode'; diff --git a/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.spec.ts b/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.spec.ts index d3ce996f61..6ede969be0 100644 --- a/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.spec.ts +++ b/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.spec.ts @@ -9,6 +9,7 @@ import { NedbankApiError } from '@121-service/src/payments/fsp-integration/nedba import { NedbankApiHelperService } from '@121-service/src/payments/fsp-integration/nedbank/services/nedbank-api.helper.service'; import { NedbankApiClientService } from '@121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service'; import { CustomHttpService } from '@121-service/src/shared/services/custom-http.service'; + jest.mock('@121-service/src/shared/services/custom-http.service'); describe('NedbankApiClientService', () => { @@ -42,8 +43,10 @@ describe('NedbankApiClientService', () => { apiHelperService = module.get( NedbankApiHelperService, ); - // Mock the httpsAgent - service.httpsAgent = {} as https.Agent; + }); + + afterEach(() => { + jest.resetModules(); }); describe('makeApiRequestOrThrow', () => { @@ -70,6 +73,12 @@ describe('NedbankApiClientService', () => { jest .spyOn(apiHelperService, 'isNedbankErrorResponse') .mockReturnValue(false); + jest + .spyOn(httpService, 'createHttpsAgentWithCertificate') + .mockReturnValue({} as https.Agent); + + // Reinitialize the service to set httpsAgent + service = new NedbankApiClientService(httpService, apiHelperService); // Act const result = await service.makeApiRequestOrThrow({ @@ -86,7 +95,13 @@ describe('NedbankApiClientService', () => { }); it('should throw an error if httpsAgent is not defined', async () => { - service.httpsAgent = undefined; + jest + .spyOn(httpService, 'createHttpsAgentWithCertificate') + .mockReturnValue(undefined as any); + + // Reinitialize the service to set httpsAgent to undefined + service = new NedbankApiClientService(httpService, apiHelperService); + await expect( service.makeApiRequestOrThrow({ endpoint: 'https://example.com', @@ -115,6 +130,12 @@ describe('NedbankApiClientService', () => { .spyOn(apiHelperService, 'isNedbankErrorResponse') .mockReturnValue(true); jest.spyOn(httpService, 'request').mockResolvedValue(errorResponse); + jest + .spyOn(httpService, 'createHttpsAgentWithCertificate') + .mockReturnValue({} as https.Agent); + + // Reinitialize the service to set httpsAgent + service = new NedbankApiClientService(httpService, apiHelperService); await expect( service.makeApiRequestOrThrow({ diff --git a/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.ts b/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.ts index 7c7621d61d..284202d728 100644 --- a/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.ts +++ b/services/121-service/src/payments/fsp-integration/nedbank/services/nedbank-api-client.service.ts @@ -16,7 +16,7 @@ const nedbankApiUrl = process.env.MOCK_NEDBANK @Injectable() export class NedbankApiClientService { - public httpsAgent: https.Agent | undefined; + private httpsAgent: https.Agent | undefined; public constructor( private readonly httpService: CustomHttpService, @@ -75,7 +75,7 @@ export class NedbankApiClientService { } return this.httpService.createHttpsAgentWithCertificate( process.env.NEDBANK_CERTIFICATE_PATH!, - process.env.NEDBANK_CERTIFICATE_PASSWORD!, + process.env.NEDBANK_CERTIFICATE_PASSWORD, ); } diff --git a/services/121-service/src/reconciliation/nedbank-reconciliation/nedbank-reconciliation.service.ts b/services/121-service/src/reconciliation/nedbank-reconciliation/nedbank-reconciliation.service.ts index c684b7f62b..960744e9ea 100644 --- a/services/121-service/src/reconciliation/nedbank-reconciliation/nedbank-reconciliation.service.ts +++ b/services/121-service/src/reconciliation/nedbank-reconciliation/nedbank-reconciliation.service.ts @@ -17,7 +17,7 @@ export class NedbankReconciliationService { ) {} public async doNedbankReconciliation(): Promise { - const vouchers = await this.nedbankVoucherScopedRepository.find({ + const voucherReferences = await this.nedbankVoucherScopedRepository.find({ select: ['orderCreateReference', 'transactionId'], where: [ { status: IsNull() }, @@ -33,12 +33,12 @@ export class NedbankReconciliationService { ], }); - for (const voucher of vouchers) { - await this.reconciliateVoucherAndTransaction(voucher); + for (const voucherReference of voucherReferences) { + await this.reconcileVoucherAndTransaction(voucherReference); } } - private async reconciliateVoucherAndTransaction({ + private async reconcileVoucherAndTransaction({ orderCreateReference, transactionId, }: { diff --git a/services/121-service/src/transaction-job-processors/transaction-job-processors.service.ts b/services/121-service/src/transaction-job-processors/transaction-job-processors.service.ts index 47926304e6..ac22a8af21 100644 --- a/services/121-service/src/transaction-job-processors/transaction-job-processors.service.ts +++ b/services/121-service/src/transaction-job-processors/transaction-job-processors.service.ts @@ -298,9 +298,9 @@ export class TransactionJobProcessorsService { }, )) as string; // This must be a string. If it is undefined the validation in payment service should have caught it. If a user set it as an array string you should get an internal server error here, this seems like an edge case; const sanitizedPaymentReferencePrefix = paymentReferencePrefix.replace( - /[^a-zA-Z0-9]/g, + /[^a-zA-Z0-9-]/g, '', - ); // All non-alphanumeric characters are removed because the nedbank API does not accept them + ); // All non-alphanumeric characters (except hyphens) are removed because the nedbank API does not accept them const paymentReference = `${sanitizedPaymentReferencePrefix.slice(0, 18)}-${transactionJob.phoneNumber}`; // 3. Check if there is an existing voucher/orderCreateReference without status if not create orderCreateReference, the nedbank voucher and the related transaction