Stripe Integration Guide
Overview
Pivot integrates with Stripe to manage subscription billing for companies. This integration automatically tracks subscription status changes and updates company records in Firebase Realtime Database.
What Does the Stripe Integration Do?
- Track subscription lifecycle: Monitor when subscriptions are created, updated, cancelled, or renewed
- Update company status: Automatically enable/disable company access based on subscription status
- Sync subscription data: Keep company records up-to-date with current subscription information
- Handle payment events: Process subscription-related payment events from Stripe
Architecture
System Overview
Complete Subscription Lifecycle
Data Flow - New Subscription
Data Flow - Payment Failure & Company Lock
Data Flow - Payment Success & Company Unlock
Environment Configuration
Pivot uses three environments for Stripe integration, each with isolated Stripe accounts and Firebase projects.
Environment Mapping
| Environment | Firebase Project | Stripe Account | Webhook URL |
|---|---|---|---|
| Development | pivot-dev-59310 | Test Mode | https://us-central1-pivot-dev-59310.cloudfunctions.net/stripeWebHookHandler |
| Staging | pivot-not-production-project | Test Mode | https://us-central1-pivot-not-production-project.cloudfunctions.net/stripeWebHookHandler |
| Production | pivot-inc | Live Mode | https://us-central1-pivot-inc.cloudfunctions.net/stripeWebHookHandler |
Environment Secrets
Each environment requires two Stripe credentials stored as GitHub Actions secrets:
- STRIPE_API_KEY: Used by Firebase Functions to make API calls to Stripe
- STRIPE_WEBHOOK_SECRET: Used to verify webhook signatures
Development Environment
# GitHub Secret: FIREBASE_ENV_DEV
STRIPE_API_KEY=sk_test_51SUqwV... # Full secret key
STRIPE_WEBHOOK_SECRET=whsec_0cXpeHoO...
Staging Environment
# GitHub Secret: FIREBASE_ENV_STAGING
STRIPE_API_KEY=sk_test_51SVxTY... # Full secret key
STRIPE_WEBHOOK_SECRET=whsec_BcDX8iXm...
Production Environment
# GitHub Secret: FIREBASE_ENV_PRODUCTION
STRIPE_API_KEY=rk_live_51QFcV3... # Restricted key (read-only)
STRIPE_WEBHOOK_SECRET=whsec_XGRKhEIt...
Security Note: Production uses a restricted API key with only
customers:readandsubscriptions:readpermissions for enhanced security.
Webhook Configuration
Webhook Events
The integration listens to these Stripe events:
customer.subscription.created- New subscription createdcustomer.subscription.updated- Subscription modified (plan change, renewal, etc.)customer.subscription.deleted- Subscription cancelled or expired
Webhook Setup Process
Webhooks are configured using the create-webhook.sh script in the pivot-devops repository:
# Location: pivot-devops/github/stripe/create-webhook.sh
# Example: Create staging webhook
./create-webhook.sh \
"sk_test_xxx" \
"https://us-central1-pivot-not-production-project.cloudfunctions.net/stripeWebHookHandler" \
customer.subscription.created \
customer.subscription.updated
The script:
- Creates a webhook endpoint in Stripe
- Configures the events to listen for
- Returns the webhook signing secret (
whsec_...) - This secret must be added to the environment variables
Webhook Security
Each webhook request includes a signature in the Stripe-Signature header. The function verifies this using STRIPE_WEBHOOK_SECRET to ensure the request came from Stripe.
Data Updates
Company Record Structure
When a subscription event occurs, the following company data is updated in Firebase:
interface Company {
// Stripe Integration Fields
stripeSubscriptionId: string; // Stripe subscription ID (e.g., "sub_1ABC123")
subscriptionStatus: string; // Subscription status from Stripe
isSuspended: boolean | null; // null = active, true = locked
// Other company fields...
name: string;
email: string;
// ...
}
Subscription Status Values
| Status | Description | Company Access | isSuspended Value |
|---|---|---|---|
active | Subscription is active and paid | Unlocked | null |
trialing | Free trial period | Unlocked | null |
past_due | Payment failed, retrying | Locked | true |
canceled | Subscription cancelled | Locked | true |
incomplete_expired | Initial payment failed after retry period | Locked | true |
Update Logic
Database Path
Company subscription data is stored directly on the company record:
/Companies/{companyId}/
Example - Active Subscription:
{
"Companies": {
"-ABC123": {
"name": "Acme Restaurant",
"email": "billing@acme.com",
"stripeSubscriptionId": "sub_1ABC123xyz",
"subscriptionStatus": "active",
"isSuspended": null
}
}
}
Example - Failed Payment (Company Locked):
{
"Companies": {
"-ABC123": {
"name": "Acme Restaurant",
"email": "billing@acme.com",
"stripeSubscriptionId": "sub_1ABC123xyz",
"subscriptionStatus": "canceled",
"isSuspended": true
}
}
}
Implementation Details
Cloud Function
Location: pivot/functions/integrations/stripe/stripe-webhook-handler.ts
Function Name: stripeWebHookHandler
Deployment: Automatically deployed via GitHub Actions when code is merged to main/staging branches
Local Testing
For local development, use the Stripe CLI to forward webhooks:
# Start Firebase emulators
firebase emulators:start --only functions,database
# In another terminal, forward webhooks
stripe listen --forward-to \
http://localhost:5001/pivot-dev-59310/us-central1/stripeWebHookHandler
# Copy the webhook signing secret (whsec_...) displayed
# Update functions/integrations/stripe/stripe-webhook-handler.ts temporarily
Trigger test events:
stripe trigger customer.subscription.created
stripe trigger customer.subscription.updated
stripe trigger customer.subscription.deleted
Managing Stripe Credentials
Creating Restricted API Keys (Production)
For production, use restricted keys with minimal permissions:
Required Permissions:
customers→ Readsubscriptions→ Read
Steps:
- Go to Stripe Dashboard → Developers → API Keys
- Click "Create restricted key"
- Name:
Pivot Production - Subscription Webhook Handler - Set permissions:
- Customers: Read
- Subscriptions: Read
- Save and copy the
rk_live_...key
Note: Restricted keys cannot be created via API - they must be created through the Stripe Dashboard.
Updating Secrets in pivot-devops
Secrets are managed in the pivot-devops repository:
pivot-devops/github/secrets/
├── env.dev # Development secrets
├── env.default # Staging secrets
└── env.production # Production secrets
After updating these files, apply with Terraform:
cd pivot-devops/github
terraform apply -target=github_actions_secret.firebase_env_production
This updates the GitHub Actions secrets that are used during deployment.
Monitoring and Troubleshooting
Checking Webhook Status
In Stripe Dashboard:
- Go to Developers → Webhooks
- Click on the webhook endpoint
- View recent deliveries and their status
Common Issues
Webhook Signature Verification Failed
Symptoms: Stripe shows 401 responses
Causes:
- Wrong
STRIPE_WEBHOOK_SECRETin environment variables - Environment variable not deployed to Firebase Functions
Solution:
- Verify the secret in
pivot-devops/github/secrets/env.* - Redeploy functions:
firebase deploy --only functions:stripeWebHookHandler
Company Not Found
Symptoms: Function logs show "Company not found" errors
Causes:
- Company record missing
customerIdfield in database - Mismatch between Stripe customer ID and database
Solution:
- Check company record in Firebase Console
- Verify
customerIdmatches Stripe customer ID - Manually update if needed:
/companies/{companyId}/customerId
Subscription Not Updating
Symptoms: Company status doesn't change when subscription changes
Causes:
- Function not deployed
- Wrong Stripe API key (different account)
- Webhook not configured in Stripe
Solution:
- Check function logs in Firebase Console
- Verify webhook is configured for correct environment
- Test webhook delivery from Stripe Dashboard
Logs
View function logs:
# Firebase Console
firebase functions:log --only stripeWebHookHandler
# Or in Firebase Console UI:
# Functions → stripeWebHookHandler → Logs tab
Look for:
[Stripe Webhook] Received webhook request[Stripe Webhook] Event verified: {...}[Stripe Webhook] Processing subscription: {...}[Stripe Webhook] Database updated successfully
Deployment
GitHub Actions Workflow
Deployment is automated via GitHub Actions in the pivot repository:
Workflow File: .github/workflows/deploy.yml
Deployment Targets:
- Dev: Merges to
devbranch → Deploy topivot-dev-59310 - Staging: Merges to
stagingbranch → Deploy topivot-not-production-project - Production: Merges to
mainbranch → Deploy topivot-inc
The workflow:
- Checks out code
- Loads environment secrets from GitHub
- Deploys functions to Firebase
- Stripe credentials are injected as environment variables
Manual Deployment
If needed, deploy manually:
# Ensure you're using the correct Firebase project
firebase use staging # or production
# Deploy only the Stripe webhook function
firebase deploy --only functions:stripeWebHookHandler
Security Best Practices
API Key Security
- ✅ DO: Use restricted keys in production
- ✅ DO: Rotate keys periodically
- ✅ DO: Store keys in GitHub Secrets, never in code
- ❌ DON'T: Commit keys to version control
- ❌ DON'T: Use production keys in development
- ❌ DON'T: Share keys in Slack or email
Webhook Security
- ✅ DO: Always verify webhook signatures
- ✅ DO: Use HTTPS endpoints only
- ✅ DO: Keep webhook secrets secure
- ❌ DON'T: Skip signature verification
- ❌ DON'T: Expose webhook secrets in logs
Reference
Useful Links
- Stripe Dashboard: https://dashboard.stripe.com
- Stripe API Docs: https://stripe.com/docs/api
- Stripe Webhooks Guide: https://stripe.com/docs/webhooks
- Firebase Console: https://console.firebase.google.com
- pivot-devops Repository: https://github.com/pivotteam/pivot-devops
Scripts
Located in pivot-devops/github/stripe/:
create-webhook.sh- Creates webhook endpoints via Stripe APIcreate-restricted-key.sh- Documentation for creating restricted keys (requires Dashboard)
Related Documentation
Getting Help
For issues:
- Stripe API errors: Check Stripe Dashboard → Developers → Logs
- Webhook delivery failures: Check Stripe Dashboard → Developers → Webhooks → Event log
- Function errors: Check Firebase Console → Functions → Logs
- Deployment issues: Check GitHub Actions workflow logs
Contact:
- Stripe account issues: Account owner or billing admin
- Technical issues: Development team
- Security concerns: DevOps team