Template
## Summary - Closes #9. Port shop-gated buyer accounts (register/login/profile, password reset, order history), purchase-gated reviews, and Stripe customer ids. Cards stay on Stripe. - Slim public contact form to email + interest + message. Shipping tracking status, scan events, paste-in numbers, and EasyPost webhook when FEATURE_SHIPPING is on. - Add template-neutral `seed_demo` (generic SKUs, demo shoppers, guest orders, tracking stubs). Prod refused. `--reset` deletes tagged demo rows after invoices. ## Test plan - [ ] `cd site && uv run python manage.py test` (173 tests passed locally) - [ ] Shop on: `/account/register`, login, profile, password reset, claim guest orders by email - [ ] Shop off: `/account/` URLs absent; Sign in / Account links hidden - [ ] Checkout as logged-in buyer stores `Order.user` and Stripe customer id; product review only after paid purchase - [ ] Shipping on: portal paste-in tracking + EasyPost webhook updates status; shipping off: orders still work without tracking UI - [ ] `seed_demo` refused in prod; `--reset` rebuilds tagged catalog/shoppers without deleting staff invoices Reviewed-on: #10
36 lines
2.3 KiB
HTML
36 lines
2.3 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
{% block title %}Checkout · {{ SITE_NAME }}{% endblock %}
|
|
{% block extra_head %}
|
|
<link rel="stylesheet" href="{% static 'css/address-autocomplete.css' %}">
|
|
{% endblock %}
|
|
{% block content %}
|
|
<section class="section section-lg">
|
|
<div class="container">
|
|
<h1>Checkout</h1>
|
|
<p>Total: {{ total }}</p>
|
|
{% if user.is_authenticated %}
|
|
<p>Signed in as {{ user.email }}. Cards stay on Stripe; we never store card numbers. <a href="{% url 'account:profile' %}">Edit profile</a></p>
|
|
{% else %}
|
|
<p>Have an account? <a href="{% url 'account:login' %}?next={{ request.path }}">Sign in</a> to prefill shipping and save cards on Stripe. Or <a href="{% url 'account:register' %}">create one</a>.</p>
|
|
{% endif %}
|
|
<form method="post">
|
|
{% csrf_token %}
|
|
<div data-address-autocomplete data-suggest-url="{% url 'address_suggest' %}">
|
|
<p><label>Email <input class="form-input" name="email" type="email" required value="{{ checkout_initial.email }}" {% if user.is_authenticated %}readonly{% endif %}></label></p>
|
|
<p><label>Name <input class="form-input" name="customer_name" value="{{ checkout_initial.customer_name }}"></label></p>
|
|
<p><label>Address <input class="form-input" name="address_line1" data-ac="line1" autocomplete="off" value="{{ checkout_initial.address.line1|default:'' }}"></label></p>
|
|
<p><label>Address 2 <input class="form-input" name="address_line2" data-ac="line2" autocomplete="address-line2" value="{{ checkout_initial.address.line2|default:'' }}"></label></p>
|
|
<p><label>City <input class="form-input" name="address_city" data-ac="city" autocomplete="address-level2" value="{{ checkout_initial.address.city|default:'' }}"></label></p>
|
|
<p><label>State <input class="form-input" name="address_state" data-ac="state" autocomplete="address-level1" value="{{ checkout_initial.address.state|default:'' }}"></label></p>
|
|
<p><label>ZIP <input class="form-input" name="address_zip" data-ac="zip" autocomplete="postal-code" value="{{ checkout_initial.address.zip|default:'' }}"></label></p>
|
|
</div>
|
|
<button class="button button-primary" type="submit">Pay with Stripe</button>
|
|
</form>
|
|
</div>
|
|
</section>
|
|
{% endblock %}
|
|
{% block extra_js %}
|
|
<script src="{% static 'js/address-autocomplete.js' %}"></script>
|
|
{% endblock %}
|