Created
March 27, 2022 01:36
-
-
Save kanzure/8c58be2bafa63b13e42679c7b43be54b to your computer and use it in GitHub Desktop.
Webcash merchant demo using python/flask
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
""" | |
A small Flask web application to demonstrate a shopping cart with simple checkout. | |
This file was written mostly by Github Co-pilot! | |
Only the webcash API endpoint was specialized knowledge. | |
""" | |
import secrets | |
import datetime | |
import json | |
# pip3 install requests webcash flask | |
import requests | |
from webcash import SecretWebcash | |
from flask import Flask, render_template, request, redirect, url_for, flash, session, render_template_string | |
def replace_webcash(webcash): | |
""" | |
Make a request to the webcash server to replace the given webcash with a new secret value. | |
""" | |
if not isinstance(webcash, SecretWebcash): | |
webcash = SecretWebcash.deserialize(webcash) | |
# generate a new webcash secret | |
secret_value = secrets.token_hex(32) | |
new_webcash = SecretWebcash(amount=webcash.amount, secret_value=secret_value) | |
# make a request to the webcash server to replace the webcash | |
replace_data = { | |
"webcashes": [str(webcash)], | |
"new_webcashes": [str(new_webcash)], | |
"legalese": {"terms": True}, | |
} | |
response = requests.post("https://webcash.tech/api/v1/replace", json=replace_data) | |
if response.status_code != 200: | |
raise Exception("Replace webcash failed: {}".format(response.text)) | |
# Seems that it worked! | |
return new_webcash | |
# Create the application object | |
app = Flask(__name__) | |
# Create a secret key for session | |
app.secret_key = 'my precious' | |
# Create a list of products | |
products = [ | |
{"SKU": 1001, "name": "T-Shirt", "price": 10}, | |
{"SKU": 1002, "name": "Jeans", "price": 60}, | |
{"SKU": 1003, "name": "Socks", "price": 7}, | |
] | |
# Create a flask endpoint to display the homepage from a static file (index.html) | |
@app.route('/') | |
def homepage(): | |
""" | |
Display the homepage. | |
""" | |
#return render_template('index.html') | |
output = """ | |
<div> | |
<h1>Welcome to the Flask/Webcash Shopping Cart demo</h1> | |
<p>Browse our selection of products and pay with webcash!</p> | |
<a href="{products_url}">Products</a> | <a href="{shopping_cart_url}">Shopping Cart</a> | |
</div> | |
""".format(products_url=url_for('products_view'), shopping_cart_url=url_for('shopping_cart')) | |
return render_template_string(output) | |
# Create a flask endpoint to display the products | |
@app.route('/products') | |
def products_view(): | |
""" | |
Display the products. | |
""" | |
#return render_template('products.html', products=products) | |
output = "" | |
for product in products: | |
add_to_cart_url = url_for('add_to_cart', product_id=product['SKU']) | |
output += f"<p><a href=\"{add_to_cart_url}\">{product['name']} - {product['price']}</a></p>" | |
# render output in the products.html template | |
return render_template_string(output) | |
#return render_template('products.html', products=output) | |
# Create a flask endpoint to add a product to the user's shopping cart | |
@app.route('/add_to_cart/<int:product_id>') | |
def add_to_cart(product_id): | |
""" | |
Add a product to the user's shopping cart. | |
""" | |
# Check if the user's shopping cart is in the session | |
if 'shopping_cart' not in session: | |
# If not, create a new shopping cart | |
session['shopping_cart'] = [] | |
# Add the product to the shopping cart | |
session['shopping_cart'].append(product_id) | |
# Display a flash message to the user | |
flash('Product added to cart.') | |
# Redirect the user to the shopping cart | |
return redirect(url_for('shopping_cart')) | |
# Create a flask endpoint to display the contents of the user's shopping cart | |
@app.route('/shopping_cart') | |
def shopping_cart(): | |
""" | |
Display the contents of the user's shopping cart. | |
""" | |
# Check if the user's shopping cart is in the session | |
if 'shopping_cart' not in session: | |
# If not, create a new shopping cart | |
session['shopping_cart'] = [] | |
# Display the shopping cart | |
#return render_template('shopping_cart.html', products=products, shopping_cart=session['shopping_cart']) | |
# Calculate the total price of the user's shopping cart | |
total_price = 0 | |
for product_id in session['shopping_cart']: | |
for product in products: | |
if product['SKU'] == product_id: | |
total_price += product['price'] | |
output = """ | |
<div id="shopping-cart"> | |
<a href="{products_url}">Products</a> | |
<h2>Shopping Cart</h2> | |
Total: {total} | |
<form action="{action}" method="POST"> | |
First name: <input type="text" name="first_name"><br /> | |
Last name: <input type="text" name="last_name"><br /> | |
Your email: <input type="text" name="email"><br /> | |
<table class="table table-striped"> | |
<thead> | |
<tr> | |
<th>Product</th> | |
<th>Price</th> | |
<th>Quantity</th> | |
<th>Actions</th> | |
</tr> | |
</thead> | |
<tbody> | |
""".format(action=url_for('checkout'), total=total_price, products_url=url_for("products_view")) | |
# Loop through the shopping cart | |
for product_id in session['shopping_cart']: | |
# Get the product | |
product = [p for p in products if p['SKU'] == product_id][0] | |
# Render the product | |
output += """ | |
<tr> | |
<td>{name}</td> | |
<td>{price}</td> | |
<td>1</td> | |
<td><a href="{remove}">Remove from cart</a></td> | |
</tr> | |
""".format(name=product['name'], price=product['price'], remove=url_for('remove_from_cart', product_id=product['SKU'])) | |
output += """ | |
</tbody> | |
</table> | |
Your webcash payment: <input type="text" name="webcash" autoComplete=no><br /> | |
<input type=submit value="Checkout" /> | |
</form> | |
</div> | |
""".format(url_for('checkout')) | |
# render the output for flask | |
# tell flask to render "output" as html | |
#return render_template('shopping_cart.html', output=output) | |
return render_template_string(output) | |
# Create a flask endpoint to remove a product from the user's shopping cart | |
@app.route('/remove_from_cart/<int:product_id>') | |
def remove_from_cart(product_id): | |
""" | |
Remove a product from the user's shopping cart. | |
""" | |
# Check if the user's shopping cart is in the session | |
if 'shopping_cart' not in session: | |
# If not, create a new shopping cart | |
session['shopping_cart'] = [] | |
# Remove the product from the shopping cart | |
session['shopping_cart'].remove(product_id) | |
# Display a flash message to the user | |
flash('Product removed from cart.') | |
# Redirect the user to the shopping cart | |
return redirect(url_for('shopping_cart')) | |
# Create a flask endpoint to submit the checkout form | |
@app.route('/checkout', methods=['POST']) | |
def checkout(): | |
""" | |
Process the checkout form. | |
""" | |
# Check if the user's shopping cart is in the session | |
if 'shopping_cart' not in session: | |
# If not, create a new shopping cart | |
session['shopping_cart'] = [] | |
flash("Your shopping cart is empty.") | |
return redirect(url_for('shopping_cart')) | |
# Calculate the total price of the user's shopping cart | |
total_price = 0 | |
for product_id in session['shopping_cart']: | |
for product in products: | |
if product['SKU'] == product_id: | |
total_price += product['price'] | |
# Get the user's webcash from the request | |
webcash = request.form['webcash'] | |
webcash = SecretWebcash.deserialize(webcash) | |
# Check that the webcash is sufficient | |
# If not, display a flash message to the user | |
# If so, process the payment | |
if webcash.amount != total_price: | |
flash('Insufficient webcash. You might want to re-insert it: {}'.format(str(webcash))) | |
else: | |
# Replace the given webcash with a new value. Note that this is kind of | |
# dangerous because the new secret is not deterministic and there's no | |
# way to get it back if the process dies while processing the payment. | |
new_webcash = replace_webcash(webcash) | |
print("new_webcash: " + str(new_webcash)) | |
# Take the shopping cart information and the new webcash, and prepare an email to send to the merchant. | |
order_data = { | |
'timestamp': datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%d %H:%M:%S"), | |
'shopping_cart': str(session['shopping_cart']), | |
'computed_total': str(total_price), | |
'webcash': str(new_webcash), | |
'original_webcash': str(webcash), | |
'first_name': request.form['first_name'], | |
'last_name': request.form['last_name'], | |
'email': request.form['email'], | |
} | |
# Send the order data to the merchant by email | |
email_address = "[email protected]" | |
email_subject = "New order" | |
#email_body = render_template('order_email.html', order_data=order_data) | |
#send_email(email_address, email_subject, email_body) | |
# Write the order data to a file using json. | |
with open('orders.json', 'a') as f: | |
f.write(json.dumps(order_data)) | |
# Display a flash message to the user | |
flash('Payment successful. Thank you for shopping with us!') | |
# Redirect the user to the homepage | |
return redirect(url_for('homepage')) | |
if __name__ == "__main__": | |
app.debug = True | |
app.run(debug=True) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment