Last active
March 23, 2020 18:08
-
-
Save cjavilla-stripe/d4ca156f40ed4a32438d90306bf33dc3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" 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