├── test ├── __mocks__ │ └── styleMock.js ├── unit │ ├── specs │ │ ├── components │ │ │ ├── __snapshots__ │ │ │ │ └── PayPalCheckout.spec.js.snap │ │ │ ├── PayPalCheckout.spec.js │ │ │ └── SimpleMethods.spec.js │ │ └── util │ │ │ ├── paypalProp.spec.js │ │ │ ├── defaultProps.spec.js │ │ │ └── additionalProps.spec.js │ ├── setup.js │ └── jest.conf.js └── .eslintrc ├── .eslintignore ├── config ├── babel.config.js └── rollup-plugin-vue.config.js ├── .eslintrc ├── .travis.yml ├── .npmignore ├── src ├── main.js ├── util │ ├── additionalProps.js │ ├── defaultProps.js │ └── paypalProp.js └── components │ └── PayPalCheckout.vue ├── .babelrc ├── LICENSE.md ├── LICENSE.txt ├── examples ├── locale.html ├── basic.html ├── experience.html ├── buttonStyle.html ├── braintree.html ├── processing.html └── shippingItems.html ├── rollup.config.js ├── package.json ├── .gitignore ├── dist ├── vue-paypal-checkout.esm.js └── vue-paypal-checkout.common.js └── README.md /test/__mocks__/styleMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | coverage/ 4 | config/ 5 | rollup.config.js -------------------------------------------------------------------------------- /config/babel.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | exclude: 'node_modules/**', 3 | runtimeHelpers: true 4 | }; 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "globals": { 4 | "window": true, 5 | }, 6 | "plugins": ["html"] 7 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - lts/* 5 | cache: 6 | yarn: true 7 | directories: 8 | - node_modules 9 | install: 10 | - yarn install 11 | script: 12 | - yarn run build 13 | - yarn run test 14 | -------------------------------------------------------------------------------- /test/unit/specs/components/__snapshots__/PayPalCheckout.spec.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`PayPalCheckout.vue iframe rendering has same HTML structure 1`] = `
`; 4 | -------------------------------------------------------------------------------- /test/unit/setup.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | Vue.config.productionTip = false; 4 | 5 | // polyfill matchMedia 6 | window.matchMedia = window.matchMedia || (() => ({ 7 | matches: false, 8 | addListener: () => {}, 9 | removeListener: () => {}, 10 | })); 11 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | lib/ 3 | examples/ 4 | test/ 5 | coverage/ 6 | config/ 7 | dist/*.map 8 | 9 | *.gitignore 10 | *.log 11 | *.swp 12 | *.yml 13 | 14 | .babelrc 15 | .eslintrc 16 | .eslintignore 17 | .travis.yml 18 | 19 | rollup.config.js 20 | yarn.lock 21 | bower.json -------------------------------------------------------------------------------- /config/rollup-plugin-vue.config.js: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs'; 3 | 4 | export const pack = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'package.json'), 'utf8')); 5 | 6 | export default { 7 | compileTemplate: true, 8 | css: false, 9 | standalone: true, // custom option. 10 | }; 11 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import PayPalCheckout from './components/PayPalCheckout.vue'; 2 | 3 | const components = { 4 | 'paypal-checkout': PayPalCheckout, 5 | }; 6 | 7 | Object.keys(components).forEach((name) => { 8 | // in browsers ~ 9 | if (typeof window !== 'undefined' && window.Vue) { 10 | window.Vue.component(name, components[name]); 11 | } 12 | }); 13 | 14 | export default PayPalCheckout; 15 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "env": { 4 | "jest": true 5 | }, 6 | "rules": { 7 | "import/no-extraneous-dependencies": ["error", { "devDependencies": ["**/*.test.js", "**/*.spec.js", "**/setup.js"] }] 8 | }, 9 | "settings": { 10 | "import/resolver": { 11 | "jest": { 12 | "jestConfigFile": "./test/unit/jest.conf.js" 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] 7 | } 8 | }], 9 | "stage-2" 10 | ], 11 | "plugins": ["transform-runtime", "external-helpers"], 12 | "env": { 13 | "test": { 14 | "presets": ["env", "stage-2"], 15 | "plugins": [ 16 | ["module-resolver", { 17 | "root": ["./src"], 18 | "alias": { 19 | "@": "./src" 20 | } 21 | }] 22 | ] 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /test/unit/jest.conf.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | testURL: 'http://localhost', 5 | rootDir: path.resolve(__dirname, '../../'), 6 | moduleFileExtensions: [ 7 | 'js', 8 | 'json', 9 | 'jsx', 10 | 'node', 11 | 'vue', 12 | ], 13 | moduleNameMapper: { 14 | '^@/(.*)$': '/src/$1', 15 | }, 16 | transform: { 17 | '^.+\\.js$': '/node_modules/babel-jest', 18 | '.*\\.(vue)$': '/node_modules/vue-jest', 19 | '\\.(css|less)$': '/node_modules/identity-obj-proxy', 20 | }, 21 | snapshotSerializers: ['/node_modules/jest-serializer-vue'], 22 | setupFiles: ['/test/unit/setup'], 23 | coverageDirectory: '/test/unit/coverage', 24 | collectCoverageFrom: [ 25 | 'src/**/*.{js,vue}', 26 | '!src/main.js', 27 | '!**/node_modules/**', 28 | ], 29 | testEnvironment: 'jest-environment-jsdom-global', 30 | }; 31 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 2 | 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | All rights reserved. 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 13 | all 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 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /examples/locale.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple Vue PayPal Checkout 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Pay with USD $10.00

21 | 28 | 29 |
30 |
31 | 32 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple Vue PayPal Checkout 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Pay with USD $10.00

21 | 27 | 28 |
29 |
30 | 31 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/experience.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue PayPal Checkout 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Pay with USD $10.00

21 | 28 | 29 |
30 |
31 | 32 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /examples/buttonStyle.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple Vue PayPal Checkout 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Pay with USD $10.00

21 | 28 | 29 |
30 |
31 | 32 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /examples/braintree.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple PayPal Checkout with Vue 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |

Pay with USD $10.00

23 | 30 | 31 |
32 |
33 | 34 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/unit/specs/util/paypalProp.spec.js: -------------------------------------------------------------------------------- 1 | import PayPalProp from '@/util/paypalProp'; 2 | 3 | describe('paypalProp.js', () => { 4 | describe('new paypalProp()', () => { 5 | test('construct with most options specified', () => { 6 | const paypal = new PayPalProp({ 7 | name: 'some-name', 8 | paypalName: 'somePaypal', 9 | injection: 'someExperience', 10 | }); 11 | 12 | expect(paypal).toEqual(expect.objectContaining({ 13 | name: 'some-name', 14 | propName: 'somePaypal', 15 | injection: 'someExperience', 16 | })); 17 | }); 18 | 19 | test('construct with just name specified', () => { 20 | const paypal = new PayPalProp({ name: 'some-name' }); 21 | 22 | expect(paypal).toEqual(expect.objectContaining({ 23 | name: 'some-name', 24 | propName: 'some-name', 25 | injection: 'button', 26 | })); 27 | }); 28 | }); 29 | 30 | test('getVmProp()', () => { 31 | const paypal = new PayPalProp({ 32 | name: 'some-name', 33 | paypalName: 'somePaypal', 34 | injection: 'someExperience', 35 | }); 36 | 37 | const prop = paypal.getVmProp(); 38 | 39 | expect(prop).toEqual({ 40 | type: Object, 41 | required: false, 42 | }); 43 | }); 44 | 45 | test('change()', () => { 46 | const paypal = new PayPalProp({ 47 | name: 'some-name', 48 | paypalName: 'somePaypal', 49 | injection: 'someExperience', 50 | }); 51 | 52 | const o = { 53 | 'some-name': { text: 'paypal is so cool' }, 54 | }; 55 | 56 | const diff = paypal.getChange(o); 57 | 58 | expect(diff).toEqual({ 59 | name: 'somePaypal', 60 | value: { text: 'paypal is so cool' }, 61 | }); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /test/unit/specs/util/defaultProps.spec.js: -------------------------------------------------------------------------------- 1 | import defaultProps from '@/util/defaultProps'; 2 | 3 | describe('defaultProps.js', () => { 4 | test('has the required props', () => { 5 | const props = defaultProps(); 6 | 7 | expect(props).toEqual(expect.objectContaining({ 8 | amount: expect.any(Object), 9 | currency: expect.any(Object), 10 | })); 11 | 12 | expect(props.amount).toEqual({ 13 | type: String, 14 | required: true, 15 | }); 16 | 17 | expect(props.currency).toEqual({ 18 | type: String, 19 | required: true, 20 | default: 'USD', 21 | }); 22 | }); 23 | 24 | test('has the optional props', () => { 25 | const props = defaultProps(); 26 | 27 | expect(props).toEqual(expect.objectContaining({ 28 | id: expect.any(Object), 29 | invoiceNumber: expect.any(Object), 30 | })); 31 | 32 | expect(props.id).toEqual({ 33 | type: String, 34 | required: false, 35 | }); 36 | 37 | expect(props.invoiceNumber).toEqual({ 38 | type: String, 39 | required: false, 40 | }); 41 | }); 42 | 43 | test('has the specific props', () => { 44 | const props = defaultProps(); 45 | 46 | expect(props).toEqual(expect.objectContaining({ 47 | client: expect.any(Object), 48 | commit: expect.any(Object), 49 | env: expect.any(Object), 50 | })); 51 | 52 | expect(props.client).toEqual({ 53 | type: Object, 54 | required: true, 55 | }); 56 | 57 | expect(props.commit).toEqual({ 58 | type: Boolean, 59 | required: false, 60 | default: true, 61 | }); 62 | 63 | expect(props.env).toEqual({ 64 | type: String, 65 | required: false, 66 | default: 'production', 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /src/util/additionalProps.js: -------------------------------------------------------------------------------- 1 | import PayPalProp, { propTypes } from './paypalProp'; 2 | 3 | // TODO: add item validator 4 | const itemsPayPalProp = new PayPalProp({ 5 | name: 'items', 6 | paypalName: 'item_list', 7 | type: Array, 8 | injection: propTypes.TRANSACTION, 9 | }); 10 | 11 | itemsPayPalProp.addChangeTransform(items => ({ items })); 12 | 13 | const shippingAddressProp = new PayPalProp({ 14 | name: 'shippingAddress', 15 | paypalName: 'shipping_address', 16 | type: Object, 17 | injection: propTypes.TRANSACTION, 18 | }); 19 | 20 | const props = [ 21 | // Button Props 22 | new PayPalProp({ name: 'buttonStyle', paypalName: 'style', injection: propTypes.BUTTON }), 23 | new PayPalProp({ name: 'braintree', injection: propTypes.BUTTON }), 24 | new PayPalProp({ name: 'locale', type: String, injection: propTypes.BUTTON }), 25 | 26 | // Payment Props 27 | new PayPalProp({ name: 'experience', injection: propTypes.PAYMENT }), 28 | 29 | // Transaction Props 30 | new PayPalProp({ 31 | name: 'invoiceNumber', 32 | paypalName: 'invoice_number', 33 | type: String, 34 | injection: propTypes.TRANSACTION, 35 | }), 36 | new PayPalProp({ 37 | name: 'notifyUrl', 38 | paypalName: 'notify_url', 39 | type: String, 40 | validator: value => (/^https?:\/\//.test(value)), 41 | injection: propTypes.TRANSACTION, 42 | }), 43 | itemsPayPalProp, 44 | shippingAddressProp, 45 | ]; 46 | 47 | function vmProps() { 48 | const vm = {}; 49 | 50 | props.forEach((prop) => { 51 | vm[prop.name] = prop.getVmProp(); 52 | }); 53 | 54 | return vm; 55 | } 56 | 57 | function getTypedProps(type) { 58 | return props.filter(prop => prop.injection === type); 59 | } 60 | 61 | export default { 62 | vmProps, 63 | getTypedProps, 64 | }; 65 | -------------------------------------------------------------------------------- /examples/processing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Simple Vue PayPal Checkout 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |
21 |

Pay with USD $10.00

22 | 30 | 31 |
32 |
33 | 34 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/util/defaultProps.js: -------------------------------------------------------------------------------- 1 | const requiredProps = [ 2 | ['amount'], 3 | ['currency', 'USD'], 4 | ]; 5 | 6 | const optionalProps = [ 7 | ['id'], 8 | ['invoiceNumber'], 9 | ]; 10 | 11 | const specificProps = [ 12 | { 13 | name: 'env', 14 | type: String, 15 | required: false, 16 | default: 'production', 17 | validator(value) { 18 | return [ 19 | 'sandbox', 20 | 'production', 21 | ].indexOf(value) !== -1; 22 | }, 23 | }, 24 | { 25 | name: 'client', 26 | type: Object, 27 | required: true, 28 | }, 29 | { 30 | name: 'details', 31 | type: Object, 32 | required: false, 33 | default() { 34 | return {}; 35 | }, 36 | }, 37 | { 38 | name: 'commit', 39 | type: Boolean, 40 | required: false, 41 | default: true, 42 | }, 43 | ]; 44 | 45 | export default function () { 46 | const props = {}; 47 | 48 | // TODO: make type configurable 49 | // all required props are type String for now 50 | requiredProps.forEach(([name, def]) => { 51 | props[name] = { 52 | type: String, 53 | required: true, 54 | default: (typeof def !== 'undefined') 55 | ? def 56 | : undefined, 57 | }; 58 | }); 59 | 60 | // TODO: make type configurable 61 | // all optional props are type String for now 62 | optionalProps.forEach(([name, def]) => { 63 | props[name] = { 64 | type: String, 65 | required: false, 66 | default: (typeof def !== 'undefined') 67 | ? def 68 | : undefined, 69 | }; 70 | }); 71 | 72 | // all specific props are declared ahead of time 73 | specificProps.forEach((prop) => { 74 | props[prop.name] = { 75 | type: prop.type, 76 | required: prop.required, 77 | }; 78 | 79 | if (prop.default !== undefined) { 80 | props[prop.name].default = prop.default; 81 | } 82 | }); 83 | 84 | return props; 85 | } 86 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import vue from 'rollup-plugin-vue'; 2 | import babel from 'rollup-plugin-babel'; 3 | import builtins from 'rollup-plugin-node-builtins'; 4 | import resolve from 'rollup-plugin-node-resolve'; 5 | import commonjs from 'rollup-plugin-commonjs'; 6 | import { uglify } from 'rollup-plugin-uglify'; 7 | import { rollup } from 'rollup'; 8 | 9 | import clone from 'lodash/cloneDeep'; 10 | import camelcase from 'camelcase'; 11 | import chalk from 'chalk'; 12 | 13 | import { default as vueConfig, pack } from './config/rollup-plugin-vue.config'; 14 | import babelConfig from './config/babel.config'; 15 | 16 | let cache; 17 | 18 | const config = { 19 | input: 'src/main.js', 20 | output: [ 21 | { format: 'es', file: `dist/${pack.name}.esm.js` }, 22 | { format: 'cjs', file: `dist/${pack.name}.common.js` }, 23 | ], 24 | plugins: [ 25 | vue(vueConfig), 26 | babel(babelConfig), 27 | ], 28 | cache, 29 | }; 30 | 31 | // --- DO NOT CHANGE BEYOND THIS --- 32 | if (vueConfig.standalone) { 33 | const options = clone(config); 34 | options.output = []; 35 | options.plugins = [ 36 | builtins(), 37 | resolve({ 38 | module: true, 39 | jsnext: true, 40 | main: true, 41 | browser: true, 42 | }), 43 | commonjs(), 44 | ].concat(options.plugins); 45 | 46 | let promise = Promise.resolve(); 47 | 48 | promise.then(() => rollup(options).then(bundle => bundle.write({ 49 | format: 'umd', 50 | file: `dist/${pack.name}.umd.js`, 51 | name: camelcase(pack.name), 52 | }))).then(() => { 53 | options.plugins.push(uglify()); 54 | 55 | return rollup(options).then(bundle => bundle.write({ 56 | format: 'umd', 57 | file: `dist/${pack.name}.min.js`, 58 | name: camelcase(pack.name), 59 | })); 60 | }).then(() => { 61 | console.log(chalk.cyan('Build complete.\n')) 62 | }).catch(err => { 63 | console.log( 64 | chalk.red('Build Failed.\n'), 65 | chalk.yellow(err), 66 | chalk.yellow(err.stack) 67 | ) 68 | }); 69 | } 70 | 71 | export default config; 72 | -------------------------------------------------------------------------------- /examples/shippingItems.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue PayPal Checkout 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |

Pay with USD $10.00

21 | 29 | 30 |
31 |
32 | 33 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/util/paypalProp.js: -------------------------------------------------------------------------------- 1 | function paypalProp(prop) { 2 | /* eslint-disable no-param-reassign */ 3 | const define = (function getDefine(object) { 4 | return function def(name, param, defaultParam) { 5 | const isDefined = typeof param !== 'undefined' 6 | && param !== null; 7 | const hasDefault = typeof defaultParam !== 'undefined' 8 | && defaultParam !== null; 9 | 10 | if (isDefined) object[name] = param; 11 | else if (hasDefault) object[name] = defaultParam; 12 | else object[name] = undefined; // TODO: throw err? 13 | }; 14 | }(this)); 15 | 16 | define('name', prop.name); 17 | define('propName', prop.paypalName, prop.name); 18 | define('injection', prop.injection, 'button'); 19 | define('type', prop.type, Object); 20 | define('required', prop.required, false); 21 | define('validator', prop.validator, undefined); 22 | 23 | this.transforms = []; 24 | } 25 | 26 | paypalProp.prototype.getVmProp = function getVmProp() { 27 | return { 28 | type: this.type, 29 | required: this.required, 30 | validator: this.validator, 31 | }; 32 | }; 33 | 34 | paypalProp.prototype.addChangeTransform = function addChangeTransform(callable) { 35 | this.transforms.push(callable); 36 | }; 37 | 38 | paypalProp.prototype.getChange = function getChange(src) { 39 | let value = src[this.name]; 40 | 41 | // change the value if necessary... 42 | if (value !== undefined && value !== null) { 43 | this.transforms.forEach((transform) => { 44 | value = transform(value); 45 | }); 46 | } 47 | 48 | return { 49 | name: this.propName, 50 | value, 51 | }; 52 | }; 53 | 54 | export default paypalProp; 55 | 56 | export const propTypes = { 57 | BUTTON: 'button', 58 | PAYMENT: 'payment', 59 | TRANSACTION: 'transaction', 60 | }; 61 | 62 | export function assignToPropertyObject(props) { 63 | return function assignTo(vm, type) { 64 | const obj = {}; 65 | 66 | props.getTypedProps(type).forEach((item) => { 67 | const { name, value } = item.getChange(vm); 68 | 69 | if (name !== undefined && value !== undefined) { 70 | obj[name] = value; 71 | } 72 | }); 73 | 74 | return obj; 75 | }; 76 | } 77 | -------------------------------------------------------------------------------- /test/unit/specs/util/additionalProps.spec.js: -------------------------------------------------------------------------------- 1 | import additionalProps from '@/util/additionalProps'; 2 | 3 | describe('additionalProps.js', () => { 4 | test('exports two functions', () => { 5 | expect(additionalProps).toEqual(expect.objectContaining({ 6 | vmProps: expect.any(Function), 7 | getTypedProps: expect.any(Function), 8 | })); 9 | }); 10 | 11 | test('has the button props', () => { 12 | const props = additionalProps.vmProps(); 13 | 14 | expect(props).toEqual(expect.objectContaining({ 15 | buttonStyle: expect.any(Object), 16 | braintree: expect.any(Object), 17 | locale: expect.any(Object), 18 | })); 19 | 20 | expect(props).toEqual(expect.objectContaining({ 21 | buttonStyle: { 22 | type: Object, 23 | required: false, 24 | }, 25 | })); 26 | 27 | expect(props).toEqual(expect.objectContaining({ 28 | braintree: { 29 | type: Object, 30 | required: false, 31 | }, 32 | })); 33 | 34 | expect(props).toEqual(expect.objectContaining({ 35 | locale: { 36 | type: String, 37 | required: false, 38 | }, 39 | })); 40 | }); 41 | 42 | test('has the payment(s) props', () => { 43 | const props = additionalProps.vmProps(); 44 | 45 | expect(props).toEqual(expect.objectContaining({ 46 | experience: expect.any(Object), 47 | })); 48 | 49 | expect(props).toEqual(expect.objectContaining({ 50 | experience: { 51 | type: Object, 52 | required: false, 53 | }, 54 | })); 55 | }); 56 | 57 | test('has the transaction props', () => { 58 | const props = additionalProps.vmProps(); 59 | 60 | expect(props).toEqual(expect.objectContaining({ 61 | invoiceNumber: expect.any(Object), 62 | notifyUrl: expect.any(Object), 63 | items: expect.any(Object), 64 | })); 65 | 66 | expect(props).toEqual(expect.objectContaining({ 67 | invoiceNumber: { 68 | type: String, 69 | required: false, 70 | }, 71 | })); 72 | 73 | expect(props).toEqual(expect.objectContaining({ 74 | notifyUrl: { 75 | type: String, 76 | required: false, 77 | validator: expect.any(Function), 78 | }, 79 | })); 80 | 81 | expect(props).toEqual(expect.objectContaining({ 82 | items: { 83 | type: Array, 84 | required: false, 85 | }, 86 | })); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-paypal-checkout", 3 | "description": "A simple Vue.js wrapper component for paypal-checkout", 4 | "version": "3.2.0", 5 | "main": "dist/vue-paypal-checkout.common.js", 6 | "module": "dist/vue-paypal-checkout.esm.js", 7 | "unpkg": "dist/vue-paypal-checkout.min.js", 8 | "author": "Khoa Nguyen ", 9 | "license": "MIT", 10 | "repository": { 11 | "url": "https://github.com/khoanguyen96/vue-paypal-checkout.git", 12 | "type": "git" 13 | }, 14 | "scripts": { 15 | "build": "rm -rf dist/ && rollup -c", 16 | "lint": "eslint --ext .js,.vue src test", 17 | "test": "eslint --ext .js,.vue src test && jest --config test/unit/jest.conf.js", 18 | "unit": "jest --config test/unit/jest.conf.js", 19 | "version": "rollup -c && git add dist" 20 | }, 21 | "dependencies": { 22 | "babel-runtime": "^6.26.0", 23 | "paypal-checkout": "^4.0.228", 24 | "vue": "^2.5.17" 25 | }, 26 | "devDependencies": { 27 | "@vue/test-utils": "^1.0.0-beta.25", 28 | "babel-core": "^6.26.3", 29 | "babel-jest": "^23.6.0", 30 | "babel-plugin-external-helpers": "^6.22.0", 31 | "babel-plugin-module-resolver": "^3.1.1", 32 | "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", 33 | "babel-plugin-transform-runtime": "^6.23.0", 34 | "babel-preset-env": "^1.7.0", 35 | "babel-preset-stage-2": "^6.24.1", 36 | "camelcase": "^5.0.0", 37 | "eslint": "^5.7.0", 38 | "eslint-config-airbnb-base": "^13.1.0", 39 | "eslint-import-resolver-jest": "^2.1.1", 40 | "eslint-plugin-html": "^4.0.6", 41 | "eslint-plugin-import": "^2.14.0", 42 | "identity-obj-proxy": "^3.0.0", 43 | "jest": "^23.6.0", 44 | "jest-environment-jsdom": "^23.4.0", 45 | "jest-environment-jsdom-global": "^1.1.0", 46 | "jest-serializer-vue": "^2.0.2", 47 | "lodash.clonedeep": "^4.5.0", 48 | "parse5": "^5.1.0", 49 | "rollup": "^0.66.6", 50 | "rollup-plugin-babel": "^3.0.7", 51 | "rollup-plugin-commonjs": "^9.2.0", 52 | "rollup-plugin-node-builtins": "^2.1.2", 53 | "rollup-plugin-node-resolve": "^3.4.0", 54 | "rollup-plugin-uglify": "^6.0.0", 55 | "rollup-plugin-vue": "^4.3.2", 56 | "vue-jest": "^3.0.0", 57 | "vue-server-renderer": "^2.5.17", 58 | "vue-template-compiler": "^2.5.17" 59 | }, 60 | "engines": { 61 | "node": ">= 4.0.0", 62 | "npm": ">= 3.0.0" 63 | }, 64 | "browserslist": [ 65 | "> 1%", 66 | "last 2 versions", 67 | "not ie <= 8" 68 | ] 69 | } 70 | -------------------------------------------------------------------------------- /src/components/PayPalCheckout.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 91 | -------------------------------------------------------------------------------- /test/unit/specs/components/PayPalCheckout.spec.js: -------------------------------------------------------------------------------- 1 | import { createLocalVue, shallowMount } from '@vue/test-utils'; 2 | import { createRenderer } from 'vue-server-renderer'; 3 | import PayPalCheckout from '@/components/PayPalCheckout.vue'; 4 | 5 | const credentials = { 6 | sandbox: 'Ad1voWYq3VL8J4jy6zWARKwz4tjbuDl_TFBa3WQqy_DwAFWd7hkU4i99jijGqaoqU3E-ODqXDayVnOdl', 7 | production: 'AVZhosFzrnZ5Mf3tiOxAD0M6NHv8pcB2IFNHAfp_h69mmbd-LElFYkJUSII3Y0FPbm7S7lxBuqWImLbl', 8 | }; 9 | 10 | function getItems() { 11 | return [ 12 | { 13 | name: 'hat', 14 | description: 'Brown hat.', 15 | quantity: '5', 16 | price: '3', 17 | currency: 'USD', 18 | }, 19 | { 20 | name: 'handbag', 21 | description: 'Black handbag.', 22 | quantity: '1', 23 | price: '15', 24 | currency: 'USD', 25 | }, 26 | ]; 27 | } 28 | 29 | function getProps() { 30 | return { 31 | id: 'vue-paypal-test', 32 | amount: '30.00', 33 | client: credentials, 34 | currency: 'USD', 35 | commit: true, 36 | env: 'sandbox', 37 | invoiceNumber: '201705051001', 38 | items: getItems(), 39 | }; 40 | } 41 | 42 | describe('PayPalCheckout.vue', () => { 43 | let checkout; 44 | 45 | beforeEach(() => { 46 | const localVue = createLocalVue(); 47 | checkout = shallowMount(PayPalCheckout, { 48 | attachToDocument: true, 49 | propsData: getProps(), 50 | localVue, 51 | }); 52 | }); 53 | 54 | test('should have the amount prop', () => { 55 | expect(checkout.props().amount).toEqual('30.00'); 56 | }); 57 | 58 | test('should have the client prop with production and sandbox', () => { 59 | expect(checkout.vm.client).toEqual(credentials); 60 | }); 61 | 62 | test('should have the currency prop', () => { 63 | expect(checkout.vm).toEqual(expect.objectContaining({ 64 | currency: expect.any(String), 65 | })); 66 | expect(checkout.vm.currency.length).toBeGreaterThan(2); 67 | }); 68 | 69 | test('should have the commit prop', () => { 70 | expect(checkout.props().commit).toBe(true); 71 | }); 72 | 73 | test('should have the env prop', () => { 74 | expect(checkout.props().env).toBeTruthy(); 75 | }); 76 | 77 | test('should have the invoiceNumber prop', () => { 78 | expect(checkout.props().invoiceNumber).toEqual('201705051001'); 79 | }); 80 | 81 | test('should have the items prop', () => { 82 | expect(checkout.vm).toEqual(expect.objectContaining({ 83 | items: expect.any(Array), 84 | })); 85 | }); 86 | 87 | // TODO: renable after jsdom fixes css parsing 88 | describe('iframe rendering', () => { 89 | test('div', () => { 90 | const div = checkout.find('div'); 91 | expect(div.is('div')).toBe(true); 92 | }); 93 | 94 | test('has zoid class', () => { 95 | expect(checkout.contains('.zoid-visible')).toBe(true); 96 | }); 97 | 98 | test('has same HTML structure', () => { 99 | const renderer = createRenderer(); 100 | renderer.renderToString(checkout.vm, (err, str) => { 101 | if (err) throw new Error(err); 102 | expect(str).toMatchSnapshot(); 103 | }); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/webstorm,phpstorm,osx,linux,node 2 | 3 | test/unit/coverage/ 4 | 5 | ### WebStorm ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 7 | 8 | *.iml 9 | 10 | ## Directory-based project format: 11 | .idea/ 12 | # if you remove the above rule, at least ignore the following: 13 | 14 | # User-specific stuff: 15 | # .idea/workspace.xml 16 | # .idea/tasks.xml 17 | # .idea/dictionaries 18 | # .idea/shelf 19 | 20 | # Sensitive or high-churn files: 21 | # .idea/dataSources.ids 22 | # .idea/dataSources.xml 23 | # .idea/sqlDataSources.xml 24 | # .idea/dynamic.xml 25 | # .idea/uiDesigner.xml 26 | 27 | # Gradle: 28 | # .idea/gradle.xml 29 | # .idea/libraries 30 | 31 | # Mongo Explorer plugin: 32 | # .idea/mongoSettings.xml 33 | 34 | ## File-based project format: 35 | *.ipr 36 | *.iws 37 | 38 | ## Plugin-specific files: 39 | 40 | # IntelliJ 41 | /out/ 42 | 43 | # mpeltonen/sbt-idea plugin 44 | .idea_modules/ 45 | 46 | # JIRA plugin 47 | atlassian-ide-plugin.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | fabric.properties 54 | 55 | 56 | ### PhpStorm ### 57 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 58 | 59 | ## Directory-based project format: 60 | # if you remove the above rule, at least ignore the following: 61 | 62 | # User-specific stuff: 63 | # .idea/workspace.xml 64 | # .idea/tasks.xml 65 | # .idea/dictionaries 66 | # .idea/shelf 67 | 68 | # Sensitive or high-churn files: 69 | # .idea/dataSources.ids 70 | # .idea/dataSources.xml 71 | # .idea/sqlDataSources.xml 72 | # .idea/dynamic.xml 73 | # .idea/uiDesigner.xml 74 | 75 | # Gradle: 76 | # .idea/gradle.xml 77 | # .idea/libraries 78 | 79 | # Mongo Explorer plugin: 80 | # .idea/mongoSettings.xml 81 | 82 | ## File-based project format: 83 | ## Plugin-specific files: 84 | 85 | # IntelliJ 86 | # mpeltonen/sbt-idea plugin 87 | # JIRA plugin 88 | # Crashlytics plugin (for Android Studio and IntelliJ) 89 | ### OSX ### 90 | .DS_Store 91 | .AppleDouble 92 | .LSOverride 93 | 94 | # Icon must end with two \r 95 | Icon 96 | 97 | 98 | # Thumbnails 99 | ._* 100 | 101 | # Files that might appear in the root of a volume 102 | .DocumentRevisions-V100 103 | .fseventsd 104 | .Spotlight-V100 105 | .TemporaryItems 106 | .Trashes 107 | .VolumeIcon.icns 108 | 109 | # Directories potentially created on remote AFP share 110 | .AppleDB 111 | .AppleDesktop 112 | Network Trash Folder 113 | Temporary Items 114 | .apdisk 115 | 116 | 117 | ### Linux ### 118 | *~ 119 | 120 | # temporary files which can be created if a process still has a handle open of a deleted file 121 | .fuse_hidden* 122 | 123 | # KDE directory preferences 124 | .directory 125 | 126 | # Linux trash folder which might appear on any partition or disk 127 | .Trash-* 128 | 129 | 130 | ### Node ### 131 | # Logs 132 | logs 133 | *.log 134 | npm-debug.log* 135 | 136 | # Runtime data 137 | pids 138 | *.pid 139 | *.seed 140 | 141 | # Directory for instrumented libs generated by jscoverage/JSCover 142 | lib-cov 143 | 144 | # Coverage directory used by tools like istanbul 145 | coverage 146 | 147 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 148 | .grunt 149 | 150 | # node-waf configuration 151 | .lock-wscript 152 | 153 | # Compiled binary addons (http://nodejs.org/api/addons.html) 154 | build/Release 155 | 156 | # Dependency directory 157 | node_modules 158 | 159 | # Optional npm cache directory 160 | .npm 161 | 162 | # Optional REPL history 163 | .node_repl_history 164 | -------------------------------------------------------------------------------- /test/unit/specs/components/SimpleMethods.spec.js: -------------------------------------------------------------------------------- 1 | import { createLocalVue, shallowMount } from '@vue/test-utils'; 2 | import PayPalCheckout from '@/components/PayPalCheckout.vue'; 3 | 4 | const credentials = { 5 | sandbox: 'Ad1voWYq3VL8J4jy6zWARKwz4tjbuDl_TFBa3WQqy_DwAFWd7hkU4i99jijGqaoqU3E-ODqXDayVnOdl', 6 | production: 'AVZhosFzrnZ5Mf3tiOxAD0M6NHv8pcB2IFNHAfp_h69mmbd-LElFYkJUSII3Y0FPbm7S7lxBuqWImLbl', 7 | }; 8 | 9 | function getItems() { 10 | return [ 11 | { 12 | name: 'hat', 13 | description: 'Brown hat.', 14 | quantity: '5', 15 | price: '3', 16 | currency: 'USD', 17 | }, 18 | { 19 | name: 'handbag', 20 | description: 'Black handbag.', 21 | quantity: '1', 22 | price: '15', 23 | currency: 'USD', 24 | }, 25 | ]; 26 | } 27 | 28 | function getProps() { 29 | return { 30 | amount: '30.00', 31 | client: credentials, 32 | currency: 'USD', 33 | commit: true, 34 | env: 'sandbox', 35 | experience: { 36 | input_fields: { 37 | no_shipping: 1, 38 | }, 39 | }, 40 | invoiceNumber: '201801011001', 41 | items: getItems(), 42 | }; 43 | } 44 | 45 | jest.mock('paypal-checkout', () => ({ 46 | Button: { 47 | render: jest.fn(), 48 | }, 49 | rest: { 50 | payment: { 51 | create: jest.fn((env, client, payment) => Promise.resolve(payment)), 52 | }, 53 | }, 54 | })); 55 | 56 | describe('Methods within PayPalCheckout.vue', () => { 57 | let checkout; 58 | 59 | beforeEach(() => { 60 | const localVue = createLocalVue(); 61 | checkout = shallowMount(PayPalCheckout, { 62 | localVue, 63 | attachToDocument: true, 64 | propsData: getProps(), 65 | }); 66 | }); 67 | 68 | describe('Environment', () => { 69 | test('env prop is true', () => { 70 | expect(checkout.props().env).toEqual('sandbox'); 71 | }); 72 | }); 73 | 74 | describe('vue.payment()', () => { 75 | test('has payment()', () => { 76 | expect(checkout.vm).toEqual(expect.objectContaining({ 77 | payment: expect.any(Function), 78 | })); 79 | }); 80 | 81 | test('returns a payment object', () => ( 82 | checkout.vm.payment().then((p) => { 83 | expect(p).toBeInstanceOf(Object); 84 | }) 85 | )); 86 | 87 | test('payment object has experience object', () => { 88 | checkout.vm.payment().then((p) => { 89 | expect(p.experience).toEqual(checkout.vm.experience); 90 | }); 91 | }); 92 | 93 | test('payment object has transactions array', () => ( 94 | checkout.vm.payment().then((p) => { 95 | expect(p.payment).toEqual(expect.objectContaining({ 96 | transactions: expect.any(Array), 97 | })); 98 | }))); 99 | 100 | test('payment object has one single transaction', () => ( 101 | checkout.vm.payment().then((p) => { 102 | expect(p.payment.transactions.length).toBe(1); 103 | }) 104 | )); 105 | 106 | test('transaction has the right amount', () => ( 107 | checkout.vm.payment().then((p) => { 108 | const transaction = p.payment.transactions[0]; 109 | expect(transaction.amount).toEqual(expect.objectContaining({ 110 | total: expect.any(String), 111 | })); 112 | expect(transaction.amount.total).toEqual('30.00'); 113 | }) 114 | )); 115 | 116 | test('transaction has the right currency', () => ( 117 | checkout.vm.payment().then((p) => { 118 | const transaction = p.payment.transactions[0]; 119 | expect(transaction.amount).toEqual(expect.objectContaining({ 120 | currency: expect.any(String), 121 | })); 122 | expect(transaction.amount.currency).toEqual('USD'); 123 | }) 124 | )); 125 | 126 | test('transaction has the right invoice number', () => ( 127 | checkout.vm.payment().then((p) => { 128 | const transaction = p.payment.transactions[0]; 129 | expect(transaction).toEqual(expect.objectContaining({ 130 | invoice_number: expect.any(String), 131 | })); 132 | expect(transaction.invoice_number).toEqual('201801011001'); 133 | }) 134 | )); 135 | 136 | test('transaction has a item_list', () => ( 137 | checkout.vm.payment().then((p) => { 138 | const transaction = p.payment.transactions[0]; 139 | expect(transaction).toEqual(expect.objectContaining({ 140 | item_list: expect.any(Object), 141 | })); 142 | }) 143 | )); 144 | 145 | test('transaction has items array', () => ( 146 | checkout.vm.payment().then((p) => { 147 | const itemList = p.payment.transactions[0].item_list; 148 | expect(itemList).toEqual(expect.objectContaining({ 149 | items: expect.any(Array), 150 | })); 151 | expect(itemList.items).toEqual(getItems()); 152 | }) 153 | )); 154 | }); 155 | 156 | describe('action methods', () => { 157 | test('has onAuthorize() and onCancel()', () => { 158 | expect(checkout.vm).toEqual(expect.objectContaining({ 159 | onAuthorize: expect.any(Function), 160 | onCancel: expect.any(Function), 161 | })); 162 | }); 163 | 164 | test('onAuthorize() returns true and not a promise if commit is false', () => { 165 | const component = shallowMount(PayPalCheckout, { 166 | localVue: createLocalVue(), 167 | attachToDocument: true, 168 | propsData: { ...getProps(), commit: false }, 169 | }); 170 | 171 | expect(component.vm.onAuthorize()).toEqual(true); 172 | }); 173 | }); 174 | }); 175 | -------------------------------------------------------------------------------- /dist/vue-paypal-checkout.esm.js: -------------------------------------------------------------------------------- 1 | import _slicedToArray from 'babel-runtime/helpers/slicedToArray'; 2 | import _Object$assign from 'babel-runtime/core-js/object/assign'; 3 | import paypal from 'paypal-checkout'; 4 | import _Object$keys from 'babel-runtime/core-js/object/keys'; 5 | 6 | var requiredProps = [['amount'], ['currency', 'USD']]; 7 | 8 | var optionalProps = [['id'], ['invoiceNumber']]; 9 | 10 | var specificProps = [{ 11 | name: 'env', 12 | type: String, 13 | required: false, 14 | default: 'production', 15 | validator: function validator(value) { 16 | return ['sandbox', 'production'].indexOf(value) !== -1; 17 | } 18 | }, { 19 | name: 'client', 20 | type: Object, 21 | required: true 22 | }, { 23 | name: 'details', 24 | type: Object, 25 | required: false, 26 | default: function _default() { 27 | return {}; 28 | } 29 | }, { 30 | name: 'commit', 31 | type: Boolean, 32 | required: false, 33 | default: true 34 | }]; 35 | 36 | function defaultProps () { 37 | var props = {}; 38 | 39 | // TODO: make type configurable 40 | // all required props are type String for now 41 | requiredProps.forEach(function (_ref) { 42 | var _ref2 = _slicedToArray(_ref, 2), 43 | name = _ref2[0], 44 | def = _ref2[1]; 45 | 46 | props[name] = { 47 | type: String, 48 | required: true, 49 | default: typeof def !== 'undefined' ? def : undefined 50 | }; 51 | }); 52 | 53 | // TODO: make type configurable 54 | // all optional props are type String for now 55 | optionalProps.forEach(function (_ref3) { 56 | var _ref4 = _slicedToArray(_ref3, 2), 57 | name = _ref4[0], 58 | def = _ref4[1]; 59 | 60 | props[name] = { 61 | type: String, 62 | required: false, 63 | default: typeof def !== 'undefined' ? def : undefined 64 | }; 65 | }); 66 | 67 | // all specific props are declared ahead of time 68 | specificProps.forEach(function (prop) { 69 | props[prop.name] = { 70 | type: prop.type, 71 | required: prop.required 72 | }; 73 | 74 | if (prop.default !== undefined) { 75 | props[prop.name].default = prop.default; 76 | } 77 | }); 78 | 79 | return props; 80 | } 81 | 82 | function paypalProp(prop) { 83 | /* eslint-disable no-param-reassign */ 84 | var define = function getDefine(object) { 85 | return function def(name, param, defaultParam) { 86 | var isDefined = typeof param !== 'undefined' && param !== null; 87 | var hasDefault = typeof defaultParam !== 'undefined' && defaultParam !== null; 88 | 89 | if (isDefined) object[name] = param;else if (hasDefault) object[name] = defaultParam;else object[name] = undefined; // TODO: throw err? 90 | }; 91 | }(this); 92 | 93 | define('name', prop.name); 94 | define('propName', prop.paypalName, prop.name); 95 | define('injection', prop.injection, 'button'); 96 | define('type', prop.type, Object); 97 | define('required', prop.required, false); 98 | define('validator', prop.validator, undefined); 99 | 100 | this.transforms = []; 101 | } 102 | 103 | paypalProp.prototype.getVmProp = function getVmProp() { 104 | return { 105 | type: this.type, 106 | required: this.required, 107 | validator: this.validator 108 | }; 109 | }; 110 | 111 | paypalProp.prototype.addChangeTransform = function addChangeTransform(callable) { 112 | this.transforms.push(callable); 113 | }; 114 | 115 | paypalProp.prototype.getChange = function getChange(src) { 116 | var value = src[this.name]; 117 | 118 | // change the value if necessary... 119 | if (value !== undefined && value !== null) { 120 | this.transforms.forEach(function (transform) { 121 | value = transform(value); 122 | }); 123 | } 124 | 125 | return { 126 | name: this.propName, 127 | value: value 128 | }; 129 | }; 130 | 131 | var propTypes = { 132 | BUTTON: 'button', 133 | PAYMENT: 'payment', 134 | TRANSACTION: 'transaction' 135 | }; 136 | 137 | function assignToPropertyObject(props) { 138 | return function assignTo(vm, type) { 139 | var obj = {}; 140 | 141 | props.getTypedProps(type).forEach(function (item) { 142 | var _item$getChange = item.getChange(vm), 143 | name = _item$getChange.name, 144 | value = _item$getChange.value; 145 | 146 | if (name !== undefined && value !== undefined) { 147 | obj[name] = value; 148 | } 149 | }); 150 | 151 | return obj; 152 | }; 153 | } 154 | 155 | // TODO: add item validator 156 | var itemsPayPalProp = new paypalProp({ 157 | name: 'items', 158 | paypalName: 'item_list', 159 | type: Array, 160 | injection: propTypes.TRANSACTION 161 | }); 162 | 163 | itemsPayPalProp.addChangeTransform(function (items) { 164 | return { items: items }; 165 | }); 166 | 167 | var shippingAddressProp = new paypalProp({ 168 | name: 'shippingAddress', 169 | paypalName: 'shipping_address', 170 | type: Object, 171 | injection: propTypes.TRANSACTION 172 | }); 173 | 174 | var props = [ 175 | // Button Props 176 | new paypalProp({ name: 'buttonStyle', paypalName: 'style', injection: propTypes.BUTTON }), new paypalProp({ name: 'braintree', injection: propTypes.BUTTON }), new paypalProp({ name: 'locale', type: String, injection: propTypes.BUTTON }), 177 | 178 | // Payment Props 179 | new paypalProp({ name: 'experience', injection: propTypes.PAYMENT }), 180 | 181 | // Transaction Props 182 | new paypalProp({ 183 | name: 'invoiceNumber', 184 | paypalName: 'invoice_number', 185 | type: String, 186 | injection: propTypes.TRANSACTION 187 | }), new paypalProp({ 188 | name: 'notifyUrl', 189 | paypalName: 'notify_url', 190 | type: String, 191 | validator: function validator(value) { 192 | return (/^https?:\/\//.test(value) 193 | ); 194 | }, 195 | injection: propTypes.TRANSACTION 196 | }), itemsPayPalProp, shippingAddressProp]; 197 | 198 | function vmProps() { 199 | var vm = {}; 200 | 201 | props.forEach(function (prop) { 202 | vm[prop.name] = prop.getVmProp(); 203 | }); 204 | 205 | return vm; 206 | } 207 | 208 | function getTypedProps(type) { 209 | return props.filter(function (prop) { 210 | return prop.injection === type; 211 | }); 212 | } 213 | 214 | var additionalProps = { 215 | vmProps: vmProps, 216 | getTypedProps: getTypedProps 217 | }; 218 | 219 | var assignTo = assignToPropertyObject(additionalProps); 220 | 221 | var script = { 222 | props: _Object$assign(defaultProps(), additionalProps.vmProps()), 223 | methods: { 224 | payment: function payment() { 225 | var vue = this; 226 | 227 | var transaction = _Object$assign({ 228 | amount: { 229 | total: this.amount, 230 | currency: this.currency, 231 | details: this.details 232 | } 233 | }, assignTo(vue, propTypes.TRANSACTION)); 234 | 235 | // TODO: clean this up 236 | if (transaction.shipping_address && transaction.item_list) { 237 | transaction.item_list.shipping_address = transaction.shipping_address; 238 | delete transaction.shipping_address; 239 | } 240 | 241 | var payment = { 242 | transactions: [transaction] 243 | }; 244 | 245 | return paypal.rest.payment.create(this.env, this.client, _Object$assign({ payment: payment }, assignTo(vue, propTypes.PAYMENT))); 246 | }, 247 | onAuthorize: function onAuthorize(data, actions) { 248 | var vue = this; 249 | vue.$emit('payment-authorized', data); 250 | 251 | if (this.commit) { 252 | return actions.payment.execute().then(function (response) { 253 | vue.$emit('payment-completed', response); 254 | }); 255 | } 256 | 257 | return true; 258 | }, 259 | onCancel: function onCancel(data) { 260 | var vue = this; 261 | vue.$emit('payment-cancelled', data); 262 | } 263 | }, 264 | mounted: function mounted() { 265 | var vue = this; 266 | var button = _Object$assign({ 267 | // Pass in env 268 | env: vue.env, 269 | 270 | // Pass in the client ids to use to create your transaction 271 | // on sandbox and production environments 272 | client: vue.client, 273 | 274 | // Pass the payment details for your transaction 275 | // See https://developer.paypal.com/docs/api/payments/#payment_create for the expected json parameters 276 | payment: vue.payment, 277 | 278 | // Display a "Pay Now" button rather than a "Continue" button 279 | commit: vue.commit, 280 | 281 | // Pass a function to be called when the customer completes the payment 282 | onAuthorize: vue.onAuthorize, 283 | 284 | // Pass a function to be called when the customer cancels the payment 285 | onCancel: vue.onCancel 286 | }, assignTo(vue, propTypes.BUTTON)); 287 | 288 | paypal.Button.render(button, vue.$el); 289 | } 290 | }; 291 | 292 | /* script */ 293 | var __vue_script__ = script; 294 | 295 | /* template */ 296 | var __vue_render__ = function __vue_render__() { 297 | var _vm = this; 298 | var _h = _vm.$createElement; 299 | var _c = _vm._self._c || _h; 300 | return _c("div", { staticClass: "paypal-button", attrs: { id: _vm.id } }); 301 | }; 302 | var __vue_staticRenderFns__ = []; 303 | __vue_render__._withStripped = true; 304 | 305 | /* style */ 306 | var __vue_inject_styles__ = undefined; 307 | /* scoped */ 308 | var __vue_scope_id__ = undefined; 309 | /* module identifier */ 310 | var __vue_module_identifier__ = undefined; 311 | /* functional template */ 312 | var __vue_is_functional_template__ = false; 313 | /* component normalizer */ 314 | function __vue_normalize__(template, style, script$$1, scope, functional, moduleIdentifier, createInjector, createInjectorSSR) { 315 | var component = (typeof script$$1 === 'function' ? script$$1.options : script$$1) || {}; 316 | 317 | // For security concerns, we use only base name in production mode. 318 | component.__file = "/home/khoa/src/github.com/khoanguyen96/paypal/src/components/PayPalCheckout.vue"; 319 | 320 | if (!component.render) { 321 | component.render = template.render; 322 | component.staticRenderFns = template.staticRenderFns; 323 | component._compiled = true; 324 | 325 | if (functional) component.functional = true; 326 | } 327 | 328 | component._scopeId = scope; 329 | 330 | return component; 331 | } 332 | /* style inject */ 333 | 334 | /* style inject SSR */ 335 | 336 | var PayPalCheckout = __vue_normalize__({ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, undefined, undefined); 337 | 338 | var components = { 339 | 'paypal-checkout': PayPalCheckout 340 | }; 341 | 342 | _Object$keys(components).forEach(function (name) { 343 | // in browsers ~ 344 | if (typeof window !== 'undefined' && window.Vue) { 345 | window.Vue.component(name, components[name]); 346 | } 347 | }); 348 | 349 | export default PayPalCheckout; 350 | -------------------------------------------------------------------------------- /dist/vue-paypal-checkout.common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } 4 | 5 | var _slicedToArray = _interopDefault(require('babel-runtime/helpers/slicedToArray')); 6 | var _Object$assign = _interopDefault(require('babel-runtime/core-js/object/assign')); 7 | var paypal = _interopDefault(require('paypal-checkout')); 8 | var _Object$keys = _interopDefault(require('babel-runtime/core-js/object/keys')); 9 | 10 | var requiredProps = [['amount'], ['currency', 'USD']]; 11 | 12 | var optionalProps = [['id'], ['invoiceNumber']]; 13 | 14 | var specificProps = [{ 15 | name: 'env', 16 | type: String, 17 | required: false, 18 | default: 'production', 19 | validator: function validator(value) { 20 | return ['sandbox', 'production'].indexOf(value) !== -1; 21 | } 22 | }, { 23 | name: 'client', 24 | type: Object, 25 | required: true 26 | }, { 27 | name: 'details', 28 | type: Object, 29 | required: false, 30 | default: function _default() { 31 | return {}; 32 | } 33 | }, { 34 | name: 'commit', 35 | type: Boolean, 36 | required: false, 37 | default: true 38 | }]; 39 | 40 | function defaultProps () { 41 | var props = {}; 42 | 43 | // TODO: make type configurable 44 | // all required props are type String for now 45 | requiredProps.forEach(function (_ref) { 46 | var _ref2 = _slicedToArray(_ref, 2), 47 | name = _ref2[0], 48 | def = _ref2[1]; 49 | 50 | props[name] = { 51 | type: String, 52 | required: true, 53 | default: typeof def !== 'undefined' ? def : undefined 54 | }; 55 | }); 56 | 57 | // TODO: make type configurable 58 | // all optional props are type String for now 59 | optionalProps.forEach(function (_ref3) { 60 | var _ref4 = _slicedToArray(_ref3, 2), 61 | name = _ref4[0], 62 | def = _ref4[1]; 63 | 64 | props[name] = { 65 | type: String, 66 | required: false, 67 | default: typeof def !== 'undefined' ? def : undefined 68 | }; 69 | }); 70 | 71 | // all specific props are declared ahead of time 72 | specificProps.forEach(function (prop) { 73 | props[prop.name] = { 74 | type: prop.type, 75 | required: prop.required 76 | }; 77 | 78 | if (prop.default !== undefined) { 79 | props[prop.name].default = prop.default; 80 | } 81 | }); 82 | 83 | return props; 84 | } 85 | 86 | function paypalProp(prop) { 87 | /* eslint-disable no-param-reassign */ 88 | var define = function getDefine(object) { 89 | return function def(name, param, defaultParam) { 90 | var isDefined = typeof param !== 'undefined' && param !== null; 91 | var hasDefault = typeof defaultParam !== 'undefined' && defaultParam !== null; 92 | 93 | if (isDefined) object[name] = param;else if (hasDefault) object[name] = defaultParam;else object[name] = undefined; // TODO: throw err? 94 | }; 95 | }(this); 96 | 97 | define('name', prop.name); 98 | define('propName', prop.paypalName, prop.name); 99 | define('injection', prop.injection, 'button'); 100 | define('type', prop.type, Object); 101 | define('required', prop.required, false); 102 | define('validator', prop.validator, undefined); 103 | 104 | this.transforms = []; 105 | } 106 | 107 | paypalProp.prototype.getVmProp = function getVmProp() { 108 | return { 109 | type: this.type, 110 | required: this.required, 111 | validator: this.validator 112 | }; 113 | }; 114 | 115 | paypalProp.prototype.addChangeTransform = function addChangeTransform(callable) { 116 | this.transforms.push(callable); 117 | }; 118 | 119 | paypalProp.prototype.getChange = function getChange(src) { 120 | var value = src[this.name]; 121 | 122 | // change the value if necessary... 123 | if (value !== undefined && value !== null) { 124 | this.transforms.forEach(function (transform) { 125 | value = transform(value); 126 | }); 127 | } 128 | 129 | return { 130 | name: this.propName, 131 | value: value 132 | }; 133 | }; 134 | 135 | var propTypes = { 136 | BUTTON: 'button', 137 | PAYMENT: 'payment', 138 | TRANSACTION: 'transaction' 139 | }; 140 | 141 | function assignToPropertyObject(props) { 142 | return function assignTo(vm, type) { 143 | var obj = {}; 144 | 145 | props.getTypedProps(type).forEach(function (item) { 146 | var _item$getChange = item.getChange(vm), 147 | name = _item$getChange.name, 148 | value = _item$getChange.value; 149 | 150 | if (name !== undefined && value !== undefined) { 151 | obj[name] = value; 152 | } 153 | }); 154 | 155 | return obj; 156 | }; 157 | } 158 | 159 | // TODO: add item validator 160 | var itemsPayPalProp = new paypalProp({ 161 | name: 'items', 162 | paypalName: 'item_list', 163 | type: Array, 164 | injection: propTypes.TRANSACTION 165 | }); 166 | 167 | itemsPayPalProp.addChangeTransform(function (items) { 168 | return { items: items }; 169 | }); 170 | 171 | var shippingAddressProp = new paypalProp({ 172 | name: 'shippingAddress', 173 | paypalName: 'shipping_address', 174 | type: Object, 175 | injection: propTypes.TRANSACTION 176 | }); 177 | 178 | var props = [ 179 | // Button Props 180 | new paypalProp({ name: 'buttonStyle', paypalName: 'style', injection: propTypes.BUTTON }), new paypalProp({ name: 'braintree', injection: propTypes.BUTTON }), new paypalProp({ name: 'locale', type: String, injection: propTypes.BUTTON }), 181 | 182 | // Payment Props 183 | new paypalProp({ name: 'experience', injection: propTypes.PAYMENT }), 184 | 185 | // Transaction Props 186 | new paypalProp({ 187 | name: 'invoiceNumber', 188 | paypalName: 'invoice_number', 189 | type: String, 190 | injection: propTypes.TRANSACTION 191 | }), new paypalProp({ 192 | name: 'notifyUrl', 193 | paypalName: 'notify_url', 194 | type: String, 195 | validator: function validator(value) { 196 | return (/^https?:\/\//.test(value) 197 | ); 198 | }, 199 | injection: propTypes.TRANSACTION 200 | }), itemsPayPalProp, shippingAddressProp]; 201 | 202 | function vmProps() { 203 | var vm = {}; 204 | 205 | props.forEach(function (prop) { 206 | vm[prop.name] = prop.getVmProp(); 207 | }); 208 | 209 | return vm; 210 | } 211 | 212 | function getTypedProps(type) { 213 | return props.filter(function (prop) { 214 | return prop.injection === type; 215 | }); 216 | } 217 | 218 | var additionalProps = { 219 | vmProps: vmProps, 220 | getTypedProps: getTypedProps 221 | }; 222 | 223 | var assignTo = assignToPropertyObject(additionalProps); 224 | 225 | var script = { 226 | props: _Object$assign(defaultProps(), additionalProps.vmProps()), 227 | methods: { 228 | payment: function payment() { 229 | var vue = this; 230 | 231 | var transaction = _Object$assign({ 232 | amount: { 233 | total: this.amount, 234 | currency: this.currency, 235 | details: this.details 236 | } 237 | }, assignTo(vue, propTypes.TRANSACTION)); 238 | 239 | // TODO: clean this up 240 | if (transaction.shipping_address && transaction.item_list) { 241 | transaction.item_list.shipping_address = transaction.shipping_address; 242 | delete transaction.shipping_address; 243 | } 244 | 245 | var payment = { 246 | transactions: [transaction] 247 | }; 248 | 249 | return paypal.rest.payment.create(this.env, this.client, _Object$assign({ payment: payment }, assignTo(vue, propTypes.PAYMENT))); 250 | }, 251 | onAuthorize: function onAuthorize(data, actions) { 252 | var vue = this; 253 | vue.$emit('payment-authorized', data); 254 | 255 | if (this.commit) { 256 | return actions.payment.execute().then(function (response) { 257 | vue.$emit('payment-completed', response); 258 | }); 259 | } 260 | 261 | return true; 262 | }, 263 | onCancel: function onCancel(data) { 264 | var vue = this; 265 | vue.$emit('payment-cancelled', data); 266 | } 267 | }, 268 | mounted: function mounted() { 269 | var vue = this; 270 | var button = _Object$assign({ 271 | // Pass in env 272 | env: vue.env, 273 | 274 | // Pass in the client ids to use to create your transaction 275 | // on sandbox and production environments 276 | client: vue.client, 277 | 278 | // Pass the payment details for your transaction 279 | // See https://developer.paypal.com/docs/api/payments/#payment_create for the expected json parameters 280 | payment: vue.payment, 281 | 282 | // Display a "Pay Now" button rather than a "Continue" button 283 | commit: vue.commit, 284 | 285 | // Pass a function to be called when the customer completes the payment 286 | onAuthorize: vue.onAuthorize, 287 | 288 | // Pass a function to be called when the customer cancels the payment 289 | onCancel: vue.onCancel 290 | }, assignTo(vue, propTypes.BUTTON)); 291 | 292 | paypal.Button.render(button, vue.$el); 293 | } 294 | }; 295 | 296 | /* script */ 297 | var __vue_script__ = script; 298 | 299 | /* template */ 300 | var __vue_render__ = function __vue_render__() { 301 | var _vm = this; 302 | var _h = _vm.$createElement; 303 | var _c = _vm._self._c || _h; 304 | return _c("div", { staticClass: "paypal-button", attrs: { id: _vm.id } }); 305 | }; 306 | var __vue_staticRenderFns__ = []; 307 | __vue_render__._withStripped = true; 308 | 309 | /* style */ 310 | var __vue_inject_styles__ = undefined; 311 | /* scoped */ 312 | var __vue_scope_id__ = undefined; 313 | /* module identifier */ 314 | var __vue_module_identifier__ = undefined; 315 | /* functional template */ 316 | var __vue_is_functional_template__ = false; 317 | /* component normalizer */ 318 | function __vue_normalize__(template, style, script$$1, scope, functional, moduleIdentifier, createInjector, createInjectorSSR) { 319 | var component = (typeof script$$1 === 'function' ? script$$1.options : script$$1) || {}; 320 | 321 | // For security concerns, we use only base name in production mode. 322 | component.__file = "/home/khoa/src/github.com/khoanguyen96/paypal/src/components/PayPalCheckout.vue"; 323 | 324 | if (!component.render) { 325 | component.render = template.render; 326 | component.staticRenderFns = template.staticRenderFns; 327 | component._compiled = true; 328 | 329 | if (functional) component.functional = true; 330 | } 331 | 332 | component._scopeId = scope; 333 | 334 | return component; 335 | } 336 | /* style inject */ 337 | 338 | /* style inject SSR */ 339 | 340 | var PayPalCheckout = __vue_normalize__({ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, __vue_inject_styles__, __vue_script__, __vue_scope_id__, __vue_is_functional_template__, __vue_module_identifier__, undefined, undefined); 341 | 342 | var components = { 343 | 'paypal-checkout': PayPalCheckout 344 | }; 345 | 346 | _Object$keys(components).forEach(function (name) { 347 | // in browsers ~ 348 | if (typeof window !== 'undefined' && window.Vue) { 349 | window.Vue.component(name, components[name]); 350 | } 351 | }); 352 | 353 | module.exports = PayPalCheckout; 354 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-paypal-checkout 2 | 3 | ## Deprecated 4 | Please use the official PayPal Checkout Components: https://github.com/paypal/paypal-checkout-components 5 | 6 | [![Greenkeeper badge](https://badges.greenkeeper.io/khoanguyen96/vue-paypal-checkout.svg)](https://greenkeeper.io/) 7 | 8 | > A simple Vue.js wrapper component for `paypal-checkout` 9 | 10 | [![Travis](https://img.shields.io/travis/khoanguyen96/vue-paypal-checkout.svg)](https://travis-ci.org/khoanguyen96/vue-paypal-checkout) 11 | [![npm](https://img.shields.io/npm/v/vue-paypal-checkout.svg)](https://www.npmjs.com/package/vue-paypal-checkout) 12 | [![David](https://img.shields.io/david/khoanguyen96/vue-paypal-checkout.svg)](https://david-dm.org/khoanguyen96/vue-paypal-checkout) 13 | 14 | ## BREAKING CHANGES 15 | Recently changed [event names](#events-fired-by-the-simple-paypal-component) due to handlers not firing in HTML. 16 | 17 | ## Usage with Vue itself 18 | Simply include Vue and `vue-paypal-checkout` into your html file (using unpkg cdn) 19 | 20 | ``` html 21 | 22 | 23 | ``` 24 | 25 | By including vue-paypal-checkout in a script tag, it will automagically register the component into Vue.js 26 | ``` html 27 |
28 | 32 | 33 |
34 | ``` 35 | 36 | ``` html 37 | 48 | ``` 49 | 50 | ## Usage with Vue Loader 51 | Simply import the package into your .vue file. 52 | 53 | ``` javascript 54 | import PayPal from 'vue-paypal-checkout' 55 | 56 | export default { 57 | data() { 58 | return { 59 | paypal: { 60 | sandbox: '', 61 | production: '' 62 | } 63 | } 64 | }, 65 | components: { 66 | PayPal 67 | } 68 | } 69 | ``` 70 | 71 | ### Using the PayPal component: 72 | ``` html 73 | 77 | 78 | ``` 79 | 80 | ``` html 81 | 93 | ``` 94 | 95 | ### Specifying the PayPal environment 96 | 97 | For testing purposes, just pass in the `env` prop as `sandbox`: 98 | ``` html 99 | 104 | 105 | ``` 106 | 107 | By default, the environment is for `production`... 108 | 109 | Further examples will be using the format for VueJS Single File Components with Vue Loader. There really shouldn't be any major changes required to get it to work in a basic HTML + Vue template. 110 | 111 | ### Specifying an Invoice Number 112 | 113 | You can also specify a specific invoice number if you need so: 114 | 115 | ``` html 116 | 121 | 122 | ``` 123 | 124 | ### Specifying Items 125 | Optionally, according to the PayPal Payments API documents, you can list out any items along with your transaction. 126 | 127 | For more information, [PayPal Item List](https://developer.paypal.com/docs/api/payments/#definition-item_list) 128 | 129 | **NOTE** 130 | 131 | The items you specify must total up to the be the same amount you specified in the _`amount`_ prop. In this example the items total up to be 10 USD. 132 | 133 | ### Using the PayPal component: 134 | ``` html 135 | 140 | 141 | ``` 142 | 143 | ``` html 144 | 172 | ``` 173 | 174 | #### Using PayPal Experience Options (v2.2.0+) 175 | 176 | Just pass a valid object with the [Experience options](https://developer.paypal.com/docs/api/payment-experience/) you require in the `experience` prop: 177 | 178 | Kudos to @ilrock for mentioning the PayPal Experience options! 179 | 180 | ``` html 181 | 186 | 187 | ``` 188 | 189 | ``` html 190 | 207 | ``` 208 | 209 | #### Using Braintree SDK (v2.2.0+) 210 | 211 | Using Braintree with the PayPal Button is possible as well. Just pass in the Braintree (Web) SDK via the `braintree` prop: 212 | 213 | Kudos to @portwatcher for suggesting Braintree support! 214 | 215 | ``` html 216 | 221 | 222 | ``` 223 | 224 | ``` html 225 | 238 | ``` 239 | 240 | ``` html 241 | 257 | ``` 258 | 259 | ## Usage with Nuxt JS 260 | Simply add the script at nuxt.config.js head property 261 | 262 | ``` 263 | export default { 264 | head: { 265 | script: [ 266 | { src: 'https://unpkg.com/vue-paypal-checkout@2.0.0/dist/vue-paypal-checkout.min.js' } 267 | ] 268 | } 269 | } 270 | ``` 271 | 272 | By including vue-paypal-checkout in a script tag, it will automagically register the component into Nuxt js 273 | 274 | ## Usage with Nuxt JS - NPM 275 | ``` 276 | npm install vue-paypal-checkout 277 | ``` 278 | create a plugins called paypal.js 279 | ``` html 280 | import Vue from 'vue' 281 | import PayPal from 'vue-paypal-checkout' 282 | Vue.component('paypal-checkout', PayPal) 283 | ``` 284 | 285 | 286 | in nuxt-config don't forget to add it on plugins, and make sure you disable SSR 287 | ``` html 288 | plugins: [ 289 | { src: '~/plugins/paypal.js', ssr: false } 290 | ], 291 | 292 | ``` 293 | 294 | 295 | and use it like this 296 | ``` html 297 | 298 | 305 | 306 | 307 | ``` 308 | 309 | #### Changing Locale (v2.3.3+) 310 | `paypal-checkout` allows changing the locale of the button via a `locale` parameter. There's a `locale` prop you can use to accomplish the same: 311 | 312 | ``` html 313 | 318 | 319 | ``` 320 | 321 | #### Setting UP IPN Notifications (v2.3.5+) 322 | According to the Payments API of PayPal, setting a `notify_url` on the transaction object will allow notifications to be sent to an IPN Listener. 323 | 324 | There's a `notify_url` prop you can use to accomplish the same. 325 | 326 | ``` html 327 | 332 | 333 | ``` 334 | 335 | For more information on implementing IPN, take a look at the [PayPal docs](https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNIntro/). 336 | 337 | #### Button Style 338 | `paypal-checkout` allows changing the style of the button via a style object like so: 339 | 340 | ``` js 341 | { 342 | label: 'checkout', 343 | size: 'responsive', // small | medium | large | responsive 344 | shape: 'pill', // pill | rect 345 | color: 'gold' // gold | blue | silver | black 346 | } 347 | ``` 348 | 349 | Due to a Vue.js restriction, you'll have to pass it as a `button-style` prop to the component instead if you want to style your PayPal Checkout Button. 350 | 351 | ``` js 352 | data () { 353 | myStyle: { 354 | { 355 | label: 'checkout', 356 | size: 'responsive', 357 | shape: 'pill', 358 | color: 'gold' 359 | } 360 | } 361 | } 362 | ``` 363 | 364 | ``` html 365 | 370 | 371 | ``` 372 | 373 | #### Events fired by the Simple PayPal component: 374 | 375 | Each of these events fired also contain a payload which is essentially the response sent back from PayPal. 376 | 377 | v3.0.0+: 378 | 379 | + `payment-authorized` 380 | + `payment-completed` 381 | + `payment-cancelled` 382 | 383 | v2.3.5 and below: 384 | 385 | + `paypal-paymentAuthorized` 386 | + `paypal-paymentCompleted` 387 | + `paypal-paymentCancelled` 388 | 389 | In the instance of `payment-authorized` or `paypal-paymentAuthorized` (v2.3.5 and below), you will get back a response object similar to this: 390 | 391 | ``` json 392 | { 393 | "intent": "sale", 394 | "payerID": "UVGR8M6W9V7ZA", 395 | "paymentID": "PAY-3L661344P7749433KLD2R5ZQ", 396 | "paymentToken": "EC-0H346145A8442392H", 397 | "returnUrl" :"https://www.sandbox.paypal.com/?paymentId=PAY-3L661344P7749433KLD2R5ZQ&token=EC-0H346145A8442392H&PayerID=UVGR8M6W9V7ZA" 398 | } 399 | ``` 400 | 401 | In the instance of `payment-completed` or `paypal-paymentCompleted` (v2.3.5 and below), you will get back a response object similar to this: 402 | 403 | [Sample Payment Execute Response](https://developer.paypal.com/docs/integration/direct/payments/paypal-payments/#execute-payment) 404 | 405 | ``` json 406 | { 407 | "id": "PAY-4N746561P0587231SKQQK6MY", 408 | "create_time": "2014-09-22T23:22:27Z", 409 | "update_time": "2014-09-22T23:31:13Z", 410 | "state": "approved", 411 | "intent": "sale", 412 | "payer": { 413 | "payment_method": "paypal", 414 | "payer_info": { 415 | "email": "npurayil-uspr-60@paypal.com", 416 | "first_name": "Brian", 417 | "last_name": "Robinson", 418 | "payer_id": "JMKDKJ4D7DG7G", 419 | "shipping_address": { 420 | "line1": "4thFloor", 421 | "line2": "unit#34", 422 | "city": "SAn Jose", 423 | "state": "CA", 424 | "postal_code": "95131", 425 | "country_code": "US", 426 | "phone": "011862212345678", 427 | "recipient_name": "HelloWorld" 428 | } 429 | } 430 | } 431 | } 432 | ``` 433 | 434 | ## License and Reference 435 | vue-paypal-checkout is available under the [MIT license](http://opensource.org/licenses/MIT). 436 | 437 | vue-paypal-checkout is a wrapper Vue component that uses `paypal-checkout` which is under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) 438 | 439 | For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader). 440 | --------------------------------------------------------------------------------