Add Stripe embedded checkout to book pages

Replaces the gumroad_url stub with an on-site embedded checkout (buy
button -> POST to the bookshop backend -> mounted Stripe iframe),
following the existing contact/submissions form's fetch/status-message
pattern. Adds thank-you and resend-link pages, an example book template
(draft), and updates the privacy policy and terms to honestly disclose
Stripe as a third-party payment processor.
This commit is contained in:
2026-07-14 18:35:50 -04:00
parent 40266cf811
commit 41475f4a5a
8 changed files with 171 additions and 12 deletions
+15
View File
@@ -0,0 +1,15 @@
---
title: "Example Book Title"
author: "Author Name"
cover_image: "https://downloads.twosquirrelspress.com/covers/example-book.jpg"
description: "One-line jacket copy for listing pages."
stripe_price_id: "price_XXXXXXXXXXXXXXXXXXXXXXXX"
price_display: "$6.99"
formats: ["EPUB", "PDF"]
draft: true
---
Full jacket-copy / long description goes here. Duplicate this file for
each real book, fill in the real Stripe Price ID (create the Product in
the Stripe Dashboard first, with `tsp_book=true` and `file_key=...` set
in the Product's metadata), and remove `draft: true` to publish it.
+8
View File
@@ -0,0 +1,8 @@
---
title: "Resend my download link"
description: "Lost your download link? Get a new one sent to your email."
layout: "book-resend"
---
Enter the email address you used when you bought a book, and if we find
a matching order we'll send a fresh download link to that address.
+10
View File
@@ -0,0 +1,10 @@
---
title: "Thanks for your order"
description: "Your ebook purchase is complete."
---
Thanks for your purchase! Check your email in a moment for a link to
download your book.
If it doesn't show up within a few minutes, check your spam folder, or
use the [resend form](/books/resend/) to get a fresh link sent to you.
+25 -10
View File
@@ -14,34 +14,49 @@ use of twosquirrelspress.com, and what happens to it.
- Your name, email address, manuscript file, author bio, a description of - Your name, email address, manuscript file, author bio, a description of
your submission, the title or call it's for, and a headshot if you your submission, the title or call it's for, and a headshot if you
provide one, when you submit the submissions form. provide one, when you submit the submissions form.
- Your email address and order details, when you buy a book. Your payment
details (card number and so on) go directly to Stripe, our payment
processor — they never touch Two Squirrels Press's own servers. Two
Squirrels Press only ever receives your email address, which book you
bought, and confirmation that the payment succeeded.
That's it — this site doesn't run a newsletter, doesn't sell anything This site doesn't run a newsletter, and doesn't use third-party analytics,
directly, and doesn't use third-party analytics, ad trackers, or tracking ad trackers, or tracking pixels. Checkout with Stripe is the one
pixels. third-party service in the loop, and it's there because payment processing
and tax compliance genuinely can't be handled responsibly by a small press
running its own servers.
## Why Two Squirrels Press collects it ## Why Two Squirrels Press collects it
- To respond to messages sent through the contact form. - To respond to messages sent through the contact form.
- To evaluate submissions sent through the submissions form. - To evaluate submissions sent through the submissions form.
- To fulfill book purchases (send your download link) and handle any
refund requests.
## What Two Squirrels Press doesn't do ## What Two Squirrels Press doesn't do
- Two Squirrels Press doesn't sell or rent your information to anyone. - Two Squirrels Press doesn't sell or rent your information to anyone.
- Two Squirrels Press doesn't share it with third parties except the - Two Squirrels Press doesn't share it with third parties except the
infrastructure providers necessary to run this site and deliver that infrastructure providers necessary to run this site, deliver that email,
email — none of whom get to use it for their own purposes. and process a payment — currently: Stripe, for payment processing — none
of whom get to use it for their own purposes.
## Your control over it ## Your control over it
- **Deletion**: reach out through [the contact form](/contact/) and Two - **Deletion**: reach out through [the contact form](/contact/) and Two
Squirrels Press will delete your information from these systems. Squirrels Press will delete your information from these systems. (Stripe
keeps its own transaction records as required by law and its own
policies — that retention isn't something Two Squirrels Press controls.)
## Security ## Security
Your information is stored on infrastructure Two Squirrels Press controls Most of your information is stored on infrastructure Two Squirrels Press
directly, not handed off to third-party SaaS platforms. That doesn't make controls directly. The one exception is payment processing: Stripe, acting
it invulnerable — no system is — but it does mean there's exactly one place as merchant of record, handles your card details and tax compliance
your data lives, and one party accountable for it. directly, and Two Squirrels Press never sees or stores your card number.
That doesn't make any of this invulnerable — no system is — but it does
mean there's exactly one clearly accountable party for each piece of your
data.
## Changes ## Changes
+14 -2
View File
@@ -5,14 +5,26 @@ description: "Terms for using twosquirrelspress.com."
_Last updated: July 2026_ _Last updated: July 2026_
These terms cover using twosquirrelspress.com and submitting the contact These terms cover using twosquirrelspress.com, submitting the contact or
form. submissions form, and buying a book.
## Using this site ## Using this site
This site is provided as-is, for informational purposes about Two Squirrels This site is provided as-is, for informational purposes about Two Squirrels
Press, its books, and its authors. Press, its books, and its authors.
## Buying a book
Payments are processed by Stripe, acting as merchant of record — Stripe
handles your card details, sales tax, and VAT directly. Two Squirrels
Press never sees or stores your card number.
Ebooks you buy are for your own personal use — please don't redistribute
or resell the files. Because these are digital downloads, all sales are
final except where required by law; if something's gone wrong with your
purchase (wrong file, failed download, etc.) [get in touch](/contact/)
and we'll sort it out.
## No warranty ## No warranty
This site and its content are provided without warranties of any kind, This site and its content are provided without warranties of any kind,
+4
View File
@@ -13,6 +13,10 @@ disableKinds = ["taxonomy", "term"]
author = "Will Estes" author = "Will Estes"
description = "Two Squirrels Press — character-driven speculative fiction and poetry." description = "Two Squirrels Press — character-driven speculative fiction and poetry."
eec_base_url = "https://eec.twosquirrelspress.com" eec_base_url = "https://eec.twosquirrelspress.com"
bookshop_base_url = "https://shop.twosquirrelspress.com"
# Publishable key is safe to commit (it's meant to be public) — fill in
# the real key once the Stripe account is set up.
stripe_publishable_key = ""
[[menu.main]] [[menu.main]]
name = "Books" name = "Books"
+43
View File
@@ -0,0 +1,43 @@
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ .Content }}
<form id="resend-form" style="margin-top:2rem;">
<label for="email">Email</label>
<input type="email" id="email" name="email" required autocomplete="email">
<div class="field-honeypot" aria-hidden="true">
<label for="website">Leave this field empty</label>
<input type="text" id="website" name="website" tabindex="-1" autocomplete="off">
</div>
<button type="submit" class="btn">Resend my link</button>
</form>
<p id="resend-status" role="status" style="margin-top:1rem;"></p>
<script>
(function () {
var form = document.getElementById("resend-form");
var status = document.getElementById("resend-status");
form.addEventListener("submit", function (e) {
e.preventDefault();
var payload = { email: form.email.value, website: form.website.value };
var submitBtn = form.querySelector("button[type=submit]");
submitBtn.disabled = true;
status.textContent = "Sending…";
fetch("{{ .Site.Params.bookshop_base_url }}/books/resend", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
}).then(function () {
form.reset();
status.textContent = "If that email has a purchase on file, a new link is on its way.";
}).catch(function () {
status.textContent = "Something went wrong. Try again, or email hello@twosquirrelspress.com directly.";
}).finally(function () {
submitBtn.disabled = false;
});
});
})();
</script>
{{ end }}
+52
View File
@@ -0,0 +1,52 @@
{{ define "main" }}
<h1>{{ .Title }}</h1>
{{ with .Params.cover_image }}
<img src="{{ . }}" alt="Cover of {{ $.Title }}" style="max-width:300px;">
{{ end }}
{{ with .Params.author }}
<p><span class="author-name">{{ . }}</span></p>
{{ end }}
{{ .Content }}
{{ with .Params.stripe_price_id }}
<div id="buy-btn-wrap">
{{ with $.Params.price_display }}<p><strong>{{ . }}</strong></p>{{ end }}
<button type="button" id="buy-btn" class="btn">Buy the ebook</button>
</div>
<div id="checkout-container"></div>
<p id="buy-status" role="status" style="margin-top:1rem;"></p>
<script src="https://js.stripe.com/v3/"></script>
<script>
(function () {
var stripe = Stripe("{{ $.Site.Params.stripe_publishable_key }}");
var priceId = "{{ . }}";
var buyBtn = document.getElementById("buy-btn");
var buyWrap = document.getElementById("buy-btn-wrap");
var status = document.getElementById("buy-status");
buyBtn.addEventListener("click", function () {
buyBtn.disabled = true;
status.textContent = "Starting checkout…";
fetch("{{ $.Site.Params.bookshop_base_url }}/books/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ price_id: priceId })
}).then(function (res) {
if (!res.ok) { throw new Error("checkout session request failed"); }
return res.json();
}).then(function (data) {
status.textContent = "";
buyWrap.hidden = true;
return stripe.initEmbeddedCheckout({ clientSecret: data.clientSecret });
}).then(function (checkout) {
checkout.mount("#checkout-container");
}).catch(function () {
buyBtn.disabled = false;
buyWrap.hidden = false;
status.textContent = "Something went wrong starting checkout. Try again, or email hello@twosquirrelspress.com.";
});
});
})();
</script>
{{ end }}
{{ end }}