Skip to content

Instantly share code, notes, and snippets.

@kanzure
Created March 27, 2022 01:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kanzure/8c58be2bafa63b13e42679c7b43be54b to your computer and use it in GitHub Desktop.
Save kanzure/8c58be2bafa63b13e42679c7b43be54b to your computer and use it in GitHub Desktop.
Webcash merchant demo using python/flask
"""
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 = "orders@myshop.com"
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