返品・リバースロジスティクスの追跡自動化ガイド

返品はECビジネスにとって避けられないものです。オンライン購入の20〜30%が返品されるというデータがあり、お客様は返品でも配送と同じレベルの追跡可視性を求めています。それにもかかわらず、多くの企業が返品追跡を手作業で処理しているのが現状です。本ガイドでは、そのプロセス全体を自動化する方法をご紹介します。

なぜ返品追跡が重要なのか

お客様の視点

お客様が商品を返品する際に知りたいことは以下のとおりです。

  • 集荷されたか? — 返品プロセスが始まったことの確認
  • 今どこにあるか? — 輸送中のステータス確認
  • 届いたか? — 倉庫で受領されたことの確認
  • いつ返金されるか? — 最も気になるポイント

追跡がなければ、「返金はまだですか?」というお問い合わせがサポートチームに殺到します。これは配送時の「荷物はどこですか?」問い合わせの返品版です。

ビジネスの視点

  • 在庫計画 — 何がいつ戻ってくるかを事前に把握できる
  • 不正防止 — 実際に発送されたかを確認できる
  • 返金の迅速化 — 商品到着時に自動的に返金処理を開始できる
  • キャリアの説明責任 — 返品配送業者のパフォーマンスを追跡できる

アーキテクチャ

お客様が返品を申請

返品ラベルを発行(配送業者+追跡番号)

WhereParcelに追跡登録

Webhook: picked_up → お客様に「配送業者が集荷しました」と通知

Webhook: in_transit → 社内ステータスを更新

Webhook: delivered → 倉庫での受入ワークフローを開始

倉庫で商品状態を確認

自動返金処理 → お客様に「返金を実施しました」と通知

ステップ1:返品ラベルの発行

お客様から返品リクエストを受けたら、着払いの返品ラベルを発行します。

async function createReturnLabel(order, returnRequest) {
  // 配送業者でラベルを生成
  const label = await shippingProvider.createLabel({
    from: order.shippingAddress,
    to: WAREHOUSE_ADDRESS,
    weight: returnRequest.estimatedWeight,
    service: 'standard',
  });

  // 返品レコードを作成
  const returnRecord = await db.returns.create({
    orderId: order.id,
    customerId: order.customerId,
    carrier: label.carrier,
    trackingNumber: label.trackingNumber,
    status: 'label_created',
    items: returnRequest.items,
    reason: returnRequest.reason,
    labelUrl: label.pdfUrl,
  });

  // すぐに追跡登録
  await registerReturnTracking(returnRecord);

  // お客様にラベルを送信
  await sendEmail(order.customerEmail, {
    subject: '返品ラベルのご用意ができました',
    body: buildReturnLabelEmail(returnRecord, label),
  });

  return returnRecord;
}

ステップ2:返品追跡の登録

ラベル発行と同時に、WhereParcelに返品配送の追跡を登録します。

async function registerReturnTracking(returnRecord) {
  const response = await fetch('https://api.whereparcel.com/v2/track', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.WHEREPARCEL_API_KEY}:${process.env.WHEREPARCEL_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      trackingItems: [{ carrier: returnRecord.carrier, trackingNumber: returnRecord.trackingNumber }],
      webhook: {
        url: 'https://yourstore.com/webhooks/returns',
        events: ['status_changed', 'delivered', 'exception'],
      },
    }),
  });

  return response.json();
}

ステップ3:返品イベントの処理

返品に特化したWebhookイベントを処理します。

app.post('/webhooks/returns', async (req, res) => {
  res.status(200).json({ received: true });

  const { data } = req.body;
  const returnRecord = await db.returns.findByTracking(data.trackingNumber);
  if (!returnRecord) return;

  switch (data.status) {
    case 'picked_up':
      await handleReturnPickedUp(returnRecord, data);
      break;
    case 'in_transit':
      await handleReturnInTransit(returnRecord, data);
      break;
    case 'delivered':
      await handleReturnDelivered(returnRecord, data);
      break;
    case 'exception':
      await handleReturnException(returnRecord, data);
      break;
  }
});

async function handleReturnPickedUp(returnRecord, data) {
  await db.returns.update(returnRecord.id, {
    status: 'in_transit',
    pickedUpAt: new Date(data.events[0].timestamp),
  });

  await sendEmail(returnRecord.customerEmail, {
    subject: '返品商品が集荷されました',
    body: `ご注文 #${returnRecord.orderId} の返品商品が配送業者により集荷されました。倉庫に到着しましたらあらためてお知らせいたします。`,
  });
}

async function handleReturnDelivered(returnRecord, data) {
  await db.returns.update(returnRecord.id, {
    status: 'received_at_warehouse',
    deliveredAt: new Date(data.events[0].timestamp),
  });

  // 倉庫での検品ワークフローを開始
  await warehouseQueue.add('inspect_return', {
    returnId: returnRecord.id,
    items: returnRecord.items,
  });

  await sendEmail(returnRecord.customerEmail, {
    subject: '返品商品が倉庫に到着しました',
    body: `ご注文 #${returnRecord.orderId} の返品商品を受領いたしました。商品を確認のうえ、2〜3営業日以内に返金手続きを行います。`,
  });
}

async function handleReturnException(returnRecord, data) {
  await db.returns.update(returnRecord.id, {
    status: 'exception',
    exceptionReason: data.events[0].description,
  });

  // 社内チームにアラート
  await sendInternalAlert({
    type: 'return_exception',
    returnId: returnRecord.id,
    reason: data.events[0].description,
  });

  await sendEmail(returnRecord.customerEmail, {
    subject: '返品配送に関するお知らせ',
    body: `返品配送に問題が発生しました。担当チームが確認のうえ、間もなくご連絡いたします。`,
  });
}

ステップ4:返金の自動処理

倉庫で商品状態が確認できたら、自動的に返金を処理します。

async function processReturnInspection(returnId, inspectionResult) {
  const returnRecord = await db.returns.findById(returnId);

  if (inspectionResult.approved) {
    // 返金額を算出
    const refundAmount = calculateRefund(returnRecord, inspectionResult);

    // 返金処理を実行
    await paymentProvider.refund(returnRecord.orderId, refundAmount);

    await db.returns.update(returnId, {
      status: 'refunded',
      refundAmount,
      refundedAt: new Date(),
    });

    await sendEmail(returnRecord.customerEmail, {
      subject: '返金処理が完了しました',
      body: `ご注文 #${returnRecord.orderId} について、${formatCurrency(refundAmount)}の返金を処理いたしました。お客様のアカウントへの反映には5〜10営業日ほどかかる場合がございます。`,
    });
  } else {
    await db.returns.update(returnId, {
      status: 'rejected',
      rejectionReason: inspectionResult.reason,
    });

    await sendEmail(returnRecord.customerEmail, {
      subject: '返品に関するお知らせ',
      body: `恐れ入りますが、今回の返品を承ることができませんでした。理由:${inspectionResult.reason}。詳細につきましては、サポートチームまでお問い合わせください。`,
    });
  }
}

ステップ5:セルフサービス返品ポータル

お客様が自分で返品状況を確認できるセルフサービスポータルを提供します。

app.get('/returns/:returnId', async (req, res) => {
  const returnRecord = await db.returns.findById(req.params.returnId);

  // WhereParcelから最新の追跡情報を取得
  const tracking = await fetch('https://api.whereparcel.com/v2/track', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.WHEREPARCEL_API_KEY}:${process.env.WHEREPARCEL_SECRET_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      trackingItems: [{ carrier: returnRecord.carrier, trackingNumber: returnRecord.trackingNumber }],
    }),
  }).then(r => r.json());

  res.render('return-tracking', {
    returnRecord,
    tracking: tracking.data,
    steps: getReturnSteps(returnRecord),
  });
});

function getReturnSteps(returnRecord) {
  return [
    { label: '返品申請', completed: true, date: returnRecord.createdAt },
    { label: 'ラベル発行', completed: true, date: returnRecord.createdAt },
    { label: '配送業者が集荷', completed: !!returnRecord.pickedUpAt, date: returnRecord.pickedUpAt },
    { label: '倉庫にて受領', completed: !!returnRecord.deliveredAt, date: returnRecord.deliveredAt },
    { label: '検品完了', completed: ['refunded', 'rejected'].includes(returnRecord.status), date: returnRecord.inspectedAt },
    { label: '返金処理', completed: returnRecord.status === 'refunded', date: returnRecord.refundedAt },
  ];
}

主要KPI

返品プロセスを最適化するために、以下の指標を追跡しましょう。

指標説明ベンチマーク
返品輸送時間集荷から倉庫到着までの日数国内3〜5日
処理時間倉庫受領から返金までの日数3日以内
トータル返品所要時間返品申請から返金までの日数10日以内
返品追跡に関する問い合わせ数返品状況に関するサポートチケット数50%以上削減が目標
自動返金率手動介入なしで処理された返品の割合80%以上

まとめ

返品追跡の自動化は、ペインポイントを競争優位に変えます。お客様には可視性を、サポートチームには手作業からの解放を、そしてビジネスには返金処理の迅速化をもたらします。WhereParcelのWebhookシステムと組み合わせることで、ラベル発行から返金処理まで完全に自動化されたパイプラインを構築できます。