Payment Gateway
One API. Every acquirer.
Built for engineers who hate downtime.
A direct-integration payments stack with native 3DS 2.2, multi-acquirer routing, idempotent webhooks, and per-currency settlement — designed to be wired up in an afternoon and audited in production.
Integration architecture
Your checkout, our router, every acquirer in the rotation.
A single endpoint replaces N acquirer SDKs. Add or remove providers from the routing pool without redeploying.
Same call, every stack
Init a transaction in the language you already write.
curl -X POST https://api.euppay.com/v1/payments/initialize \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ord_42f7" \
-d '{
"amount": 4999,
"currency": "KES",
"reference": "ord_42f7",
"customer": { "email": "ada@startup.io" },
"routing": "smart",
"metadata": { "campaign": "spring-launch" }
}'
import EupPay from '{{ str_replace(' ', '-', strtolower(config('app.name'))) }}/node';
const eup = new EupPay(process.env.EUP_API_KEY);
const session = await eup.payments.initialize({
amount: 4999,
currency: 'KES',
reference: order.id,
customer: { email: user.email },
routing: 'smart',
idempotencyKey: order.id,
});
return res.redirect(session.checkout_url);
use EuPPay\Client;
$eup = new Client(config('services.eup.key'));
$session = $eup->payments()->initialize([
'amount' => 4999,
'currency' => 'KES',
'reference' => $order->id,
'customer' => ['email' => $order->customer_email],
'routing' => 'smart',
'idempotency_key' => $order->id,
]);
return redirect($session->checkout_url);
import euppay
eup = euppay.Client(api_key=os.environ["EUP_API_KEY"])
session = eup.payments.initialize(
amount=4999,
currency="KES",
reference=order.id,
customer={"email": user.email},
routing="smart",
idempotency_key=order.id,
)
return redirect(session.checkout_url)
Reliability
Engineered against the failure modes you actually see at 2am.
- Idempotency keys — replay any initialize call safely; we return the original session.
- Signed webhooks with HMAC-SHA256 and timestamp anti-replay window.
- Exponential-backoff retries on webhook delivery for 24 hours, then a queryable failed-events log.
- Acquirer health scores recomputed every 30 seconds — degraded providers drop out of the routing pool automatically.
- Per-environment API keys with scoped permissions and audit log of every key action.
// Verify signed webhook before trusting payload
$signature = $request->header('X-EupPay-Signature');
$timestamp = $request->header('X-EupPay-Timestamp');
$payload = $request->getContent();
$expected = hash_hmac('sha256', "$timestamp.$payload", config('services.eup.webhook_secret'));
if (! hash_equals($expected, $signature)) abort(401);
if (abs(time() - (int) $timestamp) > 300) abort(401);
$event = json_decode($payload, true);
match ($event['event']) {
'payment.captured' => Order::find($event['reference'])->markPaid(),
'payment.failed' => Order::find($event['reference'])->markFailed($event['decline_reason']),
'refund.completed' => Refund::find($event['refund_id'])->complete(),
default => null,
};
return response()->json(['ok' => true]);
Compliance posture
Audit-ready by default. No surprise findings during your next assessment.
PCI DSS L1
Service-provider Level 1 certified annually. Card data tokenized at our edge.
3DS 2.2
Native EMV 3DS with frictionless authentication and step-up fallback.
SCA-ready
PSD2 Strong Customer Authentication exemption logic baked into routing decisions.
GDPR + local DP
Regional data residency for EU and African data-protection regimes.
Ready to ship the integration?
Sandbox keys land in your inbox the moment you create an account.