/// Developers

Three calls and a webhook.

Gateway is a hosted checkout, so the integration is deliberately small: create an order, send the customer to the page, verify the signed result. Everything below is the whole surface — the payment UI, the method logic, 3-D Secure, wallets, and receipts are ours to build and keep current.

/// Shape of it

What you're integrating against.

  • REST over HTTPS
  • JSON in, JSON out
  • Bearer API keys
  • Idempotency keys
  • Signed webhooks
  • Replay protection
  • Test and live modes
  • iOS & Android SDKs
  • Store plugins
  • Embedded fields
/// Quickstart

From nothing to a paid order.

The whole happy path. Amounts are in minor units, references are yours, and the checkout URL is what you redirect the customer to. The API base URL is issued with your sandbox keys — export it as PROTOCORE_API_BASE and the calls below run exactly as written.

[ 01 ]

Create the order

One call with the amount, the currency, and your own reference. The idempotency key is yours to generate — a retried request returns the original order rather than creating a second one.

POST /v1/orders
curl -X POST "$PROTOCORE_API_BASE/v1/orders" \
  -H "Authorization: Bearer $PROTOCORE_API_KEY" \
  -H "Idempotency-Key: 9F21-0716" \
  -H "Content-Type: application/json" \
  -d '{
        "amount": 4900,
        "currency": "EUR",
        "reference": "9F21-0716",
        "line_items": [
          { "name": "Brand identity sprint", "amount": 3850 },
          { "name": "Typeface licensing",   "amount": 600 },
          { "name": "Rush delivery",        "amount": 400 }
        ],
        "return_url": "https://arcstudio.ro/thanks"
      }'
[ 02 ]

Send the customer to the checkout

The response carries a checkout URL on your own checkout host — the one issued with your sandbox, or your own domain once you point it at us. Redirect to it, or open it in the SDK; that page is where the methods, the wallets, and 3-D Secure live, so there is nothing to build on your side.

201 Created
{
  "id": "ord_2h4Kq9wRz",
  "reference": "9F21-0716",
  "amount": 4900,
  "currency": "EUR",
  "status": "pending",
  "checkout_url": "https://<your-checkout-host>/c/2h4Kq9wRz"
}
[ 03 ]

Verify the signed result

When the payment completes, a signed event reaches your endpoint. Verify the signature against your webhook secret before you trust anything, then fulfil against the reference you already know.

POST /your/webhook — order.paid
{
  "type": "order.paid",
  "id": "evt_7Ka2Lm",
  "created": "2026-07-23T09:41:02Z",
  "data": {
    "id": "ord_2h4Kq9wRz",
    "reference": "9F21-0716",
    "amount": 4900,
    "currency": "EUR",
    "method": "usdc",
    "settlement": { "currency": "EUR", "amount": 4900, "rate": "0.9184" }
  }
}
/// Reference

The endpoints you'll actually use.

There is more in the docs, but this is the set most integrations never grow beyond.

POST /v1/orders
Create an order and receive its checkout URL. Takes an idempotency key.
GET /v1/orders/:id
The current state of an order — status, method, settlement, and refunds.
POST /v1/orders/:id/refunds
Refund against the order, full or partial, with a required reason.
POST /v1/orders/:id/void
Release the authorization before settlement, so no money moves at all.
POST /v1/links
Create a payment link — single-use or collecting, with an optional expiry.
GET /v1/payouts/:id
A settlement batch and the orders that funded it, with fees itemised.
POST /v1/customers/:id/cards
Vault a card for one-click and recurring charges; returns a token reference.
POST /v1/subscriptions
Charge a vaulted card on a schedule; each cycle produces its own order.
/// Webhooks

What your backend hears about.

Every event carries the order reference you created, so your handler can be a switch on the type and a lookup on your own side.

order.paid
Authorization and capture succeeded — fulfil against the reference.
order.failed
The payment did not complete, with a reason an operator can act on.
order.refunded
A refund was issued, full or partial, with its reason and note.
order.disputed
A chargeback was opened, with the evidence already attached to the order.
payout.settled
A settlement batch left for your account, listing the orders behind it.
subscription.charged
A recurring cycle produced its own order and receipt.
[ Verify ]

Check the signature before you trust the body.

Every delivery carries a timestamp and an HMAC signature over the raw body. Compare in constant time, reject anything older than your tolerance window, and treat a duplicate event id as already handled — we retry on non-2xx responses, so your handler should be idempotent too.

Node — signature verification
import { createHmac, timingSafeEqual } from "node:crypto";

export function verify(rawBody, header, secret) {
  const [ts, sig] = header.split(",").map((p) => p.split("=")[1]);
  const expected = createHmac("sha256", secret)
    .update(`${ts}.${rawBody}`)
    .digest("hex");

  const a = Buffer.from(sig, "hex");
  const b = Buffer.from(expected, "hex");
  if (a.length !== b.length || !timingSafeEqual(a, b)) return false;

  // Reject replays outside a five-minute window.
  return Math.abs(Date.now() / 1000 - Number(ts)) < 300;
}
/// Guarantees

The properties you can build against.

[ 01 ]

Idempotent creates

Every order creation takes an idempotency key. A retried request — after a timeout, a deploy, or a queue redelivery — returns the original order rather than charging twice.

[ 02 ]

Signed, replay-protected events

Webhooks carry a timestamped HMAC signature and a stable event id. Deliveries retry with backoff until your endpoint answers 2xx, so a deploy window doesn't lose a payment.

[ 03 ]

One reference, end to end

The reference you supply on creation appears on the checkout, the receipt, the webhook, the payout, and any later refund. There is no second identifier to map between systems.

[ 04 ]

Test mode is the same code path

Test keys run the identical flows against test sessions. What passes in test is what ships — the difference is which keys you send, not which endpoints you call.

/// Test data

How to exercise the paths that matter.

Each path below has a test card behind it, listed with your keys when the sandbox is issued — the numbers are per-sandbox rather than published here, so they stay correct as scheme test ranges change. Behaviour is deterministic either way: a test asserts on a decline as easily as on a success.

Approved outright
Authorizes and captures with no 3-D Secure challenge — the happy path.
3-D Secure challenge
Forces a challenge you can walk through, so you can test the return leg.
Soft decline
Declines for insufficient funds, then retries on an alternative route.
Hard decline
Declines permanently as an expired card, with no retry.
USDC test networks
Test-net USDC on Ethereum, Base, and Solana, priced at a fixed rate.
Transfer simulation
Mark a test vIBAN transfer as arrived to move the order to paid.

Get keys and start.

A sandbox merchant with API keys, webhook secrets, and every method switched on — plus the full reference, so your team can integrate before anything is signed.

Request sandbox access