forked from 3wordchant/capsul-flask
btcpay working! added bitpay client code to source tree to fix a bug
fixed a stripe race condition added account balance warning to account balance page
This commit is contained in:
@ -2,6 +2,7 @@ import stripe
|
||||
import json
|
||||
import time
|
||||
import decimal
|
||||
from time import sleep
|
||||
|
||||
from flask import Blueprint
|
||||
from flask import request
|
||||
@ -55,7 +56,7 @@ def btcpay_payment():
|
||||
currency="USD",
|
||||
itemDesc="Capsul Cloud Compute",
|
||||
transactionSpeed="high",
|
||||
redirectURL=f"{current_app.config['BASE_URL']}/account-balance",
|
||||
redirectURL=f"{current_app.config['BASE_URL']}/console/account-balance",
|
||||
notificationURL=f"{current_app.config['BASE_URL']}/payment/btcpay/webhook"
|
||||
))
|
||||
# print(invoice)
|
||||
@ -89,16 +90,18 @@ def btcpay_webhook():
|
||||
|
||||
dollars = invoice['price']
|
||||
|
||||
if invoice['status'] == "paid" or invoice['status'] == "confirmed":
|
||||
if invoice['status'] == "paid" or invoice['status'] == "confirmed" or invoice['status'] == "complete":
|
||||
success_account = get_model().consume_payment_session("btcpay", invoice_id, dollars)
|
||||
|
||||
if success_account:
|
||||
print(f"{success_account} paid ${dollars} successfully (btcpay_invoice_id={invoice_id})")
|
||||
|
||||
elif invoice['status'] == "complete":
|
||||
if invoice['status'] == "complete":
|
||||
get_model().btcpay_invoice_resolved(invoice_id, True)
|
||||
elif invoice['status'] == "expired" or invoice['status'] == "invalid":
|
||||
get_model().btcpay_invoice_resolved(invoice_id, False)
|
||||
|
||||
return {"msg": "ok"}, 200
|
||||
|
||||
|
||||
|
||||
@ -122,7 +125,7 @@ def stripe_payment():
|
||||
success_url=current_app.config['BASE_URL'] + "/payment/stripe/success?session_id={CHECKOUT_SESSION_ID}",
|
||||
cancel_url=current_app.config['BASE_URL'] + "/payment/stripe",
|
||||
payment_method_types=["card"],
|
||||
customer_email=session["account"],
|
||||
#customer_email=session["account"],
|
||||
line_items=[
|
||||
{
|
||||
"name": "Capsul Cloud Compute",
|
||||
@ -139,6 +142,17 @@ def stripe_payment():
|
||||
|
||||
get_model().create_payment_session("stripe", stripe_checkout_session_id, session["account"], dollars)
|
||||
|
||||
# We can't do this because stripe requires a bunch of server-authenticated data to be sent in the hash
|
||||
# of the URL. I briefly looked into reverse-engineering their proprietary javascript in order to try to figure out
|
||||
# how it works and gave up after I discovered that it would require multiple complex interactions with stripe's
|
||||
# servers, and it looked like they were trying to make what I was trying to do impossible.
|
||||
|
||||
# I never tried running the stripe proprietary javascript in a headless brower and passing the hash from the
|
||||
# headless browser to the client, but I suspect it might not work anyway because they probably have thier tracking
|
||||
# cookie info in there somewhere, and if the cookie doesn't match they may refuse to display the page.
|
||||
|
||||
#return redirect(f"https://checkout.stripe.com/pay/{stripe_checkout_session_id}")
|
||||
|
||||
for error in errors:
|
||||
flash(error)
|
||||
|
||||
@ -148,6 +162,31 @@ def stripe_payment():
|
||||
stripe_public_key=current_app.config["STRIPE_PUBLISHABLE_KEY"]
|
||||
)
|
||||
|
||||
def validate_stripe_checkout_session(stripe_checkout_session_id):
|
||||
checkout_session_completed_events = stripe.Event.list(
|
||||
type='checkout.session.completed',
|
||||
created={
|
||||
# Check for events created in the last half hour
|
||||
'gte': int(time.time() - (30 * 60)),
|
||||
},
|
||||
)
|
||||
|
||||
for event in checkout_session_completed_events.auto_paging_iter():
|
||||
checkout_session = event['data']['object']
|
||||
|
||||
if checkout_session and 'id' in checkout_session and checkout_session['id'] == stripe_checkout_session_id:
|
||||
cents = checkout_session['display_items'][0]['amount']
|
||||
dollars = decimal.Decimal(cents)/100
|
||||
|
||||
#consume_payment_session deletes the checkout session row and inserts a payment row
|
||||
# its ok to call consume_payment_session more than once because it only takes an action if the session exists
|
||||
success_email = get_model().consume_payment_session("stripe", stripe_checkout_session_id, dollars)
|
||||
|
||||
if success_email:
|
||||
return dict(email=success_email, dollars=dollars)
|
||||
|
||||
return None
|
||||
|
||||
@bp.route("/stripe/success", methods=("GET",))
|
||||
def success():
|
||||
stripe_checkout_session_id = request.args.get('session_id')
|
||||
@ -155,29 +194,14 @@ def success():
|
||||
print("/payment/stripe/success returned 400: missing required URL parameter session_id")
|
||||
abort(400, "missing required URL parameter session_id")
|
||||
else:
|
||||
checkout_session_completed_events = stripe.Event.list(
|
||||
type='checkout.session.completed',
|
||||
created={
|
||||
# Check for events created in the last half hour
|
||||
'gte': int(time.time() - (30 * 60)),
|
||||
},
|
||||
)
|
||||
|
||||
for event in checkout_session_completed_events.auto_paging_iter():
|
||||
checkout_session = event['data']['object']
|
||||
|
||||
if checkout_session and 'id' in checkout_session and checkout_session['id'] == stripe_checkout_session_id:
|
||||
cents = checkout_session['display_items'][0]['amount']
|
||||
dollars = decimal.Decimal(cents)/100
|
||||
|
||||
#consume_payment_session deletes the checkout session row and inserts a payment row
|
||||
# its ok to call consume_payment_session more than once because it only takes an action if the session exists
|
||||
success_account = get_model().consume_payment_session("stripe", stripe_checkout_session_id, dollars)
|
||||
|
||||
if success_account:
|
||||
print(f"{success_account} paid ${dollars} successfully (stripe_checkout_session_id={stripe_checkout_session_id})")
|
||||
|
||||
for _ in range(0, 5):
|
||||
paid = validate_stripe_checkout_session(stripe_checkout_session_id)
|
||||
if paid:
|
||||
print(f"{paid['email']} paid ${paid['dollars']} successfully (stripe_checkout_session_id={stripe_checkout_session_id})")
|
||||
return redirect(url_for("console.account_balance"))
|
||||
else:
|
||||
sleep(1)
|
||||
|
||||
|
||||
abort(400, "this checkout session is not paid yet")
|
||||
|
||||
|
Reference in New Issue
Block a user