← Partner integration overview
The complete technical spec — auth, fields, refunds, SSO, idempotency, sandbox & going live. Hand it to your developer. · Get the drop-in connector →
# 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=<assigned, e.g. blurepo>
ABCPRO_KEY_ID=<ak_test_… (sandbox) or ak_live_… (production) — the key sets the mode>
ABCPRO_SECRET=<HMAC secret for sale reporting — keep private, server-side only>
ABCPRO_SSO_SECRET=<shared SSO secret — same value stored on the project row>
```
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: <ABCPRO_KEY_ID>
X-AbcPro-Timestamp: <unix seconds>
X-AbcPro-Signature: hex( HMAC_SHA256( ABCPRO_SECRET, "<timestamp>.<raw_json_body>" ) )
```
- The signed base string is **`"<timestamp>.<body>"`** — 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
`<your sso_url>?t=<token>[&goto=<path>]`. 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: <e.g. BluRepo>
project_key: <e.g. blurepo>
home_url / sso_url: <https://… / https://…/sso.php>
Commission mode: always | per_event | off
Pool model: product-computed | hub-rate-driven
Commissionable base: <EXACTLY what earns — and what does NOT>
Pool amount / %: <e.g. 30% of commissionable revenue, after Stripe>
transaction_type(s): <names you will send, + which count toward rank volume>
external_id scheme: <e.g. blurepo:storage:{user}:{YYYY-MM-DD cycle-start}>
Reporting cadence: <per sale | per period per customer | …>
SSO landing path: <e.g. /app/>
Catalog entries: <title, buy_url, price — optional, for GC storefronts>
Confirm-back questions: <anything you need ABCPro to name or decide>
```
---
## 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.