Skip to main content

What UPG is and why we built it

UPG (Unified Payment Gateway) is a Synterra product: a multi-tenant subscription and entitlement service. It owns the integration with every payment rail, turns them into one event contract and one state machine, and calls back into each product's backend. Read this before you add payments to any product, or before you propose calling a payment provider directly.

UPG is not a payment processor. It never moves money, never sees a card number, and never holds funds. The rails do that.

How it works​

  • A tenant is a company using UPG. An app is one product surface of that tenant, in one environment. Test and live are two app rows.
  • A connection is one provider account: one Apple app, one Play app, one Stripe account. It holds the credentials, the webhook route and the product ids.
  • The product backend tells UPG who the user is with an opaque user_ref. UPG holds no email, phone or name.

Why build it instead of buying or calling rails directly​

OptionWhy not
Each app calls Apple, Google and Stripe itselfEvery app re-implements four signature schemes, about 45 provider event types, four state machines, retries, reconciliation, and every yearly breaking change
RevenueCat, Adapty, QonversionNo support for the India and GCC gateways and India's e-mandates, which were our biggest markets. We would still hand-build those rails and have two sources of truth. Also priced as a percentage of revenue
Kill BillAn active billing engine that owns the clock and the invoices. We need a passive receiver of state that Apple, Google and the gateways already decided. No in-app purchase plugins
Hyperswitch, Lago, PolarUseful ideas, no in-app purchases

What we took from prior art: keep entitlements separate from subscriptions, and do multi-tenancy as logical instances in one deployment. What we used instead of writing it: Apple's app-store-server-library for certificate-chain verification.

Be honest about the cost. Rail adapters are a permanent treadmill. About once a year each rail changes something that breaks: Apple moved receipts to the App Store Server API and notifications to v2, Google forces a new Billing Library and broke subscriptions v1 to v2, and India's rules move. That ongoing work is what a vendor's percentage buys. We pay it once, in UPG, for every product.

The design rules​

Rule

UPG is product-agnostic. Three tests: rename the first tenant with sed and nothing breaks; a second tenant needs no code change; adding an account, route, plan or rail needs no deploy. Product-specific data goes in metadata, never in a column.

Rule

Entitlement names are strings UPG never interprets. UPG says "this user holds pro". The product decides what pro unlocks and how much of it.

Rule

A tenant's read path never calls UPG synchronously. Products keep their own entitlement table, filled by callbacks. If UPG is down, products keep working.

Rule

No personal data. user_ref is opaque. A database check refuses values that look like an email, and validation refuses phone- and name-shaped values. Erasure pseudonymises user_ref and keeps the financial rows.

Who owns what​

UPG ownsThe product owns
Plans, prices per country, billing period, trialFeatures and what each entitlement unlocks
Routing: which rail serves which surface and countryLimit values, usage counting, metering
Plan groups (tiers) and upgrade rulesPlan display text and marketing copy
Entitlement namesThe paywall and its local entitlement table
Subscription state, money ledger, refundsSending invites and emails

Where to go next​