diff --git a/README.md b/README.md index 65e387a..45d6faa 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,10 @@ For PCI compliance to be maintained, tokenization of credit cards info should be + [Get Transaction](#get-transaction) + [List Transactions](#list-transactions) * [XenPlatform Service](#xenplatform-service) - + [Create sub-accounts](#create-sub-accounts) + + [Create sub-account](#create-sub-account) + + [Create sub-account using V2](#create-sub-account-using-v2) + + [Get sub-account by ID](#get-sub-account-by-id) + + [Update sub-account](#update-sub-account) + [Set Callback URL](#set-callback-url) + [Create transfers](#create-transfers) + [Create fee rules](#create-fee-rules) @@ -1291,7 +1294,7 @@ p.createAccount({ Refer to [Xendit API Reference](https://developers.xendit.co/api-reference/#xenplatform) for more info about methods' parameters -#### Create sub-accounts +#### Create sub-account ```ts p.createAccount(data: { @@ -1303,6 +1306,38 @@ p.createAccount(data: { }) ``` +#### Create sub-account using V2 + +```ts +p.createV2Account(data: { + email: string; + type: string; + publicProfile?: { + businessName: string; + }; +}) +``` + +#### Get sub-account by ID + +```ts +p.getAccountByID(data: { + id: string; +}) +``` + +#### Update sub-account + +```ts +p.updateAccount(data: { + id: string; + email: string; + publicProfile?: { + businessName: string; + }; +}) +``` + #### Set Callback URL ```ts diff --git a/examples/with_async/platform.js b/examples/with_async/platform.js index aceaa6f..eb50383 100644 --- a/examples/with_async/platform.js +++ b/examples/with_async/platform.js @@ -49,6 +49,35 @@ const p = new Platform({}); // eslint-disable-next-line no-console console.log('created fee rule detail:', feeRule); + const accountV2 = await p.createV2Account({ + email: `example+${Date.now().toString()}@gmail.com`, + type: 'OWNED', + publicProfile: { + businessName: `example+${Date.now().toString()}`, + }, + }); + + // eslint-disable-next-line no-console + console.log('created account details (using V2):', accountV2); + + const getAccount = await p.getAccountByID({ + id: accountV2.id, + }); + + // eslint-disable-next-line no-console + console.log('get account details: ', getAccount); + + const updateAccount = await p.updateAccount({ + id: accountV2.id, + email: `example_updated+${Date.now().toString()}@gmail.com`, + publicProfile: { + businessName: `example_updated+${Date.now().toString()}`, + }, + }); + + // eslint-disable-next-line no-console + console.log('update account details: ', updateAccount); + process.exit(0); } catch (e) { console.error(e); // eslint-disable-line no-console diff --git a/examples/with_promises/platform.js b/examples/with_promises/platform.js index 994a74e..cfb3edf 100644 --- a/examples/with_promises/platform.js +++ b/examples/with_promises/platform.js @@ -58,6 +58,44 @@ p.createAccount({ console.log('created fee rule detail:', r); return r; }) + .then(() => + p.createV2Account({ + email: `example+${Date.now().toString()}@gmail.com`, + type: 'OWNED', + publicProfile: { + businessName: `example+${Date.now().toString()}`, + }, + }), + ) + .then(r => { + // eslint-disable-next-line no-console + console.log('created account details (using V2):', r); + return r; + }) + .then(r => + p.getAccountByID({ + id: r.id, + }), + ) + .then(r => { + // eslint-disable-next-line no-console + console.log('get account details: ', r); + return r; + }) + .then(r => + p.updateAccount({ + id: r.id, + email: `example_updated+${Date.now().toString()}@gmail.com`, + publicProfile: { + businessName: `example_updated+${Date.now().toString()}`, + }, + }), + ) + .then(r => { + // eslint-disable-next-line no-console + console.log('update account details: ', r); + return r; + }) .catch(e => { console.error(e); // eslint-disable-line no-console process.exit(1); diff --git a/integration_test/platform.test.js b/integration_test/platform.test.js index 3ea7559..fabf5fc 100644 --- a/integration_test/platform.test.js +++ b/integration_test/platform.test.js @@ -40,6 +40,29 @@ module.exports = function() { ], }), ) + .then(() => + p.createV2Account({ + email: `example+${Date.now().toString()}@gmail.com`, + type: 'OWNED', + publicProfile: { + businessName: `example+${Date.now().toString()}`, + }, + }), + ) + .then(r => + p.getAccountByID({ + id: r.id, + }), + ) + .then(r => + p.updateAccount({ + id: r.id, + email: `example_updated+${Date.now().toString()}@gmail.com`, + publicProfile: { + businessName: `example_updated+${Date.now().toString()}`, + }, + }), + ) .then(() => { // eslint-disable-next-line no-console console.log('Platform integration test done...'); diff --git a/package.json b/package.json index fb12212..952b112 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xendit-node", - "version": "1.19.8", + "version": "1.19.9", "description": "NodeJS client for Xendit API", "main": "index.js", "types": "index.d.ts", diff --git a/src/platform/platform.d.ts b/src/platform/platform.d.ts index 6ee45b4..4c6dca4 100644 --- a/src/platform/platform.d.ts +++ b/src/platform/platform.d.ts @@ -21,6 +21,21 @@ export = class Platform { businessName: string; }; }): Promise; + createV2Account(data: { + email: string; + type: string; + publicProfile?: { + businessName: string; + }; + }): Promise; + getAccountByID(data: { id: string }): Promise; + updateAccount(data: { + id: string; + email: string; + publicProfile?: { + businessName: string; + }; + }): Promise; setCallbackURL(data: { type: string; url: string; diff --git a/src/platform/platform.js b/src/platform/platform.js index ab2922b..624b1d0 100644 --- a/src/platform/platform.js +++ b/src/platform/platform.js @@ -49,6 +49,73 @@ Platform.prototype.createAccount = function(data) { }); }; +Platform.prototype.createV2Account = function(data) { + return promWithJsErr((resolve, reject) => { + let validationFields = ['email', 'type']; + if (data.type === 'OWNED') { + validationFields.push('publicProfile'); + } + Validate.rejectOnMissingFields(validationFields, data, reject); + const body = { email: data.email, type: data.type }; + if (data.publicProfile) { + body.public_profile = { + business_name: data.publicProfile.businessName, + }; + } + fetchWithHTTPErr(`${this.API_ENDPOINT}/v2/accounts`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: Auth.basicAuthHeader(this.opts.secretKey), + }, + body: JSON.stringify(body), + }) + .then(resolve) + .catch(reject); + }); +}; + +Platform.prototype.getAccountByID = function(data) { + return promWithJsErr((resolve, reject) => { + Validate.rejectOnMissingFields(['id'], data, reject); + fetchWithHTTPErr(`${this.API_ENDPOINT}/v2/accounts/${data.id}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: Auth.basicAuthHeader(this.opts.secretKey), + }, + }) + .then(resolve) + .catch(reject); + }); +}; + +Platform.prototype.updateAccount = function(data) { + return promWithJsErr((resolve, reject) => { + let validationFields = ['email', 'id']; + if (data.type === 'OWNED') { + validationFields.push('publicProfile'); + } + Validate.rejectOnMissingFields(validationFields, data, reject); + const body = { email: data.email }; + if (data.publicProfile) { + body.public_profile = { + business_name: data.publicProfile.businessName, + }; + } + fetchWithHTTPErr(`${this.API_ENDPOINT}/v2/accounts/${data.id}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + Authorization: Auth.basicAuthHeader(this.opts.secretKey), + }, + body: JSON.stringify(body), + }) + .then(resolve) + .catch(reject); + }); +}; + Platform.prototype.setCallbackURL = function(data) { return promWithJsErr((resolve, reject) => { Validate.rejectOnMissingFields(['type', 'url'], data, reject); diff --git a/test/platform/constants.js b/test/platform/constants.js index b391173..7ff2c5d 100644 --- a/test/platform/constants.js +++ b/test/platform/constants.js @@ -1,4 +1,8 @@ const ACCOUNT_EMAIL = 'angie@pinkpanther.com'; +const ID = '61fb6d4d92b2ff75d2b45059'; +const BUSINESS_NAME = 'angies pink panther'; +const UPDATED_EMAIL = 'angie_updated@pinkpanther.com'; +const UPDATED_BUSINESS_NAME = 'angies updated pink panther'; const TYPE = 'MANAGED'; const URL = 'https://www.xendit.co/callback_catcher'; const CALLBACK_TYPE = 'invoice'; @@ -15,6 +19,14 @@ const ROUTES = [ }, ]; +const PUBLIC_PROFILE = { + business_name: BUSINESS_NAME, +}; + +const UPDATED_PUBLIC_PROFILE = { + business_name: UPDATED_BUSINESS_NAME, +}; + const VALID_CREATE_ACCOUNT_RESPONSE = { created: '2019-01-01T08:51:44.484Z', status: 'SUCCESSFUL', @@ -51,9 +63,54 @@ const VALID_CREATE_FEE_RULE_RESPONSE = { metadata: {}, }; +const VALID_CREATE_V2_ACCOUNT_RESPONSE = { + id: ID, + created: '2022-02-01T07:00:00.000Z', + updated: '2022-02-01T07:00:00.000Z', + email: ACCOUNT_EMAIL, + type: TYPE, + public_profile: { + business_name: BUSINESS_NAME, + }, + country: 'ID', + status: 'REGISTERED', +}; + +const VALID_GET_ACCOUNT_RESPONSE = { + id: ID, + created: '2022-02-01T07:00:00.000Z', + updated: '2022-02-01T07:00:00.000Z', + email: ACCOUNT_EMAIL, + type: TYPE, + public_profile: { + business_name: BUSINESS_NAME, + }, + country: 'ID', + status: 'REGISTERED', +}; + +const VALID_UPDATE_ACCOUNT_RESPONSE = { + id: ID, + created: '2022-02-01T07:00:00.000Z', + updated: '2022-02-01T07:00:00.000Z', + email: UPDATED_EMAIL, + type: TYPE, + public_profile: { + business_name: UPDATED_BUSINESS_NAME, + }, + country: 'ID', + status: 'REGISTERED', +}; + module.exports = { + ID, ACCOUNT_EMAIL, TYPE, + UPDATED_EMAIL, + BUSINESS_NAME, + UPDATED_BUSINESS_NAME, + PUBLIC_PROFILE, + UPDATED_PUBLIC_PROFILE, URL, CALLBACK_TYPE, REFERENCE, @@ -66,4 +123,7 @@ module.exports = { VALID_SET_CALLBACK_URL_RESPONSE, VALID_CREATE_TRANSFER_RESPONSE, VALID_CREATE_FEE_RULE_RESPONSE, + VALID_CREATE_V2_ACCOUNT_RESPONSE, + VALID_GET_ACCOUNT_RESPONSE, + VALID_UPDATE_ACCOUNT_RESPONSE, }; diff --git a/test/platform/platform.test.js b/test/platform/platform.test.js index 227104e..034fa64 100644 --- a/test/platform/platform.test.js +++ b/test/platform/platform.test.js @@ -43,6 +43,22 @@ before(function() { routes: TestConstants.ROUTES, }) .reply(200, TestConstants.VALID_CREATE_FEE_RULE_RESPONSE); + nock(x.opts.xenditURL) + .post('/v2/accounts', { + email: TestConstants.ACCOUNT_EMAIL, + type: TestConstants.TYPE, + public_profile: TestConstants.PUBLIC_PROFILE, + }) + .reply(200, TestConstants.VALID_CREATE_V2_ACCOUNT_RESPONSE); + nock(x.opts.xenditURL) + .get(`/v2/accounts/${TestConstants.ID}`) + .reply(200, TestConstants.VALID_GET_ACCOUNT_RESPONSE); + nock(x.opts.xenditURL) + .patch(`/v2/accounts/${TestConstants.ID}`, { + email: TestConstants.UPDATED_EMAIL, + public_profile: TestConstants.UPDATED_PUBLIC_PROFILE, + }) + .reply(200, TestConstants.VALID_UPDATE_ACCOUNT_RESPONSE); }); describe('Platform Service', function() { @@ -70,6 +86,83 @@ describe('Platform Service', function() { .catch(done); }); }); + describe('createV2Account', () => { + it('should create a sub-account using the V2 endpoint', done => { + expect( + platform.createV2Account({ + email: TestConstants.ACCOUNT_EMAIL, + type: TestConstants.TYPE, + publicProfile: { + businessName: TestConstants.BUSINESS_NAME, + }, + }), + ) + .to.eventually.deep.eq(TestConstants.VALID_CREATE_V2_ACCOUNT_RESPONSE) + .then(() => done()) + .catch(done); + }); + it('should report missing required fields', done => { + expect(platform.createV2Account({})) + .to.eventually.be.rejected.then(e => + Promise.all([ + expect(e).to.have.property('status', 400), + expect(e).to.have.property('code', Errors.API_VALIDATION_ERROR), + ]), + ) + .then(() => done()) + .catch(done); + }); + }); + describe('getAccountByID', () => { + it('should get a sub-account using the V2 endpoint', done => { + expect( + platform.getAccountByID({ + id: TestConstants.ID, + }), + ) + .to.eventually.deep.eq(TestConstants.VALID_GET_ACCOUNT_RESPONSE) + .then(() => done()) + .catch(done); + }); + it('should report missing required fields', done => { + expect(platform.getAccountByID({})) + .to.eventually.be.rejected.then(e => + Promise.all([ + expect(e).to.have.property('status', 400), + expect(e).to.have.property('code', Errors.API_VALIDATION_ERROR), + ]), + ) + .then(() => done()) + .catch(done); + }); + }); + describe('updateAccount', () => { + it('should update a sub-account using the V2 endpoint', done => { + expect( + platform.updateAccount({ + id: TestConstants.ID, + email: TestConstants.UPDATED_EMAIL, + publicProfile: { + businessName: TestConstants.UPDATED_BUSINESS_NAME, + }, + }), + ) + .to.eventually.deep.eq(TestConstants.VALID_UPDATE_ACCOUNT_RESPONSE) + .then(() => done()) + .catch(done); + }); + it('should report missing required fields', done => { + expect(platform.updateAccount({})) + .to.eventually.be.rejected.then(e => + Promise.all([ + expect(e).to.have.property('status', 400), + expect(e).to.have.property('code', Errors.API_VALIDATION_ERROR), + ]), + ) + .then(() => done()) + .catch(done); + }); + }); describe('setCallbackURL', () => { it('should set callback URL of an account', done => { expect(