Procházet zdrojové kódy

Stripe marketplace sandbox

Benoît Hubert před 8 roky
revize
8bbbf170d0

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+
+node_modules/
+database.sqlite
+config.js

+ 5 - 0
config.sample.js

@@ -0,0 +1,5 @@
+module.exports = {
+  stripeKey: 'sk_test_YOURSTRIPEKEY',
+  stripeClientId: 'ca_YOURAPPCLIENTID',
+  port: 8000
+};

+ 16 - 0
migrations/001-initial-schema.sql

@@ -0,0 +1,16 @@
+-- Up
+CREATE TABLE orders (id INTEGER PRIMARY KEY, orderIncrement INTEGER);
+CREATE TABLE access_tokens (
+	id INTEGER PRIMARY KEY,
+	access_token VARCHAR(255),
+	livemode BOOLEAN,
+	token_type VARCHAR(32),
+	stripe_publishable_key VARCHAR(128),
+	stripe_user_id VARCHAR(128),
+	scope VARCHAR(32),
+	refresh_token VARCHAR(128)
+);
+
+-- Down
+DROP TABLE orders;
+DROP TABLE access_tokens;

+ 1 - 0
orderRef.json

@@ -0,0 +1 @@
+{"ref":9}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1906 - 0
package-lock.json


+ 12 - 0
package.json

@@ -0,0 +1,12 @@
+{
+	"name": "stripe-dummy-marketplace",
+	"dependencies": {
+		"bluebird": "^3.5.1",
+		"express": "^4.16.2",
+		"pug": "^2.0.0-rc.4",
+		"request": "^2.83.0",
+		"request-promise": "^4.2.2",
+		"sqlite": "^2.8.0",
+		"stripe": "^5.3.0"
+	}
+}

+ 291 - 0
public/css/main.css

@@ -0,0 +1,291 @@
+/*! HTML5 Boilerplate v6.0.1 | MIT License | https://html5boilerplate.com/ */
+
+/*
+ * What follows is the result of much research on cross-browser styling.
+ * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
+ * Kroc Camen, and the H5BP dev community and team.
+ */
+
+/* ==========================================================================
+   Base styles: opinionated defaults
+   ========================================================================== */
+
+html {
+    color: #222;
+    font-size: 1em;
+    line-height: 1.4;
+}
+
+/*
+ * Remove text-shadow in selection highlight:
+ * https://twitter.com/miketaylr/status/12228805301
+ *
+ * Vendor-prefixed and regular ::selection selectors cannot be combined:
+ * https://stackoverflow.com/a/16982510/7133471
+ *
+ * Customize the background color to match your design.
+ */
+
+::-moz-selection {
+    background: #b3d4fc;
+    text-shadow: none;
+}
+
+::selection {
+    background: #b3d4fc;
+    text-shadow: none;
+}
+
+/*
+ * A better looking default horizontal rule
+ */
+
+hr {
+    display: block;
+    height: 1px;
+    border: 0;
+    border-top: 1px solid #ccc;
+    margin: 1em 0;
+    padding: 0;
+}
+
+/*
+ * Remove the gap between audio, canvas, iframes,
+ * images, videos and the bottom of their containers:
+ * https://github.com/h5bp/html5-boilerplate/issues/440
+ */
+
+audio,
+canvas,
+iframe,
+img,
+svg,
+video {
+    vertical-align: middle;
+}
+
+/*
+ * Remove default fieldset styles.
+ */
+
+fieldset {
+    border: 0;
+    margin: 0;
+    padding: 0;
+}
+
+/*
+ * Allow only vertical resizing of textareas.
+ */
+
+textarea {
+    resize: vertical;
+}
+
+/* ==========================================================================
+   Browser Upgrade Prompt
+   ========================================================================== */
+
+.browserupgrade {
+    margin: 0.2em 0;
+    background: #ccc;
+    color: #000;
+    padding: 0.2em 0;
+}
+
+/* ==========================================================================
+   Author's custom styles
+   ========================================================================== */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* ==========================================================================
+   Helper classes
+   ========================================================================== */
+
+/*
+ * Hide visually and from screen readers
+ */
+
+.hidden {
+    display: none !important;
+}
+
+/*
+ * Hide only visually, but have it available for screen readers:
+ * https://snook.ca/archives/html_and_css/hiding-content-for-accessibility
+ *
+ * 1. For long content, line feeds are not interpreted as spaces and small width
+ *    causes content to wrap 1 word per line:
+ *    https://medium.com/@jessebeach/beware-smushed-off-screen-accessible-text-5952a4c2cbfe
+ */
+
+.visuallyhidden {
+    border: 0;
+    clip: rect(0 0 0 0);
+    -webkit-clip-path: inset(50%);
+    clip-path: inset(50%);
+    height: 1px;
+    margin: -1px;
+    overflow: hidden;
+    padding: 0;
+    position: absolute;
+    width: 1px;
+    white-space: nowrap; /* 1 */
+}
+
+/*
+ * Extends the .visuallyhidden class to allow the element
+ * to be focusable when navigated to via the keyboard:
+ * https://www.drupal.org/node/897638
+ */
+
+.visuallyhidden.focusable:active,
+.visuallyhidden.focusable:focus {
+    clip: auto;
+    -webkit-clip-path: none;
+    clip-path: none;
+    height: auto;
+    margin: 0;
+    overflow: visible;
+    position: static;
+    width: auto;
+    white-space: inherit;
+}
+
+/*
+ * Hide visually and from screen readers, but maintain layout
+ */
+
+.invisible {
+    visibility: hidden;
+}
+
+/*
+ * Clearfix: contain floats
+ *
+ * For modern browsers
+ * 1. The space content is one way to avoid an Opera bug when the
+ *    `contenteditable` attribute is included anywhere else in the document.
+ *    Otherwise it causes space to appear at the top and bottom of elements
+ *    that receive the `clearfix` class.
+ * 2. The use of `table` rather than `block` is only necessary if using
+ *    `:before` to contain the top-margins of child elements.
+ */
+
+.clearfix:before,
+.clearfix:after {
+    content: " "; /* 1 */
+    display: table; /* 2 */
+}
+
+.clearfix:after {
+    clear: both;
+}
+
+/* ==========================================================================
+   EXAMPLE Media Queries for Responsive Design.
+   These examples override the primary ('mobile first') styles.
+   Modify as content requires.
+   ========================================================================== */
+
+@media only screen and (min-width: 35em) {
+    /* Style adjustments for viewports that meet the condition */
+}
+
+@media print,
+       (-webkit-min-device-pixel-ratio: 1.25),
+       (min-resolution: 1.25dppx),
+       (min-resolution: 120dpi) {
+    /* Style adjustments for high resolution devices */
+}
+
+/* ==========================================================================
+   Print styles.
+   Inlined to avoid the additional HTTP request:
+   http://www.phpied.com/delay-loading-your-print-css/
+   ========================================================================== */
+
+@media print {
+    *,
+    *:before,
+    *:after {
+        background: transparent !important;
+        color: #000 !important; /* Black prints faster:
+                                   http://www.sanbeiji.com/archives/953 */
+        box-shadow: none !important;
+        text-shadow: none !important;
+    }
+
+    a,
+    a:visited {
+        text-decoration: underline;
+    }
+
+    a[href]:after {
+        content: " (" attr(href) ")";
+    }
+
+    abbr[title]:after {
+        content: " (" attr(title) ")";
+    }
+
+    /*
+     * Don't show links that are fragment identifiers,
+     * or use the `javascript:` pseudo protocol
+     */
+
+    a[href^="#"]:after,
+    a[href^="javascript:"]:after {
+        content: "";
+    }
+
+    pre {
+        white-space: pre-wrap !important;
+    }
+    pre,
+    blockquote {
+        border: 1px solid #999;
+        page-break-inside: avoid;
+    }
+
+    /*
+     * Printing Tables:
+     * http://css-discuss.incutio.com/wiki/Printing_Tables
+     */
+
+    thead {
+        display: table-header-group;
+    }
+
+    tr,
+    img {
+        page-break-inside: avoid;
+    }
+
+    p,
+    h2,
+    h3 {
+        orphans: 3;
+        widows: 3;
+    }
+
+    h2,
+    h3 {
+        page-break-after: avoid;
+    }
+}

+ 447 - 0
public/css/normalize.css

@@ -0,0 +1,447 @@
+/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */
+
+/* Document
+   ========================================================================== */
+
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in
+ *    IE on Windows Phone and in iOS.
+ */
+
+html {
+  line-height: 1.15; /* 1 */
+  -ms-text-size-adjust: 100%; /* 2 */
+  -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+
+/**
+ * Remove the margin in all browsers (opinionated).
+ */
+
+body {
+  margin: 0;
+}
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+article,
+aside,
+footer,
+header,
+nav,
+section {
+  display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ * 1. Add the correct display in IE.
+ */
+
+figcaption,
+figure,
+main { /* 1 */
+  display: block;
+}
+
+/**
+ * Add the correct margin in IE 8.
+ */
+
+figure {
+  margin: 1em 40px;
+}
+
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+
+/**
+ * 1. Remove the gray background on active links in IE 10.
+ * 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
+ */
+
+a {
+  background-color: transparent; /* 1 */
+  -webkit-text-decoration-skip: objects; /* 2 */
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57- and Firefox 39-.
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Prevent the duplicate application of `bolder` by the next rule in Safari 6.
+ */
+
+b,
+strong {
+  font-weight: inherit;
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+
+b,
+strong {
+  font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font style in Android 4.3-.
+ */
+
+dfn {
+  font-style: italic;
+}
+
+/**
+ * Add the correct background and color in IE 9-.
+ */
+
+mark {
+  background-color: #ff0;
+  color: #000;
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+
+small {
+  font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+audio,
+video {
+  display: inline-block;
+}
+
+/**
+ * Add the correct display in iOS 4-7.
+ */
+
+audio:not([controls]) {
+  display: none;
+  height: 0;
+}
+
+/**
+ * Remove the border on images inside links in IE 10-.
+ */
+
+img {
+  border-style: none;
+}
+
+/**
+ * Hide the overflow in IE.
+ */
+
+svg:not(:root) {
+  overflow: hidden;
+}
+
+/* Forms
+   ========================================================================== */
+
+/**
+ * 1. Change the font styles in all browsers (opinionated).
+ * 2. Remove the margin in Firefox and Safari.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: sans-serif; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+
+button,
+input { /* 1 */
+  overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+
+button,
+select { /* 1 */
+  text-transform: none;
+}
+
+/**
+ * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
+ *    controls in Android 4.
+ * 2. Correct the inability to style clickable types in iOS and Safari.
+ */
+
+button,
+html [type="button"], /* 1 */
+[type="reset"],
+[type="submit"] {
+  -webkit-appearance: button; /* 2 */
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+
+button::-moz-focus-inner,
+[type="button"]::-moz-focus-inner,
+[type="reset"]::-moz-focus-inner,
+[type="submit"]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+
+button:-moz-focusring,
+[type="button"]:-moz-focusring,
+[type="reset"]:-moz-focusring,
+[type="submit"]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+
+fieldset {
+  padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+
+legend {
+  box-sizing: border-box; /* 1 */
+  color: inherit; /* 2 */
+  display: table; /* 1 */
+  max-width: 100%; /* 1 */
+  padding: 0; /* 3 */
+  white-space: normal; /* 1 */
+}
+
+/**
+ * 1. Add the correct display in IE 9-.
+ * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+
+progress {
+  display: inline-block; /* 1 */
+  vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Remove the default vertical scrollbar in IE.
+ */
+
+textarea {
+  overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10-.
+ * 2. Remove the padding in IE 10-.
+ */
+
+[type="checkbox"],
+[type="radio"] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+
+[type="number"]::-webkit-inner-spin-button,
+[type="number"]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+
+[type="search"] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
+ */
+
+[type="search"]::-webkit-search-cancel-button,
+[type="search"]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+
+/*
+ * Add the correct display in IE 9-.
+ * 1. Add the correct display in Edge, IE, and Firefox.
+ */
+
+details, /* 1 */
+menu {
+  display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+
+summary {
+  display: list-item;
+}
+
+/* Scripting
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 9-.
+ */
+
+canvas {
+  display: inline-block;
+}
+
+/**
+ * Add the correct display in IE.
+ */
+
+template {
+  display: none;
+}
+
+/* Hidden
+   ========================================================================== */
+
+/**
+ * Add the correct display in IE 10-.
+ */
+
+[hidden] {
+  display: none;
+}

+ 32 - 0
public/css/styles.css

@@ -0,0 +1,32 @@
+body {
+	background: #f4f4f4;
+	font-family: Arial, Helvetica;
+}
+.container {
+  margin: 150px auto;
+	background: #fff;
+	border: 1px solid #eee;
+	padding: 30px;
+	width: 600px;
+}
+button {
+  padding: 10px;
+  color: white;
+  border: none;
+  border-radius: 3px;
+}
+.blue {
+  background: #24d;
+}
+.red {
+  background: #d42;
+}
+input {
+  border: 1px solid #ddd;  
+  padding: 8px;
+  border-radius: 3px;
+}
+a {
+  text-decoration: none;
+  color: #4ac;
+}

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 4 - 0
public/js/jquery-3.2.1.min.js


+ 20 - 0
public/js/script.js

@@ -0,0 +1,20 @@
+(function($) {
+	$(document).ready(() => {
+    $('a.catch').click(e => {
+      const link = $(e.target);
+      console.log(link);
+      e.preventDefault();
+      $.ajax({
+        type: link.data('method'),
+        url: link.attr('href'),
+        dataType: 'json',
+        success: data => {
+          console.log(data);
+        },
+        error: () => {
+          console.log(arguments);
+        }
+      })
+    })
+	});
+})(jQuery)

+ 153 - 0
routes.js

@@ -0,0 +1,153 @@
+const config     = require('./config');
+const stripe     = require('stripe')(config.stripeKey);
+const db         = require('sqlite');
+const request    = require('request-promise');
+const fs         = require('fs');
+
+function lastOrder() {
+  let _ref;
+  return fs.readFileAsync('orderRef.json')
+  .then(buf => JSON.parse(buf.toString()))
+  .then(json => {
+    json.ref++;
+    _ref = json.ref;
+    return fs.writeFileAsync('orderRef.json', JSON.stringify(json))
+    .then(() => ('order-' + _ref));
+  })
+}
+
+
+
+/**
+ * Send index page
+ */
+function getIndex(req, res)  {
+
+  // Build Stripe Authorization URL
+  const authUrl = 'https://connect.stripe.com/oauth/authorize?response_type=code&client_id=' +
+    config.stripeClientId + '&scope=read_write&redirect_uri=http://localhost:' + config.port + '/stripe-connect';
+  
+  // Render template
+  res.render('index', {
+    authUrl
+  });
+}
+
+
+var mappers = {
+  string: v => ("'" + v.replace(/'/g, '\'') + "'"),
+  boolean: v => (v ? 1 : 0)
+};
+
+function buildInsertQuery(table, fields) {
+  const keys = Object.keys(fields);
+  const values = Object.values(fields);
+
+  const valuesString = values.map(v => (
+    mappers[typeof v] ? mappers[typeof v](v) : v
+  ));
+
+  return 'INSERT INTO ' + table + ' (' +
+    keys.join(',') + ') VALUES(' +
+    valuesString + ')';
+}
+
+/**
+ * Handle return from Stripe Authorization page
+ */
+function getStripeCallback(req, res) {
+  // res.json(req.query);
+  // Sending to slack channel
+  request({
+    uri: 'https://connect.stripe.com/oauth/token',
+    method: "POST",
+    json: {
+      client_secret: config.stripeKey,
+      grant_type: 'authorization_code',
+      code: req.query.code
+    }
+  })
+  .then(response => {
+    const query = buildInsertQuery('access_tokens', response);
+    console.log('stripe response', response, query);
+    db.get(query)
+    .then(result => {
+      console.log(result);
+      res.json({ success: true });
+    })
+  })
+  .catch(err => {
+    console.log(err);
+  });
+}
+
+/**
+ * Create a Stripe Charge from Stripe Checkout form
+ */
+function postStripeCharge(req, res) {
+  const { stripeEmail, stripeToken, stripeTokenType } = req.body;
+  lastOrder()
+  .then(transfer_group => {
+    console.log('got last order ref', transfer_group)
+    return stripe.charges.create({
+      amount: 999,
+      currency: "eur",
+      description: "Example charge",
+      source: stripeToken,
+      transfer_group,
+      destination: {
+        account: 'acct_1BJQ2TCxNRnLITys'
+      }
+    })
+  })
+  .then(charge => {
+    res.json(charge);
+  })
+  .catch(err => {
+    console.log(err)
+    res.send(err.toString());
+  });
+}
+
+function postStripeImmediateCharge(req, res) {
+  stripe.charges.create({
+    amount: 2085,
+    description: "Immediate charge",
+    currency: "eur",
+    source: 'tok_bypassPending'
+  })
+  .then(charge => {
+    console.log('charge created', charge);
+    res.json(charge);
+  })
+}
+
+function postStripeTransfer(req, res) {
+  db.get('SELECT * from access_tokens WHERE id=2')
+  .then(record => {
+    // res.json(record);
+    console.log(record, {
+      amount: 100,
+      currency: "eur",
+      destination: 'acct_1BHuOvI6vDethKu9', //  record.stripe_user_id,
+      transfer_group: "order-7",
+    });
+    stripe.transfers.create({
+      amount: 100,
+      currency: "eur",
+      destination: record.stripe_user_id,
+      transfer_group: "order-7",
+    }).then(function(transfer) {
+      // asynchronously called
+      res.json({ transfer });
+    });
+  })
+}
+
+module.exports = {
+  getIndex,
+  getStripeCallback,
+  postStripeCharge,
+  postStripeImmediateCharge,
+  postStripeTransfer
+};

+ 37 - 0
server.js

@@ -0,0 +1,37 @@
+const config     = require('./config');
+const express    = require('express');
+const bodyParser = require('body-parser');
+const fs         = require('fs');
+const db         = require('sqlite');
+const app        = express();
+const routes     = require('./routes');
+const Promise    = require('bluebird');
+Promise.promisifyAll(fs);
+
+
+/**
+ * Express app setup
+ */
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded());
+app.use(express.static(__dirname + '/public'));
+app.set('view engine', 'pug');
+
+
+/**
+ * Express routes setup
+ */
+app.get('/', routes.getIndex);
+app.get('/stripe-connect', routes.getStripeCallback);
+app.post('/stripe-charge', routes.postStripeCharge);
+app.post('/stripe-immediate-charge', routes.postStripeImmediateCharge);
+app.post('/stripe-transfer', routes.postStripeTransfer);
+
+
+Promise.resolve()
+  // First, try connect to the database
+  .then(() => db.open('./database.sqlite', { Promise }))
+  .then(() => db.migrate({ force: 'last' }))
+  .catch(err => console.error(err.stack))
+  // Finally, launch Node.js app
+  .finally(() => app.listen(config.port));

+ 28 - 0
views/index.pug

@@ -0,0 +1,28 @@
+doctype html
+html.no-js(lang="")
+  head
+    meta(charset="utf-8")
+    meta(http-equiv="x-ua-compatible", content="ie=edge")
+    title
+    meta(name="description", content="")
+    meta(name="viewport", content="width=device-width, initial-scale=1")
+    link(rel="manifest", href="site.webmanifest")
+    link(rel="apple-touch-icon", href="icon.png")
+    // Place favicon.ico in the root directory
+    link(rel="stylesheet", href="css/normalize.css")
+    link(rel="stylesheet", href="css/main.css")
+    link(rel="stylesheet", href="css/styles.css")
+    script(type="text/javascript", src="js/jquery-3.2.1.min.js")
+    script(type="text/javascript", src="js/script.js")
+  body
+    .container
+      h1 Stripe Marketplace
+      p
+        a(href=authUrl) Stripe Authorization
+      p
+        a(class="catch", href="/stripe-transfer", data-method="POST") Stripe Transfer
+      p
+        a(class="catch", href="/stripe-immediate-charge", data-method="POST") Immediate 20€ Charge
+      p
+        form(action="/stripe-charge", method="POST")
+          script.stripe-button(src="https://checkout.stripe.com/checkout.js", data-key="pk_test_XKPhAkrlFi4kLGIODkRSxP8s", data-amount="999", data-name="Demo Site", data-description="Widget", data-image="https://stripe.com/img/documentation/checkout/marketplace.png", data-locale="auto", data-zip-code="true", data-currency="eur")