2 min read
· The MariPay teamA payment is not finished when you create it. It finishes when the customer approves on their phone, which can be seconds or minutes later. A webhook is how MariPay tells your server that moment has arrived, so it is worth getting right.
Always verify the signature
Anyone can send a request to your endpoint, so never trust one on its own. Every MariPay webhook carries a MariPay-Signature header containing a timestamp and an HMAC of the exact raw body, made with your endpoint's secret. Recompute it, compare in constant time, and reject anything older than five minutes.
import crypto from "node:crypto"
function verify(rawBody, header, secret) {
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")))
if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false
const expected = crypto.createHmac("sha256", secret).update(t + "." + rawBody).digest("hex")
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1))
}Verify against the raw request body. If your framework parses the JSON first and you re-serialise it, the bytes change and the signature will never match.
Acknowledge quickly, work later
Return a 2xx as soon as you have verified and stored the event. Do slow work, like sending emails or updating other systems, in a background job. If your endpoint is slow or returns an error, we treat the delivery as failed and retry with exponential backoff for about a day.
Expect duplicates
Retries mean the same event can arrive more than once. Each event has a unique id in the MariPay-Event-Id header. Record the ids you have processed, or better, make the action itself safe to repeat: marking an invoice paid twice should do nothing the second time.
Do not rely on order
Events can arrive out of order, for example a refund event before you finished processing the completed one. Read the payment's current status from the event, and when in doubt fetch the payment from the API and trust that.
Events worth handling
- payment.completed: fulfil the order.
- payment.failed and payment.expired: let the customer try again.
- payment.refunded and refund.completed: update your books.
If you miss one
Check the delivery log in the dashboard to see what was sent and how your server responded. A reconciliation sweep also re-delivers events that never got through, and you can always look a payment up by your own reference.
Ready to try it?
Create a free account, grab your sandbox keys and take a test payment in minutes. No verification needed to start testing.