├── .env.example ├── src ├── config.tsx ├── api │ ├── api.ts │ ├── donationGet.ts │ └── donationPost.ts ├── index.html ├── index.tsx └── App.tsx ├── .gitignore ├── README.md ├── webpack.config.js ├── babel.config.js └── package.json /.env.example: -------------------------------------------------------------------------------- 1 | PORT=5667 2 | API_URL=http://localhost:5666 3 | -------------------------------------------------------------------------------- /src/config.tsx: -------------------------------------------------------------------------------- 1 | export const config = { 2 | API_URL: process.env.API_URL, 3 | }; 4 | -------------------------------------------------------------------------------- /src/api/api.ts: -------------------------------------------------------------------------------- 1 | import { config } from '../config'; 2 | 3 | export const baseUrl = config.API_URL; 4 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenPix Frontend Integration 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import 'core-js/stable'; 2 | import 'isomorphic-fetch'; 3 | import 'regenerator-runtime/runtime'; 4 | 5 | import React from 'react'; 6 | import ReactDOM from 'react-dom'; 7 | import App from './App'; 8 | 9 | ReactDOM.render( 10 | 11 | 12 | , 13 | document.getElementById('root') 14 | ); 15 | -------------------------------------------------------------------------------- /.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 | /dist 14 | 15 | # notes 16 | notes/*.md 17 | 18 | # misc 19 | .DS_Store 20 | .eslintcache 21 | .env.local 22 | .env.development.local 23 | .env.test.local 24 | .env.production.local 25 | 26 | npm-debug.log* 27 | yarn-debug.log* 28 | yarn-error.log* 29 | 30 | # vscode 31 | .vscode 32 | .env 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenPix Frontend Integration 2 | 3 | This is a simple React app that consumes the Node Backend Integration using OpenPix 4 | 5 | It will generate a donation based on Pix and render it in a QRCode 6 | 7 | ## Setup 8 | Generate a [App ID](https://developers.openpix.com.br/docs/plugin/app-id) in your OpenPix Account 9 | 10 | Run the [Node Backend Integration](https://github.com/Open-Pix/node-backend-integration) code 11 | 12 | Create a .env file with the following data 13 | 14 | ```jsx 15 | PORT=5667 16 | API_URL=http://localhost:5666 17 | ``` 18 | 19 | ## Running 20 | 21 | ```jsx 22 | yarn start 23 | ``` 24 | -------------------------------------------------------------------------------- /src/api/donationGet.ts: -------------------------------------------------------------------------------- 1 | import { baseUrl } from './api'; 2 | 3 | const url = (id: string) => `/donation/${id}`; 4 | 5 | const getUrl = (id: string) => `${baseUrl}${url(id)}`; 6 | 7 | export type Donation = { 8 | id: string, 9 | value: number; 10 | comment: string; 11 | status: string; 12 | brCode: string; 13 | } 14 | 15 | export const donationGet = async (id: string): Promise => { 16 | const response = await fetch(getUrl(id), { 17 | method: 'GET', 18 | headers: { 19 | Accept: 'application/json', 20 | 'Content-Type': 'application/json', 21 | }, 22 | }); 23 | 24 | const data = await response.json(); 25 | 26 | return data; 27 | } 28 | -------------------------------------------------------------------------------- /src/api/donationPost.ts: -------------------------------------------------------------------------------- 1 | import { baseUrl } from './api'; 2 | 3 | const url = '/donation' 4 | 5 | const getUrl = () => `${baseUrl}${url}`; 6 | 7 | export type DonationPostResponse = { 8 | id: string, 9 | value: number; 10 | comment: string; 11 | status: string; 12 | brCode: string; 13 | } 14 | export type DonationPayload = { 15 | comment: string; 16 | value: number; 17 | }; 18 | export const donationPost = async (payload: DonationPayload): Promise => { 19 | const response = await fetch(getUrl(), { 20 | method: 'POST', 21 | headers: { 22 | Accept: 'application/json', 23 | 'Content-Type': 'application/json', 24 | }, 25 | body: JSON.stringify(payload), 26 | }); 27 | 28 | const data = await response.json(); 29 | 30 | return data; 31 | } 32 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 4 | const dotEnv = require('dotenv-webpack'); 5 | const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); 6 | 7 | const cwd = process.cwd(); 8 | const outputPath = path.join(cwd, 'build'); 9 | 10 | const PORT = parseInt(process.env.PORT || '5667'); 11 | 12 | module.exports = { 13 | mode: 'development', 14 | devtool: 'cheap-module-source-map', 15 | context: path.resolve(cwd, './'), 16 | entry: ['./src/index.tsx'], 17 | output: { 18 | path: outputPath, 19 | publicPath: '/', 20 | pathinfo: false, 21 | }, 22 | resolve: { 23 | extensions: ['.ts', '.tsx', '.js', '.json', '.mjs'], 24 | }, 25 | module: { 26 | rules: [ 27 | { 28 | test: /\.(js|jsx|ts|tsx)?$/, 29 | exclude: [/node_modules/], 30 | use: ['babel-loader?cacheDirectory'], 31 | }, 32 | { 33 | test: /\.(jpe?g|png|gif|svg|pdf|csv|xlsx|ttf|woff(2)?)$/i, 34 | use: [ 35 | { 36 | loader: 'file-loader', 37 | options: { 38 | name: '[name].[ext]', 39 | outputPath: 'img/', 40 | }, 41 | }, 42 | ], 43 | }, 44 | { 45 | test: /\.css$/, 46 | use: ['style-loader', 'css-loader'], 47 | }, 48 | ], 49 | }, 50 | devServer: { 51 | contentBase: outputPath, 52 | disableHostCheck: true, 53 | historyApiFallback: { 54 | disableDotRule: true, 55 | }, 56 | hot: true, 57 | hotOnly: false, 58 | compress: true, 59 | open: true, 60 | port: PORT, 61 | }, 62 | plugins: [ 63 | new dotEnv({ 64 | path: './.env', 65 | }), 66 | new ReactRefreshPlugin(), 67 | new HtmlWebpackPlugin({ 68 | template: './src/index.html', 69 | }), 70 | ], 71 | }; 72 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache.using(() => process.env.NODE_ENV); 3 | 4 | const enableFastRefresh = !api.env('production') && !api.env('test'); 5 | 6 | return { 7 | presets: [ 8 | [ 9 | '@babel/preset-react', 10 | { 11 | runtime: 'automatic', 12 | }, 13 | ], 14 | [ 15 | '@babel/preset-env', 16 | { 17 | corejs: 3, 18 | modules: false, 19 | useBuiltIns: 'usage', 20 | }, 21 | ], 22 | '@babel/preset-typescript', 23 | ], 24 | plugins: [ 25 | '@babel/plugin-proposal-object-rest-spread', 26 | '@babel/plugin-proposal-class-properties', 27 | '@babel/plugin-proposal-export-default-from', 28 | '@babel/plugin-proposal-export-namespace-from', 29 | '@babel/plugin-proposal-nullish-coalescing-operator', 30 | '@babel/plugin-proposal-optional-chaining', 31 | // Applies the react-refresh Babel plugin on non-production modes only 32 | ...(enableFastRefresh ? ['react-refresh/babel'] : []), 33 | ], 34 | env: { 35 | test: { 36 | presets: [ 37 | [ 38 | '@babel/preset-env', 39 | { 40 | corejs: 3, 41 | useBuiltIns: 'usage', 42 | }, 43 | ], 44 | [ 45 | '@babel/preset-react', 46 | { 47 | runtime: 'automatic', 48 | }, 49 | ], 50 | '@babel/preset-typescript', 51 | ], 52 | plugins: [ 53 | '@babel/plugin-transform-runtime', 54 | 'dynamic-import-node', 55 | '@babel/plugin-syntax-dynamic-import', 56 | '@babel/plugin-proposal-object-rest-spread', 57 | '@babel/plugin-proposal-class-properties', 58 | '@babel/plugin-proposal-export-default-from', 59 | '@babel/plugin-proposal-export-namespace-from', 60 | ], 61 | }, 62 | }, 63 | }; 64 | }; 65 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Text } from 'rebass'; 2 | import { useState } from 'react'; 3 | import { TextField, Button } from '@material-ui/core'; 4 | import QRCode from 'qrcode.react'; 5 | import { donationPost } from './api/donationPost'; 6 | import { donationGet, Donation } from './api/donationGet'; 7 | 8 | const App = () => { 9 | const [donation, setDonation] = useState(null); 10 | const [comment, setComment] = useState(''); 11 | const [error, setError] = useState(null); 12 | 13 | const createDonation = async () => { 14 | const payload = { 15 | comment, 16 | value: 1, // always 1 cent per now 17 | } 18 | const result = await donationPost(payload); 19 | 20 | console.log({ 21 | result, 22 | }); 23 | 24 | if (result.error) { 25 | setError(result.error); 26 | return; 27 | } 28 | 29 | setDonation(result); 30 | } 31 | 32 | const checkStatus = async () => { 33 | const result = await donationGet(donation.id); 34 | 35 | if (result.error) { 36 | setError(result.error); 37 | return; 38 | } 39 | 40 | setDonation(result); 41 | } 42 | 43 | console.log({ 44 | donation, 45 | }); 46 | 47 | const renderContent = () => { 48 | if (!donation) { 49 | return ( 50 | 51 | setComment(e.target.value)}/> 52 | 55 | {!!error && ({error.toString()})} 56 | 57 | ); 58 | } 59 | 60 | return ( 61 | 62 | Pay Donation 63 | 69 | 72 | Status: {donation.status} 73 | 74 | ) 75 | } 76 | 77 | return ( 78 | <> 79 | OpenPix Frontend Integration 80 | {renderContent()} 81 | 82 | ); 83 | } 84 | 85 | export default App; 86 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "openpix-frontend-integration", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "@material-ui/core": "4.11.0", 6 | "@material-ui/styles": "^4.11.3", 7 | "core-js": "^3.9.1", 8 | "isomorphic-fetch": "^3.0.0", 9 | "react": "17.0.1", 10 | "react-dom": "17.0.1", 11 | "rebass": "^4.0.7", 12 | "styled-components": "^5.2.1", 13 | "styled-system": "^5.1.5", 14 | "qrcode.react": "^1.0.1" 15 | }, 16 | "devDependencies": { 17 | "@babel/cli": "7.13.0", 18 | "@babel/core": "7.13.8", 19 | "@babel/plugin-proposal-class-properties": "7.13.0", 20 | "@babel/plugin-proposal-export-default-from": "7.12.13", 21 | "@babel/plugin-proposal-export-namespace-from": "7.12.13", 22 | "@babel/plugin-proposal-nullish-coalescing-operator": "7.13.8", 23 | "@babel/plugin-proposal-optional-chaining": "7.13.8", 24 | "@babel/plugin-transform-react-jsx-source": "7.12.13", 25 | "@babel/plugin-transform-typescript": "7.13.0", 26 | "@babel/preset-env": "7.13.8", 27 | "@babel/preset-react": "7.12.13", 28 | "@babel/preset-typescript": "7.13.0", 29 | "@pmmmwh/react-refresh-webpack-plugin": "0.4.3", 30 | "@types/babel__core": "7.1.12", 31 | "@types/babel__preset-env": "7.9.1", 32 | "@types/core-js": "^2.5.4", 33 | "@types/dotenv-safe": "^8.1.1", 34 | "@types/dotenv-webpack": "^5.0.0", 35 | "@types/hard-source-webpack-plugin": "1.0.1", 36 | "@types/isomorphic-fetch": "^0.0.35", 37 | "@types/qrcode.react": "^1.0.1", 38 | "@types/react": "17.0.1", 39 | "@types/react-dom": "17.0.1", 40 | "@types/rebass": "^4.0.8", 41 | "@types/styled-components": "^5.1.7", 42 | "@types/styled-system": "^5.1.10", 43 | "@types/terser-webpack-plugin": "5.0.2", 44 | "@types/unused-files-webpack-plugin": "3.4.0", 45 | "@types/webpack-dev-server": "3.11.1", 46 | "@types/webpack-plugin-serve": "1.2.0", 47 | "@webpack-cli/serve": "1.3.0", 48 | "autodll-webpack-plugin": "^0.4.2", 49 | "babel-loader": "8.2.2", 50 | "cache-loader": "4.1.0", 51 | "css-loader": "^5.1.1", 52 | "dotenv-safe": "^8.2.0", 53 | "dotenv-webpack": "^6.0.2", 54 | "hard-source-webpack-plugin": "0.13.1", 55 | "html-webpack-plugin": "5.2.0", 56 | "react-refresh": "0.9.0", 57 | "style-loader": "2.0.0", 58 | "terser-webpack-plugin": "5.1.1", 59 | "unused-files-webpack-plugin": "3.4.0", 60 | "webpack": "5.24.2", 61 | "webpack-cli": "4.5.0", 62 | "webpack-dev-server": "3.11.2", 63 | "webpack-merge": "5.7.3", 64 | "webpack-plugin-serve": "1.3.0" 65 | }, 66 | "license": "MIT", 67 | "scripts": { 68 | "start": "webpack serve --hot --config webpack.config.js --progress" 69 | } 70 | } 71 | --------------------------------------------------------------------------------