Gmail×GASで楽天ペイ利用履歴をスプレッドシートに自動集計する方法

メール通知をデータ化して、支払い履歴を自動で蓄積する

Gmailに届く楽天ペイ通知メールをGoogle Apps Scriptで取得し、利用日時、店舗、金額、ポイント利用、楽天キャッシュ、カード支払いなどをスプレッドシートへ自動集計する方法を整理します。

概要

Gmailに届く楽天ペイの利用通知メールを、Google Apps Scriptで読み取り、スプレッドシートへ自動集計する方法を整理します。

本質は、メールを読むことではありません。メール通知をデータ化することです。

決済サービスや業務ツールは、利用履歴をメールで通知してくれることがあります。その通知を人間が毎回確認するだけでは、後から集計しにくくなります。

Gmail、Google Apps Script、スプレッドシートを組み合わせると、通知メールを簡易的な業務ログとして扱えます。

何を作るのか

作るものは、楽天ペイの通知メールをGmailから検索し、必要な項目を抜き出してスプレッドシートに追記する仕組みです。

処理の流れは次のとおりです。

  • Gmailから楽天ペイ通知メールを検索する
  • メール本文をテキスト行へ変換する
  • 利用日時、店舗、金額などを抽出する
  • Message IDで重複登録を防ぐ
  • スプレッドシートへ追記する
  • 時間主導トリガーで毎日実行する

スプレッドシート自体を公開する必要はありません。あくまで自分のGoogleアカウント内で、支払い通知を整理するための仕組みです。

スプレッドシートに記録する項目

この記事では、次の項目を記録する前提にします。

  • Message ID
  • 受信日時
  • 利用日時
  • 店舗名
  • 合計金額
  • ポイント利用
  • 楽天キャッシュ
  • カード支払い
  • 元メール件名

Message IDを保存しておくと、同じメールを何度も取り込むことを防げます。

Gmail検索条件を決める

Gmailの検索条件は、できるだけ明確にします。

from:[email protected] subject:楽天ペイ

送信元だけで絞るより、件名も合わせて指定した方が、対象外のメールを拾いにくくなります。

ただし、楽天ペイ側の通知メール形式や件名は将来変わる可能性があります。実際に使う場合は、自分のGmailに届いている通知メールに合わせて検索条件を調整してください。

Google Apps Scriptの基本コード

次のコードは、楽天ペイ通知メールを検索し、スプレッドシートへ追記するサンプルです。

const CONFIG = {
  SHEET_NAME: '楽天ペイ利用履歴',
  QUERY: 'from:[email protected] subject:楽天ペイ',
  MAX_THREADS: 50
};

function importRakutenPayHistory() {
  const sheet = getOrCreateSheet_();
  setupHeader_(sheet);

  const existingIds = getExistingMessageIds_(sheet);
  const threads = GmailApp.search(CONFIG.QUERY, 0, CONFIG.MAX_THREADS);

  const rows = [];

  for (const thread of threads) {
    const messages = thread.getMessages();

    for (const message of messages) {
      const messageId = message.getId();

      if (existingIds.has(messageId)) {
        continue;
      }

      const parsed = parseRakutenPayMessage_(message);

      if (!parsed) {
        continue;
      }

      rows.push([
        messageId,
        message.getDate(),
        parsed.usedAt,
        parsed.shopName,
        parsed.totalAmount,
        parsed.pointAmount,
        parsed.cashAmount,
        parsed.cardAmount,
        message.getSubject()
      ]);
    }
  }

  if (rows.length > 0) {
    sheet.getRange(sheet.getLastRow() + 1, 1, rows.length, rows[0].length).setValues(rows);
  }
}

function getOrCreateSheet_() {
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = spreadsheet.getSheetByName(CONFIG.SHEET_NAME);

  if (sheet) {
    return sheet;
  }

  return spreadsheet.insertSheet(CONFIG.SHEET_NAME);
}

function setupHeader_(sheet) {
  if (sheet.getLastRow() > 0) {
    return;
  }

  sheet.appendRow([
    'Message ID',
    '受信日時',
    '利用日時',
    '店舗名',
    '合計金額',
    'ポイント利用',
    '楽天キャッシュ',
    'カード支払い',
    '元メール件名'
  ]);
}

function getExistingMessageIds_(sheet) {
  const lastRow = sheet.getLastRow();

  if (lastRow <= 1) {
    return new Set();
  }

  const values = sheet.getRange(2, 1, lastRow - 1, 1).getValues();
  return new Set(values.flat().filter(Boolean));
}

function parseRakutenPayMessage_(message) {
  const lines = htmlToLines_(message.getBody());

  return {
    usedAt: getValueAfterLabel_(lines, '利用日時'),
    shopName: getValueAfterLabel_(lines, '店舗'),
    totalAmount: toNumber_(getValueAfterLabel_(lines, '合計金額')),
    pointAmount: toNumber_(getValueAfterLabel_(lines, 'ポイント利用')),
    cashAmount: toNumber_(getValueAfterLabel_(lines, '楽天キャッシュ')),
    cardAmount: toNumber_(getValueAfterLabel_(lines, 'カード支払い'))
  };
}

function htmlToLines_(html) {
  return html
    .replace(/<br\s*\/?>/gi, '\n')
    .replace(/<\/p>/gi, '\n')
    .replace(/<\/div>/gi, '\n')
    .replace(/<[^>]+>/g, '')
    .replace(/&nbsp;/g, ' ')
    .replace(/&amp;/g, '&')
    .split('\n')
    .map(line => line.trim())
    .filter(Boolean);
}

function getValueAfterLabel_(lines, label) {
  const index = lines.findIndex(line => line.includes(label));

  if (index === -1) {
    return '';
  }

  return lines[index + 1] || '';
}

function toNumber_(value) {
  if (!value) {
    return 0;
  }

  const normalized = String(value).replace(/[円,\s]/g, '');
  const number = Number(normalized);

  return Number.isFinite(number) ? number : 0;
}

HTMLメールを行単位に変換する

楽天ペイ通知メールはHTMLメールとして届くことがあります。

HTMLメールを正規表現だけで一気に抜き出そうとすると、メール形式の変化に弱くなります。

そこで、まずHTMLをテキスト行に変換し、「利用日時」「店舗」「合計金額」のようなラベルを探して、その次の行を値として取得する形にしています。

この方法なら、メール本文の細かいHTML構造に依存しすぎず、比較的読みやすいコードになります。

毎日自動実行する

Apps Scriptでは、時間主導トリガーを設定できます。

たとえば、毎日1回 `importRakutenPayHistory` を実行するようにすれば、Gmailに届いた通知メールを定期的にスプレッドシートへ取り込めます。

手動でメールを確認して転記するより、記録漏れや転記ミスを減らしやすくなります。

注意点

このコードは、記事執筆時点の楽天ペイ通知メール形式を前提にしています。

メールの件名、本文のラベル、HTML構造が変わると、抽出できない項目が出る可能性があります。

また、Gmailやスプレッドシートを扱うため、初回実行時にはGoogleアカウント側で権限承認が必要です。

スプレッドシートには利用履歴が蓄積されます。共有設定を誤ると支払い情報が見える可能性があるため、公開範囲には注意してください。

まとめ

Gmailに届く楽天ペイ通知メールは、Google Apps Scriptを使うことでスプレッドシートに自動集計できます。

ポイントは、メールを読むことではなく、通知メールをデータ化することです。

Message IDで重複を防ぎ、時間主導トリガーで毎日実行すれば、支払い履歴を軽い業務ログとして蓄積できます。

メール通知は、見て終わりにするだけでなく、構造化して残すことで、後から集計・確認しやすいデータになります。