@afriex/payment-batches
Version:
Bulk payouts to bank accounts and mobile money wallets with the Afriex Business API
186 lines • 7.38 kB
JavaScript
import { ValidationBuilder, ValidationError } from "@afriex/core";
export class PaymentBatchService {
constructor(httpClient) {
this.httpClient = httpClient;
}
/**
* List payment batches with pagination
* GET /payment-batch
*/
async list(params) {
return this.httpClient.get("/payment-batch", {
params,
});
}
/**
* Create an empty payment batch
* POST /payment-batch
*
* Add recipients next, then start the payouts with `withdraw()`.
*/
async create(request) {
this.validateBatchRequest(request);
const response = await this.httpClient.post("/payment-batch", request);
return response.data;
}
/**
* Get a payment batch by ID
* GET /payment-batch/{batchId}
*/
async get(batchId) {
this.requireBatchId(batchId);
const response = await this.httpClient.get(`/payment-batch/${batchId}`);
return response.data;
}
/**
* Update a payment batch
* PATCH /payment-batch/{batchId}
*
* The batch is replaced whole: send the name and the funding method, even
* when only one of them changes.
*/
async update(batchId, request) {
this.requireBatchId(batchId);
this.validateBatchRequest(request);
const response = await this.httpClient.patch(`/payment-batch/${batchId}`, request);
return response.data;
}
/**
* Delete a payment batch
* DELETE /payment-batch/{batchId}
*/
async delete(batchId) {
this.requireBatchId(batchId);
await this.httpClient.delete(`/payment-batch/${batchId}`);
}
/**
* List the recipients in a batch
* GET /payment-batch/{batchId}/recipients
*/
async listRecipients(batchId, params) {
this.requireBatchId(batchId);
return this.httpClient.get(`/payment-batch/${batchId}/recipients`, { params });
}
/**
* Add one recipient to a batch
* POST /payment-batch/{batchId}/recipients
*
* The returned `id` is the saved account's id, not the recipient's id in
* the batch. Read the `recipientId` from `listRecipients()` to update or
* remove the recipient.
*/
async addRecipient(batchId, recipient) {
this.requireBatchId(batchId);
this.validateRecipient(new ValidationBuilder(), recipient).throwIfInvalid();
const response = await this.httpClient.post(`/payment-batch/${batchId}/recipients`, recipient);
return response.data;
}
/**
* Add several recipients to a batch
* POST /payment-batch/{batchId}/recipients/bulk
*
* Each recipient is added independently, so the call succeeds even when
* some of them fail. Both outcome maps are keyed by account number.
*/
async addRecipients(batchId, recipients) {
this.requireBatchId(batchId);
if (!Array.isArray(recipients) || recipients.length === 0) {
throw new ValidationError("At least one recipient is required");
}
const builder = new ValidationBuilder();
recipients.forEach((recipient, index) => this.validateRecipient(builder, recipient, `recipients[${index}].`));
builder.throwIfInvalid();
const response = await this.httpClient.post(`/payment-batch/${batchId}/recipients/bulk`, recipients);
return response.data;
}
/**
* Update a recipient in a batch
* PATCH /payment-batch/{batchId}/recipients/{recipientId}
*
* The recipient is replaced whole: send its account details and amount,
* even when only one of them changes. Pass `paymentMethodId` to update the
* saved account in place.
*/
async updateRecipient(batchId, recipientId, request) {
this.requireBatchId(batchId);
this.requireRecipientId(recipientId);
this.validateRecipient(new ValidationBuilder(), request).throwIfInvalid();
const response = await this.httpClient.patch(`/payment-batch/${batchId}/recipients/${recipientId}`, request);
return response.data;
}
/**
* Remove a recipient from a batch
* DELETE /payment-batch/{batchId}/recipients/{recipientId}
*
* Removes the recipient from this batch only. The saved account is kept.
*/
async removeRecipient(batchId, recipientId) {
this.requireBatchId(batchId);
this.requireRecipientId(recipientId);
await this.httpClient.delete(`/payment-batch/${batchId}/recipients/${recipientId}`);
}
/**
* Start the payouts for a batch
* POST /payment-batch/{batchId}/withdraw
*
* Every call is a new run and pays every recipient again. To retry only the
* payouts that failed, pass the `sessionId` of the run to retry. The
* outcomes are keyed by `recipientId`, and a payout that was accepted still
* settles asynchronously.
*/
async withdraw(batchId, params) {
this.requireBatchId(batchId);
const response = await this.httpClient.post(`/payment-batch/${batchId}/withdraw`, undefined, params?.sessionId ? { params: { sessionId: params.sessionId } } : undefined);
return response.data;
}
/**
* List the runs of a batch
* GET /payment-batch/{batchId}/sessions
*/
async listSessions(batchId, params) {
this.requireBatchId(batchId);
return this.httpClient.get(`/payment-batch/${batchId}/sessions`, { params });
}
requireBatchId(batchId) {
if (!batchId) {
throw new ValidationError("Batch ID is required");
}
}
requireRecipientId(recipientId) {
if (!recipientId) {
throw new ValidationError("Recipient ID is required");
}
}
validateBatchRequest(request) {
new ValidationBuilder()
.required("name", request?.name)
.required("sourcePaymentMethod", request?.sourcePaymentMethod)
.condition("sourcePaymentMethod.currencyCode", Boolean(request?.sourcePaymentMethod) &&
!request.sourcePaymentMethod.currencyCode, "sourcePaymentMethod.currencyCode is required")
.throwIfInvalid();
}
/**
* As on a payment method, UPI and INTERAC recipients take no institution.
*/
validateRecipient(builder, recipient, prefix = "") {
const channel = recipient?.channel;
const needsInstitution = channel !== "UPI" && channel !== "INTERAC";
const amount = recipient?.amount;
builder
.required(`${prefix}channel`, channel)
.required(`${prefix}accountName`, recipient?.accountName)
.required(`${prefix}accountNumber`, recipient?.accountNumber)
.required(`${prefix}countryCode`, recipient?.countryCode)
.required(`${prefix}amount`, amount)
.condition(`${prefix}amount.value`, Boolean(amount) &&
(amount.value === undefined ||
amount.value === null ||
amount.value === ""), `${prefix}amount.value is required`)
.condition(`${prefix}amount.currencyCode`, Boolean(amount) && !amount.currencyCode, `${prefix}amount.currencyCode is required`);
if (needsInstitution) {
builder.required(`${prefix}institution`, recipient?.institution);
}
return builder;
}
}
//# sourceMappingURL=PaymentBatchService.js.map