Skip to content

Configure staging billing from SSM and verify test purchases - #629

Merged
thomasluizon merged 1 commit into
mainfrom
feature/ticket-808-staging-billing
Sep 28, 2026
Merged

thomasluizon merged 1 commit into
mainfrom
feature/ticket-808-staging-billing

Conversation

@thomasluizon

Copy link
Copy Markdown
Owner

Change

  • Read the staging Stripe product and four price IDs from /orbit/staging/api/ String parameters in infra/configuration.tf. The existing test-mode SecureString secret and webhook key remain staging-specific. Set the staging Google Play RTDN audience to the staging API URL.
  • Remove the unused Stripe publishable key from both environment groups, StripeSettings, and appsettings.json.
  • Document the staging billing parameters and rollout checks in infra/README.md. Add application tests for Play Subscriptions V2 test purchases through verify and RTDN, plus a signed Stripe test-mode checkout event.

This keeps billing IDs in SSM and reuses the existing purchase handlers, which already accept test purchases and signed test events.

Test evidence

  • Focused billing tests: env -u LANG LC_ALL=en_US.UTF-8 dotnet test tests/Orbit.Application.Tests/Orbit.Application.Tests.csproj --filter 'FullyQualifiedName~VerifyPlayPurchaseCommandHandlerTests|FullyQualifiedName~HandlePlayNotificationCommandHandlerTests|FullyQualifiedName~HandleWebhookCommandHandlerTests' --no-restore -v minimal passed, 98 tests.
  • env -u LANG LC_ALL=en_US.UTF-8 dotnet build Orbit.slnx -v minimal passed with zero errors.
  • env -u LANG LC_ALL=en_US.UTF-8 dotnet test Orbit.slnx --no-restore -v minimal passed, 6,813 tests.
  • terraform fmt -check -recursive infra and terraform -chdir=infra validate passed after provider initialization without the remote backend.
  • A live Terraform plan was unavailable because this worktree has no Render or Cloudflare credentials and no infra/local.tfvars. The source diff keeps production price IDs, product ID, secret inputs, and Google Play values unchanged. The unused production publishable key is removed as the ticket comment requires.
  • The installed Google Android Publisher SDK describes SubscriptionPurchaseV2.TestPurchase as present only for a test purchase. AWS provider v6.66.0 schema introspection confirms aws_ssm_parameter.value is a computed, sensitive string. No live SSM value was read in this worktree.

Assumptions

  • Model a Play license-tester subscription with the Subscriptions V2 testPurchase marker rather than the older purchaseType field named in the ticket body.

Manual steps

  • In AWS Systems Manager Parameter Store, confirm /orbit/staging/api/Stripe__ProProductId, Stripe__MonthlyPriceIdUsd, Stripe__YearlyPriceIdUsd, Stripe__MonthlyPriceIdBrl, and Stripe__YearlyPriceIdBrl exist as String parameters, and Stripe__SecretKey and Stripe__WebhookSecret exist as SecureString test-mode values. A Terraform plan that reads all seven and maps the five IDs to orbit-staging-api proves the parameters are available.
  • In the Terraform CLI, supply RENDER_API_KEY, CLOUDFLARE_API_TOKEN, AWS credentials, and infra/local.tfvars, then plan and apply. Inspect the plan before apply: production Stripe product and price IDs and Google Play values must stay unchanged, apart from removal of Stripe__PublishableKey. Confirm the orbit-staging-api environment group contains the five test IDs and the staging RTDN audience after apply.
  • In Google Cloud Console, Pub/Sub > Subscriptions, configure the staging push subscription endpoint and OIDC audience as https://api-staging.useorbit.org/api/subscriptions/play/rtdn, using the configured GooglePlay__RtdnServiceAccountEmail. A delivered test-purchase RTDN accepted by the staging API proves the audience matches.
  • In Google Play Console, Settings > License testing, add the internal and closed track testers. A license tester purchase followed by play/verify and an RTDN update granting Pro in staging proves the Play path works.
  • In Stripe Dashboard test mode, Developers > Webhooks, confirm the endpoint https://api-staging.useorbit.org/api/subscriptions/webhook uses the SSM signing secret and delivers the five events listed in infra/README.md. A test card checkout and signed checkout.session.completed that grants Pro proves the Stripe path works.

Closes thomasluizon/orbit-tickets#808

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes in commit 19a2f2e0, including staging billing configuration, rollout instructions, and purchase tests.

  • Staging Stripe configuration: Five test-mode product and price IDs now come from SSM into the staging API environment group; the unused publishable key is removed.
  • Play notifications: The staging RTDN audience points to the staging API, with matching Pub/Sub setup documented.
  • Purchase coverage: Play license-tester responses pass through the Google SDK test client and existing verify and RTDN handlers; the signed Stripe checkout test now asserts the stored entitlement identifiers.

Pullfrog  | View workflow run | Using GPT Sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

@thomasluizon
thomasluizon merged commit 5e5623e into main Sep 28, 2026
27 checks passed
@thomasluizon
thomasluizon deleted the feature/ticket-808-staging-billing branch September 28, 2026 00:03
thomasluizon added a commit that referenced this pull request Sep 28, 2026
)

* Add Render Terraform for production and staging (#621)

* Add Terraform for Render production and staging

* Handle Render database URLs without explicit ports

* Keep production intact through the first apply and isolate staging integrations

The imported API service ignores its own env_vars so the first apply only
adds and links the environment group, and the existing Render project is
imported (environments keyed by their live names) so the API stays in its
Production environment while a Staging environment is added. Staging gets
placeholder Stripe identifiers, staging return URLs, its own redirect
allowlist and an invalid Supabase host, so it cannot touch production
billing or storage. Empty custom domain lists become null, the landing no
longer auto-deploys, and the web health check uses /api/health.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 1480d1b)

* Store confirmed waitlist contacts in Orbit database (#620)

* feat: store confirmed waitlist marketing contacts locally

* fix: preserve marketing opt-outs and canonicalize waitlist languages

* Restore user marketing delivery after explicit opt-in

(cherry picked from commit 4c7b76a)

* Add Google authorization code sign in (#622)

* Implement Google authorization code sign in for ticket 796

* Register Google code routes in agent catalog

* Parse Google token responses with installed SDK

* fix: retry Google code sign-in after redemption

(cherry picked from commit 2301ccc)

* Add Cloudflare DNS and Turnstile to Terraform (#623)

* Add Cloudflare DNS and Turnstile Terraform resources

* Verify Cloudflare DNS answers before the registrar switch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 7e946a6)

* Gate production API releases through Render (#624)

* Add gated production API release workflow

* Handle queued Render deploys and recheck the live commit before recording a release

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 78b42f2)

* Keep Terraform from reverting web release digests (#627)

* fix: preserve workflow deployed web digests in Terraform

* docs: guard web service applies against Render image tag bug

(cherry picked from commit b8ea2c1)

* Configure staging billing from SSM and verify test purchases (#629)

(cherry picked from commit 5e5623e)

* Align Google code error assertion with redesign response

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant