Webhook-ovi
Dokaz da je svaka payment notifikacija od Perpera prije fulfill-a. Ovo je merchant trust surface (Stripe: Verify signatures).
Zašto ovo postoji
Svako može otvoriti lažni success URL u browseru. Vaš apiUrl mora kriptografski provjeriti da je Perper potpisao opaque order data vašim project secretom. Tek tada označite narudžbu plaćenom.
Callback ugovor (apiUrl POST)
Polja JSON tijela:
- status — success | fail
- data — opaque string koji ste poslali na /pay (npr. order id)
- hash — HS256 JWT tog data stringa, potpisan project secretom
- transactionId — na uspjehu kad je ledger transfer završen
- customId — echo kad ste ga dali na /pay ili Contabo QR
Verifikacija hash-a (obavezno)
Backend potpisuje sa jwt.sign(dataString, projectSecret) (HS256). Istu provjeru radite na serveru. Ako verify padne, odgovorite 400 i ne radite fulfill.
Pinuj algorithms: ['HS256']. Odbij alg=none / RS*. Pogrešan secret → invalid signature.
const jwt = require("jsonwebtoken");
/** Same as Perper backend generateHash — HS256 of opaque data string */
function verifyPayHash({ data, hash, secret }) {
const payload = jwt.verify(hash, secret, { algorithms: ["HS256"] });
if (payload !== data) throw new Error("mismatch");
return data;
}
// apiUrl POST body: { status, data, hash, transactionId?, customId? }
app.post("/perper/webhook", (req, res) => {
const { status, data, hash, transactionId } = req.body;
try {
verifyPayHash({
data,
hash,
secret: process.env.PERPER_PROJECT_SECRET,
});
} catch {
return res.sendStatus(400); // never fulfill
}
// de-dupe on transactionId || data, then fulfill once
res.sendStatus(200);
});Idempotency
Contabo trenutno šalje svaki success webhook jednom (single fetch). I dalje de-dupe po transactionId kad postoji, inače po opaque data / customId — duplikat ne smije double-fulfill.
Dva hash ugovora
Pay callback / redirect hash = JWT samo opaque data stringa. checkStatus hash = JWT od { projectId, customId }. Ne miješajte payload-e.
Status vokabular
Redirect query koristi status=success|error. Webhook body koristi status=success|fail. error i fail tretirajte kao isti failure class.
Failure režimi
Ovo je reject / investigate — nikad fulfill:
- Unsigned — data i hash nedostaju (legacy ili pogrešno konfigurisan success path).
- Invalid / mismatch — tampered data, loš JWT ili algorithms confusion.
- Pogrešan secret — JsonWebTokenError invalid signature (rotirajte ili zalijepite trenutni project secret).
Dokaz u portalu (live)
Giants imaju Dashboard “Send test webhook”. Perper daje isti loop bez CLI-ja:
- Detalj projekta → zalijepi secret → Pošalji success/fail callback na apiUrl.
- Ili /demo sa apiUrl = https://sdk.perper.net/api/demo/callback i pregled na /demo/callbacks. /demo · /demo/callbacks
- Na /demo/callbacks zalijepi project secret → Verify → badge Verified / Invalid / Unsigned. Secret se ne čuva.
Šta znači “Verified”
Verified = jwt.verify(hash, secret, { algorithms: ['HS256'] }) vratio isti data string. To je system proof da je Perper autor callback-a. Invalid / Unsigned = ne radite fulfill.
Test mode (sk_test_)
Samo portal BFF sandbox: sk_test_ živi u store-u SDK website-a, ne u Contabo ledgeru. Koristite Pošalji test callback (Test mode) da dokažete endpoint — stanje se ne pomjera. Prave pay webhook-ove potpisuje Contabo project secret. Ishodi dostave su na stranici projekta (Nedavne webhook dostave).
Dalje: ugradite ovo u produkcijski checkout, pa skenirajte Plaćanja za hostovani /pay URL. Plaćanja · Bezbednost (bankarski nivo)