Skip to content

Instantly share code, notes, and snippets.

@cjavilla-stripe
Last active March 23, 2020 18:08
Show Gist options
  • Save cjavilla-stripe/d4ca156f40ed4a32438d90306bf33dc3 to your computer and use it in GitHub Desktop.
Save cjavilla-stripe/d4ca156f40ed4a32438d90306bf33dc3 to your computer and use it in GitHub Desktop.
""" stripe """
import stripe
import json
import configparser
from classes.models.models_user import Cart
from flask_login import current_user
from classes import app, db
from flask import redirect, url_for, render_template, jsonify, request
# from dotenv import load_dotenv, find_dotenv
from datetime import datetime
from classes.models.models_admin import Course
from classes.models.models_user import Cart, Order, Library
config = configparser.ConfigParser()
config.read('config.ini')
config_type = "STRIPE-SANDBOX"
publishable_key = config.get(config_type, "api_publishable_key")
stripe.api_key = config.get(config_type, "api_secret_key")
stripe.api_version = config.get(config_type, "api_version")
webhook_secret = config.get(config_type, "webook_secret")
@app.route('/stripe_payment', methods=['GET'])
def stripe_payment():
""" stripe_payment page """
# get some of the cart information
cart_total = float(request.args.get("cart_total", 0))
cart_count = 0
if current_user.is_authenticated:
cart_count = current_user.cart_count()
return render_template(
'stripe.html',
title="Card payment",
breadcrumb="Card payment",
user=current_user,
cart_count=cart_count,
cart_total=cart_total
)
@app.route('/create-payment-intent', methods=["GET", "POST"])
def create_payment_intent():
""" payment """
# data = json.loads(request.data)
# print("intent", data)
# Calculate the purchase amount
cart_items = db.query(Cart).filter(Cart.user_id == current_user.id).all()
cart_total = 0
for cart_item in cart_items:
cart_total += cart_item.course.price
# set the payment description to the course titles purchased
# this may not be needed if order items are used
description = ""
for cart_item in cart_items:
description += cart_item.course.title + ", "
description = description[:-2]
# Create a PaymentIntent with the order amount and currency
intent = stripe.PaymentIntent.create(
amount=int(cart_total * 100),
currency='USD',
description=description,
receipt_email=current_user.email,
metadata={
'integration_check': 'accept_a_payment',
'user_id': current_user.id, # TODO: Note that I'd recommend passing the userID here, but you could also store the ID of the paymetn intent in the db.
}
)
# provide the name of the payor
name = current_user.first_name + " " + current_user.last_name
try:
# Send publishable key and PaymentIntent details to client
return jsonify({'publishableKey': publishable_key, 'clientSecret': intent.client_secret, 'payor_name': name})
except Exception as e:
return jsonify(error=str(e)), 403
@app.route('/webhook', methods=['POST'])
def webhook():
""" webhook """
# You can use webhooks to receive information about asynchronous payment events.
# For more about our webhook events check out https://stripe.com/docs/webhooks.
# webhook_secret = os.getenv('STRIPE_WEBHOOK_SECRET')
webhook_secret = "whsec_xxx"
request_data = json.loads(request.data)
if webhook_secret:
# Retrieve the event by verifying the signature using the raw body and secret if webhook signing is configured.
signature = request.headers.get('stripe-signature')
try:
event = stripe.Webhook.construct_event(
payload=request.data, sig_header=signature, secret=webhook_secret)
data = event['data']
except Exception as e:
return e
# Get the type of webhook event sent - used to check the status of PaymentIntents.
event_type = event['type']
else:
data = request_data['data']
event_type = request_data['type']
data_object = data['object']
if event_type == 'payment_intent.succeeded':
status = data_object.status
amount = data_object.amount
payment_method = data_object.charges.data[0].payment_method_details.card.brand
# TODO: Add a call here to fulfill the order
fulfill_order(data_object)
print('💰 Payment received!')
print("status", status)
print("amount", amount)
print("payment_method", payment_method)
# TODO: Dont do this bit as redirecting in the webhook handler is considered a failure to Stripe:
#return redirect(url_for(
# "thank_you",
# status=status,
# amount=amount,
# payment_method=payment_method
#))
print("didn't redirect")
# Fulfill any orders, e-mail receipts, etc
# To cancel the payment you will need to issue a Refund (https://stripe.com/docs/api/refunds)
elif event_type == 'payment_intent.payment_failed':
print('❌ Payment failed.')
return jsonify({'status': 'success'})
def fulfill_order(payment_intent):
print("fulfilling order...")
# TODO: Pull these data off of the payment_intent directly..
status = payment_intent['status']
amount = float(payment_intent['amount'])
payment_method = payment_intent['payment_method']
print("amount", amount)
# TODO: we don't redirect inside of webhook handlers
#if not status or not amount or not payment_method:
# return redirect(url_for("cart_view"))
# TODO: we can't access the current_user because were in webhook handler, instead pull out of db
# user = current_user
# I don't know what your ORM looks like
user = db.query(User).get(payment_intent['metadata']['user_id'])
cart_count = user.cart_count()
cart_items = db.query(Cart).filter(Cart.user_id == user.id).all()
date = datetime.now()
# TODO: enable sending email
# send_email(subject, user.email, html_body)
# TODO move items to library before clearing cart
# record an order
order = Order(
user=user,
purchase_date=datetime.now(),
order_amount=amount
)
db.add(order)
# update the user's library
for cart_item in cart_items:
course = db.query(Course).get(cart_item.course_id)
library = Library(
user=user,
course=course,
order=order,
course_price=cart_item.course.price
)
db.add(library)
# get the cart items for display before they are deleted
purchased_items = cart_items
cart_count = 0
# empty the cart
db.query(Cart).filter(Cart.user_id == user.id).delete()
try:
db.commit()
except Exception as error:
print("error")
@app.route("/thank_you")
def thank_you():
""" thank you """
# TODO: Pass in the ID of the PaymentIntent
try:
payment_intent = stripe.PaymentIntent.retrieve(
request.args.get('payment_intent_id'),
expand=['payment_method'],
)
except stripe.error.CardError as e:
# Not sure if you still need this or not... maybe instead, fail above?
if not status or not amount or not payment_method:
return redirect(url_for("cart_view"))
print("thank you")
status = payment_intent.status
amount = float(payment_intent.amount)
payment_method = payment_intent.payment_method
print("amount", amount)
user = current_user
cart_count = # Fetch from db?
library = # Fetch from db
purchased_item = # Fetch from db, I think you may want to store this list either on the PaymentIntent metadata, or on the Order saved with the webhook.
return render_template(
"thankYou.html",
title="Thank you",
breadcrumb="Thank you",
cart_count=cart_count,
user=current_user,
library=library,
purchased_items=purchased_items,
status=status,
amount=amount,
payment_method=payment_method
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment