Payment Orchestrator
Quickstart

Quickstart

This guide takes you from zero to a completed card payment using the hosted checkout form — the fastest, lowest-compliance path. Your server creates a payment intent, the buyer pays on Lunixi's form, and you confirm the result. Card data never touches your server.

All amounts are integers in minor units (kuruş): "amount": 10050 = ₺100.50.

Get your API keys

From the merchant panel, create:

  • an Ed25519 key pair — used from your server to mint a short-lived Bearer token (at_live_…) at POST /api/v1/auth/token, and
  • an Ed25519 signing key — its key id (kid) and private key are used to sign money-movement requests.

Creating a payment intent is a money-movement call, so it requires Step-Up auth (Bearer + signature). The signing procedure is covered step by step in Authentication.

Create a payment intent

From your backend, create an intent for ₺100.50. The minimal body is just the amount, currency and your order reference:

curl -X POST "https://api-gateway.lunixi.com/api/v1/payments/intents" \
  -H "Authorization: Bearer at_live_5f3e2b1a..." \
  -H "Content-Type: application/json" \
  -H "X-Key-Id: key_01JZ8H2M4Q" \
  -H "X-Date: 2026-07-06T12:00:00.000Z" \
  -H "X-Nonce: 9f8d2c4e-1a3b-4c5d-8e7f-0a1b2c3d4e5f" \
  -H "Digest: SHA-256=k6I5cUt...=" \
  -H "X-Signature: MEUCIQDx...==" \
  -H "Idempotency-Key: order-ORD-9988-attempt-1" \
  -d '{
    "amount": 10050,
    "currency": "TRY",
    "orderId": "ORD-9988",
    "callbackUrl": "https://shop.example.com/payment/result"
  }'

The response contains the form token you will hand to the browser:

{
  "status": "success",
  "code": "CHECKOUT_INITIALIZED",
  "data": {
    "paymentId": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "checkoutFormContent": "<script>…</script>"
  }
}

The token is single-purpose, origin-bound, and expires — 15 minutes by default — so create a fresh intent per checkout attempt. checkoutFormContent is a ready-to-inject bootstrap script alternative to mounting the SDK yourself.

The intent create accepts many more fields — buyer and basket details, installments, force3D, per-request settings overrides (3D mode, Buyer Protection, payment-method narrowing). See the full reference on the Checkout Form page.

Open the hosted checkout form

In the buyer's browser, load the Lunixi checkout SDK and mount the form with the intent token. The SDK renders the card form, runs the fraud bootstrap, resolves installment options and drives the 2D/3D charge — all inside Lunixi's secure fields.

<div id="lunixi-checkout-form"></div>
 
<script src="https://cdn.lunixi.com/lunixi-checkout.min.js"></script>
<script>
  const checkout = Lunixi.checkout.create({
    type: 'payment',
    target: 'lunixi-checkout-form',
    options: {
      token: 'eyJhbGciOiJIUzI1NiIs...', // token from POST /payments/intents
    },
  });
 
  checkout.mount('#lunixi-checkout-form');
</script>

If 3D Secure is required, the buyer is redirected to the bank challenge and then back; the final result is also posted to your callbackUrl as an HMAC-signed form POST.

Confirm the result

Never trust only the browser redirect — confirm server-side, two ways:

1. Webhooks (recommended). Lunixi pushes the outcome to your endpoint:

{
  "type": "payment.captured",
  "data": {
    "intentId": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "orderId": "ORD-9988",
    "currency": "TRY",
    "status": "CAPTURED",
    "capturedAmountMinor": "10050"
  }
}

(abridged — the full delivery envelope and signature headers are on the Webhooks page)

A failed charge arrives as payment.failed, an abandoned checkout as payment.expired. Set up delivery and signature verification on the Webhooks page.

2. Query the payment. Fetch the current state at any time:

curl "https://api-gateway.lunixi.com/api/v1/payments/9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30" \
  -H "Authorization: Bearer at_live_5f3e2b1a..."
{
  "status": "success",
  "code": "PAYMENT_FOUND",
  "data": {
    "id": "9c6f2b1a-4e8d-4a3f-b5e6-2d7c1a9b8e30",
    "orderId": "ORD-9988",
    "status": "CAPTURED",
    "amount": 10050,
    "currency": "TRY",
    "installment": 1,
    "createdAt": 1783772591000
  }
}

Ship the order when the status is CAPTURED (or AUTHORIZED, if you capture later).

Next steps