tl;dr: Mit einem Browser-Plugin und diesem Skript kann man sich in seinem American Express Portal alle Angebote zur Punktumwandlung in Orange anmarkern lassen, die einen höheren Punktwert als die üblichen 250:1 (250 Punkte für einen Euro Wert) haben.
Beim Membership Rewards Programm von American Express sammelt man mit jeder Ausgabe Punkte. Anstatt diese mit sinnlosen Anschaffungen aus dem Prämienshop zu verpulvern oder damit zum Faktor 250:1 einzelne Rechnungsposten zu bezahlen, konnte man diese früher zum Faktor 200:1 in einen Amazon-Gutschein umtauschen.
Leider hat Amex diese Möglichkeit gestrichen. Im Kundenportal kann man allerdings nach wie vor einzelne Rechnungsposten mit Punkten bezahlen, zum schlechten Faktor von 200:1. Einige Transaktionen (z.B. Gebühren von Amex selbst oder bei bestimmten Aktionen) haben allerdings einen besseren Umtauschfaktor. Damit man diese in der Liste einfach findet, werden sie von diesem Skript Orange markiert.
Wie geht das?
Angefangen hat das alles vor fast 20 Jahren mit “Greasemonkey”, einer Firefox-Erweiterung, mit der man gezielt eigene Skripten beim Betrachten fremder Webseiten laufen lassen konnte, auch und gerade mit Funktionalitäten, die der Websitebetreiber nicht möchte. Bestes Beispiel ist z.B. das Skript, das auf allen Amazon-Produktseiten einen Preisvergleich mit anderen Shops einblendet oder eine Grafik des bisherigen Preisverlaufs.
Heute gibt es verschiedene, kompatible Varianten, dieses Plug-ins, ich benutze z.B. TamperMonkey, den es für Chrome, Microsoft Edge, Safari, Opera Next und Firefox gibt.
Wenn man diesen Plug-in installiert hat, kann man mein Skript direkt aus dem GreasyFork Repository installieren. Geht man jetzt im Amex-Kundenportal auf die Option “Zahlen mit Punkten”, dann werden nach einem kurzen Moment alle Angebote entweder gelb oder orange hinterlegt, je nach Umtauschfaktor.
Umsetzung
Nachdem ich da nicht viel Zeit hineinstecken wollte, ist das alles etwas schmutzig zusammengehackt:
Die eigentlichen Transaktionsdaten werden von der Webseite per GraphQL nachgeladen und somit erst weit nach dem ursprünglichen Rendern der Seite angezeigt. Da ich keinen Hook habe, um diesen Moment mitzubekommen, bleibt mir nur, zyklisch den Zustand der Seite abzufragen. Die Amex-Server sind zudem häufig sehr, sehr langsam im Beantworten der Anfragen (30 Sekunden und Ähnliches sind nicht ungewöhnlich), sodass ich diese Überprüfung eine ganze Weile lang laufen lassen muss.
Zudem werden initial nicht alle Transaktionen angezeigt, das Nachladen erfolgt aber durch einen Klick auf den Button “alle anzeigen”, an dessen Klick-Event ich mich hier zumindest huckepack hängen kann, um das zyklische Checken der Seiten erneut zu starten, das dann die neuen, zusätzlichen Zeilen einfärbt.
Das Skript gibt es hier zur direkten Installation oder hier zum nachlesen:
// ==UserScript==
// @name Highlight Amex Membership Reward points value
// @namespace http://sisyphus.de/
// @version 1.0
// @description Mark all offers with regular point/EUR ratio yellow, all with a high ratio orange!
// @author Tobias Henöckl <hoeni-greasyfork@sisyphus.de>
// @match https://global.americanexpress.com/rewards/points-for-charges/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=americanexpress.com
// @grant none
// @license MIT
// ==/UserScript==
var threshold = 250;
var maxAttempts = 50;
var waitBetweenAttempts = 500;
var waitAfterClickMillis = 2000;
(function() {
'use strict';
var attempts = 0;
var interval = setInterval(function() {
attempts++;
var table = document.querySelector("#roc-table");
if (table || attempts > maxAttempts) {
clearInterval(interval);
highlightRows();
setupButtonListener();
}
}, waitBetweenAttempts);
})();
function highlightRows() {
var table = document.querySelector("table");
if (!table) return;
table.querySelectorAll("tr").forEach(function(row) {
var cells = row.querySelectorAll("td");
if (cells.length < 6) {
return;
}
var valueCents = parseFloat(cells[5].textContent.replace(/\./g, '').replace(/,/g, '.'), 10);
if(isNaN(valueCents)) {
return;
}
var points = parseFloat(cells[6].textContent.replace(/\./g, ''), 10);
if(isNaN(points)) {
return;
}
if (Math.round(points / valueCents) >= threshold) {
row.style.backgroundColor = "yellow";
} else {
row.style.backgroundColor = "orange";
}
});
}
function setupButtonListener() {
var viewAllButton = document.getElementById("view-all-button");
if (viewAllButton) {
viewAllButton.addEventListener('click', function() {
setTimeout(highlightRows, waitAfterClickMillis);
});
}
}