├── .babelrc ├── .editorconfig ├── .gitignore ├── .npmignore ├── LICENSE.txt ├── README.md ├── __tests__ ├── StripeForm.test.js └── __snapshots__ │ └── StripeForm.test.js.snap ├── gulpfile.js ├── package.json └── src ├── CheckoutForm.js ├── DonationForm.js ├── StripeForm.js ├── index.js └── order.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react", "es2015", "stage-0"], 3 | "plugins": ["add-module-exports", "transform-runtime"], 4 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | npm-debug.log -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .gitignore 2 | .git 3 | gulpfile.js 4 | npm-debug.log 5 | node_modules 6 | __tests__ -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Roman Nosov 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Checkout 2 | 3 | Quickly create simple React checkout forms styled for Bootstrap 4. Bank cards are processed via [stripe.js](https://stripe.com/docs/stripe.js) low level library. Redux store is used to maintain internal state. Forms are generated using the [SimpleForm](https://www.npmjs.com/package/simpleform) package. 4 | 5 | ## Live Examples 6 | 7 | Two examples are available: 8 | 9 | 1. [Donation Form](https://www.solarleague.org/about/donations/) - donation form example 10 | 2. [Checkout](https://www.solarleague.org/shop/macbook-case/) - In order to see the Checkout page you'll need to add some products to the shopping cart. To do it, press green "Add to Cart" button. Next, press blue "Checkout" button. 11 | 12 | ## Introduction 13 | 14 | `react-checkout` is a package that is designed to simplify the process of making bespoke checkout pages. Alternatively, you could use Stripe's own Checkout widget to process card payments but it does have some issues: 15 | 16 | 1. You will need to load the rather large Stripe Checkout library *before* you can even show Checkout UI. With this package you can bundle the checkout page with the rest of you code and show it immediately to the user. Low level lightweight `stripe.js` library will be asynchronously loaded only after `react-checkout` has been mounted. 17 | 2. Stripe Checkout widget will redirect you to the Stripe web site on mobile devices. 18 | 3. Stripe Checkout widget look and feel is not really customizable and it can look out of place on your web site. With this package you control checkout look and feel. 19 | 4. REST API handling is left as an exercise to the reader. 20 | 21 | ## Installation 22 | 23 | To install the package run the following command in the command prompt: 24 | 25 | ```sh 26 | npm install react-checkout bootstrap@4.0.0-alpha.4 redux react-redux --save 27 | 28 | ``` 29 | 30 | Import `react-checkout` in the component where you want to use it like so: 31 | 32 | ```javascript 33 | import 'bootstrap/dist/css/bootstrap.css'; //import Bootstrap if you haven't done it already 34 | import { createStore, combineReducers } from 'redux'; // redux store utilities 35 | import reducers from '/reducers'; // your other reducers 36 | import Checkout, { formReducer, order } from 'react-checkout'; 37 | ``` 38 | 39 | Create Redux store with the supplied reducer. `formReducer` must be on the `simpleform` key: 40 | 41 | ```javascript 42 | const store = createStore( 43 | combineReducers({ 44 | ...reducers, 45 | simpleform: formReducer 46 | }) 47 | ); 48 | 49 | ``` 50 | 51 | Use `order` action to specify the particulars of the order: 52 | 53 | ```javascript 54 | store.dispatch(order({ amount: 10000 })); 55 | ``` 56 | 57 | Somewhere in your component `render` method: 58 | 59 | ```javascript 60 | 76 | ``` 77 | 78 | Form fields named `number`, `cvc` and `exp` are required and must be present for Checkout to function. 79 | Refer to [SimpleForm](https://www.npmjs.com/package/simpleform) docs on how to create form fields. 80 | 81 | ## Documentation 82 | 83 | ### Checkout Properties 84 | 85 | - `endpoint` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**. RESTful API endpoint that should parse JSON object in the `POST` body. The JSON object sent will look like this: 86 | 87 | ```javascript 88 | { 89 | mode: 'string',// either "live" or "test" depending whether application is in production or not 90 | token: 'Stripe token', // Token returned by the Stripe API 91 | data: {} // Object which will have all key/value pairs from the form and the order object. 92 | // Card details will NOT be sent. 93 | } 94 | ``` 95 | The endpoint should set `success` key to `true` to indicate successful payment: 96 | 97 | ```javascript 98 | { 99 | "success": true 100 | } 101 | ``` 102 | 103 | or error message otherwise: 104 | 105 | ```javascript 106 | { 107 | "message": "Payment failed" 108 | } 109 | ``` 110 | 111 | **Required**. 112 | - `testStripeKey` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**. Public Stripe API test key. **Required**. 113 | - `liveStripeKey` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**. Public Stripe API live key. **Required**. 114 | - `amountPrefix` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**. Text displayed on the form submit button in front of the amount. Defaults to "Pay £". **Optional**. 115 | - `zeroAmountText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**. Text shown when Redux store order amount is zero. Defaults to "Your order is empty". **Optional**. 116 | - `stripeNotLoadedText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**. Error message shown when `stripe.js` library is not found. Defaults to "Unfortunately we are unable to process your payment". **Optional**. 117 | - `waitText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Text displayed when form is being uploaded . Defaults to "Processing your payment. Please wait ...". **Optional**. 118 | - `errorText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Text displayed when form has encountered errors on upload. Defaults to "Houston, we have a problem!". **Optional**. 119 | - `successText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Text displayed when form is uploaded without any issues. Defaults to "Your payment has been made". **Optional**. 120 | - `welcomeText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Text displayed when form is initially displayed. Defaults to "Please pay for your order". **Optional**. 121 | - `invalidFieldText` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** Text displayed when field fails Stripe validation. Defaults to "This field is invalid". **Optional**. 122 | 123 | ### Checkout Children 124 | 125 | Children are **ignored**. 126 | 127 | ### `order` Action 128 | 129 | Use this redux action to specify the particulars of the order. It takes one argument which is a javascript object. This object must contain key/value pairs of which `amount` key is the most important one. It should be an integer number corresponding to gross amount of the transaction, in pence. Other key/value pairs will be passed along to the RESTful endpoint. For example: 130 | 131 | ```javascript 132 | import { order } from 'react-checkout'; 133 | // import Redux store 134 | store.dispatch(order({ amount: 10000, description: 'Green widget' })); // ordering a green widget which costs 100.00 135 | ``` 136 | 137 | ### Universal Rendering 138 | 139 | This component is compatible with universal or server side rendering (SSR). 140 | 141 | ## Step by Step Instructions 142 | 143 | In order to start from scratch we'll use Facebook react starter kit called [Create React App](https://github.com/facebookincubator/create-react-app). In the command prompt type: 144 | 145 | 146 | ```sh 147 | npm install -g create-react-app 148 | 149 | create-react-app my-app 150 | cd my-app/ 151 | npm install react-checkout bootstrap@4.0.0-alpha.4 redux react-redux --save 152 | subl src/App.js #open with Sublime Text. Or use any other text editor. 153 | npm start 154 | 155 | ``` 156 | 157 | Replace `src/app.js` file contents with the following code: 158 | 159 | ```javascript 160 | import React, { Component } from 'react'; 161 | import 'bootstrap/dist/css/bootstrap.css'; //import Bootstrap if you haven't done it already 162 | import { createStore, combineReducers } from 'redux'; // redux store utilities 163 | import Checkout, { formReducer, order } from 'react-checkout'; 164 | import logo from './logo.svg'; 165 | import './App.css'; 166 | 167 | const store = createStore( 168 | combineReducers({ 169 | simpleform: formReducer, 170 | }) 171 | ); 172 | store.dispatch(order({ amount: 10000 })); 173 | 174 | class App extends Component { 175 | render() { 176 | return ( 177 |
178 |
179 | logo 180 |

Welcome to React

181 |
182 |

183 | To get started, edit src/App.js and save to reload. 184 |

185 |
186 | 202 |
203 |
204 | ); 205 | } 206 | } 207 | 208 | export default App; 209 | ``` 210 | 211 | Save it, then open [http://localhost:3000/](http://localhost:3000/) to see the result. 212 | 213 | ## Forking This Package 214 | 215 | Clone the this repository using the following command: 216 | 217 | ```sh 218 | git clone https://github.com/rnosov/react-checkout.git 219 | ``` 220 | 221 | In the cloned directory, you can run following commands: 222 | 223 | ### `npm install` 224 | 225 | Installs required node modules 226 | 227 | ### `npm run build` 228 | 229 | Builds the package for production to the `dist` folder 230 | 231 | ### `npm test` 232 | 233 | Runs tests 234 | 235 | ## License 236 | 237 | Copyright © 2016 Roman Nosov. This source code is licensed under the MIT license. 238 | -------------------------------------------------------------------------------- /__tests__/StripeForm.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * StripeForm Test Suite 3 | * 4 | * Copyright © Roman Nosov 2016 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import React from 'react'; 11 | import { mount } from 'enzyme'; 12 | import StripeForm from '../src/StripeForm'; 13 | 14 | describe('StripeForm', () => { 15 | it('renders a initial view', () => { 16 | const form = mount( 17 | void 0 } 26 | testStripeKey="123" 27 | liveStripeKey="123" 28 | endpoint="https://example.com/" 29 | order={{ description: 'donation' }} 30 | isDisplayed={true} 31 | /> 32 | ); 33 | expect(form.html()).toMatchSnapshot(); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /__tests__/__snapshots__/StripeForm.test.js.snap: -------------------------------------------------------------------------------- 1 | exports[`StripeForm renders a initial view 1`] = `"

Please pay for your order

Long number on the front of your card
The 3 digits to the right of the signature strip located on the back of your card
The address where your order will be shipped
*Mandatory fields
  
"`; 2 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const babel = require('gulp-babel'); 3 | const uglify = require('gulp-uglify'); 4 | 5 | gulp.task('build', () => 6 | gulp.src('./src/**/*.js') 7 | .pipe(babel()) 8 | .pipe(uglify()) 9 | .pipe(gulp.dest('dist')) 10 | ); 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-checkout", 3 | "version": "0.1.3", 4 | "author": "Roman Nosov ", 5 | "description": "Quickly create React checkout forms", 6 | "license": "MIT", 7 | "keywords": [ 8 | "react", 9 | "checkout", 10 | "form", 11 | "stripe", 12 | "stripe.js", 13 | "bootstrap", 14 | "bootstrap4", 15 | "bs4", 16 | "redux" 17 | ], 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/rnosov/react-checkout" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/rnosov/react-checkout/issues" 24 | }, 25 | "homepage": "https://github.com/rnosov/react-checkout", 26 | "main": "./dist/index.js", 27 | "dependencies": { 28 | "babel-runtime": "^6.11.6", 29 | "redux-simpleform": "0.1.2" 30 | }, 31 | "peerDependencies": { 32 | "react": "^15.3.1", 33 | "bootstrap": "^4.0.0-alpha.4" 34 | }, 35 | "devDependencies": { 36 | "react": "^15.3.1", 37 | "react-addons-test-utils": "^15.3.2", 38 | "babel-cli": "^6.14.0", 39 | "babel-core": "^6.11.4", 40 | "babel-jest": "^15.0.0", 41 | "babel-plugin-add-module-exports": "^0.2.1", 42 | "babel-plugin-react-transform": "^2.0.2", 43 | "babel-plugin-transform-runtime": "^6.12.0", 44 | "babel-preset-es2015": "^6.9.0", 45 | "babel-preset-react": "^6.5.0", 46 | "babel-preset-stage-0": "^6.5.0", 47 | "bootstrap": "^4.0.0-alpha.4", 48 | "enzyme": "^2.4.1", 49 | "gulp": "^3.9.1", 50 | "gulp-babel": "^6.1.2", 51 | "gulp-uglify": "^2.0.0", 52 | "jest-cli": "^15.1.0", 53 | "react-dom": "^15.3.2", 54 | "uglify-js": "^2.7.3" 55 | }, 56 | "scripts": { 57 | "build": "./node_modules/gulp/bin/gulp.js build", 58 | "test": "jest .", 59 | "prepublish": "npm run build && npm run test -- -u;" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/CheckoutForm.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Checkout Redux Container 3 | * 4 | * Copyright © Roman Nosov 2016 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { connect } from 'react-redux'; 11 | import StripeForm from './StripeForm'; 12 | 13 | const mapStateToProps = ({ simpleform: { checkout } }, { amountPrefix = 'Pay £' }) => ( 14 | checkout?{ 15 | submitText: `${amountPrefix}${(checkout.order.amount/100).toFixed(2)}`, 16 | isDisplayed: (!!checkout.order.amount) || checkout.formStatus ==='success', 17 | formStatus: checkout.formStatus, 18 | formMsg: checkout.formMsg, 19 | order: checkout.order, 20 | formName: 'checkout', 21 | } 22 | :{ isDisplayed: false, formName: 'checkout', } 23 | ); 24 | 25 | const mapDispatchToProps = (dispatch) => ({ 26 | setStatus: (formStatus = '', formMsg = false, spinner = false) => dispatch({ 27 | type: 'simpleform/SET_STATUS', 28 | formName: 'checkout', 29 | formStatus, 30 | formMsg, 31 | }), 32 | }); 33 | 34 | export default connect(mapStateToProps, mapDispatchToProps)(StripeForm); 35 | 36 | -------------------------------------------------------------------------------- /src/DonationForm.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Donation Redux Container 3 | * 4 | * Copyright © Roman Nosov 2016 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | 10 | import { connect } from 'react-redux'; 11 | import StripeForm from './StripeForm'; 12 | const order = { description: 'donation' }; 13 | const mapStateToProps = ({ simpleform: { donation } }) => ({ 14 | order, 15 | isDisplayed: true, 16 | formStatus: donation? donation.formStatus : '', 17 | formMsg: donation? donation.formMsg : false, 18 | formName: 'donation', 19 | }); 20 | 21 | const mapDispatchToProps = (dispatch) => ({ 22 | setStatus: (formStatus = '', formMsg = false) => dispatch({ 23 | type: 'simpleform/SET_STATUS', 24 | formName: 'donation', 25 | formStatus, 26 | formMsg, 27 | }), 28 | }); 29 | 30 | export default connect(mapStateToProps, mapDispatchToProps)(StripeForm); 31 | 32 | -------------------------------------------------------------------------------- /src/StripeForm.js: -------------------------------------------------------------------------------- 1 | /* 2 | * StripeForm React Component 3 | * 4 | * Copyright © Roman Nosov 2016 5 | * 6 | * This source code is licensed under the MIT license found in the 7 | * LICENSE.txt file in the root directory of this source tree. 8 | */ 9 | import React, { Component, PropTypes } from 'react'; 10 | import RestForm from 'redux-simpleform/dist/RestForm'; 11 | 12 | const 13 | propTypes = { 14 | endpoint: PropTypes.string.isRequired, 15 | testStripeKey: PropTypes.string.isRequired, 16 | liveStripeKey: PropTypes.string.isRequired, 17 | zeroAmountText: PropTypes.string, 18 | stripeNotLoadedText: PropTypes.string, 19 | waitText: PropTypes.string, 20 | errorText: PropTypes.string, 21 | successText: PropTypes.string, 22 | welcomeText: PropTypes.string, 23 | invalidFieldText: PropTypes.string, 24 | setStatus: PropTypes.func.isRequired, 25 | }, 26 | defaultProps = { 27 | zeroAmountText: 'Your order is empty', 28 | stripeNotLoadedText: 'Unfortunately we are unable to process your payment', 29 | waitText: 'Processing your payment. Please wait ...', 30 | errorText: 'Houston, we have a problem!', 31 | successText: 'Your payment has been made', 32 | welcomeText: 'Please pay for your order', 33 | invalidFieldText: 'This field is invalid', 34 | }; 35 | 36 | class StripeForm extends Component { 37 | 38 | static isStripeLoading = false; 39 | 40 | static isStripe() { 41 | return typeof Stripe !== 'undefined'; 42 | } 43 | 44 | componentDidMount() { 45 | if (StripeForm.isStripeLoading) return; 46 | StripeForm.isStripeLoading = true; 47 | setTimeout( () => { 48 | const script = document.createElement('script'); 49 | script.src = 'https://js.stripe.com/v2/'; 50 | script.async = true; 51 | document.body.appendChild(script); 52 | }, 500); 53 | } 54 | 55 | getStripeToken(card) { 56 | return new Promise((resolve, reject) => { 57 | Stripe.card.createToken(card, (status, { error, id }) => { 58 | if (error) { 59 | reject(error); 60 | } else { 61 | resolve(id); 62 | } 63 | }); 64 | }); 65 | } 66 | 67 | async handleFormWillFetch(form) { 68 | if (!StripeForm.isStripe()) 69 | throw new Error( this.props.stripeNotLoadedText || 'Could not load Stripe.js' ); 70 | const { number, cvc, exp, ...data } = form; 71 | const [ mode, stripeKey ] = process.env.NODE_ENV === 'production' ? ['live', this.props.liveStripeKey] : ['test', this.props.testStripeKey]; 72 | Stripe.setPublishableKey(stripeKey); 73 | const token = await this.getStripeToken({ number, cvc, exp }); 74 | return { mode, token, data: { ...data, ...this.props.order } }; 75 | } 76 | 77 | async handleResponse(response) { 78 | const payload = await response.json(); 79 | if (!payload.success) 80 | throw new Error(payload.errorMessage || payload.message || 'Unknown error'); 81 | return this.props.successText; 82 | } 83 | 84 | handleParsingComplete(fields) { 85 | return fields.map( field => { 86 | switch (field.name) { 87 | case 'number': return field.merge({ 88 | required: true, 89 | type: 'tel', 90 | autoComplete: 'cc-number', 91 | validate: field => StripeForm.isStripe() ? this.validate(field, Stripe.card.validateCardNumber) : field, 92 | onChange: (newVal, oldVal) => { 93 | if (newVal && oldVal && newVal.length>oldVal.length) { 94 | if (/^\d{4}$/.test(newVal) || /^\d{4}\s\d{4}$/.test(newVal) || /^\d{4}\s\d{4}\s\d{4}$/.test(newVal)) 95 | return newVal + ' '; 96 | } 97 | return /^[\d\s]*$/.test(newVal)?newVal:oldVal; 98 | }, 99 | }); 100 | case 'cvc': return field.merge({ 101 | required: true, 102 | type: 'number', 103 | autoComplete: 'cc-csc', 104 | validate: field => StripeForm.isStripe() ? this.validate(field, Stripe.card.validateCVC) : field, 105 | }); 106 | case 'exp': return field.merge({ 107 | required: true, 108 | type: 'tel', 109 | autoComplete: 'cc-exp', 110 | validate: field => StripeForm.isStripe() ? this.validate(field, Stripe.card.validateExpiry) : field, 111 | onChange: (newVal, oldVal) => { 112 | if (newVal && oldVal && newVal.length>oldVal.length) { 113 | if (/^\d{2}$/.test(newVal)) 114 | return newVal + '/'; 115 | if (/^\d{2}\/\/$/.test(newVal)) 116 | return oldVal; 117 | } 118 | return /^[\d\/]*$/.test(newVal)?newVal:oldVal; 119 | }, 120 | }); 121 | } 122 | return field; 123 | }); 124 | } 125 | 126 | validate(field, func) { 127 | return func(field.value) ? field.set('error', false) : field 128 | .set('error', true) 129 | .set('message', this.props.invalidFieldText ); 130 | } 131 | 132 | render() { 133 | const { 134 | testStripeKey, 135 | liveStripeKey, 136 | zeroAmountText, 137 | stripeNotLoadedText, 138 | amountPrefix, 139 | isDisplayed, 140 | invalidFieldText, 141 | order, 142 | ...props 143 | } = this.props; 144 | if (!isDisplayed) 145 | return

{zeroAmountText}

; 146 | return( 147 | 153 | ); 154 | } 155 | 156 | } 157 | 158 | StripeForm.propTypes = propTypes; 159 | StripeForm.defaultProps = defaultProps; 160 | 161 | export default StripeForm; 162 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export { formReducer } from 'redux-simpleform'; 2 | export { default as StripeForm } from './StripeForm'; 3 | export { default as DonationForm } from './DonationForm'; 4 | export { default as order } from './order'; 5 | export { default } from './CheckoutForm'; 6 | 7 | -------------------------------------------------------------------------------- /src/order.js: -------------------------------------------------------------------------------- 1 | export default function(order) { 2 | return { 3 | type: 'simpleform/checkout/ORDER_FILL', 4 | order, 5 | } 6 | }; 7 | --------------------------------------------------------------------------------