Cara memverifikasi webhook Qvard

Panduan singkat tentang header X-Qvard-Signature: apa itu dan cara memverifikasinya dengan beberapa baris kode.

Apa itu tanda tangan

Ketika status pembayaran berubah, Qvard mengirimkan pemberitahuan (webhook) ke alamat di pengaturan toko Anda. Siapa pun dapat mengirim permintaan ke alamat itu, jadi Anda memerlukan cara untuk membedakan pemberitahuan nyata dari yang palsu.

Tanda tangan adalah bukti itu: kode pendek yang Qvard hitung dari pesan dan kunci rahasia Anda. Hanya Qvard dan Anda yang tahu kunci itu, jadi hanya Qvard yang dapat menghasilkan kode yang benar.

Apa yang Anda terima

Setiap webhook membawa satu header tambahan dan badan JSON kecil:

X-Qvard-Signature: t=1760950000,v1=3f2a…c91e {"id":"order_1024","status":"paid"}

t adalah waktu pesan dikirim (Unix, detik). v1 adalah kodenya. Selama 24 jam setelah Anda memutar kunci rahasia, header membawa dua nilai v1: satu untuk kunci baru dan satu untuk yang lama.

Field id dan status tetap seperti sebelumnya. Empat field nilai ditambahkan ke badan yang sama dan ikut ditandatangani:

  • amount dan currency: nominal dan mata uangnya, seperti yang Anda kirim dalam permintaan.
  • amountUsd: nominal dalam USD yang dipakai untuk pembukuan.
  • rate: jumlah satuan mata uang untuk 1 USD saat pembayaran dibuat.
  • Integrasi lama tetap berjalan: mereka membaca id dan status serta mengabaikan field baru. Tanda tangan diperiksa pada badan yang sama seperti sebelumnya.

Cara memeriksanya

  1. 1
    Ambil badan permintaan persis seperti yang tiba: teks mentah, sebelum parsing JSON apa pun.
  2. 2
    Baca t dan setiap v1 dari header X-Qvard-Signature.
  3. 3
    Hitung HMAC-SHA256 dari teks t + "." + badan dengan kunci rahasia Anda (qvard_sk_…) dan tulis sebagai heksadesimal huruf kecil.
  4. 4
    Jika hasil Anda cocok dengan v1 apa pun (bandingkan dalam waktu konstan), pesannya asli.
  5. 5
    Periksa bahwa t tidak lebih lama dari 5 menit, sehingga pesan lama tidak dapat diputar ulang.
  6. 6
    Hanya kemudian gunakan data dan konfirmasi jumlah yang dibayarkan untuk pesanan itu di database Anda sendiri.

Kode siap

const crypto = require('crypto');

function verifyQvardSignature(rawBody, header, secret, toleranceSec = 300) {
  const parts = header.split(',').map((p) => p.trim());
  const t = Number((parts.find((p) => p.startsWith('t=')) || '').slice(2));
  const sigs = parts.filter((p) => p.startsWith('v1=')).map((p) => p.slice(3));
  if (!t || sigs.length === 0) return false;
  if (Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest();
  return sigs.some((s) => {
    const got = Buffer.from(s, 'hex');
    return got.length === expected.length && crypto.timingSafeEqual(got, expected);
  });
}

// Use the RAW body: express.raw, not express.json
app.post('/qvard-webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const raw = req.body.toString('utf8');
  if (!verifyQvardSignature(raw, req.get('X-Qvard-Signature') || '', process.env.QVARD_SECRET_KEY)) {
    return res.sendStatus(400);
  }
  const { id, status } = JSON.parse(raw);
  // Confirm the paid amount for order `id` in your own database before fulfilling it.
  res.sendStatus(200);
});

Selama 24 jam setelah memutar kunci rahasia Anda, header membawa dua nilai v1: terima webhook jika salah satu cocok.

Kesalahan umum

  • Mengurai JSON dan menseriesnya lagi mengubah teks. Selalu gunakan badan mentah.
  • Menggunakan kunci publik (qvard_pk_…) alih-alih kunci rahasia.
  • Jam server yang tidak sinkron membuat pemeriksaan 5 menit gagal. Tetap sinkron.
  • Membandingkan dengan == alih-alih perbandingan waktu konstan.

Tempat menemukan kunci rahasia Anda

Di panel: toko Anda, Pengaturan, Kunci API. Simpan hanya di server Anda. Jika bocor, putar di sana: kunci lama terus berfungsi selama 24 jam untuk pembayaran (bukan untuk penarikan).

Buka panel

Referensi API

Kunci lama

Jika toko Anda masih memiliki kunci format lama (tanpa awalan qvard_sk_), webhook juga membawa header x-api-key dengan kunci itu. Setelah Anda memutarnya, header itu menghilang. Beralih ke pemeriksaan tanda tangan.

Ingin mencoba jendela pembayaran?

Buka demo