Over PAM's lifetime, we've integrated more than fifty third-party providers: payment processors, game providers, KYC vendors, CRM tools, communication services, lotteries, and sports data feeds. Each one has its own API format, its own callback conventions, its own failure modes, and its own upgrade schedule that has nothing to do with yours.
The challenge isn't adding a new integration. It's making sure that every integration you've already built stays working while you add the next one — and that when a provider changes something on their end, the blast radius is exactly one module.
We didn't start there. We learned our way there.
How integrations start
The first payment provider was PaymentIQ. The integration was built directly into the core platform. The callback handler knew about the deposit flow. The deposit flow knew about PaymentIQ's response format. When a deposit completed successfully, the same code path that parsed the PaymentIQ response also triggered the bonus evaluation and sent the confirmation email.
This worked. For one provider.
Adding the second payment provider required branching the callback handler. Adding game providers required adding new callback endpoints that touched the casino module directly. Communication providers were called inline from the business logic that needed them. The integration layer, such as it was, grew by accretion — each new provider wired into the nearest thing that needed it.
A payment provider updated their callback format. A field was renamed. Our callback parser failed to read it, and since the callback parser was tightly coupled to the deposit completion logic, deposits appeared to complete but bonus evaluations never fired. Players who deposited during a promotion received no bonus. The failure mode was silent — no exception, just a missing trigger. It took hours to trace because the callback format change and the bonus failure looked like separate issues.
The coupling problem
The root cause wasn't that the provider changed their format. Providers change their format. That's normal. The root cause was that our handling of the provider's format was entangled with business logic that had nothing to do with the provider.
A payment callback parser's only job is to parse a payment callback. It should not know that a successful parse triggers a bonus evaluation. The bonus evaluation should not know where the deposit signal came from. The communication service should not be called by whichever module happened to need a confirmation email sent.
These are separate concerns. Treating them as one concern is how a renamed field in a provider's JSON breaks your bonus engine.
The module isolation pattern
The integration layer in the current generation of PAM is structured around a clear principle: every provider integration is a self-contained, independently deployable module. The module boundary enforces what the module is allowed to know about.
Each integration module exposes a set of inbound endpoints (for provider callbacks) and implements a typed contract interface that the platform core can call. The module is the only code that knows what the provider's API looks like. The platform core doesn't know — and can't know — what format PaymentIQ uses for callbacks. It only knows what a completed deposit looks like from the platform's perspective.
When a provider changes their callback format, the fix is entirely inside the module. Nothing else needs to know it happened. The platform core continues calling ProcessDepositAsync and receives a DepositResult. The DepositResult is generated by the module after parsing whatever format the provider sent. The rest of the platform doesn't care what that format was.
Independent deployment
The module isolation goes further than just code separation. In the current architecture, each integration module is an independently deployable unit. Adding a new provider doesn't require a platform-wide deployment. Updating an existing provider's module doesn't require coordinating with other modules or restarting services that don't need to change.
This matters operationally. A game provider integration update that doesn't touch the payment layer or the bonus engine should not require testing those layers. It should not require a platform-wide deployment window. The deployment surface should match the change surface.
What fifty-plus integrations looks like in practice
Over PAM's lifetime, the integration surface has covered:
- Payment processors: PaymentIQ, Hexopay, WorldLine, SafeCharge — each with distinct callback formats, retry conventions, and reconciliation requirements
- Game providers: SportTech, PlayStar, AleaPlay, NetEnt, PragmaticPlay, Yggdrasil, Evolution, NoLimitCity, and more — each with their own game launch URL format, round callback structure, and session management model
- Communications: Twilio (SMS), SendGrid (Email), AWS SES (Email), Nexmo, Jojka, FortyTwo — each with different rate limits, template formats, and delivery confirmation models
- KYC and identity: DevCodeIdentity (SSO), jurisdiction-specific verification services
- CRM: FastTrack — player event streaming, segmentation callbacks
- Lotteries: LottoWarehouse, Lottoland, BitVille — with their own ticket formats and draw result structures
Each of these integrations has had at least one breaking change in its lifetime. Format updates, authentication model changes, endpoint deprecations, new required fields. In every case, the fix has been inside the module. Nothing else moved.
Provider failure isolation
The isolation model also affects failure behavior. When a game provider goes down, the module that handles that provider returns an appropriate error to the platform core. The platform core handles it. Other game providers keep working. Payment flows keep working. The bonus engine keeps working.
In the old tightly-coupled architecture, a provider failure could propagate in unexpected ways — because the provider's code was mixed into flows that shouldn't depend on it. With module isolation, the failure boundary is the module boundary.
When a provider changes their API, we update one module. We write tests for that module. We deploy that module. Nothing else is tested, touched, or at risk. The platform core doesn't know the change happened. That's the right outcome — a provider API update should have exactly as much impact as it deserves, which is as little as possible.
This isn't a microservices pitch
Module isolation doesn't require microservices. PAM's integration modules are deployed as part of the same host process for many providers. The isolation is at the code and dependency level — not necessarily at the network level. The key is that the module boundary is respected: code inside the module is the only code that knows about the provider's details.
The test for whether you have real isolation is simple: can you update a provider's integration without running tests for anything else? If the answer is no — if your test suite has cross-provider dependencies, or if the deployment pipeline requires platform-wide smoke tests for a single provider update — you have coupling you haven't named yet.