//------------------------------------------------------------------------------------ //npm install --save axios dotenv lodash crypto-js const axios = require("axios"); require('dotenv').config() const lodash = require("lodash"); const CryptoJS = require("crypto-js"); //------------------------------------------------------------------------------------ /* BEFORE RUNNING THIS EXAMPLE: * Set AMPLIFI_BASE_URL, AMPLIFI_TEST_DEVICETAG, and SERVER_SECRET in an .env file. (Speak to a support representative to be issued client credentials and URL after receiving access to the sandbox.) * AMPLIFI_TEST_DEVICETAG and SERVER_SECRET are obtained from onboarding a prospect. */ const AMPLIFI_BASE_URL = process.env.AMPLIFI_BASE_URL; const SERVER_SECRET = process.env.SERVER_SECRET; //obtained when onboarding prospect const DEVICE_TAG = process.env.AMPLIFI_TEST_DEVICETAG; //use same deviceTag from onboarding prospect const dtsValueString = (new Date()).valueOf().toString(); const INT_TAG = lodash.padStart(dtsValueString, 10, "0") + Math.floor(Math.random() * 1000); //Unique tag to identify a generic request and avoid duplicates, typically a string of digits const halfRef = `some1RANDOM2string${(dtsValueString / 1).toString()}`; const cryptotext = CryptoJS.AES.encrypt(DEVICE_TAG + dtsValueString, SERVER_SECRET).toString(); const credentialsTestChannel = { channel: "test", deviceTag: DEVICE_TAG, deviceData: { "platform": "test" } }; const credentialsAndroidChannel = { "channel": "android_v1", "dtsValueString": dtsValueString, "deviceTag": DEVICE_TAG, "socket": { halfRef: halfRef }, "cryptotext": cryptotext }; const credentialsIOSChannel = { "channel": "ios_v1", "dtsValueString": dtsValueString, "deviceTag": DEVICE_TAG, "socket": { halfRef: halfRef }, "cryptotext": cryptotext }; const credentialsBrowserChannel = { "channel": "browser", "deviceId": DEVICE_TAG }; //------------------------------------------------------------------------------------ //Get Authorization Token /* The ampliFi system exposes a REST API and expects calls directly from the front-end. After onboarding, ampliFi authenticates each individual customer and performs transactions only explicitly allowed to that specific customer. Requests must have a valid authorization token in the request headers, so your first request in any workflow should be to /token in order to receive an authorization token. A valid token should be included in the headers of all subsequent requests. */ async function getAuthToken(credentials) { const data = credentials; const config = { method: 'PUT', url: `${AMPLIFI_BASE_URL}/token`, headers: { 'Content-Type': "application/json" }, data }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Open Account /* A new account may be requested using the "newCurrentAccount" generic request handler and the PUT /requests endpoint. Opening cards and accounts is a segment specific process, so it is handled through a generic request. Generic Requests can be defined to open vAccounts for businesses or individuals, debit cards for businesses or individuals, or credit cards for individuals. In addition, cards or accounts can be specific to family members, business representatives, employees, etc. This is just an illustrative example of one type of account that can be opened. */ async function openAccount(authToken, intTag) { const data = { typeId: "newCurrentAccount", intTag } const config = { method: 'PUT', url: `${AMPLIFI_BASE_URL}/requests`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required }, data }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //List Accounts /* The GET /accounts endpoint will allow you to get an array of records for all accounts belonging to the currently logged in user. */ async function listAccounts(authToken) { const config = { method: 'GET', url: `${AMPLIFI_BASE_URL}/accounts`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required } }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Get Account /* The GET /accounts/:AFiAccountId endpoint will retrieve the record for one specific account. The account is identified by the path parameter and must belong to the current user. */ async function getAccount(authToken, AFiAccountId) { const config = { method: 'GET', url: `${AMPLIFI_BASE_URL}/accounts/${AFiAccountId}`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required } }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Modify Account /* The POST /accounts/:AFiAccountId endpoint may be used to make changes to an account's properties. The account is identified by the path parameter, AFiAccountId. Only the properties to be modified should be specified in the payload, and the ampliFi API will only add or update the permitted (editable) properties. */ async function modifyAccount(authToken, AFiAccountId) { const data = { "name": "MainAccount", "isMain": true, "isHidden": false }; const config = { method: 'POST', url: `${AMPLIFI_BASE_URL}/accounts/${AFiAccountId}`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required }, data }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Close Account /* The DELETE /accounts/:AFiAccountId endpoint closes the specified account. The account is identified by the path parameter and must belong to the current user. If the closed account was the main account, this will assign another account as the main. If the account has a balance, it will be transferred to the new main account. If the user has no other accounts, this API will mark the user as inactive and will fire an event with the message: "Account closed money needs to be returned." */ async function closeAccount(authToken, AFiAccountId) { const config = { method: 'DELETE', url: `${AMPLIFI_BASE_URL}/accounts/${AFiAccountId}`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required } }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Get Recent Transactions /* The GET /accounts/:AFiAccountId/transactions/latest endpont returns an array containing records of the last 30 transactions for a specified account, or all transactions within the last 12 months, whichever is fewer. The account is identified by the path parameter AFiAccountId. */ async function getRecentTransactions(authToken, AFiAccountId) { const config = { method: 'GET', url: `${AMPLIFI_BASE_URL}/accounts/${AFiAccountId}/transactions/latest`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required } }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Get Transactions by Date Range /* The GET /accounts/:AFiAccountId/transactions/:dateStart/:dateEnd endpont an array containing records of all transactions for a specified account for a given time period. The account is identified by the path parameter AFiAccountId. The time period is defined by the path parameters dateStart and dateEnd. The dates are given in ISO date format, and the period runs from dateStart to dateEnd, inclusive. Transaction records include various fields depending on their nature, status, and circumstances. The format used for :dateStart and :dateEnd is the number of milliseconds since midnight January 1, 1970, obtained by new Date(//desired date).valueOf() */ async function getTransactionsByRange(authToken, AFiAccountId, dateStart, dateEnd) { const config = { method: 'GET', url: `${AMPLIFI_BASE_URL}/accounts/${AFiAccountId}/transactions/${dateStart}/${dateEnd}`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required } }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Modify Transaction /* The POST /account/:AFiAccountId/transaction/:transactionId endpont makes changes to a transaction record, as long as the specified items can be changed. The account is identified by the path parameter AFiAccountId, and the transaction is identified by the path parameter transactionId. NOTE: "account" may also be given as "accounts", and "transaction" may also be given as "transactions". */ async function modifyTransaction(authToken, AFiAccountId, transactionId) { const data = { "changes": { "comment": "test123", "labels": ["test123"] } }; const config = { method: 'POST', url: `${AMPLIFI_BASE_URL}/account/${AFiAccountId}/transaction/${transactionId}`, headers: { 'Content-Type': "application/json", 'token': authToken //previously obtained authorization token is required }, data }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Run the walkthrough async function ampliFiAccountsWalkthrough() { //Get Authorization Token console.log(`Start /token example.\n`); const authObjectTest = await getAuthToken(credentialsTestChannel); let authToken = undefined; if (authObjectTest) { console.log(`Successfully obtained user authorization: ${JSON.stringify(authObjectTest)}\n`); authToken = authObjectTest.token; console.log(`Authorization token: ${authToken}\n`); } else { console.log(`Error getting authorization token\n`) } console.log(`End /token example.\n`); //Open Account console.log(`Start PUT /requests example.\n`); let openAccountsResult; if (authToken) { openAccountsResult = await openAccount(authToken, INT_TAG); } else { console.log(`Authorization token is required.\n`) } if (openAccountsResult) { console.log(`Successfully requested new account to be opened: ${JSON.stringify(openAccountsResult)}\n`); } console.log(`End PUT /requests example.\n`); //List Accounts console.log(`Start GET /accounts example.\n`); let listAccountsResult; let AFiAccountId if (authToken) { listAccountsResult = await listAccounts(authToken); } else { console.log(`Authorization token is required.\n`) } if (listAccountsResult) { console.log(`Successfully listed accounts (if any): ${JSON.stringify(listAccountsResult)}\n`); AFiAccountId = listAccountsResult.accounts[0] ? listAccountsResult.accounts[0].AFiAccountId : undefined; console.log(AFiAccountId ? `AFiAccountId: ${AFiAccountId}\n` : `No accounts found.`) } console.log(`End GET /accounts example.\n`); //Get Account console.log(`Start GET /accounts/:AFiAccountId example.\n`); let getAccountResult; if (authToken && AFiAccountId) { getAccountResult = await getAccount(authToken, AFiAccountId); } else { console.log(`Authorization token and AFiAccountId are required.\n`) } if (getAccountResult) { console.log(`Successfully found account: ${JSON.stringify(getAccountResult)}\n`); } console.log(`End GET /accounts/:AFiAccountId example.\n`); //Modify Account console.log(`Start POST /accounts/:AFiAccountId example.\n`); let modifyAccountResult; if (authToken && AFiAccountId) { modifyAccountResult = await modifyAccount(authToken, AFiAccountId); } else { console.log(`Authorization token and AFiAccountId are required.\n`) } if (modifyAccountResult) { console.log(`Successfully modified account: ${JSON.stringify(modifyAccountResult)}\n`); } console.log(`End POST /accounts/:AFiAccountId example.\n`); //Get Recent Transactions console.log(`Start GET /accounts/:AFiAccountId/transactions/latest example.\n`); let getRecentTransactionsResult; if (authToken && AFiAccountId) { getRecentTransactionsResult = await getRecentTransactions(authToken, AFiAccountId); } else { console.log(`Authorization token and AFiAccountId are required.\n`) } if (getRecentTransactionsResult) { console.log(`Successfully retrieved recent transactions: ${JSON.stringify(getRecentTransactionsResult)}\n`); } console.log(`End GET /accounts/:AFiAccountId/transactions/latest example.\n`); //Get Transactions by Date Range console.log(`Start GET /accounts/:AFiAccountId/transactions/:dateStart/:dateEnd example.\n`); let getTransactionsByRangeResult; let transactionId; if (authToken && AFiAccountId) { getTransactionsByRangeResult = await getTransactionsByRange(authToken, AFiAccountId, new Date("2024-01-01").valueOf(), new Date("2024-05-28").valueOf()); } else { console.log(`Authorization token and AFiAccountId are required.\n`) } if (getTransactionsByRangeResult) { console.log(`Successfully retrieved transactions by date range: ${JSON.stringify(getTransactionsByRangeResult)}\n`); transactionId = getTransactionsByRangeResult[0] ? getTransactionsByRangeResult[0].transactionId : undefined; console.log(transactionId ? `transactionId: ${transactionId}\n` : `No transactions found.`) } console.log(`End GET /accounts/:AFiAccountId/transactions/:dateStart/:dateEnd example.\n`); //Modify Transaction console.log(`Start POST /account/:AFiAccountId/transaction/:transactionId example.\n`); let modifyTransactionResult; if (authToken && AFiAccountId && transactionId) { modifyTransactionResult = await modifyTransaction(authToken, AFiAccountId, transactionId); } else { console.log(`Authorization token, AFiAccountId, and transactionId are required.\n`) } if (modifyTransactionResult) { console.log(`Successfully pushed from external account: ${JSON.stringify(modifyTransactionResult)}\n`); } console.log(`End /POST /account/:AFiAccountId/transaction/:transactionId example.\n`); //Close Account /*console.log(`Start DELETE /accounts/:AFiAccountId example.\n`); let closeAccountResult; if (authToken && AFiAccountId) { closeAccountResult = await closeAccount(authToken, AFiAccountId); } else { console.log(`Authorization token and AFiAccountId are required.\n`) } if (closeAccountResult) { console.log(`Successfully closed account: ${JSON.stringify(closeAccountResult)}\n`); } console.log(`End DELETE /accounts/:AFiAccountId example.\n`); */ } if (process.env.AMPLIFI_BASE_URL) { ampliFiAccountsWalkthrough(); } else { console.log("Before running the walkthrough, set the required .env variables."); }