├── .eslintignore ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── APIDOCS.md ├── CHANGELOG.md ├── LICENSE ├── README.md ├── Stripe_Connect_Workflow_1.jpg ├── Stripe_Connect_Workflow_2.jpg ├── TODO ├── index.js ├── package-lock.json ├── package.json └── test ├── _stripe.stub.js ├── add-customer-card.test.js ├── capture-payment.test.js ├── customer-create.test.js ├── customer-delete.test.js ├── delete-customer-card.test.js ├── fetch-customer-cards.test.js ├── get-default-customer-card.test.js ├── initiate-payment.test.js ├── refund-create.test.js ├── set-default-customer-card.test.js ├── set-vendor-bank-account.test.js ├── vendor-accept-toc.test.js ├── vendor-create.test.js ├── vendor-delete.test.js └── vendor-kyc.test.js /.eslintignore: -------------------------------------------------------------------------------- 1 | .nyc_output/ 2 | .vscode/ 3 | coverage/ 4 | node_modules/ 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "extends": "airbnb-base", 8 | "rules": { 9 | "func-names": [ 10 | "error", 11 | "never" 12 | ], 13 | "comma-dangle": [ 14 | "error", 15 | "only-multiline" 16 | ], 17 | "semi": [ 18 | "warn", 19 | "never" 20 | ], 21 | "quotes": [ 22 | "warn", 23 | "double" 24 | ], 25 | "max-len": [ 26 | "warn", 27 | { 28 | "ignoreComments": true, 29 | "ignoreTrailingComments": true, 30 | "ignoreUrls": true, 31 | "ignoreStrings": true, 32 | "ignoreTemplateLiterals": true, 33 | "ignoreRegExpLiterals": true 34 | } 35 | ], 36 | "no-console": 1, 37 | "no-unused-vars": 1, 38 | "prefer-const": 1, 39 | "no-var": 1, 40 | "eol-last": 1, 41 | "padded-blocks": 1, 42 | "import/newline-after-import": 0, 43 | "no-underscore-dangle": 0, 44 | "camelcase": 1 45 | } 46 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | ## Editors 61 | .vscode/ 62 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.jpg 2 | TODO 3 | .nyc_output/ 4 | coverage/ 5 | .vscode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - stable 5 | 6 | install: 7 | - npm install 8 | 9 | script: 10 | - npm run lint 11 | - npm test 12 | 13 | # Send coverage data to Coveralls 14 | after_script: npm run coveralls -------------------------------------------------------------------------------- /APIDOCS.md: -------------------------------------------------------------------------------- 1 | ## Functions 2 | 3 |
4 |
customerCreate([email])Promise
5 |

Create a Customer on Stripe

6 |
7 |
customerDelete(stripeCustomerId)Promise
8 |

Delete a Customer on Stripe

9 |
10 |
vendorCreate([email], [country])Promise
11 |

Create a Vendor (Custom Account on Stripe)

12 |
13 |
vendorDelete(stripeAccountId)Promise
14 |

Delete a Vendor (Custom Account on Stripe)

15 |
16 |
fetchCustomerCards(stripeCustomerId)Promise
17 |

Fetch all the Cards (or other sources) associated with the Customer with the provided id. The default card is marked as such.

18 |
19 |
addCustomerCard(stripeCustomerId, stripeToken)Promise
20 |

Add a Card to a Customer

21 |
22 |
deleteCustomerCard(stripeCustomerId, cardId)Promise
23 |

Delete a Customer's Card

24 |
25 |
getDefaultCustomerCard(stripeCustomerId)Promise
26 |

Get a Customer's default Card. Note that, this info is already available implicitly from fetchCustomerCards()

27 |
28 |
setDefaultCustomerCard(stripeCustomerId, stripeCardId)Promise
29 |

Set an existing Customer Card as the default payment source

30 |
31 |
setVendorBankAccount(stripeAccountId, _)Promise
32 |

Set the default Bank A/c for a Vendor (Custom Account on Stripe)

33 |
34 |
vendorKyc(stripeAccountId, _)Promise
35 |

Update KYC details for a Vendor (Stripe Custom Account). These are mandatory to be able to receive payments and payouts.

36 |
37 |
vendorAcceptTos(stripeAccountId, _)Promise
38 |

ToS acceptance of a Vendor (Stripe Custom Account)

39 |
40 |
initiatePayment(_)Promise
41 |

Initiate a Payment (Stripe Charge creation)

42 |
43 |
capturePayment(transactionId, [vendorAmount], [statementDescriptor])Promise
44 |

Capture an existing Payment (Stripe Charge)

45 |
46 |
refund(transactionId, amount, [reason])Promise
47 |

Refund a previously captured (but unrefunded) Charge

48 |
49 |
50 | 51 | 52 | 53 | ## customerCreate([email]) ⇒ Promise 54 | Create a Customer on Stripe 55 | 56 | **Kind**: global function 57 | 58 | | Param | Type | Default | 59 | | --- | --- | --- | 60 | | [email] | string | null | 61 | 62 | 63 | 64 | ## customerDelete(stripeCustomerId) ⇒ Promise 65 | Delete a Customer on Stripe 66 | 67 | **Kind**: global function 68 | 69 | | Param | Type | Description | 70 | | --- | --- | --- | 71 | | stripeCustomerId | string | Customer ID to delete (begins with "cus_") | 72 | 73 | 74 | 75 | ## vendorCreate([email], [country]) ⇒ Promise 76 | Create a Vendor (Custom Account on Stripe) 77 | 78 | **Kind**: global function 79 | 80 | | Param | Type | Default | 81 | | --- | --- | --- | 82 | | [email] | string | null | 83 | | [country] | string | "US" | 84 | 85 | 86 | 87 | ## vendorDelete(stripeAccountId) ⇒ Promise 88 | Delete a Vendor (Custom Account on Stripe) 89 | 90 | **Kind**: global function 91 | 92 | | Param | Type | Description | 93 | | --- | --- | --- | 94 | | stripeAccountId | string | Vendor ID (Custom Account on Stripe) to delete (begins with "acct_") | 95 | 96 | 97 | 98 | ## fetchCustomerCards(stripeCustomerId) ⇒ Promise 99 | Fetch all the Cards (or other sources) associated with the Customer with the provided id. The default card is marked as such. 100 | 101 | **Kind**: global function 102 | 103 | | Param | Type | Description | 104 | | --- | --- | --- | 105 | | stripeCustomerId | string | Customer Id (begins with "cus_") | 106 | 107 | 108 | 109 | ## addCustomerCard(stripeCustomerId, stripeToken) ⇒ Promise 110 | Add a Card to a Customer 111 | 112 | **Kind**: global function 113 | 114 | | Param | Type | Description | 115 | | --- | --- | --- | 116 | | stripeCustomerId | string | Id of the Customer to whom the Card is to be added (begins with "cus_") | 117 | | stripeToken | string | The token representing the Card to add, genetrated client-side using Stripe.js, etc. | 118 | 119 | 120 | 121 | ## deleteCustomerCard(stripeCustomerId, cardId) ⇒ Promise 122 | Delete a Customer's Card 123 | 124 | **Kind**: global function 125 | 126 | | Param | Type | Description | 127 | | --- | --- | --- | 128 | | stripeCustomerId | string | Id of the Customer for whom the Card is to be deleted (begins with "cus_") | 129 | | cardId | string | Id of the Card to be deleted | 130 | 131 | 132 | 133 | ## getDefaultCustomerCard(stripeCustomerId) ⇒ Promise 134 | Get a Customer's default Card. Note that, this info is already available implicitly from `fetchCustomerCards()` 135 | 136 | **Kind**: global function 137 | 138 | | Param | Type | Description | 139 | | --- | --- | --- | 140 | | stripeCustomerId | string | Customer Id (begins with "cus_") | 141 | 142 | 143 | 144 | ## setDefaultCustomerCard(stripeCustomerId, stripeCardId) ⇒ Promise 145 | Set an existing Customer Card as the default payment source 146 | 147 | **Kind**: global function 148 | 149 | | Param | Type | Description | 150 | | --- | --- | --- | 151 | | stripeCustomerId | string | Customer Id (begins with "cus_") | 152 | | stripeCardId | string | The id of the Card to be set as default. Must be one of the Customer's existing Cards. | 153 | 154 | 155 | 156 | ## setVendorBankAccount(stripeAccountId, _) ⇒ Promise 157 | Set the default Bank A/c for a Vendor (Custom Account on Stripe) 158 | 159 | **Kind**: global function 160 | 161 | | Param | Type | Default | Description | 162 | | --- | --- | --- | --- | 163 | | stripeAccountId | string | | Vendor Id (begins with "acct_") | 164 | | _ | object | | The Bank A/c details | 165 | | _.routingNo | string | | | 166 | | _.accountNo | string | | | 167 | | [_.accountHolderName] | string | null | | 168 | | [_.country] | string | "US" | | 169 | | [_.currency] | string | "usd" | | 170 | 171 | 172 | 173 | ## vendorKyc(stripeAccountId, _) ⇒ Promise 174 | Update KYC details for a Vendor (Stripe Custom Account). These are mandatory to be able to receive payments and payouts. 175 | 176 | **Kind**: global function 177 | 178 | | Param | Type | Description | 179 | | --- | --- | --- | 180 | | stripeAccountId | string | Vendor Id (begins with "acct_") | 181 | | _ | object | The KYC details | 182 | | _.address | object | Address details | 183 | | _.address.city | string | City | 184 | | _.address.line1 | string | Address Line 1 | 185 | | _.address.line2 | string | Address Line 2 | 186 | | _.address.postal_code | string | ZIP/Postal Code | 187 | | _.address.state | string | State/County/Province/Region | 188 | | _.address.country | string | 2-letter Country Code | 189 | | _.dob | object | Date of Birth | 190 | | _.dob.day | string | Day of Birth | 191 | | _.dob.month | string | Month of Birth | 192 | | _.dob.year | string | Year of Birth | 193 | | _.name | object | Name | 194 | | _.name.first | string | First Name | 195 | | _.name.last | string | Last Name | 196 | | _.email | string | Email Id | 197 | | _.phone | string | Phone Number | 198 | | _.businessUrl | string | Business url | 199 | | _.personalIdNumber | string | Personal ID Number (For some Non US Countries) | 200 | | _.ssnLastFour | string | Last 4 digits of the SSN | 201 | | _.mcc | string | Stripe Merchant Category Code (Ref: https://stripe.com/docs/connect/setting-mcc) | 202 | 203 | 204 | 205 | ## vendorAcceptTos(stripeAccountId, _) ⇒ Promise 206 | ToS acceptance of a Vendor (Stripe Custom Account) 207 | 208 | **Kind**: global function 209 | 210 | | Param | Type | Default | Description | 211 | | --- | --- | --- | --- | 212 | | stripeAccountId | string | | Vendor Id (begins with "acct_") | 213 | | _ | object | | ToS Acceptance details | 214 | | _.tosAcceptanceDate | number | | Date (in UNIX timestamp format) of ToS acceptance | 215 | | _.tosAcceptanceIp | string | | IP address from where the ToS was accepted | 216 | | [_.tosUserAgent] | string | null | User Agent string of the browser using which the ToS was accepted | 217 | 218 | 219 | 220 | ## initiatePayment(_) ⇒ Promise 221 | Initiate a Payment (Stripe Charge creation) 222 | 223 | **Kind**: global function 224 | 225 | | Param | Type | Default | Description | 226 | | --- | --- | --- | --- | 227 | | _ | object | | Payment (Stripe Charge) parameters | 228 | | [_.capture] | boolean | false | Whether to capture this charge later or immediately. By default, it is set to false, signifying later capture. Note that such charges need to be captured manually within 7 days, beyond which it is automatically reveresed/refunded by Stripe. | 229 | | _.customer | string | | Id of the Customer (Stripe Customer) making the payment (begins with "cus_") | 230 | | _.vendor | string | | Id of the Vendor (Stripe Custom Account) to receive the payment (begins with "acct_") | 231 | | _.amount | number | | Amount to deduct from Customer | 232 | | _.vendorAmount | number | | Amount payable to the Vendor. Must be less than or equal to `amount`. Ideally, the difference between `amount` & `vendorAmount`, minus the Stripe fees, is what the Marketplace retains as profit. | 233 | | _.currency | string | | Currency code | 234 | | [_.receiptEmail] | string | null | Email to mail the receipt from Stripe | 235 | | [_.description] | string | | Optional description | 236 | | [_.statementDescriptor] | string | | Text appearing on Bank/Card statements | 237 | 238 | 239 | 240 | ## capturePayment(transactionId, [vendorAmount], [statementDescriptor]) ⇒ Promise 241 | Capture an existing Payment (Stripe Charge) 242 | 243 | **Kind**: global function 244 | 245 | | Param | Type | Default | Description | 246 | | --- | --- | --- | --- | 247 | | transactionId | string | | Id of the Stripe Charge to capture (begins with "ch_") | 248 | | [vendorAmount] | number | | Optionally update the amount payable to Vendor. If not mentioned, the amount in original charge is used. | 249 | | [statementDescriptor] | string | | Text appearing on Bank/Card statements (overrides the one mentioned in original charge) | 250 | 251 | 252 | 253 | ## refund(transactionId, amount, [reason]) ⇒ Promise 254 | Refund a previously captured (but unrefunded) Charge 255 | 256 | **Kind**: global function 257 | 258 | | Param | Type | Default | Description | 259 | | --- | --- | --- | --- | 260 | | transactionId | string | | The Stripe Charge Id to Refund | 261 | | amount | string | | Amount to refund | 262 | | [reason] | string | null | Reason for refund | 263 | 264 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## [0.2.2](https://github.com/sayanriju/stripe-connect-functions/compare/v0.2.1...v0.2.2) (2018-05-23) 3 | 4 | 5 | 6 | 7 | ## [0.2.1](https://github.com/sayanriju/stripe-connect-functions/compare/v0.2.0...v0.2.1) (2018-05-15) 8 | 9 | 10 | 11 | 12 | # [0.2.0](https://github.com/sayanriju/stripe-connect-functions/compare/v0.1.2...v0.2.0) (2018-05-06) 13 | 14 | 15 | 16 | 17 | ## [0.1.2](https://github.com/sayanriju/stripe-connect-functions/compare/v0.1.1...v0.1.2) (2018-05-01) 18 | 19 | 20 | 21 | 22 | ## [0.1.1](https://github.com/sayanriju/stripe-connect-functions/compare/v0.1.0...v0.1.1) (2018-05-01) 23 | 24 | 25 | 26 | 27 | # 0.1.0 (2018-04-30) 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Sayan "Riju" Chakrabarti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # stripe-connect-functions ![node](https://img.shields.io/node/v/stripe-connect-functions.svg) 2 | 3 | ![Travis](https://img.shields.io/travis/sayanriju/stripe-connect-functions.svg) ![Coveralls github](https://img.shields.io/coveralls/github/sayanriju/stripe-connect-functions.svg) ![David](https://img.shields.io/david/sayanriju/stripe-connect-functions.svg) 4 | 5 | [![NPM](https://nodei.co/npm/stripe-connect-functions.png)](https://nodei.co/npm/stripe-connect-functions/) 6 | 7 | This package provides a collection of functions to help with a _Customers -> Marketplace -> Vendors_ Workflow using [Stripe Connect](https://stripe.com/connect). 8 | 9 | All the provided functions internally uses the [official NodeJS library for Stripe](https://www.npmjs.com/package/stripe). 10 | 11 | ## Example Workflow 12 | 13 | The following activity diagrams try to elucidate a rudimentary workflow. The various stages are labelled with the librarry function names to use in each case. 14 | 15 | ![https://github.com/sayanriju/stripe-connect-functions/blob/master/Stripe_Connect_Workflow_1.jpg](https://github.com/sayanriju/stripe-connect-functions/blob/master/Stripe_Connect_Workflow_1.jpg "Activity Diagram: User Management") 16 | 17 | ![https://github.com/sayanriju/stripe-connect-functions/blob/master/Stripe_Connect_Workflow_2.jpg](https://github.com/sayanriju/stripe-connect-functions/blob/master/Stripe_Connect_Workflow_2.jpg "Activity Diagram: Payment Flow") 18 | 19 | ## Installation & Basic Usage 20 | 21 | 0. Set up a Stripe Connect Account and obtain the _Secret key_, which is in the form `sk_myapp_k9DHwQESw7ntTGzdjS7vFsHs` 22 | 23 | 1. Install: 24 | 25 | ```shell 26 | npm install stripe-connect-functions 27 | ``` 28 | 29 | ​ 30 | 31 | 2. Initialize: 32 | 33 | ```javascript 34 | const stripeConnect = require("stripe-connect-functions")("sk_myapp_k9DHwQESw7ntTGzdjS7vFsHs") 35 | // ^^ Remember to replace with your own key! 36 | ``` 37 | 38 | 3. Use: 39 | 40 | ```javascript 41 | stripeConnect.fetchCustomerCards("cus_Ckc6NCwnBdzDCb") 42 | .then(console.log, console.log) // returns a Promise! 43 | ``` 44 | 45 | For details, check the Api Docs [HERE](./APIDOCS.md). 46 | 47 | ## Testing 48 | 49 | For Unit Tests, [Ava](https://github.com/avajs/ava) is being used. Code Coverage is provided by nyc/istanbul. All calls to the Stripe API are stubbed. 50 | 51 | To run the included tests: 52 | 53 | ```shell 54 | npm run test 55 | ## Or, if you don't want code coverage: 56 | npm run test:nocoverage 57 | ``` 58 | 59 | ## License 60 | 61 | [MIT](https://github.com/sayanriju/stripe-connect-functions/blob/master/LICENSE) 62 | -------------------------------------------------------------------------------- /Stripe_Connect_Workflow_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sayanriju/stripe-connect-functions/5b2cf68ab1967b763e1d9622b2768ef3c7e0da32/Stripe_Connect_Workflow_1.jpg -------------------------------------------------------------------------------- /Stripe_Connect_Workflow_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sayanriju/stripe-connect-functions/5b2cf68ab1967b763e1d9622b2768ef3c7e0da32/Stripe_Connect_Workflow_2.jpg -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | 2 | Todo: 3 | ✔ Coding: @done(18-04-29 14:00) 4 | ✔ Mark default card in list cards function itself @done(18-04-29 14:00) 5 | ✔ Documentation: @done(18-05-01 18:15) 6 | ✔ Draw Activity Diagrams @done(18-04-29 20:21) 7 | ✔ Write README.md (link Diagrams) @done(18-05-01 18:15) 8 | ✔ Complete documenting code @done(18-05-01 18:15) 9 | ✔ Generate Docs using DocumentationJS @done(18-05-01 18:15) 10 | ✔ Testing, Coverage & CI: @done(18-05-01 01:00) 11 | ✔ Write Tests using AVA. Stub using Sinon (Read up!). Add Code Coverage using nyc/istanbul. @done(18-05-01 01:00) 12 | ✔ Set up CI (Ref: https://codeburst.io/how-to-create-and-publish-your-first-node-js-module-444e7585b738), Travis & Coveralls. @done(18-05-01 01:00) 13 | ✔ Publish npm package @done(18-05-01 01:00) 14 | ✔ Get some Badges (shields.io & nodei.co) and add them to README.md @done(18-05-01 01:00) 15 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const Stripe = require("stripe") 2 | module.exports = (stripeSecretKey) => { 3 | const stripe = Stripe(stripeSecretKey) 4 | return { 5 | /** 6 | * Create a Customer on Stripe 7 | * @param {string} [email=null] 8 | * @returns {Promise} 9 | */ 10 | async customerCreate(email = null) { 11 | try { 12 | return await stripe.customers.create({ email }) 13 | } catch (err) { 14 | // istanbul ignore next 15 | throw err 16 | } 17 | }, 18 | 19 | /** 20 | * Delete a Customer on Stripe 21 | * @param {string} stripeCustomerId Customer ID to delete (begins with "cus_") 22 | * @returns {Promise} 23 | */ 24 | async customerDelete(stripeCustomerId) { 25 | try { 26 | return await stripe.customers.del(stripeCustomerId) 27 | } catch (err) { 28 | // istanbul ignore next 29 | throw err 30 | } 31 | }, 32 | 33 | /** 34 | * Create a Vendor (Custom Account on Stripe) 35 | * @param {string} [email=null] 36 | * @param {string} [country=US] 37 | * @returns {Promise} 38 | */ 39 | async vendorCreate(email = null, country = "US") { 40 | try { 41 | return await stripe.accounts.create({ 42 | country, 43 | business_type: "individual", 44 | requested_capabilities: ["transfers"], 45 | type: "custom", 46 | email, 47 | }) 48 | } catch (err) { 49 | // istanbul ignore next 50 | throw err 51 | } 52 | }, 53 | 54 | /** 55 | * Delete a Vendor (Custom Account on Stripe) 56 | * @param {string} stripeAccountId Vendor ID (Custom Account on Stripe) to delete (begins with "acct_") 57 | * @returns {Promise} 58 | */ 59 | async vendorDelete(stripeAccountId) { 60 | try { 61 | return await stripe.accounts.del(stripeAccountId) 62 | } catch (err) { 63 | // istanbul ignore next 64 | throw err 65 | } 66 | }, 67 | 68 | /** 69 | * Fetch all the Cards (or other sources) associated with the Customer with the provided id. The default card is marked as such. 70 | * @param {string} stripeCustomerId Customer Id (begins with "cus_") 71 | * @returns {Promise} 72 | */ 73 | async fetchCustomerCards(stripeCustomerId) { 74 | try { 75 | const result = await Promise.all([ 76 | await stripe.customers.listSources(stripeCustomerId), 77 | await stripe.customers.retrieve(stripeCustomerId) 78 | ]) 79 | const { data } = result[0] 80 | const { default_source } = result[1] // eslint-disable-line camelcase 81 | return data.map(card => Object.assign(card, { 82 | isDefault: (card.id === default_source) // eslint-disable-line camelcase 83 | })) 84 | } catch (err) { 85 | // istanbul ignore next 86 | throw err 87 | } 88 | }, 89 | 90 | /** 91 | * Add a Card to a Customer 92 | * @param {string} stripeCustomerId Id of the Customer to whom the Card is to be added (begins with "cus_") 93 | * @param {string} stripeToken The token representing the Card to add, genetrated client-side using Stripe.js, etc. 94 | * @returns {Promise} 95 | */ 96 | async addCustomerCard(stripeCustomerId, stripeToken) { 97 | try { 98 | return await stripe.customers.createSource(stripeCustomerId, { 99 | source: stripeToken 100 | }) 101 | } catch (err) { 102 | // istanbul ignore next 103 | throw err 104 | } 105 | }, 106 | 107 | /** 108 | * Delete a Customer's Card 109 | * @param {string} stripeCustomerId Id of the Customer for whom the Card is to be deleted (begins with "cus_") 110 | * @param {string} cardId Id of the Card to be deleted 111 | * @returns {Promise} 112 | */ 113 | async deleteCustomerCard(stripeCustomerId, cardId) { 114 | try { 115 | return await stripe 116 | .customers 117 | .deleteSource(stripeCustomerId, cardId) 118 | } catch (err) { 119 | // istanbul ignore next 120 | throw err 121 | } 122 | }, 123 | 124 | /** 125 | * Get a Customer's default Card. Note that, this info is already available implicitly from `fetchCustomerCards()` 126 | * @param {string} stripeCustomerId Customer Id (begins with "cus_") 127 | * @returns {Promise} 128 | */ 129 | async getDefaultCustomerCard(stripeCustomerId) { 130 | try { 131 | const { 132 | default_source // eslint-disable-line camelcase 133 | } = await stripe.customers.retrieve(stripeCustomerId) 134 | return default_source // eslint-disable-line camelcase 135 | } catch (err) { 136 | // istanbul ignore next 137 | throw err 138 | } 139 | }, 140 | 141 | /** 142 | * Set an existing Customer Card as the default payment source 143 | * @param {string} stripeCustomerId Customer Id (begins with "cus_") 144 | * @param {string} stripeCardId The id of the Card to be set as default. Must be one of the Customer's existing Cards. 145 | * @returns {Promise} 146 | */ 147 | async setDefaultCustomerCard(stripeCustomerId, stripeCardId) { 148 | try { 149 | const { 150 | default_source // eslint-disable-line camelcase 151 | } = await stripe.customers.update(stripeCustomerId, { // eslint-disable-line camelcase 152 | default_source: stripeCardId 153 | }) 154 | return default_source // eslint-disable-line camelcase 155 | } catch (err) { 156 | // istanbul ignore next 157 | throw err 158 | } 159 | }, 160 | 161 | /** 162 | * Set the default Bank A/c for a Vendor (Custom Account on Stripe) 163 | * @param {string} stripeAccountId Vendor Id (begins with "acct_") 164 | * @param {object} _ The Bank A/c details 165 | * @param {string} _.routingNo 166 | * @param {string} _.accountNo 167 | * @param {string} [_.accountHolderName=null] 168 | * @param {string} [_.country=US] 169 | * @param {string} [_.currency=usd] 170 | * @returns {Promise} 171 | */ 172 | async setVendorBankAccount(stripeAccountId, { 173 | routingNo, 174 | accountNo, 175 | accountHolderName = null, 176 | country = "US", 177 | currency = "usd" 178 | }) { 179 | try { 180 | const accountObj = { 181 | object: "bank_account", 182 | country, 183 | currency, 184 | account_number: accountNo, 185 | routing_number: routingNo, 186 | } 187 | if (accountHolderName !== null) { 188 | accountObj.account_holder_name = accountHolderName 189 | } 190 | return await stripe.accounts.update(stripeAccountId, { 191 | external_account: accountObj 192 | }) 193 | } catch (err) { 194 | // istanbul ignore next 195 | throw err 196 | } 197 | }, 198 | 199 | /** 200 | * Update KYC details for a Vendor (Stripe Custom Account). These are mandatory to be able to receive payments and payouts. 201 | * @param {string} stripeAccountId Vendor Id (begins with "acct_") 202 | * @param {object} _ The KYC details 203 | * @param {object} _.address Address details 204 | * @param {string} _.address.city City 205 | * @param {string} _.address.line1 Address Line 1 206 | * @param {string} _.address.line2 Address Line 2 207 | * @param {string} _.address.postal_code ZIP/Postal Code 208 | * @param {string} _.address.state State/County/Province/Region 209 | * @param {string} _.address.country 2-letter Country Code 210 | * @param {object} _.dob Date of Birth 211 | * @param {string} _.dob.day Day of Birth 212 | * @param {string} _.dob.month Month of Birth 213 | * @param {string} _.dob.year Year of Birth 214 | * @param {object} _.name Name 215 | * @param {string} _.name.first First Name 216 | * @param {string} _.name.last Last Name 217 | * @param {string} _.email Email Id 218 | * @param {string} _.phone Phone Number 219 | * @param {string} _.businessUrl Business url 220 | * @param {string} _.personalIdNumber Personal ID Number (For some Non US Countries) 221 | * @param {string} _.ssnLastFour Last 4 digits of the SSN 222 | * @param {string} _.mcc Stripe Merchant Category Code (Ref: https://stripe.com/docs/connect/setting-mcc) 223 | * @returns {Promise} 224 | */ 225 | async vendorKyc(stripeAccountId, { 226 | address = {}, 227 | dob = {}, 228 | name = {}, 229 | email = null, 230 | phone = null, 231 | businessUrl = null, 232 | ssnLastFour = null, 233 | personalIdNumber = null, 234 | mcc = null 235 | }) { 236 | try { 237 | const entityObj = { 238 | individual: { 239 | address, 240 | dob, 241 | first_name: name.first, 242 | last_name: name.last, 243 | id_number: personalIdNumber, 244 | ssn_last_4: ssnLastFour, 245 | email, 246 | phone 247 | }, 248 | business_type: "individual", 249 | business_profile: { 250 | mcc, 251 | url: businessUrl 252 | } 253 | } 254 | if (ssnLastFour !== null && personalIdNumber !== null) { 255 | entityObj.individual.ssn_last_4 = ssnLastFour 256 | entityObj.individual.id_number = personalIdNumber 257 | } 258 | return await stripe.accounts.update(stripeAccountId, entityObj) 259 | } catch (err) { 260 | // istanbul ignore next 261 | throw err 262 | } 263 | }, 264 | 265 | /** 266 | * ToS acceptance of a Vendor (Stripe Custom Account) 267 | * @param {string} stripeAccountId Vendor Id (begins with "acct_") 268 | * @param {object} _ ToS Acceptance details 269 | * @param {number} _.tosAcceptanceDate Date (in UNIX timestamp format) of ToS acceptance 270 | * @param {string} _.tosAcceptanceIp IP address from where the ToS was accepted 271 | * @param {string} [_.tosUserAgent=null] User Agent string of the browser using which the ToS was accepted 272 | * @returns {Promise} 273 | */ 274 | async vendorAcceptTos(stripeAccountId, { 275 | tosAcceptanceDate, 276 | tosAcceptanceIp, 277 | tosUserAgent = null 278 | }) { 279 | try { 280 | const tosObj = { 281 | date: tosAcceptanceDate, 282 | ip: tosAcceptanceIp, 283 | } 284 | if (tosUserAgent !== null) tosObj.user_agent = tosUserAgent 285 | return await stripe.accounts.update(stripeAccountId, { 286 | tos_acceptance: tosObj 287 | }) 288 | } catch (err) { 289 | // istanbul ignore next 290 | throw err 291 | } 292 | }, 293 | 294 | /** 295 | * Initiate a Payment (Stripe Charge creation) 296 | * @param {object} _ Payment (Stripe Charge) parameters 297 | * @param {boolean} [_.capture=false] Whether to capture this charge later or immediately. By default, it is set to false, signifying later capture. Note that such charges need to be captured manually within 7 days, beyond which it is automatically reveresed/refunded by Stripe. 298 | * @param {string} _.customer Id of the Customer (Stripe Customer) making the payment (begins with "cus_") 299 | * @param {string} _.vendor Id of the Vendor (Stripe Custom Account) to receive the payment (begins with "acct_") 300 | * @param {number} _.amount Amount to deduct from Customer 301 | * @param {number} _.vendorAmount Amount payable to the Vendor. Must be less than or equal to `amount`. Ideally, the difference between `amount` & `vendorAmount`, minus the Stripe fees, is what the Marketplace retains as profit. 302 | * @param {string} _.currency Currency code 303 | * @param {string} [_.receiptEmail=null] Email to mail the receipt from Stripe 304 | * @param {string} [_.description] Optional description 305 | * @param {string} [_.statementDescriptor] Text appearing on Bank/Card statements 306 | * @returns {Promise} 307 | */ 308 | async initiatePayment({ 309 | customer, 310 | vendor, 311 | amount, 312 | vendorAmount, 313 | currency, 314 | receiptEmail = null, 315 | description = null, 316 | statementDescriptor = null, 317 | capture = false 318 | }) { 319 | try { 320 | const opts = { 321 | capture, 322 | customer, 323 | amount: amount * 100, // convert to cents from dollar 324 | currency, 325 | description, 326 | destination: { 327 | amount: vendorAmount * 100, 328 | account: vendor, 329 | } 330 | } 331 | if (receiptEmail !== null) opts.receipt_email = receiptEmail 332 | if (statementDescriptor !== null) opts.statement_descriptor = statementDescriptor 333 | return await stripe.charges.create(opts) 334 | } catch (err) { 335 | // istanbul ignore next 336 | throw err 337 | } 338 | }, 339 | 340 | /** 341 | * Capture an existing Payment (Stripe Charge) 342 | * @param {string} transactionId Id of the Stripe Charge to capture (begins with "ch_") 343 | * @param {number} [vendorAmount=null] Optionally update the amount payable to Vendor. If not mentioned, the amount in original charge is used. 344 | * @param {string} [statementDescriptor] Text appearing on Bank/Card statements (overrides the one mentioned in original charge) 345 | * @returns {Promise} 346 | */ 347 | async capturePayment( 348 | transactionId, 349 | vendorAmount = null, 350 | statementDescriptor = null 351 | ) { 352 | try { 353 | const objToCapture = {} 354 | if (statementDescriptor !== null) { 355 | objToCapture.statement_descriptor = statementDescriptor 356 | } 357 | if (vendorAmount !== null) { // Update amount payable to Vendor; otherwise send the initiated amount 358 | objToCapture.destination = { 359 | amount: vendorAmount * 100, // convert to cents from dollar 360 | } 361 | } 362 | return await stripe.charges.capture(transactionId, objToCapture) 363 | } catch (err) { 364 | // istanbul ignore next 365 | throw err 366 | } 367 | }, 368 | 369 | /** 370 | * Refund a previously captured (but unrefunded) Charge 371 | * @param {string} transactionId The Stripe Charge Id to Refund 372 | * @param {string} amount Amount to refund 373 | * @param {string} [reason=null] Reason for refund 374 | * @returns {Promise} 375 | */ 376 | async refund(transactionId, amount, reason = null) { 377 | try { 378 | const refundObj = { 379 | charge: transactionId, 380 | amount: amount * 100 381 | } 382 | if (reason !== null) { 383 | refundObj.reason = reason 384 | } 385 | return await stripe.refunds.create(refundObj) 386 | } catch (err) { 387 | // istanbul ignore next 388 | throw err 389 | } 390 | } 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stripe-connect-functions", 3 | "version": "1.0.0", 4 | "description": "A collection of functions to help with a Customers -> Marketplace -> Vendors Workflow using Stripe Connect", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=10" 8 | }, 9 | "scripts": { 10 | "test:nocoverage": "ava", 11 | "test:withcoverage": "nyc ava", 12 | "test": "npm run test:withcoverage", 13 | "coveralls": "nyc report --reporter=text-lcov | coveralls", 14 | "lint": "npm run lint:quiet", 15 | "lint:quiet": "eslint --quiet './**/*.js'", 16 | "lint:all": "eslint './**/*.js'", 17 | "lint:fix": "eslint './**/*.js' --quiet --fix" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/sayanriju/stripe-connect-functions.git" 22 | }, 23 | "author": "Sayan \"Riju\" Chakrabarti ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/sayanriju/stripe-connect-functions/issues" 27 | }, 28 | "homepage": "https://github.com/sayanriju/stripe-connect-functions#readme", 29 | "keywords": [ 30 | "stripe", 31 | "stripe connect" 32 | ], 33 | "dependencies": { 34 | "stripe": "^8.24.0" 35 | }, 36 | "devDependencies": { 37 | "ava": "^3.5.0", 38 | "coveralls": "^3.0.9", 39 | "eslint": "^5.16.0", 40 | "eslint-config-airbnb": "^17.1.1", 41 | "eslint-plugin-import": "^2.20.1", 42 | "nyc": "^15.0.0", 43 | "proxyquire": "^2.1.3" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/_stripe.stub.js: -------------------------------------------------------------------------------- 1 | module.exports = () => ({ 2 | accounts: { 3 | create({ email }) { 4 | return Promise.resolve({ 5 | id: "acct_1CK10nIacGIwwFOI", 6 | object: "account", 7 | country: "US", 8 | email 9 | }) 10 | }, 11 | 12 | del(stripeAccountId) { 13 | return Promise.resolve({ 14 | deleted: true, 15 | id: stripeAccountId 16 | }) 17 | }, 18 | 19 | update(acctId, { 20 | external_account = {}, 21 | business_profile = {}, 22 | tos_acceptance = {}, 23 | individual = {} 24 | }) { 25 | return Promise.resolve({ 26 | id: acctId, 27 | object: "account", 28 | business_profile: { 29 | mcc: business_profile.mcc || null, 30 | name: null, 31 | product_description: null, 32 | support_address: null, 33 | support_email: null, 34 | support_phone: null, 35 | support_url: null, 36 | url: business_profile.url || null 37 | }, 38 | business_type: "individual", 39 | capabilities: { 40 | transfers: "active" 41 | }, 42 | charges_enabled: true, 43 | country: "US", 44 | created: 1582800920, 45 | default_currency: "usd", 46 | details_submitted: true, 47 | email: "indrajit@logic-square.com", 48 | external_accounts: { 49 | object: "list", 50 | data: [ 51 | { 52 | id: "ba_1GGk1VL1F8SCaTrVwklLM6H5", 53 | object: "bank_account", 54 | account: acctId, 55 | account_holder_name: external_account.account_holder_name || null, 56 | account_holder_type: null, 57 | bank_name: "STRIPE TEST BANK", 58 | country: external_account.country || null, 59 | currency: external_account.currency || null, 60 | default_for_currency: true, 61 | fingerprint: "0cXClcKtgVTOE0iW", 62 | last4: external_account.account_number ? external_account.account_number.substr(-4) : null, 63 | metadata: {}, 64 | routing_number: external_account.routing_number || null, 65 | status: "new" 66 | } 67 | ], 68 | has_more: false, 69 | total_count: 1, 70 | url: "/v1/accounts/acct_1GGk1UL1F8SCaTrV/external_accounts" 71 | }, 72 | individual: { 73 | id: "person_GoMlL8zwfPV0UZ", 74 | object: "person", 75 | account: "acct_1GGk1UL1F8SCaTrV", 76 | address: { 77 | city: individual.address && individual.address.city ? individual.address.city : null, 78 | country: individual.address && individual.address.country ? individual.address.country : null, 79 | line1: individual.address && individual.address.line1 ? individual.address.line1 : null, 80 | line2: individual.address && individual.address.line2 ? individual.address.line2 : null, 81 | postal_code: individual.address && individual.address.postal_code ? individual.address.postal_code : null, 82 | state: individual.address && individual.address.state ? individual.address.state : null 83 | }, 84 | created: 1582800923, 85 | dob: { 86 | day: individual.dob && individual.dob.day ? individual.dob.day : null, 87 | month: individual.dob && individual.dob.month ? individual.dob.month : null, 88 | year: individual.dob && individual.dob.year ? individual.dob.year : null 89 | }, 90 | email: individual.email || null, 91 | first_name: individual.first_name || null, 92 | id_number_provided: !!individual.id_number, 93 | last_name: individual.last_name || null, 94 | metadata: {}, 95 | phone: individual.phone || null, 96 | relationship: { 97 | director: false, 98 | executive: false, 99 | owner: false, 100 | percent_ownership: null, 101 | representative: true, 102 | title: null 103 | }, 104 | requirements: { 105 | currently_due: [], 106 | eventually_due: [], 107 | past_due: [], 108 | pending_verification: [] 109 | }, 110 | ssn_last_4_provided: !!individual.ssn_last_4, 111 | verification: { 112 | additional_document: { 113 | back: null, 114 | details: null, 115 | details_code: null, 116 | front: null 117 | }, 118 | details: null, 119 | details_code: null, 120 | document: { 121 | back: null, 122 | details: null, 123 | details_code: null, 124 | front: null 125 | }, 126 | status: "pending" 127 | } 128 | }, 129 | metadata: {}, 130 | payouts_enabled: true, 131 | requirements: { 132 | current_deadline: null, 133 | currently_due: [], 134 | disabled_reason: null, 135 | eventually_due: [], 136 | past_due: [], 137 | pending_verification: [] 138 | }, 139 | settings: { 140 | branding: { 141 | icon: null, 142 | logo: null, 143 | primary_color: null 144 | }, 145 | card_payments: { 146 | decline_on: { 147 | avs_failure: false, 148 | cvc_failure: false 149 | }, 150 | statement_descriptor_prefix: null 151 | }, 152 | dashboard: { 153 | display_name: "Example", 154 | timezone: "Etc/UTC" 155 | }, 156 | payments: { 157 | statement_descriptor: "WWW.EXAMPLE.COM", 158 | statement_descriptor_kana: null, 159 | statement_descriptor_kanji: null 160 | }, 161 | payouts: { 162 | debit_negative_balances: false, 163 | schedule: { 164 | delay_days: 2, 165 | interval: "daily" 166 | }, 167 | statement_descriptor: null 168 | } 169 | }, 170 | tos_acceptance: { 171 | date: tos_acceptance.date || null, 172 | ip: tos_acceptance.ip || null, 173 | user_agent: tos_acceptance.user_agent || null 174 | }, 175 | type: "custom" 176 | }) 177 | } 178 | }, 179 | 180 | charges: { 181 | create({ 182 | customer, 183 | destination = {}, 184 | amount, // inputs in cents 185 | currency, 186 | receipt_email, 187 | description, 188 | statement_descriptor, 189 | capture 190 | }) { 191 | return Promise.resolve({ 192 | id: "ch_1CLPR9IacGIwwFOIwaEdKU16", 193 | object: "charge", 194 | amount: amount / 100, // outputs in dollars 195 | captured: capture, 196 | currency, 197 | customer, 198 | description, 199 | destination: destination.account, 200 | receipt_email, 201 | statement_descriptor, 202 | status: "succeeded", 203 | }) 204 | }, 205 | 206 | capture(transactionId, { statement_descriptor = null, destination = { } }) { 207 | return Promise.resolve({ 208 | id: transactionId, 209 | object: "charge", 210 | amount: (destination.amount) ? 420 - (destination.amount / 100) : 420, 211 | amount_refunded: 0, 212 | receipt_email: "foo@bar.com", 213 | statement_descriptor 214 | }) 215 | } 216 | }, 217 | 218 | customers: { 219 | create({ email }) { 220 | return Promise.resolve({ 221 | id: "cus_ClfSlfACLDjib2", 222 | object: "customer", 223 | default_source: "card_1CLrcwIacGIwwFOIAfrKTKHu", 224 | email 225 | }) 226 | }, 227 | del(stripeCustomerId) { 228 | return Promise.resolve({ 229 | deleted: true, 230 | id: stripeCustomerId 231 | }) 232 | }, 233 | listSources() { 234 | return Promise.resolve({ 235 | object: "list", 236 | url: "/v1/customers/cus_ClOPUSnueMks9A/sources", 237 | has_more: false, 238 | data: [ 239 | { 240 | id: "card_1CLrcwIacGIwwFOIAfrKTKHu", 241 | object: "card", 242 | brand: "Visa", 243 | country: "US", 244 | customer: "cus_ClfSlfACLDjib2", 245 | cvc_check: null, 246 | dynamic_last4: null, 247 | exp_month: 8, 248 | exp_year: 2019, 249 | fingerprint: "61RsRe9SJC3hjgTW", 250 | funding: "credit", 251 | last4: "4242", 252 | }, 253 | { 254 | id: "card_1CLrcwIacGIwwFOIAfrKTKHv", 255 | object: "card", 256 | brand: "Visa", 257 | country: "US", 258 | customer: "cus_ClfSlfACLDjib2", 259 | cvc_check: null, 260 | dynamic_last4: null, 261 | exp_month: 8, 262 | exp_year: 2019, 263 | fingerprint: "61RsRe9SJC3hjgTY", 264 | funding: "credit", 265 | last4: "4242", 266 | }, 267 | { 268 | id: "card_1CLrcwIacGIwwFOIAfrKTKHw", 269 | object: "card", 270 | brand: "Visa", 271 | country: "US", 272 | customer: "cus_ClfSlfACLDjib2", 273 | cvc_check: null, 274 | dynamic_last4: null, 275 | exp_month: 8, 276 | exp_year: 2019, 277 | fingerprint: "61RsRe9SJC3hjgTZ", 278 | funding: "credit", 279 | last4: "4242", 280 | } 281 | ] 282 | }) 283 | }, 284 | retrieve() { 285 | return Promise.resolve({ 286 | id: "cus_ClfSlfACLDjib2", 287 | object: "customer", 288 | email: "foo@bar.com", 289 | default_source: "card_1CLrcwIacGIwwFOIAfrKTKHu" 290 | }) 291 | }, 292 | createSource() { 293 | return Promise.resolve({ 294 | id: "card_1CLrcwIacGIwwFOIAfrKTKHu", 295 | object: "card", 296 | country: "US", 297 | currency: "usd", 298 | fingerprint: "AhBp6wMc4VW1dL3r", 299 | status: "new", 300 | customer: "cus_ClfSlfACLDjib2" 301 | }) 302 | }, 303 | deleteSource(stripeCustomerId, cardId) { 304 | return Promise.resolve({ 305 | deleted: true, 306 | id: cardId 307 | }) 308 | }, 309 | update() { 310 | return Promise.resolve({ 311 | id: "cus_ClfSlfACLDjib2", 312 | object: "customer", 313 | default_source: "card_1CLrcwIacGIwwFOIAfrKTKHv", 314 | email: "changed@bar.com" 315 | }) 316 | } 317 | }, 318 | 319 | refunds: { 320 | create({ charge, amount, reason = null }) { 321 | return Promise.resolve({ 322 | id: "re_1COhlaIacGIwwFOIPsf6AkGh", 323 | object: "refund", 324 | amount: amount / 100, 325 | currency: "usd", 326 | charge, 327 | reason, 328 | created: 1525593526, 329 | status: "succeeded" 330 | }) 331 | } 332 | } 333 | }) 334 | -------------------------------------------------------------------------------- /test/add-customer-card.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { addCustomerCard } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof addCustomerCard, "function") 11 | }) 12 | 13 | test("Should return a card", async (t) => { 14 | const { 15 | id, object, country, currency, customer, status 16 | } = await addCustomerCard() 17 | t.true(id.startsWith("card")) 18 | t.is(object, "card") 19 | t.is(country, "US") 20 | t.is(currency, "usd") 21 | t.is(status, "new") 22 | t.is(customer, "cus_ClfSlfACLDjib2") 23 | }) 24 | -------------------------------------------------------------------------------- /test/capture-payment.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { capturePayment } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof capturePayment, "function") 11 | }) 12 | 13 | test("Should return the captured charge from stripe", async (t) => { 14 | const { 15 | id, amount, object, statement_descriptor // eslint-disable-line camelcase 16 | } = await capturePayment("ch_foo") 17 | t.is(id, "ch_foo") 18 | t.is(object, "charge") 19 | t.is(statement_descriptor, null) 20 | t.is(amount, 420) 21 | }) 22 | 23 | test("Should return the captured charge from stripe with vendor amount", async (t) => { 24 | const { 25 | id, amount, object, statement_descriptor // eslint-disable-line camelcase 26 | } = await capturePayment("ch_foo", 100) 27 | t.true(id.startsWith("ch_")) 28 | t.is(object, "charge") 29 | t.is(statement_descriptor, null) 30 | t.is(amount, 320) 31 | }) 32 | 33 | test("Should return the captured charge from stripe with statement descriptor", async (t) => { 34 | const { 35 | id, amount, object, statement_descriptor // eslint-disable-line camelcase 36 | } = await capturePayment("ch_foo", 0, "bar") 37 | t.true(id.startsWith("ch_")) 38 | t.is(object, "charge") 39 | t.is(statement_descriptor, "bar") 40 | t.is(amount, 420) 41 | }) 42 | -------------------------------------------------------------------------------- /test/customer-create.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { customerCreate } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof customerCreate, "function") 11 | }) 12 | 13 | test("Should return a customer from stripe without email", async (t) => { 14 | const { id, email, object } = await customerCreate() 15 | t.true(id.startsWith("cus")) 16 | t.is(object, "customer") 17 | t.falsy(email) 18 | }) 19 | 20 | test("Should return a customer from stripe with email", async (t) => { 21 | const { id, email, object } = await customerCreate("foo@bar.com") 22 | t.true(id.startsWith("cus")) 23 | t.is(object, "customer") 24 | t.is(email, "foo@bar.com") 25 | }) 26 | -------------------------------------------------------------------------------- /test/customer-delete.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { customerDelete } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof customerDelete, "function") 11 | }) 12 | 13 | test("Should delete a Customer by id", async (t) => { 14 | const { 15 | id, deleted 16 | } = await customerDelete("cus_1234") 17 | t.is(id, "cus_1234") 18 | t.true(deleted) 19 | }) 20 | -------------------------------------------------------------------------------- /test/delete-customer-card.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { deleteCustomerCard } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof deleteCustomerCard, "function") 11 | }) 12 | 13 | test("Should delete a customer card", async (t) => { 14 | const { 15 | id, deleted 16 | } = await deleteCustomerCard("cus_1234", "card_4567") 17 | t.true(id.startsWith("card_")) 18 | t.is(id, "card_4567") 19 | t.true(deleted) 20 | }) 21 | -------------------------------------------------------------------------------- /test/fetch-customer-cards.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { fetchCustomerCards } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof fetchCustomerCards, "function") 11 | }) 12 | 13 | test("Should fetch Cards associated with a customer, with a default card set", async (t) => { 14 | const cards = await fetchCustomerCards() 15 | t.true(Array.isArray(cards)) 16 | t.true(cards.every(c => c.id.startsWith("card_"))) 17 | t.true(cards.every(c => c.object === "card")) 18 | t.true(cards.every(c => c.customer === "cus_ClfSlfACLDjib2")) 19 | t.true(cards.every(c => c.country === "US")) 20 | t.true(cards.every(c => c.fingerprint)) 21 | t.true(cards.find(c => c.id === "card_1CLrcwIacGIwwFOIAfrKTKHu").isDefault) 22 | }) 23 | -------------------------------------------------------------------------------- /test/get-default-customer-card.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { getDefaultCustomerCard } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof getDefaultCustomerCard, "function") 11 | }) 12 | 13 | test("Should return a customer's default card'", async (t) => { 14 | t.is(await getDefaultCustomerCard("cus_ClfSlfACLDjib2"), "card_1CLrcwIacGIwwFOIAfrKTKHu") 15 | }) 16 | -------------------------------------------------------------------------------- /test/initiate-payment.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { initiatePayment } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof initiatePayment, "function") 11 | }) 12 | 13 | test("Should initiate a payment (charge) to be captured later", async (t) => { 14 | const charge = await initiatePayment({ 15 | customer: "cus_Ckc6NCwnBdzDCb", 16 | vendor: "acct_1CK10nIacGIwwFOI", 17 | amount: 420, 18 | vendorAmount: 100, 19 | currency: "usd" 20 | }) 21 | t.true(charge.id.startsWith("ch_")) 22 | t.is(charge.object, "charge") 23 | t.is(charge.customer, "cus_Ckc6NCwnBdzDCb") 24 | t.is(charge.destination, "acct_1CK10nIacGIwwFOI") 25 | t.is(charge.amount, 420) 26 | t.is(charge.currency, "usd") 27 | t.is(charge.receipt_email, undefined) 28 | t.is(charge.description, null) 29 | t.is(charge.statement_descriptor, undefined) 30 | t.is(charge.status, "succeeded") 31 | t.false(charge.captured) 32 | }) 33 | 34 | test("Should initiate a payment (charge) to be captured immediately", async (t) => { 35 | const charge = await initiatePayment({ 36 | customer: "cus_Ckc6NCwnBdzDCb", 37 | vendor: "acct_1CK10nIacGIwwFOI", 38 | amount: 420, 39 | vendorAmount: 100, 40 | currency: "usd", 41 | capture: true 42 | }) 43 | t.true(charge.id.startsWith("ch_")) 44 | t.is(charge.object, "charge") 45 | t.is(charge.customer, "cus_Ckc6NCwnBdzDCb") 46 | t.is(charge.destination, "acct_1CK10nIacGIwwFOI") 47 | t.is(charge.amount, 420) 48 | t.is(charge.currency, "usd") 49 | t.is(charge.receipt_email, undefined) 50 | t.is(charge.description, null) 51 | t.is(charge.statement_descriptor, undefined) 52 | t.is(charge.status, "succeeded") 53 | t.true(charge.captured) 54 | }) 55 | 56 | test("Should initiate a payment (charge) to be captured later with description, etc set", async (t) => { 57 | const charge = await initiatePayment({ 58 | customer: "cus_Ckc6NCwnBdzDCb", 59 | vendor: "acct_1CK10nIacGIwwFOI", 60 | amount: 420, 61 | vendorAmount: 100, 62 | currency: "usd", 63 | receiptEmail: "receipient@abc.com", 64 | description: "Test", 65 | statementDescriptor: "Test2", 66 | capture: false 67 | }) 68 | t.true(charge.id.startsWith("ch_")) 69 | t.is(charge.object, "charge") 70 | t.is(charge.customer, "cus_Ckc6NCwnBdzDCb") 71 | t.is(charge.destination, "acct_1CK10nIacGIwwFOI") 72 | t.is(charge.amount, 420) 73 | t.is(charge.currency, "usd") 74 | t.is(charge.receipt_email, "receipient@abc.com") 75 | t.is(charge.description, "Test") 76 | t.is(charge.statement_descriptor, "Test2") 77 | t.is(charge.status, "succeeded") 78 | t.false(charge.captured) 79 | }) 80 | -------------------------------------------------------------------------------- /test/refund-create.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { refund } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof refund, "function") 11 | }) 12 | 13 | test("Should return a stripe refund object (no reason)", async (t) => { 14 | const { 15 | id, amount, object, charge, currency, reason, status 16 | } = await refund("ch_1CLPR9IacGIwwFOIwaEdKU16", 100) 17 | t.true(id.startsWith("re_")) 18 | t.is(object, "refund") 19 | t.is(amount, 100) 20 | t.is(currency, "usd") 21 | t.is(charge, "ch_1CLPR9IacGIwwFOIwaEdKU16") 22 | t.is(status, "succeeded") 23 | t.is(reason, null) 24 | }) 25 | 26 | test("Should return a stripe refund object (with reason)", async (t) => { 27 | const { 28 | id, amount, object, charge, currency, reason, status 29 | } = await refund("ch_1CLPR9IacGIwwFOIwaEdKU16", 100, "A Reason") 30 | t.true(id.startsWith("re_")) 31 | t.is(object, "refund") 32 | t.is(amount, 100) 33 | t.is(currency, "usd") 34 | t.is(charge, "ch_1CLPR9IacGIwwFOIwaEdKU16") 35 | t.is(status, "succeeded") 36 | t.is(reason, "A Reason") 37 | }) 38 | -------------------------------------------------------------------------------- /test/set-default-customer-card.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { setDefaultCustomerCard } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof setDefaultCustomerCard, "function") 11 | }) 12 | 13 | test("Should set & return the default card for a customer from stripe", async (t) => { 14 | const default_source = await setDefaultCustomerCard("cus_ClfSlfACLDjib2", "card_1CLrcwIacGIwwFOIAfrKTKHv") 15 | t.is(default_source, "card_1CLrcwIacGIwwFOIAfrKTKHv") 16 | }) 17 | -------------------------------------------------------------------------------- /test/set-vendor-bank-account.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { setVendorBankAccount } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof setVendorBankAccount, "function") 11 | }) 12 | 13 | test("Should update a Vendor's Bank A/c details with A/c holder name", async (t) => { 14 | const { id, external_accounts } = await setVendorBankAccount("acct_1CK10nIacGIwwFOI", { accountNo: "12346789", routingNo: "4567", accountHolderName: "Foo Bar" }) 15 | t.is(id, "acct_1CK10nIacGIwwFOI") 16 | t.is(external_accounts.object, "list") 17 | t.true(Array.isArray(external_accounts.data)) 18 | t.not(external_accounts.data.length, 0) 19 | t.is(external_accounts.data[0].routing_number, "4567") 20 | t.is(external_accounts.data[0].country, "US") 21 | t.is(external_accounts.data[0].currency, "usd") 22 | t.is(external_accounts.data[0].last4, "6789") 23 | t.is(external_accounts.data[0].account_holder_name, "Foo Bar") 24 | }) 25 | test("Should update a Vendor's Bank A/c details w/o A/c holder name", async (t) => { 26 | const { id, external_accounts } = await setVendorBankAccount("acct_1CK10nIacGIwwFOI", { accountNo: "12346789", routingNo: "4567" }) 27 | t.is(id, "acct_1CK10nIacGIwwFOI") 28 | t.is(external_accounts.object, "list") 29 | t.true(Array.isArray(external_accounts.data)) 30 | t.not(external_accounts.data.length, 0) 31 | t.is(external_accounts.data[0].routing_number, "4567") 32 | t.is(external_accounts.data[0].country, "US") 33 | t.is(external_accounts.data[0].currency, "usd") 34 | t.is(external_accounts.data[0].last4, "6789") 35 | }) 36 | -------------------------------------------------------------------------------- /test/vendor-accept-toc.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { vendorAcceptTos } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof vendorAcceptTos, "function") 11 | }) 12 | 13 | test("Should update a Vendors TOC acceptance (with UA)", async (t) => { 14 | const { id, tos_acceptance } = await vendorAcceptTos("acct_1CK10nIacGIwwFOI", { 15 | tosAcceptanceDate: "1525112092", 16 | tosAcceptanceIp: "127.0.0.1", 17 | tosUserAgent: "Mozilla" 18 | }) 19 | t.is(id, "acct_1CK10nIacGIwwFOI") 20 | t.deepEqual(tos_acceptance, { 21 | date: "1525112092", 22 | ip: "127.0.0.1", 23 | user_agent: "Mozilla" 24 | }) 25 | }) 26 | 27 | test("Should update a Vendors TOC acceptance (w/o UA)", async (t) => { 28 | const { id, tos_acceptance } = await vendorAcceptTos("acct_1CK10nIacGIwwFOI", { 29 | tosAcceptanceDate: "1525112092", 30 | tosAcceptanceIp: "127.0.0.1" 31 | }) 32 | t.is(id, "acct_1CK10nIacGIwwFOI") 33 | t.deepEqual(tos_acceptance, { 34 | date: "1525112092", 35 | ip: "127.0.0.1", 36 | user_agent: null 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/vendor-create.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { vendorCreate } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof vendorCreate, "function") 11 | }) 12 | 13 | test("Should return a custom account from stripe without email", async (t) => { 14 | const { 15 | id, email, object, country 16 | } = await vendorCreate() 17 | t.true(id.startsWith("acct")) 18 | t.is(object, "account") 19 | t.is(country, "US") 20 | t.falsy(email) 21 | }) 22 | 23 | test("Should return a custom account from stripe with email", async (t) => { 24 | const { 25 | id, email, object, country 26 | } = await vendorCreate("foo@bar.com") 27 | t.true(id.startsWith("acct")) 28 | t.is(object, "account") 29 | t.is(country, "US") 30 | t.is(email, "foo@bar.com") 31 | }) 32 | -------------------------------------------------------------------------------- /test/vendor-delete.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { vendorDelete } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof vendorDelete, "function") 11 | }) 12 | 13 | test("Should delete a Vendor by id", async (t) => { 14 | const { 15 | id, deleted 16 | } = await vendorDelete("acct_1234") 17 | t.is(id, "acct_1234") 18 | t.true(deleted) 19 | }) 20 | -------------------------------------------------------------------------------- /test/vendor-kyc.test.js: -------------------------------------------------------------------------------- 1 | const test = require("ava") 2 | const proxyquire = require("proxyquire") 3 | const stripe = require("./_stripe.stub") // stubbed 4 | 5 | const { vendorKyc } = proxyquire("../index.js", { 6 | stripe 7 | })("dummy_stripe_token") 8 | 9 | test("Should be a function.", (t) => { 10 | t.is(typeof vendorKyc, "function") 11 | }) 12 | 13 | test("Should update a Vendors KYC (with Personal ID Number and SSN)", async (t) => { 14 | const { id, individual } = await vendorKyc("acct_1CK10nIacGIwwFOI", { personalIdNumber: "abcd1234", ssnLastFour: "1234" }) 15 | t.is(id, "acct_1CK10nIacGIwwFOI") 16 | t.is(individual.ssn_last_4_provided, true) 17 | t.is(individual.id_number_provided, true) 18 | }) 19 | 20 | test("Should update a Vendors KYC (without SSN)", async (t) => { 21 | const { id, individual } = await vendorKyc("acct_1CK10nIacGIwwFOI", { 22 | address: { 23 | city: "Brockton", 24 | line1: "700 Oak Street", 25 | line2: "", 26 | postal_code: "2301", 27 | state: "MA", 28 | country: "US" 29 | }, 30 | dob: { 31 | day: "6", 32 | month: "1", 33 | year: "1975" 34 | }, 35 | name: { 36 | first: "Indrajit", 37 | last: "Roy" 38 | } 39 | }) 40 | t.is(id, "acct_1CK10nIacGIwwFOI") 41 | t.is(individual.first_name, "Indrajit") 42 | t.is(individual.last_name, "Roy") 43 | t.deepEqual(individual.address, { 44 | city: "Brockton", 45 | line1: "700 Oak Street", 46 | line2: null, 47 | postal_code: "2301", 48 | state: "MA", 49 | country: "US" 50 | }) 51 | t.deepEqual(individual.dob, { 52 | day: "6", 53 | month: "1", 54 | year: "1975" 55 | }) 56 | }) 57 | --------------------------------------------------------------------------------