How cp10 works

A guided walkthrough of the standalone checkout engine at web/checkout-eet/p/static/checkout-eet-engine-cp10/ — how it gets onto a page, how a card and a PayPal purchase flow through it, what happens when a bank says no, and why it shares zero code with its siblings. Every screenshot below is the real thing, captured from the engine running locally against the mock backend.

Status: cp10 is a reference build — fully built and tested, servable locally, with no production traffic (the rollout was deliberately discarded). Its job is to make the checkout understandable. Line numbers in this document drift as code changes; the file names are the stable pointers.

1The cast

Five things cooperate to turn a visitor into a subscriber. Only the first two are "ours" on the page:

The host page
What the customer visits (p/static/cp-eet_9-layout-update/). It owns the branding, plan selection, order summary, jQuery — and one empty <div class="epcheckout-target"> where the engine will appear.
The engine — cp10
The payment form itself: email/name fields, card boxes, PayPal, the Subscribe button, and all the logic. One big file (src/index.js, ~3,000 lines) plus small extracted modules, each with tests beside it.
The loader
utils/et_utils.js — a page-side helper. Its epCheckout.load() fetches the engine's HTML over ajax and injects it into that div. The engine never calls the loader; it is only carried by it.
The BFF backend
The subscription API the engine POSTs to (offer details, email checks, create_subscription). Locally, ?localBackend=1 swaps it for a one-file PHP mock (p/mock-backend.php).
Braintree
The payment gateway. Its SDK renders the card boxes as its own iframes and turns card data into a one-time token — our code never sees a card number.

2How the engine gets onto the page

The one fact that explains everything else: the engine is not an iframe. Its HTML is injected into the page's own document, so page and engine share one window, one jQuery, one URL. That is why the page can read the engine's results from plain globals, and why the page must provide jQuery ≥ 3.5 before the engine arrives.
sequenceDiagram
    participant P as Host page
    participant L as Loader (et_utils.js)
    participant R as PHP route
    participant E as cp10 engine
    P->>L: epCheckout.load({selector, cp_engine_url})
    Note over P: sets window.epCheckoutParams
(offer id, version label) L->>R: ajax GET ?page=checkout-eet-engine-cp10 R-->>L: index.php with dist/bundle.js BAKED INLINE L->>P: inject HTML into .epcheckout-target Note over E: inline script runs, initInlineCP() boots E->>E: read epCheckoutParams, freeze checkoutEngineVersion E->>E: mount Braintree hosted fields (card iframes)

The bundle is baked into the HTML server-side rather than loaded via <script src> — a deploy filter once dropped dist/ and silently killed the form, so cp10 inlines it and logs a precise console error if it's ever missing (index.php, the bundle-include guard).

Here is the real result — the host page with cp10 injected (captured live; the engine request was pointed at the cp10 route at the network layer):

The cp9 host page with the cp10 engine injected: express PayPal, card form, plan selection and order summary
  1. Page-owned territory — plan selection, order summary, header. The engine never touches these; it reads the chosen offer from epCheckoutParams.
  2. Injected cp10 — everything in the form column from "Email" down is the engine's markup living inside the page's div. The card boxes are Braintree's iframes mounted into #bt-card-number etc.
  3. The delegation seam — the express PayPal button belongs to the page, but its clicks are delegated into the engine's #paypal-option. This only works because both share one jQuery instance — the reason jQuery is a page-provided precondition, never bundled.

3A card purchase, end to end

sequenceDiagram
    participant C as Customer
    participant E as cp10 engine
    participant B as Braintree (iframes)
    participant S as BFF backend
    C->>E: fill email + names, click Subscribe
    E->>S: known customer? (email check)
    E->>B: tokenize card (inside Braintree's iframes)
    B-->>E: one-time payment nonce
    Note over E: optional 3DS challenge here
    E->>S: POST create_subscription (nonce + offer + fraud signature)
    S-->>E: subscription_id
    E->>E: window.newSubData = payload
    E->>C: redirect to onboarding
  
  1. Tokenization keeps card data out of our hands. The digits live in Braintree's iframes; we receive only a nonce — a single-use stand-in token. PCI burden stays with the gateway.
  2. The fraud signature rides along: buildForterCardParams (src/forter-card-params.js) assembles device/payer metadata; absent PayPal fields are omitted entirely (they used to go out as the literal string "undefined").
  3. 3D-Secure can interpose a bank challenge. One predicate decides whether to block: block ⟺ a liability shift was possible but did not happen (shouldBlockForThreeDsResult, src/three-d-secure-result.js).
  4. The result contract is two globals: window.newSubData (new customer) or window.existingSubData (existing customer). The page's post-purchase hooks read them directly — same window, no messaging needed — then the engine redirects to onboarding.

4When the bank says no

Failures arrive as a raw text body — no structured decline code reaches the frontend. The mapper in src/decline-codes.js chooses the customer's banner in two tiers:

TierRuleWhy
1 — reliable signal If the attempt's payment method was PayPal (_paymentType), show the PayPal banner. Period. The engine knows ground truth about which instrument was used; every other banner gives card-specific advice ("the number on the back of your card") that is wrong for a PayPal payment.
2 — substring fallback An ordered needle list maps the body to one of nine verbatim banners ("Insufficient Funds" is checked before "Declined", etc.). Byte-identical to the legacy behaviour; the order is load-bearing and golden-master-tested, so nobody can silently change what a customer reads.

This is what tier 2 looks like for a body containing Processor Declined (2046) on the card path — captured live:

The generic decline banner rendered in red above the Subscribe button
  1. The generic-decline banner, rendered into #payment-error by handlePurchaseErrormapDeclineResponse. Every one of the nine banners is preserved character-for-character (phone numbers, link copy) and pinned by a golden-master test against the legacy implementation.

5The PayPal flow

Selecting PayPal starts the express handshake: the engine opens a popup, asks the backend for a checkout token, sends the popup to PayPal, and dims the page behind an overlay while it polls for the popup to come back:

The PayPal overlay dimming the checkout while the popup handshake is in flight
  1. The overlay (.paypal-checkout-overlay) blocks the form while the popup is open; "Click to Continue" refocuses the popup. The engine broadcasts paypalModalStarted / Failed / Closed via origin-locked postMessage — three events the page's overlay handling depends on. Dropping any of them breaks this handshake.
  2. PayPal selected_paymentType is now 'paypal', which is the tier-1 decline signal from §4.
  1. The popup returns from PayPal carrying token=…&PayerID=…; the engine's 100ms poll spots it, closes the popup, and hides the overlay.
  2. The engine exchanges the token for a billing agreement (BAID) and fetches the payer's details — watch the form below: First/Last name became "Mock Buyer", written back from the (mock) PayPal account.
  3. The same Subscribe button now drives the same create_subscription call as the card path — just with the BAID instead of a card nonce. Here it was answered with a decline, and tier 1 correctly produced the PayPal banner:
After the PayPal handshake: payer name written back into the form and the PayPal decline banner rendered
  1. Payer details written back — "Mock Buyer" came from the PayPal account via get_express_checkout_detail, not from typing.
  2. The PayPal banner Before the fix, this exact failure body showed the generic card banner, because banner choice depended on a fragile magic phrase in the response text.

6Why it shares nothing with cp8/cp9

cp10 is a fork of cp9's engine with every shared path severed: its own copy of the gateway constants, its own modules, dependencies, and build. The duplication is deliberate — the goal is fault isolation, so nothing anyone does to cp8/cp9 can break cp10, and vice versa. The only tolerated couplings run to the page and the platform, and both are documented preconditions:

Host page (cp-eet_9) provides: window.jQuery ≥ 3.5, .epcheckout-target container, epCheckoutParams (offer ids, version) et_utils.js loader fetches the engine and injects it same-document checkout-eet-engine-cp10/ (sealed) index.php bundle baked in server-side (no script src); loud console error if dist/bundle.js missing src/ — own copy of everything vendored gateway constants, decline-code map, origin-scoped messaging, immutable version, 3DS predicate, Google Pay, jQuery plugins own package.json + node_modules + dist/ Outbound contract paypalModalStarted / Failed / Closed, analytics events — all same-origin only; window.newSubData / existingSubData on success cp8 / cp9 engines untouched throughout; a break there cannot reach cp10, or vice versa Platform PHP mounts forter / pipa / OneTrust — tolerated serve-time deps, guarded so cp10 runs standalone inject zero shared code

The accepted cost: a gateway-ID rotation is now an N-place edit (canonical file + every engine that vendored it). The keys are browser-public already, so the duplication adds no secret exposure.

7Poke it yourself

# the bare engine, no host page (you'll see the un-hidden address block —
# that's what a missing page-provided jQuery looks like)
cd web/checkout-eet/p/static/checkout-eet-engine-cp10
php -S localhost:8123        # open http://localhost:8123/index.php

# 135 unit tests — decline map, 3DS predicate, messaging, version, modules
npm run test:unit

# 4 e2e tests driving the real page+engine+mock-backend flow (needs network)
npm run test:e2e

# the full experience in a browser: host page + engine + mock backend
cd ../../..   # web/checkout-eet
php -S localhost:8100
# open: http://localhost:8100/p/?page=cp-eet_9-layout-update&o=fs-1p1w8w-2_49p1w&localBackend=1
# (loads the cp9 engine by design; the e2e shows the same page driving cp10)

Reading order

  1. prime-checkout-eet-engine-cp10.md — the contract (load handshake, isolation boundary, traps).
  2. index.php — serving: the inline bundle, the guarded platform mounts.
  3. src/index.js — the engine; everything in §2–§5 lives here.
  4. The extracted modules in src/ — each small, pure, and tested: decline-codes.js, three-d-secure-result.js, engine-messaging.js, checkout-engine-version.js, forter-card-params.js, and friends.
  5. decline-codes-investigation.md — why the decline mapping looks the way it does.
  6. tests/e2e/load-contract.spec.ts — the four flows of §2–§5, executable.

AI-generated walkthrough (2026-06-10). All four screenshots were captured live from the engine running against the mock backend during generation; flows and file pointers verified against the code at that date. Source assets: 2026-06-10_0113_cp10-how-it-works-walkthrough/screenshots/.