Başlarken
Hızlı Başlangıç

Hızlı Başlangıç

Sıfırdan ilk başarılı API çağrısına. En kritik adım imzalama; onu çalışır kodla veriyoruz.

Ed25519 anahtar çifti üretin

Uzun ömürlü secret'ınız budur ve sunucunuzda kalır.

openssl genpkey -algorithm ed25519 -out lunixi-private.pem
openssl pkey -in lunixi-private.pem -pubout -out lunixi-public.pem
⚠️

lunixi-private.pem dosyasını asla depoya, tarayıcıya veya mobil uygulamaya koymayın. Sızarsa panelden anahtarı iptal edip yenisini üretmeniz gerekir.

Public anahtarı kaydedin

Panel (opens in a new tab)'e girin, public anahtarı (lunixi-public.pem) kaydedin. Karşılığında bir kid (key id) alırsınız — imzalı her isteğe bunu koyacaksınız.

Erişim token'ı basın

Aşağıdaki kod kanonik dizeyi kurar, imzalar ve token'ı alır. Ek bağımlılık yok — Node.js'in kendi crypto modülü yeterli.

import crypto from 'crypto';
import fs from 'fs';
 
const BASE = 'https://api-gateway.lunixi.com';
const KID = process.env.LUNIXI_KEY_ID!;
const privateKey = crypto.createPrivateKey(
  fs.readFileSync(process.env.LUNIXI_PRIVATE_KEY_PATH!),
);
 
export async function mintToken() {
  const method = 'POST';
  const url = '/api/v1/auth/token';
  const date = new Date().toISOString();
  const nonce = crypto.randomUUID();
 
  // 🔴 Bu istekte GÖVDE YOK → `Digest:` satırı kanonik dizeye HİÇ EKLENMEZ.
  // Boş değerle eklerseniz imza tutmaz. Gövdeli isteklerde 5. satır olarak girer.
  const canonical = [
    method.toUpperCase(),
    url,
    `X-Date:${date}`,
    `X-Nonce:${nonce}`,
  ].join('\n');
 
  const signature = crypto
    .sign(null, Buffer.from(canonical), privateKey)
    .toString('base64');
 
  const res = await fetch(BASE + url, {
    method,
    headers: {
      'X-Key-Id': KID,
      'X-Date': date,
      'X-Nonce': nonce,
      'X-Signature': signature,
    },
  });
 
  if (!res.ok) {
    const err = await res.json();
    throw new Error(`${err.code}: ${err.message}`);
  }
 
  return res.json(); // { access_token, expires_in, expires_at, session_id, ... }
}
🚫

Ed25519'da hash algoritması belirtilmez. crypto.sign(null, ...) — ilk argüman null olmalı. 'sha256' yazarsanız imza üretilir ama doğrulanmaz; 401 alır ve nedenini bulamazsınız.

Token'ı önbelleğe alın

expires_in tipik olarak 300 saniye. Her çağrıda yeni token basmayın:

let cached: { token: string; expiresAt: number } | null = null;
 
export async function getToken(): Promise<string> {
  const now = Math.floor(Date.now() / 1000);
  if (cached && cached.expiresAt - 30 > now) return cached.token;
 
  const t = await mintToken();
  cached = { token: t.access_token, expiresAt: t.expires_at };
  return cached.token;
}

30 saniyelik pay, çağrı yoldayken token'ın sona ermesini engeller.

İlk çağrınızı yapın

Fraud kararı iyi bir başlangıçtır — para hareket ettirmez, hemen sonuç döner:

const token = await getToken();
 
const res = await fetch(
  'https://api-gateway.lunixi.com/api/v1/fraud/decisions/evaluate',
  {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      subjectType: 'card_payment',
      payment: { amount: 250000, currency: 'TRY', is3D: false },
      customer: { id: 'cust_84213' },
    }),
  },
);
 
const decision = await res.json();
 
if (decision.decision === 'block') {
  throw new Error(`Engellendi: ${decision.reasonCodes.join(', ')}`);
}
if (decision.decision === 'force_3d') {
  // İşlemi 3DS'e yükseltin
}

Bilmeniz gereken üç şey

⚠️

1 — Tutarlar minör birim. 250000 = ₺2.500,00. Ondalık gönderirseniz tutar 100 kat yanlış olur.

⚠️

2 — decision küçük harf. Değerler allow · force_3d · review · block. Büyük harfle karşılaştırma yapmayın.

🚫

3 — Hiç kural yayınlamadıysanız allow almazsınız. Yapılandırılmamış merchant fraud.decision.unconfigured koduyla varsayılan kararı (tipik review) alır. "Kural yazmadım" ≠ "risk yok". → Akışlar ve Çözümleme

İlk çağrı 401 dönerse

BelirtiSebep
INVALID_DATEX-Date sunucu saatinden ±5 dakikadan uzak — sunucunuzun saatini kontrol edin
İmza tutmuyorGövdesiz istekte Digest: satırını eklemiş olabilirsiniz — eklenmemeli. Satır sırası ve başlık yazımı da birebir olmalı
Gövdeli istekte tutmuyorDigest başlığı SHA-256= önekli olmalı: SHA-256=<base64>
Yine tutmuyorcrypto.sign'ın ilk argümanı null mı?
403IP izinli listede değil, ya da merchant askıya alınmış

Sonraki adımlar