vellTRDocs
SDK'larNode.js

Kvell Node.js SDK

v1.4.2Gereksinimler: Node.js 18+

Yükle

Shell
npm install @kvell/kvell-node

Başlatma

TypeScript
import Kvell from '@kvell/kvell-node';

const kvell = new Kvell({
  apiKey: process.env.KVELL_API_KEY!,
  secretKey: process.env.KVELL_SECRET_KEY!,
  environment: 'sandbox', // switch to 'production' when you go live
});

export default kvell;

Hızlı başlangıç

TypeScript
import Kvell from '@kvell/kvell-node';

const kvell = new Kvell({
  apiKey: process.env.KVELL_API_KEY!,
  secretKey: process.env.KVELL_SECRET_KEY!,
  environment: 'sandbox',
});

async function createCheckoutSession() {
  // Amounts are always integers in kuruş (1/100 TRY) — 15000 = 150.00 TRY
  const session = await kvell.paymentSessions.create({
    transactionId: 'ORDER-2026-001',
    amount: 15000,
    currency: 'TRY',
    description: 'Order #1234',
    returnUrl: 'https://merchant.example.com/return',
  });

  console.log('Redirect the buyer to: ' + session.checkoutUrl);
  return session.checkoutUrl;
}

createCheckoutSession();

Hata yönetimi

TypeScript
import Kvell, { KvellError } from '@kvell/kvell-node';

const kvell = new Kvell({
  apiKey: process.env.KVELL_API_KEY!,
  secretKey: process.env.KVELL_SECRET_KEY!,
  environment: 'sandbox',
});

async function chargeCard() {
  try {
    const payment = await kvell.payments.charge({
      transactionId: 'ORDER-2026-001',
      amount: 15000, // kuruş
      currency: 'TRY',
      cardToken: 'tok_9f8e7d6c5b4a',
    });
    console.log('Captured payment ' + payment.paymentId);
    return payment;
  } catch (err) {
    if (err instanceof KvellError) {
      switch (err.code) {
        case 'KVELL-PAY-4001':
          console.warn('Card declined (requestId=' + err.requestId + ')');
          break;
        case 'KVELL-GW-1002':
          console.error('Signature invalid, check your secret key (requestId=' + err.requestId + ')');
          break;
        default:
          console.error('Kvell error ' + err.code + ' (requestId=' + err.requestId + ')');
      }
    }
    throw err;
  }
}

Webhook doğrulama

TypeScript
import crypto from 'crypto';
import express from 'express';

const app = express();
const seenEventIds = new Set<string>(); // swap for Redis/DB in production

app.post('/webhooks/kvell', express.raw({ type: 'application/json' }), (req, res) => {
  const signatureHeader = req.header('X-Kvell-Signature') || '';
  const eventId = req.header('X-Kvell-Event-Id') || '';

  const expected = 'sha256=' + crypto
    .createHmac('sha256', process.env.KVELL_WEBHOOK_SECRET!)
    .update(req.body) // raw Buffer, never the parsed JSON
    .digest('hex');

  const valid = signatureHeader.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected));

  if (!valid) return res.status(401).send('invalid signature');
  if (seenEventIds.has(eventId)) return res.status(200).send('duplicate');
  seenEventIds.add(eventId);

  const event = JSON.parse(req.body.toString('utf8'));
  switch (event.eventType) {
    case 'payment.captured':
      // Fulfil the order for event.payload.paymentId asynchronously
      break;
  }

  res.status(200).send('ok');
});