├── ruby
├── .ruby-version
├── .env.example
├── Gemfile
├── Dockerfile
├── Gemfile.lock
└── app.rb
├── node
├── .dockerignore
├── .env.example
├── .eslintrc.js
├── Dockerfile
├── package.json
└── server.js
├── frontend
├── .dockerignore
├── src
│ ├── index.css
│ ├── index.js
│ ├── helpers
│ │ └── utils.js
│ ├── setupProxy.js
│ ├── logo.svg
│ ├── App.js
│ ├── Components
│ │ ├── ConnectLink
│ │ │ └── ConnectLink.js
│ │ ├── MovementList
│ │ │ └── MovementList.js
│ │ └── AccountList
│ │ │ └── AccountList.js
│ └── imagotipo.svg
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── isotipo.png
│ ├── manifest.json
│ └── index.html
├── .eslintrc.js
├── .env.example
├── Dockerfile
├── craco.config.js
├── .gitignore
├── tailwind.config.js
└── package.json
├── python
├── requirements.txt
├── .env.example
├── Dockerfile
└── app.py
├── _media
└── fintoc.png
├── .gitignore
├── .env.example
├── Makefile
├── docker-compose.yml
└── README.md
/ruby/.ruby-version:
--------------------------------------------------------------------------------
1 | 2.7.3
2 |
--------------------------------------------------------------------------------
/node/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/frontend/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
--------------------------------------------------------------------------------
/python/requirements.txt:
--------------------------------------------------------------------------------
1 | flask==1.1.4
2 | fintoc==0.3.1
3 | python-dotenv==0.17.1
--------------------------------------------------------------------------------
/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/_media/fintoc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fintoc-com/quickstart/HEAD/_media/fintoc.png
--------------------------------------------------------------------------------
/node/.env.example:
--------------------------------------------------------------------------------
1 | # Get your Secret Key (sk_) from https://app.fintoc.com/api-keys
2 | SECRET_KEY=
--------------------------------------------------------------------------------
/ruby/.env.example:
--------------------------------------------------------------------------------
1 | # Get your Secret Key (sk_) from https://app.fintoc.com/api-keys
2 | SECRET_KEY=
--------------------------------------------------------------------------------
/python/.env.example:
--------------------------------------------------------------------------------
1 | # Get your Secret Key (sk_) from https://app.fintoc.com/api-keys
2 | SECRET_KEY=
--------------------------------------------------------------------------------
/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'airbnb-base',
3 | //disable: 'no-console',
4 | };
5 |
--------------------------------------------------------------------------------
/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fintoc-com/quickstart/HEAD/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/frontend/public/isotipo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fintoc-com/quickstart/HEAD/frontend/public/isotipo.png
--------------------------------------------------------------------------------
/node/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: 'airbnb-base',
3 | // disable: 'no-console',
4 | };
5 |
--------------------------------------------------------------------------------
/ruby/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source 'https://rubygems.org'
4 |
5 | gem 'dotenv', '~> 2.7.6'
6 | gem 'fintoc', '~> 0.1.0'
7 | gem 'sinatra', '~> 2.1.0'
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #.env
2 | .env
3 |
4 | # Dependency directories
5 | node_modules/
6 |
7 | # Jetbrain
8 | .idea
9 |
10 | # VSCode
11 | .vscode
12 |
13 | # OSX
14 | .DS_Store
--------------------------------------------------------------------------------
/frontend/.env.example:
--------------------------------------------------------------------------------
1 | # Get your Public Key (pk_) from https://app.fintoc.com/api-keys
2 | REACT_APP_PUBLIC_KEY=
3 |
4 | # URL to receive your link_token from widget
5 | REACT_APP_WEBHOOK_URL=
6 |
--------------------------------------------------------------------------------
/node/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG NODE_VER=14.17.0
2 | FROM node:${NODE_VER}
3 |
4 | WORKDIR /opt/app
5 |
6 | COPY . .
7 |
8 | RUN npm install
9 |
10 | EXPOSE 5000
11 |
12 | ENTRYPOINT ["npm"]
13 | CMD ["start"]
--------------------------------------------------------------------------------
/frontend/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG NODE_VER=14.17.0
2 | FROM node:${NODE_VER}
3 |
4 | WORKDIR /opt/app
5 |
6 | COPY . .
7 |
8 | RUN npm install
9 |
10 | EXPOSE 3000
11 |
12 | ENTRYPOINT ["npm"]
13 | CMD ["start"]
14 |
--------------------------------------------------------------------------------
/ruby/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG RUBY_VER=2.7.3
2 | FROM ruby:${RUBY_VER}
3 |
4 | WORKDIR /opt/app
5 |
6 | COPY . .
7 |
8 | RUN bundle install
9 |
10 | EXPOSE 5000
11 |
12 | ENTRYPOINT ["ruby"]
13 | CMD ["app.rb", "-o", "0.0.0.0"]
14 |
--------------------------------------------------------------------------------
/frontend/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 |
6 | ReactDOM.render(
7 |
8 |
9 | ,
10 | document.getElementById('root'),
11 | );
12 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Get your Secret Key (sk_) from https://app.fintoc.com/api-keys
2 | SECRET_KEY=
3 | # Get your Public Key (pk_) from https://app.fintoc.com/api-keys
4 | REACT_APP_PUBLIC_KEY=
5 | # URL where Fintoc will Post a link_token (more: https://docs.fintoc.com/docs/integration)
6 | REACT_APP_WEBHOOK_URL=
7 |
--------------------------------------------------------------------------------
/frontend/src/helpers/utils.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment';
2 |
3 | export function formatDate(date) {
4 | return moment(date, 'YYYY-MM-DDTHH:mm:ss.sssZ').format('DD-MMM-YYYY');
5 | }
6 |
7 | export function formatAmount(amount) {
8 | return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, '.');
9 | }
10 |
--------------------------------------------------------------------------------
/frontend/craco.config.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-extraneous-dependencies */
2 | /* eslint-disable global-require */
3 | module.exports = {
4 | style: {
5 | postcss: {
6 | plugins: [
7 | require('tailwindcss'),
8 | require('autoprefixer'),
9 | ],
10 | },
11 | },
12 | };
13 |
--------------------------------------------------------------------------------
/python/Dockerfile:
--------------------------------------------------------------------------------
1 | ARG PYTHON_VER=3.6.1
2 | FROM python:${PYTHON_VER}
3 |
4 | WORKDIR /opt/app
5 |
6 | COPY . .
7 |
8 | RUN pip3 install -r requirements.txt
9 |
10 | EXPOSE 5000
11 |
12 | ENV FLASK_APP=/opt/app/python/app.py
13 |
14 | ENTRYPOINT ["python"]
15 | CMD ["-m", "flask", "run", "--host=0.0.0.0", "--port=5000"]
--------------------------------------------------------------------------------
/frontend/src/setupProxy.js:
--------------------------------------------------------------------------------
1 | const createProxyMiddleware = require('http-proxy-middleware');
2 |
3 | module.exports = (app) => {
4 | app.use(
5 | '/api',
6 | createProxyMiddleware({
7 | target: process.env.REACT_APP_BACKEND_HOST || 'http://localhost:5000',
8 | changeOrigin: true,
9 | }),
10 | );
11 | };
12 |
--------------------------------------------------------------------------------
/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | purge: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
3 | darkMode: false, // or 'media' or 'class'
4 | theme: {
5 | extend: {
6 | colors: {
7 | darkBlue: '#272634',
8 | fintocBlue: '#475FF1',
9 | fintocLight: '#6A8DF9',
10 | },
11 | },
12 | },
13 | variants: {
14 | extend: {},
15 | },
16 | plugins: [],
17 | };
18 |
--------------------------------------------------------------------------------
/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Fintoc Sample App",
3 | "name": "Fintoc Sample App",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "isotipo.png",
12 | "type": "image/png",
13 | "sizes": "20x24"
14 | }
15 | ],
16 | "start_url": ".",
17 | "display": "standalone",
18 | "theme_color": "#000000",
19 | "background_color": "#ffffff"
20 | }
21 |
--------------------------------------------------------------------------------
/frontend/src/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/node/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fintoc-quickstart",
3 | "version": "1.0.0",
4 | "description": "Fintoc quickstart with React and Express",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js",
8 | "server": "nodemon server.js",
9 | "client": "cd ../frontend && npm start",
10 | "install-client": "cd ../frontend && npm install",
11 | "dev": "concurrently \"npm run server\" \"npm run client\""
12 | },
13 | "author": "Paulina Araya",
14 | "license": "ISC",
15 | "dependencies": {
16 | "body-parser": "^1.19.0",
17 | "concurrently": "^6.0.2",
18 | "dotenv": "^8.5.1",
19 | "eslint": "^7.25.0",
20 | "eslint-config-airbnb-base": "^14.2.1",
21 | "eslint-plugin-import": "^2.22.1",
22 | "express": "^4.17.1",
23 | "fintoc": "^0.1.0",
24 | "moment": "^2.29.1"
25 | },
26 | "devDependencies": {
27 | "nodemon": "^2.0.7"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/frontend/src/App.js:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import logo from './imagotipo.svg';
3 | import AccountList from './Components/AccountList/AccountList';
4 | import ConnectLink from './Components/ConnectLink/ConnectLink';
5 | import MovementList from './Components/MovementList/MovementList';
6 |
7 | function App() {
8 | const [linkId, setLinkId] = useState('');
9 | const [accountId, setAccountId] = useState('');
10 | return (
11 |
12 |
17 | {!linkId &&
}
18 | {linkId && !accountId
19 | &&
}
20 | {linkId && accountId
21 | &&
}
22 |
23 |
24 | );
25 | }
26 |
27 | export default App;
28 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | language := ruby
2 |
3 | help:
4 | @echo "Usage: make COMMAND [OPTIONS]"
5 | @echo
6 | @echo "Options:"
7 | @echo " language=[language] - The language can be any of the supported languages such as: node, ruby or python."
8 | @echo
9 | @echo "Commands:"
10 | @echo " start\t\tStart the containers (and builds them if they don't exist)."
11 | @echo " build\t\tBuild the Docker images."
12 | @echo " destroy\tDestroy the containers."
13 | @echo " stop\t\tStop the containers."
14 | @echo " restart\tRestart the containers."
15 | @echo " logs\t\tShow container logs."
16 | .PHONY: help
17 |
18 | start: ## Start the containers
19 | @REACT_APP_BACKEND_HOST=http://$(language):5000 \
20 | docker-compose up --remove-orphans --detach $(language) frontend
21 | .PHONY: start
22 |
23 | build: ## Build the containers
24 | docker-compose build --no-cache
25 | .PHONY: build
26 |
27 | destroy: ## Destroy the containers
28 | @docker-compose down
29 | .PHONY: destroy
30 |
31 | stop: ## Stop the containers
32 | @docker-compose stop
33 | .PHONY: stop
34 |
35 | restart: ## Restart the containers
36 | @make -s destroy
37 | @make -s start
38 | .PHONY: restart
39 |
40 | logs: ## Show containers logs
41 | @docker-compose logs
42 | .PHONY: logs
43 |
--------------------------------------------------------------------------------
/ruby/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | addressable (2.7.0)
5 | public_suffix (>= 2.0.2, < 5.0)
6 | domain_name (0.5.20190701)
7 | unf (>= 0.0.5, < 1.0.0)
8 | dotenv (2.7.6)
9 | ffi (1.15.0)
10 | ffi-compiler (1.0.1)
11 | ffi (>= 1.0.0)
12 | rake
13 | fintoc (0.1.0)
14 | http
15 | tabulate
16 | http (5.0.0)
17 | addressable (~> 2.3)
18 | http-cookie (~> 1.0)
19 | http-form_data (~> 2.2)
20 | llhttp-ffi (~> 0.0.1)
21 | http-cookie (1.0.3)
22 | domain_name (~> 0.5)
23 | http-form_data (2.3.0)
24 | llhttp-ffi (0.0.1)
25 | ffi-compiler (~> 1.0)
26 | rake (~> 13.0)
27 | mustermann (1.1.1)
28 | ruby2_keywords (~> 0.0.1)
29 | public_suffix (4.0.6)
30 | rack (2.2.3)
31 | rack-protection (2.1.0)
32 | rack
33 | rake (13.0.3)
34 | ruby2_keywords (0.0.4)
35 | sinatra (2.1.0)
36 | mustermann (~> 1.0)
37 | rack (~> 2.2)
38 | rack-protection (= 2.1.0)
39 | tilt (~> 2.0)
40 | tabulate (0.1.2)
41 | tilt (2.0.10)
42 | unf (0.1.4)
43 | unf_ext
44 | unf_ext (0.0.7.7)
45 |
46 | PLATFORMS
47 | ruby
48 | x86_64-darwin-20
49 |
50 | DEPENDENCIES
51 | dotenv (~> 2.7.6)
52 | fintoc (~> 0.1.0)
53 | sinatra (~> 2.1.0)
54 |
55 | BUNDLED WITH
56 | 2.2.15
57 |
--------------------------------------------------------------------------------
/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@craco/craco": "^6.1.2",
7 | "@testing-library/jest-dom": "^5.12.0",
8 | "@testing-library/react": "^11.2.6",
9 | "@testing-library/user-event": "^12.8.3",
10 | "eslint": "^7.25.0",
11 | "eslint-config-airbnb-base": "^14.2.1",
12 | "eslint-plugin-import": "^2.22.1",
13 | "http-proxy-middleware": "^0.19.1",
14 | "moment": "^2.29.1",
15 | "react": "^17.0.2",
16 | "react-dom": "^17.0.2",
17 | "react-script-hook": "^1.3.0",
18 | "react-scripts": "4.0.3",
19 | "web-vitals": "^1.1.1"
20 | },
21 | "scripts": {
22 | "start": "craco start",
23 | "build": "craco build",
24 | "test": "craco test",
25 | "eject": "react-scripts eject"
26 | },
27 | "proxy": "http://localhost:5000",
28 | "eslintConfig": {
29 | "extends": [
30 | "react-app",
31 | "react-app/jest"
32 | ]
33 | },
34 | "browserslist": {
35 | "production": [
36 | ">0.2%",
37 | "not dead",
38 | "not op_mini all"
39 | ],
40 | "development": [
41 | "last 1 chrome version",
42 | "last 1 firefox version",
43 | "last 1 safari version"
44 | ]
45 | },
46 | "devDependencies": {
47 | "autoprefixer": "^9.8.6",
48 | "postcss": "^7.0.35",
49 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.1.2"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ruby/app.rb:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | require 'dotenv'
4 | require 'sinatra'
5 | require 'fintoc'
6 | Dotenv.load
7 |
8 | set :port, 5000
9 | set :protection, :except => [:json_csrf]
10 |
11 | link_token = ''
12 |
13 | fintoc = Fintoc::Client.new(ENV['SECRET_KEY'])
14 |
15 | get '/api/accounts' do
16 | link = fintoc.get_link(link_token)
17 | accounts = link.find_all(type: 'checking_account')
18 | content_type :json
19 | accounts.map do |account|
20 | { balance: { available: account.balance.available, current: account.balance.current },
21 | id: account.id,
22 | name: account.name,
23 | holderName: account.holder_name,
24 | currency: account.currency }
25 | end.to_json
26 | end
27 |
28 | get '/api/accounts/:account_id/movements' do
29 | start_of_month = Date.new(Date.today.year, Date.today.month, 1)
30 | link = fintoc.get_link(link_token)
31 | account = link.find(id: params[:account_id])
32 |
33 | content_type :json
34 | last_month_movements = account.get_movements(since: start_of_month.to_s)
35 | last_month_movements.to_a.map do |movement|
36 | { id: movement.id,
37 | postDate: movement.post_date,
38 | currency: movement.currency,
39 | amount: movement.amount,
40 | description: movement.description }
41 | end.to_json
42 | end
43 |
44 | post '/api/link_token' do
45 | request.body.rewind
46 | link_token = (JSON.parse request.body.read)['data']['link_token']
47 | 'Post request to /api/link_token'
48 | end
49 |
--------------------------------------------------------------------------------
/node/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const bodyParser = require('body-parser');
3 | const dotenv = require('dotenv');
4 | const Fintoc = require('fintoc');
5 | const moment = require('moment');
6 |
7 | dotenv.config();
8 | let linkToken = '';
9 |
10 | const fintoc = new Fintoc(process.env.SECRET_KEY);
11 | const app = express();
12 | app.use(
13 | bodyParser.urlencoded({
14 | extended: false,
15 | }),
16 | );
17 | app.use(bodyParser.json());
18 |
19 | app.get('/api/accounts', async (req, res) => {
20 | try {
21 | const link = await fintoc.getLink(linkToken);
22 | const accounts = link.findAll({ type_: 'checking_account' });
23 | res.json(accounts);
24 | } catch (error) {
25 | res.status(400);
26 | res.json(error);
27 | }
28 | });
29 |
30 | app.get('/api/accounts/:accountId/movements', async (req, res) => {
31 | try {
32 | const startOfMonth = moment().startOf('month').format('YYYY-MM-DD');
33 | const link = await fintoc.getLink(linkToken);
34 | const account = link.find({ id_: req.params.accountId });
35 | const lastMonthMovements = await account.getMovements({ since: startOfMonth });
36 | res.json(lastMonthMovements);
37 | } catch (error) {
38 | res.status(400);
39 | res.json(error);
40 | }
41 | });
42 |
43 | app.post('/api/link_token', (req, res) => {
44 | linkToken = req.body.data.link_token;
45 | res.send('Post request to /api/link_token');
46 | });
47 |
48 | const port = process.env.PORT || 5000;
49 |
50 | app.listen(port, () => console.log(`Server started on port ${port}`));
51 |
--------------------------------------------------------------------------------
/python/app.py:
--------------------------------------------------------------------------------
1 | from datetime import date
2 | from fintoc import Client
3 | from flask import Flask, request, jsonify
4 | import os
5 |
6 | app = Flask(__name__)
7 |
8 | client = Client(os.environ['SECRET_KEY'])
9 | link_token = None
10 |
11 |
12 | @app.route('/api/link_token', methods=['POST'])
13 | def set_link_token():
14 | global link_token
15 | link_token = request.json['data']['link_token']
16 | return 'link token set'
17 |
18 |
19 | @app.route('/api/accounts', methods=['GET'])
20 | def get_accounts():
21 | def transform_account(account):
22 | return {
23 | 'id': account.id_,
24 | 'name': account.name,
25 | 'holderName': account.holder_name,
26 | 'currency': account.currency,
27 | 'balance': {
28 | 'available': account.balance.available,
29 | 'current': account.balance.current,
30 | }
31 | }
32 | link = client.get_link(link_token)
33 | return jsonify([transform_account(account) for account in link])
34 |
35 |
36 | @app.route('/api/accounts//movements', methods=['GET'])
37 | def get_account_movements(account_id):
38 | def transform_movement(movement):
39 | return {
40 | 'id': movement.id_,
41 | 'postDate': movement.post_date.isoformat(),
42 | 'amount': movement.amount,
43 | 'currency': movement.currency,
44 | 'description': movement.description,
45 | }
46 | current_month = date.today().replace(day=1)
47 | link = client.get_link(link_token)
48 | account = link.find(id_=account_id)
49 | movements = account.get_movements(since=current_month)
50 | return jsonify([transform_movement(movement) for movement in movements])
51 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | # X-COMMON-VARIABLES
2 | x-backend-environment: &backend-environment
3 | SECRET_KEY: ${SECRET_KEY}
4 | x-frontend-environment: &frontend-environment
5 | REACT_APP_PUBLIC_KEY: ${REACT_APP_PUBLIC_KEY}
6 | REACT_APP_WEBHOOK_URL: ${REACT_APP_WEBHOOK_URL}
7 | REACT_APP_BACKEND_HOST: ${REACT_APP_BACKEND_HOST}
8 | x-backend-networks: &backend-networks
9 | - backend_network
10 | x-backend-ports: &backend-ports
11 | - 5000:5000
12 |
13 | version: '3.8'
14 | services:
15 | # Backend Services
16 | node:
17 | container_name: fintoc_node
18 | build:
19 | dockerfile: Dockerfile
20 | context: ./node
21 | ports: *backend-ports
22 | environment:
23 | <<: *backend-environment
24 | networks: *backend-networks
25 | python:
26 | container_name: fintoc_python
27 | build:
28 | dockerfile: Dockerfile
29 | context: ./python
30 | ports: *backend-ports
31 | environment:
32 | <<: *backend-environment
33 | networks: *backend-networks
34 | ruby:
35 | container_name: fintoc_ruby
36 | build:
37 | dockerfile: Dockerfile
38 | context: ./ruby
39 | ports: *backend-ports
40 | environment:
41 | <<: *backend-environment
42 | networks: *backend-networks
43 |
44 | # Frontend services
45 | frontend:
46 | container_name: fintoc_frontend
47 | build:
48 | dockerfile: Dockerfile
49 | context: ./frontend
50 | ports:
51 | - 3000:3000
52 | environment:
53 | <<: *frontend-environment
54 | networks:
55 | - backend_network
56 | - frontend_network
57 |
58 | networks:
59 | frontend_network:
60 | name: frontend_network
61 | driver: bridge
62 | backend_network:
63 | name: backend_network
64 | driver: bridge
65 |
--------------------------------------------------------------------------------
/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | Fintoc Quickstart
28 |
29 |
30 | You need to enable JavaScript to run this app.
31 |
32 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/frontend/src/Components/ConnectLink/ConnectLink.js:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react';
2 | import useScript from 'react-script-hook';
3 |
4 | const widgetOptions = {
5 | publicKey: process.env.REACT_APP_PUBLIC_KEY,
6 | holderType: 'individual', // business or individual
7 | product: 'movements', // movements or suscription
8 | webhookUrl: process.env.REACT_APP_WEBHOOK_URL,
9 | };
10 |
11 | function ConnectLink(props) {
12 | const [isOpen, setIsOpen] = useState(true);
13 | const [loadingScript, errorScript] = useScript({ src: 'https://js.fintoc.com/v1/' });
14 |
15 | const handleSuccess = (params) => {
16 | props.setLinkId(params.id);
17 | };
18 | const openWidget = () => setIsOpen(true);
19 | const closeWidget = () => setIsOpen(false);
20 | useEffect(() => {
21 | if (!isOpen) return;
22 | if (loadingScript) return;
23 | if (errorScript || !window.Fintoc) return;
24 | const params = {
25 | ...widgetOptions,
26 | onSuccess: handleSuccess,
27 | onExit: closeWidget,
28 | };
29 | const widget = window.Fintoc.create(params);
30 | widget.open();
31 | }, [loadingScript, errorScript, isOpen]);
32 |
33 | return (
34 |
35 |
36 | List@ para probar fintoc?
37 | Conecta tu cuenta para comenzar
38 |
39 |
40 |
41 |
45 | Conectar
46 |
47 |
48 |
49 |
50 | );
51 | }
52 |
53 | export default ConnectLink;
54 |
--------------------------------------------------------------------------------
/frontend/src/Components/MovementList/MovementList.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { formatAmount, formatDate } from '../../helpers/utils';
3 |
4 | function MovementList(props) {
5 | const [movements, setMovements] = useState([]);
6 | const [isLoading, setIsLoading] = useState(false);
7 | const [error, setError] = useState(false);
8 |
9 | useEffect(async () => {
10 | setIsLoading(true);
11 | const response = await fetch(`/api/accounts/${props.accountId}/movements?linkId=${props.linkId}`);
12 | const data = await response.json();
13 | if (response.ok) {
14 | setMovements(data);
15 | setError(false);
16 | } else {
17 | setError(data.message);
18 | }
19 | setIsLoading(false);
20 | }, []);
21 |
22 | const handleBack = () => {
23 | props.setAccountId('');
24 | };
25 |
26 | return (
27 |
28 |
Atrás
29 |
Movimientos de la cuenta
30 | {error && (
31 |
32 |
Error
33 |
{error || 'Se produjo un error'}
34 |
35 | )}
36 | {!error
37 | && (
38 |
39 |
40 |
41 |
42 | Fecha contable
46 | Monto
50 | Descripción
54 |
55 |
56 |
57 | {!isLoading && movements.map((movement) => (
58 |
59 | {formatDate(movement.postDate)}
60 | {movement.currency} {formatAmount(movement.amount)}
61 | {movement.description}
62 |
63 | ))}
64 | {isLoading && (
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | )}
73 |
74 |
75 |
76 | )}
77 |
78 | );
79 | }
80 |
81 | export default MovementList;
82 |
--------------------------------------------------------------------------------
/frontend/src/Components/AccountList/AccountList.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from 'react';
2 | import { formatAmount } from '../../helpers/utils';
3 |
4 | function AccountList(props) {
5 | const [accounts, setAccounts] = useState([]);
6 | const [isLoading, setIsLoading] = useState(false);
7 | const [error, setError] = useState(false);
8 |
9 | useEffect(async () => {
10 | setIsLoading(true);
11 | const response = await fetch(`/api/accounts?linkId=${props.linkId}`);
12 | const data = await response.json();
13 | if (response.ok) {
14 | setAccounts(data);
15 | setError(false);
16 | } else {
17 | setError(data.message);
18 | }
19 | setIsLoading(false);
20 | }, []);
21 |
22 | const handleButtonClick = (event) => {
23 | props.setAccountId(event.target.getAttribute('account-id'));
24 | };
25 |
26 | const handleReconnect = () => {
27 | props.setLinkId('');
28 | };
29 |
30 | return (
31 |
32 |
Balance de tus cuentas
33 | {error && (
34 |
35 |
Error
36 |
{error || 'Se produjo un error'}
37 |
Reconectar
38 |
39 | )}
40 | {!error
41 | && (
42 |
43 |
44 |
45 |
46 | Tipo de cuenta
50 | Nombre
54 | Saldo disponible
58 | Saldo contable
62 |
63 | Ver movimientos
64 |
65 |
66 |
67 |
68 | {!isLoading && accounts.map((account) => (
69 |
70 | {account.name}
71 | {account.holderName}
72 | {account.currency} {formatAmount(account.balance.available)}
73 | {account.currency} {formatAmount(account.balance.current)}
74 |
75 |
76 | Ver movimientos
77 |
78 |
79 |
80 | ))}
81 | {isLoading && (
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | )}
90 |
91 |
92 |
93 | )}
94 |
95 | );
96 | }
97 |
98 | export default AccountList;
99 |
--------------------------------------------------------------------------------
/frontend/src/imagotipo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Fintoc Quickstart
3 | **Fintoc Quickstart** is the repository that allows you to test Fintoc tools locally!
4 | The purpose of the repository is that you know how to integrate the Fintoc Widget and the SDKs that we offer for the different supported languages.
5 | **Fintoc Quickstart** is composed mainly by what we call a frontend application, which is an application written in [React](https://reactjs.org/) and that integrates the Fintoc [Widget](https://docs.fintoc.com/docs/usando-el-widget#flujo-del-widget), and also by backend applications which represent the backend and that use the SDKs that Fintoc provides.
6 | Currently the languages supported by the Fintoc SDKs are: [**Node.js**](https://github.com/fintoc-com/fintoc-node), [**Ruby**](https://github.com/fintoc-com/fintoc-ruby) and [**Python**](https://github.com/fintoc-com/fintoc-python), but if you are looking for another language do not worry, we are working to support others!
7 |
8 |
9 |
10 |
11 |
12 | # Table of content
13 | * [Fintoc Quickstart](#fintoc-quickstart)
14 | * [Table of content](#table-of-content)
15 | * [1. Clone the repository](#1-clone-the-repository)
16 | * [2. Get your keys and setup](#2-get-your-keys-and-setup)
17 | * [3. Run Fintoc Quickstart](#3-run-fintoc-quickstart)
18 | * [3.1. Expose backend to internet (ngrok)](#31-expose-backend-to-internet-ngrok)
19 | * [3.2. Run](#32-run)
20 | * [3.2.1. Prerequisites](#321-prerequisites)
21 | * [3.2.2. Commands](#322-commands)
22 | * [Frontend](#frontend)
23 | * [Node](#node)
24 | * [Python](#python)
25 | * [Ruby](#ruby)
26 | * [3.3. Run + Docker](#33-run--docker)
27 | * [3.3.1. Prerequisites](#331-prerequisites)
28 | * [3.3.2. Commands](#332-commands)
29 | * [help](#help)
30 | * [start](#start)
31 | * [build](#build)
32 | * [destroy](#destroy)
33 | * [stop](#stop)
34 | * [restart](#restart)
35 | * [logs](#logs)
36 |
37 | # 1. Clone the repository
38 | Clone the repository using your prefered terminal using any of the following commands:
39 |
40 | | Protocol | Command |
41 | |------------|----------------------------------------------------------------|
42 | | HTTPS | ```$ git clone https://github.com/fintoc-com/quickstart.git``` |
43 | | SSH | ```$ git clone git@github.com:fintoc-com/quickstart.git``` |
44 | | GitHub CLI | ```$ gh repo clone fintoc-com/quickstart``` |
45 |
46 | And go to the **Fintoc Quickstart** directory:
47 |
48 | ```$ cd quickstart```
49 | # 2. Get your keys and setup
50 | To obtain the keys it will be necessary that you have a Fintoc account created, and that you have a bank account associated with it.
51 | After that you can get the Public and Secret keys by accessing the API Keys page ([Dashboard](https://app.fintoc.com/api-keys) > API Keys). More information on the process can be found at: [docs.fintoc.com/docs/quickstart](https://docs.fintoc.com/docs/quickstart).
52 |
53 | Your keys will look like this:
54 |
55 | | Key | Value |
56 | |-----------------|------------------------------------|
57 | | YOUR_PUBLIC_KEY | pk_live_XXXXXXXXXXXXXXXXXXXXXXXXXX |
58 | | YOUR_SECRET_KEY | sk_live_XXXXXXXXXXXXXXXXXXXXXXXXXX |
59 |
60 | > **Note:** If you use your **TEST KEYS**, then your keys will have the word ```_test_``` instead of ```_live_```.
61 |
62 | Create an ```.env``` file (where the environment variables will be loaded) by copying our example ```.env.example``` file:
63 |
64 | ```shell
65 | $ cp .env.example .env
66 | ```
67 | And add the **Secret** and **Public** keys to the environment variables ```SECRET_KEY``` and ```REACT_APP_PUBLIC_KEY``` respectively, as shown below.
68 |
69 | ```.dotenv
70 | SECRET_KEY=
71 | REACT_APP_PUBLIC_KEY=
72 | ...
73 | ```
74 |
75 | # 3. Run Fintoc Quickstart
76 | **Fintoc Quickstart** allows you to run the services in two ways, directly or with [Docker](https://www.docker.com/).
77 | We recommend running it with **Docker** because it is very fast and simple!
78 | But if you want to run the applications directly, no problem, we provide you with the instructions to do so!
79 |
80 | ## 3.1. Expose backend to internet (ngrok)
81 | Widget integration requires exposing a backend endpoint (Webhook) to the internet, so that Fintoc can send a *link_token* to the backend application and with this access the necessary information.
82 | > **Note:** *link_token* is the token that allows access to the information belonging to the user who started the session.
83 | > More information about the Widget can be found at: [docs.fintoc.com/docs/usando-el-widget#flujo-del-widget](https://docs.fintoc.com/docs/usando-el-widget#flujo-del-widget).
84 |
85 | For this we will use [ngrok](https://ngrok.com/), which is a cross-platform program that enables developers to expose local servers behind NATs and firewalls to the public internet over secure tunnels with minimal effort.
86 | In this way, using **ngrok** we can expose the backend (and the webhook) so that Fintoc can communicate with it.
87 |
88 | To do this, download ngrok ([ngrok.com/download](https://ngrok.com/download)) for the operating system you use and execute the following command in a new session of the terminal:
89 |
90 | ```shell
91 | $ ./ngrok http 5000
92 | ```
93 | > **Note**: **Fintoc Quickstart** backend applications run (and expose) on port **5000**
94 |
95 | This will show a message in which we are interested in the line that says ```Forwarding https...```, as shown below:
96 | ```shell
97 | ...
98 | Forwarding https://xxxxxxxxxxxxxx.ngrok.io -> http://localhost:5000
99 | ...
100 | ```
101 | So we need to copy the public url (```https://xxxxxxxxxxxxxx.ngrok.io```), and paste it in the ```.env``` file to the ```REACT_APP_WEBHOOK_URL``` environment variable.
102 | At the end the ```.env``` file should look like this:
103 | ```.dotenv
104 | SECRET_KEY=
105 | REACT_APP_PUBLIC_KEY=
106 | REACT_APP_WEBHOOK_URL=/api/link_token
107 | ```
108 | > **Important:** It is important that the public url (```NGROK_PUBLIC_WEBHOOK_URL```) is suffixed with the path ```/api/link_token```, since that is the webhook endpoint that the backend applications of this project have.
109 | ## 3.2. Run
110 | ### 3.2.1. Prerequisites
111 | To run the applications directly it is necessary to have the language installed on your machine.
112 | * **node (frontend & node):** 10 or higher
113 | * **python:** 3.6.1 or higher
114 | * **ruby:** 2.7 or higher
115 |
116 | ### 3.2.2. Commands
117 | * #### Frontend
118 | ```shell
119 | $ cd frontend
120 | $ npm install
121 | $ npm start
122 | ```
123 | * #### Node
124 | ```shell
125 | $ cd node
126 | $ npm install
127 | $ npm start
128 | ```
129 | * #### Python
130 | ```shell
131 | $ cd python
132 |
133 | # optional: create a virtual environment to keep your dependencies clean
134 | $ virtualenv venv
135 | $ source ./venv/bin/activate
136 |
137 | $ pip install -r requirements.txt
138 | $ FLASK_APP=app.py flask run
139 | ```
140 |
141 | * #### Ruby
142 | ```shell
143 | $ cd ruby
144 | $ bundle install
145 | $ ruby app.rb
146 | ```
147 |
148 | ## 3.3. Run + Docker
149 | ### 3.3.1. Prerequisites
150 | To run **Fintoc Quickstart** with **Docker** you need to have:
151 | * [**Docker**](https://docs.docker.com/get-docker/)
152 | * [**GNU make**](https://www.gnu.org/software/make/manual/make.html)
153 |
154 | ### 3.3.2. Commands
155 | * #### help
156 | Show commands and their options.
157 | ```shell
158 | $ make help
159 | ```
160 |
161 | * #### start
162 | Start the containers (and builds them if they don't exist).
163 | ```shell
164 | $ make start language=[language]
165 | ```
166 | ```[language]``` can be one of the following options: ```node```, ```python``` or ```ruby```.
167 |
168 | * #### build
169 | Build the Docker images.
170 | ```shell
171 | $ make build
172 | ```
173 |
174 | * #### destroy
175 | Destroy the containers.
176 | ```shell
177 | $ make destroy
178 | ```
179 |
180 | * #### stop
181 | Stop the containers.
182 | ```shell
183 | $ make stop
184 | ```
185 |
186 | * #### restart
187 | Restart the containers.
188 | ```shell
189 | $ make restart
190 | ```
191 |
192 | * #### logs
193 | Show containers logs.
194 | ```shell
195 | $ make logs
196 | ```
197 |
--------------------------------------------------------------------------------