Skip to main content

Cancelling Appointments via Postback

Overview

When the system sends an appointment confirmation to a LINE user, that message usually includes a "cancel appointment" button. The button embeds postback data in the form appt_cancel:{bookingId}, so when the user taps it, LINE delivers a postback event to this webhook.

webhook-go's job is to translate that postback into a cancellation command and push it onto the booking_notification queue, where worker-go performs the actual cancellation in the database and sends the follow-up notifications.

Although it is a small feature, it warrants its own page: it is the only path in the project that publishes to the booking_notification queue, and it is a good example of postback routing with a prefix hardcoded in the source.

Business Flow

  1. LINE delivers an event with type: "postback" to POST /api/line/:id.

  2. ProcessLine has already loaded webhook_config; a config must exist and mboxEnabled must be "1".

    :::note Important caveat The entire postback block is wrapped in the mboxEnabled == "1" condition, which means an OA without mbox enabled cannot cancel appointments through this button. The postback instead falls through to the default path and is published to line_webhook. This mirrors the original NestJS behaviour. :::

  3. Read postback.data from the first postback event.

  4. If it does not start with mbox_team: but does start with appt_cancel:, this path is taken.

  5. Take everything after the first colon and parse it as an int64. On a parse failure, the service logs the error processLine: appt_cancel invalid bookingId and returns without publishing and without falling through to the default path. This guard is deliberate — it avoids a silent parse that would yield 0.

  6. Publish bare JSON to the booking_notification queue:

{
"type": "cancel",
"bookingId": 12345,
"userId": "LINE user id from event.source.userId",
"lineOaId": 123
}
  1. Return immediately without publishing to line_webhook, since the bot has no need to see this postback.

Key Files & Functions

This feature is reached through the POST /api/line/:id endpoint (see LINE Webhook Gateway) and has no route of its own.

FileRelevant part
internal/line/service.goService.ProcessLine, in the block checking the appt_cancel: prefix
internal/line/service.goThe bookingCancelPayload type, holding Type, BookingID, UserID, and LineOaID
internal/line/service.gosplitSecond(data) extracts the second segment after the colon using SplitN
internal/line/service.gopostbackData(e), sourceUserID(e), parseLineOaID(raw)

The destination queue name comes from deps.Config.RabbitMQ.QueueBookingNotification, read from RABBITMQ_QUEUE_BOOKING_NOTIFICATION and defaulting to booking_notification.

Connections to Other Services

  • RabbitMQ — the booking_notification queue on the line_exchange exchange.
  • worker-go — the consumer; it updates the booking status in the database and sends the cancellation confirmation back to the LINE user and to the shop owner.
  • Database domain — this relates to the appointment domain that stores bookings, but webhook-go never touches the database itself; it only passes the bookingId along to the worker.
  • cms-api — the apps-appointment module creates the bookings and determines that the cancel button embeds postback data in the form appt_cancel: followed by the id.
  • Rediswebhook_config must supply lineOaId and have mboxEnabled set to "1" (see Redis Cache for Webhook Config).
  • This code shares a block with Handoff to Human Agents; the mbox_team: postback is checked first.