Webhook

LINEで配送通知を自動化する|荷物追跡APIのWebhook×LINE Messaging API

・読了 約8分

日本のお客様への配送通知は、メールよりLINEが確実です。メールは開封されないことも多い一方、LINEのメッセージはほとんどの人がその日のうちに読みます。この記事では、荷物追跡APIのWebhookとLINE Messaging APIを組み合わせて、「発送しました」「本日お届け予定です」「お届けしました」をお客様のLINEへ自動送信する仕組みを作ります。

なぜ今は LINE Messaging API なのか

かつて手軽な通知手段だった LINE Notify は、2025年3月にサービスを終了しました。現在、プログラムからLINEに通知を送る正攻法は LINE Messaging API です。LINE公式アカウントを作成し、チャネルアクセストークンを発行すれば、APIで任意のユーザーにメッセージをプッシュできます。

全体の流れ

  1. 荷物追跡APIに、通知の届け先となる自社サーバーのURL(エンドポイント)を登録する
  2. 発送時に追跡番号をWebhook購読として登録する
  3. 配送状況が変わると自社サーバーに通知が届く → LINE Messaging APIでお客様へプッシュする

ポーリングは不要です。配送状況の監視は荷物追跡API側が行い、変化したときだけ通知が届きます。

準備するもの

1. Webhookエンドポイントを登録する

通知を受け取る自社サーバーのURLを登録します。応答に含まれる endpointId と署名検証用の webhookSecret を控えてください。

curl https://api.trackingapi.jp/v1/webhooks/endpoints \
  -H "Authorization: Bearer pk_***:sk_***" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/webhook", "name": "LINE通知用" }'

2. 発送時に追跡番号を登録する

出荷処理のタイミングで、送り状番号をWebhook購読として登録します。recurring: true で14日間、配送状況の変化を監視します。

curl https://api.trackingapi.jp/v1/webhooks/register \
  -H "Authorization: Bearer pk_***:sk_***" \
  -H "Content-Type: application/json" \
  -d '{
    "endpointId": "ep_...",
    "items": [{ "courierCode": "yamato", "trackingNumber": "追跡番号" }],
    "recurring": true
  }'

3. 通知を受けてLINEへプッシュする

配送状況が変わると、登録したURLに deliveryStatus を含むJSONがPOSTされます。署名を検証してから、状態に応じたメッセージをLINEへ送ります。Node.js(Express)での実装例です。

import express from 'express';
import crypto from 'crypto';

const app = express();
// Signature is computed over the raw body — do not JSON-parse before verifying
app.use('/webhook', express.raw({ type: 'application/json' }));

const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;        // whsec_...
const LINE_TOKEN = process.env.LINE_CHANNEL_ACCESS_TOKEN;

// Per-status message templates
const MESSAGES = {
  OUT_FOR_DELIVERY: '本日お届け予定です。ご在宅をお願いいたします。',
  DELIVERED: 'お荷物をお届けしました。ご利用ありがとうございました。',
  FAILED: 'ご不在のため持ち戻りとなりました。再配達のご依頼をお願いいたします。',
};

function verifySignature(rawBody, signature, timestamp) {
  // Reject stale deliveries (replay protection, tolerance 5 min)
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - Number(timestamp)) > 300) return false;

  const expected = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(timestamp + '.' + rawBody)
    .digest('hex');
  const received = String(signature || '').replace('sha256=', '');

  // Constant-time comparison (timingSafeEqual throws on length mismatch)
  if (received.length !== expected.length) return false;
  return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}

app.post('/webhook', async (req, res) => {
  const signature = req.header('X-Webhook-Signature');
  const timestamp = req.header('X-Webhook-Timestamp');
  const rawBody = req.body.toString('utf8');

  if (!verifySignature(rawBody, signature, timestamp)) {
    return res.status(401).end();
  }
  res.status(200).end(); // ack first, notify after

  const event = JSON.parse(rawBody);
  const text = MESSAGES[event.deliveryStatus];
  if (!text) return; // notify only the statuses that matter

  // Look up the LINE user linked to this shipment (your own DB)
  const lineUserId = await findLineUserByTracking(event.trackingNumber);
  if (!lineUserId) return;

  await fetch('https://api.line.me/v2/bot/message/push', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: 'Bearer ' + LINE_TOKEN,
    },
    body: JSON.stringify({
      to: lineUserId,
      messages: [{ type: 'text', text: 'お荷物(' + event.trackingNumber + '): ' + text }],
    }),
  });
});

app.listen(3000);

署名は「X-Webhook-Timestamp の値 + ピリオド + 受信ボディ」をHMAC-SHA256で署名したものが X-Webhook-Signature ヘッダー(sha256=接頭辞つき)と一致するかで検証します。鍵はエンドポイント登録時に発行された webhookSecret です。比較には timingSafeEqual を使い、タイムスタンプが現在時刻から大きくずれた通知(目安: 5分超)は再送攻撃の可能性があるため拒否します。

どのステータスで通知すべきか

すべての変化を通知するとノイズになります。おすすめは次の3つです。

運用上の注意

関連リンク

荷物追跡APIを無料で試す

月3,000件まで無料。追跡番号を送るだけで配送状況が返ります。