# Log every Paddle payment and refund to a Google Sheet

> Every payment, refund and chargeback lands as a new row in your spreadsheet, with amounts already converted so the net column adds up.

- Workflow type: code
- Services: Paddle, Google Sheets
- Categories: Finance, Operations
- Published: 2026-08-09

## What it does

- Adds a row to your spreadsheet the moment a Paddle payment completes or a refund, credit or chargeback is issued.
- Fills in the customer's email and country, the product and plan they bought, and the currency, so you can pivot by customer, region or product.
- Records refunds and chargebacks as negative amounts, so totalling the net column gives you real revenue rather than gross sales.
- Converts Paddle's raw amounts into normal money values, including for currencies like Japanese yen that do not use cents.

## What you'll need

- A Paddle account, plus the access needed to add a notification in Paddle's Developer Tools so Paddle can tell us when a billing event happens.
- A Google account with edit access to the spreadsheet you want to use as your ledger.
- A spreadsheet with a tab set aside for the ledger and a header row across the top: event type, date, record id, customer email, country, product, plan, currency, gross, fees, net.

## Prompt

Whenever Paddle reports a billing event, append a row to a Google Sheet so I have a live transaction ledger I can pivot on without exporting reports by hand. Trigger this on a Paddle webhook. In Paddle, under Developer Tools, the notification destination should be subscribed to transaction.completed and adjustment.created. Ignore anything else that arrives: read event_type off the incoming payload and stop early if it is not one of those two.

Work out which transaction the event is about. For transaction.completed the transaction id is data.id. For adjustment.created the adjustment id is data.id and the transaction it adjusts is data.transaction_id. In both cases call Paddle Get Transaction with that transaction id to fetch the full record, which gives me details.totals, details.line_items, items, customer_id, address_id and billed_at.

Resolve the customer with Paddle Get Customer using the transaction's customer_id and take the email. Resolve the country with Paddle Get Address for Customer using that same customer_id plus the transaction's address_id, and take country_code. If the transaction has no address_id, or either lookup fails, leave those cells blank and still write the row. A missing email must never drop a revenue event from the ledger.

Take the money straight from Paddle so the figures stay internally consistent. For transaction.completed use details.totals: total as gross, fee as fees, earnings as net, and currency_code as the currency. For adjustment.created use the adjustment's own totals object, which has the same shape (total, fee, earnings, currency_code). Do not recompute net from gross minus fees.

Convert the amounts before writing. Paddle's docs state it plainly: "Monetary amounts are strings in the currency's smallest unit (for example "1000" is $10.00 USD, but "1000" is ¥1000 for zero-decimal JPY)." So do not hard-code a divide by 100. Build a small map of currency code to decimal places, defaulting to 2, with 0 for the zero-decimal currencies I bill in, Japanese yen and Korean won being the common ones. Divide by ten to the power of that number of places and write a real number rather than a string, so the sheet can sum the column.

Signs decide whether the net column is real revenue. transaction.completed rows are positive. adjustment.created rows depend on data.action: refund, credit, chargeback and chargeback_warning are money leaving, so write gross, fees and net as negative; chargeback_reverse and credit_reverse are money coming back, so keep those positive. Apply the same sign to all three amount columns so any one of them can be summed on its own.

Append exactly one row per event with Google Sheets Append Values, in this column order: event type (the raw event_type), date, record id, customer email, country, product, plan, currency, gross amount, fees, net amount. Date is the UTC calendar day formatted YYYY-MM-DD, taken from billed_at for transactions (falling back to created_at) and from the adjustment's created_at for adjustments. Record id is the transaction id for transaction.completed and the adjustment id for adjustment.created, which also makes it the key I can dedupe on if Paddle retries a delivery. Product is the line item product names from details.line_items joined with commas, and plan is the matching price names joined the same way, so a multi item transaction still writes a single row. Send the values as USER_ENTERED so dates and numbers land as dates and numbers rather than text.

This one is strictly deterministic. No summarizing, no judgement, no enrichment, no deciding whether an event is interesting. One event in, one clean row out. The sheet already has its header row, so never write headers and never read the sheet back, just append below the last row.

## How to customize

- Choose which billing events get logged. Completed payments and adjustments are the default, but you can add failed payments or subscription changes.
- Change the columns or their order, or add your own such as subscription id, tax or discount.
- Point it at a different tab per year, per brand or per currency if you want separate ledgers.

## FAQ

### Will refunds and chargebacks show up as negative numbers?

Yes. Anything that takes money back is written as a negative amount, so you can select the net column and the total you see is real revenue rather than gross sales. Reversed chargebacks are written as positive again, since that money comes back to you.

### What happens with currencies like Japanese yen that do not use cents?

Paddle sends amounts in the smallest unit of each currency, which means a value like 1000 is ten dollars in USD but a thousand yen in JPY. The ledger converts each amount using the right number of decimal places for its currency, so nothing gets divided by a hundred when it should not be.

### Do I need to set up the spreadsheet first?

Yes, and it takes a minute. Create a tab and put the column headings in the first row. From then on every event is appended underneath the last row, so the sheet just grows and nothing gets overwritten.

### Does this work with Paddle Classic or only the current Paddle?

This is built for Paddle Billing, the current version of Paddle. If you are still on Paddle Classic you would need to migrate first.

### Can I test it before pointing it at real revenue?

Yes. Paddle has a sandbox account that behaves like the real thing. Connect the sandbox first, run a few test payments and refunds, check the rows look right, then switch to your live account.

Use this prompt in General Input: https://www.generalinput.com/prompts/log-every-paddle-payment-and-refund-to-a-google-sheet