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.