Push Push Payments Gateway API

Version 1 — server-to-server, JSON over HTTPS. Operated by Push Push Payments Ltd.

Authentication
Every request carries an API key and an HMAC-SHA256 signature over the raw body.
  • X-Api-Key — your key id
  • X-Timestamp — Unix seconds, valid for 5 minutes
  • X-Signature — hex HMAC-SHA256 of {timestamp}.{METHOD}.{path}.{rawBody} using your API secret
  • Idempotency-Key — optional, safe retries on POST
const base = `${ts}.POST./api/public/v1/payments.${body}`;
const signature = crypto.createHmac("sha256", secret).update(base).digest("hex");
Create a payment
POST /api/public/v1/payments
{
  "order_id": "A-1001",
  "amount": 4990,           // minor units
  "currency": "EUR",        // EUR | USD
  "description": "Order A-1001",
  "return_url": "https://shop.example/thanks",
  "callback_url": "https://shop.example/hooks/pushpush",
  "customer": { "email": "buyer@example.com", "country": "DE" },
  "card": {
    "number": "4012888888881881",
    "exp_month": 12,
    "exp_year": 2030,
    "cvv": "123",
    "holder": "JOHN DOE"
  },
  "test_scenario": "success" // sandbox only: success | decline | pending | error
}

card is required for live keys. The card number is never stored — only the brand and last four digits are kept for reconciliation.

{
  "id": "1f0c...",
  "object": "payment",
  "status": "succeeded",       // succeeded | failed | pending
  "amount": 4990,
  "currency": "EUR",
  "payment_url": null,         // set when the bank requires 3-D Secure
  "refunded_amount": 0,
  "failure": null,
  "metadata": { "card": { "brand": "visa", "last4": "1881" } }
}

When status is pending and payment_url is present, redirect the shopper there to complete bank verification. The final result arrives as a webhook.

Test cards
Any future expiry date and any CVV.
  • 4012888888881881 — Visa, approved
  • 4111111111111112 — Visa, declined
  • 4539148803436467 — Visa, 3-D Secure
  • 2223000048450011 — Mastercard, approved
  • 5300000000000006 — Mastercard, declined
  • 5454545454545454 — Mastercard, 3-D Secure
3-D Secure flow
Everything stays on the Push Push Payments domain.

1. Create the payment. If the bank requires verification, the response has status: "pending" and a payment_url on our domain.

2. Redirect the shopper to that URL — the bank page loads inside it.

3. After the bank step we return the shopper to your return_url with payment_id and status query parameters.

4. The authoritative result always arrives as a signed webhook.

Retrieve and refund
GET /api/public/v1/payments/{id} · POST /api/public/v1/refunds

For GET requests the signature base string uses an empty body.

{ "payment_id": "1f0c...", "amount": 2000, "reason": "partial return" }
Webhooks
Delivered to your callback URL, signed with your API secret.

Verify X-Signature as HMAC-SHA256 of {X-Timestamp}.{rawBody}. Respond 2xx; failures are retried with backoff up to 6 times.

{
  "id": "evt_...",
  "type": "payment.succeeded",
  "created_at": "2026-01-01T10:00:00.000Z",
  "data": { "id": "1f0c...", "status": "succeeded", "amount": 4990 }
}
Sandbox
Deterministic testing without real funds.
  • success — settles immediately as succeeded
  • decline — settles immediately as failed
  • pending or omitted — returns a hosted checkout link you can complete manually
  • error — simulates a routing failure (HTTP 502)