Skip to content

Reserve, then confirm

reserve holds the selected slot and refreshes the hold every 15 minutes. Nobody else can book it while you run your own step. confirm turns the hold into a booking; cancel releases it.

vue
<template>
  <ZaptimeCalendar :config="config" @time-slot-changed="(s) => (slot = s)" />

  <div v-if="slot && step === 'details'">
    <input v-model="email" type="email" placeholder="Email" />
    <button @click="hold">Continue to payment</button>
  </div>

  <div v-if="step === 'payment'">
    <p>Slot is held for you. Complete the payment.</p>
    <button @click="pay">Pay</button>
    <button @click="release">Cancel</button>
  </div>

  <p v-if="step === 'done'">Booked!</p>
</template>

<script setup lang="ts">
import { onUnmounted, ref } from "vue";
import { ZaptimeCalendar, reserve, confirm, cancel } from "@zaptime/vue3";
import type { ZaptimeConfig } from "@zaptime/vue3";
import { stopReservationRefresh } from "@zaptime/core";
import type { TimeSlot } from "@zaptime/core";

const config: ZaptimeConfig = { token: import.meta.env.VITE_ZAPTIME_TOKEN, externalBooking: true };

const slot = ref<TimeSlot>();
const email = ref("");
const step = ref<"details" | "payment" | "done">("details");
let reservationUuid = "";

async function hold() {
  const res = await reserve({ email: email.value });
  reservationUuid = res.data.uuid;
  step.value = "payment";
}

async function pay() {
  try {
    await chargeCustomer(reservationUuid); // your payment step
    await confirm();
    step.value = "done";
  } catch {
    await cancel();
    step.value = "details";
  }
}

async function release() {
  await cancel();
  step.value = "details";
}

onUnmounted(() => stopReservationRefresh());

async function chargeCustomer(uuid: string) {}
</script>

Details passed to confirm (firstName, lastName, phone, customFields, guests) are merged into the reservation, so you can collect them after the hold.