├── .cli.json ├── .env.example ├── .github └── dependabot.yml ├── .gitignore ├── .readme └── demo.png ├── .rspec ├── .vscode └── extensions.json ├── Gemfile ├── LICENSE ├── README.md ├── client ├── css │ ├── global.css │ └── normalize.css ├── favicon.ico ├── index.html └── success.html ├── server ├── README.md ├── dotnet │ ├── Program.cs │ ├── Properties │ │ └── launchSettings.json │ ├── appsettings.Development.json │ ├── appsettings.json │ └── dotnet.csproj ├── go │ ├── README.md │ ├── go.mod │ ├── go.sum │ └── server.go ├── java │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── stripe │ │ └── sample │ │ └── Server.java ├── node │ ├── README.md │ ├── package-lock.json │ ├── package.json │ └── server.js ├── php │ ├── .htaccess │ ├── README.md │ ├── composer.json │ ├── composer.lock │ └── public │ │ ├── css │ │ ├── global.css │ │ └── normalize.css │ │ ├── index.html │ │ ├── onboard-user.php │ │ ├── shared.php │ │ └── success.html ├── python │ ├── Pipfile │ ├── Pipfile.lock │ ├── README.md │ ├── requirements.txt │ └── server.py └── ruby │ ├── Gemfile │ ├── README.md │ └── server.rb └── spec ├── server_spec.rb └── spec_helper.rb /.cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "connect-onboarding-for-standard", 3 | "configureDotEnv": true, 4 | "integrations": [ 5 | { 6 | "name": "main", 7 | "clients": ["web"], 8 | "servers": ["java", "node", "php", "python", "ruby", "go"] 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | # Stripe keys 2 | STRIPE_SECRET_KEY=sk_12345 3 | 4 | # Environment variables 5 | STATIC_DIR=../../client 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | # ruby dependencies 9 | - package-ecosystem: "bundler" 10 | directory: "/server/ruby/" 11 | schedule: 12 | interval: "weekly" 13 | day: "thursday" 14 | 15 | # python dependencies 16 | - package-ecosystem: "pip" 17 | directory: "/server/python/" 18 | schedule: 19 | interval: "weekly" 20 | day: "thursday" 21 | 22 | # php dependencies 23 | - package-ecosystem: "composer" 24 | directory: "/server/php/" 25 | schedule: 26 | interval: "weekly" 27 | day: "thursday" 28 | 29 | # node dependencies 30 | - package-ecosystem: "npm" 31 | directory: "/server/node/" 32 | schedule: 33 | interval: "weekly" 34 | day: "thursday" 35 | 36 | # go dependencies 37 | - package-ecosystem: "gomod" 38 | directory: "/server/go/" 39 | schedule: 40 | interval: "weekly" 41 | 42 | # java dependencies 43 | - package-ecosystem: "maven" 44 | directory: "/server/java/" 45 | schedule: 46 | interval: "weekly" 47 | day: "thursday" 48 | 49 | # dotnet dependencies 50 | - package-ecosystem: "nuget" 51 | directory: "/server/dotnet/" 52 | schedule: 53 | interval: "weekly" 54 | day: "thursday" 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | .DS_Store 3 | .vscode/* 4 | !.vscode/extensions.json 5 | 6 | # Node files 7 | node_modules/ 8 | 9 | # Ruby files 10 | Gemfile.lock 11 | 12 | # Python files 13 | __pycache__ 14 | venv 15 | 16 | # PHP files 17 | vendor 18 | logs 19 | 20 | # Java files 21 | .settings 22 | target/ 23 | .classpath 24 | .factorypath 25 | .project 26 | 27 | # Typescript 28 | dist 29 | 30 | **/virtual 31 | 32 | # Dotnet 33 | bin/ 34 | obj/ 35 | -------------------------------------------------------------------------------- /.readme/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stripe-samples/connect-onboarding-for-standard/27f114fa5ed87ba401124427dfb67856ebf66365/.readme/demo.png -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["stripe.vscode-stripe"] 3 | } 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # NOTE: This file manages dependencies used for automated testing of the 4 | # sample, feel free to ignore this and the /spec directory. 5 | 6 | source "https://rubygems.org" 7 | 8 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 9 | 10 | gem 'rspec' 11 | gem 'rest-client' 12 | gem 'byebug' 13 | gem 'stripe' 14 | gem 'dotenv' 15 | 16 | gem 'selenium-webdriver' 17 | gem 'capybara' 18 | gem 'capybara-screenshot' 19 | 20 | gem 'rspec-github', require: false 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019- Stripe, Inc. (https://stripe.com) 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 | # Connect Onboarding for Standard accounts 2 | 3 | This Stripe sample shows you how to use Connect Onboarding for Standard for 4 | seamless user on-boarding with [Stripe Connect](https://stripe.com/connect). 5 | 6 | ![demo](.readme/demo.png) 7 | 8 | ## How to run locally 9 | 10 | This sample includes 6 server implementations in Node, Ruby, Python, Java, PHP, and Go. 11 | 12 | Follow the steps below to run locally. 13 | 14 | **1. Clone and configure the sample** 15 | 16 | The Stripe CLI is the fastest way to clone and configure a sample to run locally. 17 | 18 | **Using the Stripe CLI** 19 | 20 | If you haven't already installed the CLI, follow the [installation 21 | steps](https://github.com/stripe/stripe-cli#installation) in the project 22 | README. The CLI is useful for cloning samples and locally testing webhooks and 23 | Stripe integrations. 24 | 25 | In your terminal shell, run the Stripe CLI command to clone the sample: 26 | 27 | ```sh 28 | stripe samples create connect-onboarding-for-standard 29 | ``` 30 | 31 | The CLI will walk you through picking your integration type, server and client 32 | languages, and configuring your .env config file with your Stripe API keys. 33 | 34 | **Installing and cloning manually** 35 | 36 | If you do not want to use the Stripe CLI, you can manually clone and configure the sample yourself: 37 | 38 | ``` 39 | git clone https://github.com/stripe-samples/connect-onboarding-for-standard 40 | ``` 41 | 42 | Copy the .env.example file into a file named .env in the folder of the server you want to use. For example: 43 | 44 | ``` 45 | cp .env.example server/node/.env 46 | ``` 47 | 48 | You will need a Stripe account in order to run the demo. Once you set up your account, go to the Stripe [developer dashboard](https://stripe.com/docs/development/quickstart#api-keys) to find your API keys. 49 | 50 | ``` 51 | STRIPE_PUBLISHABLE_KEY= 52 | STRIPE_SECRET_KEY= 53 | ``` 54 | 55 | `STATIC_DIR` tells the server where to the client files are located and does not need to be modified unless you move the server files. 56 | 57 | **2. Follow the server instructions on how to run:** 58 | 59 | Pick the server language you want and follow the instructions in the server folder README on how to run. 60 | 61 | For example, if you want to run the Node server: 62 | 63 | ``` 64 | cd server/node # there's a README in this folder with instructions 65 | npm install 66 | npm start 67 | ``` 68 | 69 | ## FAQ 70 | 71 | Q: Why did you pick these frameworks? 72 | 73 | A: We chose the most minimal framework to convey the key Stripe calls and concepts you need to understand. These demos are meant as an educational tool that helps you roadmap how to integrate Stripe within your own system independent of the framework. 74 | 75 | ## Get support 76 | 77 | If you found a bug or want to suggest a new [feature/use case/sample], please [file an issue](../../issues). 78 | 79 | If you have questions, comments, or need help with code, we're here to help: 80 | 81 | - on [Discord](https://stripe.com/go/developer-chat) 82 | - on Twitter at [@StripeDev](https://twitter.com/StripeDev) 83 | - on Stack Overflow at the [stripe-payments](https://stackoverflow.com/tags/stripe-payments/info) tag 84 | - by [email](mailto:support+github@stripe.com) 85 | 86 | Sign up to [stay updated with developer news](https://go.stripe.global/dev-digest). 87 | 88 | ## Author(s) 89 | 90 | [@auchenberg-stripe](https://twitter.com/auchenberg) 91 | -------------------------------------------------------------------------------- /client/css/global.css: -------------------------------------------------------------------------------- 1 | /* Variables */ 2 | :root { 3 | --gray-offset: rgba(0, 0, 0, 0.03); 4 | --gray-border: rgba(0, 0, 0, 0.15); 5 | --gray-light: rgba(0, 0, 0, 0.4); 6 | --gray-mid: rgba(0, 0, 0, 0.7); 7 | --gray-dark: rgba(0, 0, 0, 0.9); 8 | --body-color: var(--gray-mid); 9 | --headline-color: var(--gray-dark); 10 | --accent-color: #0066f0; 11 | --body-font-family: -apple-system, BlinkMacSystemFont, sans-serif; 12 | --radius: 6px; 13 | --form-width: 343px; 14 | } 15 | 16 | /* Base */ 17 | * { 18 | box-sizing: border-box; 19 | } 20 | body { 21 | font-family: var(--body-font-family); 22 | font-size: 16px; 23 | color: var(--body-color); 24 | -webkit-font-smoothing: antialiased; 25 | } 26 | h1, 27 | h2, 28 | h3, 29 | h4, 30 | h5, 31 | h6 { 32 | color: var(--body-color); 33 | margin-top: 2px; 34 | margin-bottom: 4px; 35 | } 36 | h1 { 37 | font-size: 27px; 38 | color: var(--headline-color); 39 | } 40 | h4 { 41 | font-weight: 500; 42 | font-size: 14px; 43 | color: var(--gray-light); 44 | } 45 | 46 | /* Layout */ 47 | .sr-root { 48 | display: flex; 49 | flex-direction: row; 50 | width: 100%; 51 | max-width: 980px; 52 | padding: 48px; 53 | align-content: center; 54 | justify-content: center; 55 | height: auto; 56 | min-height: 100vh; 57 | margin: 0 auto; 58 | } 59 | .sr-header { 60 | margin-bottom: 32px; 61 | } 62 | .sr-payment-summary { 63 | margin-bottom: 20px; 64 | } 65 | .sr-main, 66 | .sr-content { 67 | display: flex; 68 | flex-direction: column; 69 | justify-content: center; 70 | height: 100%; 71 | align-self: center; 72 | } 73 | .sr-main { 74 | width: var(--form-width); 75 | } 76 | .sr-content { 77 | padding-left: 48px; 78 | } 79 | .sr-header__logo { 80 | background-image: var(--logo-image); 81 | height: 24px; 82 | background-size: contain; 83 | background-repeat: no-repeat; 84 | width: 100%; 85 | } 86 | .sr-legal-text { 87 | color: var(--gray-light); 88 | text-align: center; 89 | font-size: 13px; 90 | line-height: 17px; 91 | margin-top: 12px; 92 | } 93 | .sr-field-error { 94 | color: var(--accent-color); 95 | text-align: left; 96 | font-size: 13px; 97 | line-height: 17px; 98 | margin-top: 12px; 99 | } 100 | 101 | /* Form */ 102 | .sr-form-row { 103 | margin: 16px 0; 104 | } 105 | label { 106 | font-size: 13px; 107 | font-weight: 500; 108 | margin-bottom: 8px; 109 | display: inline-block; 110 | } 111 | 112 | /* Inputs */ 113 | .sr-input, 114 | .sr-select, 115 | input[type="text"] { 116 | border: 1px solid var(--gray-border); 117 | border-radius: var(--radius); 118 | padding: 5px 12px; 119 | height: 44px; 120 | width: 100%; 121 | transition: box-shadow 0.2s ease; 122 | background: white; 123 | -moz-appearance: none; 124 | -webkit-appearance: none; 125 | appearance: none; 126 | color: #32325d; 127 | } 128 | .sr-input:focus, 129 | input[type="text"]:focus, 130 | button:focus, 131 | .focused { 132 | box-shadow: 0 0 0 1px rgba(50, 151, 211, 0.3), 0 1px 1px 0 rgba(0, 0, 0, 0.07), 133 | 0 0 0 4px rgba(50, 151, 211, 0.3); 134 | outline: none; 135 | z-index: 9; 136 | } 137 | .sr-input::placeholder, 138 | input[type="text"]::placeholder { 139 | color: var(--gray-light); 140 | } 141 | 142 | /* Checkbox */ 143 | .sr-checkbox-label { 144 | position: relative; 145 | cursor: pointer; 146 | } 147 | 148 | .sr-checkbox-label input { 149 | opacity: 0; 150 | margin-right: 6px; 151 | } 152 | 153 | .sr-checkbox-label .sr-checkbox-check { 154 | position: absolute; 155 | left: 0; 156 | height: 16px; 157 | width: 16px; 158 | background-color: white; 159 | border: 1px solid var(--gray-border); 160 | border-radius: 4px; 161 | transition: all 0.2s ease; 162 | } 163 | 164 | .sr-checkbox-label input:focus ~ .sr-checkbox-check { 165 | box-shadow: 0 0 0 1px rgba(50, 151, 211, 0.3), 0 1px 1px 0 rgba(0, 0, 0, 0.07), 166 | 0 0 0 4px rgba(50, 151, 211, 0.3); 167 | outline: none; 168 | } 169 | 170 | .sr-checkbox-label input:checked ~ .sr-checkbox-check { 171 | background-color: var(--accent-color); 172 | background-repeat: no-repeat; 173 | background-size: 16px; 174 | background-position: -1px -1px; 175 | } 176 | 177 | /* Select */ 178 | .sr-select { 179 | display: block; 180 | height: 44px; 181 | margin: 0; 182 | background-repeat: no-repeat, repeat; 183 | background-position: right 12px top 50%, 0 0; 184 | background-size: 0.65em auto, 100%; 185 | } 186 | .sr-select:after { 187 | } 188 | .sr-select::-ms-expand { 189 | display: none; 190 | } 191 | .sr-select:hover { 192 | cursor: pointer; 193 | } 194 | .sr-select:focus { 195 | box-shadow: 0 0 0 1px rgba(50, 151, 211, 0.3), 0 1px 1px 0 rgba(0, 0, 0, 0.07), 196 | 0 0 0 4px rgba(50, 151, 211, 0.3); 197 | outline: none; 198 | } 199 | .sr-select option { 200 | font-weight: 400; 201 | } 202 | .sr-select:invalid { 203 | color: var(--gray-light); 204 | background-opacity: 0.4; 205 | } 206 | 207 | /* Combo inputs */ 208 | .sr-combo-inputs { 209 | display: flex; 210 | flex-direction: column; 211 | } 212 | .sr-combo-inputs input, 213 | .sr-combo-inputs .sr-select { 214 | border-radius: 0; 215 | border-bottom: 0; 216 | } 217 | .sr-combo-inputs > input:first-child, 218 | .sr-combo-inputs > .sr-select:first-child { 219 | border-radius: var(--radius) var(--radius) 0 0; 220 | } 221 | .sr-combo-inputs > input:last-child, 222 | .sr-combo-inputs > .sr-select:last-child { 223 | border-radius: 0 0 var(--radius) var(--radius); 224 | border-bottom: 1px solid var(--gray-border); 225 | } 226 | .sr-combo-inputs > .sr-combo-inputs-row:last-child input:first-child { 227 | border-radius: 0 0 0 var(--radius); 228 | border-bottom: 1px solid var(--gray-border); 229 | } 230 | .sr-combo-inputs > .sr-combo-inputs-row:last-child input:last-child { 231 | border-radius: 0 0 var(--radius) 0; 232 | border-bottom: 1px solid var(--gray-border); 233 | } 234 | .sr-combo-inputs > .sr-combo-inputs-row:first-child input:first-child { 235 | border-radius: var(--radius) 0 0 0; 236 | } 237 | .sr-combo-inputs > .sr-combo-inputs-row:first-child input:last-child { 238 | border-radius: 0 var(--radius) 0 0; 239 | } 240 | .sr-combo-inputs > .sr-combo-inputs-row:first-child input:only-child { 241 | border-radius: var(--radius) var(--radius) 0 0; 242 | } 243 | .sr-combo-inputs-row { 244 | width: 100%; 245 | display: flex; 246 | } 247 | 248 | .sr-combo-inputs-row > input { 249 | width: 100%; 250 | border-radius: 0; 251 | } 252 | 253 | .sr-combo-inputs-row > input:first-child:not(:only-child) { 254 | border-right: 0; 255 | } 256 | 257 | .sr-combo-inputs-row:not(:first-of-type) .sr-input { 258 | border-radius: 0 0 var(--radius) var(--radius); 259 | } 260 | 261 | /* Buttons and links */ 262 | button { 263 | background: var(--accent-color); 264 | border-radius: var(--radius); 265 | color: white; 266 | border: 0; 267 | padding: 12px 16px; 268 | margin-top: 16px; 269 | font-weight: 600; 270 | cursor: pointer; 271 | transition: all 0.2s ease; 272 | display: block; 273 | } 274 | button:hover { 275 | filter: contrast(115%); 276 | } 277 | button:active { 278 | transform: translateY(0px) scale(0.98); 279 | filter: brightness(0.9); 280 | } 281 | button:disabled { 282 | opacity: 0.5; 283 | cursor: none; 284 | } 285 | 286 | .sr-payment-form button, 287 | .fullwidth { 288 | width: 100%; 289 | } 290 | 291 | a { 292 | color: var(--accent-color); 293 | text-decoration: none; 294 | transition: all 0.2s ease; 295 | } 296 | 297 | a:hover { 298 | filter: brightness(0.8); 299 | } 300 | 301 | a:active { 302 | filter: brightness(0.5); 303 | } 304 | 305 | /* Code block */ 306 | .sr-callout { 307 | background: var(--gray-offset); 308 | padding: 12px; 309 | border-radius: var(--radius); 310 | max-height: 200px; 311 | overflow: auto; 312 | } 313 | code, 314 | pre { 315 | font-family: "SF Mono", "IBM Plex Mono", "Menlo", monospace; 316 | font-size: 12px; 317 | } 318 | 319 | /* Stripe Element placeholder */ 320 | .sr-card-element { 321 | padding-top: 12px; 322 | } 323 | 324 | /* Responsiveness */ 325 | @media (max-width: 720px) { 326 | .sr-root { 327 | flex-direction: column; 328 | justify-content: flex-start; 329 | padding: 48px 20px; 330 | min-width: 320px; 331 | } 332 | 333 | .sr-header__logo { 334 | background-position: center; 335 | } 336 | 337 | .sr-payment-summary { 338 | text-align: center; 339 | } 340 | 341 | .sr-content { 342 | display: none; 343 | } 344 | 345 | .sr-main { 346 | width: 100%; 347 | } 348 | } 349 | 350 | /* todo: spinner/processing state, errors, animations */ 351 | 352 | .spinner, 353 | .spinner:before, 354 | .spinner:after { 355 | border-radius: 50%; 356 | } 357 | .spinner { 358 | color: #ffffff; 359 | font-size: 22px; 360 | text-indent: -99999px; 361 | margin: 0px auto; 362 | position: relative; 363 | width: 20px; 364 | height: 20px; 365 | box-shadow: inset 0 0 0 2px; 366 | -webkit-transform: translateZ(0); 367 | -ms-transform: translateZ(0); 368 | transform: translateZ(0); 369 | } 370 | .spinner:before, 371 | .spinner:after { 372 | position: absolute; 373 | content: ""; 374 | } 375 | .spinner:before { 376 | width: 10.4px; 377 | height: 20.4px; 378 | background: var(--accent-color); 379 | border-radius: 20.4px 0 0 20.4px; 380 | top: -0.2px; 381 | left: -0.2px; 382 | -webkit-transform-origin: 10.4px 10.2px; 383 | transform-origin: 10.4px 10.2px; 384 | -webkit-animation: loading 2s infinite ease 1.5s; 385 | animation: loading 2s infinite ease 1.5s; 386 | } 387 | .spinner:after { 388 | width: 10.4px; 389 | height: 10.2px; 390 | background: var(--accent-color); 391 | border-radius: 0 10.2px 10.2px 0; 392 | top: -0.1px; 393 | left: 10.2px; 394 | -webkit-transform-origin: 0px 10.2px; 395 | transform-origin: 0px 10.2px; 396 | -webkit-animation: loading 2s infinite ease; 397 | animation: loading 2s infinite ease; 398 | } 399 | @-webkit-keyframes loading { 400 | 0% { 401 | -webkit-transform: rotate(0deg); 402 | transform: rotate(0deg); 403 | } 404 | 100% { 405 | -webkit-transform: rotate(360deg); 406 | transform: rotate(360deg); 407 | } 408 | } 409 | @keyframes loading { 410 | 0% { 411 | -webkit-transform: rotate(0deg); 412 | transform: rotate(0deg); 413 | } 414 | 100% { 415 | -webkit-transform: rotate(360deg); 416 | transform: rotate(360deg); 417 | } 418 | } 419 | 420 | /* Animated form */ 421 | 422 | .sr-root { 423 | animation: 0.4s form-in; 424 | animation-fill-mode: both; 425 | animation-timing-function: ease; 426 | } 427 | 428 | .sr-payment-form .sr-form-row { 429 | animation: 0.4s field-in; 430 | animation-fill-mode: both; 431 | animation-timing-function: ease; 432 | transform-origin: 50% 0%; 433 | } 434 | 435 | /* need saas for loop :D */ 436 | .sr-payment-form .sr-form-row:nth-child(1) { 437 | animation-delay: 0; 438 | } 439 | .sr-payment-form .sr-form-row:nth-child(2) { 440 | animation-delay: 60ms; 441 | } 442 | .sr-payment-form .sr-form-row:nth-child(3) { 443 | animation-delay: 120ms; 444 | } 445 | .sr-payment-form .sr-form-row:nth-child(4) { 446 | animation-delay: 180ms; 447 | } 448 | .sr-payment-form .sr-form-row:nth-child(5) { 449 | animation-delay: 240ms; 450 | } 451 | .sr-payment-form .sr-form-row:nth-child(6) { 452 | animation-delay: 300ms; 453 | } 454 | .hidden { 455 | display: none; 456 | } 457 | 458 | @keyframes field-in { 459 | 0% { 460 | opacity: 0; 461 | transform: translateY(8px) scale(0.95); 462 | } 463 | 100% { 464 | opacity: 1; 465 | transform: translateY(0px) scale(1); 466 | } 467 | } 468 | 469 | @keyframes form-in { 470 | 0% { 471 | opacity: 0; 472 | transform: scale(0.98); 473 | } 474 | 100% { 475 | opacity: 1; 476 | transform: scale(1); 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /client/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } -------------------------------------------------------------------------------- /client/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stripe-samples/connect-onboarding-for-standard/27f114fa5ed87ba401124427dfb67856ebf66365/client/favicon.ico -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stripe Sample Connect Onboarding for Standard accounts 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

Setup payouts to list your home on Kavholm

18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /client/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stripe Sample Connect Onboarding for Standard accounts 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

The user returned to the app

18 |
19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /server/README.md: -------------------------------------------------------------------------------- 1 | # Running the server 2 | 3 | We included several RESTful server that each implement the same endpoints and logic. 4 | Pick the language you are most comfortable in and follow the instructions in the directory on how to run. 5 | 6 | # Supported languages 7 | 8 | * [JavaScript (Node)](node/README.md) 9 | * [Python (Flask)](python/README.md) 10 | * [Ruby (Sinatra)](ruby/README.md) 11 | * [PHP (Slim)](php/README.md) 12 | * [Java (Spark)](java/README.md) 13 | * [Golang](go/README.md) 14 | -------------------------------------------------------------------------------- /server/dotnet/Program.cs: -------------------------------------------------------------------------------- 1 | using Stripe; 2 | using Microsoft.Extensions.FileProviders; 3 | 4 | var builder = WebApplication.CreateBuilder(args); 5 | 6 | // This is to keep track of the Account's ID in the session. 7 | builder.Services.AddDistributedMemoryCache(); 8 | builder.Services.AddSession(options => 9 | { 10 | options.IdleTimeout = TimeSpan.FromSeconds(10); 11 | options.Cookie.HttpOnly = true; 12 | options.Cookie.IsEssential = true; 13 | }); 14 | 15 | var app = builder.Build(); 16 | app.UseSession(); 17 | 18 | app.UseStaticFiles(new StaticFileOptions 19 | { 20 | FileProvider = new PhysicalFileProvider( 21 | Path.Combine(builder.Environment.ContentRootPath, "../../client")), 22 | RequestPath = "" 23 | }); 24 | 25 | DotNetEnv.Env.Load(); 26 | StripeConfiguration.ApiKey = Environment.GetEnvironmentVariable("STRIPE_SECRET_KEY"); 27 | 28 | app.MapGet("/", () => 29 | { 30 | return Results.Redirect("/index.html"); 31 | }); 32 | 33 | app.MapPost("/onboard-user", (HttpContext context) => 34 | { 35 | // Create a new standard connected account. 36 | var accountOptions = new AccountCreateOptions 37 | { 38 | Type = "standard", 39 | }; 40 | var accountService = new AccountService(); 41 | var account = accountService.Create(accountOptions); 42 | context.Session.SetString("account_id", account.Id); 43 | 44 | // Create a new account link to onboard the new account. 45 | var options = new AccountLinkCreateOptions 46 | { 47 | Account = account.Id, 48 | RefreshUrl = "http://localhost:4242/onboard-user/refresh", 49 | ReturnUrl = "http://localhost:4242/success.html", 50 | Type = "account_onboarding", 51 | }; 52 | var service = new AccountLinkService(); 53 | var accountLink = service.Create(options); 54 | return Results.Redirect(accountLink.Url); 55 | }); 56 | 57 | app.MapGet("/onboard-user/refresh", (HttpContext context) => 58 | { 59 | // Create a new standard connected account. 60 | var accountId = context.Session.GetString("account_id"); 61 | 62 | // Create a new account link to onboard the new account. 63 | var options = new AccountLinkCreateOptions 64 | { 65 | Account = accountId, 66 | RefreshUrl = "http://localhost:4242/onboard-user/refresh", 67 | ReturnUrl = "http://localhost:4242/success.html", 68 | Type = "account_onboarding", 69 | }; 70 | var service = new AccountLinkService(); 71 | var accountLink = service.Create(options); 72 | return Results.Redirect(accountLink.Url); 73 | }); 74 | 75 | app.Run("http://localhost:4242"); 76 | -------------------------------------------------------------------------------- /server/dotnet/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:55502", 7 | "sslPort": 44341 8 | } 9 | }, 10 | "profiles": { 11 | "dotnet": { 12 | "commandName": "Project", 13 | "dotnetRunMessages": true, 14 | "launchBrowser": true, 15 | "applicationUrl": "https://localhost:7118;http://localhost:5270", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "IIS Express": { 21 | "commandName": "IISExpress", 22 | "launchBrowser": true, 23 | "environmentVariables": { 24 | "ASPNETCORE_ENVIRONMENT": "Development" 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/dotnet/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /server/dotnet/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /server/dotnet/dotnet.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /server/go/README.md: -------------------------------------------------------------------------------- 1 | # Connect onboarding for Standard accounts 2 | 3 | A [Go server](https://go.dev/) implementation 4 | 5 | ## Requirements 6 | 7 | - Go 1.18.2+ 8 | - [Configured .env file](../README.md) 9 | 10 | ## How to run 11 | 12 | 1. Run the application 13 | 14 | ``` 15 | go run server.go 16 | ``` 17 | 18 | 2. Go to `localhost:4242` to see the demo 19 | -------------------------------------------------------------------------------- /server/go/go.mod: -------------------------------------------------------------------------------- 1 | module stripe-checkout 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/gorilla/sessions v1.2.1 // indirect 7 | github.com/joho/godotenv v1.4.0 // indirect 8 | github.com/stripe/stripe-go/v72 v72.107.0 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /server/go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= 3 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 4 | github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= 5 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 6 | github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= 7 | github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 8 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 9 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 10 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 11 | github.com/stripe/stripe-go/v72 v72.107.0 h1:munfcQGG/STMSzdu55Q12vYCCNwWC5sZcpKsnCNa1FA= 12 | github.com/stripe/stripe-go/v72 v72.107.0/go.mod h1:QwqJQtduHubZht9mek5sds9CtQcKFdsykV9ZepRWwo0= 13 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 14 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 15 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 16 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 17 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 18 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 19 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 20 | -------------------------------------------------------------------------------- /server/go/server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "log" 5 | "net/http" 6 | "os" 7 | 8 | "github.com/gorilla/sessions" 9 | "github.com/joho/godotenv" 10 | "github.com/stripe/stripe-go/v72" 11 | "github.com/stripe/stripe-go/v72/account" 12 | "github.com/stripe/stripe-go/v72/accountlink" 13 | ) 14 | 15 | // Set this to a random string that is kept secure 16 | var store = sessions.NewCookieStore([]byte("secret-session")) 17 | 18 | func main() { 19 | err := godotenv.Load() 20 | if err != nil { 21 | log.Fatal("Error loading .env file") 22 | } 23 | 24 | stripe.Key = os.Getenv("STRIPE_SECRET_KEY") 25 | 26 | // For sample support and debugging, not required for production: 27 | stripe.SetAppInfo(&stripe.AppInfo{ 28 | Name: "stripe-samples/connect-onboarding-for-standard", 29 | Version: "0.0.1", 30 | URL: "https://github.com/stripe-samples", 31 | }) 32 | 33 | http.Handle("/", http.FileServer(http.Dir(os.Getenv("STATIC_DIR")))) 34 | http.HandleFunc("/onboard-user", handleOnboardUser) 35 | http.HandleFunc("/onboard-user/refresh", handleOnboardUserRefresh) 36 | 37 | log.Println("server running at 0.0.0.0:4242") 38 | http.ListenAndServe("0.0.0.0:4242", nil) 39 | } 40 | 41 | func handleOnboardUser(w http.ResponseWriter, r *http.Request) { 42 | // Create account 43 | accountParams := &stripe.AccountParams{ 44 | Type: stripe.String(string(stripe.AccountTypeStandard)), 45 | } 46 | accountDetails, _ := account.New(accountParams) 47 | accountID := accountDetails.ID 48 | 49 | // Store the accountID in the session 50 | session, _ := store.Get(r, "account-link-session") 51 | session.Values["accountID"] = accountID 52 | err := session.Save(r, w) 53 | 54 | if err != nil { 55 | http.Error(w, err.Error(), http.StatusInternalServerError) 56 | return 57 | } 58 | 59 | origin := r.Header.Get("Origin") 60 | refreshURL := origin + "/onboard-user/refresh" 61 | returnURL := origin + "/success.html" 62 | 63 | // Create account link 64 | accountLinkParams := &stripe.AccountLinkParams{ 65 | Account: stripe.String(string(accountID)), 66 | RefreshURL: stripe.String(refreshURL), 67 | ReturnURL: stripe.String(returnURL), 68 | Type: stripe.String("account_onboarding"), 69 | } 70 | result, err := accountlink.New(accountLinkParams) 71 | 72 | if err != nil { 73 | http.Error(w, err.Error(), http.StatusInternalServerError) 74 | return 75 | } 76 | 77 | http.Redirect(w, r, result.URL, 303) 78 | return 79 | } 80 | 81 | func handleOnboardUserRefresh(w http.ResponseWriter, r *http.Request) { 82 | session, _ := store.Get(r, "account-link-session") 83 | var origin string 84 | 85 | if r.TLS == nil { 86 | origin = "http://" + r.Host 87 | } else { 88 | origin = "https://" + r.Host 89 | } 90 | 91 | refreshURL := origin + "/onboard-user/refresh" 92 | returnURL := origin + "/success.html" 93 | 94 | if session.Values["accountID"] != nil { 95 | accountLinkParams := &stripe.AccountLinkParams{ 96 | Account: stripe.String(session.Values["accountID"].(string)), 97 | RefreshURL: stripe.String(refreshURL), 98 | ReturnURL: stripe.String(returnURL), 99 | Type: stripe.String("account_onboarding"), 100 | } 101 | result, err := accountlink.New(accountLinkParams) 102 | 103 | if err != nil { 104 | http.Error(w, err.Error(), http.StatusInternalServerError) 105 | return 106 | } 107 | 108 | http.Redirect(w, r, result.URL, 303) 109 | return 110 | } 111 | 112 | http.Redirect(w, r, "/", 303) 113 | return 114 | } -------------------------------------------------------------------------------- /server/java/README.md: -------------------------------------------------------------------------------- 1 | # Connect onboarding for Standard accounts 2 | 3 | ## Requirements 4 | 5 | - Maven 6 | - Java 7 | 8 | 1. Build the jar 9 | 10 | ```sh 11 | mvn package 12 | ``` 13 | 14 | 2. Run the packaged jar 15 | 16 | ```sh 17 | java -cp target/sample-jar-with-dependencies.jar com.stripe.sample.Server 18 | ``` 19 | 20 | 3. Go to `localhost:4242` in your browser to see the demo 21 | -------------------------------------------------------------------------------- /server/java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | com.stripe.sample 7 | connect-onboarding-for-standard 8 | 1.0.0-SNAPSHOT 9 | 10 | 11 | 12 | 13 | org.slf4j 14 | slf4j-simple 15 | 2.0.3 16 | 17 | 18 | com.sparkjava 19 | spark-core 20 | 2.9.4 21 | 22 | 23 | com.stripe 24 | stripe-java 25 | 21.12.0 26 | 27 | 28 | io.github.cdimascio 29 | java-dotenv 30 | 5.2.2 31 | 32 | 33 | 34 | sample 35 | 36 | 37 | org.apache.maven.plugins 38 | maven-compiler-plugin 39 | 3.10.1 40 | 41 | 1.8 42 | 1.8 43 | 44 | 45 | 46 | maven-assembly-plugin 47 | 48 | 49 | package 50 | 51 | single 52 | 53 | 54 | 55 | 56 | 57 | 58 | jar-with-dependencies 59 | 60 | 61 | 62 | Server 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /server/java/src/main/java/com/stripe/sample/Server.java: -------------------------------------------------------------------------------- 1 | package com.stripe.sample; 2 | 3 | import java.nio.file.Paths; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | import static spark.Spark.get; 9 | import static spark.Spark.post; 10 | import static spark.Spark.port; 11 | import static spark.Spark.staticFiles; 12 | 13 | import com.stripe.Stripe; 14 | import com.stripe.exception.*; 15 | 16 | import io.github.cdimascio.dotenv.Dotenv; 17 | 18 | import com.stripe.param.AccountCreateParams; 19 | import com.stripe.param.AccountLinkCreateParams; 20 | import com.stripe.model.AccountLink; 21 | import com.stripe.model.Account; 22 | 23 | public class Server { 24 | public static void main(String[] args) { 25 | port(4242); 26 | Dotenv dotenv = Dotenv.load(); 27 | Stripe.apiKey = dotenv.get("STRIPE_SECRET_KEY"); 28 | // For sample support and debugging, not required for production: 29 | Stripe.setAppInfo( 30 | "stripe-samples/connect-onboarding-for-standard", 31 | "0.0.1", 32 | "https://github.com/stripe-samples" 33 | ); 34 | staticFiles.externalLocation( 35 | Paths.get( 36 | Paths.get("").toAbsolutePath().toString(), 37 | dotenv.get("STATIC_DIR") 38 | ).normalize().toString()); 39 | 40 | post("/onboard-user", (request, response) -> { 41 | response.type("application/json"); 42 | 43 | AccountCreateParams createAccountParams = new AccountCreateParams 44 | .Builder() 45 | .setType(AccountCreateParams.Type.STANDARD) 46 | .build(); 47 | Account account = Account.create(createAccountParams); 48 | 49 | request.session().attribute("account_id", account.getId()); 50 | 51 | String origin = request.headers("origin"); 52 | String accountID = account.getId(); 53 | 54 | AccountLinkCreateParams createAccountLinkParams = new AccountLinkCreateParams 55 | .Builder() 56 | .setAccount(accountID) 57 | .setType(AccountLinkCreateParams.Type.ACCOUNT_ONBOARDING) 58 | .setRefreshUrl(String.format("%s/onboard-user/refresh", origin)) 59 | .setReturnUrl(String.format("%s/success.html", origin)) 60 | .build(); 61 | 62 | AccountLink accountLink = AccountLink.create(createAccountLinkParams); 63 | 64 | response.redirect(accountLink.getUrl(), 303); 65 | return ""; 66 | }); 67 | 68 | get("/onboard-user/refresh", (request, response) -> { 69 | String sessionAccountId = request.session().attribute("account_id"); 70 | 71 | if (sessionAccountId == null) { 72 | response.redirect("/"); 73 | return ""; 74 | } 75 | 76 | String origin = String.format("http://%s", request.headers("host")); 77 | 78 | AccountLinkCreateParams createAccountLinkParams = new AccountLinkCreateParams 79 | .Builder() 80 | .setAccount(sessionAccountId) 81 | .setType(AccountLinkCreateParams.Type.ACCOUNT_ONBOARDING) 82 | .setRefreshUrl(String.format("%s/onboard-user/refresh", origin)) 83 | .setReturnUrl(String.format("%s/success.html", origin)) 84 | .build(); 85 | 86 | AccountLink accountLink = AccountLink.create(createAccountLinkParams); 87 | 88 | response.redirect(accountLink.getUrl(), 303); 89 | return ""; 90 | }); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /server/node/README.md: -------------------------------------------------------------------------------- 1 | # Connect onboarding for Standard accounts 2 | 3 | An [Express server](http://expressjs.com) implementation 4 | 5 | ## Requirements 6 | 7 | * Node v10+ 8 | * [Configured .env file](../README.md) 9 | 10 | ## How to run 11 | 12 | 1. Install dependencies 13 | 14 | ``` 15 | npm install 16 | ``` 17 | 18 | 2. Run the application 19 | 20 | ``` 21 | npm start 22 | ``` 23 | 24 | 3. Go to `localhost:4242` to see the demo 25 | -------------------------------------------------------------------------------- /server/node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stripe-sample-connect-onboarding-for-standard", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "18.11.2", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.2.tgz", 10 | "integrity": "sha512-BWN3M23gLO2jVG8g/XHIRFWiiV4/GckeFIqbU/C4V3xpoBBWSMk4OZomouN0wCkfQFPqgZikyLr7DOYDysIkkw==" 11 | }, 12 | "accepts": { 13 | "version": "1.3.8", 14 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 15 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 16 | "requires": { 17 | "mime-types": "~2.1.34", 18 | "negotiator": "0.6.3" 19 | } 20 | }, 21 | "array-flatten": { 22 | "version": "1.1.1", 23 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 24 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 25 | }, 26 | "body-parser": { 27 | "version": "1.20.1", 28 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 29 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 30 | "requires": { 31 | "bytes": "3.1.2", 32 | "content-type": "~1.0.4", 33 | "debug": "2.6.9", 34 | "depd": "2.0.0", 35 | "destroy": "1.2.0", 36 | "http-errors": "2.0.0", 37 | "iconv-lite": "0.4.24", 38 | "on-finished": "2.4.1", 39 | "qs": "6.11.0", 40 | "raw-body": "2.5.1", 41 | "type-is": "~1.6.18", 42 | "unpipe": "1.0.0" 43 | } 44 | }, 45 | "bytes": { 46 | "version": "3.1.2", 47 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 48 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" 49 | }, 50 | "call-bind": { 51 | "version": "1.0.2", 52 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", 53 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", 54 | "requires": { 55 | "function-bind": "^1.1.1", 56 | "get-intrinsic": "^1.0.2" 57 | } 58 | }, 59 | "content-disposition": { 60 | "version": "0.5.4", 61 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 62 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 63 | "requires": { 64 | "safe-buffer": "5.2.1" 65 | } 66 | }, 67 | "content-type": { 68 | "version": "1.0.4", 69 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 70 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 71 | }, 72 | "cookie": { 73 | "version": "0.5.0", 74 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 75 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" 76 | }, 77 | "cookie-signature": { 78 | "version": "1.0.6", 79 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 80 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 81 | }, 82 | "debug": { 83 | "version": "2.6.9", 84 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 85 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 86 | "requires": { 87 | "ms": "2.0.0" 88 | } 89 | }, 90 | "depd": { 91 | "version": "2.0.0", 92 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 93 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 94 | }, 95 | "destroy": { 96 | "version": "1.2.0", 97 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 98 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" 99 | }, 100 | "dotenv": { 101 | "version": "16.0.3", 102 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 103 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 104 | }, 105 | "ee-first": { 106 | "version": "1.1.1", 107 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 108 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 109 | }, 110 | "encodeurl": { 111 | "version": "1.0.2", 112 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 113 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" 114 | }, 115 | "escape-html": { 116 | "version": "1.0.3", 117 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 118 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 119 | }, 120 | "etag": { 121 | "version": "1.8.1", 122 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 123 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" 124 | }, 125 | "express": { 126 | "version": "4.18.2", 127 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", 128 | "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", 129 | "requires": { 130 | "accepts": "~1.3.8", 131 | "array-flatten": "1.1.1", 132 | "body-parser": "1.20.1", 133 | "content-disposition": "0.5.4", 134 | "content-type": "~1.0.4", 135 | "cookie": "0.5.0", 136 | "cookie-signature": "1.0.6", 137 | "debug": "2.6.9", 138 | "depd": "2.0.0", 139 | "encodeurl": "~1.0.2", 140 | "escape-html": "~1.0.3", 141 | "etag": "~1.8.1", 142 | "finalhandler": "1.2.0", 143 | "fresh": "0.5.2", 144 | "http-errors": "2.0.0", 145 | "merge-descriptors": "1.0.1", 146 | "methods": "~1.1.2", 147 | "on-finished": "2.4.1", 148 | "parseurl": "~1.3.3", 149 | "path-to-regexp": "0.1.7", 150 | "proxy-addr": "~2.0.7", 151 | "qs": "6.11.0", 152 | "range-parser": "~1.2.1", 153 | "safe-buffer": "5.2.1", 154 | "send": "0.18.0", 155 | "serve-static": "1.15.0", 156 | "setprototypeof": "1.2.0", 157 | "statuses": "2.0.1", 158 | "type-is": "~1.6.18", 159 | "utils-merge": "1.0.1", 160 | "vary": "~1.1.2" 161 | }, 162 | "dependencies": { 163 | "body-parser": { 164 | "version": "1.20.1", 165 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", 166 | "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", 167 | "requires": { 168 | "bytes": "3.1.2", 169 | "content-type": "~1.0.4", 170 | "debug": "2.6.9", 171 | "depd": "2.0.0", 172 | "destroy": "1.2.0", 173 | "http-errors": "2.0.0", 174 | "iconv-lite": "0.4.24", 175 | "on-finished": "2.4.1", 176 | "qs": "6.11.0", 177 | "raw-body": "2.5.1", 178 | "type-is": "~1.6.18", 179 | "unpipe": "1.0.0" 180 | } 181 | }, 182 | "qs": { 183 | "version": "6.11.0", 184 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 185 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 186 | "requires": { 187 | "side-channel": "^1.0.4" 188 | } 189 | } 190 | } 191 | }, 192 | "express-session": { 193 | "version": "1.17.3", 194 | "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz", 195 | "integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==", 196 | "requires": { 197 | "cookie": "0.4.2", 198 | "cookie-signature": "1.0.6", 199 | "debug": "2.6.9", 200 | "depd": "~2.0.0", 201 | "on-headers": "~1.0.2", 202 | "parseurl": "~1.3.3", 203 | "safe-buffer": "5.2.1", 204 | "uid-safe": "~2.1.5" 205 | }, 206 | "dependencies": { 207 | "cookie": { 208 | "version": "0.4.2", 209 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 210 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" 211 | } 212 | } 213 | }, 214 | "finalhandler": { 215 | "version": "1.2.0", 216 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 217 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 218 | "requires": { 219 | "debug": "2.6.9", 220 | "encodeurl": "~1.0.2", 221 | "escape-html": "~1.0.3", 222 | "on-finished": "2.4.1", 223 | "parseurl": "~1.3.3", 224 | "statuses": "2.0.1", 225 | "unpipe": "~1.0.0" 226 | } 227 | }, 228 | "forwarded": { 229 | "version": "0.2.0", 230 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 231 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" 232 | }, 233 | "fresh": { 234 | "version": "0.5.2", 235 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 236 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" 237 | }, 238 | "function-bind": { 239 | "version": "1.1.1", 240 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", 241 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" 242 | }, 243 | "get-intrinsic": { 244 | "version": "1.1.1", 245 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", 246 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", 247 | "requires": { 248 | "function-bind": "^1.1.1", 249 | "has": "^1.0.3", 250 | "has-symbols": "^1.0.1" 251 | } 252 | }, 253 | "has": { 254 | "version": "1.0.3", 255 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", 256 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", 257 | "requires": { 258 | "function-bind": "^1.1.1" 259 | } 260 | }, 261 | "has-symbols": { 262 | "version": "1.0.3", 263 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 264 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" 265 | }, 266 | "http-errors": { 267 | "version": "2.0.0", 268 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 269 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 270 | "requires": { 271 | "depd": "2.0.0", 272 | "inherits": "2.0.4", 273 | "setprototypeof": "1.2.0", 274 | "statuses": "2.0.1", 275 | "toidentifier": "1.0.1" 276 | } 277 | }, 278 | "iconv-lite": { 279 | "version": "0.4.24", 280 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 281 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 282 | "requires": { 283 | "safer-buffer": ">= 2.1.2 < 3" 284 | } 285 | }, 286 | "inherits": { 287 | "version": "2.0.4", 288 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 289 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 290 | }, 291 | "ipaddr.js": { 292 | "version": "1.9.1", 293 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 294 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 295 | }, 296 | "media-typer": { 297 | "version": "0.3.0", 298 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 299 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 300 | }, 301 | "merge-descriptors": { 302 | "version": "1.0.1", 303 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 304 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 305 | }, 306 | "methods": { 307 | "version": "1.1.2", 308 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 309 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==" 310 | }, 311 | "mime": { 312 | "version": "1.6.0", 313 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 314 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 315 | }, 316 | "mime-db": { 317 | "version": "1.51.0", 318 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", 319 | "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==" 320 | }, 321 | "mime-types": { 322 | "version": "2.1.34", 323 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz", 324 | "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==", 325 | "requires": { 326 | "mime-db": "1.51.0" 327 | } 328 | }, 329 | "ms": { 330 | "version": "2.0.0", 331 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 332 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 333 | }, 334 | "negotiator": { 335 | "version": "0.6.3", 336 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 337 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 338 | }, 339 | "object-inspect": { 340 | "version": "1.12.0", 341 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz", 342 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==" 343 | }, 344 | "on-finished": { 345 | "version": "2.4.1", 346 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 347 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 348 | "requires": { 349 | "ee-first": "1.1.1" 350 | } 351 | }, 352 | "on-headers": { 353 | "version": "1.0.2", 354 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 355 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 356 | }, 357 | "parseurl": { 358 | "version": "1.3.3", 359 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 360 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 361 | }, 362 | "path-to-regexp": { 363 | "version": "0.1.7", 364 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 365 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 366 | }, 367 | "proxy-addr": { 368 | "version": "2.0.7", 369 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 370 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 371 | "requires": { 372 | "forwarded": "0.2.0", 373 | "ipaddr.js": "1.9.1" 374 | } 375 | }, 376 | "qs": { 377 | "version": "6.11.0", 378 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 379 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 380 | "requires": { 381 | "side-channel": "^1.0.4" 382 | } 383 | }, 384 | "random-bytes": { 385 | "version": "1.0.0", 386 | "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", 387 | "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" 388 | }, 389 | "range-parser": { 390 | "version": "1.2.1", 391 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 392 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 393 | }, 394 | "raw-body": { 395 | "version": "2.5.1", 396 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", 397 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", 398 | "requires": { 399 | "bytes": "3.1.2", 400 | "http-errors": "2.0.0", 401 | "iconv-lite": "0.4.24", 402 | "unpipe": "1.0.0" 403 | }, 404 | "dependencies": { 405 | "depd": { 406 | "version": "2.0.0", 407 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 408 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 409 | }, 410 | "http-errors": { 411 | "version": "2.0.0", 412 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 413 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 414 | "requires": { 415 | "depd": "2.0.0", 416 | "inherits": "2.0.4", 417 | "setprototypeof": "1.2.0", 418 | "statuses": "2.0.1", 419 | "toidentifier": "1.0.1" 420 | } 421 | }, 422 | "statuses": { 423 | "version": "2.0.1", 424 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 425 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 426 | } 427 | } 428 | }, 429 | "safe-buffer": { 430 | "version": "5.2.1", 431 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 432 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" 433 | }, 434 | "safer-buffer": { 435 | "version": "2.1.2", 436 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 437 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 438 | }, 439 | "send": { 440 | "version": "0.18.0", 441 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 442 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 443 | "requires": { 444 | "debug": "2.6.9", 445 | "depd": "2.0.0", 446 | "destroy": "1.2.0", 447 | "encodeurl": "~1.0.2", 448 | "escape-html": "~1.0.3", 449 | "etag": "~1.8.1", 450 | "fresh": "0.5.2", 451 | "http-errors": "2.0.0", 452 | "mime": "1.6.0", 453 | "ms": "2.1.3", 454 | "on-finished": "2.4.1", 455 | "range-parser": "~1.2.1", 456 | "statuses": "2.0.1" 457 | }, 458 | "dependencies": { 459 | "ms": { 460 | "version": "2.1.3", 461 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 462 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 463 | } 464 | } 465 | }, 466 | "serve-static": { 467 | "version": "1.15.0", 468 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 469 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 470 | "requires": { 471 | "encodeurl": "~1.0.2", 472 | "escape-html": "~1.0.3", 473 | "parseurl": "~1.3.3", 474 | "send": "0.18.0" 475 | } 476 | }, 477 | "setprototypeof": { 478 | "version": "1.2.0", 479 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 480 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 481 | }, 482 | "side-channel": { 483 | "version": "1.0.4", 484 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", 485 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", 486 | "requires": { 487 | "call-bind": "^1.0.0", 488 | "get-intrinsic": "^1.0.2", 489 | "object-inspect": "^1.9.0" 490 | } 491 | }, 492 | "statuses": { 493 | "version": "2.0.1", 494 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 495 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" 496 | }, 497 | "stripe": { 498 | "version": "10.14.0", 499 | "resolved": "https://registry.npmjs.org/stripe/-/stripe-10.14.0.tgz", 500 | "integrity": "sha512-cXqUdZvrU20KElhLjzw/kW5t2SLLUENqeEftaiQXLogkUdNHyz/L12S4WHmQ8h0KHHFhxO39I7Q9GkJO1MMzzw==", 501 | "requires": { 502 | "@types/node": ">=8.1.0", 503 | "qs": "^6.11.0" 504 | }, 505 | "dependencies": { 506 | "qs": { 507 | "version": "6.11.0", 508 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 509 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 510 | "requires": { 511 | "side-channel": "^1.0.4" 512 | } 513 | } 514 | } 515 | }, 516 | "toidentifier": { 517 | "version": "1.0.1", 518 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 519 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" 520 | }, 521 | "type-is": { 522 | "version": "1.6.18", 523 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 524 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 525 | "requires": { 526 | "media-typer": "0.3.0", 527 | "mime-types": "~2.1.24" 528 | } 529 | }, 530 | "uid-safe": { 531 | "version": "2.1.5", 532 | "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", 533 | "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", 534 | "requires": { 535 | "random-bytes": "~1.0.0" 536 | } 537 | }, 538 | "unpipe": { 539 | "version": "1.0.0", 540 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 541 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 542 | }, 543 | "utils-merge": { 544 | "version": "1.0.1", 545 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 546 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==" 547 | }, 548 | "vary": { 549 | "version": "1.1.2", 550 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 551 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 552 | } 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /server/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stripe-sample-connect-onboarding-for-standard", 3 | "version": "1.0.0", 4 | "description": "A Stripe demo", 5 | "main": "server.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "stripe-demos", 11 | "license": "MIT", 12 | "dependencies": { 13 | "dotenv": "^16.0.3", 14 | "express": "^4.18.2", 15 | "stripe": "^10.14.0", 16 | "body-parser": "^1.20.1", 17 | "express-session": "^1.17.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/node/server.js: -------------------------------------------------------------------------------- 1 | // Replace if using a different env file or config 2 | require("dotenv").config(); 3 | const bodyParser = require("body-parser"); 4 | const express = require("express"); 5 | const { resolve } = require("path"); 6 | const session = require("express-session"); 7 | 8 | const app = express(); 9 | const port = process.env.PORT || 4242; 10 | 11 | const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY, { 12 | apiVersion: '2020-08-27', 13 | appInfo: { // For sample support and debugging, not required for production: 14 | name: "stripe-samples/connect-onboarding-for-standard", 15 | version: "0.0.1", 16 | url: "https://github.com/stripe-samples" 17 | } 18 | }); 19 | 20 | app.use(express.static(process.env.STATIC_DIR)); 21 | app.use( 22 | session({ 23 | secret: "Set this to a random string that is kept secure", 24 | resave: false, 25 | saveUninitialized: true, 26 | }) 27 | ); 28 | 29 | app.get("/", (req, res) => { 30 | const path = resolve(process.env.STATIC_DIR + "/index.html"); 31 | res.sendFile(path); 32 | }); 33 | 34 | app.post("/onboard-user", async (req, res) => { 35 | try { 36 | const account = await stripe.accounts.create({ 37 | type: 'standard', 38 | }); 39 | 40 | // Store the ID of the new Standard connected account. 41 | req.session.accountID = account.id; 42 | 43 | const origin = `${req.headers.origin}`; 44 | const accountLink = await stripe.accountLinks.create({ 45 | type: "account_onboarding", 46 | account: account.id, 47 | refresh_url: `${origin}/onboard-user/refresh`, 48 | return_url: `${origin}/success.html`, 49 | }); 50 | 51 | res.redirect(303, accountLink.url); 52 | } catch (err) { 53 | res.status(500).send({ 54 | error: err.message, 55 | }); 56 | } 57 | }); 58 | 59 | app.get("/onboard-user/refresh", async (req, res) => { 60 | if (!req.session.accountID) { 61 | res.redirect("/"); 62 | return; 63 | } 64 | 65 | try { 66 | const { accountID } = req.session; 67 | const origin = `${req.secure ? "https://" : "http://"}${req.headers.host}`; 68 | 69 | const accountLink = await stripe.accountLinks.create({ 70 | type: "account_onboarding", 71 | account: accountID, 72 | refresh_url: `${origin}/onboard-user/refresh`, 73 | return_url: `${origin}/success.html`, 74 | }); 75 | 76 | res.redirect(303, accountLink.url); 77 | } catch (err) { 78 | res.status(500).send({ 79 | error: err.message, 80 | }); 81 | } 82 | }); 83 | 84 | app.listen(port, () => console.log(`Node server listening at http://localhost:${port}!`)); 85 | -------------------------------------------------------------------------------- /server/php/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | 3 | RewriteCond %{REQUEST_FILENAME} !-d 4 | RewriteCond %{REQUEST_FILENAME} !-f 5 | RewriteRule ^ index.php [QSA,L] 6 | -------------------------------------------------------------------------------- /server/php/README.md: -------------------------------------------------------------------------------- 1 | # Connect onboarding for Standard accounts 2 | 3 | ## Requirements 4 | 5 | * PHP >= 7.1.3 6 | * Composer 7 | 8 | ## How to run 9 | 10 | 1. Install dependencies 11 | 12 | ``` 13 | composer install 14 | ``` 15 | 16 | 2. Run the application 17 | 18 | ``` 19 | composer start 20 | ``` 21 | 22 | 3. Go to `localhost:4242` in your browser to see the demo 23 | -------------------------------------------------------------------------------- /server/php/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "vlucas/phpdotenv": "^5.4", 4 | "stripe/stripe-php": "^9.0" 5 | }, 6 | "scripts": { 7 | "start": "cd public && php -S localhost:4242" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /server/php/composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "b4caa164a423b32aedcdbb218e07f065", 8 | "packages": [ 9 | { 10 | "name": "graham-campbell/result-type", 11 | "version": "v1.1.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/GrahamCampbell/Result-Type.git", 15 | "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/a878d45c1914464426dc94da61c9e1d36ae262a8", 20 | "reference": "a878d45c1914464426dc94da61c9e1d36ae262a8", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": "^7.2.5 || ^8.0", 25 | "phpoption/phpoption": "^1.9" 26 | }, 27 | "require-dev": { 28 | "phpunit/phpunit": "^8.5.28 || ^9.5.21" 29 | }, 30 | "type": "library", 31 | "autoload": { 32 | "psr-4": { 33 | "GrahamCampbell\\ResultType\\": "src/" 34 | } 35 | }, 36 | "notification-url": "https://packagist.org/downloads/", 37 | "license": [ 38 | "MIT" 39 | ], 40 | "authors": [ 41 | { 42 | "name": "Graham Campbell", 43 | "email": "hello@gjcampbell.co.uk", 44 | "homepage": "https://github.com/GrahamCampbell" 45 | } 46 | ], 47 | "description": "An Implementation Of The Result Type", 48 | "keywords": [ 49 | "Graham Campbell", 50 | "GrahamCampbell", 51 | "Result Type", 52 | "Result-Type", 53 | "result" 54 | ], 55 | "support": { 56 | "issues": "https://github.com/GrahamCampbell/Result-Type/issues", 57 | "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.0" 58 | }, 59 | "funding": [ 60 | { 61 | "url": "https://github.com/GrahamCampbell", 62 | "type": "github" 63 | }, 64 | { 65 | "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", 66 | "type": "tidelift" 67 | } 68 | ], 69 | "time": "2022-07-30T15:56:11+00:00" 70 | }, 71 | { 72 | "name": "phpoption/phpoption", 73 | "version": "1.9.0", 74 | "source": { 75 | "type": "git", 76 | "url": "https://github.com/schmittjoh/php-option.git", 77 | "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab" 78 | }, 79 | "dist": { 80 | "type": "zip", 81 | "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", 82 | "reference": "dc5ff11e274a90cc1c743f66c9ad700ce50db9ab", 83 | "shasum": "" 84 | }, 85 | "require": { 86 | "php": "^7.2.5 || ^8.0" 87 | }, 88 | "require-dev": { 89 | "bamarni/composer-bin-plugin": "^1.8", 90 | "phpunit/phpunit": "^8.5.28 || ^9.5.21" 91 | }, 92 | "type": "library", 93 | "extra": { 94 | "bamarni-bin": { 95 | "bin-links": true, 96 | "forward-command": true 97 | }, 98 | "branch-alias": { 99 | "dev-master": "1.9-dev" 100 | } 101 | }, 102 | "autoload": { 103 | "psr-4": { 104 | "PhpOption\\": "src/PhpOption/" 105 | } 106 | }, 107 | "notification-url": "https://packagist.org/downloads/", 108 | "license": [ 109 | "Apache-2.0" 110 | ], 111 | "authors": [ 112 | { 113 | "name": "Johannes M. Schmitt", 114 | "email": "schmittjoh@gmail.com", 115 | "homepage": "https://github.com/schmittjoh" 116 | }, 117 | { 118 | "name": "Graham Campbell", 119 | "email": "hello@gjcampbell.co.uk", 120 | "homepage": "https://github.com/GrahamCampbell" 121 | } 122 | ], 123 | "description": "Option Type for PHP", 124 | "keywords": [ 125 | "language", 126 | "option", 127 | "php", 128 | "type" 129 | ], 130 | "support": { 131 | "issues": "https://github.com/schmittjoh/php-option/issues", 132 | "source": "https://github.com/schmittjoh/php-option/tree/1.9.0" 133 | }, 134 | "funding": [ 135 | { 136 | "url": "https://github.com/GrahamCampbell", 137 | "type": "github" 138 | }, 139 | { 140 | "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", 141 | "type": "tidelift" 142 | } 143 | ], 144 | "time": "2022-07-30T15:51:26+00:00" 145 | }, 146 | { 147 | "name": "stripe/stripe-php", 148 | "version": "v9.9.0", 149 | "source": { 150 | "type": "git", 151 | "url": "https://github.com/stripe/stripe-php.git", 152 | "reference": "479b5c2136fde0debb93d290ceaf20dd161c358f" 153 | }, 154 | "dist": { 155 | "type": "zip", 156 | "url": "https://api.github.com/repos/stripe/stripe-php/zipball/479b5c2136fde0debb93d290ceaf20dd161c358f", 157 | "reference": "479b5c2136fde0debb93d290ceaf20dd161c358f", 158 | "shasum": "" 159 | }, 160 | "require": { 161 | "ext-curl": "*", 162 | "ext-json": "*", 163 | "ext-mbstring": "*", 164 | "php": ">=5.6.0" 165 | }, 166 | "require-dev": { 167 | "friendsofphp/php-cs-fixer": "3.5.0", 168 | "php-coveralls/php-coveralls": "^2.5", 169 | "phpstan/phpstan": "^1.2", 170 | "phpunit/phpunit": "^5.7 || ^9.0", 171 | "squizlabs/php_codesniffer": "^3.3" 172 | }, 173 | "type": "library", 174 | "extra": { 175 | "branch-alias": { 176 | "dev-master": "2.0-dev" 177 | } 178 | }, 179 | "autoload": { 180 | "psr-4": { 181 | "Stripe\\": "lib/" 182 | } 183 | }, 184 | "notification-url": "https://packagist.org/downloads/", 185 | "license": [ 186 | "MIT" 187 | ], 188 | "authors": [ 189 | { 190 | "name": "Stripe and contributors", 191 | "homepage": "https://github.com/stripe/stripe-php/contributors" 192 | } 193 | ], 194 | "description": "Stripe PHP Library", 195 | "homepage": "https://stripe.com/", 196 | "keywords": [ 197 | "api", 198 | "payment processing", 199 | "stripe" 200 | ], 201 | "support": { 202 | "issues": "https://github.com/stripe/stripe-php/issues", 203 | "source": "https://github.com/stripe/stripe-php/tree/v9.9.0" 204 | }, 205 | "time": "2022-11-08T20:25:52+00:00" 206 | }, 207 | { 208 | "name": "symfony/polyfill-ctype", 209 | "version": "v1.26.0", 210 | "source": { 211 | "type": "git", 212 | "url": "https://github.com/symfony/polyfill-ctype.git", 213 | "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" 214 | }, 215 | "dist": { 216 | "type": "zip", 217 | "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", 218 | "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", 219 | "shasum": "" 220 | }, 221 | "require": { 222 | "php": ">=7.1" 223 | }, 224 | "provide": { 225 | "ext-ctype": "*" 226 | }, 227 | "suggest": { 228 | "ext-ctype": "For best performance" 229 | }, 230 | "type": "library", 231 | "extra": { 232 | "branch-alias": { 233 | "dev-main": "1.26-dev" 234 | }, 235 | "thanks": { 236 | "name": "symfony/polyfill", 237 | "url": "https://github.com/symfony/polyfill" 238 | } 239 | }, 240 | "autoload": { 241 | "files": [ 242 | "bootstrap.php" 243 | ], 244 | "psr-4": { 245 | "Symfony\\Polyfill\\Ctype\\": "" 246 | } 247 | }, 248 | "notification-url": "https://packagist.org/downloads/", 249 | "license": [ 250 | "MIT" 251 | ], 252 | "authors": [ 253 | { 254 | "name": "Gert de Pagter", 255 | "email": "BackEndTea@gmail.com" 256 | }, 257 | { 258 | "name": "Symfony Community", 259 | "homepage": "https://symfony.com/contributors" 260 | } 261 | ], 262 | "description": "Symfony polyfill for ctype functions", 263 | "homepage": "https://symfony.com", 264 | "keywords": [ 265 | "compatibility", 266 | "ctype", 267 | "polyfill", 268 | "portable" 269 | ], 270 | "support": { 271 | "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" 272 | }, 273 | "funding": [ 274 | { 275 | "url": "https://symfony.com/sponsor", 276 | "type": "custom" 277 | }, 278 | { 279 | "url": "https://github.com/fabpot", 280 | "type": "github" 281 | }, 282 | { 283 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 284 | "type": "tidelift" 285 | } 286 | ], 287 | "time": "2022-05-24T11:49:31+00:00" 288 | }, 289 | { 290 | "name": "symfony/polyfill-mbstring", 291 | "version": "v1.26.0", 292 | "source": { 293 | "type": "git", 294 | "url": "https://github.com/symfony/polyfill-mbstring.git", 295 | "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" 296 | }, 297 | "dist": { 298 | "type": "zip", 299 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", 300 | "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", 301 | "shasum": "" 302 | }, 303 | "require": { 304 | "php": ">=7.1" 305 | }, 306 | "provide": { 307 | "ext-mbstring": "*" 308 | }, 309 | "suggest": { 310 | "ext-mbstring": "For best performance" 311 | }, 312 | "type": "library", 313 | "extra": { 314 | "branch-alias": { 315 | "dev-main": "1.26-dev" 316 | }, 317 | "thanks": { 318 | "name": "symfony/polyfill", 319 | "url": "https://github.com/symfony/polyfill" 320 | } 321 | }, 322 | "autoload": { 323 | "files": [ 324 | "bootstrap.php" 325 | ], 326 | "psr-4": { 327 | "Symfony\\Polyfill\\Mbstring\\": "" 328 | } 329 | }, 330 | "notification-url": "https://packagist.org/downloads/", 331 | "license": [ 332 | "MIT" 333 | ], 334 | "authors": [ 335 | { 336 | "name": "Nicolas Grekas", 337 | "email": "p@tchwork.com" 338 | }, 339 | { 340 | "name": "Symfony Community", 341 | "homepage": "https://symfony.com/contributors" 342 | } 343 | ], 344 | "description": "Symfony polyfill for the Mbstring extension", 345 | "homepage": "https://symfony.com", 346 | "keywords": [ 347 | "compatibility", 348 | "mbstring", 349 | "polyfill", 350 | "portable", 351 | "shim" 352 | ], 353 | "support": { 354 | "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" 355 | }, 356 | "funding": [ 357 | { 358 | "url": "https://symfony.com/sponsor", 359 | "type": "custom" 360 | }, 361 | { 362 | "url": "https://github.com/fabpot", 363 | "type": "github" 364 | }, 365 | { 366 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 367 | "type": "tidelift" 368 | } 369 | ], 370 | "time": "2022-05-24T11:49:31+00:00" 371 | }, 372 | { 373 | "name": "symfony/polyfill-php80", 374 | "version": "v1.26.0", 375 | "source": { 376 | "type": "git", 377 | "url": "https://github.com/symfony/polyfill-php80.git", 378 | "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" 379 | }, 380 | "dist": { 381 | "type": "zip", 382 | "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", 383 | "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", 384 | "shasum": "" 385 | }, 386 | "require": { 387 | "php": ">=7.1" 388 | }, 389 | "type": "library", 390 | "extra": { 391 | "branch-alias": { 392 | "dev-main": "1.26-dev" 393 | }, 394 | "thanks": { 395 | "name": "symfony/polyfill", 396 | "url": "https://github.com/symfony/polyfill" 397 | } 398 | }, 399 | "autoload": { 400 | "files": [ 401 | "bootstrap.php" 402 | ], 403 | "psr-4": { 404 | "Symfony\\Polyfill\\Php80\\": "" 405 | }, 406 | "classmap": [ 407 | "Resources/stubs" 408 | ] 409 | }, 410 | "notification-url": "https://packagist.org/downloads/", 411 | "license": [ 412 | "MIT" 413 | ], 414 | "authors": [ 415 | { 416 | "name": "Ion Bazan", 417 | "email": "ion.bazan@gmail.com" 418 | }, 419 | { 420 | "name": "Nicolas Grekas", 421 | "email": "p@tchwork.com" 422 | }, 423 | { 424 | "name": "Symfony Community", 425 | "homepage": "https://symfony.com/contributors" 426 | } 427 | ], 428 | "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", 429 | "homepage": "https://symfony.com", 430 | "keywords": [ 431 | "compatibility", 432 | "polyfill", 433 | "portable", 434 | "shim" 435 | ], 436 | "support": { 437 | "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" 438 | }, 439 | "funding": [ 440 | { 441 | "url": "https://symfony.com/sponsor", 442 | "type": "custom" 443 | }, 444 | { 445 | "url": "https://github.com/fabpot", 446 | "type": "github" 447 | }, 448 | { 449 | "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", 450 | "type": "tidelift" 451 | } 452 | ], 453 | "time": "2022-05-10T07:21:04+00:00" 454 | }, 455 | { 456 | "name": "vlucas/phpdotenv", 457 | "version": "v5.5.0", 458 | "source": { 459 | "type": "git", 460 | "url": "https://github.com/vlucas/phpdotenv.git", 461 | "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7" 462 | }, 463 | "dist": { 464 | "type": "zip", 465 | "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", 466 | "reference": "1a7ea2afc49c3ee6d87061f5a233e3a035d0eae7", 467 | "shasum": "" 468 | }, 469 | "require": { 470 | "ext-pcre": "*", 471 | "graham-campbell/result-type": "^1.0.2", 472 | "php": "^7.1.3 || ^8.0", 473 | "phpoption/phpoption": "^1.8", 474 | "symfony/polyfill-ctype": "^1.23", 475 | "symfony/polyfill-mbstring": "^1.23.1", 476 | "symfony/polyfill-php80": "^1.23.1" 477 | }, 478 | "require-dev": { 479 | "bamarni/composer-bin-plugin": "^1.4.1", 480 | "ext-filter": "*", 481 | "phpunit/phpunit": "^7.5.20 || ^8.5.30 || ^9.5.25" 482 | }, 483 | "suggest": { 484 | "ext-filter": "Required to use the boolean validator." 485 | }, 486 | "type": "library", 487 | "extra": { 488 | "bamarni-bin": { 489 | "bin-links": true, 490 | "forward-command": true 491 | }, 492 | "branch-alias": { 493 | "dev-master": "5.5-dev" 494 | } 495 | }, 496 | "autoload": { 497 | "psr-4": { 498 | "Dotenv\\": "src/" 499 | } 500 | }, 501 | "notification-url": "https://packagist.org/downloads/", 502 | "license": [ 503 | "BSD-3-Clause" 504 | ], 505 | "authors": [ 506 | { 507 | "name": "Graham Campbell", 508 | "email": "hello@gjcampbell.co.uk", 509 | "homepage": "https://github.com/GrahamCampbell" 510 | }, 511 | { 512 | "name": "Vance Lucas", 513 | "email": "vance@vancelucas.com", 514 | "homepage": "https://github.com/vlucas" 515 | } 516 | ], 517 | "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", 518 | "keywords": [ 519 | "dotenv", 520 | "env", 521 | "environment" 522 | ], 523 | "support": { 524 | "issues": "https://github.com/vlucas/phpdotenv/issues", 525 | "source": "https://github.com/vlucas/phpdotenv/tree/v5.5.0" 526 | }, 527 | "funding": [ 528 | { 529 | "url": "https://github.com/GrahamCampbell", 530 | "type": "github" 531 | }, 532 | { 533 | "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", 534 | "type": "tidelift" 535 | } 536 | ], 537 | "time": "2022-10-16T01:01:54+00:00" 538 | } 539 | ], 540 | "packages-dev": [], 541 | "aliases": [], 542 | "minimum-stability": "stable", 543 | "stability-flags": [], 544 | "prefer-stable": false, 545 | "prefer-lowest": false, 546 | "platform": [], 547 | "platform-dev": [], 548 | "plugin-api-version": "2.3.0" 549 | } 550 | -------------------------------------------------------------------------------- /server/php/public/css/global.css: -------------------------------------------------------------------------------- 1 | /* Variables */ 2 | :root { 3 | --gray-offset: rgba(0, 0, 0, 0.03); 4 | --gray-border: rgba(0, 0, 0, 0.15); 5 | --gray-light: rgba(0, 0, 0, 0.4); 6 | --gray-mid: rgba(0, 0, 0, 0.7); 7 | --gray-dark: rgba(0, 0, 0, 0.9); 8 | --body-color: var(--gray-mid); 9 | --headline-color: var(--gray-dark); 10 | --accent-color: #0066f0; 11 | --body-font-family: -apple-system, BlinkMacSystemFont, sans-serif; 12 | --radius: 6px; 13 | --form-width: 343px; 14 | } 15 | 16 | /* Base */ 17 | * { 18 | box-sizing: border-box; 19 | } 20 | body { 21 | font-family: var(--body-font-family); 22 | font-size: 16px; 23 | color: var(--body-color); 24 | -webkit-font-smoothing: antialiased; 25 | } 26 | h1, 27 | h2, 28 | h3, 29 | h4, 30 | h5, 31 | h6 { 32 | color: var(--body-color); 33 | margin-top: 2px; 34 | margin-bottom: 4px; 35 | } 36 | h1 { 37 | font-size: 27px; 38 | color: var(--headline-color); 39 | } 40 | h4 { 41 | font-weight: 500; 42 | font-size: 14px; 43 | color: var(--gray-light); 44 | } 45 | 46 | /* Layout */ 47 | .sr-root { 48 | display: flex; 49 | flex-direction: row; 50 | width: 100%; 51 | max-width: 980px; 52 | padding: 48px; 53 | align-content: center; 54 | justify-content: center; 55 | height: auto; 56 | min-height: 100vh; 57 | margin: 0 auto; 58 | } 59 | .sr-header { 60 | margin-bottom: 32px; 61 | } 62 | .sr-payment-summary { 63 | margin-bottom: 20px; 64 | } 65 | .sr-main, 66 | .sr-content { 67 | display: flex; 68 | flex-direction: column; 69 | justify-content: center; 70 | height: 100%; 71 | align-self: center; 72 | } 73 | .sr-main { 74 | width: var(--form-width); 75 | } 76 | .sr-content { 77 | padding-left: 48px; 78 | } 79 | .sr-header__logo { 80 | background-image: var(--logo-image); 81 | height: 24px; 82 | background-size: contain; 83 | background-repeat: no-repeat; 84 | width: 100%; 85 | } 86 | .sr-legal-text { 87 | color: var(--gray-light); 88 | text-align: center; 89 | font-size: 13px; 90 | line-height: 17px; 91 | margin-top: 12px; 92 | } 93 | .sr-field-error { 94 | color: var(--accent-color); 95 | text-align: left; 96 | font-size: 13px; 97 | line-height: 17px; 98 | margin-top: 12px; 99 | } 100 | 101 | /* Form */ 102 | .sr-form-row { 103 | margin: 16px 0; 104 | } 105 | label { 106 | font-size: 13px; 107 | font-weight: 500; 108 | margin-bottom: 8px; 109 | display: inline-block; 110 | } 111 | 112 | /* Inputs */ 113 | .sr-input, 114 | .sr-select, 115 | input[type="text"] { 116 | border: 1px solid var(--gray-border); 117 | border-radius: var(--radius); 118 | padding: 5px 12px; 119 | height: 44px; 120 | width: 100%; 121 | transition: box-shadow 0.2s ease; 122 | background: white; 123 | -moz-appearance: none; 124 | -webkit-appearance: none; 125 | appearance: none; 126 | color: #32325d; 127 | } 128 | .sr-input:focus, 129 | input[type="text"]:focus, 130 | button:focus, 131 | .focused { 132 | box-shadow: 0 0 0 1px rgba(50, 151, 211, 0.3), 0 1px 1px 0 rgba(0, 0, 0, 0.07), 133 | 0 0 0 4px rgba(50, 151, 211, 0.3); 134 | outline: none; 135 | z-index: 9; 136 | } 137 | .sr-input::placeholder, 138 | input[type="text"]::placeholder { 139 | color: var(--gray-light); 140 | } 141 | 142 | /* Checkbox */ 143 | .sr-checkbox-label { 144 | position: relative; 145 | cursor: pointer; 146 | } 147 | 148 | .sr-checkbox-label input { 149 | opacity: 0; 150 | margin-right: 6px; 151 | } 152 | 153 | .sr-checkbox-label .sr-checkbox-check { 154 | position: absolute; 155 | left: 0; 156 | height: 16px; 157 | width: 16px; 158 | background-color: white; 159 | border: 1px solid var(--gray-border); 160 | border-radius: 4px; 161 | transition: all 0.2s ease; 162 | } 163 | 164 | .sr-checkbox-label input:focus ~ .sr-checkbox-check { 165 | box-shadow: 0 0 0 1px rgba(50, 151, 211, 0.3), 0 1px 1px 0 rgba(0, 0, 0, 0.07), 166 | 0 0 0 4px rgba(50, 151, 211, 0.3); 167 | outline: none; 168 | } 169 | 170 | .sr-checkbox-label input:checked ~ .sr-checkbox-check { 171 | background-color: var(--accent-color); 172 | background-repeat: no-repeat; 173 | background-size: 16px; 174 | background-position: -1px -1px; 175 | } 176 | 177 | /* Select */ 178 | .sr-select { 179 | display: block; 180 | height: 44px; 181 | margin: 0; 182 | background-repeat: no-repeat, repeat; 183 | background-position: right 12px top 50%, 0 0; 184 | background-size: 0.65em auto, 100%; 185 | } 186 | .sr-select:after { 187 | } 188 | .sr-select::-ms-expand { 189 | display: none; 190 | } 191 | .sr-select:hover { 192 | cursor: pointer; 193 | } 194 | .sr-select:focus { 195 | box-shadow: 0 0 0 1px rgba(50, 151, 211, 0.3), 0 1px 1px 0 rgba(0, 0, 0, 0.07), 196 | 0 0 0 4px rgba(50, 151, 211, 0.3); 197 | outline: none; 198 | } 199 | .sr-select option { 200 | font-weight: 400; 201 | } 202 | .sr-select:invalid { 203 | color: var(--gray-light); 204 | background-opacity: 0.4; 205 | } 206 | 207 | /* Combo inputs */ 208 | .sr-combo-inputs { 209 | display: flex; 210 | flex-direction: column; 211 | } 212 | .sr-combo-inputs input, 213 | .sr-combo-inputs .sr-select { 214 | border-radius: 0; 215 | border-bottom: 0; 216 | } 217 | .sr-combo-inputs > input:first-child, 218 | .sr-combo-inputs > .sr-select:first-child { 219 | border-radius: var(--radius) var(--radius) 0 0; 220 | } 221 | .sr-combo-inputs > input:last-child, 222 | .sr-combo-inputs > .sr-select:last-child { 223 | border-radius: 0 0 var(--radius) var(--radius); 224 | border-bottom: 1px solid var(--gray-border); 225 | } 226 | .sr-combo-inputs > .sr-combo-inputs-row:last-child input:first-child { 227 | border-radius: 0 0 0 var(--radius); 228 | border-bottom: 1px solid var(--gray-border); 229 | } 230 | .sr-combo-inputs > .sr-combo-inputs-row:last-child input:last-child { 231 | border-radius: 0 0 var(--radius) 0; 232 | border-bottom: 1px solid var(--gray-border); 233 | } 234 | .sr-combo-inputs > .sr-combo-inputs-row:first-child input:first-child { 235 | border-radius: var(--radius) 0 0 0; 236 | } 237 | .sr-combo-inputs > .sr-combo-inputs-row:first-child input:last-child { 238 | border-radius: 0 var(--radius) 0 0; 239 | } 240 | .sr-combo-inputs > .sr-combo-inputs-row:first-child input:only-child { 241 | border-radius: var(--radius) var(--radius) 0 0; 242 | } 243 | .sr-combo-inputs-row { 244 | width: 100%; 245 | display: flex; 246 | } 247 | 248 | .sr-combo-inputs-row > input { 249 | width: 100%; 250 | border-radius: 0; 251 | } 252 | 253 | .sr-combo-inputs-row > input:first-child:not(:only-child) { 254 | border-right: 0; 255 | } 256 | 257 | .sr-combo-inputs-row:not(:first-of-type) .sr-input { 258 | border-radius: 0 0 var(--radius) var(--radius); 259 | } 260 | 261 | /* Buttons and links */ 262 | button { 263 | background: var(--accent-color); 264 | border-radius: var(--radius); 265 | color: white; 266 | border: 0; 267 | padding: 12px 16px; 268 | margin-top: 16px; 269 | font-weight: 600; 270 | cursor: pointer; 271 | transition: all 0.2s ease; 272 | display: block; 273 | } 274 | button:hover { 275 | filter: contrast(115%); 276 | } 277 | button:active { 278 | transform: translateY(0px) scale(0.98); 279 | filter: brightness(0.9); 280 | } 281 | button:disabled { 282 | opacity: 0.5; 283 | cursor: none; 284 | } 285 | 286 | .sr-payment-form button, 287 | .fullwidth { 288 | width: 100%; 289 | } 290 | 291 | a { 292 | color: var(--accent-color); 293 | text-decoration: none; 294 | transition: all 0.2s ease; 295 | } 296 | 297 | a:hover { 298 | filter: brightness(0.8); 299 | } 300 | 301 | a:active { 302 | filter: brightness(0.5); 303 | } 304 | 305 | /* Code block */ 306 | .sr-callout { 307 | background: var(--gray-offset); 308 | padding: 12px; 309 | border-radius: var(--radius); 310 | max-height: 200px; 311 | overflow: auto; 312 | } 313 | code, 314 | pre { 315 | font-family: "SF Mono", "IBM Plex Mono", "Menlo", monospace; 316 | font-size: 12px; 317 | } 318 | 319 | /* Stripe Element placeholder */ 320 | .sr-card-element { 321 | padding-top: 12px; 322 | } 323 | 324 | /* Responsiveness */ 325 | @media (max-width: 720px) { 326 | .sr-root { 327 | flex-direction: column; 328 | justify-content: flex-start; 329 | padding: 48px 20px; 330 | min-width: 320px; 331 | } 332 | 333 | .sr-header__logo { 334 | background-position: center; 335 | } 336 | 337 | .sr-payment-summary { 338 | text-align: center; 339 | } 340 | 341 | .sr-content { 342 | display: none; 343 | } 344 | 345 | .sr-main { 346 | width: 100%; 347 | } 348 | } 349 | 350 | /* todo: spinner/processing state, errors, animations */ 351 | 352 | .spinner, 353 | .spinner:before, 354 | .spinner:after { 355 | border-radius: 50%; 356 | } 357 | .spinner { 358 | color: #ffffff; 359 | font-size: 22px; 360 | text-indent: -99999px; 361 | margin: 0px auto; 362 | position: relative; 363 | width: 20px; 364 | height: 20px; 365 | box-shadow: inset 0 0 0 2px; 366 | -webkit-transform: translateZ(0); 367 | -ms-transform: translateZ(0); 368 | transform: translateZ(0); 369 | } 370 | .spinner:before, 371 | .spinner:after { 372 | position: absolute; 373 | content: ""; 374 | } 375 | .spinner:before { 376 | width: 10.4px; 377 | height: 20.4px; 378 | background: var(--accent-color); 379 | border-radius: 20.4px 0 0 20.4px; 380 | top: -0.2px; 381 | left: -0.2px; 382 | -webkit-transform-origin: 10.4px 10.2px; 383 | transform-origin: 10.4px 10.2px; 384 | -webkit-animation: loading 2s infinite ease 1.5s; 385 | animation: loading 2s infinite ease 1.5s; 386 | } 387 | .spinner:after { 388 | width: 10.4px; 389 | height: 10.2px; 390 | background: var(--accent-color); 391 | border-radius: 0 10.2px 10.2px 0; 392 | top: -0.1px; 393 | left: 10.2px; 394 | -webkit-transform-origin: 0px 10.2px; 395 | transform-origin: 0px 10.2px; 396 | -webkit-animation: loading 2s infinite ease; 397 | animation: loading 2s infinite ease; 398 | } 399 | @-webkit-keyframes loading { 400 | 0% { 401 | -webkit-transform: rotate(0deg); 402 | transform: rotate(0deg); 403 | } 404 | 100% { 405 | -webkit-transform: rotate(360deg); 406 | transform: rotate(360deg); 407 | } 408 | } 409 | @keyframes loading { 410 | 0% { 411 | -webkit-transform: rotate(0deg); 412 | transform: rotate(0deg); 413 | } 414 | 100% { 415 | -webkit-transform: rotate(360deg); 416 | transform: rotate(360deg); 417 | } 418 | } 419 | 420 | /* Animated form */ 421 | 422 | .sr-root { 423 | animation: 0.4s form-in; 424 | animation-fill-mode: both; 425 | animation-timing-function: ease; 426 | } 427 | 428 | .sr-payment-form .sr-form-row { 429 | animation: 0.4s field-in; 430 | animation-fill-mode: both; 431 | animation-timing-function: ease; 432 | transform-origin: 50% 0%; 433 | } 434 | 435 | /* need saas for loop :D */ 436 | .sr-payment-form .sr-form-row:nth-child(1) { 437 | animation-delay: 0; 438 | } 439 | .sr-payment-form .sr-form-row:nth-child(2) { 440 | animation-delay: 60ms; 441 | } 442 | .sr-payment-form .sr-form-row:nth-child(3) { 443 | animation-delay: 120ms; 444 | } 445 | .sr-payment-form .sr-form-row:nth-child(4) { 446 | animation-delay: 180ms; 447 | } 448 | .sr-payment-form .sr-form-row:nth-child(5) { 449 | animation-delay: 240ms; 450 | } 451 | .sr-payment-form .sr-form-row:nth-child(6) { 452 | animation-delay: 300ms; 453 | } 454 | .hidden { 455 | display: none; 456 | } 457 | 458 | @keyframes field-in { 459 | 0% { 460 | opacity: 0; 461 | transform: translateY(8px) scale(0.95); 462 | } 463 | 100% { 464 | opacity: 1; 465 | transform: translateY(0px) scale(1); 466 | } 467 | } 468 | 469 | @keyframes form-in { 470 | 0% { 471 | opacity: 0; 472 | transform: scale(0.98); 473 | } 474 | 100% { 475 | opacity: 1; 476 | transform: scale(1); 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /server/php/public/css/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /* Document 4 | ========================================================================== */ 5 | 6 | /** 7 | * 1. Correct the line height in all browsers. 8 | * 2. Prevent adjustments of font size after orientation changes in iOS. 9 | */ 10 | 11 | html { 12 | line-height: 1.15; /* 1 */ 13 | -webkit-text-size-adjust: 100%; /* 2 */ 14 | } 15 | 16 | /* Sections 17 | ========================================================================== */ 18 | 19 | /** 20 | * Remove the margin in all browsers. 21 | */ 22 | 23 | body { 24 | margin: 0; 25 | } 26 | 27 | /** 28 | * Render the `main` element consistently in IE. 29 | */ 30 | 31 | main { 32 | display: block; 33 | } 34 | 35 | /** 36 | * Correct the font size and margin on `h1` elements within `section` and 37 | * `article` contexts in Chrome, Firefox, and Safari. 38 | */ 39 | 40 | h1 { 41 | font-size: 2em; 42 | margin: 0.67em 0; 43 | } 44 | 45 | /* Grouping content 46 | ========================================================================== */ 47 | 48 | /** 49 | * 1. Add the correct box sizing in Firefox. 50 | * 2. Show the overflow in Edge and IE. 51 | */ 52 | 53 | hr { 54 | box-sizing: content-box; /* 1 */ 55 | height: 0; /* 1 */ 56 | overflow: visible; /* 2 */ 57 | } 58 | 59 | /** 60 | * 1. Correct the inheritance and scaling of font size in all browsers. 61 | * 2. Correct the odd `em` font sizing in all browsers. 62 | */ 63 | 64 | pre { 65 | font-family: monospace, monospace; /* 1 */ 66 | font-size: 1em; /* 2 */ 67 | } 68 | 69 | /* Text-level semantics 70 | ========================================================================== */ 71 | 72 | /** 73 | * Remove the gray background on active links in IE 10. 74 | */ 75 | 76 | a { 77 | background-color: transparent; 78 | } 79 | 80 | /** 81 | * 1. Remove the bottom border in Chrome 57- 82 | * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. 83 | */ 84 | 85 | abbr[title] { 86 | border-bottom: none; /* 1 */ 87 | text-decoration: underline; /* 2 */ 88 | text-decoration: underline dotted; /* 2 */ 89 | } 90 | 91 | /** 92 | * Add the correct font weight in Chrome, Edge, and Safari. 93 | */ 94 | 95 | b, 96 | strong { 97 | font-weight: bolder; 98 | } 99 | 100 | /** 101 | * 1. Correct the inheritance and scaling of font size in all browsers. 102 | * 2. Correct the odd `em` font sizing in all browsers. 103 | */ 104 | 105 | code, 106 | kbd, 107 | samp { 108 | font-family: monospace, monospace; /* 1 */ 109 | font-size: 1em; /* 2 */ 110 | } 111 | 112 | /** 113 | * Add the correct font size in all browsers. 114 | */ 115 | 116 | small { 117 | font-size: 80%; 118 | } 119 | 120 | /** 121 | * Prevent `sub` and `sup` elements from affecting the line height in 122 | * all browsers. 123 | */ 124 | 125 | sub, 126 | sup { 127 | font-size: 75%; 128 | line-height: 0; 129 | position: relative; 130 | vertical-align: baseline; 131 | } 132 | 133 | sub { 134 | bottom: -0.25em; 135 | } 136 | 137 | sup { 138 | top: -0.5em; 139 | } 140 | 141 | /* Embedded content 142 | ========================================================================== */ 143 | 144 | /** 145 | * Remove the border on images inside links in IE 10. 146 | */ 147 | 148 | img { 149 | border-style: none; 150 | } 151 | 152 | /* Forms 153 | ========================================================================== */ 154 | 155 | /** 156 | * 1. Change the font styles in all browsers. 157 | * 2. Remove the margin in Firefox and Safari. 158 | */ 159 | 160 | button, 161 | input, 162 | optgroup, 163 | select, 164 | textarea { 165 | font-family: inherit; /* 1 */ 166 | font-size: 100%; /* 1 */ 167 | line-height: 1.15; /* 1 */ 168 | margin: 0; /* 2 */ 169 | } 170 | 171 | /** 172 | * Show the overflow in IE. 173 | * 1. Show the overflow in Edge. 174 | */ 175 | 176 | button, 177 | input { /* 1 */ 178 | overflow: visible; 179 | } 180 | 181 | /** 182 | * Remove the inheritance of text transform in Edge, Firefox, and IE. 183 | * 1. Remove the inheritance of text transform in Firefox. 184 | */ 185 | 186 | button, 187 | select { /* 1 */ 188 | text-transform: none; 189 | } 190 | 191 | /** 192 | * Correct the inability to style clickable types in iOS and Safari. 193 | */ 194 | 195 | button, 196 | [type="button"], 197 | [type="reset"], 198 | [type="submit"] { 199 | -webkit-appearance: button; 200 | } 201 | 202 | /** 203 | * Remove the inner border and padding in Firefox. 204 | */ 205 | 206 | button::-moz-focus-inner, 207 | [type="button"]::-moz-focus-inner, 208 | [type="reset"]::-moz-focus-inner, 209 | [type="submit"]::-moz-focus-inner { 210 | border-style: none; 211 | padding: 0; 212 | } 213 | 214 | /** 215 | * Restore the focus styles unset by the previous rule. 216 | */ 217 | 218 | button:-moz-focusring, 219 | [type="button"]:-moz-focusring, 220 | [type="reset"]:-moz-focusring, 221 | [type="submit"]:-moz-focusring { 222 | outline: 1px dotted ButtonText; 223 | } 224 | 225 | /** 226 | * Correct the padding in Firefox. 227 | */ 228 | 229 | fieldset { 230 | padding: 0.35em 0.75em 0.625em; 231 | } 232 | 233 | /** 234 | * 1. Correct the text wrapping in Edge and IE. 235 | * 2. Correct the color inheritance from `fieldset` elements in IE. 236 | * 3. Remove the padding so developers are not caught out when they zero out 237 | * `fieldset` elements in all browsers. 238 | */ 239 | 240 | legend { 241 | box-sizing: border-box; /* 1 */ 242 | color: inherit; /* 2 */ 243 | display: table; /* 1 */ 244 | max-width: 100%; /* 1 */ 245 | padding: 0; /* 3 */ 246 | white-space: normal; /* 1 */ 247 | } 248 | 249 | /** 250 | * Add the correct vertical alignment in Chrome, Firefox, and Opera. 251 | */ 252 | 253 | progress { 254 | vertical-align: baseline; 255 | } 256 | 257 | /** 258 | * Remove the default vertical scrollbar in IE 10+. 259 | */ 260 | 261 | textarea { 262 | overflow: auto; 263 | } 264 | 265 | /** 266 | * 1. Add the correct box sizing in IE 10. 267 | * 2. Remove the padding in IE 10. 268 | */ 269 | 270 | [type="checkbox"], 271 | [type="radio"] { 272 | box-sizing: border-box; /* 1 */ 273 | padding: 0; /* 2 */ 274 | } 275 | 276 | /** 277 | * Correct the cursor style of increment and decrement buttons in Chrome. 278 | */ 279 | 280 | [type="number"]::-webkit-inner-spin-button, 281 | [type="number"]::-webkit-outer-spin-button { 282 | height: auto; 283 | } 284 | 285 | /** 286 | * 1. Correct the odd appearance in Chrome and Safari. 287 | * 2. Correct the outline style in Safari. 288 | */ 289 | 290 | [type="search"] { 291 | -webkit-appearance: textfield; /* 1 */ 292 | outline-offset: -2px; /* 2 */ 293 | } 294 | 295 | /** 296 | * Remove the inner padding in Chrome and Safari on macOS. 297 | */ 298 | 299 | [type="search"]::-webkit-search-decoration { 300 | -webkit-appearance: none; 301 | } 302 | 303 | /** 304 | * 1. Correct the inability to style clickable types in iOS and Safari. 305 | * 2. Change font properties to `inherit` in Safari. 306 | */ 307 | 308 | ::-webkit-file-upload-button { 309 | -webkit-appearance: button; /* 1 */ 310 | font: inherit; /* 2 */ 311 | } 312 | 313 | /* Interactive 314 | ========================================================================== */ 315 | 316 | /* 317 | * Add the correct display in Edge, IE 10+, and Firefox. 318 | */ 319 | 320 | details { 321 | display: block; 322 | } 323 | 324 | /* 325 | * Add the correct display in all browsers. 326 | */ 327 | 328 | summary { 329 | display: list-item; 330 | } 331 | 332 | /* Misc 333 | ========================================================================== */ 334 | 335 | /** 336 | * Add the correct display in IE 10+. 337 | */ 338 | 339 | template { 340 | display: none; 341 | } 342 | 343 | /** 344 | * Add the correct display in IE 10. 345 | */ 346 | 347 | [hidden] { 348 | display: none; 349 | } -------------------------------------------------------------------------------- /server/php/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Stripe Sample Connect Onboarding for Standard accounts 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

Setup payouts to list your home on Kavholm

18 |
19 | 20 |
21 | 22 |
23 |
24 |
25 | 26 | 27 | -------------------------------------------------------------------------------- /server/php/public/onboard-user.php: -------------------------------------------------------------------------------- 1 | accounts->create(['type' => 'standard']); 6 | 7 | if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') 8 | $base = "https://"; 9 | else 10 | $base = "http://"; 11 | $base .= $_SERVER['HTTP_HOST']; 12 | 13 | // Create an account link 14 | $account_link = $stripe->accountLinks->create([ 15 | 'account' => $stripe_account->id, 16 | 'refresh_url' => $base . '/refresh.php', 17 | 'return_url' => $base . '/success.html', 18 | 'type' => 'account_onboarding', 19 | ]); 20 | 21 | header("HTTP/1.1 303 See Other"); 22 | header("Location: " . $account_link->url); 23 | -------------------------------------------------------------------------------- /server/php/public/shared.php: -------------------------------------------------------------------------------- 1 | 10 |

Make a copy of .env.example, place it in the same directory as composer.json, and name it .env, then populate the variables.

11 |

It should look something like the following, but contain your API keys:

12 |
STRIPE_SECRET_KEY=sk_test...
13 |
14 | 15 |

You can use this command to get started:

16 |
cp .env.example .env
17 | 18 | load(); 24 | 25 | // Make sure the configuration file is good. 26 | if (!$_ENV['STRIPE_SECRET_KEY']) { 27 | http_response_code(400); 28 | ?> 29 | 30 |

Invalid .env

31 |

Make a copy of .env.example, place it in the same directory as composer.json, and name it .env, then populate the variables.

32 |

It should look something like the following, but contain your API keys:

33 |
STRIPE_SECRET_KEY=sk_test...
34 |
35 | 36 |
37 | 38 |

You can use this command to get started:

39 |
cp .env.example .env
40 | 41 | 2 | 3 | 4 | 5 | Stripe Sample Connect Onboarding for Standard accounts 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

The user returned to the app

18 |
19 |
20 |
21 | 22 | 23 | -------------------------------------------------------------------------------- /server/python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | certifi = "==2022.9.24" 10 | chardet = "==5.0.0" 11 | click = "==7.1.2" 12 | idna = "==3.4" 13 | itsdangerous = "==1.1.0" 14 | python-dotenv = "==0.20.0" 15 | requests = "==2.28.1" 16 | stripe = "==4.2.0" 17 | toml = "==0.10.2" 18 | urllib3 = "==1.26.12" 19 | Flask = "==1.1.4" 20 | Jinja2 = "==2.11.3" 21 | MarkupSafe = "==2.1.1" 22 | Werkzeug = "==1.0.1" 23 | 24 | [requires] 25 | python_version = "3.8" 26 | -------------------------------------------------------------------------------- /server/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "e5d916b17a54d45899f33049894e41eb7d9b1e71183d1b77d56966f0651680c9" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.8" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "certifi": { 20 | "hashes": [ 21 | "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", 22 | "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" 23 | ], 24 | "index": "pypi", 25 | "version": "==2022.9.24" 26 | }, 27 | "chardet": { 28 | "hashes": [ 29 | "sha256:0368df2bfd78b5fc20572bb4e9bb7fb53e2c094f60ae9993339e8671d0afb8aa", 30 | "sha256:d3e64f022d254183001eccc5db4040520c0f23b1a3f33d6413e099eb7f126557" 31 | ], 32 | "index": "pypi", 33 | "version": "==5.0.0" 34 | }, 35 | "charset-normalizer": { 36 | "hashes": [ 37 | "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", 38 | "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" 39 | ], 40 | "markers": "python_version >= '3.6'", 41 | "version": "==2.1.1" 42 | }, 43 | "click": { 44 | "hashes": [ 45 | "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a", 46 | "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc" 47 | ], 48 | "index": "pypi", 49 | "version": "==7.1.2" 50 | }, 51 | "flask": { 52 | "hashes": [ 53 | "sha256:0fbeb6180d383a9186d0d6ed954e0042ad9f18e0e8de088b2b419d526927d196", 54 | "sha256:c34f04500f2cbbea882b1acb02002ad6fe6b7ffa64a6164577995657f50aed22" 55 | ], 56 | "index": "pypi", 57 | "version": "==1.1.4" 58 | }, 59 | "idna": { 60 | "hashes": [ 61 | "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", 62 | "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" 63 | ], 64 | "index": "pypi", 65 | "version": "==3.4" 66 | }, 67 | "itsdangerous": { 68 | "hashes": [ 69 | "sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19", 70 | "sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749" 71 | ], 72 | "index": "pypi", 73 | "version": "==1.1.0" 74 | }, 75 | "jinja2": { 76 | "hashes": [ 77 | "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419", 78 | "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6" 79 | ], 80 | "index": "pypi", 81 | "version": "==2.11.3" 82 | }, 83 | "markupsafe": { 84 | "hashes": [ 85 | "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", 86 | "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", 87 | "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", 88 | "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", 89 | "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", 90 | "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", 91 | "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", 92 | "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", 93 | "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", 94 | "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", 95 | "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", 96 | "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", 97 | "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", 98 | "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", 99 | "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", 100 | "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", 101 | "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", 102 | "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", 103 | "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", 104 | "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", 105 | "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", 106 | "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", 107 | "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", 108 | "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", 109 | "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", 110 | "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", 111 | "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", 112 | "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", 113 | "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", 114 | "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", 115 | "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", 116 | "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", 117 | "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", 118 | "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", 119 | "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", 120 | "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", 121 | "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", 122 | "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", 123 | "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", 124 | "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" 125 | ], 126 | "index": "pypi", 127 | "version": "==2.1.1" 128 | }, 129 | "python-dotenv": { 130 | "hashes": [ 131 | "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f", 132 | "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938" 133 | ], 134 | "index": "pypi", 135 | "version": "==0.20.0" 136 | }, 137 | "requests": { 138 | "hashes": [ 139 | "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", 140 | "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" 141 | ], 142 | "index": "pypi", 143 | "version": "==2.28.1" 144 | }, 145 | "stripe": { 146 | "hashes": [ 147 | "sha256:8ce03bfc099465740e33890000c454e79316c8730e45ad1efbaec3d52a019d05", 148 | "sha256:f0134704bd4e9410fae25034836dc6f5849d92c0f9083d58d43e01b3e631ac4c" 149 | ], 150 | "index": "pypi", 151 | "version": "==4.2.0" 152 | }, 153 | "toml": { 154 | "hashes": [ 155 | "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", 156 | "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" 157 | ], 158 | "index": "pypi", 159 | "version": "==0.10.2" 160 | }, 161 | "urllib3": { 162 | "hashes": [ 163 | "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", 164 | "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" 165 | ], 166 | "index": "pypi", 167 | "version": "==1.26.12" 168 | }, 169 | "werkzeug": { 170 | "hashes": [ 171 | "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43", 172 | "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c" 173 | ], 174 | "index": "pypi", 175 | "version": "==1.0.1" 176 | } 177 | }, 178 | "develop": {} 179 | } 180 | -------------------------------------------------------------------------------- /server/python/README.md: -------------------------------------------------------------------------------- 1 | # Connect onboarding for Standard accounts 2 | 3 | ## Requirements 4 | * Python 3.6+ 5 | * [Configured .env file](../README.md) 6 | 7 | ## How to run 8 | 9 | 1. Create and activate a new virtual environment 10 | 11 | ```sh 12 | # If you don't have pipenv installed, install it 13 | pip install pipenv 14 | # Run `pipenv install` to install requirements 15 | pipenv install 16 | ``` 17 | 18 | 3. Export and run the application 19 | 20 | **On Linux / Unix / MacOS** 21 | 22 | ``` 23 | pip install -r requirements.txt 24 | export FLASK_APP=server.py 25 | python3 -m flask run --port=4242 26 | ``` 27 | 28 | **On Windows** (PowerShell) 29 | 30 | ``` 31 | pip install -r requirements.txt 32 | $env:FLASK_APP=“server.py" 33 | python3 -m flask run --port=4242 34 | ``` 35 | 36 | 4. Go to `localhost:4242` in your browser to see the demo 37 | -------------------------------------------------------------------------------- /server/python/requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2021.10.8 2 | charset-normalizer==3.0.0 3 | click==8.0.3 4 | Flask==2.0.2 5 | idna==3.3 6 | itsdangerous==2.0.1 7 | Jinja2==3.0.3 8 | MarkupSafe==2.0.1 9 | python-dotenv==0.19.2 10 | requests==2.26.0 11 | stripe==2.64.0 12 | urllib3==1.26.7 13 | Werkzeug==2.0.2 14 | -------------------------------------------------------------------------------- /server/python/server.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3.6 2 | 3 | """ 4 | server.py 5 | Stripe Sample. 6 | Python 3.6 or newer required. 7 | """ 8 | 9 | import json 10 | import os 11 | 12 | import stripe 13 | from dotenv import load_dotenv, find_dotenv 14 | from flask import Flask, jsonify, render_template, redirect, request, session 15 | 16 | # Setup Stripe python client library 17 | load_dotenv(find_dotenv()) 18 | 19 | # For sample support and debugging, not required for production: 20 | stripe.set_app_info( 21 | 'stripe-samples/connect-onboarding-for-standard', 22 | version='0.0.1', 23 | url='https://github.com/stripe-samples') 24 | 25 | stripe.api_key = os.getenv('STRIPE_SECRET_KEY') 26 | stripe.api_version = os.getenv('STRIPE_API_VERSION', '2020-08-27') 27 | 28 | 29 | static_dir = str(os.path.abspath(os.path.join(__file__ , "..", os.getenv("STATIC_DIR")))) 30 | app = Flask( 31 | __name__, 32 | static_folder=static_dir, 33 | static_url_path="", 34 | template_folder=static_dir) 35 | 36 | # Set the secret key to some random bytes. Keep this really secret! 37 | # This enables Flask sessions. 38 | app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' 39 | 40 | @app.route('/', methods=['GET']) 41 | def get_example(): 42 | return render_template('index.html') 43 | 44 | 45 | @app.route('/onboard-user', methods=['POST']) 46 | def onboard_user(): 47 | origin = request.headers['origin'] 48 | 49 | try: 50 | account = stripe.Account.create(type='standard') 51 | 52 | # Store the account ID. 53 | session['account_id'] = account.id 54 | 55 | account_link = stripe.AccountLink.create( 56 | type='account_onboarding', 57 | account=account.id, 58 | refresh_url=f'{origin}/onboard-user/refresh', 59 | return_url=f'{origin}/success.html', 60 | ) 61 | return redirect(account_link.url, code=303) 62 | except Exception as e: 63 | return jsonify(error=str(e)), 403 64 | 65 | 66 | @app.route('/onboard-user/refresh', methods=['GET']) 67 | def onboard_user_refresh(): 68 | if 'account_id' not in session: 69 | return redirect('/') 70 | 71 | account_id = session['account_id'] 72 | 73 | origin = ('https://' if request.is_secure else 'http://') + request.headers['host'] 74 | try: 75 | account_link = stripe.AccountLink.create( 76 | type='account_onboarding', 77 | account=account_id, 78 | refresh_url=f'{origin}/onboard-user/refresh', 79 | return_url=f'{origin}/success.html', 80 | ) 81 | return redirect(account_link_url) 82 | except Exception as e: 83 | return jsonify(error=str(e)), 403 84 | 85 | 86 | if __name__== '__main__': 87 | app.run(port=4242, debug=True) 88 | -------------------------------------------------------------------------------- /server/ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org/' 2 | 3 | gem 'dotenv' 4 | gem 'json' 5 | gem 'sinatra' 6 | gem 'stripe' 7 | -------------------------------------------------------------------------------- /server/ruby/README.md: -------------------------------------------------------------------------------- 1 | # Connect onboarding for Standard accounts 2 | 3 | A [Sinatra](http://sinatrarb.com/) implementation. 4 | 5 | ## Requirements 6 | * Ruby v2.4.5+ 7 | * [Configured .env file](../README.md) 8 | 9 | ## How to run 10 | 11 | 1. Install dependencies 12 | ``` 13 | bundle install 14 | ``` 15 | 16 | 2. Run the application 17 | ``` 18 | ruby server.rb 19 | ``` 20 | 21 | 3. Go to `localhost:4242` in your browser to see the demo 22 | -------------------------------------------------------------------------------- /server/ruby/server.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | require 'stripe' 3 | require 'sinatra' 4 | require 'dotenv' 5 | 6 | # Replace if using a different env file or config. 7 | Dotenv.load 8 | 9 | # Used for sample support, not required for production. 10 | Stripe.set_app_info( 11 | 'stripe-samples/connect-onboarding-for-standard', 12 | version: '0.0.1', 13 | url: 'https://github.com/stripe-samples' 14 | ) 15 | Stripe.api_version = '2020-08-27' 16 | Stripe.api_key = ENV['STRIPE_SECRET_KEY'] 17 | 18 | enable :sessions 19 | set :static, true 20 | set :public_folder, File.join(File.dirname(__FILE__), ENV['STATIC_DIR']) 21 | set :port, 4242 22 | set :bind, '0.0.0.0' 23 | 24 | helpers do 25 | def request_headers 26 | env.each_with_object({}) { |(k, v), acc| acc[Regexp.last_match(1).downcase] = v if k =~ /^http_(.*)/i; } 27 | end 28 | end 29 | 30 | get '/' do 31 | content_type 'text/html' 32 | send_file File.join(settings.public_folder, 'index.html') 33 | end 34 | 35 | post '/onboard-user' do 36 | content_type 'application/json' 37 | 38 | begin 39 | account = Stripe::Account.create(type: 'standard') 40 | 41 | session[:account_id] = account.id 42 | 43 | origin = request_headers['origin'] 44 | 45 | account_link = Stripe::AccountLink.create( 46 | type: 'account_onboarding', 47 | account: account.id, 48 | refresh_url: "#{origin}/onboard-user/refresh", 49 | return_url: "#{origin}/success.html" 50 | ) 51 | 52 | redirect account_link.url 53 | rescue => e 54 | puts e 55 | end 56 | end 57 | 58 | get '/onboard-user/refresh' do 59 | redirect '/' if session[:account_id].nil? 60 | 61 | account_id = session[:account_id] 62 | origin = "http://#{request_headers['host']}" 63 | 64 | account_link = Stripe::AccountLink.create( 65 | type: 'account_onboarding', 66 | account: account_id, 67 | refresh_url: "#{origin}/onboard-user/refresh", 68 | return_url: "#{origin}/success.html" 69 | ) 70 | 71 | redirect account_link.url 72 | end 73 | -------------------------------------------------------------------------------- /spec/server_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative './spec_helper.rb' 2 | 3 | RSpec.describe 'server endpoints' do 4 | it 'creates a new account and redirects to the account link URL' do 5 | response = RestClient.post( 6 | "#{SERVER_URL}/onboard-user", 7 | {}, 8 | { 9 | max_redirects: 0, 10 | origin: 'http://localhost:4242' 11 | } 12 | ) 13 | redirect_response = response.history.first 14 | expect(redirect_response.code).to eq(303) 15 | 16 | redirect_url = redirect_response.headers[:location] 17 | expect(redirect_url).to start_with('https://connect.stripe.com') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | require 'rest-client' 3 | require 'stripe' 4 | require 'dotenv' 5 | 6 | # This file was generated by the `rspec --init` command. Conventionally, all 7 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 8 | # The generated `.rspec` file contains `--require spec_helper` which will cause 9 | # this file to always be loaded, without a need to explicitly require it in any 10 | # files. 11 | # 12 | # Given that it is always loaded, you are encouraged to keep this file as 13 | # light-weight as possible. Requiring heavyweight dependencies from this file 14 | # will add to the boot time of your test suite on EVERY test run, even for an 15 | # individual file that may not need all of that loaded. Instead, consider making 16 | # a separate helper file that requires the additional dependencies and performs 17 | # the additional setup, and require it from the spec files that actually need 18 | # it. 19 | # 20 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 21 | RSpec.configure do |config| 22 | # rspec-expectations config goes here. You can use an alternate 23 | # assertion/expectation library such as wrong or the stdlib/minitest 24 | # assertions if you prefer. 25 | config.expect_with :rspec do |expectations| 26 | # This option will default to `true` in RSpec 4. It makes the `description` 27 | # and `failure_message` of custom matchers include text for helper methods 28 | # defined using `chain`, e.g.: 29 | # be_bigger_than(2).and_smaller_than(4).description 30 | # # => "be bigger than 2 and smaller than 4" 31 | # ...rather than: 32 | # # => "be bigger than 2" 33 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 34 | end 35 | 36 | # rspec-mocks config goes here. You can use an alternate test double 37 | # library (such as bogus or mocha) by changing the `mock_with` option here. 38 | config.mock_with :rspec do |mocks| 39 | # Prevents you from mocking or stubbing a method that does not exist on 40 | # a real object. This is generally recommended, and will default to 41 | # `true` in RSpec 4. 42 | mocks.verify_partial_doubles = true 43 | end 44 | 45 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 46 | # have no way to turn it off -- the option exists only for backwards 47 | # compatibility in RSpec 3). It causes shared context metadata to be 48 | # inherited by the metadata hash of host groups and examples, rather than 49 | # triggering implicit auto-inclusion in groups with matching metadata. 50 | config.shared_context_metadata_behavior = :apply_to_host_groups 51 | 52 | # The settings below are suggested to provide a good initial experience 53 | # with RSpec, but feel free to customize to your heart's content. 54 | =begin 55 | # This allows you to limit a spec run to individual examples or groups 56 | # you care about by tagging them with `:focus` metadata. When nothing 57 | # is tagged with `:focus`, all examples get run. RSpec also provides 58 | # aliases for `it`, `describe`, and `context` that include `:focus` 59 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 60 | config.filter_run_when_matching :focus 61 | 62 | # Allows RSpec to persist some state between runs in order to support 63 | # the `--only-failures` and `--next-failure` CLI options. We recommend 64 | # you configure your source control system to ignore this file. 65 | config.example_status_persistence_file_path = "spec/examples.txt" 66 | 67 | # Limits the available syntax to the non-monkey patched syntax that is 68 | # recommended. For more details, see: 69 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 70 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 71 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 72 | config.disable_monkey_patching! 73 | 74 | # This setting enables warnings. It's recommended, but in some cases may 75 | # be too noisy due to issues in dependencies. 76 | config.warnings = true 77 | 78 | # Many RSpec users commonly either run the entire suite or an individual 79 | # file, and it's useful to allow more verbose output when running an 80 | # individual spec file. 81 | if config.files_to_run.one? 82 | # Use the documentation formatter for detailed output, 83 | # unless a formatter has already been configured 84 | # (e.g. via a command-line flag). 85 | config.default_formatter = "doc" 86 | end 87 | 88 | # Print the 10 slowest examples and example groups at the 89 | # end of the spec run, to help surface which specs are running 90 | # particularly slow. 91 | config.profile_examples = 10 92 | 93 | # Run specs in random order to surface order dependencies. If you find an 94 | # order dependency and want to debug it, you can fix the order by providing 95 | # the seed, which is printed after each run. 96 | # --seed 1234 97 | config.order = :random 98 | 99 | # Seed global randomization in this process using the `--seed` CLI option. 100 | # Setting this allows you to use `--seed` to deterministically reproduce 101 | # test failures related to randomization by passing the same `--seed` value 102 | # as the one that triggered the failure. 103 | Kernel.srand config.seed 104 | =end 105 | end 106 | 107 | 108 | SERVER_URL = ENV.fetch('SERVER_URL', 'http://localhost:4242') 109 | Dotenv.load 110 | Stripe.api_key = ENV['STRIPE_SECRET_KEY'] 111 | Stripe.max_network_retries = 2 112 | Stripe.api_version = "2020-08-27" 113 | 114 | def server_url 115 | SERVER_URL 116 | end 117 | 118 | def get(path, *args, **kwargs) 119 | RestClient.get("#{SERVER_URL}#{path}", *args, **kwargs) 120 | end 121 | 122 | def get_json(path, *args, **kwargs) 123 | response = RestClient.get("#{SERVER_URL}#{path}", *args, **kwargs) 124 | JSON.parse(response.body) 125 | end 126 | 127 | def post_json(path, payload, **kwargs) 128 | defaults = {content_type: :json} 129 | response = RestClient.post( 130 | "#{SERVER_URL}#{path}", 131 | payload.to_json, 132 | defaults.merge(**kwargs) 133 | ) 134 | [JSON.parse(response.body), response.code] 135 | rescue => e 136 | begin 137 | [JSON.parse(e.http_body), e.http_code] 138 | rescue => e 139 | puts "Response:" 140 | p response 141 | throw "Failed to parse failed response" 142 | end 143 | end 144 | --------------------------------------------------------------------------------