1. In your Sheet: Extensions → Apps Script
2. Paste the script below → Deploy → New deployment → Web app
3. Execute as: Me · Who has access: Anyone
4. Copy the deployment URL and paste it below
function doGet(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Transactions');
var action = e.parameter.action || 'add';
if (action === 'list') {
var last = sheet.getLastRow();
if (last < 12) return out('[]', 'json');
var rows = sheet.getRange(12, 2, last - 11, 5).getValues();
var txs = [];
rows.forEach(function(r) {
var dateVal = r[0];
var dateStr = '';
try {
var d = new Date(dateVal);
if (!isNaN(d.getTime()) && d.getFullYear() > 2000) {
dateStr = Utilities.formatDate(d, Session.getScriptTimeZone(), 'yyyy-MM-dd');
}
} catch(ex) {}
if (!dateStr) return;
var amount = parseFloat(r[2]);
if (isNaN(amount) || amount <= 0) return;
if (String(r[1]) === 'PERIOD TOTAL') return;
txs.push({ date: dateStr, merchant: String(r[1] || 'Unknown'), amount: amount, note: String(r[4] || '') });
});
return out(JSON.stringify(txs), 'json');
}
if (action === 'delete') {
var dDate = e.parameter.date, dMerchant = e.parameter.merchant, dAmount = parseFloat(e.parameter.amount);
var last = sheet.getLastRow();
var rows = sheet.getRange(12, 2, last - 11, 4).getValues();
for (var i = 0; i < rows.length; i++) {
var d = new Date(rows[i][0]);
var ds = isNaN(d.getTime()) ? '' : Utilities.formatDate(d, Session.getScriptTimeZone(), 'yyyy-MM-dd');
if (ds === dDate && String(rows[i][1]) === dMerchant && Math.abs(parseFloat(rows[i][2]) - dAmount) < 0.01) {
sheet.deleteRow(12 + i);
return out('DELETED');
}
}
return out('NOT_FOUND');
}
if (action === 'update') {
var dDate = e.parameter.date, dMerchant = e.parameter.merchant, dAmount = parseFloat(e.parameter.amount);
var newDate = e.parameter.newDate, newMerchant = e.parameter.newMerchant, newAmount = parseFloat(e.parameter.newAmount);
var last = sheet.getLastRow();
var rows = sheet.getRange(12, 2, last - 11, 4).getValues();
for (var i = 0; i < rows.length; i++) {
var d = new Date(rows[i][0]);
var ds = isNaN(d.getTime()) ? '' : Utilities.formatDate(d, Session.getScriptTimeZone(), 'yyyy-MM-dd');
if (ds === dDate && String(rows[i][1]) === dMerchant && Math.abs(parseFloat(rows[i][2]) - dAmount) < 0.01) {
var nd = new Date(newDate + 'T12:00:00');
sheet.getRange(12 + i, 2, 1, 3).setValues([[nd, newMerchant, newAmount]]);
sheet.getRange(12 + i, 2).setNumberFormat('MMM D');
sheet.getRange(12 + i, 4).setNumberFormat('$#,##0.00');
return out('UPDATED');
}
}
return out('NOT_FOUND');
}
var amount = parseFloat(e.parameter.amount);
var note = e.parameter.note || 'Apple Pay';
if (isNaN(amount) || amount <= 0) return out('ERROR');
var today = new Date();
var last = sheet.getLastRow();
var col = sheet.getRange('C12:C' + last).getValues();
var ins = last + 1;
for (var i = col.length - 1; i >= 0; i--) {
if (col[i][0] === 'PERIOD TOTAL') { ins = 12 + i; break; }
}
sheet.insertRowBefore(ins);
sheet.getRange(ins, 2, 1, 5).setValues([[today, note, amount, '', '']]);
sheet.getRange(ins, 2).setNumberFormat('MMM D');
sheet.getRange(ins, 4).setNumberFormat('$#,##0.00');
return out('OK: $' + amount);
}
function out(s, t) {
var m = t === 'json' ? ContentService.MimeType.JSON : ContentService.MimeType.TEXT;
return ContentService.createTextOutput(s).setMimeType(m);
}
Apps Script Web App URL
iPhone Shortcut
After deploying the Apps Script, create a Shortcut:
1. Ask for Input → Number → "Amount?"
2. Ask for Input → Text → "Note? (optional)"
3. URL → paste your Apps Script URL, add: ?action=add&amount=[Input]¬e=[Input]
4. Get Contents of URL → Done
Add Transaction
Amount
Merchant
Date
Select a WalletPal CSV. Format: Merchant, Amount, Account, Category, Date (DD/MM/YY), Note.