Migrating from CoinGate to EukaPay: an endpoint map

August 14, 2026

Migrating from CoinGate to EukaPay: an endpoint map

Plenty of articles compare crypto payment providers on fees and supported coins. Very few tell a developer (or coding agent) which line of code changes. If you have a working CoinGate integration and you are evaluating EukaPay, the question is not which platform is better in the abstract. It is how many hours the move costs and where the surprises are.

This guide is the answer at endpoint level. EukaPay is a licensed processor registered with FINTRAC in Canada and FinCEN in the United States, settling to your bank account in USD, EUR, GBP, CAD, and the migration from CoinGate is a mapping exercise rather than a rewrite. Two things do require real work, and both are in the callback handler.

Note:

docs.eukapay.com

is the source of truth for every EukaPay endpoint, field and event named in this guide. Check it for the latest before you write code.

In this guide, you'll learn:

  • How CoinGate's order model maps onto EukaPay's invoice model, field by field

  • Why the callback handler is the bulk of the migration, and exactly what changes

  • How CoinGate's nine order statuses map onto EukaPay's webhook events

  • How to run both integrations in parallel and cut over by traffic percentage

The endpoint map

Start with the shape of the two APIs side by side.

Concern

CoinGate

EukaPay

Base URL

https://api.coingate.com

- see the path note below

https://api.eukapay.com

Sandbox

https://api-sandbox.coingate.com/v2
https://api-stg.eukapay.com

Version in path

Yes,

/v2

No prefix

Auth header

Authorization: Token YOUR_API_TOKEN
x-api-key: sk_live_YOUR_KEY

Create payment

POST /orders
POST /invoices

Fetch payment

GET /orders/{id}
GET /invoices/{code}

List payments

GET /orders
GET /invoices

Pagination

Page-based:

page

,

per_page

Cursor:

starting_after

,

ending_before

,

limit

Refunds

POST /orders/{order_id}/refunds
POST /refunds

,

GET /refunds

Payouts

Withdrawals are read-only

POST /crypto_payouts

and five more endpoints

Callback verification

Token match, not cryptographic

HMAC SHA-512 signature

Idempotency

Not offered

x-idempotent-key

header

Note: this table is a snapshot taken while writing. Confirm current hosts, paths and parameters at

docs.eukapay.com

.

A path detail worth checking before you write anything

CoinGate's own documentation is inconsistent about the base path. The environments page lists

https://api.coingate.com/v2

, while the official OpenAPI specification declares

https://api.coingate.com/api/v2

in its

servers

block, and reference pages render paths as

/api/v2/orders

.

If you are auditing an existing integration before migrating, check which form your code actually uses rather than trusting either document. EukaPay removes the question by carrying no version prefix at all: the endpoint is

/invoices

, even though the reference is labelled v1.0.

Authentication - a different header, same simplicity

A correction worth making, because it is commonly written down wrong: CoinGate uses

Authorization: Token

, not

Authorization: Bearer

. If you have ever debugged a 401 against CoinGate, that is often why.

# CoinGate
curl "https://api.coingate.com/v2/orders" \
  -H "Authorization: Token YOUR_API_TOKEN"

# EukaPay
curl "https://api.eukapay.com/invoices" \
  -H "x-api-key: sk_live_YOUR_KEY"

One useful difference. CoinGate notes that credentials generated on the live site will not work in test mode, which means environment mistakes surface as authentication failures. EukaPay encodes the environment in the key itself,

sk_test_

against

https://api-stg.eukapay.com

and

sk_live_

against

https://api.eukapay.com

, so a mismatched key is visible by reading it.

Note: key formats, header names and environment hosts are documented at

docs.eukapay.com

, which is where any change lands first.

Creating a payment

CoinGate's

POST /orders

requires

price_amount

,

price_currency

,

title

between 3 and 150 characters, and

description

between 3 and 500 characters. Optional fields include

order_id

,

receive_currency

or the literal

DO_NOT_CONVERT

,

callback_url

,

success_url

,

cancel_url

,

token

and a

shopper

object.

EukaPay's equivalent is

POST /invoices

. Two migration notes matter more than the field names.

Your `order_id` becomes your idempotency key.

CoinGate's

order_id

is your own reference carried on the order. EukaPay accepts the same kind of reference and adds something CoinGate does not offer: an

x-idempotent-key

header. Derive it from the identifier you were already passing as

order_id

, a V4 UUID is recommended, keys expire after 24 hours, and a network timeout followed by a retry can no longer create two payments for one order.

`DO_NOT_CONVERT` has no direct equivalent.

If your CoinGate integration uses

DO_NOT_CONVERT

to hold received crypto rather than converting it, there is nothing to configure on the EukaPay side. EukaPay is non-custodial and never holds crypto on your behalf, so every payment is converted at a locked exchange rate and settled to your bank account.

Pagination - the one place you rewrite rather than remap

This is the change most likely to be missed in a quick migration, because both endpoints return lists and the code looks like it should work.

CoinGate is page-based. You request

page=1

, then

page=2

, with

per_page

capped at 100, plus

sort

values of

created_at_asc

or

created_at_desc

and date filters like

created_at[from]

.

EukaPay is cursor-based. You pass

limit

with either

starting_after

or

ending_before

, never both, and read

has_more

from the

{data, has_more}

envelope to decide whether to continue.

A page counter cannot be translated into a cursor. Rewrite the loop rather than adapting it. Note also that EukaPay's default

limit

varies by resource, 100 for invoices but 10 for customers, refunds and crypto payouts, so set it explicitly.

Note: default and maximum page sizes vary by resource and are listed at

docs.eukapay.com

. Read the current values there rather than hard-coding the ones above.

What changes in your callback handler

Here is the real work, and there are two distinct changes.

From token matching to cryptographic signatures

CoinGate posts a callback to your

callback_url

whenever an order status, refund status or billing status changes. The payload includes

id

,

order_id

,

status

,

price_amount

,

price_currency

,

receive_currency

,

receive_amount

,

pay_amount

,

pay_currency

,

underpaid_amount

,

overpaid_amount

,

is_refundable

,

created_at

,

token

,

fees

and

paid_at

.

Verification works by matching the

token

field against the callback token returned when the order was created. CoinGate's guidance is to check the token, allowlist their IP addresses, and re-fetch the order to confirm its status and amount independently.

That is a coherent design, and re-fetching to confirm is good practice under any scheme. It is a different security model from a signed payload, though, and the difference matters when you are the one maintaining the handler. A shared secret compared field-by-field proves the sender knew a token. An HMAC over the request body proves the payload itself was not altered in transit.

EukaPay sends an

x-eukapay-signature

header containing an HMAC of the payload, keyed to that webhook's secret, using

SHA-512

. Most gateways use SHA-256, so if a code review suggests correcting the algorithm, the suggestion is wrong.

Two implementation rules:

  1. Capture the raw bytes before parsing.

    Any JSON library that reorders keys or normalises whitespace produces a different digest. Be aware that EukaPay's own documentation is inconsistent on this point: the written steps call for the unaltered payload, while the published code sample re-serialises an already-parsed body with

    JSON.stringify

    . Verify one real delivery against the raw body and try the re-serialised form only if that fails.

  2. Test the negative case.

    Alter one byte of a captured payload, leave the signature untouched, and confirm your handler rejects it. A verification function that never returns false has not been tested.

  3. Compare in constant time.

    Use

    crypto.timingSafeEqual

    rather than

    ===

    , guarding for equal lengths first, so a comparison does not exit early on the first differing character.

Keeping the re-fetch habit from CoinGate is still worthwhile. Verify the signature, then read the invoice back with

GET /invoices/{code}

before you fulfil anything expensive.

Note: the webhook payload format, the signature header and the verification steps are all documented at

docs.eukapay.com

. Where a sample here and the reference disagree, the reference wins.

From a status string to named events

CoinGate drives your logic from a

status

field with nine documented lowercase values. EukaPay drives it from named webhook events. This table is the core of the migration:

CoinGate status

EukaPay equivalent

What your handler does

new

No event, invoice created by your own API call

Nothing, you already know

pending

No event

Nothing, wait

confirming

No event

Nothing, wait

paid
paymentCompleted

Mark paid, fulfil

invalid

No direct equivalent

Handle as unpaid, reconcile

expired

No direct equivalent

Handle as unpaid, reconcile

canceled

No direct equivalent

Handle as unpaid, reconcile

refunded
refundCompleted

Close the refund

partially_refunded
refundCompleted

with amount

Close the partial refund

Two things fall out of the mapping.

Underpayment and overpayment become explicit.

CoinGate carries

underpaid_amount

and

overpaid_amount

as fields on a callback, so your handler has to inspect them. EukaPay raises

paymentUnderpaid

and

paymentOverpaid

as separate events carrying

paidAmount

,

totalPaidAmount

and

invoicedAmount

. If your CoinGate handler quietly ignores those two fields, which is common, writing the two branches is new work that removes a class of stuck order.

Timing assumptions need re-checking.

CoinGate expires a

new

order after two hours if no currency is selected, and times out a

pending

order after 20 minutes. If any of your logic hard-codes those windows, confirm EukaPay's behaviour against the current documentation rather than carrying the numbers across. Separately, note that EukaPay's 20-minute figure is a webhook retry interval, not an order timeout, and the two are unrelated.

Note: the current event list, payload fields and timing behaviour are published at

docs.eukapay.com

. If you are building today, map against that page rather than this table alone.

Retries and duplicate delivery

EukaPay retries a failed webhook delivery every 20 minutes for up to two hours. Record the

webhookId

from each payload and return early on a repeat. Verify, deduplicate, return a 2xx quickly, and do slow work asynchronously.

Use

invoiceCode

as the join key back to your own order, in the same place your CoinGate handler used

order_id

.

Refunds and payouts

CoinGate's refund endpoint is

POST /orders/{order_id}/refunds

and takes a substantial body:

amount

,

address

,

currency_id

,

platform_id

,

reason

,

email

and

ledger_account_id

, with optional

address_memo

,

callback_url

and

skip_user_address_confirmation

.

EukaPay separates refunds from the order path entirely, with

POST /refunds

and

GET /refunds

, and publishes refund statuses of

Created

,

Pending

,

Success

,

Fail

and

Unknown

.

Payouts are a genuine capability difference rather than a mapping. CoinGate's withdrawals API is read-only, offering

GET /withdrawals

and

GET /withdrawals/{id}

. EukaPay exposes six payout endpoints including

POST /crypto_payouts

to create a payout,

GET /crypto_payouts/balance

and

POST /crypto_payouts/estimate

. If your business also sends money out, that moves from a dashboard task to an API operation.

One EukaPay payout status deserves a note because it has no CoinGate counterpart. Alongside

Pending

,

Processing

,

Sent

,

Error

and

Revoked

, there is

Reviewing

. A registered processor screens outbound transfers, and

Reviewing

surfaces that screening in the API rather than hiding it in a silent delay. Build for it so your finance team sees progress rather than an apparent failure.

Note: refund and payout statuses change as the product develops. The current set is published at

docs.eukapay.com

.

Rate limits and error handling

CoinGate publishes concrete numbers: 200 requests per minute, and 500 orders per hour per business including orders created in the dashboard, with a

RateLimitExceeded

reason on the error.

EukaPay documents rate limiting and recommends exponential backoff without publishing a specific requests-per-second figure. The practical consequence is that you build against the 429 response itself rather than tuning to a threshold, which is the more robust approach in either case. EukaPay returns three error types,

api_error

,

idempotency_error

and

invalid_request_error

, and an

x-request-id

on every response that is worth logging for support conversations.

On client libraries, CoinGate maintains an official Ruby gem and a PHP package, and lists .NET, Node.js and Python as community-maintained. EukaPay does not publish official SDKs, and the documentation recommends calling the API with

fetch

or

axios

. There is also an official documentation MCP server at

docs.eukapay.com/mcp

, which is useful when a coding agent is doing the migration.

Note: rate limit guidance, error types and tooling recommendations are documented at

docs.eukapay.com

and are updated as they change.

Running both in parallel

  1. Build against staging.

    Register at

    stg.eukapay.com

    , generate a

    sk_test_

    key, and run the full flow on testnet. Test USDT and USDC come from EukaPay support rather than a public faucet, so request them early.

  2. Add a provider column

    to your order records. Every refund path, reconciliation report and support query needs to know which processor created a payment.

  3. Route new payments by percentage.

    Send a small share of new checkouts to EukaPay and raise it as confidence builds.

  4. Let in-flight payments finish where they started.

    An order created on CoinGate settles and reconciles on CoinGate.

  5. Run both handlers until the last old order resolves.

    Token matching and SHA-512 verification coexisting for a period is correct, not untidy.

  6. Reconcile both providers for a full period

    before you decommission anything.

One platform underneath

Whichever EukaPay product you collect through, the infrastructure is shared: instant crypto-to-fiat conversion at a locked exchange rate to remove all crypto volatility, protection against chargebacks, support for a wide range of cryptocurrencies, and settlement in USD, EUR, GBP, CAD to your bank account. EukaPay supports most major cryptocurrencies like BTC, ETH, LTC, SOL, USDC, USDT.

Invoices, payment links, hosted checkouts, subscriptions that send invoices on a recurring schedule, refunds and crypto payouts all sit on one account and one reconciliation surface. For a migrating team, the value is that pay-ins and payouts stop being two integrations with two sets of records.

Get started with EukaPay

The full reference, including request schemas, the webhook payload format and every error type, is public at

docs.eukapay.com

. Register for staging, request approval and test stablecoins from support in a single message, and build the migration while your business review runs. For the commercial comparison rather than the endpoint detail, see

CoinGate alternatives for accepting USDT and stablecoins

.

Frequently asked questions

How long does a CoinGate to EukaPay migration take?

The API mapping is small: a header, a host, and two endpoint names. Budget your real time for the callback handler, where signature verification and status-to-event mapping both change, and for rewriting page-based pagination as cursor-based.

Does EukaPay sign its webhooks?

Yes. Every delivery carries an

x-eukapay-signature

header with an HMAC of the payload keyed to that webhook's secret, using SHA-512. Compute your comparison over the raw request body.

Does EukaPay offer idempotency keys?

Yes. Send

x-idempotent-key

on writes, ideally a V4 UUID derived from the reference you previously passed as CoinGate's

order_id

. Keys expire after 24 hours.

Can I create crypto payouts through the EukaPay API?

Yes.

POST /crypto_payouts

creates a payout, with

GET /crypto_payouts/balance

and

POST /crypto_payouts/estimate

alongside it. There is no bulk endpoint, so batch runs are either a loop with per-recipient idempotency keys or a CSV upload in the dashboard.

What does the Reviewing payout status mean?

The payout is under compliance review before release. EukaPay is registered with FINTRAC and FinCEN, and a registered processor screens outbound transfers.

Does EukaPay publish a rate limit?

No specific requests-per-second figure is published. EukaPay documents rate limiting and recommends exponential backoff, so build against the 429 response.

Which stablecoins and networks does EukaPay support?

USDT on Ethereum and Tron, and USDC on Ethereum, alongside BTC, ETH, BCH, LTC, SOL and BTC over the Lightning Network.

Does EukaPay publish official client libraries?

No. The documentation recommends calling the API directly with

fetch

or

axios

, and an official documentation MCP server is available at

docs.eukapay.com/mcp

.

Note: these answers reflect the API at the time of writing.

docs.eukapay.com

is the source of truth for EukaPay's API and the place to check for the latest updates.