How to Connect Stripe Webhooks to Google Sheets Using Python
This guide shows you how to capture Stripe events (such as payment_intent.succeeded) and automatically log them to a Google Sheet using Python, Flask, and the Google Sheets API. This setup uses Stripe's official SDK to verify webhook signatures, ensuring your endpoint remains secure.
1. Set Up Google Sheets API Access
Before writing Python code, you must enable API access to your Google Sheet:
- Go to the Google Cloud Console and create a new project.
- Enable the Google Drive API and Google Sheets API.
- Go to Credentials, click Create Credentials, and select Service Account.
- Generate a new JSON key for the service account and download it. Rename this file to
credentials.jsonand place it in your project directory. - Open your Google Sheet, click Share, and invite the service account email address (found in your JSON file) as an Editor.
2. Install Required Python Packages
Install the necessary libraries for handling webhooks, validating Stripe signatures, and interacting with Google Sheets:
pip install Flask stripe gspread google-auth
3. Write the Python Webhook Listener
Create a file named app.py. This script runs a local Flask server, listens for POST requests from Stripe, verifies the webhook signature, and appends the payload data to your Google Sheet.
import os
import json
from flask import Flask, request, jsonify
import stripe
import gspread
from google.oauth2.service_account import Credentials
# Configuration
stripe.api_key = "your_stripe_secret_key"
endpoint_secret = "your_stripe_webhook_signing_secret"
SPREADSHEET_ID = "your_google_sheet_id"
app = Flask(__name__)
# Initialize Google Sheets client
scopes = ["https://www.googleapis.com/auth/spreadsheets"]
creds = Credentials.from_service_account_file("credentials.json", scopes=scopes)
client = gspread.authorize(creds)
sheet = client.open_by_key(SPREADSHEET_ID).sheet1
@app.route('/webhook', methods=['POST'])
def stripe_webhook():
payload = request.data
sig_header = request.headers.get('Stripe-Signature')
event = None
try:
# Verify webhook signature
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
except ValueError as e:
# Invalid payload
return jsonify({'status': 'invalid payload'}), 400
except stripe.error.SignatureVerificationError as e:
# Invalid signature
return jsonify({'status': 'invalid signature'}), 400
# Handle specific event types
if event['type'] == 'payment_intent.succeeded':
payment_intent = event['data']['object']
amount = payment_intent['amount'] / 100 # Convert cents to dollars
currency = payment_intent['currency'].upper()
customer_email = payment_intent.get('receipt_email') or "N/A"
payment_id = payment_intent['id']
# Append data to Google Sheet
try:
sheet.append_row([payment_id, customer_email, amount, currency, "Succeeded"])
except Exception as e:
print(f"Error writing to Google Sheets: {e}")
return jsonify({'status': 'sheet write error'}), 500
return jsonify({'status': 'success'}), 200
if __name__ == '__main__':
app.run(port=4242)
4. Connect Stripe to Your Local Server
To test this setup locally, Stripe needs a public URL to send events to. Use the Stripe CLI to forward events directly to your local Flask app:
- Download and log in to the Stripe CLI.
- Run the following command to forward webhooks to your running Flask application:
stripe listen --forward-to localhost:4242/webhook
The CLI will output a webhook signing secret starting with whsec_. Copy this value and replace your_stripe_webhook_signing_secret in your app.py file.
5. Deploying to Production
When deploying this script to a live server (e.g., AWS, Heroku, or DigitalOcean):
- Ensure your production endpoint uses HTTPS.
- Replace the Stripe CLI signing secret with your live webhook signing secret from the Stripe Dashboard (Developers > Webhooks).
- Store sensitive credentials (like your Stripe API keys and Google JSON credentials) securely using environment variables rather than hardcoding them in the source code.
Need this done fast? order it on Kwork.
I take on freelance fixes and builds in this area.