# ABCPro Integration Contract — v1.0 (CANONICAL) > **This document is the single source of truth for connecting any product to ABCPro.** > ABCPro is the parent company and the system of record for money, identity, ranks and > payouts. **Partner products conform to this contract — not the other way around.** A > product does not tell ABCPro what to build; it fills out a **Product Profile** (§11) > declaring its own economics, and builds its side to the mechanism defined here. > > Only ABCPro changes this contract, and only with a version bump (§13). Anything a > product needs that isn't here is a change **request** to ABCPro, not a given. --- ## 1. The ownership split (read this first) Every integration has two kinds of decisions. They have different owners: | | **The mechanism** | **The economics** | |---|---|---| | **Owner** | **ABCPro** (this contract) | **The product** (its Product Profile) | | Covers | endpoint, auth/signing, payload *fields*, idempotency, reversals, the pool→commission law, SSO token shape, ledger precision | what counts as *commissionable* revenue, the pool **%** or amount, its `transaction_type` names, its `external_id` scheme, its landing path, its catalog entries | | Why | one uniform, enforceable money surface across all products | only the product knows its own pricing and what it's willing to pay | A product **reports its economics back** so ABCPro can configure the project row and sanity-check it against the contract. That's the whole product-authored surface. --- ## 2. Onboarding process 1. **Product submits a Product Profile** (§11) — its economics, in the template's shape. 2. **ABCPro registers the project** and issues credentials (§4): `project_key`, an API key (`ABCPRO_KEY_ID` + `ABCPRO_SECRET`), and a shared `ABCPRO_SSO_SECRET`. 3. **Product builds to this contract** — sale reporting (§5–6), refunds (§7), and SSO (§9) — using the canonical client `connectors/abcpro_connector.php` (copy it; do not re-implement the HMAC). 4. **Sandbox — test with a TEST key.** ABCPro issues a test key (`ak_test_…`). Reports signed with it run through ABCPro's *real* engine and return the *exact* split a live sale would produce — but nothing is persisted and no money moves (a true dry-run). Perfect your sale reporting, refunds, and SSO here, as long as you like. See §4.5. 5. **Go live.** Once the agreement is signed, ABCPro approves the company for live and issues a live key (`ak_live_…`). Swap the test key for the live key — same code, same endpoints — and real reports flow. Live keys are refused until ABCPro approves; test keys work immediately. The product never needs — and must never be given — ABCPro server, database, or `.env` access. The credentials in §4 plus the connector file are everything. --- ## 3. Non-negotiable laws - **Commissions are paid from the MARKETING POOL, never from gross.** ABCPro pays out of the pool amount the product reports (or a hub rate configured on the project). No pool → no commission. The sale still records for the books. - **All money is integer cents.** The ledger is journaled and reconciled in whole cents (see §6 for the sub-cent rule). - **The product stays merchant of record and keeps its funds at point of sale.** ABCPro never touches the product's checkout or money — it receives *reports* only. - **ABCPro keeps its company share and runs ALL distribution.** The product sends the whole pool and stays ignorant of levels, ranks, downlines, and the company cut. - **Idempotent by `external_id`.** Re-sending the same one is always safe. --- ## 4. Credentials ABCPro issues (per product) ``` ABCPRO_URL=https://abcpro.com ABCPRO_PROJECT_KEY= ABCPRO_KEY_ID= ABCPRO_SECRET= ABCPRO_SSO_SECRET= ``` SSO token audience (`aud`) is always the `project_key`. Never commit these to git or expose them client-side. ## 4.5 Sandbox (test mode) & going live Exactly like Stripe: **the key decides the mode.** Same endpoints, same payloads — no separate URL, no mode flag in your requests. - **`ak_test_…` (test key):** the report is authenticated and run through ABCPro's real engine; the response is the exact split a live sale would return (levels, fee, leadership, onboarding, unallocated) — but **nothing is persisted, no commissions accrue, no rank or payout is touched.** Every response includes `"mode":"test"`. Test reversals are accepted and acknowledged. Use this to perfect auth, payloads, and error handling with zero risk. - **`ak_live_…` (live key):** real reports. Persisted, journaled, attributed, payable. - **Go live = swap keys.** When your agreement is signed and ABCPro approves you, we hand you the live key. Change `ABCPRO_KEY_ID`/`ABCPRO_SECRET` to the live values and you're live — no code change. Until approval, live keys return `403 not approved`; test keys always work. --- ## 5. Auth & signing (every API call) `POST` JSON to the endpoint. Sign the request: ``` X-AbcPro-Key: X-AbcPro-Timestamp: X-AbcPro-Signature: hex( HMAC_SHA256( ABCPRO_SECRET, "." ) ) ``` - The signed base string is **`"."`** — the exact raw bytes you POST. - **Timestamp skew window is ±300 seconds.** Sign at send time; don't reuse old signatures. - Bad/absent signature → `401`. Unknown/revoked key → `401`. Project not active → `403`. The canonical client (`abcpro_connector.php`) does all of this for you. --- ## 6. Reporting a sale — `POST /api/v1/sale.php` One report per commissionable transaction (see each product's profile for its unit). | Field | Type | Req | Meaning | |---|---|---|---| | `external_id` | string | ✔ | **Stable, unique** id for this transaction. Idempotency key. | | `gross_cents` | int | ✔ | What the customer actually paid, in cents. | | `fee_cents` | int | ○ | Processor (Stripe) fee withheld on this charge. See pool rules. | | `marketing_pool_cents` | int | ✔* | The pool for this sale (integer cents). *Required unless the project is hub-rate-driven (§6.2). | | `transaction_type` | string | ✔ | The product's declared type (e.g. `retail_sale`, `subscription`, `storage_period`). Must match a row ABCPro configured. | | `ref_code` | string | ○ | The ABCPro referral code attributed to this sale (§10). Omit → recorded, attributed to nobody. | | `buyer_email` | string | ○ | Buyer's email (used for included-account perk exclusion). | | `buyer_external_id` | string | ○ | The product's own id for the buyer. | | `occurred_at` | ISO-8601 | ○ | When it happened. | | `clears_at` | ISO-8601 | ○ | Payout-clearance gate; else project default clearance applies. | | `meta` | object | ○ | Free-form product audit data. **Put authoritative sub-cent figures here** (§6.1). | Response: `200 {ok:true, …}` on success; `4xx {ok:false, error}` otherwise. ### 6.1 The pool & the sub-cent rule (ABCPro-owned) - Report `marketing_pool_cents` as an **integer**. ABCPro's ledger is whole-cent; it will not silently invent sub-cent precision, because that would break reconciliation. - **If your per-report pool would be less than 1¢, you accumulate it on your side and report it when it crosses a whole cent** (or aggregate across the period). The product owns its own rounding/carry — the hub receives whole cents. - Always include the **authoritative unrounded figures in `meta`** for audit, e.g. `meta:{ marketing_pool_microcents: 9000, rate_pct: 30, commissionable_revenue_microcents: 30000 }`. ABCPro journals `meta` verbatim, so nothing is ever *lost* — it just isn't paid below a cent. ### 6.2 Two ways the pool is set (declared per product in the profile) - **Product-computed (default; GigPeach / BluSpots / BluRepo):** the product computes and **sends `marketing_pool_cents`** (typically `pct × (gross − fee)`). ABCPro pays exactly that. No hub rate involved. - **Hub-rate-driven (GuestBookit):** the product **omits** `marketing_pool_cents` and sends `gross_cents` + `fee_cents` + `transaction_type`. ABCPro applies the % configured on the Marketing Pool Rates admin to `(gross − fee)`. The rate is adjustable in ABCPro settings. A product uses one model, not both. If you send a pool, ABCPro uses it and ignores the rate. --- ## 7. Refunds & reversals — same endpoint Send `event` (or `transaction_type`) = one of `refund`, `partial_refund`, `chargeback`, `reversal`, with the **same `external_id`** as the original. ABCPro reverses the commissions and nets the ledger to zero. Period-stamped ids reverse the exact period. --- ## 8. Idempotency & the manifest cross-check - `external_id` dedupes. For recurring/period charges, **period-stamp** it (e.g. `…:{YYYY-MM}`) so each period is its own idempotent report. - Optionally call `POST /api/v1/manifest.php` from cron with `{seq_high, report_count, gross_total_cents}` so ABCPro can detect gaps between what you sent and what it received. - If ABCPro is briefly unreachable, queue locally and re-send (`abcpro_flush_queue()`); never block your own checkout on ABCPro. --- ## 9. Single sign-on (SSO) — the token & the consumer's duties ABCPro is the identity provider. It redirects the member to `?t=[&goto=]`. The token is `base64url(json) . "." . base64url(HMAC_SHA256(json, ABCPRO_SSO_SECRET))`. **Claims:** `aud` (must equal your `project_key`), `email` (verified), `name`, **`abcpro_id`** (stable, permanent member id — **key identity on THIS, not email**), `iat`, `exp` (~2 min), `jti`, plus `pro`/`sponsor` for perk accounts. Copy `sso_verify_token()` from the connector verbatim. **Your `sso.php` MUST (these are universal, learned the hard way):** 1. **Discard any existing session** and regenerate the session id — rebuild identity ONLY from the token. Never trust a pre-existing cookie (it can be a *different* member). 2. **Treat the token as email-verification** — no confirmation email, no "verify your account" wall, no password. Auto-provision SSO members **active + verified + passwordless**. 3. **Key the member on the stable `abcpro_id`** — store it; if the token's email differs from a known `abcpro_id`, re-point that member (don't fork a new account). Email is a mutable display field. 4. **Land on your app's default surface** (your dashboard/app home) — never the homepage or a signup wall. Honor `goto` only if it's a safe same-site path; otherwise fall back to your default. Provisioning (`?provision=1`, server-to-server) marks the email verified and returns `{ok:true}` without a redirect. --- ## 10. Attribution (`ref_code`) ABCPro referral codes ride in as `?ref=CODE`. Capture on landing, **persist onto the order** at purchase time (webhooks have no browser cookie), and pass it back as `ref_code` when you report the sale. ABCPro resolves who gets paid — you never compute commissions. --- ## 11. Product Profile — the template a product submits This is the **only** product-authored surface. Fill it out; ABCPro configures the project. ``` Product name: project_key: home_url / sso_url: Commission mode: always | per_event | off Pool model: product-computed | hub-rate-driven Commissionable base: Pool amount / %: transaction_type(s): external_id scheme: Reporting cadence: SSO landing path: Catalog entries: Confirm-back questions: ``` --- ## 12. Confirm-back answers ABCPro gives every product - **`transaction_type` naming** — you propose it in your profile; ABCPro accepts it and creates the matching config row. Keep it stable; it's namespaced per product. - **Sub-cent journaling** — see §6.1: whole-cent reporting, you carry sub-cent remainders, authoritative microcents go in `meta` for audit. ABCPro does not pay below a cent. - **Extra fields** — put anything product-specific in `meta`; it's stored verbatim. New *top-level* fields are a contract change (§13), not a per-product add. --- ## 13. Versioning & change control This contract is versioned (currently **v1.0**). Only ABCPro edits it. Additive changes (new optional field) bump the minor version; breaking changes bump the major and are announced to connected products with a migration window. A product's need for something new is a request to ABCPro, evaluated on the whole network's terms — never a unilateral build. --- ## Appendix A — Current Product Profiles (configured) | Product | Mode | Pool model | Commissionable base | Pool | txn_type(s) | |---|---|---|---|---|---| | **GigPeach** | always | product-computed | per-product (varies) | producer's % of net, sent per sale | `retail_sale` | | **BluSpots** | always | product-computed | per-product/widget (varies) | sent per sale | `retail_sale`, `subscription` | | **GuestBookit** | per_event→rate | hub-rate-driven | direct $20 event (not partner subs) | **50%** of `(gross − fee)` via admin rate | `retail_sale` (50%), `subscription` (0%) | | **BluRepo** | always | product-computed | **NET storage profit** — storage bill − AWS cost − Stripe fee; excludes retrieval | **40%** of net storage profit | `storage_period` (see Appendix B) | ## Appendix B — BluRepo profile (LIVE) - **Unit:** one report per **prepaid 30-day cycle** per attributed customer, on the customer's own anniversary — reported **only when the cycle is actually collected**. - **external_id:** `blurepo:storage:{user}:{cycle_start_date}` where cycle_start_date is `YYYY-MM-DD` (e.g. `blurepo:storage:42:2026-09-01`) — period-stamped → idempotent + reversible. *(Changed 2026-08-30 from `YYYY-MM` to the cycle-start DATE; ABCPro dedupes off the full `external_id` string, so the change is transparent to reconciliation.)* - **Commissionable base:** **NET storage profit** = storage bill ($10 base incl. 1 TB + per-TB overage) − AWS storage cost − Stripe fee on the storage share. Excludes retrieval/restore charges. - **Pool:** `marketing_pool_cents = round(0.40 × net_storage_profit)`, product-computed and sent explicitly (rate **40%**; `meta.rate_pct = 40`, preserved per ledger row). `meta.basis = "net_storage_profit"`; `meta` also carries `storage_revenue_microcents`, `aws_cost_microcents`, `marketing_pool_microcents`, `pool_carry_microcents`. Whole cents only; sub-cent remainder carried per-customer on BluRepo's side (§6.1). - **Example:** 1 TB Vault → $10 − AWS $0.99 − Stripe $0.59 = **$8.42 net** → pool = 40% = **$3.37**. 1 TB Instant → net $5.41 → pool = **$2.16**. - **transaction_type:** `storage_period` — **counts toward rank qualifying volume** (verified: not in `rank_nonqualifying_types`). Real recurring *customer* revenue, so it both funds the pool AND builds rank; only internal FEES are excluded from rank volume. - **Non-payment:** grace → lock → suspend → delete lifecycle; reports fire ONLY on collected revenue (a delinquent cycle reports nothing until paid). Refund/chargeback → `event:"refund"` with the same period-stamped `external_id` (nets that cycle's pool back out). - **SSO landing:** `/app/`. - **Status (2026-08-30):** **LIVE — billing ON (`billing_enabled=1`).** BluRepo's own Stripe account (under the ABCPro org), live keys + webhook verified; prepaid 30-day cycles fully automated. Real `storage_period` reports are flowing to the hub on each collected cycle.