Documentation Index
Fetch the complete documentation index at: https://docs.crypto-cash.world/llms.txt
Use this file to discover all available pages before exploring further.
Fiat Module
Note: all numeric values, amounts, rates, and identifiers in the examples are illustrative and provided for demonstration purposes only. Real values depend on market conditions, account configuration, and the parameters of a specific transaction.
Overview
Fiat Module is a subsystem of the CryptoCash platform that allows a merchant to accept fiat payments from end users and receive cryptocurrency to their wallet.
Integration is performed through signed API requests. Payments are processed via a payment provider (Mercuryo). The end user completes the payment in the CryptoCash widget, where they select a payment method. After the payment is processed, the merchant receives a webhook with the final status.
Supported features:
- Payment by card, Apple Pay, Google Pay, SEPA, and other payment methods
- Signed requests (
ED25519 or LEGACY/SHA256) for merchant authentication
- Webhooks to the merchant on every transaction status change
- Automatic calculation of the merchant’s earnings based on
partner_fee
High-level flow
1. Merchant sends a signed API request → receives a payment URL
2. User opens the URL → sees the CryptoCash widget
3. User selects a payment method
4. Payment provider processes the payment
5. Merchant receives a webhook on every status change
6. After successful payment, the merchant’s earnings are calculated automatically
Two payment flows in the widget:
| Payment method | Flow |
|---|
| Apple Pay (native) | The user pays directly in the widget — without being redirected to an external page |
| Card / other methods | The user is redirected to the payment provider’s page to complete the payment |
For a detailed description of the native Apple Pay flow, see the Apple Native Pay section.
Merchant setup and Fiat API Key
After the administrator has prepared the account, the merchant generates a Fiat API Key in the dashboard. When creating the key, the merchant sets parameters that apply to all payments associated with this key:
| Parameter | Description |
|---|
keyType | Signature algorithm: ED25519 (recommended) or LEGACY (SHA256) |
keyName | Custom key name |
partnerFee | Share of the provider fee that goes to the merchant; configured by the administrator on the provider account |
paymentMethods | Payment methods available in the widget, for example ["card", "apple_pay"] |
webhookUrl | URL for receiving payment status webhooks |
logoUrl | Merchant logo displayed in the widget |
ipRestrictionEnabled | Whether to restrict API access by IP address |
allowedIpAddresses | List of allowed IP addresses or CIDR ranges |
expiresAt | Specific key expiration date |
neverExpires | If true, the key does not expire (expiresAt = null); by default, the key validity period is 1 year |
The response returns a publicKey / privateKey pair. The private key is stored on the platform in encrypted form and is shown to the merchant only once — during creation. Save it immediately.
If two-factor authentication is enabled on the merchant account, 2FA confirmation is required to generate the key.
Fiat API Key fields
| Field | Type | Description | |
|---|
publicKey | string | Key identifier, passed in every request | |
privateKey | string | Secret for signing requests; stored encrypted on the platform | |
keyType | ED25519 | LEGACY | Signature algorithm |
isActive | boolean | Whether the key is active | |
expiresAt | datetime | null | Expiration time (null — no expiration) |
paymentMethods | string[] | Allowed payment methods | |
webhookUrl | string | null | URL for webhook delivery |
logoUrl | string | null | Logo URL |
ipRestrictionEnabled | boolean | Whether IP restriction is enabled | |
allowedIpAddresses | string[] | Allowed IP addresses | |
ED25519 vs LEGACY
| ED25519 | LEGACY |
|---|
| Algorithm | Asymmetric cryptography (Ed25519) | SHA256-based signature |
| Key relation | publicKey is mathematically derived from privateKey | publicKey is an identifier; privateKey is a shared secret used for signing |
| Verification | Signature is verified using publicKey without knowing privateKey | The platform uses the stored privateKey to verify the signature |
| Recommended | Yes | Only for compatibility with legacy integrations |
What is checked on each request
When receiving a signed request, the platform checks:
- The key is active
- The key has not expired
- The IP of the API caller (merchant server) is included in the allowed list if IP restriction is enabled
- The signature is valid for the selected algorithm
Important: IP restriction applies to the address of the server making the API request, not to the end user’s IP in the payment payload ip field.
Signed requests
All merchant API requests must be signed. The signature algorithm is defined by the keyType value selected when creating the key.
Request structure
{
"data": "<base64-encoded JSON payload>",
"signature": "<base64-encoded signature>"
}
publicKey is passed either in the x-public-key header or inside the data object. If both are present, the header takes priority.
ED25519 (recommended)
1. payload → JSON.stringify(payload)
2. json → Buffer.from(json, 'utf8').toString('base64') → data
3. sign → Ed25519.sign(Buffer.from(data), privateKeyBytes) → signatureBytes
4. signature → signatureBytes.toString('base64')
LEGACY (SHA256)
1. payload → JSON.stringify(payload)
2. json → Buffer.from(json, 'utf8').toString('base64') → data
3. hash → SHA256(privateKey + data) → hashHex
4. signature → Buffer.from(hashHex, 'utf8').toString('base64')
Full example
Payload for form/retrieve:
{
"publicKey": "a1b2c3d4...",
"fiatCurrency": "SGD",
"fiatAmount": "100",
"currency": "USDT",
"network": "TRC20",
"address": "TXxxx...",
"ip": "203.0.113.5"
}
Final body of the signed request:
{
"data": "eyJwdWJsaWNLZXkiOiJhMWIyYzNkNC4uLiIsImZpYXRDdXJyZW5jeSI6IlNHRCIsImZpYXRBbW91bnQiOiIxMDAiLCJjdXJyZW5jeSI6IlVTRFQiLCJuZXR3b3JrIjoiVFJDMjAiLCJhZGRyZXNzIjoiVFh4eHguLi4iLCJpcCI6IjIwMy4wLjExMy41In0=",
"signature": "base64-encoded-signature"
}
Endpoint
POST /merchant/api/v1/pay-fiat/form/retrieve
Signed request. The signature and IP are checked before the request is processed.
Request
The data field of the signed request contains the following JSON:
| Field | Type | Required | Description |
|---|
ticker | string | ✓* | Crypto asset and network ticker, for example "USDTTRC20" |
currency | string | ✓* | Cryptocurrency code, for example "USDT" |
network | string | ✓* | Blockchain network, for example "TRC20" |
fiatCurrency | string | ✓ | Fiat currency code, for example "SGD" |
fiatAmount | string | ✓ | Fiat amount as a string, for example "100" |
address | string | ✓ | Wallet address for cryptocurrency crediting |
ip | string | ✓ | End user’s IP address |
email | string | — | Customer email |
redirectUrl | string | — | URL to return the user to after payment |
lang | string | — | Widget language, default "en" |
externalId | string | — | External order ID on the merchant side; must be unique within the key |
* To select a crypto asset, pass either ticker or the currency + network pair.
Response
{
"code": 200,
"data": {
"item": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "order-123",
"url": "https://buy.cryptocash.world/merchant/payment/550e8400-e29b-41d4-a716-446655440000"
}
}
}
| Field | Description |
|---|
id | Internal transaction UUID |
externalId | External ID from the request or null |
url | CryptoCash widget URL — redirect the user to this URL |
What happens after the request
- Signature and IP are checked.
externalId uniqueness is checked within the merchant key.
- The fiat amount is checked against the minimum allowed amount for the selected currency.
- The
network value is normalized case-insensitively.
- A transaction is created with the
New status.
- The widget URL is generated.
- A
payment::created webhook is sent to the merchant.
id and url are returned.
CryptoCash widget and provider page
| CryptoCash widget | Payment provider page |
|---|
| URL | https://buy.cryptocash.world/merchant/payment/{id} | Generated inside the widget |
| Branding | Merchant logo | Provider styling |
| Purpose | Payment method selection, rate preview, native Apple Pay | Direct payment processing |
The merchant always receives the CryptoCash widget URL. The provider page URL is generated inside the widget after the payment method is selected and is not returned to the merchant.
Merchant Public API
Retrieve payment
POST /merchant/api/v1/pay-fiat/payments/retrieve
Signed request. The data field contains one of the following:
{ "internalId": "550e8400-..." }
or
{ "externalId": "order-123" }
Response:
{
"code": 200,
"data": {
"item": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "order-123",
"status": "Paid",
"fiatCurrency": "SGD",
"fiatAmount": "100",
"cryptoCurrency": "USDT",
"network": "TRC20",
"cryptoAmount": "73.21",
"address": "TXxxx...",
"hash": "blockchain-hash",
"widgetUrl": "https://buy.cryptocash.world/merchant/payment/550e8400-...",
"createdAt": "2026-04-16T10:00:00.000Z",
"updatedAt": "2026-04-16T10:07:00.000Z"
}
}
}
Payment list
POST /merchant/api/v1/pay-fiat/payments/list
Signed request. The data field:
| Field | Type | Default | Description |
|---|
page | number | 1 | Page number |
pageSize | number | 100 | Number of items per page |
dateAfter | datetime | — | Filter: created after this date |
dateBefore | datetime | — | Filter: created before this date |
Response:
{
"code": 200,
"data": {
"items": [ /* array of payments in the same structure as retrieve */ ],
"pagination": {
"page": 1,
"pageSize": 100,
"total": 42
}
}
}
Currency lists
POST /merchant/api/v1/fiat-currencies/list
POST /merchant/api/v1/pay-fiat/crypto-currencies/list
Both endpoints use signed requests. Minimum payload in data:
crypto-currencies/list returns the cryptocurrencies available to the merchant together with networks and tickers. Values from tickers can be passed to form/retrieve instead of the currency + network pair.
Payment response fields
| Field | Type | Nullable | Description |
|---|
id | string | | Transaction UUID |
externalId | string | ✓ | Merchant external ID |
status | string | | Current status |
fiatCurrency | string | | Fiat currency code |
fiatAmount | string | | Fiat amount |
cryptoCurrency | string | | Cryptocurrency code |
network | string | | Blockchain network |
cryptoAmount | string | ✓ | Received crypto amount |
address | string | | Wallet address |
hash | string | ✓ | Blockchain transaction hash |
widgetUrl | string | ✓ | Widget URL |
createdAt | datetime | | Creation time |
updatedAt | datetime | | Last update time |
Transaction statuses
| Status | Description |
|---|
New | Transaction created, awaiting user action |
Waiting | Payment accepted and being processed |
Paid | Cryptocurrency sent to the wallet address |
Fail | Payment failed |
Canceled | Payment canceled |
Statuses are set by the platform based on payment provider events. The merchant cannot change the status via API.
Error handling
All API errors are returned in one of two formats:
Format A — coded error:
{ "code": 2020, "message": "..." }
Format B — error list used for decoding errors:
Error codes
| Code | Description |
|---|
1000 | Bad request |
1102 | Invalid request: unsupported network, amount below minimum, duplicate externalId |
1110 | Request validation error |
2010 | data decoding error |
2011 | signature decoding error |
2020 | Invalid signature |
2021 | API key blocked |
2030 | IP address forbidden |
3001 | Order not found |
3002 | Transaction not found |
Apple Pay
The CryptoCash widget supports the native Apple Pay flow: the iOS payment sheet opens directly in the widget without redirecting to an external page.
For a detailed description of native mode availability conditions, fallback behavior, and error handling, see the Apple Native Pay section.
Diagrams
General payment flow