Stripe Integration Summary
Quick Reference Guide for Issue #202 and #216
π― Key Decision: Credit Burndown Modelβ
Answer to your question: Stripe integration happens ONLY during balance top-up, NOT during balance consumption.
Why?β
Balance Top-Up (Stripe API):
User clicks "Add $50" β Stripe processes payment β Balance updated in Firestore
Frequency: Low (maybe once per month)
Stripe Cost: ~2.9% + $0.30 per transaction
Balance Consumption (Local Only):
User runs task β Check local balance β Deduct from Firestore β No Stripe API
Frequency: High (100s per day)
Stripe Cost: $0 (all local)
Result: Simple, fast, cheap! π
π Architecture Diagramβ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β USER ACTIONS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
[Top Up $50] [Run Task $2.50]
β β
βΌ βΌ
ββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
β TOP-UP FLOW β β CONSUMPTION FLOW β
β (Stripe Integration) β β (Local Only) β
ββββββββββββββββββββββββββββββββ ββββββββββββββββββββββββββββββββ
β β
β 1. Create PaymentIntent β 1. Check balance
ββββββββββΊ Stripe API ββββββββββΊ Firestore
β β
β 2. Confirm payment (frontend) β 2. Deduct amount
ββββββββββΊ Stripe.js ββββββββββΊ Firestore
β β
β 3. Webhook: payment_succeeded β 3. Create transaction
ββββββββββ€ Stripe ββββββββββΊ Firestore
β β
β 4. Update balance β 4. Return new balance
ββββββββββΊ Firestore ββββββββββ€ Firestore
β β
β 5. Create CREDIT transaction β (Done - no more API calls)
ββββββββββΊ Firestore β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FIRESTORE COLLECTIONS β
ββββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ€
β billing_profiles β billing_transactions β
β - user@example.com β - txn_123 (CREDIT: +$50) β
β - available_balance: $300 β - txn_124 (DEBIT: -$2.50) β
β - stripe_customer_id β - txn_125 (DEBIT: -$1.20) β
β - complimentary_credits β - ... β
ββββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββ
β What You Already Haveβ
Backend:
- β
billing.proto- Complete schema - β
BillingServiceImpl- Fully implemented (mock mode) - β
StripeService- Ready for real mode - β Firestore collections - Set up and working
- β Transaction tracking - Working
- β Balance deduction - Atomic and safe
Frontend:
- β Billing page UI
- β Balance display in top bar
- β Transaction history
- β Expense tracking by project
Dependencies:
- β
Stripe Java SDK (v29.5.0) in
pom.xml
π What You Need to Doβ
Phase 1: Development Setup (This Week)β
1. Create Stripe Test Account
# Visit: https://dashboard.stripe.com/register
# Complete signup
# Toggle to "Test mode"
2. Get API Keys
# From Stripe Dashboard β Developers β API keys
STRIPE_PUBLISHABLE_KEY=pk_test_51xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
STRIPE_SECRET_KEY=sk_test_51xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
π Security Note: Test vs. Production Keys
| Key Type | Prefix | Can Process Real Payments? | Safe to Commit? | Storage |
|---|---|---|---|---|
| Test Secret | sk_test_ | β No (test mode only) | β οΈ Not recommended | Use Secret Manager |
| Test Publishable | pk_test_ | β No (test mode only) | β οΈ Not recommended | Use Secret Manager |
| Prod Secret | sk_live_ | β YES! Real money! | β NEVER! | MUST use Secret Manager |
| Prod Publishable | pk_live_ | Used with secret key | β Safe (client-side) | Can commit (domain-restricted) |
Why NOT commit even test keys?
- Someone could spam your Stripe test account
- Leaked patterns help attackers
- Accidentally promoting testβprod with keys in code
- Best practice: No secrets in git, ever
3. Configure Environment
β οΈ IMPORTANT: Secret Storage Strategy
Even for test/dev environments, use Google Secret Manager (not vars.yaml):
# DO NOT add Stripe keys to vars.yaml (it's committed to git!)
# Instead, use Secret Manager for ALL environments:
# Development environment
ENV=dev
GCP_PROJECT_ID="construction-code-expert-${ENV}"
# Create secrets (test keys for dev)
echo -n "sk_test_51xxxxx" | gcloud secrets create stripe-secret-key \
--data-file=- \
--project=${GCP_PROJECT_ID}
# NOTE: Get webhook secret AFTER creating webhook endpoint (see step 3a below)
echo -n "whsec_xxxxx" | gcloud secrets create stripe-webhook-secret \
--data-file=- \
--project=${GCP_PROJECT_ID}
echo -n "pk_test_51xxxxx" | gcloud secrets create stripe-publishable-key \
--data-file=- \
--project=${GCP_PROJECT_ID}
3a. How to Get Webhook Secret:
The webhook secret is NOT in your API keys - you get it when creating a webhook endpoint:
For Local Development (using Stripe CLI):
# Run Stripe CLI webhook forwarding
stripe listen --forward-to localhost:8080/v1/billing/webhook
# This outputs a webhook signing secret like:
# > Ready! Your webhook signing secret is whsec_1234567890abcdef...
# Copy this secret and use it in the command above
For Cloud Run (deployed environments):
- Deploy your backend first (so you have a webhook URL)
- Go to Stripe Dashboard β Developers β Webhooks
- Click "Add endpoint"
- Enter webhook URL:
https://your-app.run.app/v1/billing/webhook - Click "Select events" and choose:
payment_intent.succeededpayment_intent.payment_failed
- Click "Add endpoint"
- On the endpoint details page, click "Reveal" next to "Signing secret"
- Copy the secret (starts with
whsec_) - Store it in Secret Manager using the command above
Order of operations:
- β Get API keys from Stripe Dashboard (available immediately)
- β Store API keys in Secret Manager
- β Deploy backend to get webhook URL
- β Create webhook endpoint in Stripe Dashboard
- β Get webhook secret from webhook endpoint details
- β Store webhook secret in Secret Manager
- β Redeploy backend with webhook secret mounted
Update vars.yaml to enable Stripe (but NOT store keys):
# env/dev/gcp/cloud-run/grpc/vars.yaml
STRIPE_ENABLED: "true" # Enable Stripe integration
# Note: Actual keys loaded from Secret Manager, not here!
Update deployment to mount secrets:
# When deploying, mount secrets as environment variables
gcloud run deploy construction-code-expert-grpc \
--set-env-vars-file=env/dev/gcp/cloud-run/grpc/vars.yaml \
--set-secrets="STRIPE_SECRET_KEY=stripe-secret-key:latest,STRIPE_WEBHOOK_SECRET=stripe-webhook-secret:latest" \
--project=construction-code-expert-dev
Why use Secret Manager even for test keys?
- β Practice good habits in all environments
- β Prevents accidental exposure in public repos
- β Easy to rotate keys without changing code
- β Audit trail of who accessed secrets
- β Same pattern for dev, test, and prod
Frontend publishable key (exception):
The STRIPE_PUBLISHABLE_KEY for frontend can go in setvars.sh:
# env/dev/firebase/m3/setvars.sh
export STRIPE_PUBLISHABLE_KEY=pk_test_51xxxxx
This is acceptable because:
- β Publishable keys are meant to be public (used in browser JavaScript)
- β They can't process payments without the secret key
- β You can restrict them to specific domains in Stripe Dashboard
- β Frontend environment files are typically committed
However, backend secret keys (sk_test_, sk_live_) MUST use Secret Manager!
4. Update BillingServiceImpl
// Change from:
this.stripeService = new StripeService(); // Mock mode
// To:
String secretKey = System.getenv("STRIPE_SECRET_KEY");
String webhookSecret = System.getenv("STRIPE_WEBHOOK_SECRET");
boolean enabled = "true".equals(System.getenv("STRIPE_ENABLED"));
if (enabled && secretKey != null) {
this.stripeService = new StripeService(secretKey, webhookSecret, false);
} else {
this.stripeService = new StripeService(); // Mock mode
}
5. Install Stripe.js in Frontend
cd web-ng-m3
npm install @stripe/stripe-js
6. Test Payment Flow
1. Load billing page
2. Click "Add Funds"
3. Enter: $25
4. Card: 4242 4242 4242 4242
5. Verify: Balance updates to $325
π Implementation Checklistβ
Backendβ
- Update
BillingServiceImplto read env vars - Test Stripe customer creation
- Test PaymentIntent creation
- Implement webhook endpoint
- Test webhook signature verification
- Test balance update on webhook
Frontendβ
- Add Stripe.js package
- Add publishable key to environment
- Create payment dialog component
- Add Stripe Elements (card input)
- Implement payment confirmation
- Handle 3D Secure authentication
- Add error handling
Testingβ
- Test with test card 4242...
- Test with 3D Secure card 4000 0027 6000 3184
- Test with declined card 4000 0000 0000 0002
- Test webhook locally with Stripe CLI
- Test concurrent payments
- Test balance reconciliation
Productionβ
- Activate Stripe live account
- Store secrets in Secret Manager
- Configure production webhook
- Deploy to production
- Test with small real payment
- Monitor for issues
π Security Best Practicesβ
β DO:
- Store secret keys in Google Secret Manager
- Validate webhook signatures
- Use HTTPS everywhere
- Log payment events (without sensitive data)
- Implement rate limiting
- Use atomic Firestore transactions
β DON'T:
- Commit secret keys to git
- Log card numbers or CVV
- Skip webhook signature verification
- Allow negative balances
- Process payments without authentication
- Store unnecessary PII
π Support & Troubleshootingβ
Common Issuesβ
"Payment processing failed"
- Check Stripe Dashboard β Logs
- Verify API keys are correct
- Check card details are valid
"Webhook not received"
- Check webhook URL is publicly accessible
- Verify webhook secret matches
- Check Stripe Dashboard β Webhooks β Recent deliveries
"Balance not updating"
- Check webhook processed successfully
- Look for errors in Cloud Run logs
- Verify Firestore transaction completed
Useful Commandsβ
# Test Stripe connectivity
stripe customers list --limit 1
# Forward webhooks locally
stripe listen --forward-to localhost:8080/v1/billing/webhook
# Trigger test webhook
stripe trigger payment_intent.succeeded
# Check Cloud Run logs
gcloud logging read "resource.type=cloud_run_revision" \
--project=construction-code-expert-dev \
--limit=20 | grep -i stripe
π Resourcesβ
Documentation:
- Full Stripe Integration Plan β Complete guide
- Billing TDD β Architecture details
- Stripe API Reference
- Credit Burndown Model
Support:
- Stripe Support: https://support.stripe.com
- Stripe Status: https://status.stripe.com
- GitHub Issues: #202, #216
π‘ Key Takeawaysβ
- Stripe is ONLY for payments - Not for tracking usage
- Top-up is the only Stripe API call - Everything else is local
- Test mode first - Use test cards, no real money
- Webhooks are critical - They update the balance
- Security matters - Never commit secrets, always validate signatures
Ready to start? Follow the Full Stripe Integration Plan!