├── .editorconfig ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── enzyme-setup.js ├── examples ├── advanced │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── index.html │ │ ├── logo.png │ │ └── manifest.json │ ├── src │ │ ├── button.tsx │ │ ├── config.tsx │ │ ├── global.d.ts │ │ ├── index.tsx │ │ ├── reducers.ts │ │ ├── styles.ts │ │ └── wrapper.tsx │ ├── tsconfig.json │ ├── tsconfig.prod.json │ └── tslint.json ├── basic │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ │ ├── index.html │ │ ├── logo.png │ │ └── manifest.json │ ├── src │ │ ├── button.tsx │ │ ├── config.ts │ │ ├── index.tsx │ │ └── styles.ts │ ├── tsconfig.json │ ├── tsconfig.prod.json │ └── tslint.json └── redux │ ├── .gitignore │ ├── README.md │ ├── package.json │ ├── public │ ├── index.html │ ├── logo.png │ └── manifest.json │ ├── src │ ├── button.tsx │ ├── config.tsx │ ├── global.d.ts │ ├── index.tsx │ ├── reducers.ts │ ├── styles.ts │ └── wrapper.tsx │ ├── tsconfig.json │ ├── tsconfig.prod.json │ └── tslint.json ├── jest.json ├── logo.png ├── package.json ├── rollup.config.js ├── src ├── index.ts ├── paymentRequest.fixture.ts ├── paymentRequest.test.tsx ├── paymentRequest.tsx └── types.ts ├── tsconfig.json └── tslint.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | dist 4 | lib 5 | .DS_Store 6 | .rpt2_cache 7 | coverage 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | examples 3 | logo.png 4 | .rpt2_cache 5 | coverage 6 | src -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - '8' 5 | 6 | install: 7 | - npm install 8 | 9 | script: 10 | - npm run test 11 | - npm run build 12 | 13 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-present, Marco Lanaro 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | React high order component to drive payment request widget on react applications 💳. 7 | 8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |

22 | 23 | 24 | ## Browser support 25 | 26 | [Payment request api](https://developers.google.com/web/fundamentals/getting-started/primers/payment-request/) is supported on Chrome for desktop v. ^61.0, Chrome for Android and Android Webview v. ^56.0, Microsoft Edge v. ^15.0. 27 | 28 | ## Demo 29 | 30 | You can find a working demo [here](https://lanaro.net/react-payment-request-api/). Be sure to use a supported browser. 31 | 32 | ## NPM Install 33 | 34 | ```bash 35 | npm install react-payment-request-api --save 36 | ``` 37 | 38 | ## Usage 39 | 40 | Consume the UI component in the hight order component `button.js`: 41 | 42 | ```js 43 | import React from "react"; 44 | import paymentRequest from 'react-payment-request-api'; 45 | 46 | const Button = ({ show, isSupported }) => isSupported 47 | ? 48 | : Payment request not supported; 49 | 50 | export default paymentRequest()(Button); 51 | ``` 52 | 53 | Pass the configuration to the high order component `smartComponent.js`: 54 | 55 | ```js 56 | import React from "react"; 57 | 58 | import Button from "./button"; 59 | 60 | const SmartComponent = (config) => 61 | 22 | ) 23 | : Payment request not supported; 24 | 25 | const ConnectedButton = connect((state) => ({ 26 | payed: state.payed, 27 | }))(Button); 28 | 29 | export default paymentRequest()(ConnectedButton); 30 | -------------------------------------------------------------------------------- /examples/advanced/src/config.tsx: -------------------------------------------------------------------------------- 1 | import { PaymentRequestParams } from 'react-payment-request-api'; 2 | 3 | const details: PaymentDetailsInit = { 4 | displayItems: [{ 5 | label: 'Original donation amount', 6 | amount: { currency: 'USD', value: '65.00' }, 7 | }, { 8 | label: 'Friends and family discount', 9 | amount: { currency: 'USD', value: '-10.00' }, 10 | }, { 11 | label: 'Delivery tax', 12 | pending: true, 13 | amount: { currency: 'USD', value: '10.00' }, 14 | }], 15 | total: { 16 | label: 'Total due', 17 | amount: { currency: 'USD', value : '55.00' }, 18 | }, 19 | }; 20 | 21 | const getConfig = (supportedPaymentCards: string[], onShowSuccess: () => void) => ({ 22 | methodData: [ 23 | { 24 | supportedMethods: ['basic-card'], 25 | data: { 26 | supportedNetworks: ['visa', 'mastercard', 'diners'], 27 | }, 28 | }, 29 | { 30 | supportedMethods: ['https://android.com/pay'], 31 | data: { 32 | merchantId: 'fake', 33 | environment: 'TEST', 34 | allowedCardNetwork: ['AMEX', 'MASTERCARD', 'VISA', 'DISCOVER'], 35 | paymentMethodTokenizationParameters: { 36 | tokenizationType: 'GATEWAY_TOKEN', 37 | parameters: { 38 | 'gateway': 'stripe', 39 | 'stripe:publishableKey': 'fake', 40 | 'stripe:version': '2016-07-06' 41 | }, 42 | }, 43 | }, 44 | }, 45 | { 46 | supportedMethods: 'https://apple.com/apple-pay', 47 | data: { 48 | version: 3, 49 | merchantIdentifier: 'merchant.com.example', 50 | merchantCapabilities: ['supportsDebit'], 51 | supportedNetworks: ['masterCard', 'visa'], 52 | countryCode: 'US', 53 | }, 54 | }, 55 | ], 56 | details: details, 57 | options: { 58 | requestShipping: true, 59 | requestPayerEmail: true, 60 | requestPayerPhone: true, 61 | }, 62 | onShowSuccess: (result, resolve, reject): void => { 63 | /* tslint:disable-next-line:no-console */ 64 | console.log('Result:', result); 65 | // make the payment 66 | setTimeout(() => { onShowSuccess(); resolve(); }, 2000); 67 | }, 68 | /* tslint:disable-next-line:no-console */ 69 | onShowFail: (error) => console.log('Error', error), 70 | onShippingAddressChange: (request, resolve, reject): void => { 71 | /* tslint:disable-next-line:no-console */ 72 | console.log('ShippingAddress:', request.shippingAddress); 73 | // recalculate details 74 | details.shippingOptions = [{ 75 | id: 'all', 76 | label: 'Wherever you want for free', 77 | amount: { currency: 'USD', value: '0.00' }, 78 | selected: true 79 | }]; 80 | details.displayItems![2] = { 81 | label: 'Tax', 82 | pending: false, 83 | amount: { currency: 'USD', value: '8.00' }, 84 | }; 85 | resolve(details); 86 | }, 87 | onShippingOptionChange: (request, resolve, reject): void => { 88 | resolve(details); 89 | }, 90 | // tslint:disable-next-line:no-any 91 | onMerchantValidation: (event: any): void => { 92 | event.complete(Promise.resolve(event.validationURL)); 93 | }, 94 | }) as PaymentRequestParams; 95 | 96 | export default getConfig; 97 | -------------------------------------------------------------------------------- /examples/advanced/src/global.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | __REDUX_DEVTOOLS_EXTENSION__?(): any; // tslint:disable-line:no-any 3 | } 4 | -------------------------------------------------------------------------------- /examples/advanced/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createStore, applyMiddleware } from 'redux'; 5 | import thunk from 'redux-thunk'; 6 | 7 | import reducers from './reducers'; 8 | import Wrapper from './wrapper'; 9 | import styles from './styles'; 10 | 11 | const store = createStore( 12 | reducers, 13 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), 14 | applyMiddleware(thunk) 15 | ); 16 | 17 | const App = () => ( 18 | 19 |
20 |
21 |

React Payment Request API

22 |

23 | High order component to drive 24 | Payment Request 25 | widget on react applications 💳 26 |

27 |

28 | This component generate the configuration from a smart component connected to redux. 29 |
30 | It will also dispatch a redux action on success. 31 |

32 |
33 | 34 |
35 |
36 | 37 | ); 38 | 39 | ReactDOM.render(, document.getElementById('app')); 40 | -------------------------------------------------------------------------------- /examples/advanced/src/reducers.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'redux-actions'; 2 | 3 | export interface State { 4 | supportedPaymentCards: string[]; 5 | } 6 | 7 | const initialState = { 8 | supportedPaymentCards: ['visa'], 9 | payed: false, 10 | }; 11 | 12 | const reducers = (previousState: State = initialState, action: Action) => { // tslint:disable-line:no-any 13 | switch (action.type) { 14 | case 'PROCESSING_PAYMENT': 15 | return ({ ...previousState, payed: true }); 16 | default: 17 | return ({ ...previousState }); 18 | } 19 | }; 20 | 21 | export default reducers; 22 | -------------------------------------------------------------------------------- /examples/advanced/src/styles.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default { 4 | container: { 5 | display: 'flex', 6 | flexDirection: 'column', 7 | padding: '1rem', 8 | alignItems: 'center', 9 | } as React.CSSProperties, 10 | a: { 11 | color: '#48b5f2', 12 | }, 13 | h1: { 14 | fontSize: '1.7rem', 15 | }, 16 | h2: { 17 | color: '#0f4b6d', 18 | fontSize: '1.2rem', 19 | }, 20 | logo: { 21 | backgroundImage: 'url(https://raw.githubusercontent.com/marcolanaro/react-payment-request-api/master/logo.png)', 22 | backgroundSize: 'cover', 23 | backgroundPosition: 'center', 24 | width: '10rem', 25 | height: '10rem', 26 | minWidth: '10rem', 27 | minHeight: '10rem', 28 | borderRadius: '0.1875rem', 29 | }, 30 | content: { 31 | display: 'flex', 32 | flexGrow: 1, 33 | flexDirection: 'column', 34 | justifyContent: 'center', 35 | } as React.CSSProperties, 36 | button: { 37 | boxShadow: '0 0 1px #fff inset', 38 | borderRadius: '1rem', 39 | padding: '1rem 2rem', 40 | font: '3rem Calibri, Arial, sans-serif', 41 | textShadow: '1px 1px 0 rgba(255,255,255,0.4)', 42 | }, 43 | toPay: { 44 | backgroundColor: '#48b5f2', 45 | border: '1px solid #84acc3', 46 | color: '#0f4b6d', 47 | }, 48 | payed: { 49 | backgroundColor: '#48f28c', 50 | border: '1px solid #84c38c', 51 | color: '#0f6d1c', 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /examples/advanced/src/wrapper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Dispatch } from 'redux'; 4 | 5 | import Button from './button'; 6 | import getConfig from './config'; 7 | import { State } from './reducers'; 8 | 9 | interface StateProps { 10 | supportedPaymentCards: string[]; 11 | } 12 | 13 | interface DispatchProps { 14 | onShowSuccess: () => void; 15 | } 16 | 17 | interface OwnProps { 18 | style: React.CSSProperties; 19 | } 20 | 21 | const Wrapper: React.StatelessComponent = ({ 22 | style, supportedPaymentCards, onShowSuccess, 23 | }) => ( 24 | 12 | : Payment request not supported; 13 | 14 | export default paymentRequest()(Button); 15 | -------------------------------------------------------------------------------- /examples/basic/src/config.ts: -------------------------------------------------------------------------------- 1 | import { PaymentRequestParams } from 'react-payment-request-api'; 2 | 3 | const details: PaymentDetailsInit = { 4 | displayItems: [{ 5 | label: 'Original donation amount', 6 | amount: { currency: 'USD', value: '65.00' }, 7 | }, { 8 | label: 'Friends and family discount', 9 | amount: { currency: 'USD', value: '-10.00' }, 10 | }, { 11 | label: 'Delivery tax', 12 | pending: true, 13 | amount: { currency: 'USD', value: '10.00' }, 14 | }], 15 | total: { 16 | label: 'Total due', 17 | amount: { currency: 'USD', value : '55.00' }, 18 | }, 19 | }; 20 | 21 | const getConfig = () => ({ 22 | methodData: [{ 23 | supportedMethods: ['basic-card'], 24 | data: { 25 | supportedNetworks: ['visa', 'mastercard', 'diners'], 26 | }, 27 | }], 28 | details: details, 29 | options: { 30 | requestShipping: true, 31 | requestPayerEmail: true, 32 | requestPayerPhone: true, 33 | }, 34 | onShowSuccess: (result, resolve, reject): void => { 35 | /* tslint:disable-next-line:no-console */ 36 | console.log('Result:', result); 37 | // make the payment 38 | setTimeout(resolve, 2000); 39 | }, 40 | /* tslint:disable-next-line:no-console */ 41 | onShowFail: (error) => console.log('Error', error), 42 | onShippingAddressChange: (request, resolve, reject): void => { 43 | /* tslint:disable-next-line:no-console */ 44 | console.log('ShippingAddress:', request.shippingAddress); 45 | // recalculate details 46 | details.shippingOptions = [{ 47 | id: 'all', 48 | label: 'Wherever you want for free', 49 | amount: { currency: 'USD', value: '0.00' }, 50 | selected: true 51 | }]; 52 | details.displayItems![2] = { 53 | label: 'Tax', 54 | pending: false, 55 | amount: { currency: 'USD', value: '8.00' }, 56 | }; 57 | resolve(details); 58 | }, 59 | onShippingOptionChange: (request, resolve, reject): void => { 60 | resolve(details); 61 | }, 62 | }) as PaymentRequestParams; 63 | 64 | export default getConfig; 65 | -------------------------------------------------------------------------------- /examples/basic/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | 4 | import Button from './button'; 5 | import getConfig from './config'; 6 | import styles from './styles'; 7 | 8 | const App = () => ( 9 |
10 |
11 |

React Payment Request API

12 |

13 | High order component to drive   14 | Payment Request 15 |   widget on react applications 💳 16 |

17 |

This component accept a standalone configuration and will log the result on the console.

18 |
19 |
21 |
22 | ); 23 | 24 | ReactDOM.render(, document.getElementById('app')); 25 | -------------------------------------------------------------------------------- /examples/basic/src/styles.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default { 4 | container: { 5 | display: 'flex', 6 | flexDirection: 'column', 7 | padding: '1rem', 8 | alignItems: 'center', 9 | } as React.CSSProperties, 10 | a: { 11 | color: '#48b5f2', 12 | }, 13 | h1: { 14 | fontSize: '1.7rem', 15 | }, 16 | h2: { 17 | color: '#0f4b6d', 18 | fontSize: '1.2rem', 19 | }, 20 | logo: { 21 | backgroundImage: 'url(https://raw.githubusercontent.com/marcolanaro/react-payment-request-api/master/logo.png)', 22 | backgroundSize: 'cover', 23 | backgroundPosition: 'center', 24 | width: '10rem', 25 | height: '10rem', 26 | minWidth: '10rem', 27 | minHeight: '10rem', 28 | borderRadius: '0.1875rem', 29 | }, 30 | content: { 31 | display: 'flex', 32 | flexGrow: 1, 33 | flexDirection: 'column', 34 | justifyContent: 'center', 35 | } as React.CSSProperties, 36 | button: { 37 | backgroundColor: '#48b5f2', 38 | border: '1px solid #84acc3', 39 | color: '#0f4b6d', 40 | boxShadow: '0 0 1px #fff inset', 41 | borderRadius: '1rem', 42 | padding: '1rem 2rem', 43 | font: '3rem Calibri, Arial, sans-serif', 44 | textShadow: '1px 1px 0 rgba(255,255,255,0.4)', 45 | }, 46 | }; 47 | -------------------------------------------------------------------------------- /examples/basic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "build/dist", 4 | "module": "commonjs", 5 | "target": "es5", 6 | "lib": ["es6", "dom"], 7 | "sourceMap": true, 8 | "allowJs": true, 9 | "jsx": "react", 10 | "moduleResolution": "node", 11 | "rootDir": "src", 12 | "forceConsistentCasingInFileNames": true, 13 | "noImplicitReturns": true, 14 | "noImplicitThis": true, 15 | "noImplicitAny": true, 16 | "skipLibCheck": true, 17 | "strictNullChecks": true, 18 | "suppressImplicitAnyIndexErrors": true, 19 | "noUnusedLocals": true 20 | }, 21 | "exclude": [ 22 | "node_modules", 23 | "build", 24 | "scripts", 25 | "acceptance-tests", 26 | "webpack", 27 | "jest", 28 | "src/setupTests.ts" 29 | ], 30 | "types": [ 31 | "typePatches" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /examples/basic/tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | } -------------------------------------------------------------------------------- /examples/basic/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint-react"], 3 | "rules": { 4 | "align": [ 5 | true, 6 | "parameters", 7 | "arguments", 8 | "statements" 9 | ], 10 | "ban": false, 11 | "class-name": true, 12 | "comment-format": [ 13 | true, 14 | "check-space" 15 | ], 16 | "curly": true, 17 | "eofline": false, 18 | "forin": true, 19 | "indent": [ true, "spaces" ], 20 | "interface-name": [true, "never-prefix"], 21 | "jsdoc-format": true, 22 | "jsx-no-lambda": false, 23 | "jsx-no-multiline-js": false, 24 | "label-position": true, 25 | "max-line-length": [ true, 120 ], 26 | "member-ordering": [ 27 | true, 28 | "public-before-private", 29 | "static-before-instance", 30 | "variables-before-functions" 31 | ], 32 | "no-any": true, 33 | "no-arg": true, 34 | "no-bitwise": true, 35 | "no-console": [ 36 | true, 37 | "log", 38 | "error", 39 | "debug", 40 | "info", 41 | "time", 42 | "timeEnd", 43 | "trace" 44 | ], 45 | "no-consecutive-blank-lines": true, 46 | "no-construct": true, 47 | "no-debugger": true, 48 | "no-duplicate-variable": true, 49 | "no-empty": true, 50 | "no-eval": true, 51 | "no-shadowed-variable": true, 52 | "no-string-literal": true, 53 | "no-switch-case-fall-through": true, 54 | "no-trailing-whitespace": false, 55 | "no-unused-expression": true, 56 | "no-use-before-declare": true, 57 | "one-line": [ 58 | true, 59 | "check-catch", 60 | "check-else", 61 | "check-open-brace", 62 | "check-whitespace" 63 | ], 64 | "quotemark": [true, "single", "jsx-double"], 65 | "radix": true, 66 | "semicolon": [true, "always"], 67 | "switch-default": true, 68 | 69 | "trailing-comma": false, 70 | 71 | "triple-equals": [ true, "allow-null-check" ], 72 | "typedef": [ 73 | true, 74 | "parameter", 75 | "property-declaration" 76 | ], 77 | "typedef-whitespace": [ 78 | true, 79 | { 80 | "call-signature": "nospace", 81 | "index-signature": "nospace", 82 | "parameter": "nospace", 83 | "property-declaration": "nospace", 84 | "variable-declaration": "nospace" 85 | } 86 | ], 87 | "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore", "allow-pascal-case"], 88 | "whitespace": [ 89 | true, 90 | "check-branch", 91 | "check-decl", 92 | "check-module", 93 | "check-operator", 94 | "check-separator", 95 | "check-type", 96 | "check-typecast" 97 | ] 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/redux/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /build 3 | .DS_Store 4 | npm-debug.log* 5 | -------------------------------------------------------------------------------- /examples/redux/README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | React high order component to drive payment request widget on react applications 💳. 7 | 8 |

9 | 10 | ## Redux Demo 11 | 12 | This component generate the configuration from a smart component connected to redux. 13 | 14 | It will also dispatch a redux action on success. 15 | 16 | ## Run the demo 17 | 18 | Install: 19 | 20 | ```bash 21 | npm install 22 | ``` 23 | 24 | Run 25 | 26 | ```bash 27 | npm start 28 | ``` 29 | 30 | Open your browser at [http://localhost:8080/](http://localhost:8080/). 31 | 32 | 33 | ## License 34 | 35 | See the [LICENSE](../../LICENSE.md) file for license rights and limitations (MIT). 36 | -------------------------------------------------------------------------------- /examples/redux/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-payment-request-api-example-redux", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@types/jest": "^21.1.4", 7 | "@types/node": "^8.0.46", 8 | "@types/react": "^16.4.14", 9 | "@types/react-dom": "^16.0.7", 10 | "react": "^16.5.2", 11 | "react-dom": "^16.5.2", 12 | "react-payment-request-api": "^3.0.0", 13 | "react-redux": "^5.0.6", 14 | "redux": "^3.7.2", 15 | "redux-thunk": "^2.2.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react-redux": "^4.4.43", 19 | "@types/redux": "^3.6.0", 20 | "@types/redux-actions": "^1.2.6", 21 | "react-scripts-ts": "2.17.0", 22 | "typescript": "^3.0.3" 23 | }, 24 | "scripts": { 25 | "start": "react-scripts-ts start", 26 | "build": "react-scripts-ts build", 27 | "eject": "react-scripts-ts eject" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/redux/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React - Payment request API 10 | 29 | 30 | 31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/redux/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcolanaro/react-payment-request-api/4b7c14feb9ac5b79bdd17aa38a89dce503ceac01/examples/redux/public/logo.png -------------------------------------------------------------------------------- /examples/redux/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Payment Request", 3 | "name": "React Payment Request API", 4 | "icons": [ 5 | { 6 | "src": "logo.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /examples/redux/src/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import paymentRequest, { PaymentRequestInterface } from 'react-payment-request-api'; 3 | import { connect } from 'react-redux'; 4 | 5 | import styles from './styles'; 6 | 7 | export interface OwnProps { 8 | style: React.CSSProperties; 9 | } 10 | 11 | export interface StateProps { 12 | payed: boolean; 13 | } 14 | 15 | const Button: React.StatelessComponent = ({ 16 | show, isSupported, style, payed, 17 | }) => isSupported 18 | ? ( 19 | 22 | ) 23 | : Payment request not supported; 24 | 25 | const ConnectedButton = connect((state) => ({ 26 | payed: state.payed, 27 | }))(Button); 28 | 29 | export default paymentRequest()(ConnectedButton); 30 | -------------------------------------------------------------------------------- /examples/redux/src/config.tsx: -------------------------------------------------------------------------------- 1 | import { PaymentRequestParams } from 'react-payment-request-api'; 2 | 3 | const details: PaymentDetailsInit = { 4 | displayItems: [{ 5 | label: 'Original donation amount', 6 | amount: { currency: 'USD', value: '65.00' }, 7 | }, { 8 | label: 'Friends and family discount', 9 | amount: { currency: 'USD', value: '-10.00' }, 10 | }, { 11 | label: 'Delivery tax', 12 | pending: true, 13 | amount: { currency: 'USD', value: '10.00' }, 14 | }], 15 | total: { 16 | label: 'Total due', 17 | amount: { currency: 'USD', value : '55.00' }, 18 | }, 19 | }; 20 | 21 | const getConfig = (supportedPaymentCards: string[], onShowSuccess: () => void) => ({ 22 | methodData: [{ 23 | supportedMethods: ['basic-card'], 24 | data: { 25 | supportedNetworks: supportedPaymentCards, 26 | }, 27 | }], 28 | details: details, 29 | options: { 30 | requestShipping: true, 31 | requestPayerEmail: true, 32 | requestPayerPhone: true, 33 | }, 34 | onShowSuccess: (result, resolve, reject): void => { 35 | /* tslint:disable-next-line:no-console */ 36 | console.log('Result:', result); 37 | // make the payment 38 | setTimeout(() => { onShowSuccess(); resolve(); }, 2000); 39 | }, 40 | /* tslint:disable-next-line:no-console */ 41 | onShowFail: (error) => console.log('Error', error), 42 | onShippingAddressChange: (request, resolve, reject): void => { 43 | /* tslint:disable-next-line:no-console */ 44 | console.log('ShippingAddress:', request.shippingAddress); 45 | // recalculate details 46 | details.shippingOptions = [{ 47 | id: 'all', 48 | label: 'Wherever you want for free', 49 | amount: { currency: 'USD', value: '0.00' }, 50 | selected: true 51 | }]; 52 | details.displayItems![2] = { 53 | label: 'Tax', 54 | pending: false, 55 | amount: { currency: 'USD', value: '8.00' }, 56 | }; 57 | resolve(details); 58 | }, 59 | onShippingOptionChange: (request, resolve, reject): void => { 60 | resolve(details); 61 | }, 62 | }) as PaymentRequestParams; 63 | 64 | export default getConfig; 65 | -------------------------------------------------------------------------------- /examples/redux/src/global.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | __REDUX_DEVTOOLS_EXTENSION__?(): any; // tslint:disable-line:no-any 3 | } 4 | -------------------------------------------------------------------------------- /examples/redux/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { createStore, applyMiddleware } from 'redux'; 5 | import thunk from 'redux-thunk'; 6 | 7 | import reducers from './reducers'; 8 | import Wrapper from './wrapper'; 9 | import styles from './styles'; 10 | 11 | const store = createStore( 12 | reducers, 13 | window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(), 14 | applyMiddleware(thunk) 15 | ); 16 | 17 | const App = () => ( 18 | 19 |
20 |
21 |

React Payment Request API

22 |

23 | High order component to drive 24 | Payment Request 25 | widget on react applications 💳 26 |

27 |

28 | This component generate the configuration from a smart component connected to redux. 29 |
30 | It will also dispatch a redux action on success. 31 |

32 |
33 | 34 |
35 |
36 | 37 | ); 38 | 39 | ReactDOM.render(, document.getElementById('app')); 40 | -------------------------------------------------------------------------------- /examples/redux/src/reducers.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'redux-actions'; 2 | 3 | export interface State { 4 | supportedPaymentCards: string[]; 5 | } 6 | 7 | const initialState = { 8 | supportedPaymentCards: ['visa'], 9 | payed: false, 10 | }; 11 | 12 | const reducers = (previousState: State = initialState, action: Action) => { // tslint:disable-line:no-any 13 | switch (action.type) { 14 | case 'PROCESSING_PAYMENT': 15 | return ({ ...previousState, payed: true }); 16 | default: 17 | return ({ ...previousState }); 18 | } 19 | }; 20 | 21 | export default reducers; 22 | -------------------------------------------------------------------------------- /examples/redux/src/styles.ts: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | 3 | export default { 4 | container: { 5 | display: 'flex', 6 | flexDirection: 'column', 7 | padding: '1rem', 8 | alignItems: 'center', 9 | } as React.CSSProperties, 10 | a: { 11 | color: '#48b5f2', 12 | }, 13 | h1: { 14 | fontSize: '1.7rem', 15 | }, 16 | h2: { 17 | color: '#0f4b6d', 18 | fontSize: '1.2rem', 19 | }, 20 | logo: { 21 | backgroundImage: 'url(https://raw.githubusercontent.com/marcolanaro/react-payment-request-api/master/logo.png)', 22 | backgroundSize: 'cover', 23 | backgroundPosition: 'center', 24 | width: '10rem', 25 | height: '10rem', 26 | minWidth: '10rem', 27 | minHeight: '10rem', 28 | borderRadius: '0.1875rem', 29 | }, 30 | content: { 31 | display: 'flex', 32 | flexGrow: 1, 33 | flexDirection: 'column', 34 | justifyContent: 'center', 35 | } as React.CSSProperties, 36 | button: { 37 | boxShadow: '0 0 1px #fff inset', 38 | borderRadius: '1rem', 39 | padding: '1rem 2rem', 40 | font: '3rem Calibri, Arial, sans-serif', 41 | textShadow: '1px 1px 0 rgba(255,255,255,0.4)', 42 | }, 43 | toPay: { 44 | backgroundColor: '#48b5f2', 45 | border: '1px solid #84acc3', 46 | color: '#0f4b6d', 47 | }, 48 | payed: { 49 | backgroundColor: '#48f28c', 50 | border: '1px solid #84c38c', 51 | color: '#0f6d1c', 52 | }, 53 | }; 54 | -------------------------------------------------------------------------------- /examples/redux/src/wrapper.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Dispatch } from 'redux'; 4 | 5 | import Button from './button'; 6 | import getConfig from './config'; 7 | import { State } from './reducers'; 8 | 9 | interface StateProps { 10 | supportedPaymentCards: string[]; 11 | } 12 | 13 | interface DispatchProps { 14 | onShowSuccess: () => void; 15 | } 16 | 17 | interface OwnProps { 18 | style: React.CSSProperties; 19 | } 20 | 21 | const Wrapper: React.StatelessComponent = ({ 22 | style, supportedPaymentCards, onShowSuccess, 23 | }) => ( 24 |