Skip to content

6. Platform gaps and the integration contract

Generated from a canonical source

This page is a read-only projection of docs/solution-review/06-platform-gaps-and-contract.md. Edit the canonical file, then run npm --prefix tools/project-knowledge-derive run derive.

BigCommerce already provides the commerce spine this product builds on: catalog and product model, one-time orders, customers, hosted checkout, and a card vault that tokenizes at checkout. What it does not provide is anything that recurs — no billing engine, no subscription product type, no dunning, no pause/skip, no subscriber portal, and no atomic way to capture "this purchase is a subscription" at the point of sale. Everything a subscription platform must do that the commerce spine does not is a gap to be closed from outside the platform.

The platform-integration contract (see ../gsi-package/05-bc-platform-integration-contract.md, section 5.2) catalogs ten such gaps and — because it is written for a first-party native delivery, the fork this review weighs against the shipping marketplace app — prices a platform-layer alternative for each. This page does one thing the contract does not: it sorts the ten gaps by kind, because the kind determines the architectural response — and, for three of them, whether there is a real decision for this review to make at all. Four kinds follow, then the one settled non-gap, then a map of all ten.

Kind A — Business logic the platform should never own

These are subscription-domain concerns the platform's core commerce objects would never model directly. The app owns them even in a fully native build; there is no meaningful platform-layer alternative to weigh, so they are scope, not decision points. The architectural response is the same regardless of native framing: own the state and the scheduling logic in the app's own data layer.

Gap How bridged Risk posture First-party alternative
No native recurring-order / billing engine (5.2.1) App owns subscription and billing-cycle state; calls the Orders API to create each cycle's order after a charge succeeds Proven; uses only existing Orders API surface A platform-native recurring-order primitive is conceivable but touches core checkout/order-pipeline internals — out of scope for this engagement; flag as a scope-confirmation item, not an open fork
No native dunning / retry logic (5.2.3) Billing engine owns a retry state machine (attempt count, next-retry time, terminal-state policy) external to the platform Proven; domain-specific business logic None — there is no platform-layer version of this gap to close
No native pause/skip primitive (5.2.4) Pause/skip state lives in the subscription record; the scheduler consults it before generating each cycle Proven None on the logic; the only open question is data-ownership — if the build gets a first-party persistent data layer, this state lives there, with no effort difference either way
No native prepaid / fixed-term model (5.2.8) Fixed-term plans modeled as a pre-computed charge schedule generated once at purchase, consumed by the same scheduler Proven None — a billing-engine scheduling concern the platform's primitives never need to know about

Kind B — Gaps bridged from outside the platform, each with a costed first-party alternative

Each of these is closed today by a proven bridge, carries no platform-schema risk in its bridged form, and holds a first-party alternative as an explicit decision the review can take either way. Two ride existing platform primitives (metafields, Price Lists, custom fields); one is an external surface. The architectural response is: ship the bridge now, and decide deliberately whether to invest in the first-party version.

Gap How bridged Risk posture First-party alternative
No native subscription product type (5.2.2) Subscribability and plan config carried in product metafields; subscription pricing delivered via Price List assignment Proven, portable, zero catalog-schema risk A first-party subscription product-type field exposed through the Products API — a bounded catalog-schema change with its own migration and backward-compatibility surface. Recommend the metafield pattern unless the catalog team has already road-mapped the addition
No native subscriber portal (5.2.5) A hosted portal plus a headless/embeddable variant, both external to the platform's customer-account pages Proven UX, lower integration risk; structurally separate from native account pages. This is a surface bridge, not a schema bridge — but its signature (proven bridge + costed first-party alternative) is pure Kind B Deepen the platform's own customer-account UI to host subscription management — tighter cohesion and higher perceived trust, but a larger effort touching core account screens. This is decision D-2 in 07-open-decisions.md
Orders API cannot tag orders as subscription-generated (5.2.6) Order-level custom fields and metafields carry a subscription reference ID, set by the app at order-creation time Proven, zero platform-schema risk A first-party subscription-reference field on the order object, natively queryable — a bounded schema-level extension. Recommend the custom-field pattern as the lower-risk default

Note that checkout-intent capture (5.2.7) uses the same shape of bridge — a cart-level metafield read back after the fact — but is pulled out into Kind C below, because its non-atomicity raises the stakes past a routine bridge-versus-first-party trade.

Kind C — The gap that matters most: intent capture is not atomic with order creation

The single gap here (5.2.7) is the highest-value "could native status close this" question in the contract. The platform's Optimized One-Page Checkout exposes no server-side hook to capture "this cart line is a subscription" at the point of purchase, and the Carts API has no line-item-level custom-field surface. The reference implementation routes around it: a storefront widget writes subscription intent into a cart-level metafield (namespace bc_subscriptions, key subscription_intents), and the store/order/created webhook handler fetches that metafield after checkout and creates the subscription from it. A thank-you-page JavaScript post is the fallback for storefronts that cannot render the in-cart widget.

This works, and it is proven — but it is reconciliation, not transaction. Intent is captured before the order exists and read back after it exists; the two are stitched together by a webhook rather than committed together. The failure surface is the seam: a dropped or delayed webhook, a metafield that did not persist onto the order, a storefront on the thank-you-page fallback. The first-party alternative closes the seam entirely — a genuine checkout-pipeline hook or a line-item-level custom-field extension that captures intent atomically with order creation — but it means touching core checkout internals, carries materially higher risk and effort, and its feasibility depends on a checkout-platform commitment outside this engagement's scope. This is decision D-1 in 07-open-decisions.md; the contract's recommended default is to accept the reconciliation pattern unless the checkout-platform commitment is confirmed.

Kind D — Platform-side data gap

One gap (5.2.9) is neither app-logic nor an integration-surface trade; it is a defect in platform data. The platform's network_transaction_id threading — the value that anchors a chain of merchant-initiated renewal charges back to the original cardholder-initiated authorization, required to preserve card-network stored-credential and SCA-exemption treatment — arrives empty on a material share of stored instruments for stores connected through Worldpay or Paymetric. Without it, renewal charges on those gateways degrade decline rates and forfeit the EU PSD2 recurring-charge exemption.

The mitigation lives in the processor adapter: persist the network transaction ID from the cardholder-initiated authorization response directly, then re-attach it on every subsequent merchant-initiated charge for those two gateways. Closing the gap upstream is BigCommerce's own payments-platform responsibility, tracked separately, on no assumed timeline. The build should therefore treat the adapter-level persist-and-reattach as permanent behavior for Worldpay and Paymetric — not a temporary shim — and not assume the platform gap closes before or during the engagement. This is carried as an open assumption in the register in 07-open-decisions.md.

The settled non-gap: marketplace-app delivery

The tenth gap (5.2.10) is different in kind from the other nine, and it is already settled inside this package — it is not a decision for the review. The platform's app framework (marketplace listing, single-click install, OAuth token exchange, App Extensions registration, uninstall lifecycle) is how third-party capability reaches a store — and it is how the shipping reference implementation reaches stores today. First-party native functionality would not ride that framework: in a native delivery there is no marketplace app and no install step.

Everything Epic 1 provisions must still happen per store — a provisioning record, the merchant-facing panels on Products/Orders/Customers pages, event wiring, teardown with equivalent cleanup guarantees. So Epic 1 ships already restated in native terms (per ../gsi-package/_native-restatements.md): same story IDs, phases, and effort tiers, with behavioral parity to the reference implementation preserved. Install becomes store enablement, OAuth/JWT becomes platform-session authentication, App Extensions become native panels, webhook registration becomes platform event wiring, and uninstall becomes disablement with a 30-day recovery window. Eight stories outside Epic 1 (US-2.1, US-2.2, US-2.3, US-4.1, US-9.5, US-14.2, US-22.2, US-28.4) carried similar install-time framing and ship restated the same way. What remains open is only how the native equivalents are realized — a solution-design position, not a scope choice.

Gap-to-kind map

All ten contract gaps, named verbatim from the contract's section 5.2, with the kind that determines their response and where each is covered in this package.

# Gap (verbatim) Kind Where covered
5.2.1 No native recurring-order / billing engine A Contract 5.2.1; scope-confirmation at solution design
5.2.2 No native subscription product type B Contract 5.2.2
5.2.3 No native dunning / retry logic A Contract 5.2.3
5.2.4 No native pause/skip primitive A Contract 5.2.4
5.2.5 No native subscriber portal B Contract 5.2.5; decision D-2
5.2.6 Orders API cannot natively tag orders as subscription-generated B Contract 5.2.6
5.2.7 No server-side checkout hook to capture subscription intent C Contract 5.2.7; decision D-1
5.2.8 No native prepaid / fixed-term subscription model A Contract 5.2.8
5.2.9 Network-transaction-id threading gap for Worldpay/Paymetric-connected stores D Contract 5.2.9; assumption register item 5
5.2.10 The reference implementation reaches stores as a marketplace app; native functionality does not settled Contract 5.2.10; native restatements (Epic 1)