How to Auto-Process Stripe Payments and Log to Google Sheets with n8n

Every Stripe payment you receive can be automatically logged to Google Sheets in real time — no manual exports, no missed transactions, no more downloading CSVs at the end of the month. With this n8n workflow, the moment a payment succeeds in Stripe, a new row appears in your spreadsheet with the customer name, email, amount, description, and payment method already filled in. If the payment is above a threshold you set, you’ll get an email alert too.

Prefer to skip the setup? Grab the ready-made template → and be up and running in under 10 minutes.

What You’ll Build

  1. A Stripe webhook listener that fires the moment a payment succeeds — no polling, no delays.
  2. A data formatting step that converts Stripe’s raw event data (amounts in cents, Unix timestamps) into clean, human-readable fields.
  3. An automatic Google Sheets append that logs every payment as a new row in a structured ledger.
  4. A conditional check that sends you an email notification for any payment over $500 so you never miss a big order.

How It Works — The Big Picture

n8n listens for Stripe’s payment_intent.succeeded webhook event. Each time a payment completes, the workflow wakes up, reformats the data into tidy fields, writes a row to your Google Sheet, then checks whether the amount qualifies as high-value — sending an alert email if so.

+----------------------------------------------------------------+
|  AUTO-PROCESS STRIPE PAYMENTS to GOOGLE SHEETS                 |
|                                                                |
|  [Stripe Trigger]                                              |
|        |  payment_intent.succeeded fires                       |
|        v                                                       |
|  [Format Payment Data]                                         |
|        |  cents-to-dollars, Unix-to-date, map fields           |
|        v                                                       |
|  [Log to Google Sheets]                                        |
|        |  append row: ID, date, name, email, amount            |
|        v                                                       |
|  [Check High Value Payment]                                    |
|        | amount >= $500?                                       |
|       YES -------------------------------------------+         |
|        |                                             v         |
|       NO (end)                        [Send High Value Alert]  |
|                                          email notification    |
+----------------------------------------------------------------+
  

What You’ll Need

  • An n8n instance — cloud or self-hosted (n8n.io; free tier works)
  • A Stripe account with at least one product or payment link set up
  • A Google account with Google Sheets access (free)
  • An SMTP account for alert emails — Gmail App Password, SendGrid, or any SMTP provider (optional but recommended)
  • A Google Sheet with the correct column headers (details in the Data Structure section below)

Estimated build time: 35–50 minutes from scratch, or under 10 minutes with the template.

Building the Workflow — Step by Step

1 Stripe Trigger (stripeTrigger)

This node creates a webhook endpoint in n8n that Stripe calls whenever a payment event fires. It’s the entry point for the entire workflow — everything else is downstream of this single trigger.

To configure it:

  1. In n8n, add a new node and search for Stripe Trigger.
  2. Connect your Stripe credential. If you don’t have one yet, click Create new, paste your Stripe Restricted API key (with webhook read permissions), and save.
  3. Under Events, select payment_intent.succeeded. This fires every time a payment is fully authorized and captured.
  4. Save the workflow. n8n will display a webhook URL — copy it.
  5. In your Stripe dashboard, go to Developers → Webhooks → Add endpoint, paste the n8n URL, and select the payment_intent.succeeded event.
💡

Tip: Use Stripe’s Send test webhook button right after adding the endpoint to confirm n8n receives it. You’ll see the raw payload in n8n’s execution log — this is what the next node will process.

A typical Stripe payment_intent.succeeded payload looks like this:

{
  "id": "pi_3QxT4kABCDEFGHIJ12345678",
  "object": "payment_intent",
  "amount": 4999,
  "currency": "usd",
  "status": "succeeded",
  "description": "Order #1042 — Premium Plan",
  "receipt_email": "emily.rodriguez@gmail.com",
  "billing_details": {
    "name": "Emily Rodriguez",
    "email": "emily.rodriguez@gmail.com"
  },
  "created": 1743865200,
  "payment_method_types": ["card"]
}

Notice that amount is in cents (4999 = $49.99) and created is a Unix timestamp. The next node handles the conversion.

2 Format Payment Data (Set)

The raw Stripe payload is useful, but messy. This Set node maps the data into clean, consistently named fields that Google Sheets can receive directly — no JavaScript required inside the sheet.

Add a Set node connected to the Stripe Trigger, then add these fields in Manual mapping mode:

Field Name n8n Expression Result Example
payment_id ={{ $json.id }} pi_3QxT4kABC…
customer_name ={{ $json.billing_details?.name || 'Unknown' }} Emily Rodriguez
customer_email ={{ $json.receipt_email || 'N/A' }} emily.rodriguez@gmail.com
amount_usd ={{ ($json.amount / 100).toFixed(2) }} 49.99
currency ={{ $json.currency.toUpperCase() }} USD
description ={{ $json.description || 'Payment' }} Order #1042 — Premium Plan
status ={{ $json.status }} succeeded
payment_date ={{ new Date($json.created * 1000).toLocaleDateString('en-US') }} 4/5/2026
payment_method ={{ $json.payment_method_types?.[0] || 'card' }} card
💡

Tip: The ?.name optional chaining prevents the workflow from erroring when billing_details is null — which happens with some payment types like bank transfers. The || 'Unknown' fallback ensures your sheet always gets a value.

After this node, data looks like this:

{
  "payment_id": "pi_3QxT4kABCDEFGHIJ12345678",
  "customer_name": "Emily Rodriguez",
  "customer_email": "emily.rodriguez@gmail.com",
  "amount_usd": "49.99",
  "currency": "USD",
  "description": "Order #1042 — Premium Plan",
  "status": "succeeded",
  "payment_date": "4/5/2026",
  "payment_method": "card"
}

3 Log to Google Sheets (googleSheets)

This node appends a new row to your Payments sheet every time a payment is processed. It uses the Append or Update operation — if a payment ID already exists (e.g., a duplicate webhook retry), it updates the existing row instead of creating a duplicate.

  1. Add a Google Sheets node and connect your Google OAuth2 credential.
  2. Set Operation to Append or Update.
  3. Paste your Spreadsheet ID (the long string in your Google Sheet’s URL).
  4. Set Sheet Name to Payments.
  5. Under Columns, switch to Define below and map each column header to its expression from the previous Set node (e.g., Payment ID={{ $json.payment_id }}).
  6. Set Matching Columns to Payment ID so duplicate events update instead of duplicate.
📌

Note: The column headers in your Google Sheet must match exactly — including capitalization and spacing. If you name the column “payment id” (lowercase) but the node sends “Payment ID”, the data will land in the wrong column or create a new one.

4 Check High Value Payment (IF)

After logging, the workflow checks whether the payment amount is $500 or more. Payments that clear this threshold go down the true branch to trigger an email alert; all others exit quietly.

  1. Add an IF node connected to the Google Sheets node.
  2. Set the Value 1 expression to ={{ parseFloat($('Format Payment Data').item.json.amount_usd) }}.
  3. Set Operation to Greater than or equal to.
  4. Set Value 2 to 500.
💡

Tip: You can change the threshold to any amount that makes sense for your business. A SaaS charging $29/month might alert at $200; an agency billing clients might only care about payments above $2,000.

5 Send High Value Alert (emailSend)

When a payment exceeds the threshold, this node fires off an email with all the relevant details so you can follow up personally, flag the account, or just celebrate a big win.

  1. Add a Send Email node to the true output of the IF node.
  2. Connect your SMTP credential (Gmail App Password works great; see the Credentials Guide).
  3. Set From to your sender address and To to your notification email.
  4. Set Subject to: =High-Value Payment: ${{ $('Format Payment Data').item.json.amount_usd }} from {{ $('Format Payment Data').item.json.customer_name }}
  5. Add a plain-text or HTML body with all the payment fields from the Format node.

A sample alert email body:

High-Value Payment Received

Customer: James Carter
Email: james.carter@gmail.com
Amount: $1,250.00
Description: Order #2089 — Enterprise Annual License
Date: 4/5/2026
Payment ID: pi_3QxT4kABCDEFGHIJ12345678

The Data Structure

Your Google Sheet needs a tab named Payments with these column headers in row 1 (exact capitalization matters):

Column Header Type Example Description
Payment ID Text pi_3QxT4kABC… Stripe’s unique payment intent ID — used to deduplicate retried webhooks
Date Text 4/5/2026 US-formatted payment date derived from Stripe’s Unix timestamp
Customer Name Text Emily Rodriguez Billing name from Stripe; falls back to “Unknown” if not provided
Customer Email Text emily.rodriguez@gmail.com Receipt email address; falls back to “N/A” if absent
Amount (USD) Number 49.99 Payment amount in dollars (converted from cents)
Currency Text USD ISO currency code, uppercased
Description Text Order #1042 — Premium Plan Stripe payment description or product name
Status Text succeeded Payment status from Stripe (always “succeeded” for this trigger)
Payment Method Text card Payment method type (card, bank_transfer, etc.)

Here’s what a few rows look like once data starts flowing:

Payment ID Date Customer Name Amount (USD) Status
pi_3QxT4k… 4/5/2026 Emily Rodriguez 49.99 succeeded
pi_3QxT7m… 4/5/2026 James Carter 1250.00 succeeded
pi_3QxV2n… 4/5/2026 Sarah Thompson 29.00 succeeded
📌

Create the sheet and add the column headers before activating the workflow. n8n’s Google Sheets node expects the headers to already exist in row 1 — it doesn’t create them automatically.

Full System Flow

  Customer Pays via Stripe
          |
          v
  Stripe fires payment_intent.succeeded
          |
          v
  +----------------------------+
  |   n8n Stripe Trigger       |  <-- webhook endpoint registered in Stripe dashboard
  +------------+---------------+
               | raw Stripe payload (cents, Unix timestamps)
               v
  +----------------------------+
  |  Format Payment Data       |  <-- Set node: converts & maps fields
  +------------+---------------+
               | clean fields: name, email, $amount, date
               v
  +----------------------------+
  |  Log to Google Sheets      |  <-- appends or updates row by Payment ID
  +------------+---------------+
               | row written OK
               v
  +----------------------------+
  | Check High Value Payment   |  <-- IF: amount_usd >= 500?
  +------+-------------+-------+
        YES            NO
         |              |
         v             end
  +-----------------+
  | Send Alert Email|  <-- SMTP: notify you of big payment
  +-----------------+
  

Testing Your Workflow

Before going live, test with Stripe’s built-in test mode:

  1. In Stripe dashboard, switch to Test mode (toggle in the top-left).
  2. Go to Developers → Webhooks → select your endpoint → click Send test webhook → choose payment_intent.succeeded.
  3. Open n8n’s execution log — you should see the workflow trigger and all 5 nodes complete successfully.
  4. Check your Google Sheet — a new row should appear with the test payment data.
  5. To test the high-value alert, temporarily lower the threshold to $1 in the IF node, trigger another test, and check your inbox.
Problem Likely Cause Fix
Workflow doesn’t trigger at all Webhook URL not registered in Stripe, or n8n not publicly reachable Check Stripe → Webhooks; for self-hosted n8n, ensure your instance has a public URL (not localhost)
Google Sheets shows data in wrong columns Column header mismatch Check that headers in row 1 of your sheet exactly match the field names in the Set node (case-sensitive)
Amount appears as “NaN” or empty $json.amount is undefined for some event types Confirm the trigger is set to payment_intent.succeeded not charge.succeeded
Duplicate rows in Google Sheets Stripe retries the webhook if n8n doesn’t return 200 fast enough Ensure your Matching Columns is set to “Payment ID” in the Google Sheets node
High-value alert email not arriving SMTP credential invalid, or spam filter Test SMTP connection in n8n; check spam folder; use an App Password for Gmail

Frequently Asked Questions

Does this work with Stripe Checkout, Payment Links, and direct API charges?

Yes — the payment_intent.succeeded event fires for all of them. Whether the payment comes from a Stripe Checkout session, a Payment Link, a subscription renewal, or a manual API charge, a PaymentIntent is always created under the hood and this workflow will catch it.

What happens if Stripe sends the same webhook twice (retry)?

Stripe retries failed webhooks up to 3 days. Because the Google Sheets node is configured with Append or Update and Payment ID as the matching column, a duplicate event will update the existing row instead of creating a second one. Your ledger stays clean.

Can I log refunds or failed payments too?

Absolutely. In the Stripe Trigger node, add additional events: charge.refunded for refunds and payment_intent.payment_failed for failures. You can add an IF or Switch node after the trigger to route each event type to a different sheet tab.

Can I use this with n8n Cloud, or does it need to be self-hosted?

Either works. n8n Cloud instances have a public URL by default, so webhook registration in Stripe is straightforward. Self-hosted instances need a public URL too — if yours is behind a router, use a service like Cloudflare Tunnel or ngrok to expose it. Once the URL is registered, behavior is identical.

Can I send the alert to Slack instead of email?

Yes — just swap the Send Email node for a Slack node set to Post Message. Connect your Slack OAuth credential, pick your alert channel, and use the same expressions to build the message text. You can even have both: add both a Send Email and a Slack node to the true branch of the IF node.

What if a customer’s name or email is missing from the Stripe event?

The Set node uses JavaScript optional chaining (?.name) and fallback values (|| 'Unknown' and || 'N/A'). This means even if Stripe sends an event with no billing details, the workflow will still complete and log the row — just with placeholder values instead of blank cells.

🚀 Get the Stripe to Google Sheets Template

Skip the setup and get the complete n8n workflow JSON, a pre-configured Google Sheet template, a step-by-step Setup Guide PDF, and a Credentials Guide PDF — everything you need to go live in minutes.

Get the Template →

Instant download · Works on n8n Cloud and self-hosted

What’s Next?

  • Add a Slack notification for every payment (not just high-value) — great for small teams who want a #payments channel in real time.
  • Create a monthly revenue summary by adding a scheduled workflow that queries your Google Sheet, calculates MRR, and emails you a report on the 1st of each month.
  • Sync to a CRM — add a HubSpot or Airtable node after the Google Sheets step to create or update a deal every time a payment lands.
  • Handle subscriptions separately — listen for invoice.payment_succeeded to capture recurring Stripe subscription payments on their own sheet tab with subscriber lifetime value tracking.
n8n
Stripe
Google Sheets
payment automation
no-code
webhooks
automation