feat: core.affiliates module — wrap_url, AffiliateProgram, eBay/Amazon builders #21

Closed
opened 2026-04-04 17:38:36 -07:00 by pyr0ball · 0 comments
Owner

Summary

New circuitforge_core.affiliates module. Handles affiliate link wrapping, opt-out enforcement, and BYOK ID resolution for all CF products.

Module layout

circuitforge_core/affiliates/
  __init__.py      # Public API: wrap_url(), get_disclosure_text(), register_program()
  programs.py      # AffiliateProgram dataclass + eBay EPN + Amazon Associates builders
  disclosure.py    # Disclosure copy constants per retailer
  router.py        # Wrapping logic — opt-out check, BYOK resolution, program lookup

Public API

from circuitforge_core.affiliates import wrap_url, get_disclosure_text

url = wrap_url(
    url="https://www.ebay.com/itm/123456789",
    retailer="ebay",
    user_id=None,           # str | None — None = anonymous/self-hosted
    product="snipe",
    get_preference=None,    # Optional callable — injected by product for Heimdall/local prefs
)

Resolution order (router.py)

  1. User opted out? → plain URL
  2. User has BYOK ID for this retailer (Premium)? → wrap with user's ID
  3. CF has a registered program for this retailer with env var set? → wrap with CF's ID
  4. No program / no ID → plain URL

Built-in programs

eBay Partner Network (retailer_key="ebay"):

  • Env: EBAY_AFFILIATE_CAMPAIGN_ID
  • Appends mkcid=1&mkrid=711-53200-19255-0&siteid=0&campid={id}&toolid=10001&mkevt=1

Amazon Associates (retailer_key="amazon"):

  • Env: AMAZON_ASSOCIATES_TAG
  • Appends/merges tag={tag} query param

opt-out design

Opt-out check uses an injected get_preference(user_id, path) callable so the module does not depend on Heimdall directly. Products inject their preferences client; anonymous/self-hosted paths fall through to env-var-only mode.

Blocked on: #21 (core.preferences persistence helpers) for cloud opt-out support.

Migration from snipe#20

Replace snipe's one-off _affiliate_url() helper with wrap_url(retailer="ebay") after this merges.

  • Affiliate links design: circuitforge-plans/shared/2026-04-04-affiliate-links-design.md
  • Heimdall#5: user_preferences column (schema side)
  • #21: core.preferences persistence helpers (client side)
## Summary New `circuitforge_core.affiliates` module. Handles affiliate link wrapping, opt-out enforcement, and BYOK ID resolution for all CF products. ## Module layout ``` circuitforge_core/affiliates/ __init__.py # Public API: wrap_url(), get_disclosure_text(), register_program() programs.py # AffiliateProgram dataclass + eBay EPN + Amazon Associates builders disclosure.py # Disclosure copy constants per retailer router.py # Wrapping logic — opt-out check, BYOK resolution, program lookup ``` ## Public API ```python from circuitforge_core.affiliates import wrap_url, get_disclosure_text url = wrap_url( url="https://www.ebay.com/itm/123456789", retailer="ebay", user_id=None, # str | None — None = anonymous/self-hosted product="snipe", get_preference=None, # Optional callable — injected by product for Heimdall/local prefs ) ``` ## Resolution order (router.py) 1. User opted out? → plain URL 2. User has BYOK ID for this retailer (Premium)? → wrap with user's ID 3. CF has a registered program for this retailer with env var set? → wrap with CF's ID 4. No program / no ID → plain URL ## Built-in programs **eBay Partner Network** (`retailer_key="ebay"`): - Env: `EBAY_AFFILIATE_CAMPAIGN_ID` - Appends `mkcid=1&mkrid=711-53200-19255-0&siteid=0&campid={id}&toolid=10001&mkevt=1` **Amazon Associates** (`retailer_key="amazon"`): - Env: `AMAZON_ASSOCIATES_TAG` - Appends/merges `tag={tag}` query param ## opt-out design Opt-out check uses an injected `get_preference(user_id, path)` callable so the module does not depend on Heimdall directly. Products inject their preferences client; anonymous/self-hosted paths fall through to env-var-only mode. Blocked on: #21 (core.preferences persistence helpers) for cloud opt-out support. ## Migration from snipe#20 Replace snipe's one-off `_affiliate_url()` helper with `wrap_url(retailer="ebay")` after this merges. ## Related - Affiliate links design: `circuitforge-plans/shared/2026-04-04-affiliate-links-design.md` - Heimdall#5: user_preferences column (schema side) - #21: core.preferences persistence helpers (client side)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/circuitforge-core#21
No description provided.