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.

Architecture diagram: merchant checkout, smart routing layer, multiple acquirers Your checkout EuPPay Smart routing + 3DS + reconciliation CyberSource MPGS Paystack Paydock Paychtec

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.
webhook-handler.php
// 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.