Skip to main content

BLIK Level 0

BLIK Level 0 is a direct integration where the customer enters a 6-digit BLIK code directly on your website, without being redirected to an external payment gateway. This provides a smoother user experience.

How does BLIK Level 0 work?

Unlike the standard integration (Simple Gateway), the customer is not redirected - the entire process takes place on your website.

Requirements

  • An active Payment Point with BLIK support enabled
  • A form on your website for entering the BLIK code
  • Access to the customer's IP address and User-Agent (required by regulations)

Endpoint

POST https://api-payments.dpay.pl/api/v1_0/payments/register
Content-Type: application/json

Request parameters

The request is analogous to the standard payment registration, with additional BLIK-specific fields:

FieldTypeRequiredDescription
transactionTypestringYes"transfers"
servicestringYesService name from the panel
valuestringYesAmount in PLN
url_successstringYesURL after successful payment
url_failstringYesURL after failed payment
url_ipnstringYesURL for IPN notifications
checksumstringYesSHA-256 checksum
blik_codestringYes6-digit BLIK code
user_ipstringYesCustomer's IP address
user_agentstringYesCustomer's User-Agent header

Generating the checksum

The checksum is generated in the same way as for a standard payment:

sha256({service}|{SecretHash}|{value}|{url_success}|{url_fail}|{url_ipn})

Request example

cURL

curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "transfers",
"service": "abc123",
"value": "29.99",
"url_success": "https://myshop.com/success",
"url_fail": "https://myshop.com/error",
"url_ipn": "https://myshop.com/api/ipn",
"checksum": "e3b0c44298fc1c149afb...",
"blik_code": "123456",
"user_ip": "192.168.1.100",
"user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}'

PHP

<?php
$service = getenv('DPAY_SERVICE');
$secretHash = getenv('DPAY_SECRET_HASH');

$value = '29.99';
$urlSuccess = 'https://myshop.com/success';
$urlFail = 'https://myshop.com/error';
$urlIpn = 'https://myshop.com/api/ipn';

$checksum = hash('sha256',
$service . '|' . $secretHash . '|' . $value . '|' .
$urlSuccess . '|' . $urlFail . '|' . $urlIpn
);

$payload = json_encode([
'transactionType' => 'transfers',
'service' => $service,
'value' => $value,
'url_success' => $urlSuccess,
'url_fail' => $urlFail,
'url_ipn' => $urlIpn,
'checksum' => $checksum,
'blik_code' => $_POST['blik_code'], // Code from the form
'user_ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
]);

$ch = curl_init('https://api-payments.dpay.pl/api/v1_0/payments/register');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);

JavaScript (Node.js / Express)

const crypto = require('crypto');
const axios = require('axios');

async function processBlikPayment(req, res) {
const service = process.env.DPAY_SERVICE;
const secretHash = process.env.DPAY_SECRET_HASH;

const value = '29.99';
const urlSuccess = 'https://myshop.com/success';
const urlFail = 'https://myshop.com/error';
const urlIpn = 'https://myshop.com/api/ipn';

const checksum = crypto
.createHash('sha256')
.update(`${service}|${secretHash}|${value}|${urlSuccess}|${urlFail}|${urlIpn}`)
.digest('hex');

const response = await axios.post(
'https://api-payments.dpay.pl/api/v1_0/payments/register',
{
transactionType: 'transfers',
service,
value,
url_success: urlSuccess,
url_fail: urlFail,
url_ipn: urlIpn,
checksum,
blik_code: req.body.blik_code,
user_ip: req.ip,
user_agent: req.headers['user-agent'],
}
);

return response.data;
}

API response

Success - payment awaiting confirmation

{
"error": false,
"msg": "Waiting for BLIK confirmation",
"status": "PENDING",
"transactionId": "abc-def-123-456"
}

After receiving this status, the customer must confirm the transaction in their banking app. You will receive the payment result via IPN.

Checking transaction status

In addition to waiting for the IPN notification, you can actively poll the dpay API for the current transaction status. This is useful, for example, when you want to update the user interface in real time.

Endpoint

POST https://panel.dpay.pl/api/v1/pbl/details
Content-Type: application/json

Parameters

FieldTypeDescription
servicestringService name from the panel
transaction_idstringTransaction ID received during registration
checksumstringSHA-256 checksum

Generating the checksum

sha256({service}|{transaction_id}|{SecretHash})

Example response

{
"transaction": {
"id": "abc-def-123-456",
"status": "paid",
"value": "29.99",
"created_at": "2026-03-15T12:00:00Z"
}
}

Possible statuses

StatusDescription
createdTransaction created, awaiting confirmation
processingTransaction is being processed
paidPayment completed successfully
canceledTransaction canceled
tip

We recommend polling every 2-3 seconds, for a maximum of 2 minutes. After that, the BLIK code expires.

Full endpoint documentation is available in the API Reference - Transaction Details.

Error - invalid BLIK code

{
"error": true,
"msg": "Transaction canceled",
"status": false,
"transactionId": "42191111-A7AE-392E-8C09-7965C1DC6B0B",
"additionalInfo": {
"error": "ER_WRONG_TICKET",
"error_description": null
}
}

Error handling

Error codeDescriptionAction
ER_WRONG_TICKETInvalid or expired BLIK codeAsk the customer for a new code
ER_USER_DECLINEDCustomer rejected the transaction in their appAsk to try again
ER_TIMEOUTConfirmation timed outAsk for a new code
ER_INSUFFICIENT_FUNDSInsufficient fundsInform the customer

BLIK form example (frontend)

<form id="blik-form">
<label for="blik-code">BLIK code</label>
<input
type="text"
id="blik-code"
name="blik_code"
maxlength="6"
pattern="[0-9]{6}"
inputmode="numeric"
placeholder="______"
required
/>
<button type="submit">Pay</button>
</form>

<div id="blik-status" style="display: none;">
<p>Confirm the payment in your banking app...</p>
</div>

<script>
document.getElementById('blik-form').addEventListener('submit', async (e) => {
e.preventDefault();

const blikCode = document.getElementById('blik-code').value;

// Show waiting message
document.getElementById('blik-form').style.display = 'none';
document.getElementById('blik-status').style.display = 'block';

const response = await fetch('/api/pay/blik', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ blik_code: blikCode }),
});

const result = await response.json();

if (result.error) {
alert('Error: ' + result.msg);
document.getElementById('blik-form').style.display = 'block';
document.getElementById('blik-status').style.display = 'none';
}
// Wait for IPN - payment status will change after confirmation
});
</script>

BLIK code validation

A BLIK code is always exactly 6 digits. Validate it on both the client and server side:

// Server-side validation
if (!preg_match('/^\d{6}$/', $blikCode)) {
http_response_code(400);
echo json_encode(['error' => 'BLIK code must consist of 6 digits']);
exit;
}
BLIK code validity

A BLIK code is valid for 2 minutes from the moment it is generated in the banking app. Make sure the form on your website informs the customer about the need to enter the code quickly.