From db41d09aeba0bc6ebe325607567b8025e114e5b2 Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:34:49 -0500 Subject: [PATCH 1/9] draft --- src/resource.ts | 16 +++++---- src/resources/Account/types.ts | 5 +-- src/resources/Entity/Connect.ts | 54 ++++++++++++++++++++++++++++--- test/resources/Account.tests.ts | 57 +++++++++++++++++++++++---------- test/resources/Report.tests.ts | 4 +-- 5 files changed, 103 insertions(+), 33 deletions(-) diff --git a/src/resource.ts b/src/resource.ts index 5a5a447..d32b303 100644 --- a/src/resource.ts +++ b/src/resource.ts @@ -206,16 +206,18 @@ export default class Resource extends ExtensibleFunction { return (await this.client.get('', { params })).data.data; } - protected async _create( + protected async _create( data: Data, + params?: Params, requestConfig: IRequestConfig = {}, ): Promise { - const _requestConfig = { headers: {} }; - if (requestConfig.idempotency_key) { - _requestConfig.headers = { - 'Idempotency-Key': requestConfig.idempotency_key, - }; - } + const _requestConfig = { + headers: requestConfig.idempotency_key + ? { 'Idempotency-Key': requestConfig.idempotency_key } + : {}, + params, + }; + return (await this.client.post('', data, _requestConfig)).data.data; } diff --git a/src/resources/Account/types.ts b/src/resources/Account/types.ts index a4e26fe..9cc3024 100644 --- a/src/resources/Account/types.ts +++ b/src/resources/Account/types.ts @@ -299,7 +299,8 @@ export interface IAccountBalance { }; export interface IAccountCardBrandInfo { - art_id: string; + id: string; + art_id?: string; url: string; name: string; }; @@ -311,7 +312,7 @@ export interface IAccountCardBrand { issuer: string | null; last4: string | null; brands: IAccountCardBrandInfo[]; - status: 'completed' | 'failed'; + status: 'in_progress' | 'completed' | 'failed'; shared: boolean; source: 'method' | 'network' | null; error: IResourceError | null; diff --git a/src/resources/Entity/Connect.ts b/src/resources/Entity/Connect.ts index 35a3a4e..24ac9ac 100644 --- a/src/resources/Entity/Connect.ts +++ b/src/resources/Entity/Connect.ts @@ -2,6 +2,50 @@ import Resource, { IResourceListOpts } from '../../resource'; import Configuration, { IResponse } from '../../configuration'; import type { IEntityConnect } from './types'; +export const AccountExpandableFields = { + sensitive: 'sensitive', + balance: 'balance', + card_brand: 'card_brand', + attribute: 'attribute', + payoff: 'payoff', + transaction: 'transaction', + update: 'update', + payment_instrument: 'payment_instrument', + latest_verification_session: 'latest_verification_session', +} as const; + +type AccountExpandableField = typeof AccountExpandableFields[keyof typeof AccountExpandableFields]; + +export interface IExpandableOpts { + expand?: AccountExpandableField[]; +} + +export interface IConnectListOpts extends IResourceListOpts, IExpandableOpts {} + +export const AccountProductsEligibleForAutomaticExecution = [ + 'account_attribute', + 'balance', + 'card_brand', + 'update', + 'payoff', +] as const; + +export const AccountSubscriptionsEligibleForAutomaticExecution = [ + 'card_brand', + 'update', + 'update.snapshot', + 'transaction', +] as const; + +export type AccountProduct = typeof AccountProductsEligibleForAutomaticExecution[number]; +export type AccountSubscription = typeof AccountSubscriptionsEligibleForAutomaticExecution[number]; + +export interface IConnectCreateOpts { + products?: AccountProduct[]; + subscriptions?: AccountSubscription[]; +} + + export default class EntityConnect extends Resource { constructor(config: Configuration) { super(config.addPath('connect')); @@ -14,8 +58,8 @@ export default class EntityConnect extends Resource { * @returns Returns a Connect object. */ - async retrieve(cxn_id: string) { - return super._getWithId>(cxn_id); + async retrieve(cxn_id: string, opts: IExpandableOpts = {}) { + return super._getWithSubPathAndParams>(cxn_id, opts); } /** @@ -24,7 +68,7 @@ export default class EntityConnect extends Resource { * @returns Returns a list of Connect objects. */ - async list(opts?: IResourceListOpts) { + async list(opts?: IConnectListOpts) { return super._list>(opts); } @@ -34,7 +78,7 @@ export default class EntityConnect extends Resource { * @returns Returns a Connect object. */ - async create() { - return super._create, {}>({}); + async create(opts: IConnectCreateOpts = {}, params: IExpandableOpts = {}) { + return super._create, IConnectCreateOpts, IExpandableOpts>(opts, params); } }; diff --git a/test/resources/Account.tests.ts b/test/resources/Account.tests.ts index 4f3b656..9badbad 100644 --- a/test/resources/Account.tests.ts +++ b/test/resources/Account.tests.ts @@ -1,4 +1,4 @@ -import { should } from 'chai'; +import { should, expect } from 'chai'; import { describe } from 'mocha'; import { client } from '../config'; import { awaitResults } from '../utils'; @@ -299,7 +299,7 @@ describe('Accounts - core methods tests', () => { id: card_create_response.id, account_id: test_credit_card_account.id, network: 'visa', - status: 'completed', + status: 'in_progress', issuer: card_create_response.issuer, last4: '1580', brands: card_create_response.brands, @@ -319,19 +319,24 @@ describe('Accounts - core methods tests', () => { .cardBrands .retrieve(card_create_response.id); - const expect_results: IAccountCardBrand = { - id: card_create_response.id, - account_id: test_credit_card_account.id, - network: 'visa', - status: 'completed', - issuer: card_retrieve_response.issuer, - last4: '1580', - brands: card_create_response.brands, - shared: false, - source: card_retrieve_response.source, - error: null, - created_at: card_retrieve_response.created_at, - updated_at: card_retrieve_response.updated_at + expect(card_retrieve_response.id).to.equal(card_create_response.id); + expect(card_retrieve_response.account_id).to.equal(test_credit_card_account.id); + expect(card_retrieve_response.network).to.equal('visa'); + expect(card_retrieve_response.status).to.equal('completed'); + expect(card_retrieve_response.issuer).to.equal(card_create_response.issuer); + expect(card_retrieve_response.last4).to.equal('1580'); + expect(card_retrieve_response.shared).to.equal(false); + expect(card_retrieve_response.source).to.equal('network'); + expect(card_retrieve_response.error).to.be.null; + expect(card_retrieve_response.created_at).to.be.a('string'); + expect(card_retrieve_response.updated_at).to.be.a('string'); + + const brand = card_retrieve_response.brands?.[0]; + expect(brand).to.exist; + expect(brand.id).to.equal('brand_UBwVzXjpP4PJ6'); + expect(brand.name).to.equal('Chase Sapphire Reserve'); + expect(brand.url).to.equal('https://static.methodfi.com/card_brands/1b7ccaba6535cb837f802d968add4700.png'); + expect(brand.art_id).to.be.a('string').and.match(/^art_/); }; card_retrieve_response.should.be.eql(expect_results); @@ -346,8 +351,26 @@ describe('Accounts - core methods tests', () => { }; const card_brands = await awaitResults(listCardBrands); - - card_brands[0].should.be.eql(card_create_response); + const result = card_brands[0]; + + expect(result.id).to.equal(card_create_response.id); + expect(result.account_id).to.equal(test_credit_card_account.id); + expect(result.network).to.equal('visa'); + expect(result.status).to.equal('completed'); + expect(result.issuer).to.equal(card_create_response.issuer); + expect(result.last4).to.equal('1580'); + expect(result.shared).to.equal(false); + expect(result.source).to.equal('network'); + expect(result.error).to.be.null; + expect(result.created_at).to.be.a('string'); + expect(result.updated_at).to.be.a('string'); + + const brand = result.brands?.[0]; + expect(brand).to.exist; + expect(brand.id).to.equal('brand_UBwVzXjpP4PJ6'); + expect(brand.name).to.equal('Chase Sapphire Reserve'); + expect(brand.url).to.equal('https://static.methodfi.com/card_brands/1b7ccaba6535cb837f802d968add4700.png'); + expect(brand.art_id).to.be.a('string').and.match(/^art_/); }); }); diff --git a/test/resources/Report.tests.ts b/test/resources/Report.tests.ts index cda4257..1091f75 100644 --- a/test/resources/Report.tests.ts +++ b/test/resources/Report.tests.ts @@ -23,7 +23,7 @@ describe('Reports - core methods tests', () => { type: 'payments.created.current', url: `https://dev.methodfi.com/reports/${reports_create_response.id}/download`, status: 'completed', - metadata: null, + metadata: reports_create_response.metadata, created_at: reports_create_response.created_at, updated_at: reports_create_response.updated_at, }; @@ -41,7 +41,7 @@ describe('Reports - core methods tests', () => { type: 'payments.created.current', url: `https://dev.methodfi.com/reports/${reports_create_response.id}/download`, status: 'completed', - metadata: null, + metadata: reports_create_response.metadata, created_at: reports_retrieve_response.created_at, updated_at: reports_retrieve_response.updated_at, }; From dffbb8da41e69afed965c513aa903f26a38f1753 Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:34:55 -0500 Subject: [PATCH 2/9] draft --- test/resources/Account.tests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/resources/Account.tests.ts b/test/resources/Account.tests.ts index 9badbad..c189c56 100644 --- a/test/resources/Account.tests.ts +++ b/test/resources/Account.tests.ts @@ -311,6 +311,7 @@ describe('Accounts - core methods tests', () => { }; card_create_response.should.be.eql(expect_results); + await new Promise(r => setTimeout(r, 2000)) }); it('should successfully retrieve a card for an account.', async () => { @@ -337,7 +338,6 @@ describe('Accounts - core methods tests', () => { expect(brand.name).to.equal('Chase Sapphire Reserve'); expect(brand.url).to.equal('https://static.methodfi.com/card_brands/1b7ccaba6535cb837f802d968add4700.png'); expect(brand.art_id).to.be.a('string').and.match(/^art_/); - }; card_retrieve_response.should.be.eql(expect_results); }); From 07fd9d6696062a46c8acb46701ccbec9f70495d0 Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Thu, 12 Jun 2025 10:37:30 -0500 Subject: [PATCH 3/9] minor fix --- test/resources/Account.tests.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/resources/Account.tests.ts b/test/resources/Account.tests.ts index c189c56..8ce8f7a 100644 --- a/test/resources/Account.tests.ts +++ b/test/resources/Account.tests.ts @@ -338,8 +338,6 @@ describe('Accounts - core methods tests', () => { expect(brand.name).to.equal('Chase Sapphire Reserve'); expect(brand.url).to.equal('https://static.methodfi.com/card_brands/1b7ccaba6535cb837f802d968add4700.png'); expect(brand.art_id).to.be.a('string').and.match(/^art_/); - - card_retrieve_response.should.be.eql(expect_results); }); it('should successfully list card brands for an account.', async () => { From b9d89ac7eb255db4052a0717a9248a0e744ccb03 Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Thu, 12 Jun 2025 11:49:07 -0500 Subject: [PATCH 4/9] update types --- src/resources/Entity/Connect.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/resources/Entity/Connect.ts b/src/resources/Entity/Connect.ts index 24ac9ac..92aee55 100644 --- a/src/resources/Entity/Connect.ts +++ b/src/resources/Entity/Connect.ts @@ -2,6 +2,7 @@ import Resource, { IResourceListOpts } from '../../resource'; import Configuration, { IResponse } from '../../configuration'; import type { IEntityConnect } from './types'; + export const AccountExpandableFields = { sensitive: 'sensitive', balance: 'balance', @@ -14,10 +15,12 @@ export const AccountExpandableFields = { latest_verification_session: 'latest_verification_session', } as const; -type AccountExpandableField = typeof AccountExpandableFields[keyof typeof AccountExpandableFields]; +type AccountFieldKey = typeof AccountExpandableFields[keyof typeof AccountExpandableFields]; + +export type ExpandField = 'accounts' | `accounts.${AccountFieldKey}`; export interface IExpandableOpts { - expand?: AccountExpandableField[]; + expand?: ExpandField[]; } export interface IConnectListOpts extends IResourceListOpts, IExpandableOpts {} From d6dc0b1bedc006e006b1f9f058b14f9b30d5d94d Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:21:30 -0500 Subject: [PATCH 5/9] hmac_secret on IWebhookCreateOpts --- README.md | 1 + src/resources/Webhook/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/README.md b/README.md index 5955238..992eed4 100644 --- a/README.md +++ b/README.md @@ -933,6 +933,7 @@ const webhook = await method.webhooks.create({ type: 'payment.update', url: 'https://api.example.app/webhook', auth_token: 'md7UqcTSmvXCBzPORDwOkE', + hmac_secret: "bd7UscLSmvEXazTOQDwOKW", }); ``` diff --git a/src/resources/Webhook/index.ts b/src/resources/Webhook/index.ts index cdc5a43..f854de8 100644 --- a/src/resources/Webhook/index.ts +++ b/src/resources/Webhook/index.ts @@ -82,6 +82,7 @@ export interface IWebhookCreateOpts { type: TWebhookTypes; url: string; auth_token?: string; + hmac_secret?: string; metadata?: {}; expand_event?: boolean; }; From 899a3514e82ab789a953d37e5ab03c825bba18a6 Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:36:49 -0500 Subject: [PATCH 6/9] Apply suggestion from @sbilalh Co-authored-by: Bilal <91101351+sbilalh@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 992eed4..0404da2 100644 --- a/README.md +++ b/README.md @@ -933,7 +933,7 @@ const webhook = await method.webhooks.create({ type: 'payment.update', url: 'https://api.example.app/webhook', auth_token: 'md7UqcTSmvXCBzPORDwOkE', - hmac_secret: "bd7UscLSmvEXazTOQDwOKW", + hmac_secret: 'bd7UscLSmvEXazTOQDwOKW', }); ``` From 8e5a7d7bda0416eb0391906656aadebbb17eca6a Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:37:27 -0500 Subject: [PATCH 7/9] minor --- src/resources/Entity/Connect.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/Entity/Connect.ts b/src/resources/Entity/Connect.ts index 92aee55..42f7e10 100644 --- a/src/resources/Entity/Connect.ts +++ b/src/resources/Entity/Connect.ts @@ -2,7 +2,6 @@ import Resource, { IResourceListOpts } from '../../resource'; import Configuration, { IResponse } from '../../configuration'; import type { IEntityConnect } from './types'; - export const AccountExpandableFields = { sensitive: 'sensitive', balance: 'balance', From 533bc01a2feee513144f165078bedbb72151fa4f Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:38:55 -0500 Subject: [PATCH 8/9] bump patch --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65dc254..97ad415 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "method-node", - "version": "1.2.2", + "version": "1.2.3", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 0ab3b39..c7d05f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "method-node", - "version": "1.2.2", + "version": "1.2.3", "description": "Node.js library for the Method API", "main": "dist/index.ts", "module": "dist/index.mjs", From f6dc1cd105d3e6b656f2b6b14d92451982538cd5 Mon Sep 17 00:00:00 2001 From: Reza Tabrizi <63474681+rezabrizi@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:39:49 -0500 Subject: [PATCH 9/9] bump patch --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 97ad415..c86683e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "method-node", - "version": "1.2.3", + "version": "1.2.5", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index c7d05f4..94a861b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "method-node", - "version": "1.2.3", + "version": "1.2.5", "description": "Node.js library for the Method API", "main": "dist/index.ts", "module": "dist/index.mjs",