Skip to content

feat(platform): DataFast revenue attribution on Stripe Checkout - #13288

Merged
0ubbe merged 11 commits into
devfrom
feat/datafast-stripe-attribution
Jun 5, 2026
Merged

feat(platform): DataFast revenue attribution on Stripe Checkout#13288
0ubbe merged 11 commits into
devfrom
feat/datafast-stripe-attribution

Conversation

@0ubbe

@0ubbe 0ubbe commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Why / What / How

Why: DataFast attributes paid conversions back to marketing sources by reading datafast_visitor_id / datafast_session_id from the Stripe Checkout Session metadata. Today our Checkout Sessions are built server-side and carry no DataFast IDs, so revenue from credit top-ups and subscriptions is not attributed.

What: Forward the two DataFast cookie IDs from the browser into the metadata of both interactive Stripe Checkout Sessions — credit top-ups (mode="payment") and subscription checkouts (mode="subscription"). Off-session auto-recharge (PaymentIntent) is intentionally excluded; there is no browser/visitor context for it.

How: Centralized, transport-level plumbing rather than per-call-site edits (there are 6+ scattered checkout triggers but only 2 request choke-points):

  1. Frontend reads the JS-readable DataFast cookies and maps them to X-Datafast-Visitor-Id / X-Datafast-Session-Id request headers (getDatafastAttribution). Best-effort: returns {} during SSR or when cookies are absent, and never throws.
  2. Both client pipelines (the Orval custom mutator and the legacy BackendAPI client) merge those headers on client-side requests only.
  3. The Next.js proxy allow-lists the two headers so they reach the backend.
  4. Backend reads them via FastAPI Header() params on the top-up and subscription-tier endpoints and threads them into top_up_intent / create_subscription_checkout, which attach a _datafast_metadata(...) dict to the Checkout Session metadata (and merge into subscription_data.metadata for subscriptions, preserving user_id / tier / billing_cycle).

Best-effort at every layer: a missing cookie/header never blocks or errors a payment.

Changes 🏗️

  • backend/data/credit.py: _datafast_metadata() helper; datafast_visitor_id / datafast_session_id params on top_up_intent and create_subscription_checkout; metadata attached to the payment and subscription Checkout Sessions.
  • backend/api/features/v1.py: X-Datafast-* Header() params on the top-up and subscription-tier endpoints, forwarded to the credit layer.
  • frontend/services/analytics/datafast-attribution.ts: cookie→header helper.
  • frontend custom mutator + legacy client: merge attribution headers on client-side requests.
  • frontend proxy route: allow-list the two headers.
  • Tests added at backend (pytest) and frontend (Vitest + RTL + MSW) levels covering the helper, header forwarding, proxy allow-listing, and the billing top-up flow.

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • poetry run test (backend) — DataFast metadata + header-forwarding tests
    • pnpm test:unit (frontend) — attribution helper, proxy allow-list, billing top-up header tests
    • Manual: with DataFast cookies set, start a top-up and a subscription checkout; confirm datafast_visitor_id / datafast_session_id appear on the resulting Stripe Checkout Session metadata
    • Manual: with cookies absent, confirm both checkouts still succeed (no metadata, no error)

Note

Tests were written but not executed locally (toolchains not installed in the authoring environment). Correctness was verified via multi-stage code review only. Please rely on CI / a reviewer run for the gates (poetry run test, pnpm test:unit, lint, types).

0ubbe and others added 5 commits June 4, 2026 17:25
Add a _datafast_metadata helper and thread optional datafast_visitor_id /
datafast_session_id IDs into the metadata of both interactive Stripe
Checkout sessions (top_up_intent payment session and
create_subscription_checkout subscription session). IDs that are not
present are omitted so Stripe never receives the literal string "None".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Read X-Datafast-Visitor-Id / X-Datafast-Session-Id request headers on the
top-up and subscription-checkout endpoints and forward them as
datafast_visitor_id / datafast_session_id to top_up_intent and
create_subscription_checkout for attribution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@0ubbe
0ubbe requested a review from a team as a code owner June 4, 2026 08:26
@0ubbe
0ubbe requested review from Swiftyos and ntindle and removed request for a team June 4, 2026 08:26
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Jun 4, 2026
@github-actions github-actions Bot added platform/frontend AutoGPT Platform - Front end platform/backend AutoGPT Platform - Back end labels Jun 4, 2026
@github-actions github-actions Bot added the size/l label Jun 4, 2026
@coderabbitai

coderabbitai Bot commented Jun 4, 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d5089102-8162-4a27-8528-236fad9e4691

📥 Commits

Reviewing files that changed from the base of the PR and between ababd33 and cea80d3.

📒 Files selected for processing (2)
  • autogpt_platform/backend/backend/data/credit.py
  • autogpt_platform/backend/backend/data/credit_metadata_test.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • autogpt_platform/backend/backend/data/credit_metadata_test.py
  • autogpt_platform/backend/backend/data/credit.py
📜 Recent review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
  • GitHub Check: integration_test
  • GitHub Check: lint
  • GitHub Check: check API types
  • GitHub Check: Seer Code Review
  • GitHub Check: types
  • GitHub Check: type-check (3.13)
  • GitHub Check: Analyze (python)
  • GitHub Check: end-to-end tests
  • GitHub Check: type-check (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.13)
  • GitHub Check: type-check (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: Check PR Status

Walkthrough

Client reads DataFast cookies and merges mapped X-Datafast headers into requests; proxy forwards those headers; backend endpoints accept them and pass sanitized IDs into Stripe Checkout session and subscription metadata; tests cover parsing, forwarding, and metadata wiring.

Changes

DataFast Attribution Integration

Layer / File(s) Summary
Frontend DataFast attribution helper
src/services/analytics/datafast-attribution.ts, src/services/analytics/datafast-attribution.test.ts
Adds getDatafastAttribution() and COOKIE_TO_HEADER; parses document.cookie, decodes values safely, and returns header mapping with tests for malformed and missing cookies.
Client header merging & proxy allowlist
src/app/api/mutators/custom-mutator.ts, src/lib/autogpt-server-api/client.ts, src/app/api/proxy/[...path]/route.ts, src/app/api/proxy/[...path]/__tests__/route.test.ts, src/app/(platform)/settings/billing/__tests__/datafast-attribution.test.tsx
Merges attribution headers into client requests, adds x-datafast-visitor-id / x-datafast-session-id to proxy FORWARDED_REQUEST_HEADERS, and adds integration/unit tests capturing outgoing headers from the billing UI and proxy.
Backend DataFast metadata infrastructure
backend/data/credit.py, backend/data/credit_metadata_test.py
Adds _sanitize_datafast_id and _datafast_metadata(); extends top_up_intent and create_subscription_checkout to accept DataFast IDs and attach sanitized metadata to Stripe checkout session and subscription_data; tests validate sanitization and metadata wiring.
Backend endpoint header intake
backend/api/features/v1.py, backend/api/features/v1_test.py, backend/api/features/subscription_routes_test.py
request_top_up and update_subscription_tier accept optional FastAPI Header parameters for DataFast IDs and forward them into top_up_intent / create_subscription_checkout; tests assert forwarded kwargs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • ntindle
  • majdyz
  • Pwuts
  • Bentlybro

Poem

🐰 I nibble cookies, tiny and spry,
I whisper headers that flutter and fly,
From browser burrow to Stripe's bright glade,
DataFast crumbs softly relayed,
A rabbit's wink — attribution made!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding DataFast revenue attribution to Stripe Checkout sessions for paid conversions.
Description check ✅ Passed The description comprehensively explains why the change is needed, what is being changed, and how it is implemented across frontend and backend components.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/datafast-stripe-attribution

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 and usage tips.

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🔴 Merge Conflicts Detected

The following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 2 conflict(s), 0 medium risk, 3 low risk (out of 5 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

@codecov

codecov Bot commented Jun 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.93617% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 72.88%. Comparing base (1480e80) to head (fe129f2).
⚠️ Report is 1 commits behind head on dev.

Additional details and impacted files
@@           Coverage Diff           @@
##              dev   #13288   +/-   ##
=======================================
  Coverage   72.87%   72.88%           
=======================================
  Files        2370     2371    +1     
  Lines      177050   177135   +85     
  Branches    17917    17927   +10     
=======================================
+ Hits       129025   129101   +76     
- Misses      44203    44206    +3     
- Partials     3822     3828    +6     
Flag Coverage Δ
platform-backend 80.71% <100.00%> (+<0.01%) ⬆️
platform-frontend 40.72% <93.75%> (+0.01%) ⬆️
platform-frontend-e2e 31.11% <61.53%> (-0.14%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Platform Backend 80.71% <100.00%> (+<0.01%) ⬆️
Platform Frontend 45.19% <93.75%> (-0.01%) ⬇️
AutoGPT Libs ∅ <ø> (∅)
Classic AutoGPT 28.43% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@autogpt_platform/backend/backend/data/credit.py`:
- Around line 106-120: _datafast_metadata currently forwards raw IDs to Stripe
which can cause Checkout to fail if values exceed Stripe's metadata constraints;
change it to sanitize and bound each ID: coerce to str, strip surrounding
whitespace, truncate to Stripe's max metadata length (500 chars), and only set
metadata keys if the cleaned value is non-empty; apply this logic for both
visitor_id and session_id in the _datafast_metadata function so oversized or
malformed values are omitted instead of causing a Stripe invalid_request_error.

In `@autogpt_platform/frontend/src/services/analytics/datafast-attribution.ts`:
- Around line 12-18: The getDatafastAttribution helper builds a cookie map (jar)
and currently decodeURIComponents every cookie value which can throw on
malformed percent-encodings; change getDatafastAttribution so it only decodes
values when the cookie name matches the DataFast keys (e.g., check for
"datafast" / the exact DataFast cookie names used in the function) or wrap each
decodeURIComponent call in a try/catch that falls back to the raw value,
ensuring the function adheres to its "Never throws" contract; then add a unit
test in
autogpt_platform/frontend/src/services/analytics/datafast-attribution.test.ts
using the existing setCookie() mock to set a malformed non-DataFast cookie like
other=%E0%A4%A and assert getDatafastAttribution() does not throw and returns {}
when no DataFast cookies exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb6b881b-62e0-4df7-b70e-4c2f7e8ec8ce

📥 Commits

Reviewing files that changed from the base of the PR and between 6811bfd and 1a1e21e.

📒 Files selected for processing (12)
  • autogpt_platform/backend/backend/api/features/subscription_routes_test.py
  • autogpt_platform/backend/backend/api/features/v1.py
  • autogpt_platform/backend/backend/api/features/v1_test.py
  • autogpt_platform/backend/backend/data/credit.py
  • autogpt_platform/backend/backend/data/credit_metadata_test.py
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/__tests__/datafast-attribution.test.tsx
  • autogpt_platform/frontend/src/app/api/mutators/custom-mutator.ts
  • autogpt_platform/frontend/src/app/api/proxy/[...path]/__tests__/route.test.ts
  • autogpt_platform/frontend/src/app/api/proxy/[...path]/route.ts
  • autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts
  • autogpt_platform/frontend/src/services/analytics/datafast-attribution.test.ts
  • autogpt_platform/frontend/src/services/analytics/datafast-attribution.ts

Comment thread autogpt_platform/backend/backend/data/credit.py
Comment thread autogpt_platform/backend/backend/api/features/v1.py Outdated
0ubbe and others added 2 commits June 4, 2026 21:19
…out of schema

Addresses PR review + CI:
- Bound/sanitize DataFast IDs before sending to Stripe metadata (strip,
  drop control chars, truncate to 500 chars) so a malformed client-supplied
  ID can never fail Checkout (best-effort attribution).
- Mark the X-Datafast-* Header() params include_in_schema=False so they stay
  out of the OpenAPI spec — fixes the check-API-types schema drift and matches
  the centralized-header design (FE injects them, never via generated hooks).
- Guard decodeURIComponent in getDatafastAttribution: decode only the DataFast
  cookies and swallow URIError, honoring the documented "Never throws" contract.
- Fix TS2339 in the billing attribution test by capturing the request headers
  via an object holder (avoids CFA narrowing to never) and apply Prettier.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ntindle
ntindle previously approved these changes Jun 4, 2026
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Jun 4, 2026
@ntindle

ntindle commented Jun 4, 2026

Copy link
Copy Markdown
Member

Plz actually test this -- I saw note in pr description

Comment thread autogpt_platform/backend/backend/data/credit.py Outdated
0ubbe and others added 2 commits June 4, 2026 23:04
Per review: a DataFast ID that exceeds Stripe's 500-char metadata limit is
malformed (real IDs are short) — truncating it produces a partial value that
is useless for attribution and only pollutes the Checkout metadata. Drop such
values entirely instead, alongside blank and control-character values. Valid
IDs still pass through untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@0ubbe

0ubbe commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Done — ran the relevant tests locally:

Frontend (pnpm vitest run): 20/20 pass

  • datafast-attribution.test.ts (5) — cookie→header helper incl. malformed-cookie regressions
  • settings/billing/__tests__/datafast-attribution.test.tsx (2) — page-level top-up flow asserting the X-Datafast-* headers
  • api/proxy/[...path]/__tests__/route.test.ts (13) — header allow-list forwarding

Backend: _datafast_metadata / _sanitize_datafast_id verified directly (present/missing IDs, drop-on-invalid, whitespace strip, control-char drop, 500-char boundary). The full credit_metadata_test.py / v1_test.py / subscription_routes_test.py suites run in CI (test (3.11/3.12/3.13)), currently green.

I've also updated the PR description to drop the earlier "not tested locally" note.

@0ubbe
0ubbe merged commit b4f8166 into dev Jun 5, 2026
44 checks passed
@0ubbe
0ubbe deleted the feat/datafast-stripe-attribution branch June 5, 2026 12:59
@github-project-automation github-project-automation Bot moved this to Done in Frontend Jun 5, 2026
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Jun 5, 2026
0ubbe added a commit that referenced this pull request Jun 9, 2026
### Why / What / How

**Why:** DataFast attributes paid conversions back to marketing sources
by reading `datafast_visitor_id` / `datafast_session_id` from the Stripe
Checkout Session `metadata`. Today our Checkout Sessions are built
server-side and carry no DataFast IDs, so revenue from credit top-ups
and subscriptions is not attributed.

**What:** Forward the two DataFast cookie IDs from the browser into the
`metadata` of both *interactive* Stripe Checkout Sessions — credit
top-ups (`mode="payment"`) and subscription checkouts
(`mode="subscription"`). Off-session auto-recharge (PaymentIntent) is
intentionally excluded; there is no browser/visitor context for it.

**How:** Centralized, transport-level plumbing rather than per-call-site
edits (there are 6+ scattered checkout triggers but only 2 request
choke-points):

1. **Frontend** reads the JS-readable DataFast cookies and maps them to
`X-Datafast-Visitor-Id` / `X-Datafast-Session-Id` request headers
(`getDatafastAttribution`). Best-effort: returns `{}` during SSR or when
cookies are absent, and never throws.
2. Both client pipelines (the Orval custom mutator and the legacy
`BackendAPI` client) merge those headers on client-side requests only.
3. The Next.js proxy allow-lists the two headers so they reach the
backend.
4. **Backend** reads them via FastAPI `Header()` params on the top-up
and subscription-tier endpoints and threads them into `top_up_intent` /
`create_subscription_checkout`, which attach a `_datafast_metadata(...)`
dict to the Checkout Session `metadata` (and merge into
`subscription_data.metadata` for subscriptions, preserving `user_id` /
`tier` / `billing_cycle`).

Best-effort at every layer: a missing cookie/header never blocks or
errors a payment.

### Changes 🏗️

- **backend/data/credit.py**: `_datafast_metadata()` helper;
`datafast_visitor_id` / `datafast_session_id` params on `top_up_intent`
and `create_subscription_checkout`; metadata attached to the `payment`
and `subscription` Checkout Sessions.
- **backend/api/features/v1.py**: `X-Datafast-*` `Header()` params on
the top-up and subscription-tier endpoints, forwarded to the credit
layer.
- **frontend/services/analytics/datafast-attribution.ts**: cookie→header
helper.
- **frontend custom mutator + legacy client**: merge attribution headers
on client-side requests.
- **frontend proxy route**: allow-list the two headers.
- Tests added at backend (pytest) and frontend (Vitest + RTL + MSW)
levels covering the helper, header forwarding, proxy allow-listing, and
the billing top-up flow.

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [ ] I have tested my changes according to the test plan:
- [ ] `poetry run test` (backend) — DataFast metadata +
header-forwarding tests
- [ ] `pnpm test:unit` (frontend) — attribution helper, proxy
allow-list, billing top-up header tests
- [ ] Manual: with DataFast cookies set, start a top-up and a
subscription checkout; confirm `datafast_visitor_id` /
`datafast_session_id` appear on the resulting Stripe Checkout Session
`metadata`
- [ ] Manual: with cookies absent, confirm both checkouts still succeed
(no metadata, no error)

> [!NOTE]
> Tests were written but **not executed locally** (toolchains not
installed in the authoring environment). Correctness was verified via
multi-stage code review only. Please rely on CI / a reviewer run for the
gates (`poetry run test`, `pnpm test:unit`, lint, types).

---------

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

Labels

platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/l

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants