System information

Technical overview of this deployment—provider requirements, server-to-server data relay, the embed generator, webhook signing, and JSON APIs.

On this page

Overview

CorePay is a SvelteKit site that sends payers to configured providers (exchanges and similar integrations), can register a signed webhook per payment link (stored until the provider confirms), and exposes small JSON APIs for listings and server-to-server relay.

Integrators typically build links or forms that hit GET /link, use GET /api/v1/providers for UI lists, and verify webhook bodies using /.well-known/jwks.json.

Stack and configuration

The app uses the MOTA ĐApp stack (SvelteKit). Locales and navigation come from site config (e.g. vite-plugin-config); payment providers are defined in src/data/providers.ts (ids, URLs, optional dataUrl, optional refCode, optional remapKeys, suspension, expiration).

The payment link handler lives under src/lib/server/link; optional wh + data on GET /link are persisted in Cloudflare D1 using the Workers binding named by env DB_BINDING_NAME. Apply SQL from _migrations/ on that database. POST /api/v1/data from the provider composes the signed webhook body and delivers it to the stored URL.

Provider requirements

Expectations for payment providers integrating with CorePay: provide a payment link that prefills transfer details in your portal, accept our payment ID, confirm settlements to us, and give us a referral code for users we send.

RequirementDescription
Payment linkCreate a payment link whose role is to prefill transfer information when the payer opens your portal.
Payment ID variableIntegrate the payment ID variable we send you—use it in your flow and keep it consistent end to end.
Webhook after successful transferAfter you know the payment state, POST JSON to our /api/v1/data with paymentId, amount, currency, status (sent or completed), addressTo, and addressFrom (and optional boolean swap if you changed the asset) so we can deliver the signed webhook to the merchant’s registered URL.
Referral codeCreate a referral code for us so users we send are attributed correctly.

Provider data relay — POST /api/v1/data

Called by the provider’s servers (not the payer’s browser). Body must be a JSON object with only the required webhook keys in the table above, optional swap (boolean), and no customData or wh—the target URL was set on the original GET /link.

If no row exists for paymentId, the response is still HTTP 200 with "ok", "wh". If a row exists, CorePay signs and POSTs to the registered wh; the response is "ok", "wh" after attempts complete. If signing is not configured, env DB_BINDING_NAME is unset, or D1 is unavailable, you may receive 503.

Payment embed generator

The /generator page builds a static HTML form (no JavaScript) whose action is this site’s /link URL. Fields follow the same rules as the payment link gateway (currency required). Use your production origin in the snippet when going live. /generator

Webhooks and X-Signature

When a payer opens GET /link with wh, CorePay stores the webhook URL and optional data in D1 under the generated payment-id, then redirects the browser to the provider. No POST to your server happens at redirect time.

The provider’s backend calls POST /api/v1/data with the required keys in the table below and may add optional swap (boolean)—unknown keys are rejected with 400. CorePay merges them with optional customData from the link, canonicalizes the full object (sorted keys, minified), signs it, and POSTs it to the registered wh with Content-Type: application/json.

JSON body POSTed to your webhook URL

JSON keyRequired / optionalDescription
paymentIdRequiredSame value as payment-id on the payment link: UUID v4 without hyphens, 32 lowercase hex characters. Use for reconciliation.
amountRequiredDecimal amount as a string (e.g. "100" or "3.14") or JSON number; normalized to a decimal string in the outgoing body. When swap is true, this is the amount in the delivered (post-swap) currency.
currencyRequiredAsset code; lowercased by CorePay. Letters, digits, “.”, “_”, and “-” only. When swap is true, this is the delivered asset after the provider’s swap.
swapOptionalOptional boolean from the provider’s POST /api/v1/data. true means the provider swapped to a different asset than the payer originally requested; amount and currency are then the post-swap values actually delivered. Omit or false when no swap occurred.
statusRequiredsent — user sent or submitted the transfer at the exchange (in-flight). completed — provider reports funds credited to the merchant account.
addressToRequiredReceiving-side identifier (e.g. merchant account or deposit id) as reported by the provider.
addressFromRequiredSending-side identifier (e.g. user wallet or exchange account id) as reported by the provider.
customDataOptionalPresent only if the payer’s GET /link included valid data. Canonical JSON object from the link (keys sorted, minified when stored); the full webhook payload is canonicalized again at send time.

Header X-Signature: detached compact JWS (JOSE, EdDSA / Ed25519, RFC 7797 b64:false) over the exact UTF-8 bytes of the JSON body—form protected..signature (two dots).

Delivery uses up to three attempts (immediate, then after 10s, then after 60s). Verify signatures using the keys in /.well-known/jwks.json.

Example JSON POST body your webhook receives (customData only if the payer’s link included data):

{"addressFrom":"user_wallet_or_id","addressTo":"merchant_recv_id","amount":"100","currency":"xcb","customData":{"orderId":"42"},"paymentId":"a1b2c3d4e5f6789012345678abcdef","status":"completed","swap":false}

REST API — /api/v1/…

JSON responses. OPTIONS is supported; responses include CORS headers (e.g. Access-Control-Allow-Origin: *) and X-APP-Version. Replace the version segment if your deployment uses another API version folder.

  • GET /api/v1/providers — JSON object with a providers array of entries (id, title, url, icon) for each active provider (not suspended, not past expiration).
  • POST /api/v1/data — provider backend reports a payment: strict JSON with required paymentId, amount, currency, status (sent or completed), addressTo, addressFrom, plus optional boolean swap. CorePay looks up the payment-id in D1; if missing, returns HTTP 200 with JSON body "ok", "wh". If found, builds canonical JSON (plus optional customData from the link), signs it, POSTs to the stored wh with X-Signature, retries up to three times, then returns HTTP 200 with "ok", "wh". Max body 64 KiB.
  • GET /api/v1/health — JSON with status, timestamp, environment, and version fields for uptime checks.
  • GET /api/v1/ping — simple JSON with status, message, and timestamp.

API responses may include the X-APP-Version header (from VITE_APP_VERSION at build time).