David Silva

Free guide · last checked October 2026

Sell your photo book from your own website

This is how I sell It Started on a Sunday: a hardcover printed only when someone orders it and posted straight to them. No shop platform, no monthly fee, no stock at home. Everything I used is here, step by step, for people who have never written code.

Want it offline or printed? The same guide is a PDF on Gumroad, pay what you like from £4.99. The web page stays free.

1. How it works

Four parts:

  1. Website. A page about your book with a Buy button. I host mine free on GitHub Pages. Any website works, and Route A works without one.
  2. Checkout. A Stripe Payment Link: a checkout page Stripe hosts for you. It takes the card payment and the delivery address.
  3. The link between them. Route A: you. Route B: a small Cloudflare Worker that is told about each payment and places the print order.
  4. Print on demand. Prodigi prints the book and posts it to your buyer.

Which route? Start with Route A. It takes an afternoon and costs nothing up front. Move to Route B when copying addresses by hand gets tiring, or if you want orders placed while you sleep.

Other print companies (Blurb, Lulu, Peecho and others) work too. I chose Prodigi for A4 landscape hardcovers with a good paper. The steps here use Prodigi and Stripe because those are the ones I've tested.

2. Before you start

Your book file

Prodigi's rules for a photo book, as one PDF:

InDesign, Affinity Publisher, Lightroom's Book module (exported as PDF) and Canva can all make this. Check the current specs on the product's page in Prodigi before you export, in case they've changed.

Accounts

AccountRoute ARoute BCost
Stripe (stripe.com)yesyesFree. A fee per sale.
Prodigi (prodigi.com) with a card addedyesyesFree. You pay per book printed.
Cloudflare (cloudflare.com)yesFree plan. R2 storage asks for a card, but 10 GB is free.
A websiteoptionaloptionalGitHub Pages is free. A domain is about £10 a year.

Stripe asks for ID and your bank details before it pays you out. In the UK you can sign up as an individual (sole trader). Allow a day for checks.

Words you'll meet

3. Set your price

Work it out once, on paper. You need the printing price first: do A1 steps 1 to 5 to see it.

Printing (Prodigi's price for your page count)P

Prodigi's delivery to your buyerD

Stripe fee: 1.5% + 20p for a standard UK cardF

What you want to keepK

Book price + delivery charge you setP + D + F + K

Example with made-up numbers: printing £30.00, delivery £6.00, and you want to keep £8.00. That's £44.00 before the fee. Price £45.00 with free delivery, or £39.00 + £6.00 delivery. The fee on £45.00 is about 88p, so you keep about £8.12.

4. Order a proof copy

Order one copy for yourself before you sell anything. Prints look darker than your screen, and text near the edge can get cut. Order it the same way your buyers' copies will be made (section 5, A1), check every page, fix the file, and order another proof if you changed a lot.

5. Route A: no code, you place each order

Buyer pays on Stripe. Stripe emails you. You copy their address into Prodigi and order the book. About five minutes per order.

A1. Set up the book in Prodigi

  1. Sign in to the Prodigi dashboard. Add a payment card in the billing settings.
  2. Start a new order and choose the photo book you want (size, hardcover or softcover, paper).
  3. Upload your PDF. Prodigi checks the page count and size.
  4. Type the spine text, and pick its colour and the spine colour.
  5. Note the price it shows. Use it in your price sum (section 3).
  6. Save the book as a product (or save the order details) so you can reuse it for each order.
  7. Put in your own address and order this one as your proof.
A finished photo book order in the Prodigi dashboard, showing the product code BOOK-FE-A4-L-HARD-G and the spine text, font, size and colours.
What your order looks like in Prodigi after you've placed it: my proof, with the product code (SKU) and the spine settings. Order number and name hidden.

A2. Make the Stripe Payment Link

  1. In Stripe, go to Product catalogue and add a product: your book's name, a photo of the cover, a one-time price.
  2. Search the dashboard for payment links. It's under Checkout, Payment links. Click Create payment link and choose your product.
  3. Turn on Collect customer addresses, choose Billing and shipping addresses, and allow only the countries you deliver to.
  4. Add a shipping rate with your delivery price and the delivery time (Prodigi's time plus a few days).
  5. Turn on Require customers to provide a phone number. Couriers sometimes need it.
  6. Turn on Require customers to accept your terms of service. Stripe asks for your terms page address first: Settings, Business, Public details, Terms of service. What goes on it is in section 7. No website? Make a free page on Google Sites or Notion, publish it, and use its address.
  7. Optional: add a custom field, a dropdown asking "Send me news about new books?" with Yes and No. Only email people who said yes.
  8. Create the link. Copy it. That's your Buy button.
Stripe dashboard search for payment links, showing Checkout, Payment links.
Search the Stripe dashboard for "payment links".
The Payment links tab in Stripe Checkout, with the Create payment link button top right.
Payment links are under Checkout. Click Create payment link, top right.

Stripe moves menus around now and then. If a name here doesn't match, search for it in the dashboard's search bar.

A3. Put the Buy button on your website

Make a button or a plain link that opens the Payment Link (the address starting https://buy.stripe.com/). In Squarespace, Wix or WordPress, add a button block and paste the link. No website yet? Put the link in your Instagram bio or your email signature. It works on its own.

A4. Test it

  1. Turn on emails for successful payments in Stripe's notification settings (search the dashboard for "communication preferences").
  2. Switch Stripe to test mode (or a sandbox). Test mode has its own product catalogue, so add the product again, then make the same Payment Link there.
  3. Buy with the test card 4242 4242 4242 4242, any future expiry date, any 3 digits.
  4. Check you get the payment email, and that the address and phone show on the payment in Stripe.

A5. Each time someone buys

  1. Stripe emails you about the payment. Open the payment in the Stripe dashboard.
  2. In Prodigi, start a new order with your saved book, and copy in the buyer's name, address, phone and email exactly as Stripe shows them.
  3. If the order form has a reference field, paste the Stripe payment ID there (it starts with pi_ and is on the payment's page in Stripe), so you can match them later. Otherwise write down both order numbers.
  4. Submit. Prodigi charges your card and prints.

Do it the same day if you can. Your delivery time starts when the buyer pays.

6. Route B: automatic orders

Same Payment Link as Route A. The difference: when someone pays, Stripe sends a message to a small program on Cloudflare, called a Worker. The Worker checks the message is real, asks Stripe for the address, and places the Prodigi order. You do nothing.

You'll copy and paste one file of code. You don't need to understand it. It's about 150 lines and most parts have a comment in plain English. Download the Worker code. Don't double-click the file. Right-click it and open it with TextEdit (Mac) or Notepad (Windows), then press Cmd+A or Ctrl+A to select everything, and copy.

Do every step below in test mode first: Stripe's test mode and Prodigi's sandbox. Nothing gets printed or charged. Switch to live only at B8.

B1. Make a Prodigi sandbox key

  1. Prodigi has a separate sandbox dashboard for testing: sandbox-beta-dashboard.pwinty.com. Signing up on Prodigi's normal dashboard sets up both. If you can't log in to the sandbox, ask Prodigi's support.
  2. In the sandbox dashboard, find the API key in the settings (look for API or Integrations). Copy it into a password manager (for example Apple Passwords, Bitwarden or 1Password).
  3. Note the product code (SKU) for your book. It's on the product's page in Prodigi, and looks like BOOK-FE-A4-L-HARD-G.

B2. Put your book file in private storage

  1. In Cloudflare, open R2 Object Storage. Turn it on (it asks for a card; up to 10 GB is free).
  2. Create bucket. Give it any name. Leave public access off.
  3. Upload your book PDF. Give it a simple name with no spaces, like book.pdf.
  4. A spine file. Route B doesn't use the spine text you typed in A1. Without a spine file, buyers get a plain spine that doesn't match your proof. To get text on it, ask Prodigi's support for the spine width at your page count, make a PDF that wide by 210 mm tall in the app you made the book in, and upload it as spine.pdf.

A 160-page A4 book at 300 ppi is usually around 200 MB, and the dashboard upload took mine. If the upload fails for a bigger file, export the PDF again with a lower JPEG quality.

B3. Create the Worker

  1. In Cloudflare, go to Workers & Pages, Create, and start from the "Hello World" Worker. Name it, for example book-shop. Deploy.
  2. Click Edit code. Delete everything in the editor. Paste the whole Worker file. Click Deploy.
  3. Note your Worker's address. It ends in .workers.dev.
An illustration of a Worker code editor with the Worker file pasted in and a Deploy button top right.
An illustration of the Worker editor with the code pasted in. Lines 4 to 26 are hidden here: paste the whole file. Cloudflare's editor looks a little different.

B4. Connect the storage

  1. In the Worker, open Settings, then Bindings, Add, R2 bucket.
  2. Variable name: BOOK (capitals). Bucket: the one from B2. Save.

B5. Make the Stripe keys

Still in Stripe's test mode:

  1. Go to Developers, API keys, Create restricted key. Give it two permissions only: Checkout Sessions: Read and PaymentIntents: Write. Everything else stays None. Copy the key (starts rk_test_).
  2. Go to Webhooks and click Create an event destination. Choose Your account, then the Snapshot payload. Not Thin, even though Stripe marks Thin as recommended: the Worker only understands Snapshot.
  3. Tick two events: checkout.session.completed and checkout.session.async_payment_succeeded.
  4. Choose Webhook endpoint. URL: your Worker address followed by /stripe-webhook, for example https://book-shop.yourname.workers.dev/stripe-webhook.
  5. If Stripe asks for an API version, leave the one it picks.
  6. Save, then reveal the signing secret (starts whsec_) and copy it.
  7. Open your test Payment Link in the Stripe dashboard and copy its ID from the address bar (starts plink_).

A restricted key can only do what you ticked. If it leaks, it can't issue refunds or payouts, but it can read your buyers' names and addresses. Delete it in Stripe straight away and make a new one. Never paste any key into the code itself, a website or a chat.

B6. Fill in the Worker's settings

In the Worker, open Settings, Variables and Secrets. Add each one below. Choose Secret for the first four, so they're stored encrypted and hidden even from you.

NameTypeValue
STRIPE_SECRET_KEYSecretthe restricted key from B5
STRIPE_WEBHOOK_SECRETSecretthe whsec_ signing secret
PRODIGI_API_KEYSecretthe sandbox key from B1
FILE_SECRETSecreta long random password (40+ characters) from a password manager
PRODIGI_BASETexthttps://api.sandbox.prodigi.com/v4.0
PRODIGI_SKUTextyour book's product code
PAGE_COUNTTextthe number of pages in your PDF file, front and back cover included (mine is 164)
BOOK_FILETextbook.pdf (the name in R2)
SPINE_FILETextoptional: spine.pdf
PAYMENT_LINK_IDTextthe plink_ ID from B5. Required: without it, no order prints, so a sale of anything else on your Stripe account can never print a book.
SHIPPING_METHODTextoptional: Budget, Standard, StandardPlus, Express or Overnight. Default Standard.

Deploy again after saving, if the dashboard asks.

B7. Test it end to end

  1. Open your test Payment Link and buy with 4242 4242 4242 4242. Use a real-looking address in your delivery country.
  2. In Stripe, open Webhooks and your destination. The delivery should show 200, which means it worked.
  3. In Prodigi's sandbox dashboard, the order should be there with the buyer's address and no issues.
  4. Back in Stripe, the payment should show prodigi_order in its metadata.
  5. From the webhook page, resend the same event. Prodigi should still have one order, not two.

If it fails, the webhook page shows the answer the Worker gave:

B8. Go live

  1. Switch Stripe to live mode. Use the live Payment Link you made in A2. Make a live restricted key (rk_live_) and a live webhook destination exactly as in test mode (B5).
  2. Get your live Prodigi API key from the normal dashboard (not the sandbox), with your card added.
  3. In the Worker, replace three secrets with the live ones: STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET and PRODIGI_API_KEY. FILE_SECRET stays the same. Change PRODIGI_BASE to https://api.prodigi.com/v4.0. Open your live Payment Link in Stripe and copy its plink_ ID from the address bar into PAYMENT_LINK_ID. Not the buy.stripe.com address.
  4. Put the live Payment Link on your website.
  5. Buy one copy yourself with a real card. That's your final test, and the book shows you the spine buyers will get.
  6. From now on, don't place orders in Prodigi by hand. The Worker does it. Stripe's payment emails still arrive, for your records.

If the Worker or Prodigi is down when someone pays, the payment still goes through. In live mode Stripe resends the message for up to 3 days and emails you if it keeps failing (in test mode it only tries a few times). The order can't be printed twice.

Prodigi downloads your files after it accepts an order. If a download fails, the order shows an issue in Prodigi's dashboard rather than in Stripe. Keep Prodigi's emails switched on and look at new orders now and then.

This is what I did, not legal advice. Check gov.uk for your own case.

8. When something goes wrong

Questions

I can't set things up for you, but if a step here is wrong or out of date, tell me at hello@davidslv.photography and I'll fix the guide.

And if you'd like to see what this setup sells: It Started on a Sunday, 72 photographs of car meets in and around London, and the people who love the cars.