From d9ac977fef7e3c2d0d7e93f4c5d1b51a1f3fd7c4 Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Wed, 29 Apr 2026 04:34:13 -0500 Subject: [PATCH] Wire Stripe billing secrets and env vars into Soma Cloud Run - stripe-billing.tf: Secret Manager resources for all billing IDs: meters (input/output), plan prices (free/professional/founding), overage prices per plan per token type - cloud-run-soma.tf: inject all 13 Stripe env vars into the Soma service via Secret Manager secret_key_ref; add billing secrets to depends_on --- servers/gcp/cloud-run-soma.tf | 147 ++++++++++++++++++++++++++++++++++ servers/gcp/stripe-billing.tf | 127 +++++++++++++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 servers/gcp/stripe-billing.tf diff --git a/servers/gcp/cloud-run-soma.tf b/servers/gcp/cloud-run-soma.tf index 0937ad5..f086ed3 100644 --- a/servers/gcp/cloud-run-soma.tf +++ b/servers/gcp/cloud-run-soma.tf @@ -287,6 +287,138 @@ resource "google_cloud_run_v2_service" "soma_us" { value = "claude-sonnet-4-5" } + # ── Stripe billing ──────────────────────────────────────────────────────── + + env { + name = "STRIPE_SECRET_KEY" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_secret_key.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_WEBHOOK_SECRET" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_webhook_secret.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_FREE_PLAN" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_free_plan.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_PROFESSIONAL_PLAN" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_professional_plan.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_FOUNDING_PLAN" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_founding_plan.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_METER_ID_INPUT_TOKENS" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_meter_id_input_tokens.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_METER_ID_OUTPUT_TOKENS" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_meter_id_output_tokens.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_FREE_INPUT_OVERAGE" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_free_input_overage.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_FREE_OUTPUT_OVERAGE" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_free_output_overage.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_PROFESSIONAL_INPUT_OVERAGE" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_professional_input_overage.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_PROFESSIONAL_OUTPUT_OVERAGE" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_professional_output_overage.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_FOUNDING_INPUT_OVERAGE" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_founding_input_overage.secret_id + version = "latest" + } + } + } + + env { + name = "STRIPE_PRICE_FOUNDING_OUTPUT_OVERAGE" + value_source { + secret_key_ref { + secret = google_secret_manager_secret.stripe_price_founding_output_overage.secret_id + version = "latest" + } + } + } + ports { container_port = 8080 name = "http1" @@ -331,6 +463,21 @@ resource "google_cloud_run_v2_service" "soma_us" { google_secret_manager_secret_version.soma_node_secret, google_secret_manager_secret_version.soma_key_secret, google_secret_manager_secret_version.soma_anthropic_api_key, + # Stripe billing secrets — must exist before Cloud Run deploy. + # Versions are populated by provision_stripe_billing.py. + google_secret_manager_secret.stripe_secret_key, + google_secret_manager_secret.stripe_webhook_secret, + google_secret_manager_secret.stripe_price_free_plan, + google_secret_manager_secret.stripe_price_professional_plan, + google_secret_manager_secret.stripe_price_founding_plan, + google_secret_manager_secret.stripe_meter_id_input_tokens, + google_secret_manager_secret.stripe_meter_id_output_tokens, + google_secret_manager_secret.stripe_price_free_input_overage, + google_secret_manager_secret.stripe_price_free_output_overage, + google_secret_manager_secret.stripe_price_professional_input_overage, + google_secret_manager_secret.stripe_price_professional_output_overage, + google_secret_manager_secret.stripe_price_founding_input_overage, + google_secret_manager_secret.stripe_price_founding_output_overage, ] } diff --git a/servers/gcp/stripe-billing.tf b/servers/gcp/stripe-billing.tf new file mode 100644 index 0000000..80e010a --- /dev/null +++ b/servers/gcp/stripe-billing.tf @@ -0,0 +1,127 @@ +# ── Stripe Billing — Secret Manager resources ───────────────────────────────── +# +# Secrets are created here as empty shell resources. Values are populated by: +# scripts/provision_stripe_billing.py --env live +# scripts/provision_stripe_billing.py --env test +# +# The provisioning script creates the secret versions via gcloud CLI after +# calling the Stripe API to create the meters, products, and prices. +# +# DO NOT hardcode secret values here. + +# ── Stripe core secrets (existing — registered here for Terraform management) ── + +resource "google_secret_manager_secret" "stripe_secret_key" { + secret_id = "stripe-secret-key" + project = var.project_id + replication { auto {} } + + lifecycle { + ignore_changes = [replication] + } +} + +resource "google_secret_manager_secret" "stripe_webhook_secret" { + secret_id = "stripe-webhook-secret" + project = var.project_id + replication { auto {} } + + lifecycle { + ignore_changes = [replication] + } +} + +# ── Plan flat price IDs ──────────────────────────────────────────────────────── + +resource "google_secret_manager_secret" "stripe_price_free_plan" { + secret_id = "stripe-price-free-plan" + project = var.project_id + replication { auto {} } +} + +resource "google_secret_manager_secret" "stripe_price_professional_plan" { + secret_id = "stripe-price-professional-plan" + project = var.project_id + replication { auto {} } +} + +resource "google_secret_manager_secret" "stripe_price_founding_plan" { + secret_id = "stripe-price-founding-plan" + project = var.project_id + replication { auto {} } +} + +# ── Billing Meter IDs ───────────────────────────────────────────────────────── + +resource "google_secret_manager_secret" "stripe_meter_id_input_tokens" { + secret_id = "stripe-meter-id-input-tokens" + project = var.project_id + replication { auto {} } +} + +resource "google_secret_manager_secret" "stripe_meter_id_output_tokens" { + secret_id = "stripe-meter-id-output-tokens" + project = var.project_id + replication { auto {} } +} + +# ── Overage price IDs — Free plan ───────────────────────────────────────────── + +resource "google_secret_manager_secret" "stripe_price_free_input_overage" { + secret_id = "stripe-price-free-input-overage" + project = var.project_id + replication { auto {} } +} + +resource "google_secret_manager_secret" "stripe_price_free_output_overage" { + secret_id = "stripe-price-free-output-overage" + project = var.project_id + replication { auto {} } +} + +# ── Overage price IDs — Professional plan ───────────────────────────────────── + +resource "google_secret_manager_secret" "stripe_price_professional_input_overage" { + secret_id = "stripe-price-professional-input-overage" + project = var.project_id + replication { auto {} } +} + +resource "google_secret_manager_secret" "stripe_price_professional_output_overage" { + secret_id = "stripe-price-professional-output-overage" + project = var.project_id + replication { auto {} } +} + +# ── Overage price IDs — Founding Member plan ────────────────────────────────── + +resource "google_secret_manager_secret" "stripe_price_founding_input_overage" { + secret_id = "stripe-price-founding-input-overage" + project = var.project_id + replication { auto {} } +} + +resource "google_secret_manager_secret" "stripe_price_founding_output_overage" { + secret_id = "stripe-price-founding-output-overage" + project = var.project_id + replication { auto {} } +} + +# ── Secret accessor grants for Soma SA ──────────────────────────────────────── +# soma_secret_accessor already has roles/secretmanager.secretAccessor on the +# project, so new secrets are automatically accessible. No per-secret grants needed. + +# ── Outputs ─────────────────────────────────────────────────────────────────── + +output "stripe_billing_secrets" { + description = "Stripe billing secret IDs — populate via provision_stripe_billing.py" + value = { + stripe_secret_key = google_secret_manager_secret.stripe_secret_key.secret_id + stripe_webhook_secret = google_secret_manager_secret.stripe_webhook_secret.secret_id + stripe_price_free_plan = google_secret_manager_secret.stripe_price_free_plan.secret_id + stripe_price_professional_plan = google_secret_manager_secret.stripe_price_professional_plan.secret_id + stripe_price_founding_plan = google_secret_manager_secret.stripe_price_founding_plan.secret_id + stripe_meter_id_input_tokens = google_secret_manager_secret.stripe_meter_id_input_tokens.secret_id + stripe_meter_id_output_tokens = google_secret_manager_secret.stripe_meter_id_output_tokens.secret_id + } +}