//------------------------------------------------------------------------------------ //npm install --save axios moment dotenv const axios = require("axios"); const moment = require("moment"); require('dotenv').config() //------------------------------------------------------------------------------------ /* BEFORE RUNNING THIS EXAMPLE: * Set CONNECTFI_CLIENTID, CONNECTFI_PASSWORD, and CONNECTFI_BASE_URL in an .env file (Speak to a support representative to be issued client credentials and URL after receiving access to the sandbox.) * Set UNIQUE_REFERENCE_ID to a unique identifier. */ const CONNECTFI_CLIENTID = process.env.CONNECTFI_CLIENTID; const CONNECTFI_PASSWORD = process.env.CONNECTFI_PASSWORD; const CONNECTFI_BASE_URL = process.env.CONNECTFI_BASE_URL; const UNIQUE_REFERENCE_ID = "exampleRef1009"; //Update this value so that it is a unique ID before running //------------------------------------------------------------------------------------ //Get Authorization Token /* All other requests must have a valid authorization token in the request headers, so your first request in any workflow should be to /auth/get-token in order to receive an authorization token. A valid token should be included in the headers of all subsequent requests. */ async function getAuthToken() { const data = { "user": { "login": `${CONNECTFI_CLIENTID}`, "password": `${CONNECTFI_PASSWORD}` } }; const config = { method: 'POST', url: `${CONNECTFI_BASE_URL}/auth/get-token`, headers: { 'Content-Type': "application/json" }, data }; let result; try { result = await axios.request(config); if (result.status === 200) { return Promise.resolve(result.data.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //List of Billers /* This service offers bill payments via MasterCard Remote Payment and Presentment Service (RPPS). RPPS allows you to bill users and accept payments from users electronically and securely. A list of active billers can be retrieved by making a GET request to /transfer-to/bills/billers/download. The billers file is extremely large and it is recommended that this file not be downloaded more than once per day. */ async function getBillers(authToken) { const config = { method: 'GET', url: `${CONNECTFI_BASE_URL}/transfer-to/bills/billers/download`, headers: { 'x-connectfi-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.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Initiate Transaction /* The client initiates a bill payment transaction through a POST request to /transfer-to/bills/initiate. This endpoint will return a cFiTransactionId, which should be stored. At this point, the bill payment will have an "Initiated" status and the transaction details have been recorded in the database. Note that initiation alone will not cause a bill payment to post. */ async function initiateBillPayment(authToken, biller_id, amount, reference) { const data = { "billerId": biller_id, "accountNumber": "123456789", //"legalName": "Acme Inc", //Use instead of firstName and lastName if payer is a business "firstName": "Joe", "lastName": "Doe", "amount": amount, "currency": "USD", "deliveryTimeCode": "N", "webhookUrl": `https://your_webhook_url/${reference}`, "reference": reference, "narrative": "Test transaction" }; const config = { method: 'POST', url: `${CONNECTFI_BASE_URL}/transfer-to/bills/initiate`, headers: { 'Content-Type': "application/json", 'x-connectfi-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.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Execute Transaction /* In order to execute a specified bill payment, the cFiTransactionId should be sent in the request body of a POST request to /transfer-to/bills/execute. This request will register the payment for execution and will result in a status change to "Processing". */ async function executeTransaction(authToken, cFiTransactionId) { const data = { "cFiTransactionId": cFiTransactionId }; const config = { method: 'POST', url: `${CONNECTFI_BASE_URL}/transfer-to/bills/execute`, headers: { 'Content-Type': "application/json", 'x-connectfi-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.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Query /* If you have not received a webhook response with an expected status change within the expected time period, you may query the current status of up to 20 transactions at a time using a /transfer-to/bills/query request. It is recommended that you run a /transfer-to/bills/query request only once or twice per day. The conditions under which a /transfer-to/bills/query should be made are as follows: * A status change was expected to have already occurred (For example, a bill was executed with a same-day delivery time code before 9:30am ET. It is now 12 hours later and a status change to "Sent" was expected to have already occurred.) * A webhook response has not been received for the transaction. * Do not send multiple queries for the same transaction within a 12 hour period. If a query confirms that the status of a transaction has not moved when expected, contact customer support with the cFiTransactionId, current status, expected status, and date of execution. * If you have more than one transaction still in a "Processing" state after the expected SFTP file transfer time, do not send multiple queries with one transaction at a time. If you have more than one transaction to query, the queries should be sent in batches of 20 cFiTransactionIds/reference IDs per request. If you have less than 20 transactions to query, they can all be queried in a single request. */ async function query(authToken, cFiTransactionId) { const data = { "cFiTransactionIds": [cFiTransactionId] }; const config = { method: 'POST', url: `${CONNECTFI_BASE_URL}/transfer-to/bills/query`, headers: { 'Content-Type': "application/json", 'x-connectfi-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.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //List /* It is possible to list up to 1000 transactions at a time using search criteria with a /transfer-to/bills/list request. Search criteria may include a specific cFiTransactionId or reference ID, a date range, a status value, a settlement date, an exception date, or a combination of these search criteria. Use case examples for a /transfer-to/bills/list request include but are not limited to the following. * Listing all transactions that settled on 2023-05-18. * Listing all transactions that still have a "Sent" state with a date range of "2023-05-10T12:00:30.000Z" to "2023-05-11T12:00:00.000Z" * Listing all transactions that resulted in exceptions on 2023-05-15 */ async function list(authToken, status, numRecords, numToSkip) { const data = { "dateCreateFrom": `${new Date(moment(new Date()).add(-1, "hour")).toISOString()}`, "dateCreateTo": `${new Date(moment(new Date()).add(1, "hour")).toISOString()}`, "status": status, "numberOfRecords": numRecords, "skipRecords": numToSkip }; const config = { method: 'POST', url: `${CONNECTFI_BASE_URL}/transfer-to/bills/list`, headers: { 'Content-Type': "application/json", 'x-connectfi-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.data); } } catch (err) { console.log({ errCode: err.code, responseStatus: err.response && err.response.status, data: err.response && JSON.stringify(err.response.data) }); } } //------------------------------------------------------------------------------------ //Settlement Summary /* It is possible to list up to 1000 wire transfers at a time using search criteria with a /transfer-to/wire/list request. Search criteria may include a date range, a status value, or a combination of these search criteria. Use case examples for a /transfer-to/wire/list request include but are not limited to the following. * Listing all wire transfers that still have a "Sent" status. * Listing all transactions that have a "Declined" status with a date range of "2023-05-10T12:00:30.000Z" to "2023-05-11T12:00:00.000Z". * Listing all transactions that have a "Completed" status in batches of 150. (You can use the "numberOfRecords" property and the "skipRecords" property to list up to 1000 transfers at a time or to skip a certain number of transfers.) */ async function list(authToken, status, numRecords, numToSkip) { const data = { "dateCreateFrom": `${new Date(moment(new Date()).add(-1, "hour")).toISOString()}`, "dateCreateTo": `${new Date(moment(new Date()).add(1, "hour")).toISOString()}`, "status": status, "numberOfRecords": numRecords, "skipRecords": numToSkip }; const config = { method: 'POST', url: `${CONNECTFI_BASE_URL}/transfer-to/bills/list`, headers: { 'Content-Type': "application/json", 'x-connectfi-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.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 billsWalkthrough() { //Get Authorization Token console.log(`Start /auth/get-token example.\n`); const authObject = await getAuthToken(); let authToken = undefined; if (authObject) { console.log(`Successfully obtained authorization: ${JSON.stringify(authObject)}\n`); authToken = authObject.token; console.log(`Authorization token: ${authToken}\n`); } else { console.log(`Error getting authorization token\n`) } console.log(`End /auth/get-token example.\n`); //Get Billers console.log(`Start /transfer-to/bills/billers/download example. Please wait...\n`); let billersResult; if (authToken) { billersResult = await getBillers(authToken); } else { console.log(`Authorization token is required.\n`) } if (billersResult) { console.log(`Successfully retrieved billers list. The first biller entry is: ${JSON.stringify(billersResult[0])}\n`); } console.log(`End /transfer-to/bills/billers/download example.\n`); //Initiate Transaction console.log(`Start /transfer-to/bills/initiate example.\n`); let initiateTransactionResult; if (authToken && billersResult && billersResult[0]) { initiateTransactionResult = await initiateBillPayment(authToken, billersResult[0].biller_id, 16.53, UNIQUE_REFERENCE_ID); } else { console.log(`Authorization token and a valid entry from billersResult are required.\n`) } if (initiateTransactionResult) { console.log(`Successfully created bill payment: ${JSON.stringify(initiateTransactionResult)}\n`); } console.log(`End /transfer-to/bills/initiate example.\n`); //Execute Transaction console.log(`Start /transfer-to/bills/execute example.\n`); let executeTransactionResult; if (authToken && initiateTransactionResult && initiateTransactionResult.cFiTransactionId) { executeTransactionResult = await executeTransaction(authToken, initiateTransactionResult.cFiTransactionId); } else { console.log(`Authorization token and cFiTransactionId are required.\n`) } if (executeTransactionResult) { console.log(`Successfully executed bill payment: ${JSON.stringify(executeTransactionResult)}\n`); } console.log(`End /transfer-to/bills/execute example.\n`); //Query console.log(`Start /transfer-to/bills/query.\n`); let billsQueryResult; if (authToken && executeTransactionResult && executeTransactionResult.cFiTransactionId) { billsQueryResult = await query(authToken, executeTransactionResult.cFiTransactionId); } else { console.log(`Authorization token and cFiTransactionId are required.\n`) } if (billsQueryResult) { console.log(`Successfully queried bills: ${JSON.stringify(billsQueryResult)}\n`); } console.log(`End /transfer-to/bills/query.\n`); //List console.log(`Start transfer-to/bills/list example.\n`); let listBillsResult; if (authToken) { listBillsResult = await list(authToken, "Processing", 5, 0); } else { console.log(`Authorization token is required.\n`) } if (listBillsResult) { console.log(`Successfully retrieved list of bills matching criteria: ${JSON.stringify(listBillsResult)}\n`); } console.log(`End transfer-to/bills/list example.\n`); } if (process.env.CONNECTFI_CLIENTID && process.env.CONNECTFI_PASSWORD && process.env.CONNECTFI_BASE_URL) { billsWalkthrough(); } else { console.log("Before running the walkthrough, set the required .env variables."); }