UNPKG

@afriex/payment-batches

Version:

Bulk payouts to bank accounts and mobile money wallets with the Afriex Business API

186 lines • 7.38 kB
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