Skip to content

Fix partner pricing to users onboarded through partner API keys - #1358

Open
ebma wants to merge 5 commits into
stagingfrom
feat/partner-key-pricing-assignment
Open

Fix partner pricing to users onboarded through partner API keys#1358
ebma wants to merge 5 commits into
stagingfrom
feat/partner-key-pricing-assignment

Conversation

@ebma

@ebma ebma commented Aug 25, 2026

Copy link
Copy Markdown
Member

Summary

Users onboarded through a partner-attributed API credential now receive a persistent ProfilePartnerAssignment, so the partner's pricing config (target discount, markup, fees) stays fixed to them even when they later return to Vortex outside the partner's funnel. The partner is always resolved server-side from api_credentials.partner_id (admin-issued; self-service keys carry no attribution), never from a client-chosen value. No schema changes.

Two onboarding channels are covered:

  • API (managed profiles): provisioning through a partner-attributed secret credential assigns the credential's partner to the newly created child, inside the provisioning transaction. Never retroactive for existing children; Bearer-authenticated managers provision without attribution.
  • Widget: new POST /v1/partner-attribution/claim (Supabase auth + existing public-key validation, impersonation rejected). The widget fires it once after sign-in when the apiKey URL param is present.

All non-admin assignment paths are first-partner-wins: an active, unexpired assignment is never replaced (replacement stays admin-only). Expired-but-active rows are deactivated before insert so the active-assignment partial unique index cannot collide, mirroring the admin and seeded-discount paths.

Security spec

docs/security-spec/03-ramp-engine/profile-partner-pricing.md updated in the same change: invariant 14 now lists the three server-controlled assignment paths, plus new threat-table rows. The leaked-pk_*-key claim is documented as accepted residual risk (keys are revocable/rotatable; upgrade path is a signed short-lived token verified against the same credential).

Partner docs

  • 08-widget-integration.md: new "Pricing Attribution At Sign-In" section. Note: session creation with only the X-Public-Key header does not embed the key in the generated widget URL; partners must pass apiKey in the session body (or link directly with apiKey=pk_...).
  • 14-managed-profiles.md: pricing bullet documents the at-creation assignment.

Tests

  • 5 service tests (creation, first-partner-wins, idempotency, expired-row replacement, inactive-partner/missing-profile skips)
  • 5 claim-route tests (auth required, invalid/secret/missing key, exactly-once assignment, competing-partner key, unattributed-key no-op)
  • 2 provisioning tests (attribution on creation, no retroactive assignment)
  • 2 frontend hook tests (guards, once-per-key claim)

bun typecheck, Biome, and bun run docs:api:check pass.

@netlify

netlify Bot commented Aug 25, 2026

Copy link
Copy Markdown

Deploy Preview for vortexfi canceled.

Name Link
🔨 Latest commit 73bb210
🔍 Latest deploy log https://app.netlify.com/projects/vortexfi/deploys/6a8dccc983dca80008d909ce

@netlify

netlify Bot commented Aug 25, 2026

Copy link
Copy Markdown

Deploy Preview for vortex-sandbox ready!

Name Link
🔨 Latest commit 73bb210
🔍 Latest deploy log https://app.netlify.com/projects/vortex-sandbox/deploys/6a8dccc97de7dc0008e30054
😎 Deploy Preview https://deploy-preview-1358--vortex-sandbox.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Aug 25, 2026

Copy link
Copy Markdown

Deploy Preview for vrtx-dashboard canceled.

Name Link
🔨 Latest commit 73bb210
🔍 Latest deploy log https://app.netlify.com/projects/vrtx-dashboard/deploys/6a8dccc9105bc40008e0968f

ebma added 5 commits August 25, 2026 19:06
Creates a profile-partner assignment from a server-resolved partner id
with first-partner-wins semantics: an active, unexpired assignment is
never replaced, and expired-but-active rows are deactivated so the
active-assignment partial unique index cannot collide.
Children created through a partner-attributed secret credential get the
credential's partner assignment inside the provisioning transaction.
Never retroactive for existing children; Bearer-authenticated managers
provision without attribution.
POST /v1/partner-attribution/claim assigns the validated public key's
partner to the authenticated profile so widget-onboarded users keep the
partner's pricing. The client presents only the key; the partner is
resolved exclusively from the credential.
Fires the claim once per session when the widget was opened with a
partner's public apiKey URL parameter and the user is authenticated.
Failures are non-fatal and retried on the next auth change.
Security spec invariant 14 now lists the three server-controlled
assignment paths and the accepted leaked-public-key residual risk;
partner docs cover attribution at widget sign-in and managed-profile
creation.
@ebma
ebma force-pushed the feat/partner-key-pricing-assignment branch from 7d29654 to 73bb210 Compare August 25, 2026 17:11
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