Setup
6 個步驟完成 Google 行事曆串接
這份設定適合「一個店家帳號管理自己的預約」。如果未來要多店、多員工或多行事曆,可以再擴充為店家獨立 Calendar ID。
設定 SECRET_TOKEN
把 SECRET_TOKEN 改成網站端與 Apps Script 都知道的密鑰。密鑰不要公開放在頁面上。
部署成 Web App
按「部署」新增部署作業,類型選「網頁應用程式」。執行身分選自己,存取權限依專案需求開放給可呼叫的對象。
/exec 的 Web App URL,測試時不要拿到 /dev 網址。網站端儲存 Web App URL
把 Web App URL 與 SECRET_TOKEN 填到 LINE 預約系統的設定檔。預約成功時,系統會把日期、時間、姓名、電話與服務名稱送過去。
新增與取消各測一次
先送出一筆測試預約,確認 Google 行事曆出現事件;再取消同一筆預約,確認事件會被刪除。
Data
預約系統需要送出的資料
新增與取消都走同一支 Webhook。新增預約會回傳 event_id,請務必存回資料庫,取消同步才找得到 Google 事件。
需要 date、time、service_name、name、phone、booking_id。
需要 event_id,系統會刪除對應的 Google 行事曆事件。
範例程式用 +08:00 建立時間,適合台灣店家。
Apps Script
可直接貼上的 Webhook 程式碼
這份版本預設寫入登入帳號的主要 Google 行事曆。若要指定某個行事曆,將 USE_DEFAULT_CALENDAR 改為 false,再填入 CALENDAR_ID。
const CONFIG = {
SECRET_TOKEN: 'replace-with-your-secret-token',
DEFAULT_DURATION_MINUTES: 60,
// true:寫入登入帳號的主要行事曆
USE_DEFAULT_CALENDAR: true,
// 若 USE_DEFAULT_CALENDAR 改為 false,請填入指定行事曆 ID
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 = `[LINE預約] ${serviceName} - ${customerName}`;
const description = [
`服務項目:${serviceName}`,
`客戶姓名:${customerName}`,
`聯絡電話:${phone}`,
`預約編號:${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);
}
Handoff
交付給預約系統的三項資料
Web App URL
部署完成後複製 /exec 網址,填回 LINE 預約系統設定。
SECRET_TOKEN
網站端送出資料時必須帶同一組 token,避免外部亂打 Webhook。
event_id 欄位
新增預約後要把回傳的 event_id 存起來,取消時才可同步刪除。
上線前檢查
請至少測試「新增預約」、「取消預約」、「token 錯誤」、「時間格式錯誤」四種情境。確認 Google 行事曆內容正確後,再接正式預約資料。
Reference
官方文件
若需要調整進階功能,例如邀請通知、提醒時間、指定行事曆或多員工排班,可從 Google 官方文件延伸。