Crypt2Merchant
API Documentation

Crypt2Merchant API

Accept card payments from any website, app, or platform. Two API calls, one callback, and that's it.

Base URL: https://api.crypt2merchant.net  ·  Hosted checkout: https://dashboard.crypt2merchant.net/checkout
Regional availability. Card payments are charged in USD and are not available to customers located in New York or the EU. Those customers see a clear message on the payment page (error code onramp_unavailable_region). Offer them another payment method.

Authentication

All API requests require your merchant API key in the X-Api-Key header. Your key starts with c2m_ and is provided when your account is created.

Headers
X-Api-Key: c2m_your_api_key_here
Content-Type: application/json
Keep your API key secret. Never expose it in client-side code or public repositories. It should only be used on your server.

Create Payment Session

Start a payment by creating a session. This generates a unique deposit address and returns the details needed to redirect the customer.

POST /api/session

Request Body

ParameterTypeDescription
amount_fiatnumberrequiredPayment amount in USD. Minimum $20. Stripe delivers this exact amount in USDC. Its fee is charged to the customer on top.
currencystringoptionalMust be USD (the default). Other codes return 400 unsupported_currency. Convert first with /api/currency/convert.
order_idstringoptionalYour internal order reference (max 128 chars). Returned in the callback. Repeating a pending order's ID returns the same session.
callback_urlstringoptionalAbsolute https URL we GET when payment is confirmed. Must return HTTP 200.
Request
curl -X POST https://api.crypt2merchant.net/api/session \
  -H "X-Api-Key: c2m_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_fiat": 50.00,
    "currency": "USD",
    "order_id": "order-123",
    "callback_url": "https://yoursite.com/payment-callback"
  }'
Response
{
  "session_id": "7a004150-8509-47de-b6fe-5b62267210d7",
  "deposit_address": "0xC690783E29bF60DE7AEfE74Dd9b455cA8980Ce14",
  "splitter_address": "0xA03f79A3B0834558a21a8dD7F3F6aeFD9931Cc53",
  "amount_fiat": "50.00",
  "currency": "USD"
}

Response Fields

FieldDescription
session_idUnique payment session identifier. Use this in the checkout redirect.
deposit_addressThe unique on-chain address this payment settles to. Informational. The hosted checkout locks it in automatically.
splitter_addressYour merchant's smart contract address.
amount_fiatThe confirmed payment amount.
Session lifetime: a session created here expires 2 hours after creation if unpaid (sessions created from the dashboard's Payment Links tab last 30 days). Create a new one if the customer comes back later.

Redirect to Payment Page

After creating a session, redirect the customer to our hosted checkout page on dashboard.crypt2merchant.net. They'll complete payment via card, so you don't need to build any payment UI. Returning customers who have paid this way before skip identity verification.

GET https://dashboard.crypt2merchant.net/checkout

Query Parameters

ParameterTypeDescription
session_idstringrequiredFrom the create session response.
amountnumberoptionalShown while the page loads. It is then replaced by the server-side amount, which is what gets charged.
currencystringoptionalDisplay only. Default: USD
Example URL
https://dashboard.crypt2merchant.net/checkout
  ?session_id=7a004150-8509-47de-b6fe-5b62267210d7
  &amount=50.00

Payment Callback

When payment is confirmed, we send a GET request to the callback_url you provided when creating the session. Your server must return HTTP 200 to acknowledge receipt.

GET your callback_url

Query Parameters (sent by us)

ParameterDescription
session_idThe payment session that was completed.
txid_outThe blockchain transaction hash. Verifiable on Basescan.
value_coinUSDC received at the deposit address. Normally equals amount_fiat; the customer may edit the amount inside the payment form, so accept anything within your tolerance and treat a shortfall as underpaid.
order_idYour order reference, as provided when creating the session.
Retry policy: If your server doesn't return HTTP 200, we retry up to 5 times with exponential backoff (1s, 2s, 4s, 8s between attempts). You can also resend the callback from the merchant dashboard. Make your handler idempotent, since the same session_id may be delivered more than once.

Convert Currency

Sessions are always charged in USD. If your store prices in another currency, convert the total first and pass the USD figure as amount_fiat. Rates come from the European Central Bank feed and are cached for an hour.

GET /api/currency/convert?amount=45.00&from=EUR&to=USD
Response
{ "amount": 45, "from": "EUR", "to": "USD", "converted": 52.31 }

Full Integration Examples

Complete working examples in popular languages. Each shows the full flow: create session, redirect, handle callback.

const express = require('express');
const app = express();

const API_KEY = 'c2m_your_key';
const API_URL = 'https://api.crypt2merchant.net';
const CHECKOUT_URL = 'https://dashboard.crypt2merchant.net/checkout';

// 1. Customer clicks "Pay" — create session and redirect
app.post('/checkout', async (req, res) => {
  const response = await fetch(`${API_URL}/api/session`, {
    method: 'POST',
    headers: { 'X-Api-Key': API_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      amount_fiat: req.body.total,
      currency: 'USD',
      order_id: req.body.orderId,
      callback_url: 'https://yoursite.com/payment-done',
    }),
  });
  const session = await response.json();

  // 2. Redirect customer to hosted payment page
  res.redirect(
    `${CHECKOUT_URL}?session_id=${session.session_id}`
    + `&amount=${session.amount_fiat}`
  );
});

// 3. We call this URL when payment is confirmed
app.get('/payment-done', (req, res) => {
  const { session_id, txid_out, value_coin, order_id } = req.query;
  // Mark order as paid in your database
  console.log(`Order ${order_id} paid! Amount: ${value_coin}`);
  res.sendStatus(200); // Must return 200
});

Error Handling

Every error response has an error field. Machine-readable failures use a stable snake_case code there plus a human-readable message; simple validation failures put the message directly in error.

StatusCodeMeaning
400Missing or invalid parameters (message in error).
400unsupported_currencyOnly USD is accepted. Convert first.
400amount_below_minimumBelow the $20 card minimum (min_amount is included).
400onramp_unavailable_regionCard payments are not available where the customer is (New York, EU). Shown on the payment page.
401Invalid or missing API key.
404not_foundUnknown session.
410session_closedThe session has expired or was already paid. Create a new one.
429Rate limited (100 requests/minute per IP). Back off and retry.
500Server error. Try again or contact support.
502onramp_unavailablePayment provider error. Try again.
503deposit_pool_emptyDeposit addresses are being provisioned for your account. Retry after the Retry-After header (60s).
Error Response
{ "error": "amount_below_minimum", "message": "Minimum card payment is $20.00 USD.", "min_amount": 20 }