diff --git a/rest-api/javascript/transaction/client-create-transaction/.env.example b/rest-api/javascript/transaction/client-create-transaction/.env.example new file mode 100644 index 0000000..46ab8e4 --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/.env.example @@ -0,0 +1,5 @@ +# Base Url endpoint +BASE_URL = 'https://api-sandbox.uphold.com' + +ACCESS_TOKEN = '' +DESTINATION_EMAIL_ACCOUNT = 'asdf.asdf@email.com' diff --git a/rest-api/javascript/transaction/client-create-transaction/.gitignore b/rest-api/javascript/transaction/client-create-transaction/.gitignore new file mode 100644 index 0000000..2d7ec5c --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/.gitignore @@ -0,0 +1,2 @@ +.env +node_modules/ diff --git a/rest-api/javascript/transaction/client-create-transaction/README.md b/rest-api/javascript/transaction/client-create-transaction/README.md new file mode 100644 index 0000000..a918b6c --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/README.md @@ -0,0 +1,40 @@ +# User-to-user transaction + +This sample project demonstrates how to perform a transaction from one Uphold user to another, +with the latter identified by their email address. +For further background, please refer to the [API documentation](https://uphold.com/en/developer/api/documentation). + +## Summary + +This sample project performs the following actions: + +- Create and commit a transaction +- Display the data about the transaction + +## Requirements + +- Node.js v13.14.0 or later +- An account at with at least $1 USD of available funds +- An access token from that account, to perform authenticated requests to the Uphold API + (see the [authentication](../../authentication) examples for how to obtain one) + +## Setup + +- Run `npm install` (or `yarn install`) +- Create a `.env` file based on the `.env.example` file, and populate it with the required data. + +## Run + +Run `node index.js`. + +The code will locate a card with nonzero balance in the source account, and prepare a $1 USD transaction +from that card to the account identified by the email in the `.env` file. + +The result will depend on the status of the destination email: + +- If it is already associated with an existing Sandbox account, the transaction will be completed immediately + and the funds will become available in the recipient's account. +- If no Sandbox account exists with that email, an "invite"-type transaction will be created, + which will be executed when the associated account is created. + This invite can be cancelled by the sender while the recipient hasn't registered + (which is useful if you use a dummy email address for this). diff --git a/rest-api/javascript/transaction/client-create-transaction/assets/transaction-scopes.png b/rest-api/javascript/transaction/client-create-transaction/assets/transaction-scopes.png new file mode 100644 index 0000000..0a69db8 Binary files /dev/null and b/rest-api/javascript/transaction/client-create-transaction/assets/transaction-scopes.png differ diff --git a/rest-api/javascript/transaction/client-create-transaction/ct-transaction.js b/rest-api/javascript/transaction/client-create-transaction/ct-transaction.js new file mode 100644 index 0000000..bf68509 --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/ct-transaction.js @@ -0,0 +1,74 @@ +/** + * Dependencies. + */ + +import axios from "axios"; +import colors from "colors"; +import dotenv from "dotenv"; +import path from "path"; + +colors.setTheme({ + silly: "rainbow", + input: "grey", + verbose: "cyan", + prompt: "grey", + info: "green", + data: "grey", + help: "cyan", + warn: "yellow", + debug: "blue", + error: "red", +}); + +dotenv.config({ path: path.resolve() + "/.env" }); + +/** + * Create and commit a transaction. + */ + +export async function createAndCommitTransaction(data = {}, myCardID = null) { + const options = { + method: "POST", + headers: { + Authorization: `Bearer ${process.env.ACCESS_TOKEN}`, + "content-type": "application/json", + }, + data, + url: `${process.env.BASE_URL}/v0/me/cards/${myCardID}/transactions?commit=true`, + }; + + const response = axios(options) + .then((response) => { + return response.data; + }) + .catch((error) => { + error.response.data.errors + ? console.log(JSON.stringify(error.response.data.errors, null, 2).error) + : console.log(JSON.stringify(error, null, 2).error); + throw error; + }); + + return response; +} + +/** + * Get the first card with available balance (if one exists). + */ + +export async function getCardWithFunds() { + try { + const response = await axios.get(`${process.env.BASE_URL}/v0/me/cards`, { + headers: { + Authorization: `Bearer ${process.env.ACCESS_TOKEN}`, + }, + }); + + // Get the the first card with nonzero available balance + return response.data.filter(card => { return Number(card.available) > 0 })[0]; + } catch (error) { + error.response.data.errors + ? console.log(JSON.stringify(error.response.data.errors, null, 2).error) + : console.log(JSON.stringify(error, null, 2).error); + throw error; + } +} diff --git a/rest-api/javascript/transaction/client-create-transaction/index.js b/rest-api/javascript/transaction/client-create-transaction/index.js new file mode 100644 index 0000000..a0ea44c --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/index.js @@ -0,0 +1,34 @@ +/** + * Dependencies. + */ + +import _ from "lodash"; +import dotenv from "dotenv"; +import path from "path"; +import { + createAndCommitTransaction, + getCardWithFunds, +} from "./ct-transaction.js"; + +dotenv.config({ path: path.resolve() + "/.env" }); + +(async () => { + // Locate a card that can be used as the source for the transaction. + const sourceCard = await getCardWithFunds(); + // Define the destination as an email address. + const destination = `${process.env.DESTINATION_EMAIL_ACCOUNT}`; + + const data = { + denomination: { + amount: "1", + currency: "USD", + }, + destination, + }; + + const transaction = await createAndCommitTransaction(data, sourceCard.id); + + if (transaction) { + console.log('Transaction:', transaction); + } +})(); diff --git a/rest-api/javascript/transaction/client-create-transaction/package-lock.json b/rest-api/javascript/transaction/client-create-transaction/package-lock.json new file mode 100644 index 0000000..b8a2232 --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/package-lock.json @@ -0,0 +1,51 @@ +{ + "name": "uphold-ct", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "axios": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" + }, + "js-base64": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.0.tgz", + "integrity": "sha512-wVdUBYQeY2gY73RIlPrysvpYx+2vheGo8Y1SNQv/BzHToWpAZzJU7Z6uheKMAe+GLSBig5/Ps2nxg/8tRB73xg==" + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" + } + } +} diff --git a/rest-api/javascript/transaction/client-create-transaction/package.json b/rest-api/javascript/transaction/client-create-transaction/package.json new file mode 100644 index 0000000..df37cb9 --- /dev/null +++ b/rest-api/javascript/transaction/client-create-transaction/package.json @@ -0,0 +1,23 @@ +{ + "name": "uphold-ct", + "version": "0.0.1", + "description": "Uphold rest Api test client transaction", + "license": "MIT", + "main": "index.js", + "type": "module", + "dependencies": { + "axios": "^0.20.0", + "btoa": "^1.2.1", + "colors": "^1.4.0", + "dotenv": "^8.2.0", + "js-base64": "^3.5.2", + "lodash": "^4.17.20", + "qs": "^6.9.4" + }, + "engines": { + "node": ">=13" + }, + "scripts": { + "run": "node index.js " + } +}