stripe payment processor
This commit is contained in:
parent
d293d43392
commit
08e23cf0d1
1
Pipfile
1
Pipfile
@ -24,6 +24,7 @@ toml = "==0.10.0"
|
|||||||
typed-ast = "==1.4.1"
|
typed-ast = "==1.4.1"
|
||||||
Werkzeug = "==1.0.1"
|
Werkzeug = "==1.0.1"
|
||||||
wrapt = "==1.12.1"
|
wrapt = "==1.12.1"
|
||||||
|
stripe = "*"
|
||||||
|
|
||||||
[dev-packages]
|
[dev-packages]
|
||||||
|
|
||||||
|
46
Pipfile.lock
generated
46
Pipfile.lock
generated
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"hash": {
|
"hash": {
|
||||||
"sha256": "9b88525881f174f421ccb5b49fc34948053fb5d9d9eafa90bdec8ef0bfbc30ea"
|
"sha256": "8355b0bc9024432220ab4f05b2997f827af534691520249c6ff8bb2db9014dc8"
|
||||||
},
|
},
|
||||||
"pipfile-spec": 6,
|
"pipfile-spec": 6,
|
||||||
"requires": {
|
"requires": {
|
||||||
@ -31,6 +31,20 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.4"
|
"version": "==1.4"
|
||||||
},
|
},
|
||||||
|
"certifi": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
|
||||||
|
"sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
|
||||||
|
],
|
||||||
|
"version": "==2020.4.5.1"
|
||||||
|
},
|
||||||
|
"chardet": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||||
|
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||||
|
],
|
||||||
|
"version": "==3.0.4"
|
||||||
|
},
|
||||||
"click": {
|
"click": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
"sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
|
||||||
@ -62,6 +76,13 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==20.0.4"
|
"version": "==20.0.4"
|
||||||
},
|
},
|
||||||
|
"idna": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
|
||||||
|
"sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa"
|
||||||
|
],
|
||||||
|
"version": "==2.9"
|
||||||
|
},
|
||||||
"isort": {
|
"isort": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
|
"sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
|
||||||
@ -195,6 +216,14 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==2.5.2"
|
"version": "==2.5.2"
|
||||||
},
|
},
|
||||||
|
"requests": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee",
|
||||||
|
"sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6"
|
||||||
|
],
|
||||||
|
"markers": "python_version >= '3.0'",
|
||||||
|
"version": "==2.23.0"
|
||||||
|
},
|
||||||
"six": {
|
"six": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
|
||||||
@ -203,6 +232,14 @@
|
|||||||
"index": "pypi",
|
"index": "pypi",
|
||||||
"version": "==1.14.0"
|
"version": "==1.14.0"
|
||||||
},
|
},
|
||||||
|
"stripe": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:515fe2cc915e639468f30150a39c162fc0fb090256ae9d6a04e5022925d136f1",
|
||||||
|
"sha256:bdbbea632b8faa983c670db61debbe0bdb5802ef98fd0613a03aa466e56cdade"
|
||||||
|
],
|
||||||
|
"index": "pypi",
|
||||||
|
"version": "==2.48.0"
|
||||||
|
},
|
||||||
"toml": {
|
"toml": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
"sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
|
||||||
@ -240,6 +277,13 @@
|
|||||||
"markers": "implementation_name == 'cpython' and python_version < '3.8'",
|
"markers": "implementation_name == 'cpython' and python_version < '3.8'",
|
||||||
"version": "==1.4.1"
|
"version": "==1.4.1"
|
||||||
},
|
},
|
||||||
|
"urllib3": {
|
||||||
|
"hashes": [
|
||||||
|
"sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
|
||||||
|
"sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
|
||||||
|
],
|
||||||
|
"version": "==1.25.9"
|
||||||
|
},
|
||||||
"werkzeug": {
|
"werkzeug": {
|
||||||
"hashes": [
|
"hashes": [
|
||||||
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
|
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import stripe
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from dotenv import load_dotenv, find_dotenv
|
from dotenv import load_dotenv, find_dotenv
|
||||||
@ -23,11 +24,15 @@ app.config.from_mapping(
|
|||||||
MAIL_PASSWORD=os.environ.get("MAIL_PASSWORD", default=""),
|
MAIL_PASSWORD=os.environ.get("MAIL_PASSWORD", default=""),
|
||||||
MAIL_DEFAULT_SENDER=os.environ.get("MAIL_DEFAULT_SENDER", default="forest@nullhex.com"),
|
MAIL_DEFAULT_SENDER=os.environ.get("MAIL_DEFAULT_SENDER", default="forest@nullhex.com"),
|
||||||
|
|
||||||
STRIPE_API_VERSION=os.environ.get("STRIPE_API_VERSION", default=""),
|
STRIPE_API_VERSION=os.environ.get("STRIPE_API_VERSION", default="2020-03-02"),
|
||||||
STRIPE_SECRET_KEY=os.environ.get("STRIPE_SECRET_KEY", default=""),
|
STRIPE_SECRET_KEY=os.environ.get("STRIPE_SECRET_KEY", default=""),
|
||||||
STRIPE_PUBLISHABLE_KEY=os.environ.get("STRIPE_PUBLISHABLE_KEY", default="")
|
STRIPE_PUBLISHABLE_KEY=os.environ.get("STRIPE_PUBLISHABLE_KEY", default="")
|
||||||
|
#STRIPE_WEBHOOK_SECRET=os.environ.get("STRIPE_WEBHOOK_SECRET", default="")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
stripe.api_key = app.config['STRIPE_SECRET_KEY']
|
||||||
|
stripe.api_version = app.config['STRIPE_API_VERSION']
|
||||||
|
|
||||||
app.config['FLASK_MAIL_INSTANCE'] = Mail(app)
|
app.config['FLASK_MAIL_INSTANCE'] = Mail(app)
|
||||||
app.config['VIRTUALIZATION_MODEL'] = virt_model.MockVirtualization()
|
app.config['VIRTUALIZATION_MODEL'] = virt_model.MockVirtualization()
|
||||||
|
|
||||||
@ -35,11 +40,12 @@ from capsulflask import db
|
|||||||
|
|
||||||
db.init_app(app)
|
db.init_app(app)
|
||||||
|
|
||||||
from capsulflask import auth, landing, console
|
from capsulflask import auth, landing, console, stripe
|
||||||
|
|
||||||
app.register_blueprint(landing.bp)
|
app.register_blueprint(landing.bp)
|
||||||
app.register_blueprint(auth.bp)
|
app.register_blueprint(auth.bp)
|
||||||
app.register_blueprint(console.bp)
|
app.register_blueprint(console.bp)
|
||||||
|
app.register_blueprint(stripe.bp)
|
||||||
|
|
||||||
app.add_url_rule("/", endpoint="index")
|
app.add_url_rule("/", endpoint="index")
|
||||||
|
|
||||||
|
@ -5,8 +5,6 @@ from flask import Blueprint
|
|||||||
from flask import flash
|
from flask import flash
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
from flask import g
|
from flask import g
|
||||||
from flask import redirect
|
|
||||||
from flask import url_for
|
|
||||||
from flask import request
|
from flask import request
|
||||||
from flask import session
|
from flask import session
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
|
@ -23,7 +23,7 @@ class DBModel:
|
|||||||
return token
|
return token
|
||||||
|
|
||||||
def consume_token(self, token):
|
def consume_token(self, token):
|
||||||
self.cursor.execute("SELECT email FROM login_tokens WHERE token = %s", (token, ))
|
self.cursor.execute("SELECT email FROM login_tokens WHERE token = %s and created > (NOW() - INTERVAL '20 min')", (token, ))
|
||||||
rows = self.cursor.fetchall()
|
rows = self.cursor.fetchall()
|
||||||
if len(rows) > 0:
|
if len(rows) > 0:
|
||||||
email = rows[0][0]
|
email = rows[0][0]
|
||||||
@ -151,3 +151,34 @@ class DBModel:
|
|||||||
self.cursor.fetchall()
|
self.cursor.fetchall()
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def create_stripe_checkout_session(self, id, email, dollars):
|
||||||
|
self.cursor.execute("""
|
||||||
|
INSERT INTO stripe_checkout_sessions (id, email, dollars)
|
||||||
|
VALUES (%s, %s, %d)
|
||||||
|
""",
|
||||||
|
(id, email, dollars)
|
||||||
|
)
|
||||||
|
self.connection.commit()
|
||||||
|
|
||||||
|
def consume_stripe_checkout_session(self, id, dollars):
|
||||||
|
self.cursor.execute("SELECT email, dollars FROM stripe_checkout_sessions WHERE id = %s", (id,))
|
||||||
|
rows = self.cursor.fetchall()
|
||||||
|
if len(rows) > 0:
|
||||||
|
if int(rows[0][1]) != int(dollars):
|
||||||
|
print(f"""
|
||||||
|
Stripe sent us a completed checkout session with a different dollar amount than what we had recorded!!
|
||||||
|
stripe_checkout_session_id: {id}
|
||||||
|
account: {rows[0][0]}
|
||||||
|
Recorded amount: {int(rows[0][1])}
|
||||||
|
Stripe sent: {int(dollars)}
|
||||||
|
""")
|
||||||
|
# not sure what to do here. For now just log and do nothing.
|
||||||
|
self.cursor.execute( "DELETE FROM stripe_checkout_sessions WHERE id = %s", (id,) )
|
||||||
|
self.cursor.execute( "INSERT INTO payments (email, dollars) VALUES (%s, %d)", (rows[0][0], rows[0][1]) )
|
||||||
|
self.connection.commit()
|
||||||
|
return rows[0][0]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -62,6 +62,13 @@ CREATE TABLE login_tokens (
|
|||||||
PRIMARY KEY (email, created)
|
PRIMARY KEY (email, created)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
CREATE TABLE stripe_checkout_sessions (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
email TEXT REFERENCES accounts(email) ON DELETE RESTRICT,
|
||||||
|
created TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||||
|
dollars NUMERIC(8, 2) NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
INSERT INTO os_images (id, template_image_file_name, description)
|
INSERT INTO os_images (id, template_image_file_name, description)
|
||||||
VALUES ('alpine311', 'alpine-cloud-2020-04-18.qcow2', 'Alpine Linux 3.11'),
|
VALUES ('alpine311', 'alpine-cloud-2020-04-18.qcow2', 'Alpine Linux 3.11'),
|
||||||
('ubuntu18', 'ubuntu-18.04-minimal-cloudimg-amd64.img', 'Ubuntu 18.04 LTS (Bionic Beaver)'),
|
('ubuntu18', 'ubuntu-18.04-minimal-cloudimg-amd64.img', 'Ubuntu 18.04 LTS (Bionic Beaver)'),
|
||||||
|
BIN
capsulflask/static/capsul-product-image.png
Normal file
BIN
capsulflask/static/capsul-product-image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
@ -122,7 +122,7 @@ select {
|
|||||||
padding-right: 2em;
|
padding-right: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=text], textarea {
|
input[type=text], input[type=number], textarea {
|
||||||
font: calc(0.40rem + 1vmin) monospace;
|
font: calc(0.40rem + 1vmin) monospace;
|
||||||
border: 1px solid #777e73;
|
border: 1px solid #777e73;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
@ -152,6 +152,17 @@ input[type=submit], select {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input::-webkit-outer-spin-button,
|
||||||
|
input::-webkit-inner-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Firefox */
|
||||||
|
input[type=number] {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
h1, h2, h3, h4, h5 {
|
h1, h2, h3, h4, h5 {
|
||||||
font-size:calc(0.40rem + 1vmin);
|
font-size:calc(0.40rem + 1vmin);
|
||||||
margin: initial;
|
margin: initial;
|
||||||
@ -195,7 +206,7 @@ td {
|
|||||||
|
|
||||||
.code {
|
.code {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.5em 2em;
|
padding: 0.5em 1.2em;
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
border: 1px solid #777e73;
|
border: 1px solid #777e73;
|
||||||
background: #bdc7b810;
|
background: #bdc7b810;
|
||||||
|
@ -1 +1,116 @@
|
|||||||
import stripe
|
import stripe
|
||||||
|
import json
|
||||||
|
import decimal
|
||||||
|
|
||||||
|
|
||||||
|
from flask import Blueprint
|
||||||
|
from flask import request
|
||||||
|
from flask import current_app
|
||||||
|
from flask import session
|
||||||
|
from flask import redirect
|
||||||
|
from flask import url_for
|
||||||
|
from flask import jsonify
|
||||||
|
from flask import flash
|
||||||
|
from flask import render_template
|
||||||
|
from werkzeug.exceptions import abort
|
||||||
|
|
||||||
|
from capsulflask.auth import account_required
|
||||||
|
|
||||||
|
from capsulflask.db import get_model
|
||||||
|
|
||||||
|
bp = Blueprint("stripe", __name__, url_prefix="/stripe")
|
||||||
|
|
||||||
|
@bp.route("/", methods=("GET", "POST"))
|
||||||
|
@account_required
|
||||||
|
def index():
|
||||||
|
|
||||||
|
stripe_checkout_session_id=None
|
||||||
|
|
||||||
|
if request.method == "POST":
|
||||||
|
errors = list()
|
||||||
|
if "dollars" not in request.form:
|
||||||
|
errors.append("dollars is required")
|
||||||
|
elif decimal.Decimal(request.form["dollars"]) < decimal.Decimal(1):
|
||||||
|
errors.append("dollars must be >= 1")
|
||||||
|
|
||||||
|
if len(errors) == 0:
|
||||||
|
|
||||||
|
print(f"creating stripe checkout session for {session['account']}, ${request.form['dollars']}")
|
||||||
|
|
||||||
|
checkout_session = stripe.checkout.Session.create(
|
||||||
|
success_url=current_app.config['BASE_URL'] + "/stripe/success?session_id={CHECKOUT_SESSION_ID}",
|
||||||
|
cancel_url=current_app.config['BASE_URL'] + "/stripe",
|
||||||
|
payment_method_types=["card"],
|
||||||
|
customer_email=session["account"],
|
||||||
|
line_items=[
|
||||||
|
{
|
||||||
|
"name": "Capsul Cloud Compute",
|
||||||
|
"images": [current_app.config['BASE_URL']+"/static/capsul-product-image.png"],
|
||||||
|
"quantity": 1,
|
||||||
|
"currency": "usd",
|
||||||
|
"amount": request.form["dollars"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
stripe_checkout_session_id = checkout_session['id']
|
||||||
|
|
||||||
|
print(f"stripe_checkout_session_id={stripe_checkout_session_id} ( {session['account']}, ${request.form['dollars']} )")
|
||||||
|
|
||||||
|
get_model().create_stripe_checkout_session(stripe_checkout_session_id, session["account"], request.form["dollars"])
|
||||||
|
|
||||||
|
for error in errors:
|
||||||
|
flash(error)
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
"stripe.html",
|
||||||
|
stripe_checkout_session_id=stripe_checkout_session_id,
|
||||||
|
stripe_public_key=current_app.config["STRIPE_PUBLISHABLE_KEY"]
|
||||||
|
)
|
||||||
|
|
||||||
|
@bp.route("/success", methods=("GET",))
|
||||||
|
def success():
|
||||||
|
stripe_checkout_session_id = request.args.get('session_id')
|
||||||
|
if not stripe_checkout_session_id:
|
||||||
|
print("/stripe/success returned 400: missing required URL parameter session_id")
|
||||||
|
abort(400, "missing required URL parameter session_id")
|
||||||
|
else:
|
||||||
|
checkout_session = stripe.checkout.Session.retrieve(stripe_checkout_session_id)
|
||||||
|
if checkout_session and 'display_items' in checkout_session:
|
||||||
|
dollars = checkout_session['display_items'][0]['amount']
|
||||||
|
|
||||||
|
#consume_stripe_checkout_session deletes the checkout session row and inserts a payment row
|
||||||
|
# its ok to call consume_stripe_checkout_session more than once because it only takes an action if the session exists
|
||||||
|
success_account = get_model().consume_stripe_checkout_session(stripe_checkout_session_id, dollars)
|
||||||
|
if success_account:
|
||||||
|
print(f"{success_account} paid ${dollars} successfully (stripe_checkout_session_id={stripe_checkout_session_id})")
|
||||||
|
|
||||||
|
return redirect(url_for("console.account_balance"))
|
||||||
|
|
||||||
|
# I don't think the webhook is needed
|
||||||
|
# @bp.route("/webhook", methods=("POST",))
|
||||||
|
# def webhook():
|
||||||
|
|
||||||
|
# request_data = json.loads(request.data)
|
||||||
|
# signature = request.headers.get('stripe-signature')
|
||||||
|
# try:
|
||||||
|
# event = stripe.Webhook.construct_event(
|
||||||
|
# payload=request_data,
|
||||||
|
# sig_header=signature,
|
||||||
|
# secret=current_app.config['STRIPE_WEBHOOK_SECRET']
|
||||||
|
# )
|
||||||
|
# if event['type'] == 'checkout.session.completed':
|
||||||
|
# dollars = event['data']['object']['display_items'][0]['amount']
|
||||||
|
# stripe_checkout_session_id = event['data']['object']['id']
|
||||||
|
|
||||||
|
# #consume_stripe_checkout_session deletes the checkout session row and inserts a payment row
|
||||||
|
# # its ok to call consume_stripe_checkout_session more than once because it only takes an action if the session exists
|
||||||
|
# get_model().consume_stripe_checkout_session(stripe_checkout_session_id, dollars)
|
||||||
|
|
||||||
|
# return jsonify({'status': 'success'})
|
||||||
|
# except ValueError as e:
|
||||||
|
# print("/stripe/webhook returned 400: bad request", e)
|
||||||
|
# abort(400, "bad request")
|
||||||
|
# except stripe.error.SignatureVerificationError:
|
||||||
|
# print("/stripe/webhook returned 400: invalid signature")
|
||||||
|
# abort(400, "invalid signature")
|
||||||
|
|
@ -38,10 +38,10 @@
|
|||||||
<h1>PAYMENT OPTIONS</h1>
|
<h1>PAYMENT OPTIONS</h1>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
<a href="/console/stripe">Add funds with Credit/Debit (stripe)</a>
|
<a href="/stripe">Add funds with Credit/Debit (stripe)</a>
|
||||||
<ul><li>notice: stripe will load nonfree javascript </li></ul>
|
<ul><li>notice: stripe will load nonfree javascript </li></ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="/console/btcpay">Add funds with Bitcoin/Litecoin/Monero (btcpay)</a></li>
|
<li><a href="/btcpay">Add funds with Bitcoin/Litecoin/Monero (btcpay)</a></li>
|
||||||
|
|
||||||
<li>Cash: email treasurer@cyberia.club</li>
|
<li>Cash: email treasurer@cyberia.club</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
<meta name="Description" content="Cyberia Capsul">
|
<meta name="Description" content="Cyberia Capsul">
|
||||||
|
{% block head %}{% endblock %}
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
39
capsulflask/templates/stripe.html
Normal file
39
capsulflask/templates/stripe.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block title %}Capsuls{% endblock %}
|
||||||
|
|
||||||
|
{% block head %}<script src="https://js.stripe.com/v3/"></script>{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="third-margin">
|
||||||
|
<h1>PAY WITH STRIPE</h1>
|
||||||
|
</div>
|
||||||
|
<div class="row half-margin">
|
||||||
|
<form method="post">
|
||||||
|
<div class="row justify-start">
|
||||||
|
<label for="content">$</label>
|
||||||
|
<input type="number" id="name" name="name"></input>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-end">
|
||||||
|
<input type="submit" value="Pay With Stripe">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if stripe_checkout_session_id %}
|
||||||
|
<script>
|
||||||
|
Stripe("{{ stripe_public_key }}")
|
||||||
|
.redirectToCheckout({
|
||||||
|
sessionId: "{{ stripe_checkout_session_id }}",
|
||||||
|
})
|
||||||
|
.then(function(result) {
|
||||||
|
if (result.error) {
|
||||||
|
alert("Stripe.redirectToCheckout() failed with: " + result.error.message)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block pagesource %}/templates/stripe.html{% endblock %}
|
Loading…
Reference in New Issue
Block a user