QRキャッシュレス決済手段として便利な楽天ペイ。私は普段ほとんどの買い物を楽天ペイにしています。そんな楽天ペイ、支払履歴はアプリから見ることができるのですが、自動でGoogleスプレッドシートにまとめたい!ということで、利用時にGmailに送られてくる「ご利用内容確認メール」を読み取って、GoogleスプレッドシートにまとめるGoogle Apps Scriptを作ってみました。
※Google Apps Scriptを用いてメールを読み取るため、「ご利用確認メール」はGmailに送る設定にする必要があります。
※メールを抽出するので、ご利用確認メールを削除してしまっていると当然抽出できません。アーカイブならOKです。
Google Apps Script コード
まず適当なGoogleスプレッドシートを作成し、「拡張機能」→「Apps Script」を選択して、スクリプトを新規作成します。
下記のコードを入力して、「実行」をクリックすると処理が走ります。実行ログに、抽出結果も表示されます。
function extractRakutenPayEmails() {
// 新旧どちらの件名もヒットするように検索
var query = 'from:pay.rakuten.co.jp subject:("楽天ペイアプリご利用内容確認メール" OR "お支払い完了のお知らせ")';
var threads = GmailApp.search(query);
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName("楽天ペイ") || spreadsheet.insertSheet("楽天ペイ");
if (sheet.getLastRow() === 0) {
sheet.appendRow(["ご利用日時", "決済総額", "ご利用店舗"]);
}
var existingData = sheet.getDataRange().getValues();
var newData = [];
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
var body = messages[j].getPlainBody();
// ご利用日時:▼の有無に関わらず、日付+時刻の形を直接拾う
var usageDate = null;
var dMatch = body.match(/ご利用日時[\s\S]*?([0-9]{4}\/[0-9]{1,2}\/[0-9]{1,2}\([^)]*\)\s*[0-9]{1,2}:[0-9]{2})/);
if (dMatch) {
usageDate = dMatch[1].replace(/\s+/g, ' ').trim();
}
// 決済総額:旧「1,870円」も新「¥100」も拾う(数字だけ取り出してカンマ除去)
var totalAmount = null;
var aMatch = body.match(/決済総額[\s\S]*?[¥¥]?\s*([\d,]+)\s*円?/);
if (aMatch) {
totalAmount = aMatch[1].replace(/,/g, '');
}
// ご利用店舗:項目名の次行の店舗名を拾う。
// 罫線文字(━─-)、改行、または「電話番号/▼」までを終端とする
var shop = null;
var sMatch = body.match(/ご利用店舗[\r\n\s]*([^\r\n━─]+?)(?:[\r\n]|電話番号|▼|━|─|-{10,})/);
if (sMatch) {
shop = sMatch[1].replace(/^[▼\s]+/, '').trim();
}
// どれか取れなければスキップ
if (!usageDate || !totalAmount || !shop) {
Logger.log(["抽出失敗・スキップ", messages[j].getSubject(), usageDate, totalAmount, shop]);
continue;
}
// 既存データと比較
var isExisting = false;
for (var k = 0; k < existingData.length; k++) {
var row = existingData[k];
if (row[0] == usageDate && row[1] == totalAmount && row[2] == shop) {
isExisting = true;
Logger.log(["記載済み", usageDate, totalAmount, shop]);
break;
}
}
if (!isExisting) {
newData.push([usageDate, totalAmount, shop]);
Logger.log([usageDate, totalAmount, shop]);
}
}
}
// 日時が古い順にソート
newData.sort(function(a, b) {
return new Date(a[0]) - new Date(b[0]);
});
newData.forEach(function(row) {
sheet.appendRow(row);
});
}
コードが実行している内容としては、Gmailから「楽天ペイアプリご利用確認メール」という件名のメールを抽出し、その中から「ご利用日時」「決済総額」「ご利用店舗/お支払先」の情報を抽出、配列に格納した上で、スプレッドシートの「楽天ペイ」というシートに記入するというものです。GASにそこまで詳しくないのでAIの力など借りながらなんとか作成しましたw
2024/6/25追記:楽天Payで税金などを支払った際、メールの内容が「ご利用店舗」から「お支払先」に変わるため、それに対応できるようにコードを変更しました。コメントいただいた方ありがとうございました。
//変更前
//var shop = body.match(/ご利用店舗\s*([\s\S]*?)(?:\n|-{40})/)[1].trim();
//変更後
var shop = body.match(/(ご利用店舗|お支払先)\s*([\s\S]*?)(?:\n|-{40})/)[2].trim();
2025/05/31追記:すでに記載済みの項目でもLoggerでログ出力することで、実行ログで様子を見やすくしました
Logger.log(["記載済み", usageDate, totalAmount, shop]);
2026/5/28追記:メールのパターン認識が失敗することがあったので正規表現を修正
//変更前
// ご利用日時、ご利用店舗、決済総額を抽出
var usageDate = body.match(/ご利用日時\s*([\s\S]*?)\n/)[1].trim();
var totalAmount = body.match(/決済総額\s*([\s\S]*?)\s*円/)[1].trim().replace(/,/g, '');
var shop = body.match(/(ご利用店舗|お支払先)\s*([\s\S]*?)(?:\n|-{40})/)[2].trim();
//変更後
// 改行や空白の揺れを吸収するためのヘルパー
function pick(regex) {
var m = body.match(regex);
return m ? m[m.length - 1].trim() : null;
}
// ご利用日時:日付と時刻の形だけを直接拾う(改行に依存しない)
var usageDate = pick(/ご利用日時\s*([0-9]{4}\/[0-9]{1,2}\/[0-9]{1,2}\([^)]*\)\s*[0-9]{1,2}:[0-9]{2})/);
// 決済総額:次に「円」が来るまでの数字(カンマ除去)
var totalAmount = null;
var amtMatch = body.match(/決済総額\s*([\d,]+)\s*円/);
if (amtMatch) {
totalAmount = amtMatch[1].replace(/,/g, '');
}
// ご利用店舗:店舗名のあと「電話番号」または改行・区切り線まで
var shop = pick(/(?:ご利用店舗|お支払先)\s*([\s\S]*?)(?:\s*電話番号|\n|-{10,})/);
// どれか取れなければスキップ(フォーマット違いのメール対策)
if (!usageDate || !totalAmount || !shop) {
Logger.log(["抽出失敗・スキップ", message.getSubject(), usageDate, totalAmount, shop]);
continue;
}
2026/5/29追記:5月16日以降くらいに、メールの件名や内容が仕様変更したので、追従できるようにコード変更
以下、旧コード
function extractRakutenPayEmails() {
// Gmailで該当メールを検索
var query = 'subject:"楽天ペイアプリご利用内容確認メール"';
var threads = GmailApp.search(query);
// Googleスプレッドシートを開く
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName("楽天ペイ") || spreadsheet.insertSheet("楽天ペイ");
// シートのヘッダー設定
if (sheet.getLastRow() === 0) {
sheet.appendRow(["ご利用日時", "決済総額", "ご利用店舗"]);
}
// スプレッドシートから既存のデータを取得
var existingData = sheet.getDataRange().getValues();
// 新しいデータを格納する配列を作成
var newData = [];
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
var message = messages[j];
var body = message.getPlainBody();
// 改行や空白の揺れを吸収するためのヘルパー
function pick(regex) {
var m = body.match(regex);
return m ? m[m.length - 1].trim() : null;
}
// ご利用日時:日付と時刻の形だけを直接拾う(改行に依存しない)
var usageDate = pick(/ご利用日時\s*([0-9]{4}\/[0-9]{1,2}\/[0-9]{1,2}\([^)]*\)\s*[0-9]{1,2}:[0-9]{2})/);
// 決済総額:次に「円」が来るまでの数字(カンマ除去)
var totalAmount = null;
var amtMatch = body.match(/決済総額\s*([\d,]+)\s*円/);
if (amtMatch) {
totalAmount = amtMatch[1].replace(/,/g, '');
}
// ご利用店舗:店舗名のあと「電話番号」または改行・区切り線まで
var shop = pick(/(?:ご利用店舗|お支払先)\s*([\s\S]*?)(?:\s*電話番号|\n|-{10,})/);
// どれか取れなければスキップ(フォーマット違いのメール対策)
if (!usageDate || !totalAmount || !shop) {
Logger.log(["抽出失敗・スキップ", message.getSubject(), usageDate, totalAmount, shop]);
continue;
}
// 既存のデータと比較して、新しいデータのみを追加
var isExisting = false;
for (var k = 0; k < existingData.length; k++) {
var existingRow = existingData[k];
if (existingRow[0] == usageDate && existingRow[1] == totalAmount && existingRow[2] == shop) {
isExisting = true;
Logger.log(["記載済み", usageDate, totalAmount, shop]);
break;
}
}
if (!isExisting) {
newData.push([usageDate, totalAmount, shop]);
Logger.log([usageDate, totalAmount, shop]);
}
}
}
// 新しいデータを日時が古い順にソート
newData.sort(function(a, b) {
var dateA = new Date(a[0]);
var dateB = new Date(b[0]);
return dateA - dateB;
});
// ソートされた新しいデータをスプレッドシートに追加
newData.forEach(function(row) {
sheet.appendRow(row);
});
}


コメント
まさにやりたいことを実現できました。
素晴らしい記事ありがとうございます。
楽天payで都税を払ったところ、
メール本文に「ご利用店舗」という文字列がなく、代わりに「お支払い先」という文字がでてきたので、
この部分を
var shop = body.match(/ご利用店舗\s*([\s\S]*?)(?:\n|-{40})/)[1].trim();
以下のように修正させていただき使わせていただきました。
var shop = body.match(/(ご利用店舗|お支払先)\s*([\s\S]*?)(?:\n|-{40})/)[2].trim();
有益な情報ありがとうございました。
コメントありがとうございます。変更を反映させていただきました。こちらこそ有益な情報ありがとうございます。