sila-sdk
Version:
SDK Access to the Sila API
1,625 lines (1,457 loc) • 59.9 kB
JavaScript
import randomBytes from 'randombytes';
import { ethers } from 'ethers'
import uuid4 from 'uuid4';
import fs from 'fs';
import crypt from 'crypto';
import lodash, { method } from 'lodash';
import regeneratorRuntime from 'regenerator-runtime'; // eslint-disable-line no-unused-vars
import axios from 'axios';
import FormData from 'form-data';
import TransactionFilters from './models/transactionFilters';
import User from './models/user';
import Wallet from './models/wallet';
import WalletFilters from './models/walletFilters';
import { post } from './utils/post';
import pkg from '../package.json';
let appKey = null;
let appHandle = null;
let sandbox = true;
let env = 'SANDBOX';
let baseUrl = 'https://sandbox.silamoney.com/0.2/';
let logging = false;
const url = (path) => baseUrl + path;
const getBalanceURL = () => {
let balanceURL = '';
switch (env) {
case 'PROD':
balanceURL = sandbox
? 'https://sandbox.silatokenapi.silamoney.com/silaBalance'
: 'https://silatokenapi.silamoney.com/silaBalance';
break;
default:
balanceURL = 'https://test.silatokenapi.silamoney.com/silaBalance';
}
return balanceURL;
};
/**
*
* @param {String} message The message to sign
* @param {*} key The key to sign the message with
*/
const sign = (message, key) => {
if (!appKey || !key) {
throw new Error('Unable to sign request: keys not set');
}
const hash = ethers.solidityPackedKeccak256(['string'], [message]);
const normalizeKey = key.startsWith('0x') ? key : '0x' + key;
const signingKey = new ethers.SigningKey(normalizeKey)
const signatureObject = signingKey.sign(hash)
const r = signatureObject.r.slice(2);
const s = signatureObject.s.slice(2);
const yParity = signatureObject.yParity === 1 ? '1c' : '1b';
const signature = `${r}${s}${yParity}`;
if (logging && env !== 'PROD') {
console.log('*** MESSAGE STRING ***');
console.log(message);
console.log('*** HASH ***');
console.log(hash);
console.log('*** SIGNING WITH KEY ***');
console.log(key);
console.log('*** SIGNATURE ***');
console.log(signature);
}
return signature;
};
const configureUrl = () => {
switch (env) {
case 'TEST':
baseUrl = 'https://test4api.silamoney.com/0.2/';
break;
case 'PROD':
baseUrl = 'https://api.silamoney.com/0.2/';
break;
case 'STAGE':
baseUrl = 'https://stageapi.silamoney.com/0.2/';
break;
default:
baseUrl = 'https://sandbox.silamoney.com/0.2/';
break;
}
};
/**
*
* @param {*} opts
* @param {String} key
* @param {String} businessPrivateKey
*/
const signOpts = (opts, key, businessPrivateKey) => {
const options = lodash.cloneDeep(opts);
if (opts.body.header) {
options.headers = {};
options.headers['User-Agent'] = `SilaSDK-node/${pkg.version}`;
const bodyString = JSON.stringify(options.body);
options.headers.authsignature = sign(bodyString, appKey);
if (key) options.headers.usersignature = sign(bodyString, key);
if (businessPrivateKey)
options.headers.businesssignature = sign(bodyString, businessPrivateKey);
}
return options;
};
/**
* Hashes a file
* @param {String} filePath The full path to the file
* @param {String} algorithm The algorithm of the hash
*/
const hashFile = (filePath, algorithm) => {
const promise = new Promise((res, rej) => {
const hash = crypt.createHash(algorithm);
const file = fs.createReadStream(filePath, { autoClose: true });
file
.on('data', (data) => {
hash.update(data);
})
.on('end', () => {
const digest = hash.digest('hex');
return res(digest);
})
.on('error', (error) => {
rej(error);
});
});
return promise;
};
/**
* Hashes a fileBuffer
* @param {Unit-8 Array} fileBuffer of the file
* @param {String} algorithm The algorithm of the hash
*/
var hashFileObject = function hashFileObject(fileBuffer, algorithm) {
var promise = new Promise(function (res, rej) {
var digest = crypt.createHash(algorithm).update(fileBuffer).digest('hex');
return res(digest);
});
return promise;
};
/**
*
* @param {Object} msg The header message
* @param {String} handle The user handle
* @param {String} businessHandle
*/
const setHeaders = (msg, handle, businessHandle) => {
const message = msg;
message.header.user_handle = handle;
message.header.business_handle = businessHandle;
message.header.app_handle = appHandle;
message.header.reference = uuid4();
message.header.created = Math.floor(Date.now() / 1000);
message.header.crypto = 'ETH';
message.header.version = '0.2';
return message;
};
const postFile = (options, filePath, fileObject) => {
const promise = new Promise((res, rej) => {
if (logging && env !== 'PROD') {
console.log('*** REQUEST ***');
console.log(options.body);
}
if (fileObject && Object.keys(fileObject).length !== 0) {
const data = new FormData();
data.append('data', JSON.stringify(options.body));
for (let key in fileObject) {
data.append(key, fileObject[key]);
}
const config = {
method: 'post',
url: options.uri,
headers: {
...options.headers,
...data.getHeaders() // Add form-data headers
},
data: data
};
axios(config)
.then(function (response) {
res({
statusCode: response.status || response.statusCode,
headers: response.headers,
data: response.data
});
})
.catch(function (error) {
res({
statusCode: error.response?.status || error.response?.statusCode,
headers: error.response?.headers,
data: error.response?.data
});
});
} else {
const formData = new FormData();
formData.append('data', JSON.stringify(options.body));
for (let key in filePath) {
formData.append(key, filePath[key]);
}
const config = {
method: 'post',
url: options.uri,
headers: {
...options.headers,
...formData.getHeaders() // Add form-data headers
},
data: formData
};
axios(config)
.then(function (response) {
res({
statusCode: response.status || response.statusCode,
headers: response.headers,
data: response.data
});
})
.catch(function (error) {
res({
statusCode: error.response?.status || error.response?.statusCode,
headers: error.response?.headers,
data: error.response?.data
});
});
}
});
return promise;
};
/**
*
* @param {String} path The path of the request
* @param {Object} body The body of the request
* @param {String} privateKey The user's private key
*/
const makeRequest = (
path,
body,
privateKey = undefined,
business_private_key = undefined,
requestMethod = 'post',
) => {
let opts = {
method: requestMethod,
uri: url(path),
json: true,
body,
};
if (path == 'get_document') {
opts['encoding'] = 'binary';
}
opts = signOpts(opts, privateKey, business_private_key);
return post(opts, env, logging);
};
const makeFileRequest = (path, body, filePath, fileObject, privateKey) => {
let opts = {
uri: url(path),
body,
};
opts = signOpts(opts, privateKey);
return postFile(opts, filePath, fileObject);
};
/**
* Returns the handle with the .silamoney.eth suffix if not present
* @param {String} handle The handle
*/
const getFullHandle = (handle) => {
let fullHandle = String(handle);
if (fullHandle && !fullHandle.endsWith('.silamoney.eth')) {
fullHandle += '.silamoney.eth';
}
return fullHandle;
};
/**
*
* @param {String} queryParameters The current query parameters
* @param {String} name The name of the query parameter
* @param {String} value The value of the query parameter
*/
const getQueryParameter = (queryParameters, name, value) => {
let newQueryParameters = queryParameters;
if (value !== undefined && value !== null) {
newQueryParameters += newQueryParameters.length > 0 ? '&' : '?';
newQueryParameters += `${name}=${value}`;
}
return newQueryParameters;
};
const getQueryParameters = (parameters) => {
let queryParameters = '';
if (parameters) {
queryParameters = getQueryParameter(
queryParameters,
'page',
parameters.page,
);
queryParameters = getQueryParameter(
queryParameters,
'per_page',
parameters.perPage,
);
queryParameters = getQueryParameter(
queryParameters,
'order',
parameters.order,
);
queryParameters = getQueryParameter(
queryParameters,
'sort_ascending',
parameters.sortAscending,
);
}
return queryParameters;
};
/**
* Makes a call to /check_handle endpoint.
* @param {String} handle The user handle to check if it's available
*/
const checkHandle = (handle) => {
const fullHandle = getFullHandle(handle);
const message = setHeaders({ header: {} }, fullHandle);
message.message = 'header_msg';
return makeRequest('check_handle', message);
};
/**
* Makes a call to /register endpoint.
* @param {User} user
*/
const register = (user) => {
const handle = getFullHandle(user.handle);
const message = setHeaders({ header: {} }, handle);
message.message = 'entity_msg';
if (
user.city ||
user.zip ||
user.state ||
user.address ||
user.addressAlias ||
user.addresAlias ||
user.address2 ||
user.country
) {
message.address = {};
message.address.city = user.city;
message.address.postal_code = user.zip;
message.address.state = user.state;
message.address.street_address_1 = user.address;
message.address.street_address_2 = user.address2;
message.address.address_alias = user.addressAlias
? user.addressAlias
: user.addresAlias;
message.address.country = user.country ? user.country : 'US';
}
if (user.contactAlias || user.phone || user.email) {
message.contact = {};
message.contact.contact_alias = user.contactAlias;
message.contact.phone = user.phone;
message.contact.email = user.email;
}
if (user.cryptoAddress || user.cryptoAlias) {
message.crypto_entry = {};
message.crypto_entry.crypto_address = user.cryptoAddress;
message.crypto_entry.crypto_code = 'ETH';
message.crypto_entry.crypto_alias = user.cryptoAlias;
}
if (
user.firstName ||
user.lastName ||
user.entity_name ||
user.business_type ||
user.businessTypeUuid ||
user.business_website ||
user.doing_business_as ||
user.naics_code
) {
message.entity = {};
message.entity.birthdate = user.dateOfBirth;
message.entity.first_name = user.firstName;
message.entity.last_name = user.lastName;
message.entity.entity_name = user.entity_name
? user.entity_name
: '';
message.entity.relationship = 'user';
if (user.type) message.entity.type = user.type;
else
message.entity.type =
user.business_type || user.businessTypeUuid ? 'business' : 'individual';
message.entity.business_type = user.business_type;
message.entity.business_website = user.business_website;
message.entity.doing_business_as = user.doing_business_as;
message.entity.naics_code = user.naics_code;
message.entity.business_type_uuid = user.businessTypeUuid;
message.entity.registration_state = user.registration_state;
}
if (user.id_document) {
message.id_document = {};
message.id_document.doc_type = user.doc_type
message.id_document.doc_id = user.doc_id
if (user.doc_state) message.id_document.doc_state = user.doc_state
if (user.doc_country) message.id_document.doc_country = user.doc_country
}
if (user.ssn || user.ein) {
message.identity = {};
message.identity.identity_value = user.ssn ? user.ssn : user.ein;
message.identity.identity_alias = user.ssn ? 'SSN' : 'EIN';
}
return makeRequest('register', message);
};
/**
* Makes a call to /request_kyc endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} kycLevel The custom kyc level
*/
const requestKYC = (handle, privateKey, kycLevel = undefined) => {
const fullHandle = getFullHandle(handle);
const message = setHeaders({ header: {} }, fullHandle);
message.message = 'header_msg';
if (kycLevel) message.kyc_level = kycLevel;
return makeRequest('request_kyc', message, privateKey);
};
/**
* Makes a call to /check_kyc endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} kycLevel The custom kyc level
*/
const checkKYC = (handle, privateKey, kycLevel = undefined) => {
const fullHandle = getFullHandle(handle);
const message = setHeaders({ header: {} }, fullHandle);
message.message = 'header_msg';
if (kycLevel) message.kyc_level = kycLevel;
return makeRequest('check_kyc', message, privateKey);
};
/**
* Makes a call to /link_account endpoint.
* This method handles the direct account link flow
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} accountNumber The account number
* @param {String} routingNumber The routing number
* @param {String} accountName The account nickname
* @param {String} accountType The account type
*/
const linkAccountDirect = (
handle,
privateKey,
accountNumber,
routingNumber,
accountName = undefined,
accountType = undefined,
) => {
const fullHandle = getFullHandle(handle);
const message = setHeaders({ header: {} }, fullHandle);
message.message = 'link_account_msg';
message.account_number = accountNumber;
message.routing_number = routingNumber;
if (accountType) message.account_type = accountType;
if (accountName) message.account_name = accountName;
return makeRequest('link_account', message, privateKey);
};
/**
* Makes a call to /link_account endpoint.
* This method handles the MX account link flow
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} providerTokenType The token type
* @param {String} providerToken The token itself
* @param {String} accountName The account nickname
* @param {String} accountId The account id
*/
const linkAccountMX = (
handle,
privateKey,
providerTokenType,
providerToken,
accountName = undefined,
accountId = undefined,
) => {
const fullHandle = getFullHandle(handle);
const message = setHeaders({ header: {} }, fullHandle);
message.message = 'link_account_msg';
message.provider = 'mx';
message.provider_token_type = providerTokenType;
message.provider_token = providerToken;
if (accountName) message.account_name = accountName;
if (accountId) message.selected_account_id = accountId;
return makeRequest('link_account', message, privateKey);
};
/**
* Makes a call to /link_account endpoint.
* This method handles the plaid's token flow.
* @param {String} handle The user hanlde
* @param {String} privateKey The user's wallet private key
* @param {String} publicToken Plaid's public token
* @param {String} accountName The account nickname
* @param {String} accountId The account id
*
*/
const linkAccount = (
handle,
privateKey,
plaidToken,
accountName = undefined,
accountId = undefined,
plaidTokenType = undefined,
) => {
const fullHandle = getFullHandle(handle);
const message = setHeaders({ header: {} }, fullHandle);
message.message = 'link_account_msg';
message.plaid_token = plaidToken;
message.plaid_token_type = plaidTokenType;
if (accountId) message.selected_account_id = accountId;
if (accountName) message.account_name = accountName;
return makeRequest('link_account', message, privateKey);
};
/**
* Makes a call to /issue_sila endpoint.
* @param {Number} amount The amount of sila tokens to issue
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} accountName The nickname of the account to debit from. It defaults to 'default' // Optional, OR "card_name": "default", never both.
* @param {String} descriptor Optional. Max Length 100. Note that only the first 10 characters show on the resulting bank statement.
* @param {String} businessUuid Optional. UUID of a business with an approved ACH name. The format should be a UUID string.
* @param {String} processingType Optional. Choice field. Examples: STANDARD_ACH or SAME_DAY_ACH
* @param {String} cardName The nickname of the card to debit from. It defaults to 'default' // Optional, OR "account_name": "default", never both.
* @param {String} sourceId source account id to debit from (optional)
* @param {String} destinationId destination account id for credit (optional)
* @param {String} transactionIdempotencyId Optional. UUID to uniquely identify the transaction to make it idempotent.
* @param {String} transactionIdempotencyIdentifier Optional. String to uniquely identify the transaction to make it idempotent.
*/
const issueSila = (
amount,
handle,
privateKey,
accountName = undefined,
descriptor = undefined,
businessUuid = undefined,
processingType = undefined,
cardName = undefined,
sourceId = undefined,
destinationId = undefined,
transactionIdempotencyId = undefined,
transactionIdempotencyIdentifier = undefined,
) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.amount = amount;
body.message = 'issue_msg';
if (cardName == undefined && accountName == undefined && sourceId == undefined) {
accountName = 'default';
}
body.account_name = accountName;
if (cardName !== undefined) {
body.card_name = cardName;
}
if (sourceId !== undefined) {
body.source_id = sourceId;
}
if (destinationId !== undefined) {
body.destination_id = destinationId;
}
if (descriptor) body.descriptor = descriptor;
if (businessUuid) body.business_uuid = businessUuid;
if (processingType) body.processing_type = processingType;
if (transactionIdempotencyId) body.transaction_idempotency_id = transactionIdempotencyId;
if (transactionIdempotencyIdentifier) {
body.transaction_idempotency_identifier = transactionIdempotencyIdentifier;
}
return makeRequest('issue_sila', body, privateKey);
};
/**
* Makes a call to /redeem_sila endpoint.
* @param {Number} amount The amount of sila tokens to reedem
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} accountName The account nickname to credit with the tokens' value.
* @param {String} descriptor Optional. Max Length 100
* @param {String} businessUuid Optional. UUID of a business with an approved ACH name. The format should be a UUID string.
* @param {String} processingType Optional. Choice field. Examples: STANDARD_ACH or SAME_DAY_ACH or CARD
* @param {String} cardName The nickname of the card to debit from. Optional, OR "account_name": "default", never both.
* @param {String} sourceId source account id to debit from (optional)
* @param {String} destinationId destination account id for credit (optional)
* @param {String} transactionIdempotencyId Optional. UUID to uniquely identify the transaction to make it idempotent.
* @param {String} transactionIdempotencyIdentifier Optional. String to uniquely identify the transaction to make it idempotent.
*/
const redeemSila = (
amount,
handle,
privateKey,
accountName = undefined,
descriptor = undefined,
businessUuid = undefined,
processingType = undefined,
cardName = undefined,
sourceId = undefined,
destinationId = undefined,
transactionIdempotencyId = undefined,
transactionIdempotencyIdentifier = undefined,
) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.amount = amount;
body.message = 'redeem_msg';
if (cardName == undefined && accountName == undefined && sourceId == undefined) {
accountName = 'default';
}
body.account_name = accountName;
if (cardName !== undefined) {
body.card_name = cardName;
}
if (sourceId !== undefined) {
body.source_id = sourceId;
}
if (destinationId !== undefined) {
body.destination_id = destinationId;
}
if (descriptor) body.descriptor = descriptor;
if (businessUuid) body.business_uuid = businessUuid;
body.processing_type = processingType;
if (transactionIdempotencyId) body.transaction_idempotency_id = transactionIdempotencyId;
if (transactionIdempotencyIdentifier) {
body.transaction_idempotency_identifier = transactionIdempotencyIdentifier;
}
return makeRequest('redeem_sila', body, privateKey);
};
/**
* Makes a call to /transfer_sila endpoint.
* @param {String} amount The amount of sila tokens to transfer
* @param {String} handle The origin user handle
* @param {String} privateKey The origin user's wallet private key
* @param {String} destinationHandle The destination user handle
* @param {String} walletNickname The destination user's wallet nickname (optional)
* @param {String} walletAddress The destination user's wallet address (optional)
* @param {String} descriptor The transaction descriptor (optional)
* @param {String} businessUuid The UUID of the business for the ACH name (optional)
* @param {String} sourceId source account id to debit from (optional)
* @param {String} destinationId destination account id for credit (optional)
* @param {String} transactionIdempotencyId Optional. UUID to uniquely identify the transaction to make it idempotent.
* @param {String} transactionIdempotencyIdentifier Optional. String to uniquely identify the transaction to make it idempotent.
*/
const transferSila = (
amount,
handle,
privateKey,
destinationHandle,
walletNickname = undefined,
walletAddress = undefined,
descriptor = undefined,
businessUuid = undefined,
sourceId = undefined,
destinationId = undefined,
transactionIdempotencyId = undefined,
transactionIdempotencyIdentifier = undefined,
) => {
const fullHandle = getFullHandle(handle);
const fullDestination = getFullHandle(destinationHandle);
const body = setHeaders({ header: {} }, fullHandle);
body.amount = amount;
body.destination_handle = fullDestination;
if (walletNickname) body.destination_wallet = walletNickname;
if (walletAddress) body.destination_address = walletAddress;
if (descriptor) body.descriptor = descriptor;
if (businessUuid) body.business_uuid = businessUuid;
if (sourceId) body.source_id = sourceId;
if (destinationId) body.destination_id = destinationId;
if (transactionIdempotencyId) body.transaction_idempotency_id = transactionIdempotencyId;
if (transactionIdempotencyIdentifier) {
body.transaction_idempotency_identifier = transactionIdempotencyIdentifier;
}
return makeRequest('transfer_sila', body, privateKey);
};
/**
* Cancel a pending transaction under certain circumstances
* @param {String} userHandle The user handle
* @param {String} userPrivateKey The user's private key
* @param {String} transactionId The transaction id to cancel
*/
const cancelTransaction = (userHandle, userPrivateKey, transactionId) => {
const fullHandle = getFullHandle(userHandle);
const body = setHeaders({ header: {} }, fullHandle);
body.transaction_id = transactionId;
return makeRequest('cancel_transaction', body, userPrivateKey);
};
const deleteRegistrationData = (path, handle, privateKey, uuid) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.uuid = uuid;
return makeRequest(`delete/${path}`, body, privateKey);
};
/**
* Makes a call to /delete/email endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} email The user's new email
*/
const deleteEmail = (handle, privateKey, uuid) =>
deleteRegistrationData('email', handle, privateKey, uuid);
/**
* Makes a call to /delete/phone endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} uuid The user's new phone
*/
const deletePhone = (handle, privateKey, uuid) =>
deleteRegistrationData('phone', handle, privateKey, uuid);
/**
* Makes a call to /delete/address endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} uuid The user's new address
*/
const deleteAddress = (handle, privateKey, uuid) =>
deleteRegistrationData('address', handle, privateKey, uuid);
/**
* Makes a call to /delete/identity endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} uuid The user's new identity
*/
const deleteIdentity = (handle, privateKey, uuid) =>
deleteRegistrationData('identity', handle, privateKey, uuid);
/**
* Makes a call to /update/email endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} email The updated email
*/
const updateEmail = (handle, privateKey, email) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.email = email.email;
body.uuid = email.uuid;
return makeRequest('update/email', body, privateKey);
};
/**
* Makes a call to /update/phone endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} optional The updated phone
* @param {String} optional.phone
* @param {String} optional.uuid
*/
const updatePhone = (
handle,
privateKey,
{ phone = undefined, uuid = undefined } = {},
) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.phone = phone;
body.uuid = uuid;
return makeRequest('update/phone', body, privateKey);
};
/**
* Makes a call to /update/address endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} address The updated address
*/
const updateAddress = (handle, privateKey, address) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.address_alias = address.alias;
body.street_address_2 = address.street_address_2;
body.street_address_1 = address.street_address_1;
body.city = address.city;
body.state = address.state;
body.postal_code = address.postal_code;
body.country = address.country;
body.uuid = address.uuid;
return makeRequest('update/address', body, privateKey);
};
/**
* Makes a call to /update/identity endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} identity The updated identity
*/
const updateIdentity = (handle, privateKey, identity) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.identity_alias = identity.alias;
body.identity_value = identity.value;
body.uuid = identity.uuid;
return makeRequest('update/identity', body, privateKey);
};
/**
* Update an existing entity (name, birthdate, or business data).
* @param {String} handle The user handle
* @param {String} privateKey The user's private key
* @param {Object} entity The updated entity
*/
const updateEntity = (handle, privateKey, entity) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.first_name = entity.first_name;
body.last_name = entity.last_name;
body.entity_name = entity.entity_name;
body.birthdate = entity.birthdate;
body.business_type = entity.business_type;
body.naics_code = entity.naics_code;
body.doing_business_as = entity.doing_business_as;
body.business_website = entity.business_website;
body.registration_state = entity.registration_state;
return makeRequest('update/entity', body, privateKey);
};
/**
* Makes a call to /add/email endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} email The user's new email
*/
const addEmail = (handle, privateKey, email) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.email = email;
return makeRequest('add/email', body, privateKey);
};
/**
* Makes a call to /add/phone endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} phone The user's new phone
* @param {Object} optional
*/
const addPhone = (handle, privateKey, phone) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.phone = phone;
return makeRequest('add/phone', body, privateKey);
};
/**
* Makes a call to /add/identity endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} identity The user's new identity alias
*
*/
const addIdentity = (handle, privateKey, identity) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.identity_alias = identity.alias;
body.identity_value = identity.value;
return makeRequest('add/identity', body, privateKey);
};
/**
* Makes a call to /add/address endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} address The user's new address
*/
const addAddress = (handle, privateKey, address) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.address_alias = address.alias;
body.street_address_2 = address.street_address_2;
body.street_address_1 = address.street_address_1;
body.city = address.city;
body.state = address.state;
body.postal_code = address.postal_code;
body.country = address.country;
return makeRequest('add/address', body, privateKey);
};
/**
* Makes a call to /get_accounts endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
*/
const getAccounts = (handle, privateKey) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.message = 'get_accounts_msg';
return makeRequest('get_accounts', body, privateKey);
};
/**
* Makes a call to /get_account_balance endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} accountName The account name to retrieve the balance
*/
const getAccountBalance = (handle, privateKey, accountName) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.account_name = accountName;
return makeRequest('get_account_balance', body, privateKey);
};
/**
* Makes a call to /plaid_sameday_auth endpoint.
* The account used in this endpoint must be in the microdeposit_pending_manual_verification status.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {String} accountName The account nickname
*/
const plaidSamedayAuth = (handle, privateKey, accountName) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.account_name = accountName;
return makeRequest('plaid_sameday_auth', body, privateKey);
};
/**
* Makes a call to /register_wallet endpoint.
* If you need a new wallet you can use the generateWallet method.
* @param {String} handle The user handle
* @param {String} privateKey An already registered user's wallet private key
* @param {Wallet} wallet The new wallet
*/
const registerWallet = (handle, privateKey, wallet, nickname, defaultVal, statementsEnabled) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
body.wallet_verification_signature = sign(wallet.address, wallet.privateKey);
body.wallet = {};
body.wallet.blockchain_address = wallet.address;
body.wallet.blockchain_network = 'ETH';
if (statementsEnabled) body.wallet.statements_enabled = statementsEnabled;
if (nickname) body.wallet.nickname = nickname;
if (defaultVal) body.wallet.default = defaultVal;
return makeRequest('register_wallet', body, privateKey);
};
/**
* Makes a call to /get_wallets endpoint and returns the list of wallets that match the filters
* @param {String} handle The user handle
* @param {String} privateKey Any of the user's registered wallet's private key
* @param {WalletFilters} filters The filters used to narrow the search results
*/
const getWallets = (handle, privateKey, filters = undefined) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
if (filters && filters.uuid) {
filters.wallet_id = filters.uuid;
}
if (filters) body.search_filters = filters;
return makeRequest('get_wallets', body, privateKey);
};
/**
* Makes a call to /update_wallet endpoint.
* The wallet to update is the one used to sign the message.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {Object} walletProperties The properties to update on the wallet
*/
const updateWallet = (handle, privateKey, walletProperties = {}) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
if (walletProperties) {
if (walletProperties.nickname) body.nickname = walletProperties.nickname;
if (walletProperties.default) body.default = walletProperties.default;
}
return makeRequest('update_wallet', body, privateKey);
};
/**
* Makes a call to /get_wallet endpoint.
* The wallet to retrieve information is the one used to sign the message.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
*/
const getWallet = (handle, privateKey) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
return makeRequest('get_wallet', body, privateKey);
};
/**
* Makes a call to /delete_wallet endpoint.
* The wallet to delete is the one used to sign the message.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
*/
const deleteWallet = (handle, privateKey) => {
const fullHandle = getFullHandle(handle);
const body = setHeaders({ header: {} }, fullHandle);
return makeRequest('delete_wallet', body, privateKey);
};
/**
* Makes a call to /get_transactions endpoint.
* @param {String} handle The user handle
* @param {String} privateKey The user's wallet private key
* @param {TransactionFilters} filters The filters used to narrow the search results
*/
const getTransactions = (handle = undefined, privateKey = undefined, filters = {}) => {
let fullHandle = null;
if (handle != undefined && handle != null) {
fullHandle = getFullHandle(handle);
}
const body = setHeaders({ header: {} }, fullHandle);
body.message = 'get_transactions_msg';
body.search_filters = filters;
return makeRequest('get_transactions', body, privateKey);
};
/**
* Makes a call to /get_sila_balance endpoint.
* This method replaces getBalance.
* @param {String} address The wallet's blockchain address
*/
const getSilaBalance = (address) => {
const body = { address };
return makeRequest('get_sila_balance', body);
};
/**
* Makes a call to /silaBalance endpoint.
* @param {String} address The wallet's blockchain address
* @deprecated Since version 0.2.7. Use getSilaBalance instead.
*/
const getBalance = (address) => {
const body = { address };
const opts = {
uri: getBalanceURL(),
json: true,
body,
};
return post(opts, env, logging);
};
/**
* Upload supporting documentation for KYC
* @param {String} userHandle The user handle
* @param {String} userPrivateKey The user's private key
* @param {Object} document
*
*/
const uploadDocument = async (userHandle, userPrivateKey, document) => {
const fullHandle = getFullHandle(userHandle);
const body = setHeaders({ header: {} }, fullHandle);
body.name = document.name;
body.filename = document.filename;
var tmpFileObj = {};
var tmpFilePathObj = {};
if(document.fileBuffer) {
body.hash = await hashFileObject(document.fileBuffer, 'sha256');
tmpFileObj = {'file':document.fileObject}
} else {
body.hash = await hashFile(document.filePath, 'sha256');
tmpFilePathObj = {'file':fs.createReadStream(document.filePath)}
}
body.mime_type = document.mimeType;
body.document_type = document.documentType;
body.description = document.description;
if (document.verification_uuid) {
body.verification_uuid = document.verification_uuid;
}
return makeFileRequest('documents', body, tmpFilePathObj, tmpFileObj, userPrivateKey);
};
/**
* Upload supporting documentation for KYC
* @param {String} userHandle The user handle
* @param {String} userPrivateKey The user's private key
* @param {Array} documents
*
*/
const uploadDocuments = async (userHandle, userPrivateKey, documents) => {
const fullHandle = getFullHandle(userHandle);
const body = setHeaders({ header: {} }, fullHandle);
body.file_metadata = {};
var filePaths = {};
let fileObjects = {};
for (let i=0;i<documents.length;i++) {
let docBodyObj = {};
let docObj = documents[i];
let index = i+1;
docBodyObj.name = docObj.name;
docBodyObj.filename = docObj.filename;
if(docObj.fileBuffer) {
docBodyObj.hash = await hashFileObject(docObj.fileBuffer, 'sha256');
fileObjects['file_'+index] = docObj.fileObject;
} else {
docBodyObj.hash = await hashFile(docObj.filePath, 'sha256');
filePaths['file_'+index] = fs.createReadStream(docObj.filePath);
}
docBodyObj.mime_type = docObj.mimeType;
docBodyObj.document_type = docObj.documentType;
docBodyObj.description = docObj.description;
body.file_metadata['file_'+index] = docBodyObj;
}
return makeFileRequest('documents', body, filePaths, fileObjects, userPrivateKey);
};
/**
* List previously uploaded supporting documentation for KYC
* @param {String} userHandle The user handle
* @param {String} userPrivateKey The user's private key
* @param {Object} filters A set of filters to send with the request
*/
const listDocuments = (userHandle, userPrivateKey, filters) => {
const fullHandle = getFullHandle(userHandle);
const body = setHeaders({ header: {} }, fullHandle);
const queryFilters = {};
if (filters) {
queryFilters.page = filters.page;
queryFilters.perPage = filters.perPage;
queryFilters.order = filters.order;
body.start_date = filters.startDate;
body.end_date = filters.endDate;
body.doc_types = filters.docTypes;
body.search = filters.search;
body.sort_by = filters.sortBy;
}
const queryParameters = getQueryParameters(queryFilters);
return makeRequest(`list_documents${queryParameters}`, body, userPrivateKey);
};
/**
* Retrieve a previously uploaded supporting documentation for KYC
* @param {String} userHandle The user handle
* @param {String} userPrivateKey The user's private key
* @param {String} documentId The document id to retrieve
*/
const getDocument = (userHandle, userPrivateKey, documentId) => {
const fullHandle = getFullHandle(userHandle);
const body = setHeaders({ header: {} }, fullHandle);
body.document_id = documentId;
return makeRequest('get_document', body, userPrivateKey);
};
/**
* Gets a list of valid business types that can be registered.
*/
const getBusinessTypes = () => {
const body = setHeaders({ header: {} });
return makeRequest('get_business_types', body);
};
/**
* Gets a list of valid business roles that can be used to link individuals to businesses.
*/
const getBusinessRoles = () => {
const body = setHeaders({ header: {} });
return makeRequest('get_business_roles', body);
};
/**
* Gets a list of valid NAICS codes sorted by category and listed with their describing subcategory.
*/
const getNaicsCategories = () => {
const body = setHeaders({ header: {} });
return makeRequest('get_naics_categories', body);
};
/**
* List the document types for KYC supporting documentation
* @param {Object} pagination This object includes the optional pagination parameters
*/
const getDocumentTypes = (pagination = undefined) => {
const body = setHeaders({ header: {} });
const queryParameters = getQueryParameters(pagination);
return makeRequest(`document_types${queryParameters}`, body);
};
/**
* @deprecated Since version 0.2.13-rc. Use getNaicsCategories instead.
*/
const getNacisCategories = () => {
return getNaicsCategories();
};
/**
* @param {String|undefined} entityType optional entity type filter.
* @param {object|undefined} pagination optional pagination control variables
*/
const getEntities = (
entityType = undefined,
{ page = undefined, perPage = undefined } = {},
) => {
const body = setHeaders({ header: {} });
const queryFilters = {};
if (page) queryFilters.page = page;
if (perPage) queryFilters.perPage = perPage;
if (entityType) body.entity_type = entityType;
const queryParameters = getQueryParameters(queryFilters);
return makeRequest(`get_entities${queryParameters}`, body);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {Object} options Optional properties to send in the request
* @param {Boolean} options.prettyDates
*/
const getEntity = (
userHandle,
userPrivateKey,
{ prettyDates = undefined } = {},
) => {
const body = setHeaders({ header: {} }, userHandle);
const queryParameters = getQueryParameter('', 'pretty_dates', prettyDates);
body.user_handle = userHandle;
return makeRequest(`get_entity${queryParameters}`, body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {String} businessHandle
* @param {String} businessPrivateKey
* @param {String} role
* @param {String} memberHandle
* @param {String} details
* @param {double} ownership_stake
*/
const linkBusinessMember = (
userHandle,
userPrivateKey,
businessHandle,
businessPrivateKey,
role,
memberHandle,
details,
ownershipStake,
) => {
const body = setHeaders({ header: {} }, userHandle, businessHandle);
body.role = role;
body.member_handle = memberHandle;
body.details = details;
body.ownership_stake = ownershipStake;
return makeRequest(
'link_business_member',
body,
userPrivateKey,
businessPrivateKey,
);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {String} businessHandle
* @param {String} businessPrivateKey
* @param {String} role
*/
const unlinkBusinessMember = (
userHandle,
userPrivateKey,
businessHandle,
businessPrivateKey,
role,
) => {
const body = setHeaders({ header: {} }, userHandle, businessHandle);
body.role = role;
return makeRequest(
'unlink_business_member',
body,
userPrivateKey,
businessPrivateKey,
);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {String} businessHandle
* @param {String} businessPrivateKey
* @param {String} memberHandle
* @param {String} certificationToken
*/
const certifyBeneficialOwner = (
userHandle,
userPrivateKey,
businessHandle,
businessPrivateKey,
memberHandle,
certificationToken,
) => {
const body = setHeaders({ header: {} }, userHandle, businessHandle);
body.member_handle = memberHandle;
body.certification_token = certificationToken;
return makeRequest(
'certify_beneficial_owner',
body,
userPrivateKey,
businessPrivateKey,
);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {String} businessHandle
* @param {String} businessPrivateKey
*/
const certifyBusiness = (
userHandle,
userPrivateKey,
businessHandle,
businessPrivateKey,
) => {
const body = setHeaders({ header: {} }, userHandle, businessHandle);
return makeRequest(
'certify_business',
body,
userPrivateKey,
businessPrivateKey,
);
};
/**
*
* @param {String} userHandle
* @param {String} userPrivateKey
*/
const plaidLinkToken = (user_handle, user_private_key, link_token_type=undefined, android_package_name=undefined) => {
const body = setHeaders({ header: {} }, user_handle);
body.link_token_type = link_token_type;
body.android_package_name = android_package_name;
return makeRequest('plaid_link_token', body, user_private_key);
};
/**
*
* @param {String} user_handle
* @param {String} account_name
* @param {String} user_private_key
*/
const deleteAccount = (user_handle, account_name, user_private_key) => {
const body = setHeaders({ header: {} }, user_handle);
body.account_name = account_name;
return makeRequest('delete_account', body, user_private_key);
};
/**
*
* @param {*} payload
* @returns
*/
const checkPartnerKyc = ({ query_app_handle, query_user_handle }) => {
const body = setHeaders({ header: {} });
body.query_app_handle = query_app_handle;
body.query_user_handle = query_user_handle;
return makeRequest('check_partner_kyc', body);
};
/**
*
* @param {*} payload
* @param {*} user_handle
* @param {*} user_private_key
* @returns
*/
const updateAccount = (
{ account_name, new_account_name, active=undefined},
user_handle,
user_private_key,
) => {
const body = setHeaders({ header: {} }, user_handle);
body.account_name = account_name;
body.new_account_name = new_account_name;
body.active = active;
return makeRequest('update_account', body, user_private_key);
};
/**
*
* @param {*} payload
* @param {String} user_handle
* @returns
*/
const plaidUpdateLinkToken = ({ account_name }, user_handle) => {
const body = setHeaders({ header: {} }, user_handle);
body.account_name = account_name;
return makeRequest('plaid_update_link_token', body);
};
/**
*
* @param {*} payload
* @param {*} user_private_key
* @returns
*/
const getInstitutions = (
payload = {
institution_name: undefined,
routing_number: undefined,
page: undefined,
per_page: undefined,
}
) => {
const body = setHeaders({ header: {} });
body.message = 'header_msg';
body.search_filters = payload;
return makeRequest('get_institutions', body);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {Object} cardObject properties to send in the request
* @returns
*/
const linkCard = (userHandle, userPrivateKey, cardObject) => {
const body = setHeaders({
header: {}
}, userHandle);
body.message = 'header_msg';
body.card_name = cardObject['card_name'];
body.account_postal_code = cardObject['account_postal_code'];
body.token = cardObject['token'];
body.provider = cardObject['provider'];
body.skip_verification = cardObject['skip_verification'];
// anonymous card fields
if (cardObject['anonymous']) body.anonymous = cardObject['anonymous'];
if (cardObject['first_name']) body.first_name = cardObject['first_name'];
if (cardObject['last_name']) body.last_name = cardObject['last_name'];
if (cardObject['address']) body.address = cardObject['address']
return makeRequest('link_card', body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @returns
*/
const getCards = (userHandle, userPrivateKey) => {
const body = setHeaders({
header: {}
}, userHandle);
return makeRequest('get_cards', body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {String} cardName
* @returns
*/
const deleteCard = (userHandle, userPrivateKey, cardName, provider) => {
const body = setHeaders({
header: {}
}, userHandle);
body.card_name = cardName;
body.provider = provider;
return makeRequest('delete_card', body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {String} transactionId
* @returns
*/
const reverseTransaction = (userHandle, userPrivateKey, transactionId) => {
const body = setHeaders({
header: {}
}, userHandle);
body.transaction_id = transactionId;
return makeRequest('reverse_transaction', body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param {Object} searchFilters properties to send in the request
* @returns
*/
const getWebhooks = (userHandle, userPrivateKey, searchFilters) => {
const body = setHeaders({
header: {}
}, userHandle);
body.message = 'header_msg';
var payload = {};
if (!searchFilters) {
payload = {
uuid: undefined,
delivered: undefined,
sort_ascending: undefined,
event_type: undefined,
endpoint_name: undefined,
user_handle: undefined,
start_epoch: undefined,
end_epoch: undefined,
page: undefined,
per_page: undefined
};
} else {
payload = searchFilters;
}
body.search_filters = payload;
return makeRequest('get_webhooks', body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @returns
*/
const getPaymentMethods = (userHandle, userPrivateKey, filters={}) => {
const body = setHeaders({
header: {}
}, userHandle);
body.search_filters = filters;
return makeRequest('get_payment_methods', body, userPrivateKey);
};
/**
* @param {String} userHandle
* @param {String} userPrivateKey
* @param