├── .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 |
--------------------------------------------------------------------------------