USDC payment API: a developer's integration guide

August 11, 2026

USDC payment API: a developer's integration guide

Search for a USDC payment API and the results are dominated by stablecoin infrastructure companies rather than payment processors. That distinction is easy to miss while comparing feature lists, and it determines how much work lands on your team. An infrastructure API gives you the money movement and leaves compliance, settlement and reconciliation as your problem. A licensed processor takes those on.

This guide covers integrating USDC through the EukaPay API, where USDC is supported on Ethereum, and where a completed payment converts at a locked rate and settles to your bank account. It also covers the licensed-versus-infrastructure choice directly, because on this particular search result page it is the real decision.

In this guide, you'll learn:

  • What EukaPay supports for USDC, and the single-network implication for your integration

  • How to authenticate, create an invoice, and verify webhooks correctly

  • What a licensed processor covers that a stablecoin infrastructure API leaves to you

  • How to reconcile USDC payments against your accounting system

USDC on Ethereum

EukaPay supports USDC on Ethereum as an ERC-20 token, listed as Cryptocurrency ID 4 in the documentation's currency tables.

That identifier is worth reading carefully, because EukaPay maintains two separate numbering systems and they overlap. Cryptocurrency ID 4 is USD Coin on Ethereum. Fiat

currencyId

4 is Pound Sterling. The

currencyId

field you send when creating an invoice refers to the fiat table, not the crypto one, which is covered in detail further down.

Being explicit about that matters more than quoting a large number of supported assets. There is no USDC on Solana and no USDC on Tron at EukaPay. If your checkout offers a Solana USDC option, or your test plan assumes one, it will fail for a reason that has nothing to do with your code.

The single-network position has one useful consequence for your integration. Multi-chain USDC support sounds like a feature, and in practice it introduces the most expensive failure mode in stablecoin payments: a customer sending USDC on a network your address does not exist on. With one supported network, your checkout has one instruction to communicate and one address format to validate.

If your customer base holds stablecoins on Tron, note that EukaPay supports USDT there. Offering USDT on Tron alongside USDC on Ethereum covers both audiences, and both are the same integration.

Authenticating and creating your first invoice

Generate an API key in the merchant dashboard under Settings > Integrations > API keys. The secret appears once at creation.

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

Production is

https://api.eukapay.com

and staging is

https://api-stg.eukapay.com

. Paths carry no version prefix, so the endpoint is

/invoices

rather than

/v1/invoices

. Keys are environment-tagged with

sk_test_

and

sk_live_

prefixes.

Creating an invoice is a single call, and it should always carry an idempotency key:

curl -X POST "https://api.eukapay.com/invoices" \
  -H "x-api-key: sk_live_YOUR_KEY" \
  -H "x-idempotent-key: c41d7f8a-2b96-4e05-8a71-3fd6b09e5c42" \
  -H "Content-Type: application/json" \
  -d '{
    "price": 620.00,
    "currencyId": 2,
    "number": "INV-260811-09",
    "message": "Platform fee, Q3 2026",
    "dueDate": "2026-08-25",
    "redirectUri": "https://example.com/orderCompleted",
    "metadata": { "externalId": "ord_5290", "productCode": "PLATFORM-Q3" }
  }'

Note what is not in that body: the cryptocurrency.

currencyId

2 is the US Dollar, and the invoice is denominated in fiat. Your customer selects USDC at the payment screen from the currencies enabled on your account. Accepting USDC is therefore an account configuration plus an ordinary fiat-priced invoice, not a per-request currency choice.

The response returns the invoice

code

and a

paymentUrl

:

{
  "code": "inv_h2qjo7e1qpaled08q0cecn1cal",
  "price": 620,
  "number": "INV-260811-09",
  "status": "Unpaid",
  "paymentUrl": "https://app.eukapay.com/payments/inv_h2qjo7e1qpaled08q0cecn1cal",
  "totalPaidAmount": 0,
  "currency": { "name": "US Dollar", "unit": "USD", "sign": "US$" }
}

The

x-idempotent-key

value is yours to generate, a V4 UUID is recommended, and keys expire after 24 hours. Derive it from your own order identifier and persist it before sending, so a timeout and retry cannot produce a second invoice for one order. A fresh key generated inside your retry logic protects nothing.

Two limits on

price

: the minimum is 1 and the maximum is 100,000. Once the total reaches 1,000,

customerCode

becomes required along with

address1

,

city

,

state

and

country

on that customer, so create the customer with

POST /customers

first for larger invoices.

Retrieve an invoice with

GET /invoices/{code}

and list them with

GET /invoices

.

Webhooks

Configure an endpoint under Settings > Integrations > Webhooks and drive your order state from events rather than polling for a status.

The eight events

EukaPay publishes eight webhook events. Four cover pay-ins and refunds, and four cover outbound payouts:

Event

Category

paymentCompleted

Pay-in

paymentUnderpaid

Pay-in

paymentOverpaid

Pay-in

refundCompleted

Refund

cryptoPayoutCreated

Payout

cryptoPayoutSent

Payout

cryptoPayoutError

Payout

cryptoPayoutRevoked

Payout

Building your state machine on these named events is more reliable than depending on a status string, because each event corresponds to a decision your code has to make.

Verifying the signature - SHA-512

Every delivery carries an

x-eukapay-signature

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

SHA-512

. Most gateways use SHA-256, so treat the algorithm as a deliberate detail rather than a typo if a reviewer questions it.

const crypto = require('crypto');

function verifyEukaPaySignature(rawBody, signatureHeader, secret) {
  const digest = crypto.createHmac('sha512', secret).update(rawBody).digest('hex');
  if (digest.length !== signatureHeader.length) {
    return false;
  }
  return crypto.timingSafeEqual(Buffer.from(digest), Buffer.from(signatureHeader));
}

Use

timingSafeEqual

rather than

===

, since a plain comparison exits on the first differing character. Test the negative case as well: alter one byte of a payload, leave the signature alone, and confirm your handler rejects it.

One point to settle against your own account. EukaPay's documentation gives conflicting instructions here, with the written steps calling for the unaltered payload and the code sample re-serialising it with

JSON.stringify

. Those inputs differ whenever key order or whitespace differs. Verify a real delivery against the raw body first, and try the re-serialised form only if that fails.

Duplicate deliveries

EukaPay retries a failed 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, then do the slow work asynchronously.

Payment payloads carry

webhookId

,

event

,

paymentCode

,

paidAmount

,

totalPaidAmount

,

status

,

paidDatetime

,

invoiceCode

,

invoiceNumber

,

invoicedAmount

and

invoiceCurrency

.

invoiceCode

is your join key back to your own order.

Licensed processor or stablecoin infrastructure

This is the decision the USDC payment API search results obscure, and it is worth making deliberately.

Stablecoin infrastructure APIs are good at moving tokens. What they generally do not do is take on the regulated parts of accepting a payment. The work does not disappear, it moves to your team.

Capability

Stablecoin infrastructure API

EukaPay

Moves USDC on-chain

Yes

Yes

Registered money services business

Usually not

FINTRAC in Canada, FinCEN in the United States

Converts to fiat at a locked rate

Rarely

Yes, to remove all crypto volatility

Settles to a business bank account

Often not

USD, EUR, GBP, CAD, plus Interac for Canadian merchants

Screens outbound transfers

Your responsibility

Surfaced as the

Reviewing

payout status

Refunds as an API operation

Varies

POST /refunds

,

GET /refunds

Who holds the compliance obligation

You

EukaPay

That last row is the one to weigh carefully. An infrastructure provider that is not a registered money services business is not taking on your obligations, whatever the integration looks like. If you accept customer payments at any scale, someone has to hold that responsibility.

The

Reviewing

payout status is a small, concrete illustration. EukaPay's crypto payout statuses are

Pending

,

Reviewing

,

Processing

,

Sent

,

Error

and

Revoked

.

Reviewing

exists because a registered processor screens outbound transfers, and it appears in the API rather than as a silent delay. An infrastructure API has no equivalent status because it is not performing that function.

None of this makes infrastructure APIs the wrong choice. If you are building a wallet or a protocol-level product, raw money movement is exactly what you want. For a business accepting payment for goods and services, the calculation usually runs the other way.

Settlement and reconciliation

A completed USDC payment converts at a locked exchange rate to remove all crypto volatility, and settles to your bank account in USD, EUR, GBP, CAD.

For month-end, cursor pagination handles period pulls.

GET /invoices

accepts

limit

with either

starting_after

or

ending_before

, never both, and returns a

{data, has_more}

envelope. Page until

has_more

is false.

One trap when writing a shared pagination helper: default page sizes differ by resource. Invoices default to 100, while customers, refunds and crypto payouts default to 10. Set

limit

explicitly.

Store two identifiers against your own order record at write time. The

invoiceCode

ties your order to the invoice and appears in every webhook payload. The

x-request-id

, returned on every API response, is what support will ask for if a specific call needs investigating. Capturing both at write time turns reconciliation into a join rather than a manual match against a bank statement.

Testing on Sepolia

USDC on Ethereum tests on

Sepolia

.

Register for staging at

stg.eukapay.com

and call

https://api-stg.eukapay.com

with a

sk_test_

key. Staging verification emails are not sent automatically, so request approval from

support@eukapay.com

. Test USDC comes from EukaPay support rather than a public faucet, so ask for both in one message.

Rehearse these before going live:

  • The happy path end to end, with

    paymentCompleted

    driving your order state

  • Underpayment, which commonly occurs when an exchange deducts its withdrawal fee from the amount a customer sends

  • Overpayment, confirming your fulfilment logic does not reject a payment for failing an equality check

  • A replayed webhook, confirming

    webhookId

    deduplication works

  • A tampered payload, confirming signature verification returns false

  • A 429, confirming your client backs off. EukaPay documents rate limiting and recommends exponential backoff without publishing a specific requests-per-second figure, so build against the response rather than a threshold

Every EukaPay merchant goes through an onboarding and business review process, and there is no instant registration. The sandbox is available during that review, so the integration is usually finished before the commercial process is.

One platform underneath

USDC is one currency on a platform that handles all of them identically: 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 run on one account. Adding USDT on Ethereum or Tron alongside USDC is a currency configuration, not a second integration, and the endpoints and webhook events do not change.

Get started with EukaPay

Create an account at

app.eukapay.com/signup

, complete verification, provide your legal business information, then generate an API key.

The full reference, including the currency table, request schemas and every error type, is public at

docs.eukapay.com

. An official documentation MCP server is available at

docs.eukapay.com/mcp

for coding agents. For the merchant-facing view rather than the developer detail, see

how to accept USDC payments

.

Frequently asked questions

Which networks does EukaPay support for USDC?

Ethereum only, as an ERC-20 token. There is no USDC on Solana or Tron at EukaPay. USDT is available on both Ethereum and Tron.

Is EukaPay a licensed payment processor?

Yes. EukaPay is registered with FINTRAC in Canada and with FinCEN in the United States, which is the main difference from a stablecoin infrastructure API that moves tokens without holding a compliance obligation.

What currency will my USDC payments settle in?

Your chosen fiat, settled to your bank account in USD, EUR, GBP, CAD, with Interac withdrawals available for Canadian merchants.

What signature algorithm does EukaPay use for webhooks?

HMAC SHA-512, in the

x-eukapay-signature

header, computed over the raw request body.

How do I avoid creating duplicate invoices on retry?

Send an

x-idempotent-key

header derived from your own order identifier, persisted before the request is sent. Keys expire after 24 hours.

Can I refund a USDC payment through the API?

Yes.

POST /refunds

creates a refund and

GET /refunds

lists them, with statuses of

Created

,

Pending

,

Success

,

Fail

and

Unknown

.

Which testnet should I use for USDC?

Sepolia. Test USDC is provided by EukaPay support rather than a public faucet.

How do I reconcile USDC payments at month-end?

Page

GET /invoices

with

limit

and a cursor until

has_more

is false, and join on the

invoiceCode

you stored against your own order record.