Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
- [update](#update)
- [delete](#delete)
- [Entity onboardings](#entity-onboardings)
- [Transfer batches](#transfer-batches)
- [Webhook Signature Validation](#webhook-signature-validation)
- [Idempotency Keys](#idempotency-keys)
- [Serialization](#serialization)
Expand Down Expand Up @@ -255,6 +256,38 @@ const submitted = await fintocClient.v2.entities.onboardings.submit(
);
```

### Transfer batches

Creating a transfer batch requires a JWS private key, just like transfers:

```javascript
const fintocClient = new Fintoc('your_api_key', 'path/to/private_key.pem');

const transferBatch = await fintocClient.v2.transferBatches.create({
idempotency_key: '12345678',
description: 'Payroll',
currency: 'MXN',
transfers: [
{
amount: 54123,
account_id: 'acc_12345678',
counterparty: {
account_number: '012969100000000026',
},
},
],
});
```

You can list batches filtering by `status`, `since` and `until`:

```javascript
const transferBatches = await fintocClient.v2.transferBatches.list({
status: ['succeeded', 'partially_succeeded'],
since: '2025-01-01',
});
```

### Webhook Signature Validation

To ensure the authenticity of incoming webhooks from Fintoc, you should always validate the signature. The SDK provides a `WebhookSignature` class to verify the `Fintoc-Signature` header
Expand Down
3 changes: 3 additions & 0 deletions src/lib/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
PaymentMethodsManager,
ProductsManager,
SimulateManager,
TransferBatchesManager,
TransfersManager,
AccountsManager as V2AccountsManager,
CheckoutSessionsManager as V2CheckoutSessionsManager,
Expand All @@ -50,6 +51,7 @@ class FintocV2 {
paymentMethods: PaymentMethodsManager;
products: ProductsManager;
subscriptions: V2SubscriptionsManager;
transferBatches: TransferBatchesManager;

constructor(client: Client) {
this.accounts = new V2AccountsManager('/v2/accounts', client);
Expand All @@ -65,6 +67,7 @@ class FintocV2 {
this.paymentMethods = new PaymentMethodsManager('/v2/payment_methods', client);
this.products = new ProductsManager('/v2/products', client);
this.subscriptions = new V2SubscriptionsManager('/v2/subscriptions', client);
this.transferBatches = new TransferBatchesManager('/v2/transfer_batches', client);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/lib/managers/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export * from './subscriptionsManager';
export * from './subscriptionItemsManager';
export * from './accountStatementsManager';
export * from './onboardingsManager';
export * from './transferBatchesManager';
7 changes: 7 additions & 0 deletions src/lib/managers/v2/transferBatchesManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { ManagerMixin } from '../../mixins';
import { TransferBatch } from '../../resources/v2/transferBatch';

export class TransferBatchesManager extends ManagerMixin<TransferBatch> {
static resource = 'transferBatch';
static methods = ['list', 'get', 'create'];
}
1 change: 1 addition & 0 deletions src/lib/resources/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ export * from './onboarding';
export * from './onboardingShareholder';
export * from './onboardingLegalRepresentative';
export * from './onboardingDocument';
export * from './transferBatch';
4 changes: 4 additions & 0 deletions src/lib/resources/v2/transferBatch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { ResourceMixin } from '../../mixins/resourceMixin';

export class TransferBatch extends ResourceMixin<TransferBatch> {
}
97 changes: 97 additions & 0 deletions src/spec/integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,103 @@ test('fintoc.v2.transfers.return()', async (t) => {
t.is(result.json.transfer_id, returnData.transfer_id);
});

test('fintoc.v2.transferBatches.list()', async (t) => {
const ctx: any = t.context;
const transferBatches = await ctx.fintoc.v2.transferBatches.list();

let count = 0;
for await (const transferBatch of transferBatches) {
count += 1;
t.is(transferBatch.method, 'get');
t.is(transferBatch.url, 'v2/transfer_batches');
}

t.true(count > 0);
});

test('fintoc.v2.transferBatches.list() with filters', async (t) => {
const ctx: any = t.context;
const filters = {
status: ['succeeded', 'partially_succeeded'],
since: '2025-01-01',
until: '2025-01-31',
};
const transferBatches = await ctx.fintoc.v2.transferBatches.list(filters);

let count = 0;
for await (const transferBatch of transferBatches) {
count += 1;
t.is(transferBatch.method, 'get');
t.is(transferBatch.url, 'v2/transfer_batches');
t.deepEqual(transferBatch.params.status, filters.status);
t.deepEqual(transferBatch.params.since, new Date(filters.since));
t.deepEqual(transferBatch.params.until, new Date(filters.until));
}

t.true(count > 0);
});

test('fintoc.v2.transferBatches.get()', async (t) => {
const ctx: any = t.context;
const transferBatchId = 'transfer_batch_id';
const transferBatch = await ctx.fintoc.v2.transferBatches.get(transferBatchId);

t.is(transferBatch.method, 'get');
t.is(transferBatch.url, `v2/transfer_batches/${transferBatchId}`);
});

test('fintoc.v2.transferBatches.create()', async (t) => {
const ctx: any = t.context;
const transferBatchData = {
description: 'Payroll',
currency: 'MXN',
transfers: [
{
amount: 10000,
account_id: 'acc_123',
counterparty: {
account_number: '012969100000000026',
},
},
],
};
const transferBatch = await ctx.fintoc.v2.transferBatches.create(transferBatchData);

t.is(transferBatch.method, 'post');
t.is(transferBatch.url, 'v2/transfer_batches');
t.is(transferBatch.json.description, transferBatchData.description);
t.is(transferBatch.json.currency, transferBatchData.currency);
t.is(transferBatch.json.transfers[0].amount, transferBatchData.transfers[0].amount);
t.is(
transferBatch.json.transfers[0].counterparty.account_number,
transferBatchData.transfers[0].counterparty.account_number,
);
t.true(transferBatch.headers['Idempotency-Key'] && transferBatch.headers['Idempotency-Key'].length > 0);
});

test('fintoc.v2.transferBatches.create() with idempotency key', async (t) => {
const ctx: any = t.context;
const transferBatchData = {
description: 'Payroll',
currency: 'MXN',
idempotency_key: 'idempotency_key_123',
transfers: [
{
amount: 10000,
account_id: 'acc_123',
counterparty: {
account_number: '012969100000000026',
},
},
],
};
const transferBatch = await ctx.fintoc.v2.transferBatches.create(transferBatchData);

t.is(transferBatch.method, 'post');
t.is(transferBatch.url, 'v2/transfer_batches');
t.is(transferBatch.headers['Idempotency-Key'], transferBatchData.idempotency_key);
});

test('fintoc.v2.simulate.receiveTransfer()', async (t) => {
const ctx: any = t.context;
const transferData = {
Expand Down
Loading