Skip to content

chore(proto): pull acknowledge_token_forfeit field for DeleteOrganizationRequest - #1864

Open
whoAbhishekSah wants to merge 1 commit into
mainfrom
org-delete-ack-proto
Open

chore(proto): pull acknowledge_token_forfeit field for DeleteOrganizationRequest#1864
whoAbhishekSah wants to merge 1 commit into
mainfrom
org-delete-ack-proto

Conversation

@whoAbhishekSah

Copy link
Copy Markdown
Member

Part of #1837. Bottom of a 3-PR stack: #this → mocks → pre-flight code (#1857).

Regenerates the vendored protos to pick up the new bool acknowledge_token_forfeit field on DeleteOrganizationRequest, added in raystack/proton#497.

Note on the PROTON_COMMIT pin: proton main has moved ahead with breaking changes frontier hasn't absorbed yet (CreatePlan/UpdatePlan moved to AdminService). So the pin points at frontier-pin/org-delete-token-forfeit, which is the previous pin (91eaffc) plus only the new field. A separate task will move frontier onto proton main.

The field is unused until the top of the stack.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Aug 10, 2026 6:27am

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1b9f754f-2a36-464a-a9b2-0b84df686c8a

📥 Commits

Reviewing files that changed from the base of the PR and between bbf43fc and 5aebb50.

⛔ Files ignored due to path filters (1)
  • proto/v1beta1/frontier.pb.go is excluded by !**/*.pb.go, !proto/**
📒 Files selected for processing (1)
  • Makefile
🚧 Files skipped from review as they are similar to previous changes (1)
  • Makefile

📝 Walkthrough

Summary by CodeRabbit

  • Chores
    • Updated an internal dependency reference to a newer revision.

Walkthrough

The Makefile updates PROTON_COMMIT from 0a5d4207bcefc231021c032cd166fd07440f11a5 to b037da5a1eb0e8123c811ec3b1e1721458d486e5.

Changes

Proton dependency pin

Layer / File(s) Summary
Update Proton commit pin
Makefile
PROTON_COMMIT now references the new Proton commit.

Estimated code review effort: 1 (Trivial) | ~2 minutes

Possibly related PRs

Suggested reviewers: rohilsurana

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@whoAbhishekSah

whoAbhishekSah commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

🧪 Manual test report: org delete preflight

Tested the new delete flow end to end against a local server built from the tip of this stack (#1864 proto → #1865 mocks → #1857 preflight). The server was connected to a Stripe test account, so every subscription and invoice below is a real Stripe object, not a mock.

How the test environment was set up

  • Every case used a fresh organization. A normal user (deltest@raystack.org) created it, so that user was the org owner. All DeleteOrganization calls were made by this owner with a session cookie — not by a superadmin.
  • Each org got a billing account through CreateBillingAccount. Six were online (registered in Stripe). One was created with offline: true for the offline case.
  • Subscriptions came from AdminService.DelegatedCheckout on a $10/month plan. A test card was attached to the Stripe customer first, so the subscription started as active and its first invoice was charged and paid right away.
  • Open invoices were created directly in Stripe (invoice item + finalize, collection method send_invoice). The 5-minute invoice sync then pulled them into the local database. Payments were also done in Stripe, and the same sync flipped the local rows to paid.
  • Tokens came from a credit product (behavior: credits) bought through delegated checkout.
  • The negative balance was made by setting credit_min to −10000 on the account and then recording a 600-token usage against a balance of 100, which left the balance at −500.
  • Two rows were inserted straight into the database to cover states that are hard to reach through the API: a trialing subscription row, and an active subscription row on the offline account.
  • Audit records were checked in the auditlogs table (audit_events: db), and every deleted org was confirmed gone at the database level, not just from the API response.

Results

Case Setup Expected Result
Clean org billing account, no activity delete succeeds ✅ HTTP 200; org and billing rows gone
Active subscription active sub on a $10/month plan, card on file blocked with ACTIVE_SUBSCRIPTION; delete works after cancel ✅ blocked; after CancelSubscription (immediate) the delete returned 200
Trialing subscription sub row in trialing state blocked the same way as active ✅ blocked with ACTIVE_SUBSCRIPTION; message says "is trialing"
Unpaid invoice open $15 invoice, synced to the local database blocked with UNPAID_INVOICE; delete works after paying ✅ blocked; after paying in Stripe and the next sync, delete returned 200
Paid invoice only paid invoices on the account at delete time paid invoices never block ✅ no violation for them; delete succeeded and removed the local invoice rows (Stripe keeps its own copy)
Unused tokens, no flag balance of 500 tokens blocked with UNUSED_TOKENS, message names the amount ✅ blocked; message says "has 500 unused tokens" and points at the flag
Unused tokens, with flag same org, retry with acknowledge_token_forfeit: true delete succeeds and the forfeit is audited ✅ HTTP 200; app.billing.tokens.forfeited audit record written with {"amount": "500"} (also verified for 300 and 200 in other cases)
Negative balance balance of −500 tokens blocked with NEGATIVE_TOKEN_BALANCE; the flag must not bypass a debt ✅ blocked without the flag and with it; after buying tokens back to 0 the delete returned 200
All blockers at once active sub + open $15 invoice + 300 tokens one response listing every blocker ✅ one failed_precondition with all three violations; the org's paid invoice was not among them
Offline account offline billing account with 200 tokens and an active sub row only the token checks run ✅ only UNUSED_TOKENS reported; with the flag the delete succeeded, and the teardown removed the sub row without calling Stripe

Error shape

Blocked deletes return failed_precondition with one google.rpc.PreconditionFailure violation per blocker. Each violation carries the machine-readable type, the id of the blocking object, and a plain description of the fix. Example from the three-blocker case:

{
  "code": "failed_precondition",
  "message": "organization cannot be deleted yet: subscription[f1e289ce-...] is active: cancel it, then retry the delete; invoice[c304285d-...] is unpaid: pay it via its hosted payment page, then retry the delete; billing account[5a77db67-...] has 300 unused tokens that deleting the organization forfeits: retry the delete with acknowledge_token_forfeit set to proceed",
  "details": [{
    "type": "google.rpc.PreconditionFailure",
    "violations": [
      { "type": "ACTIVE_SUBSCRIPTION", "subject": "f1e289ce-..." },
      { "type": "UNPAID_INVOICE", "subject": "c304285d-..." },
      { "type": "UNUSED_TOKENS", "subject": "5a77db67-..." }
    ]
  }]
}

The structured details reach the caller intact, so the error sanitizer is letting non-internal codes through as intended.

Side observation (not from this stack)

AdminService.UpdateBillingAccountLimits returns a bare internal server error when called with only org_id. It works when the billing account id is passed too. This exists on main as well — noting it because it is the same class of problem #1851 cleaned up.

…tionRequest

Generated from raystack/proton@0b8bdb2 (91eaffc + the new field only,
so the recent breaking proto changes on proton main stay out of this
change).

Co-Authored-By: Claude Fable 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