Skip to content
Healthcare booking system architecture using Stripe, Supabase, Next.js, and Google Calendar

Internal Case Study

Real-Time Healthcare Booking System with Stripe and Google Calendar Sync

May 3, 2026

See how a real-time healthcare booking system prevents double bookings with Stripe payments, Supabase holds, Google Calendar sync, and live slot checks.

Real-Time Healthcare Booking System with Stripe and Google Calendar Sync

Online booking looks simple from the outside.

A patient picks a time.
They pay.
They receive confirmation.
The clinic sees the appointment.

Simple, right?

For a healthcare clinic, it is rarely that clean.

A staff member may add an appointment directly in Google Calendar. A patient may sit in Stripe checkout for several minutes. Another patient may try to book the same slot at the same time. A cached page may show an appointment time that is already gone.

That is how double bookings happen.

That is how front desk teams lose trust in online booking.

That is why we built a real-time healthcare booking system that connects Next.js, Supabase, Stripe, Google Calendar, and email notifications into one clear appointment flow.

This system was designed for two groups:

  • Clinic owners who care about fewer mistakes, fewer calls, and better patient flow.
  • Developers who care about race conditions, webhook safety, calendar sync, and clean backend logic.

Why healthcare booking is harder than regular e-commerce

A normal e-commerce store sells products.

A healthcare clinic sells time.

That changes everything.

If two customers buy the same shirt, inventory can be adjusted. If two patients book the same 2:00 PM appointment, the clinic has a real problem.

The front desk has to call someone back. The patient gets frustrated. Staff lose confidence in the website. The clinic owner starts thinking, “Maybe online booking is more trouble than it is worth.”

The real issue is usually not the booking page.

The issue is weak appointment logic behind the page.

Many basic booking tools only check their own database. That is not enough for a clinic. Your staff may still use Google Calendar for manual bookings, blocked times, meetings, lunch breaks, follow-ups, and internal work.

A serious clinic appointment booking system must check both:

  • Your internal booking database
  • Your live clinic calendar

That is the difference between a booking form and a real scheduling system.

The core business problem

The goal was clear.

Patients should only see appointment times that are truly available.

That means the system must hide a slot when:

  • The slot is already full
  • The clinic calendar has a busy event
  • A patient is currently paying for that slot
  • The slot was already booked after payment
  • The time is too close to an existing appointment
  • The website cache is stale

For clinic owners, this protects the schedule.

For developers, this means availability can never depend on one weak client-side check.

The system architecture

The booking flow connects three core systems:

  • Next.js handles the booking page and API routes.
  • Supabase stores slots, holds, users, and orders.
  • Google Calendar checks live clinic availability.
  • Stripe handles secure checkout and payment confirmation.
  • Email sends patient confirmations and admin alerts.

Here is the simple version:

  • Patient selects a slot
  • Next.js checks available times
  • Supabase checks open slots
  • Google Calendar checks busy times
  • Stripe creates the checkout session
  • Supabase creates a temporary hold
  • Stripe webhook confirms payment
  • Supabase blocks the slot
  • Google Calendar creates the appointment
  • Patient and admin receive emails

This matters because payment confirmation becomes the final source of truth.

The clinic does not rely on the browser. The clinic does not rely on hope. The clinic does not rely on “please do not click twice.”

Good software should not need luck.

Step 1: Real-time appointment availability

When a patient opens the booking page, the server checks two places at the same time.

First, it checks Supabase for open booking slots.

The system only considers slots where:

  • The status is open
  • The reserved count is lower than capacity
  • The slot is inside the allowed booking window

Second, it checks Google Calendar using the freebusy API.

That matters because the clinic team may add events manually. If the website ignores Google Calendar, patients may book times that staff already blocked.

The system only shows a slot if it passes both checks.

This is one of the most important parts of a real-time healthcare booking system.

The 30-minute buffer rule

Clinics rarely want back-to-back appointments with zero breathing room.

Staff need time for notes, cleanup, intake, late arrivals, and small delays. So the system adds a 30-minute buffer around Google Calendar events.

If there is a staff meeting from 2:00 PM to 2:30 PM, the system also hides nearby slots that touch the buffer window.

For developers, here is the core logic:

export function intersectsBusyRange(
  slotStart: Date,
  slotEnd: Date,
  busyRanges: BusyRange[],
  bufferMs = 30 * 60 * 1000,
) {
  return busyRanges.some((busy) => {
    const bufferedStart = new Date(busy.start.getTime() - bufferMs);
    const bufferedEnd = new Date(busy.end.getTime() + bufferMs);

    return slotStart < bufferedEnd && slotEnd > bufferedStart;
  });
}

That small buffer prevents a lot of real-world clinic stress.

It also makes the calendar feel human. Clinics are run by people, not robots with coffee subscriptions.

Step 2: No stale availability

Healthcare booking pages should not show old slot data.

This system sends the slot response with:

Cache-Control: no-store

That tells the hosting layer not to serve stale slot results.

This is important on platforms like Vercel, where caching can be great for content pages but dangerous for live appointment data.

A blog post can be cached. A clinic appointment slot should not be cached like a blog post.

That one detail protects the clinic from showing old availability after a patient already booked.

Step 3: Stripe checkout with a temporary booking hold

Payment creates another challenge.

What happens when a patient selects 3:00 PM and spends five minutes entering card details?

You cannot leave that slot fully open. Another patient may pick it.

You also cannot mark it permanently booked before payment succeeds.

The answer is a temporary booking hold.

When the patient starts checkout, the system creates a hold in Supabase. This hold claims the slot for a short time while the patient pays.

The hold includes:

  • Slot ID
  • Hold ID
  • Expiry time
  • Patient or user context
  • Product details
  • Checkout metadata

Then Stripe creates a Checkout Session.

The important part is this: critical booking data is passed through Stripe metadata.

That includes:

  • slot_id
  • hold_id
  • product name
  • actor kind
  • user_id when signed in

This lets the webhook know exactly what to finalize after payment.

Step 4: Server-side coupon and membership rules

Coupon validation happens on the server.

That is the only place it should happen.

If coupon logic lives only in the browser, someone will eventually tamper with it. Not maybe. Eventually.

The system also blocks guest checkout for memberships. If a product is a subscription or membership, the patient must be signed in.

That protects the clinic from anonymous membership purchases that cannot be tied cleanly to a user account.

For owners, this means cleaner records.

For developers, this means business rules live where they belong: on the server.

Step 5: Stripe webhook confirms the booking

The webhook is where the booking becomes real.

Stripe sends a checkout.session.completed event after payment succeeds.

Then the backend performs the final booking steps:

  • Marks the temporary hold as converted
  • Increments the reserved count on the slot
  • Blocks the slot from future booking
  • Creates the Google Calendar event
  • Sends the patient confirmation email
  • Sends admin notification emails

This is the right order.

Do not trust the success page alone. A patient can close the browser. A network request can fail. A client-side callback can break.

The Stripe webhook is the reliable confirmation point.

Step 6: Atomic slot booking to prevent race conditions

Developers know the scary part.

Two patients can click the same time at nearly the same moment.

If your code does this:

Read slot
Check count
Update count

you may still have a race condition.

This system avoids that by using a database RPC to increment the reserved count as one atomic operation.

That means the database handles the critical update safely.

For healthcare owners, this means fewer double bookings.

For developers, this means the booking logic is not hanging by a thread.

Step 7: Correct slot status values

The database only allows valid slot states:

  • open
  • blocked
  • cancelled

This protects the data from random status values.

There was a real bug where the code tried to write reserved as the slot status. PostgreSQL rejected it because reserved was not allowed.

The fix was to use blocked.

That is the right word. Once payment succeeds, the slot is no longer temporary. It is taken.

Clean database rules save you from messy application code.

Step 8: Google Calendar event creation

After Stripe confirms payment, the system writes the appointment to Google Calendar.

The calendar event includes:

  • Appointment time
  • Service name
  • Patient name
  • Patient email
  • Phone number
  • Order reference

Example:

await createGoogleCalendarEvent({
  startIso: slot.starts_at,
  endIso: slot.ends_at,
  summary: `${productName} - ${patientName}`,
  description: `Phone: ${phone}\nOrder: ${orderId}`,
  attendeeEmail: patientEmail,
});

This creates a real clinic calendar event.

The patient can receive a calendar invite. The clinic sees the appointment in the same calendar staff already use. The next availability check sees that time as busy.

That creates a clean loop:

  • Payment confirms
  • Calendar event is created
  • Future slot checks see the calendar as busy
  • The patient cannot be double-booked

That is the backbone of reliable clinic scheduling software.

Why Google Calendar sync matters for clinic owners

Clinic staff already live in their calendar.

A booking system that ignores that reality creates extra work.

With Google Calendar sync, staff do not need to manage two separate schedules. Manual events, blocked time, and paid website bookings all affect availability.

That means:

  • Fewer phone calls
  • Fewer schedule conflicts
  • Less manual checking
  • Less front desk stress
  • Better patient experience
  • More trust in online booking

The best healthcare booking system should fit how the clinic already works.

It should not force the clinic to rebuild its daily process around software.

Why this matters for developers

This project has several lessons that matter in real production builds.

  • Do not check availability only in the browser.
  • Do not trust payment success pages.
  • Do not rely only on your internal database if staff use an external calendar.
  • Do not let CDN caching touch live appointment slots.
  • Do not write flexible status strings without database checks.
  • Do not update booking counts with a weak read-then-write pattern.
  • Do use Stripe metadata carefully.
  • Do use webhooks as the final booking trigger.
  • Do use temporary holds during checkout.
  • Do write back to the clinic calendar after payment.
  • Do make the database enforce the rules.

That is how you build a healthcare appointment booking system that can survive real users.

Real users click twice. Real users abandon checkout. Real users use old tabs. Real staff add calendar events manually. Real clinics need the system to handle all of it.

Tech stack used

This healthcare booking system was built with:

  • Next.js App Router
  • TypeScript
  • Supabase
  • PostgreSQL
  • Supabase Auth
  • Stripe Checkout Sessions
  • Stripe Webhooks
  • Google Calendar API
  • OAuth2
  • Nodemailer
  • Gmail SMTP
  • Vercel

Each tool has a clear job. Simple roles. Clear boundaries. Fewer surprises.

What clinic owners should ask before buying booking software

Before choosing healthcare appointment booking software, ask these questions:

  • Does it check my real clinic calendar before showing slots?
  • Can it stop two patients from booking the same time?
  • Does it hold a slot while a patient is paying?
  • Does it update the calendar after payment?
  • Does it send patient and admin emails?
  • Does it block stale cached slot data?
  • Can it support memberships and guest checkout rules?
  • Does it work with the tools my staff already use?
  • Can my developer explain how race conditions are handled?

If the vendor cannot answer clearly, be careful. A pretty calendar UI is not enough.

Privacy and compliance note for healthcare teams

This architecture does not automatically mean HIPAA compliance.

For clinics that handle electronic protected health information, privacy and security review must happen before launch. HHS states that the HIPAA Security Rule requires administrative, physical, and technical safeguards for electronic protected health information.

At minimum, a healthcare booking system should be reviewed for:

  • Access control
  • Audit logs
  • Data retention
  • Email content
  • Vendor agreements
  • Payment data handling
  • Calendar data handling
  • Patient data exposure
  • Staff permissions

Do not treat compliance as a checkbox. Treat it as part of the build from day one.

Business impact for healthcare owners

A real-time clinic booking system can help the business in practical ways.

  • It reduces front desk scheduling work.
  • It lets patients book without waiting for a phone call.
  • It collects payment before the appointment is confirmed.
  • It protects staff from calendar conflicts.
  • It gives patients clear confirmation.
  • It keeps the clinic calendar current.
  • It lowers the risk of double bookings.

For a health and wellness clinic, that means smoother operations and fewer avoidable mistakes.

Developer summary

Here is the technical pattern in plain terms.

  • Use Supabase to store slot records.
  • Use Google Calendar freebusy to check external calendar conflicts.
  • Add a time buffer around busy events.
  • Return live slot data with no-store cache headers.
  • Create a temporary hold before Stripe checkout.
  • Pass slot and hold IDs through Stripe metadata.
  • Use Stripe webhook completion as the booking trigger.
  • Update the slot with an atomic database operation.
  • Set the slot to blocked after confirmed payment.
  • Create the Google Calendar event after payment.
  • Send confirmation and admin emails.

That is the pattern. It is clean. It is practical. It works for real clinic booking behavior.

Final takeaway

Healthcare booking is not regular e-commerce.

You are not selling a product on a shelf. You are selling a fixed time on a real clinic calendar.

That means your system must protect every appointment slot from the moment a patient selects it to the moment payment confirms.

The winning setup is simple in concept:

  • Check the database.
  • Check Google Calendar.
  • Hold the slot during checkout.
  • Confirm through Stripe.
  • Block the slot in the database.
  • Write the event back to Google Calendar.
  • Notify the patient and the clinic.

That is how you build a real-time healthcare booking system that clinic owners can trust and developers can respect.

FAQs

What is a real-time healthcare booking system?

A real-time healthcare booking system shows appointment times based on live availability. It checks the clinic database, calendar events, payment status, and booking holds before showing a slot to a patient.

Why do clinics need Google Calendar sync?

Clinics often manage staff schedules, blocked time, and manual appointments inside Google Calendar. If the website does not check that calendar, patients may book times that are already unavailable.

How does Stripe help with clinic booking?

Stripe handles secure checkout and sends a webhook after payment succeeds. The backend can then confirm the booking, block the slot, create the calendar event, and send emails.

What is a booking hold?

A booking hold temporarily claims an appointment slot while a patient completes checkout. It prevents another patient from taking the same time during payment.

Can this be built with Next.js?

Yes. Next.js works well for this type of app because API routes can handle slot checks, checkout session creation, webhook processing, and calendar integration in one application.

Does this system prevent double bookings?

The system is designed to reduce double-booking risk by checking Supabase, checking Google Calendar, creating checkout holds, using Stripe webhooks, and updating slot counts with an atomic database operation.

Is this HIPAA compliant?

The architecture alone does not prove HIPAA compliance. A clinic handling protected health information needs legal, security, vendor, and process review before launch.

Publishing notes

Use Article schema for the blog post. Use LocalBusiness or a healthcare-specific local business type on the clinic site where business hours, address, phone, and department data are relevant. Google says structured data helps it understand page content, and LocalBusiness structured data can help describe business details such as hours and departments.

Use FAQPage schema only when the site fits Google’s current eligibility rules. Google says FAQ rich results are limited to well-known, authoritative government-focused or health-focused websites.