When a customer authorizes a transaction you will receive a callback at the callback_url specified when you created a payment, the callback will be a POST request with a JSON request body in the following format:

{
  "type": "payment|recurring|mandate",
  "event": "payment.capture_failed",
  "message": "some error message",
  "created_at": "2024-01-31 12:23:23",
  "resource_id": 23992,
  "resource": {
   "id": 1000000,
    "order_id": "123",
    "state": "authorized",
    "amount": 500,
    "currency": "DKK",
    "variables": {
        "key": "value"
    },
    "created_at": "2020-01-01 12:00:00"
  }
}

Currently we deliver callbacks for these events:
"authorized"
"captured"
"capture_failed"

If a payment is successfully authorized the type will read "payment" and the event will be "payment.authorized".

📘

Expected response code

We expect a response with a HTTP status code between 200 and 299 to consider the callback successful.

If we fail to deliver the callback, we will retry hourly for the next 12 hours.

Callback validation

Callback requests are accompanied by a Pensopay-Signature header that can be used to validate that the callback originates from us.

To verify the callback you can generate a checksum using HMAC with SHA256 and your private key (found on app.pensopay.com) to verify that it's equal to the one we sent

<?php

$request_body = file_get_contents("php://input");
$private_key  = 'your-private-key';
$checksum     = hash_hmac("sha256", $request_body, $private_key);

if (hash_equals($_SERVER["HTTP_PENSOPAY_SIGNATURE"], $checksum)) {
    // Callback is valid
} else {
    // Invalid callback
}
import crypto from 'crypto';

// if using the body-parser middleware - https://github.com/expressjs/body-parser 
const privateKey = 'private-key'; 
const checksum = req.headers['http_pensopay_signature']; 
const body = req.body; 
const bodyAsString = JSON.stringify(body); 
const calculated = crypto 
  .createHmac('sha256', privateKey) 
  .update(bodyAsString) 
  .digest('hex');

if (calculated === checksum) { 
  // Authentic callback
} else { 
  // Invalid callback
}