125 lines
4.5 KiB
TypeScript
125 lines
4.5 KiB
TypeScript
import { apiClient } from '../../../services/api/apiClient';
|
|
import { baseApi } from '../../../services/api/baseApi';
|
|
import { endpoints } from '../../../services/api/endpoints';
|
|
import { resolveLive, resolveWithMock } from '../../../services/api/serviceHelpers';
|
|
import type { Beneficiary } from '../../../types/app';
|
|
import type {
|
|
BeneficiarySaveRequest,
|
|
BeneficiaryValidateRequest,
|
|
BeneficiaryValidateResult,
|
|
CbsBeneficiary,
|
|
} from '../../../types/cbs';
|
|
import { favoriteStore, toBeneficiary } from '../utils/beneficiaryMapper';
|
|
|
|
/**
|
|
* TEMPORARY — fixed payer account for payee validation.
|
|
*
|
|
* Every validate call goes out with this as the payer, overriding whichever
|
|
* account the customer selected on screen. The development CBS only recognises
|
|
* this account as a payer; the profile's own accounts come back
|
|
* `ACCOUNT_NOT_FOUND`, which reads on screen as the *payee* being unverifiable.
|
|
*
|
|
* Delete this and pass `request` through untouched once the backend accepts
|
|
* the profile's accounts. It must not ship: in production it would validate
|
|
* every payee against an account that is not the customer's.
|
|
*/
|
|
const HARDCODED_PAYER_ACCOUNT = '002002000100019';
|
|
|
|
export interface BankTransferRequest {
|
|
beneficiaryId: string;
|
|
amount: number;
|
|
transferType: 'IMPS' | 'NEFT' | 'RTGS' | 'Internal';
|
|
remarks?: string;
|
|
sourceAccountId: string;
|
|
}
|
|
|
|
export interface BankTransferResponse {
|
|
referenceId: string;
|
|
status: 'Process_Success' | 'Process_Pending';
|
|
}
|
|
|
|
export const paymentsApi = baseApi.injectEndpoints({
|
|
endpoints: (builder) => ({
|
|
/** The customer's saved payees, newest first. */
|
|
getBeneficiaries: builder.query<Beneficiary[], void>({
|
|
queryFn: () =>
|
|
resolveLive(async () => {
|
|
const [response, favorites] = await Promise.all([
|
|
apiClient.get<CbsBeneficiary[]>(endpoints.beneficiaries.list),
|
|
favoriteStore.read(),
|
|
]);
|
|
|
|
const favoriteSet = new Set(favorites);
|
|
|
|
return (response.data ?? []).map((payee) => toBeneficiary(payee, favoriteSet));
|
|
}),
|
|
providesTags: ['Beneficiaries'],
|
|
}),
|
|
|
|
/**
|
|
* Asks IMPS whether this payee exists, and who they are. Saves nothing.
|
|
*
|
|
* This is what the confirmation step shows, because a mistyped account
|
|
* number is the most common way money reaches the wrong person. Read
|
|
* `result` rather than the status code: the switch answers 200 whether or
|
|
* not it recognised the payee.
|
|
*/
|
|
validateBeneficiary: builder.mutation<BeneficiaryValidateResult, BeneficiaryValidateRequest>({
|
|
queryFn: (request) =>
|
|
resolveLive(async () => {
|
|
const response = await apiClient.post<BeneficiaryValidateResult>(
|
|
endpoints.beneficiaries.validate,
|
|
// See HARDCODED_PAYER_ACCOUNT — the caller's payer is discarded.
|
|
{ ...request, payer: { accountNumber: HARDCODED_PAYER_ACCOUNT } },
|
|
);
|
|
|
|
return response.data;
|
|
}),
|
|
}),
|
|
|
|
/**
|
|
* Saves the payee, resolved through the bank's CBS lookup.
|
|
*
|
|
* A different body and a different lookup from validate, which goes to
|
|
* IMPS — so the two can disagree, and this one's answer is what gets
|
|
* stored. Validate first regardless: the server does not enforce the
|
|
* order, which means nothing but the app stops a customer saving a payee
|
|
* nobody has confirmed.
|
|
*/
|
|
saveBeneficiary: builder.mutation<Beneficiary, BeneficiarySaveRequest>({
|
|
queryFn: (request) =>
|
|
resolveLive(async () => {
|
|
const response = await apiClient.post<CbsBeneficiary>(
|
|
endpoints.beneficiaries.save,
|
|
request,
|
|
);
|
|
|
|
return toBeneficiary(response.data);
|
|
}),
|
|
invalidatesTags: ['Beneficiaries'],
|
|
}),
|
|
|
|
/** No transfer endpoint exists yet, so this stays on fixtures. */
|
|
submitBankTransfer: builder.mutation<BankTransferResponse, BankTransferRequest>({
|
|
queryFn: (req) =>
|
|
resolveWithMock({
|
|
mockData: () => ({
|
|
referenceId: `${req.transferType}${Date.now()}`,
|
|
status: req.transferType === 'NEFT' ? 'Process_Pending' : 'Process_Success',
|
|
}),
|
|
request: async () => {
|
|
throw new Error('Transfer submission is not implemented by the CBS API yet');
|
|
},
|
|
}),
|
|
invalidatesTags: ['Payments', 'Transactions'],
|
|
}),
|
|
}),
|
|
});
|
|
|
|
export const {
|
|
useGetBeneficiariesQuery,
|
|
useValidateBeneficiaryMutation,
|
|
useSaveBeneficiaryMutation,
|
|
useSubmitBankTransferMutation,
|
|
} = paymentsApi;
|