├── .babelrc ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── dist └── index.js ├── index.js ├── package.json ├── scripts └── mocha_runner.js └── src └── index.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-2", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "ecmaFeatures": { 9 | "modules": true 10 | }, 11 | "rules": { 12 | "no-bitwise": 2, 13 | "no-else-return": 2, 14 | "no-eq-null": 2, 15 | "no-extra-parens": 0, 16 | "no-floating-decimal": 2, 17 | "no-inner-declarations": [2, "both"], 18 | "no-lonely-if": 2, 19 | "no-multiple-empty-lines": [2, {"max": 3}], 20 | "no-self-compare": 2, 21 | "no-underscore-dangle": 0, 22 | "no-use-before-define": 0, 23 | "no-unused-expressions": 0, 24 | "no-void": 2, 25 | "brace-style": [2, "1tbs"], 26 | "camelcase": [1, {"properties": "never"}], 27 | "consistent-return": 0, 28 | "comma-style": [2, "last"], 29 | "complexity": [1, 12], 30 | "func-names": 0, 31 | "guard-for-in": 2, 32 | "indent": [2, 4], 33 | "max-len": [0, 120, 4], 34 | "new-cap": [2, {"newIsCap": true, "capIsNew": false}], 35 | "quotes": [2, "single"], 36 | "keyword-spacing": [2, {"before": true, "after": true}], 37 | "space-before-blocks": [2, "always"], 38 | "array-bracket-spacing": [2, "never"], 39 | "space-in-parens": [2, "never"], 40 | "strict": [0], 41 | "valid-jsdoc": 2, 42 | "wrap-iife": [2, "any"], 43 | "yoda": [1, "never"] 44 | }, 45 | "plugins": [ 46 | "react" 47 | ], 48 | "globals": { 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.iml 4 | .*.haste_cache.* 5 | .DS_Store 6 | .idea 7 | .babelrc 8 | .eslintrc 9 | npm-debug.log 10 | lib 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 thinhvo0108 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-paypal-express-checkout 2 | React component that renders Paypal's express check out button 3 | 4 | ## Install 5 | 6 | ```bash 7 | npm install --save react-paypal-express-checkout 8 | ``` 9 | 10 | or 11 | ```bash 12 | yarn add react-paypal-express-checkout 13 | ``` 14 | 15 | ## Usage 16 | 17 | ## Simplest Example (with minimum set of parameters, this will use the "sandbox" environment) 18 | 19 | ```javascript 20 | import PaypalExpressBtn from 'react-paypal-express-checkout'; 21 | 22 | export default class MyApp extends React.Component { 23 | render() { 24 | const client = { 25 | sandbox: 'Your-Sandbox-Client-ID', 26 | production: 'Your-Production-Client-ID', 27 | } 28 | return ( 29 | 30 | ); 31 | } 32 | } 33 | ``` 34 | 35 | ### Full Example 36 | 37 | ```javascript 38 | import React from 'react'; 39 | import PaypalExpressBtn from 'react-paypal-express-checkout'; 40 | 41 | export default class MyApp extends React.Component { 42 | render() { 43 | const onSuccess = (payment) => { 44 | // 1, 2, and ... Poof! You made it, everything's fine and dandy! 45 | console.log("Payment successful!", payment); 46 | // You can bind the "payment" object's value to your state or props or whatever here, please see below for sample returned data 47 | } 48 | 49 | const onCancel = (data) => { 50 | // The user pressed "cancel" or closed the PayPal popup 51 | console.log('Payment cancelled!', data); 52 | // You can bind the "data" object's value to your state or props or whatever here, please see below for sample returned data 53 | } 54 | 55 | const onError = (err) => { 56 | // The main Paypal script could not be loaded or something blocked the script from loading 57 | console.log("Error!", err); 58 | // Because the Paypal's main script is loaded asynchronously from "https://www.paypalobjects.com/api/checkout.js" 59 | // => sometimes it may take about 0.5 second for everything to get set, or for the button to appear 60 | } 61 | 62 | let env = 'sandbox'; // you can set this string to 'production' 63 | let currency = 'USD'; // you can set this string from your props or state 64 | let total = 1; // this is the total amount (based on currency) to charge 65 | // Document on Paypal's currency code: https://developer.paypal.com/docs/classic/api/currency_codes/ 66 | 67 | const client = { 68 | sandbox: 'YOUR-SANDBOX-APP-ID', 69 | production: 'YOUR-PRODUCTION-APP-ID', 70 | } 71 | // In order to get production's app-ID, you will have to send your app to Paypal for approval first 72 | // For your sandbox Client-ID (after logging into your developer account, please locate the "REST API apps" section, click "Create App" unless you have already done so): 73 | // => https://developer.paypal.com/docs/classic/lifecycle/sb_credentials/ 74 | // Note: IGNORE the Sandbox test AppID - this is ONLY for Adaptive APIs, NOT REST APIs) 75 | // For production app-ID: 76 | // => https://developer.paypal.com/docs/classic/lifecycle/goingLive/ 77 | 78 | // NB. You can also have many Paypal express checkout buttons on page, just pass in the correct amount and they will work! 79 | return ( 80 | 81 | ); 82 | } 83 | } 84 | ``` 85 | 86 | ### Props 87 | 88 | - `env: String (default: "sandbox")` - You can set this to "production" for production 89 | - `client: Object (with "sandbox" and "production" as keys)` - MUST set, please see the above example 90 | - `currency: String ("USD", "JPY" etc.)` - MUST set, please see the above example 91 | - `total: Number (1.00 or 1 - depends on currency)` - MUST set, please see the above example 92 | - `shipping: Number (1 or 2 or 3)` - Ability to change if a shipping address is included in checkout, 0 is default and means optional, 1 is no shipping address, 2 is shipping is required 93 | - `onError: Callback function (happens when Paypal's main script cannot be loaded)` - If not set, this will take the above function (in example) as a default return 94 | - `onSuccess: Callback function (happens after payment has been finished successfully)` - If not set, this will take the above function (in example) as a default return 95 | - `onCancel: Callback function (happens when users press "cancel" or close Paypal's popup)` - If not set, this will take the above function (in example) as a default return 96 | - `paymentOptions: (optional) enable many options: full "transactions" object and also "note_to_payer", "redirect_urls", "intent" etc.` - https://developer.paypal.com/docs/integration/direct/payments/authorize-and-capture-payments/#authorize-the-payment 97 | https://developer.paypal.com/docs/api/payments/v1/ 98 | 99 | - `style: (optional) change appearance of Paypal button based on their document: size, color, shape, label` - https://developer.paypal.com/docs/integration/direct/express-checkout/integration-jsv4/customize-button/ 100 | 101 | ## Sample Returned Data 102 | 103 | - `onSuccess` - the returned `payment` object will look like: 104 | + `{paid: true, cancelled: false, payerID: "H8S4CU73PFRAG", paymentID: "PAY-47J75876PA321622TLESPATA", paymentToken: "EC-8FE085188N269774L", returnUrl: "https://www.sandbox.paypal.com/?paymentId=PAY-47J75876PA321622TLESPATA&token=EC-8FE085188N269774L&PayerID=H8S4CU73PFRAG"}` 105 | - `onCancel` - the returned `data` object will look like: 106 | + `{paymentToken: "EC-42A825696K839141X", cancelUrl: "https://www.sandbox.paypal.com?token=EC-42A825696K839141X"}` 107 | 108 | ### Reference Document on How to Have Paypal's Developer Account and Merchant + Buyer accounts 109 | 110 | - In fact, please just go to Paypal's developer page, login with your "Real" Paypal account: 111 | + Click on the "Sandbox accounts" on the left hand side 112 | + You will see Merchant + Buyer accounts have been created automatically 113 | + You can then change password, profile, can also clone those accounts, or create more accounts (choose country, default currency, set amount for new accounts) 114 | - You can check balance & transaction history of those testing accounts (the same as real accounts) by login with the accounts' credentials with: 115 | + https://www.sandbox.paypal.com/ 116 | - Official documents here: (just be patient and go step-by-step, by clicking their next topic's button on page, you can then understand the whole process!) 117 | + https://developer.paypal.com/docs/classic/paypal-payments-standard/integration-guide/create_payment_button/ 118 | 119 | ## TODO 120 | 121 | - Upgrade this library into more advanced Paypal button library (with features like recurring, add-to-cart, checkout-now etc.) 122 | - Fork & pull-request are very welcomed! 123 | 124 | ## Thank you 125 | 126 | - [React NPM Boilerplate](https://github.com/juliancwirko/react-npm-boilerplate) 127 | 128 | ## License 129 | 130 | MIT 131 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(['exports', 'react', 'react-dom', 'react-async-script-loader', 'prop-types'], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(exports, require('react'), require('react-dom'), require('react-async-script-loader'), require('prop-types')); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports, global.react, global.reactDom, global.reactAsyncScriptLoader, global.propTypes); 11 | global.index = mod.exports; 12 | } 13 | })(this, function (exports, _react, _reactDom, _reactAsyncScriptLoader, _propTypes) { 14 | 'use strict'; 15 | 16 | Object.defineProperty(exports, "__esModule", { 17 | value: true 18 | }); 19 | 20 | var _react2 = _interopRequireDefault(_react); 21 | 22 | var _reactDom2 = _interopRequireDefault(_reactDom); 23 | 24 | var _reactAsyncScriptLoader2 = _interopRequireDefault(_reactAsyncScriptLoader); 25 | 26 | var _propTypes2 = _interopRequireDefault(_propTypes); 27 | 28 | function _interopRequireDefault(obj) { 29 | return obj && obj.__esModule ? obj : { 30 | default: obj 31 | }; 32 | } 33 | 34 | function _classCallCheck(instance, Constructor) { 35 | if (!(instance instanceof Constructor)) { 36 | throw new TypeError("Cannot call a class as a function"); 37 | } 38 | } 39 | 40 | var _createClass = function () { 41 | function defineProperties(target, props) { 42 | for (var i = 0; i < props.length; i++) { 43 | var descriptor = props[i]; 44 | descriptor.enumerable = descriptor.enumerable || false; 45 | descriptor.configurable = true; 46 | if ("value" in descriptor) descriptor.writable = true; 47 | Object.defineProperty(target, descriptor.key, descriptor); 48 | } 49 | } 50 | 51 | return function (Constructor, protoProps, staticProps) { 52 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 53 | if (staticProps) defineProperties(Constructor, staticProps); 54 | return Constructor; 55 | }; 56 | }(); 57 | 58 | function _possibleConstructorReturn(self, call) { 59 | if (!self) { 60 | throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); 61 | } 62 | 63 | return call && (typeof call === "object" || typeof call === "function") ? call : self; 64 | } 65 | 66 | function _inherits(subClass, superClass) { 67 | if (typeof superClass !== "function" && superClass !== null) { 68 | throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); 69 | } 70 | 71 | subClass.prototype = Object.create(superClass && superClass.prototype, { 72 | constructor: { 73 | value: subClass, 74 | enumerable: false, 75 | writable: true, 76 | configurable: true 77 | } 78 | }); 79 | if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 80 | } 81 | 82 | var PaypalButton = function (_React$Component) { 83 | _inherits(PaypalButton, _React$Component); 84 | 85 | function PaypalButton(props) { 86 | _classCallCheck(this, PaypalButton); 87 | 88 | var _this = _possibleConstructorReturn(this, (PaypalButton.__proto__ || Object.getPrototypeOf(PaypalButton)).call(this, props)); 89 | 90 | window.React = _react2.default; 91 | window.ReactDOM = _reactDom2.default; 92 | _this.state = { 93 | showButton: false 94 | }; 95 | return _this; 96 | } 97 | 98 | _createClass(PaypalButton, [{ 99 | key: 'componentWillReceiveProps', 100 | value: function componentWillReceiveProps(_ref) { 101 | var isScriptLoaded = _ref.isScriptLoaded, 102 | isScriptLoadSucceed = _ref.isScriptLoadSucceed; 103 | 104 | if (!this.state.show) { 105 | if (isScriptLoaded && !this.props.isScriptLoaded) { 106 | if (isScriptLoadSucceed) { 107 | this.setState({ showButton: true }); 108 | } else { 109 | console.log('Cannot load Paypal script!'); 110 | this.props.onError(); 111 | } 112 | } 113 | } 114 | } 115 | }, { 116 | key: 'componentDidMount', 117 | value: function componentDidMount() { 118 | var _props = this.props, 119 | isScriptLoaded = _props.isScriptLoaded, 120 | isScriptLoadSucceed = _props.isScriptLoadSucceed; 121 | 122 | if (isScriptLoaded && isScriptLoadSucceed) { 123 | this.setState({ showButton: true }); 124 | } 125 | } 126 | }, { 127 | key: 'render', 128 | value: function render() { 129 | var _this2 = this; 130 | 131 | var payment = function payment() { 132 | return paypal.rest.payment.create(_this2.props.env, _this2.props.client, Object.assign({ 133 | transactions: [{ amount: { total: _this2.props.total, currency: _this2.props.currency } }] 134 | }, _this2.props.paymentOptions), { 135 | input_fields: { 136 | // any values other than null, and the address is not returned after payment execution. 137 | no_shipping: _this2.props.shipping 138 | } 139 | }); 140 | }; 141 | 142 | var onAuthorize = function onAuthorize(data, actions) { 143 | return actions.payment.execute().then(function (payment_data) { 144 | // console.log(`payment_data: ${JSON.stringify(payment_data, null, 1)}`) 145 | var payment = Object.assign({}, _this2.props.payment); 146 | payment.paid = true; 147 | payment.cancelled = false; 148 | payment.payerID = data.payerID; 149 | payment.paymentID = data.paymentID; 150 | payment.paymentToken = data.paymentToken; 151 | payment.returnUrl = data.returnUrl; 152 | // getting buyer's shipping address and email 153 | payment.address = payment_data.payer.payer_info.shipping_address; 154 | payment.email = payment_data.payer.payer_info.email; 155 | _this2.props.onSuccess(payment); 156 | }); 157 | }; 158 | 159 | var ppbtn = ''; 160 | if (this.state.showButton) { 161 | ppbtn = _react2.default.createElement(paypal.Button.react, { 162 | env: this.props.env, 163 | client: this.props.client, 164 | style: this.props.style, 165 | payment: payment, 166 | commit: true, 167 | onAuthorize: onAuthorize, 168 | onCancel: this.props.onCancel 169 | 170 | // "Error: Unrecognized prop: shipping" was caused by the next line 171 | // shipping={this.props.shipping} 172 | }); 173 | } 174 | return _react2.default.createElement( 175 | 'div', 176 | null, 177 | ppbtn 178 | ); 179 | } 180 | }]); 181 | 182 | return PaypalButton; 183 | }(_react2.default.Component); 184 | 185 | PaypalButton.propTypes = { 186 | currency: _propTypes2.default.string.isRequired, 187 | total: _propTypes2.default.number.isRequired, 188 | client: _propTypes2.default.object.isRequired, 189 | style: _propTypes2.default.object 190 | }; 191 | 192 | PaypalButton.defaultProps = { 193 | paymentOptions: {}, 194 | env: 'sandbox', 195 | // null means buyer address is returned in the payment execution response 196 | shipping: null, 197 | onSuccess: function onSuccess(payment) { 198 | console.log('The payment was succeeded!', payment); 199 | }, 200 | onCancel: function onCancel(data) { 201 | console.log('The payment was cancelled!', data); 202 | }, 203 | onError: function onError(err) { 204 | console.log('Error loading Paypal script!', err); 205 | } 206 | }; 207 | 208 | exports.default = (0, _reactAsyncScriptLoader2.default)('https://www.paypalobjects.com/api/checkout.js')(PaypalButton); 209 | }); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/index'); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-paypal-express-checkout", 3 | "version": "1.0.5", 4 | "description": "React component that renders Paypal's express check out button", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/thinhvo0108/react-paypal-express-checkout.git" 8 | }, 9 | "author": "Thinh Vo ", 10 | "license": "MIT", 11 | "bugs": { 12 | "url": "https://github.com/thinhvo0108/react-paypal-express-checkout/issues" 13 | }, 14 | "homepage": "https://github.com/thinhvo0108/react-paypal-express-checkout", 15 | "keywords": [ 16 | "react-component", 17 | "react", 18 | "react-paypal", 19 | "paypal-checkout", 20 | "express-checkout", 21 | "paypal-express-checkout", 22 | "paypal-button", 23 | "checkout-button", 24 | "paypal-checkout-button", 25 | "paypal", 26 | "express", 27 | "button", 28 | "checkout", 29 | "check-out" 30 | ], 31 | "options": { 32 | "mocha": "--require scripts/mocha_runner src/**/__tests__/**/*.js" 33 | }, 34 | "scripts": { 35 | "prepublish": "babel --plugins 'transform-es2015-modules-umd' src --ignore __tests__ --out-dir ./dist", 36 | "lint": "eslint ./src", 37 | "lintfix": "eslint ./src --fix", 38 | "testonly": "mocha $npm_package_options_mocha", 39 | "test": "npm run lint && npm run testonly", 40 | "test-watch": "npm run testonly -- --watch --watch-extensions js" 41 | }, 42 | "devDependencies": { 43 | "babel-cli": "^6.6.4", 44 | "babel-core": "^6.7.4", 45 | "babel-eslint": "^6.0.2", 46 | "babel-plugin-transform-es2015-modules-umd": "^6.6.5", 47 | "babel-polyfill": "^6.7.4", 48 | "babel-preset-es2015": "^6.6.0", 49 | "babel-preset-react": "^6.5.0", 50 | "babel-preset-stage-2": "^6.5.0", 51 | "chai": "^3.5.0", 52 | "enzyme": "^2.2.0", 53 | "eslint": "^2.7.0", 54 | "eslint-plugin-babel": "^3.1.0", 55 | "eslint-plugin-react": "^4.2.3", 56 | "jsdom": "^8.1.0", 57 | "mocha": "^2.4.5", 58 | "nodemon": "^1.9.1", 59 | "react-addons-test-utils": "^15.0.0", 60 | "react": "^16.0.0", 61 | "react-dom": "^16.0.0", 62 | "sinon": "^1.17.3" 63 | }, 64 | "peerDependencies": { 65 | "react": "~0.14.8 || ^15.5.0", 66 | "react-dom": "~0.14.8 || ^15.5.0" 67 | }, 68 | "dependencies": { 69 | "babel-runtime": "^6.6.1", 70 | "prop-types": "^15.5.10", 71 | "react-async-script-loader": "^0.3.0" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /scripts/mocha_runner.js: -------------------------------------------------------------------------------- 1 | var jsdom = require('jsdom').jsdom; 2 | 3 | var exposedProperties = ['window', 'navigator', 'document']; 4 | 5 | global.document = jsdom(''); 6 | global.window = document.defaultView; 7 | Object.keys(document.defaultView).forEach((property) => { 8 | if (typeof global[property] === 'undefined') { 9 | exposedProperties.push(property); 10 | global[property] = document.defaultView[property]; 11 | } 12 | }); 13 | 14 | global.navigator = { 15 | userAgent: 'node.js' 16 | }; 17 | 18 | documentRef = document; 19 | 20 | require('babel-core/register'); 21 | require('babel-polyfill'); -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import scriptLoader from 'react-async-script-loader'; 4 | import PropTypes from 'prop-types'; 5 | 6 | class PaypalButton extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | window.React = React; 10 | window.ReactDOM = ReactDOM; 11 | this.state = { 12 | showButton: false 13 | } 14 | } 15 | 16 | componentWillReceiveProps ({ isScriptLoaded, isScriptLoadSucceed }) { 17 | if (!this.state.show) { 18 | if (isScriptLoaded && !this.props.isScriptLoaded) { 19 | if (isScriptLoadSucceed) { 20 | this.setState({ showButton: true }); 21 | } else { 22 | console.log('Cannot load Paypal script!'); 23 | this.props.onError(); 24 | } 25 | } 26 | } 27 | } 28 | 29 | componentDidMount() { 30 | const { isScriptLoaded, isScriptLoadSucceed } = this.props; 31 | if (isScriptLoaded && isScriptLoadSucceed) { 32 | this.setState({ showButton: true }); 33 | } 34 | } 35 | 36 | render() { 37 | let payment = () => { 38 | return paypal.rest.payment.create(this.props.env, this.props.client, Object.assign({ 39 | transactions: [ 40 | { amount: { total: this.props.total, currency: this.props.currency } } 41 | ] 42 | }, this.props.paymentOptions), { 43 | input_fields: { 44 | // any values other than null, and the address is not returned after payment execution. 45 | no_shipping: this.props.shipping 46 | } 47 | }); 48 | } 49 | 50 | const onAuthorize = (data, actions) => { 51 | return actions.payment.execute().then((payment_data) => { 52 | // console.log(`payment_data: ${JSON.stringify(payment_data, null, 1)}`) 53 | const payment = Object.assign({}, this.props.payment); 54 | payment.paid = true; 55 | payment.cancelled = false; 56 | payment.payerID = data.payerID; 57 | payment.paymentID = data.paymentID; 58 | payment.paymentToken = data.paymentToken; 59 | payment.returnUrl = data.returnUrl; 60 | // getting buyer's shipping address and email 61 | payment.address = payment_data.payer.payer_info.shipping_address; 62 | payment.email = payment_data.payer.payer_info.email; 63 | this.props.onSuccess(payment); 64 | }) 65 | } 66 | 67 | let ppbtn = ''; 68 | if (this.state.showButton) { 69 | ppbtn = 81 | } 82 | return
{ppbtn}
; 83 | } 84 | } 85 | 86 | PaypalButton.propTypes = { 87 | currency: PropTypes.string.isRequired, 88 | total: PropTypes.number.isRequired, 89 | client: PropTypes.object.isRequired, 90 | style: PropTypes.object 91 | } 92 | 93 | PaypalButton.defaultProps = { 94 | paymentOptions: {}, 95 | env: 'sandbox', 96 | // null means buyer address is returned in the payment execution response 97 | shipping: null, 98 | onSuccess: (payment) => { 99 | console.log('The payment was succeeded!', payment); 100 | }, 101 | onCancel: (data) => { 102 | console.log('The payment was cancelled!', data) 103 | }, 104 | onError: (err) => { 105 | console.log('Error loading Paypal script!', err) 106 | } 107 | }; 108 | 109 | export default scriptLoader('https://www.paypalobjects.com/api/checkout.js')(PaypalButton); 110 | --------------------------------------------------------------------------------