What HyperSwitch is, who it's for, and the PHP SDK we just open sourced
Ask AI about this article
HyperSwitch is an open-source payment orchestration layer: one API in front of many payment processors, with routing, failover, and retries handled for you instead of hard-coded into your checkout. It's built by Juspay, Apache 2.0 licensed, and you can self-host it or use their managed service. The one thing missing was a decent PHP client, so we built one and open-sourced it: x2y/hyperswitch-client, MIT, framework-agnostic, covering the full public API surface. `composer require x2y/hyperswitch-client` and you're going.
What is HyperSwitch?
Juspay had been running payments at serious scale for years, 300 million transactions a day for enterprises and banks, and in 2022 they open-sourced the orchestrator behind it. That's HyperSwitch. Today it fronts 200+ connectors, 150+ payment methods, and 135+ currencies.
The word "orchestration" gets used loosely, so here's the concrete version. Instead of your application integrating Stripe, then Adyen, then a regional processor, then a wallet provider, each with its own SDK, its own webhook format, its own idea of what a refund is, you integrate HyperSwitch once. It talks to the processors. You get one payment object, one webhook signature scheme, one refund call.
Two things make it different from the commercial orchestrators. It's Apache 2.0, so you can read the code that moves your money. And it's deployable on your own infrastructure, cloud or on-prem, which means the orchestration layer isn't itself a vendor you're locked into. For a category where the whole selling point is "don't get locked into one processor," that consistency matters.
What problem does orchestration actually solve?
Four, mostly:
- Processor redundancy. When your primary gateway has a bad afternoon, traffic routes to a secondary instead of your checkout returning errors. If you've ever watched a processor incident from the wrong side, you know what that hour costs.
- Rates and routing. Different processors are cheaper for different card types, regions, and volumes. Routing rules let you act on that without a deploy.
- Local payment methods. Selling into a new market usually means a new local method and a new integration. With orchestration it's configuration, not an engineering project.
- Leverage. A processor that knows it would take you six months to leave negotiates differently than one that knows it would take you an afternoon.
Who actually benefits, and who doesn't?
Worth being straight about this, because orchestration is oversold.
You probably want it if: you take payments in more than one country; you already run more than one processor, or you've been wanting to and dreaded the work; you're a marketplace or platform that has to pay people out as well as charge them; you run subscriptions and mandates, where processor-specific behavior gets genuinely painful; you have compliance or data-residency requirements that make self-hosting the payment layer attractive; or you're big enough that a few basis points of routing efficiency is real money.
You probably don't if: you're a single-region store on one processor, happy with it, doing modest volume. Adding an orchestration layer means one more service to run and understand. Stay on your gateway's SDK and revisit when a second processor is actually on the table. We'd rather tell you that than sell you an integration you don't need.
So why did we write a PHP SDK?
Because the ecommerce world our clients live in runs on PHP. Magento, Adobe Commerce, WooCommerce, Laravel. That's where the payment code that actually needs orchestrating is. HyperSwitch's own client libraries didn't cover PHP, so every PHP integration meant somebody hand-rolling HTTP calls, re-deriving the webhook signature scheme, and getting the auth headers subtly wrong.
We needed it for our own Magento work, and that's the honest origin story. Building it as a standalone MIT library instead of burying it inside a module was the easy call: the module gets a maintained dependency, and everyone else gets a client library.
What's actually in it
Full coverage of the public API surface. Not "the payments endpoint and we'll see":
| Group | Endpoints | Accessor | | --- | --- | --- | | Payments | 11 | $client->payments() | | Subscriptions | 11 | $client->subscriptions() | | Customers | 6 | $client->customers() | | Payouts | 6 | $client->payouts() | | PaymentMethods (V1) | 6 | $client->paymentMethods() | | PaymentMethodSessions | 6 | $client->paymentMethodSessions() | | Refunds | 4 | $client->refunds() | | Disputes | 4 | $client->disputes() | | Mandates | 3 | $client->mandates() | | Connectors | 5 | $client->connectors() | | ApiKeys | 5 | $client->apiKeys() | | BusinessProfile | 5 | $client->businessProfile() | | MerchantAccounts | 4 | $client->merchantAccounts() | | Platform | 3 | $client->platform() | | Relay | 2 | $client->relay() | | PaymentLinks | 1 | $client->paymentLinks() | | Proxy | 1 | $client->proxy() |
17 of 17 resource groups, roughly 83 endpoints, plus inbound webhook handling. Creating a payment looks like this:
use Symfony\Component\HttpClient\Psr18Client;
use Nyholm\Psr7\Factory\Psr17Factory;
use X2Y\HyperSwitchClient\HyperSwitchClient;
use X2Y\HyperSwitchClient\Auth\MerchantKeyAuthResolver;
use X2Y\HyperSwitchClient\Dto\Payment\Request\CreatePaymentRequest;
use X2Y\HyperSwitchClient\Enum\Currency;
$psr17 = new Psr17Factory();
$client = HyperSwitchClient::sandbox(
httpClient: new Psr18Client(),
requestFactory: $psr17,
streamFactory: $psr17,
auth: new MerchantKeyAuthResolver('snd_…'),
);
$response = $client->payments()->create(new CreatePaymentRequest(
amount: 4500,
currency: Currency::USD,
));
echo $response->paymentId; // pay_abc123
echo $response->clientSecret; // pay_…_secret_…
Typed DTOs and enums throughout, so your IDE and PHPStan can actually help you. Named arguments because it's PHP 8.1+ and payment requests have a lot of optional fields.
Webhooks, and the one detail worth stealing
HyperSwitch signs webhook bodies with HMAC, SHA-512 by default. The SDK ships a verifier and a typed decoder, and there's one ordering decision in there worth calling out: verification happens before the JSON decode. An invalid signature short-circuits without the body ever reaching the parser. Handing unverified attacker-controlled input to a parser is a small habit with a long tail of consequences, and plenty of hand-rolled webhook endpoints get it backwards.
$decoder = new EventDecoder(new SignatureVerifier(getenv('HYPERSWITCH_WEBHOOK_KEY')));
try {
$event = $decoder->decode($body, $request->getHeaderLine('X-Webhook-Signature-512'));
} catch (InvalidSignatureException) {
return $response->withStatus(401);
} catch (InvalidWebhookPayloadException) {
return $response->withStatus(400);
}
match ($event->eventType) {
EventType::PAYMENT_SUCCEEDED => $this->markOrderPaid($event->asPayment()),
EventType::REFUND_SUCCEEDED => $this->creditMemoFor($event->asRefund()),
EventType::DISPUTE_OPENED => $this->flagDispute($event->asDispute()),
null => $this->logUnknown($event->eventTypeRaw),
default => $this->logUnhandled($event),
};
Note the `null` arm. When HyperSwitch ships an event type the SDK doesn't enumerate yet, `eventType` is null and the raw string is still on `eventTypeRaw`. An upstream addition shouldn't throw an exception in your webhook handler at two in the morning.
How we keep it from drifting
HyperSwitch publishes no public OpenAPI spec, which is the main challenge in maintaining a client for it. Our source of truth is a pinned Postman export checked into the repo, and `composer test:drift` diffs the SDK against it. When upstream moves, the drift test tells us what changed instead of a customer telling us. Each release records the date it was reconciled against upstream.
It's not as clean as generating from a spec, and moving to an OpenAPI basis is on the roadmap. But it's mechanical, it's in CI alongside the unit tests, the integration tests and PHPStan at strict level, and it beats finding out by hand.
Bring your own everything
PHP 8.1+, PSR-18 HTTP client, PSR-17 factories, PSR-3 logging. No framework dependency, so it drops into Laravel, Symfony, Magento, or a single bare PHP file with equal indifference. You supply the HTTP client, Guzzle or Symfony HttpClient or whatever you already have configured, which means your existing timeouts, retries, proxy settings and logging apply to payment calls too rather than being reinvented inside our library.
It's v0.1.0 and pre-1.0, so treat the API as still settling. It's tested and we're using it in production work, but SemVer means what it says before 1.0.
Get it, or get help with it
composer require x2y/hyperswitch-client
Source and docs: github.com/X2Y-DEV/hyperswitch-php-sdk. There's more on the SDK landing page, including the webhook receiver example. Issues and pull requests welcome; if you find a gap in the coverage, that's useful to us.
And if you'd rather not do the integration yourself: payment work is unforgiving, the edge cases are where the money is, and we've been in it a while. We're building a Magento module on top of this SDK, and we do this on Laravel and custom PHP too. If you're moving onto HyperSwitch and want a hand, whether that's the whole integration or a review of one you've already written, get in touch.