Skip to content

Require visitor match for affiliate attribution cookies#200

Merged
ralyodio merged 1 commit into
profullstack:masterfrom
morganschp:fix-affiliate-attribution-visitor-click
May 23, 2026
Merged

Require visitor match for affiliate attribution cookies#200
ralyodio merged 1 commit into
profullstack:masterfrom
morganschp:fix-affiliate-attribution-visitor-click

Conversation

@morganschp
Copy link
Copy Markdown
Contributor

Fixes #199.

Summary

  • require tracking-code attribution to match the same visitor_id when one is available
  • keep the existing in-window click requirement for tracking-code lookups without a visitor id
  • move fallback visitor filtering before limit(1) so the latest matching visitor click is selected
  • add regression coverage for mismatched and matching visitor-id cookie attribution

Validation

  • ./node_modules/.bin/vitest run src/lib/affiliates/tracking.test.ts
  • ./node_modules/.bin/eslint src/lib/affiliates/tracking.ts src/lib/affiliates/tracking.test.ts
  • ./node_modules/.bin/tsc --noEmit --pretty false
  • ./node_modules/.bin/prettier --check src/lib/affiliates/tracking.ts src/lib/affiliates/tracking.test.ts
  • git diff --check -- src/lib/affiliates/tracking.ts src/lib/affiliates/tracking.test.ts

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 23, 2026

Greptile Summary

This PR tightens affiliate attribution by requiring that a tracking-code cookie click belong to the same visitor_id as the current request when a visitor cookie is present. It also fixes the fallback (visitor-only) path so .limit(1) is applied after the visitor_id filter rather than before it.

  • tracking.ts: In the trackingCode fast path, a conditional .eq(\"visitor_id\", visitorId) is added before .limit(1) so mismatched-visitor clicks cannot satisfy attribution. In the fallback path, .limit(1) is moved to after the conditional .eq(\"visitor_id\", visitorId) call, ensuring the DB limit is applied over the already-filtered result set.
  • tracking.test.ts: Two new regression tests are added — one asserting a mismatch returns { affiliated: false }, and one asserting the correct visitor's click is selected even when a newer click from a different visitor exists.

Confidence Score: 5/5

Safe to merge — the visitor-id guard is narrowly scoped and the fallback limit fix is a correctness improvement with no behavioral regression risk.

Both changed paths are exercised by dedicated regression tests. The visitor-id filter is additive and the .limit(1) relocation in the fallback path aligns the code with how the Supabase builder applies filters before execution.

No files require special attention.

Important Files Changed

Filename Overview
src/lib/affiliates/tracking.ts Adds visitor_id guard to tracking-code attribution path; moves .limit(1) after the conditional filter in the fallback path — both changes are logically correct.
src/lib/affiliates/tracking.test.ts Adds two regression tests for visitor-id mismatch and match; minor ID naming inconsistency in the 'credits matching visitor' test (the 'older' click is actually newer chronologically).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[findAttribution called] --> B{trackingCode present?}
    B -- Yes --> C[Lookup affiliate application]
    C --> D{app found & approved?}
    D -- No --> E[Fall through to visitor fallback]
    D -- Yes --> F[Build click query\ntracking_code + offer_id + window]
    F --> G{visitorId present?}
    G -- Yes --> H[Add .eq visitor_id filter]
    G -- No --> I[No visitor filter]
    H --> J[.limit 1 execute query]
    I --> J
    J --> K{clicks found?}
    K -- No --> L[return affiliated: false]
    K -- Yes --> M[return affiliated: true with click_id]
    B -- No --> E
    E --> N{visitorId present?}
    N -- No --> O[return null]
    N -- Yes --> P[Build click query offer_id + window + ORDER DESC]
    P --> Q[Add .eq visitor_id filter]
    Q --> R[.limit 1 execute query]
    R --> S{clicks found?}
    S -- No --> T[return affiliated: false]
    S -- Yes --> U[return affiliated: true with click_id and tracking_code]
Loading

Reviews (3): Last reviewed commit: "Fix affiliate attribution visitor matchi..." | Re-trigger Greptile

Comment on lines 223 to 225
if (visitorId) {
query = query.eq("visitor_id", visitorId);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Redundant visitorId guard in fallback path

The if (visitorId) block at line 223 is unreachable-false: the early return at line 211 (if (!visitorId) return null) guarantees visitorId is always truthy here. This is pre-existing, but the refactor in this PR (moving the query from a chained .limit(1) call to a separate .limit(1) at the end) is a good opportunity to remove the dead branch and call .eq("visitor_id", visitorId) unconditionally, which would make the intent clearer.

@ralyodio ralyodio closed this May 23, 2026
@ralyodio ralyodio reopened this May 23, 2026
@ralyodio ralyodio closed this May 23, 2026
@ralyodio ralyodio reopened this May 23, 2026
@ralyodio ralyodio merged commit f85995f into profullstack:master May 23, 2026
4 checks passed
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.

bug: affiliate attribution can use another visitor click

2 participants