Merchant API

Payment status and confirmations

Webhook destinations must be public HTTP(S) endpoints. Internal, loopback and reserved IP addresses are blocked, including DNS answers. Destinations are checked on every delivery and retry; redirects are not followed. Local test exceptions require an operator-configured exact endpoint.

Payment webhooks use payment_status: pending (detected, waiting for confirmations), confirmed (incoming threshold reached), and completed (distribution sent to the network, not outgoing confirmation).

UTXO, EVM and TRON report observed confirmation progress through pending, then confirmed at the configured threshold once execution checks pass. confirmations is the observed count; required_confirmations is the merchant threshold. Counts may jump, so do not require an event for every intermediate number. A threshold of one can skip pending.

Solana sends confirmed, then completed, without pending depth updates. Both confirmation fields are 1 and represent the accepted network stage, not slot depth. Custom numeric depth is not enforced. Native SOL is discovered at finalized commitment; SPL tokens at confirmed commitment.

TRON credit and distribution have separate thresholds. With confirmations=5, credit occurs at 5; distribution waits for at least 23 confirmations for TRX or 20 for TRC20 and a successful solidified receipt. A higher configured threshold delays distribution too. Pending updates stop after confirmed. Low credit thresholds do not guarantee finality or automatic reversal on a reorganization.

These fields apply to checkout, deposit, custom and subscription payment webhooks. invoice_status describes invoice settlement separately; a confirmed payment may be only a partial payment. Forwarded amounts and fees are omitted until available.

Credit each incoming transaction once at confirmed or completed. Acknowledge every event with {"status":"ok"}; deduplicate UUIDs and ignore status/count regressions because retries can arrive out of order. Event types have a status suffix, for example checkout.payment.confirmed.

Webhooks

Webhooks (also called callbacks or IPNs) are real-time notifications sent to your application. Instead of constantly checking an API for updates, your server gets notified instantly when a payment is received, confirmed, or when other important events happen.

Verify signatures Respond fast (JSON status: ok) Idempotent handler

Overview

A webhook is an HTTP request sent to your server whenever an event occurs. You provide a webhook URL when creating a payment / deposit / subscription, and updates are delivered there.

  • Default delivery is GET. post=1 selects form POST; json=1 selects JSON POST and takes precedence, even when both flags are enabled.
  • Your endpoint must reply with HTTP 2xx and JSON {"status":"ok"}. Legacy text *ok* is also accepted.
  • Any other response is treated as a failure and may be retried.

Amount fields and units

Amounts use ordinary currency units, not cents or blockchain base units. JSON types below belong to the signed payload; GET/form encodes them as text. Preserve types for signature verification and use decimal arithmetic for amounts.

Context / fieldJSON typeMeaning and availability
Checkout valuestring or nullRequested invoice fiat in currency, every payment stage; null when no amount is set. Not included in deposit webhooks.
Checkout/deposit paid_amount / paid_amount_fiatstring / string or nullIncoming crypto in paid_coin / fiat in currency, before fees, every payment stage.
Checkout/deposit received_amount / received_amount_fiatstring / string or nullDistributed crypto / fiat equivalent after fees, only at completed.
Subscription subscription_option_valuenumberRequested plan fiat in payment_currency, rounded to two decimals; 0 if unavailable. Renewal notifications; no payment_value field.
Subscription payment_paid_amount / payment_paid_amount_fiatnumber / number or nullIncoming crypto in payment_paid_coin / fiat in payment_currency, before fees, every renewal payment stage.
Subscription payment_received_amount / payment_received_amount_fiatnumber / number or nullDistributed crypto / fiat equivalent after fees, only at completed.
Subscription list/fetch payment_list[].valuestring or nullRequested invoice fiat in coin, for invoices with a detected payment; null when no amount is set.
Subscription list/fetch payment_list[].received_fiatstring or nullAccepted incoming fiat in coin, before fees. Our commission is not deducted. Counts confirmed/completed payments once; "0" if none qualify, null if any qualifying valuation is unknown.
Custom/Core value_coin / value_forwarded_coinnumber or stringIncoming crypto before fees / distributed crypto after fees, in coin. Source network determines the type; distribution details are absent until available.
Custom/Core fee_coin / network_feenumber, string or nullService fee / separate network-cost deduction in payment coin or token units. Distribution details, absent or null when unknown; network deduction is not necessarily the final on-chain gas cost.

Fiat fallback for checkout/deposit/subscription webhooks: missing incoming fiat uses the requested invoice amount if available; missing net fiat uses incoming fiat (or its invoice fallback). Both can be null. Subscription fields carry the payment_ prefix. Fallback fiat does not prove the exact transferred amount or fee; reconcile settlement using crypto amounts. List/fetch received_fiat does not use these fallbacks.

Example with known valuations: a customer pays 10 USD, total fees are 0.20 USD. Subscription history reports received_fiat: "10"; the completed renewal webhook reports payment_received_amount_fiat: 9.8. Completed does not add another 10 USD to the history.

These stage rules describe new webhooks. Logs retain stored data and historical payloads; a zero or null distribution value before completed is not proof of settlement. Core conversion fields have their own JSON-encoded string format, even in JSON transport.

HOW WEBHOOKS WORK

"Your App"
"KingPay"
Customer
Request payment with webhook URL Return payment address Makes payment Send webhook notification HTTP 2xx + {"status":"ok"}
"Your App"
"KingPay"
Customer

On small screens you can scroll horizontally to view the full sequence.

1. Create a Webhook Endpoint

Create an endpoint (URL) in your application that can receive notifications. This endpoint must:

  • Be publicly accessible over HTTPS.
  • Accept the configured transport: GET by default, form POST with post=1, or JSON POST with json=1.
  • Respond with HTTP 2xx and a JSON object with exactly "ok" as its status value, for example {"status":"ok"}. Additional JSON fields are allowed. Legacy plain text *ok* is also accepted, case-insensitively and ignoring surrounding whitespace. Plain ok, empty bodies and {"status":"error"} do not acknowledge delivery. Any other response will be considered a failure.

2. Provide the Endpoint URL

When you make an API request to create a payment, deposit, or subscription, provide your endpoint URL:

  • notify_url (for Checkout)
  • callback (for Custom Payment Flow)

Example (Custom Payment Flow)

curl --get "https://api-service.kingpay.online/btc/create/" -H "apikey: YOUR_API_KEY" --data-urlencode "callback=https://yoursite.com/webhook"

URL encode your webhook URL if it contains its own query parameters. For example, https://yoursite.com/webhook?order_id=123 becomes https%3A//yoursite.com/webhook%3Forder_id%3D123.

const originalUrl = 'https://yoursite.com/webhook?order_id=123';
const encodedUrl = encodeURIComponent(originalUrl);
// encodedUrl: "https%3A%2F%2Fyoursite.com%2Fwebhook%3Forder_id%3D123"

3. Secure Your Endpoint

CRITICAL: You must verify webhook signatures

This is essential to ensure the notification was sent by the provider and has not been tampered with. Validate every webhook request before processing it.

Webhook Payloads

The data fields included in a webhook notification depend on the service and the event that occurred. For example, a webhook for a new deposit will have different information than a webhook for a completed checkout payment.

Refer to the specific documentation for payload details:

  • Custom Payment Flow Webhooks
  • Checkout Webhooks
  • Deposit Webhooks
  • Subscription Webhooks

Best Practices & Troubleshooting

To ensure your webhook integration is robust, follow these best practices:

  • Respond quickly (within 30 seconds): acknowledge webhooks with HTTP 2xx and {"status":"ok"}. For longer processing, handle it asynchronously after responding.
  • Handle retries with idempotency: your endpoint may receive the same webhook multiple times. Log processed webhook uuid and ignore duplicates.
  • Verify the webhook signature: use your project's IPN secret and the signature headers. Do not rely on IP addresses copied from another service.
  • Use ngrok for local testing: expose your local development server to test without deploying.

Retry Schedule

Notifications are saved before sending. The first attempt starts as soon as a worker is available. Failed deliveries are retried automatically for 48 hours from notification creation:

  • After failures: 1 minute, 5 minutes, 15 minutes, then 1 hour.
  • Subsequent delays: 3 hours. Each delay starts when the preceding attempt finishes.
  • HTTP errors, invalid acknowledgements and 30-second timeouts trigger retries. Redirects are not followed.
  • Retries preserve the payload and UUID. Each attempt has a fresh HMAC signature timestamp using the current project secret.
  • Queued notifications survive restarts. Duplicate or out-of-order delivery is possible: acknowledge an already processed UUID without applying the payment again.
  • After 48 hours, automatic delivery ends with failed. An in-flight attempt may finish.
  • The dashboard shows delivery state, attempt history and next retry time. Manual resend remains available to the project owner.
  • A manual resend does not extend the original 48-hour deadline. After the deadline it permits one additional attempt; within the window automatic retries can continue after a failure.
  • Historical logs are not replayed automatically. They can be resent manually from the dashboard.

Troubleshooting Common Issues

  • Webhooks not received: URL not publicly accessible, firewall/Cloudflare blocking requests, or customer sends a different coin/token than the generated address.
  • Duplicate processing: retries are not handled idempotently. Always check webhook uuid before processing.