單一店家 / 私人主要 Google 行事曆

LINE預約系統可以,同步資料到私人 Google 行事曆

這份教學先教店家把預約寫進自己正在用的主要 Google 行事曆。完成後,網站預約成功會自動出現在私人行事曆裡,取消預約也可以同步刪除。

這個方法怎麼運作

Apps Script 會用店家登入的 Google 帳號執行,所以可以直接寫入那個帳號的主要行事曆。預約系統只需要把預約資料送到 Web App 網址。

使用店家 Google 帳號

寫入店家平常看的私人主要行事曆。

建立 Apps Script

接收新增與取消預約的資料。

部署 Web App

取得給網站後端呼叫的 Webhook URL。

Store Setup

店家申請與設定步驟

1

決定要寫入哪一個 Google 帳號

請用店家真正要查看行程的 Google 帳號。因為這版會寫入該帳號的主要私人行事曆,所以登入哪個帳號,預約就會出現在哪個帳號的主要日曆。

打開 Google 行事曆確認帳號
2

確認私人行事曆可以正常使用

進入 Google 行事曆,手動新增一筆測試行程。如果可以新增、可以看到,代表 Apps Script 之後也能用這個帳號把預約寫進來。

這個版本不需要先建立專用日曆,也不需要先找日曆 ID。
3

建立 Apps Script 專案

打開 Apps Script,建立新專案。專案名稱建議填「預約系統寫入私人行事曆」。左側「專案設定」裡確認時區為台北。

打開 Apps Script
4

貼上程式並替換 SECRET_TOKEN

把下方 Apps Script 範例貼到編輯器,只要先替換 SECRET_TOKEN。這組 token 是網站後端呼叫時用的密碼,不要公開。

5

部署成 Web App

右上角按「部署」→「新增部署作業」→ 類型選「網頁應用程式」。執行身分選「我」,存取權選「任何人」。授權完成後,複製結尾是 /exec 的 Web App URL。

這個網址會寫入你的私人行事曆,請只交給工程端,不要貼到公開頁面。
6

做一次新增預約測試

工程端用 Web App URL 送一筆測試資料,如果 Google 行事曆出現一筆標題為「[預約]」開頭的行程,就代表新增同步成功。

Webhook Code

Apps Script 範例碼

這段預設寫入私人主要行事曆,並同時支援新增預約與取消預約。取消時需要網站後端傳回當初建立事件得到的 event_id

只要先改 SECRET_TOKEN
const CONFIG = {
  SECRET_TOKEN: '請換成一組只有你知道的密碼',
  DEFAULT_DURATION_MINUTES: 60,

  // 預設寫入這個 Google 帳號的主要私人行事曆。
  USE_DEFAULT_CALENDAR: true,

  // 如果之後想改成專用日曆,才需要填 CALENDAR_ID 並把 USE_DEFAULT_CALENDAR 改成 false。
  CALENDAR_ID: ''
};

function doGet() {
  return jsonOutput({
    status: 'ok',
    message: 'Google Calendar webhook is ready.'
  });
}

function doPost(e) {
  try {
    const body = JSON.parse((e.postData && e.postData.contents) || '{}');

    if (body.token !== CONFIG.SECRET_TOKEN) {
      return jsonOutput({
        status: 'error',
        message: 'Token 不正確'
      });
    }

    const action = body.action || 'create';
    const calendar = getTargetCalendar();

    if (action === 'cancel') {
      return cancelBookingEvent(calendar, body);
    }

    return createBookingEvent(calendar, body);
  } catch (error) {
    return jsonOutput({
      status: 'error',
      message: error.message
    });
  }
}

function getTargetCalendar() {
  if (CONFIG.USE_DEFAULT_CALENDAR) {
    return CalendarApp.getDefaultCalendar();
  }

  const calendar = CalendarApp.getCalendarById(CONFIG.CALENDAR_ID);
  if (!calendar) {
    throw new Error('找不到指定的 Google 日曆,請檢查 CALENDAR_ID');
  }

  return calendar;
}

function createBookingEvent(calendar, body) {
  const date = body.date || '';
  const time = body.time || '';
  const serviceName = body.service_name || '預約服務';
  const customerName = body.name || '未填姓名';
  const phone = body.phone || '未填手機';
  const duration = Number(body.duration_minutes || CONFIG.DEFAULT_DURATION_MINUTES);

  const start = new Date(`${date}T${time}:00+08:00`);
  const end = new Date(start.getTime() + duration * 60 * 1000);
  const title = `[預約] ${serviceName} - ${customerName}`;
  const description = [
    `服務項目:${serviceName}`,
    `預約姓名:${customerName}`,
    `手機號碼:${phone}`,
    `網站預約 ID:${body.booking_id || ''}`
  ].join('\n');

  const event = calendar.createEvent(title, start, end, {
    description: description
  });

  return jsonOutput({
    status: 'success',
    action: 'create',
    event_id: event.getId()
  });
}

function cancelBookingEvent(calendar, body) {
  if (!body.event_id) {
    return jsonOutput({
      status: 'error',
      message: '缺少 event_id,無法取消 Google 行事曆事件'
    });
  }

  const event = calendar.getEventById(body.event_id);
  if (!event) {
    return jsonOutput({
      status: 'not_found',
      action: 'cancel',
      message: 'Google 行事曆上找不到這筆事件,可能已經刪除'
    });
  }

  event.deleteEvent();

  return jsonOutput({
    status: 'success',
    action: 'cancel',
    event_id: body.event_id
  });
}

function jsonOutput(payload) {
  return ContentService
    .createTextOutput(JSON.stringify(payload))
    .setMimeType(ContentService.MimeType.JSON);
}

Cancel Sync

這個方法有包含預約取消嗎?

有,Apps Script 這份範例已經包含取消事件。但網站預約系統要多做兩個動作,取消才會真的同步到 Google 行事曆。

新增時保存 event_id

Apps Script 新增成功會回傳 event_id,預約系統必須把它存到預約資料表。

取消時再呼叫 Webhook

使用者取消預約時,網站後端要送 action: "cancel" 和原本的 event_id

Google 事件會被刪除

Apps Script 找到事件後會執行 deleteEvent(),私人行事曆上的該筆預約就會消失。

目前 line4 預約程式還沒有保存 Google event_id,所以「方法支援取消」,但真正接上時需要先加資料表欄位與取消呼叫流程。

Handoff

完成後交給工程端的資料

Web App URL

部署後產生的網址,通常以 /exec 結尾。

SECRET_TOKEN

Apps Script 裡設定的密碼,用來避免陌生請求亂寫私人行事曆。

使用的 Google 帳號

確認 Web App 是用店家要查看行程的 Google 帳號部署。

測試標準

新增預約後,私人 Google 行事曆上應該出現一筆事件;取消同一筆預約後,該事件應該從 Google 行事曆消失。

Optional

如果你不想和私人行程混在一起

如果你的需求不是整合私人主要行事曆,而是要把預約和私人行程分開,可以另外建立一個「預約專用」日曆。建立後到該日曆的「設定與共用」→「整合日曆」複製日曆 ID,然後把 Apps Script 的 USE_DEFAULT_CALENDAR 改成 false,並填入 CALENDAR_ID