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:
@@ -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.
|
||||||
@@ -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.
|
||||||
@@ -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
@@ -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
@@ -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,
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 }}
|
||||||
@@ -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 }}
|
||||||
Reference in New Issue
Block a user