BLIK Level 0
BLIK Level 0 is a direct integration in which the customer enters a six-digit BLIK code on your website without being redirected to an external payment gateway. This provides a smoother user experience.
How BLIK Level 0 works
Unlike the standard Simple Gateway integration, the customer is not redirected. The entire process takes place on your website.
POST/api/v1_0/payments/registerComplete registration contract: parameters, checksum, and error codes.Full contract in the API Reference
Requirements
- An active Payment Point with BLIK enabled
- A form on your website for entering the BLIK code
- Access to the customer's IP address and User-Agent, as required by applicable regulations
Endpoint
POST https://api-payments.dpay.pl/api/v1_0/payments/register
Content-Type: application/json
The payment registration endpoint accepts up to 120 requests per minute.
Request parameters
The request is the same as for standard payment registration, with additional BLIK-specific fields:
| Field | Type | Required | Description |
|---|---|---|---|
transactionType | string | Yes | "transfers" |
service | string | Yes | Service name from the panel |
value | string | Yes | Amount in PLN |
url_success | string | Yes | URL used after a successful payment |
url_fail | string | Yes | URL used after a failed payment |
url_ipn | string | Yes | URL for IPN notifications |
checksum | string | Yes | SHA-256 checksum |
blik_code | string | Yes | Six-digit BLIK code |
user_ip | string | Yes | Customer's IP address |
user_agent | string | Yes | Customer's User-Agent header |
Checksum generation
Generate the checksum exactly as for a standard payment:
sha256({service}|{SecretHash}|{value}|{url_success}|{url_fail}|{url_ipn})
Example request
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.example/success",
"url_fail": "https://myshop.example/error",
"url_ipn": "https://myshop.example/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.example/success';
$urlFail = 'https://myshop.example/error';
$urlIpn = 'https://myshop.example/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 submitted through 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.example/success';
const urlFail = 'https://myshop.example/error';
const urlIpn = 'https://myshop.example/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": "Internal processing",
"status": true,
"transactionId": "abc-def-123-456"
}
The status field is a Boolean (true or false). After receiving this response, the customer must confirm the transaction in their banking app. You receive the payment result through IPN.
Check transaction status
In addition to waiting for an IPN notification, you can poll the dpay API for the current transaction status. This is useful 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
| Field | Type | Description |
|---|---|---|
service | string | Service name from the panel |
transaction_id | string | Transaction ID returned during registration |
checksum | string | SHA-256 checksum |
Checksum generation
sha256({service}|{transaction_id}|{SecretHash})
Example response
The response contains the top-level status: "success" field and a transaction object. The most important fields are shown below; see Transaction Details in the API Reference for the complete response:
{
"status": "success",
"transaction": {
"id": "abc-def-123-456",
"status": "paid",
"value": "29.99",
"creation_date": "2026-03-15 12:00:00"
}
}
Possible statuses
| Status | Description |
|---|---|
created | Transaction created and awaiting confirmation |
processing | Transaction is being processed |
paid | Payment completed successfully |
captured | Funds captured; final status for a pre-authorized payment |
expired | Transaction canceled or expired |
We recommend polling every 2–3 seconds for no more than 2 minutes. The BLIK code expires after that time.
The complete endpoint documentation is available in Transaction Details in the API Reference.
Error: payment declined
{
"error": true,
"msg": "Transaction canceled",
"status": false,
"transactionId": "42191111-A7AE-392E-8C09-7965C1DC6B0B",
"additionalInfo": {
"error": "USER_DECLINED",
"error_description": "User declined the transaction"
}
}
Error handling
The error code is returned in additionalInfo.error, with an optional description in additionalInfo.error_description.
In test mode (sandbox), the following simulation codes are used:
| Error code | Description | Action |
|---|---|---|
DECLINE | Transaction declined | Ask the customer to retry |
EXPIRED_CARD | Payment instrument expired | Inform the customer |
INSUFFICIENT_FUNDS | Insufficient funds | Inform the customer |
USER_DECLINED | Customer declined the transaction in their banking app | Ask the customer to retry |
TIMEOUT | Timed out while waiting for confirmation | Ask for a new code |
SYSTEM_ERROR | System error | Retry later |
In production, additionalInfo.error contains the raw BLIK decline code passed through without modification, or INTERNAL_ERROR for an internal error. Treat the list as open-ended and handle unknown codes as a generic payment decline.
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 the 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 the IPN; the payment status changes after confirmation
});
</script>
Testing in sandbox mode
In test mode, you can use special BLIK codes that simulate different scenarios without contacting the BLIK provider:
| BLIK code | Scenario |
|---|---|
777200 | Payment succeeds |
777201 | Payment succeeds after a longer wait |
777400 | Customer declines in their banking app |
777401 | Timeout with no confirmation |
777402 | Insufficient funds |
777500 | System error |
The complete list of test data and details is available in Test environment.
BLIK code validation
A BLIK code always consists of exactly six digits. Validate it on both the client and server:
// Server-side validation
if (!preg_match('/^\d{6}$/', $blikCode)) {
http_response_code(400);
echo json_encode(['error' => 'The BLIK code must contain exactly 6 digits']);
exit;
}
A BLIK code is valid for 2 minutes after it is generated in the banking app. Make sure the form tells the customer to enter it promptly.