Should I vibe code
Booking pages for services, events, products, and payments
A booking link that takes a deposit is a shop. Refunds, no-shows and coupon codes are your accounting now.
?
Their verdict, the Cappuccino price and the build-time estimate come from their entry, MIT-licensed. Checked 2026-08-04.
?
Our verdict, the regret score and everything below it. Editorial and unsponsored — nobody can pay to be moved.
The honest answer
why the verdict is what it is
Calendly with a Stripe key attached is not the same product. What Book Like A Boss sells at the tier people actually buy is a shop: paid bookings, coupon codes, recurring memberships, classes with a seat count, and a branded page on a domain you point at it. Every one of those is a small feature and a large obligation. The moment money changes hands before the service happens, you have signed up for refunds, cancellation policy, dunning, and the specific structural problem that your payment provider and your database are two systems which can disagree — and when they disagree, somebody has a receipt for an appointment that does not exist in your calendar.
What actually breaks
not "if". the specific failures.
- The gap between the charge and the booking row — the payment succeeds, your insert does not, and there is now a customer holding a receipt for a slot nobody has reserved
- Webhook idempotency: Stripe will deliver checkout.session.completed more than once, and a handler without a dedupe key creates the booking twice and looks like it worked
- Refunds, which are the first thing skipped and the only route out of a cancellation you caused, complete with a clock before the dispute opens instead
- Coupon codes, because a public validation endpoint with no rate limit is a guessing oracle, and your 40% code ends up on a deals forum by the weekend
- Memberships, which are recurring charges you now have to stop on time — a subscription that outlives the service is a chargeback with a paper trail attached
- Concurrency on the popular slot: checking free/busy is a read, writing the booking is a write, and nothing you generated holds a lock between them
- SMS reminders, which stop arriving the day a carrier decides an unregistered sender is spam, silently and without a bounce
- Custom domains, where the certificate you forgot to renew belongs to a customer whose booking page is now a browser warning
The Saturday workshop is capped at twelve and nineteen people have confirmation emails. Six of them paid twice, because Stripe redelivered the checkout webhook during a deploy and your handler has no idempotency key, so each redelivery decremented a seat count read a moment earlier and sent another confirmation. The mail all looks correct — that is the part that stings, because every one of those people did exactly what you asked and got a receipt for it. Now the work is strictly ordered and none of it is code: refund six duplicate card payments before anybody opens a dispute, decide which seven of the remaining thirteen you are turning away, and write to people who arranged childcare around a Saturday morning to tell them the room holds twelve.
Is that you?
the verdict is a default, not a law
- Bookings are free and the page only publishes when you are available
- Money is taken afterwards, in person, by a card reader somebody else wrote
- It is one person, one service, one calendar, and a page you would happily take down for a day
- Payment is a link you paste into the confirmation email by hand, so no code owns the charge
- A customer is charged before the service happens
- You are selling memberships, packages or class seats — that is inventory and recurring billing wearing a booking form
- Coupon codes exist
- The page carries someone else's brand on someone else's domain
- You cannot say, right now, what your code does when the payment succeeds and the booking write fails
If you build it anyway
the checklist, then the prompt that enforces it
- Build cancellation and refund before you build checkout. If a customer can be charged, they must be refundable by a path that already exists and has been tested, not one you write during the incident.
- Make the payment webhook idempotent from the first line: store the event id, insert once, and treat every redelivery as a no-op. Stripe redelivers by design and a double booking that emails a confirmation looks exactly like success.
- Reserve the slot before you send anyone to checkout, with a short expiry, and release it on abandonment. Reading free/busy and then writing a booking is a race that a popular 10am will find within a week.
- Never let your database and Stripe be the only two records. Keep an append-only ledger row per charge, refund and cancellation, written before the side effect, so you can reconcile without reading a dashboard.
- Let Stripe Checkout own the card. Hosted redirect only, no card fields on your page, no PAN in a log line — that is the difference between SAQ A and a compliance project.
- Rate-limit and log coupon validation per IP and per code, and make codes long enough that guessing is pointless. Discount codes are the cheapest thing on the page to attack.
- Send exactly one confirmation per booking, keyed on the booking id, and make the reminder job idempotent too. Duplicate confirmations are how a capacity bug becomes a public one.
I am building a booking page that takes payment before the service happens.
Treat the money as the dangerous part and push back when I ask for shortcuts.
1. Before any checkout code exists, build cancellation and refund: an operator
path and a customer path, both tested, both writing to a ledger. If I ask to
ship without refunds, refuse and say why.
2. Make every payment webhook handler idempotent on the provider event id.
Insert-once semantics, unique constraint in the database, redelivery is a
no-op. Assume the provider will deliver the same event three times.
3. Reserve the slot with a short-lived hold before redirecting to checkout, and
release it on expiry. Do not check availability and then insert a booking as
two unguarded statements — write the test where two requests hit the same
slot concurrently and exactly one wins.
4. Handle the payment-succeeded-write-failed case explicitly. Tell me what the
customer sees, what the operator sees, and how the money gets back.
5. Use hosted checkout. No card fields on my page, no card data in my database,
no PAN or CVV anywhere in a log. Say out loud which PCI SAQ this keeps me in.
6. Append-only ledger for charges, refunds, disputes and cancellations, written
before the side effect, never mutated afterwards.
7. Only then build the booking page, availability rules and confirmation email.
One confirmation per booking id, and the reminder job idempotent as well.
8. Rate-limit the public endpoints: coupon validation, availability lookup and
booking creation, per IP and per identifier. Long random coupon codes.
9. Store timestamps in UTC with the IANA zone of the booker recorded alongside,
and render in their zone. Do not store offsets.
10. Refuse to build recurring memberships in this pass. Say that subscription
billing needs dunning, proration and cancellation-at-period-end, and that it
is a separate project with its own failure mode.
11. Out of scope: SMS, white-label custom domains, class waitlists. If I insist,
tell me what breaks when a carrier filters my sender or a certificate
renewal fails on a customer's domain — and that BLAB is $20 a month.That one keeps you out of trouble. For the prompt that actually builds it, canivibecodeit.com has one.
their build prompt ↗Or don’t build it
the boring option, and the way back out
The moment you want to charge for a booking. Twenty dollars a month buys refunds, coupon logic, class seats and recurring memberships that somebody else has already debugged against real chargebacks, and it buys the boring answer to 'the payment went through but the appointment vanished'. Your own version reaches that same question in week two and answers it with an apology email.
$20/mo is cheaper than your weekend.
The exportable asset is three tables, not the page: bookings with their UTC timestamps and IANA zones, customers with their consent and contact details, and the payment ledger with provider ids for every charge and refund. Keep those in your own database from day one and keep the provider id on every row, because the day you migrate to a real product the reconciliation between what you charged and what you delivered is the only part that cannot be redone from a CSV. Keep the booking page URL redirectable too — it is on business cards.
Mature open-source scheduling platform, and the best available reference for calendar OAuth and availability maths.
Self-hosted appointment scheduler aimed at service businesses; development happens on the develop branch rather than master.
Questions
Stripe handles the money. Doesn't that make this safe?
Stripe handles the card, which removes the worst of the compliance problem and is exactly why you should use hosted checkout. It does not handle the part that actually breaks, which is the consistency between Stripe's view of the world and yours. Every real incident in this category is the same shape: a charge exists and a booking does not, or a booking exists twice because a webhook arrived twice. Those are your bugs, in your code, on your reconciliation.
Why is the refund path more important than the checkout path?
Because checkout is the part you will test forty times while building it, and refunds are the part you first attempt while a customer is emailing you. A cancellation is not an edge case in a booking product, it is a normal weekday, and it is the one operation where getting it wrong converts a mild annoyance into a card dispute with a deadline you did not set.
I only sell free intro calls. Does any of this apply?
Barely, and that version is close to the ship-it case at the top of this page. Once nothing is charged, most of what makes this a your-funeral disappears and you are left with a booking link — go and read the Calendbook entry instead, because the race between reading availability and writing a booking is still yours.
What about the branded custom domain?
It is the feature that quietly makes someone else's uptime your problem. A booking page on your customer's domain means you are terminating TLS for a name you do not own, renewing a certificate on their behalf, and being the reason their page shows a browser warning at 8am on a Monday. It is genuinely easy to set up and genuinely miserable to be responsible for.
- PCI DSS document library (Security Standards Council)
- Stripe — reducing your PCI scope with hosted checkout
Every week, someone ships something they shouldn’t have.
New verdicts, the worst thing that landed in the trap, and the occasional incident report. No other email, ever.
Two people load the same 3pm and both hit Book. Everything after that is a phone call you have to make.
The appointment is a van driving to a house. Your booking form has no idea how far apart the last two jobs were.
Team booking means colleagues’ calendars depend on your cron job being awake.
last reviewed 2026-08-04 · verdict is editorial and unsponsored · shared entry data from canivibecodeit under MIT · not legal advice