BLIK recurring payments (subscriptions)
BLIK recurring payments let you charge a customer without their participation in every payment, which makes them suitable for subscriptions and recurring plans. The customer registers a PAYID alias once by confirming a BLIK code in their banking app. You then initiate subsequent charges from your server using the saved alias.
POST/api/v1_0/payments/registerComplete registration contract: parameters, checksum, and error codes.Full contract in the API Reference
How it works
The process has two stages:
- Register a PAYID alias by creating a transaction with an amount of
0, a BLIK code, and aregister_blik_recurring_aliasobject. The customer approves recurring payments in their banking app. - Create recurring charges by sending subsequent payments with
transactionType: "blik_recurring"and theblik_aliasfield. Your server initiates them without customer interaction.
Requirements
- An active Payment Point with BLIK recurring payments and the selected renewal model enabled; contact dpay support to enable them
- Secure server-side storage for the alias value, associated with the customer's account
- Access to the customer's IP address and User-Agent during registration, as required by applicable regulations
Renewal models
When registering an alias, choose the charge-control model:
| Model | Description |
|---|---|
A | The customer's bank controls limits and renewals through its banking app |
M | The merchant controls them; dpay validates every charge against the registration limits (limit_amt, tot_limit_amt) |
O | No limit control |
The selected model must be enabled for your service. Using a model that is not enabled returns a registration error.
Endpoint
POST https://api-payments.dpay.pl/api/v1_0/payments/register
Content-Type: application/json
Step 1: Register a PAYID alias
Registration is a transaction with an amount equal to 0. The customer is not charged; they only approve recurring payments with a BLIK code.
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
transactionType | string | Yes | "blik_recurring" |
service | string | Yes | Service name from the panel |
value | string | Yes | Must be 0 for registration without a charge |
url_success | string | Yes | URL used after successful registration |
url_fail | string | Yes | URL used after failed registration |
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 |
register_blik_recurring_alias | object | Yes | PAYID alias parameters described below |
alias_ipn_url | string | No | URL for alias-status notifications, maximum 500 characters; if omitted, notifications are sent to url_ipn |
description | string | No | Subscription description |
register_blik_recurring_alias object
| Field | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Subscription label visible to the customer, maximum 50 characters |
type | string | Yes | Alias type; it must be "PAYID" |
model | string | Yes | Renewal model: "A", "M", or "O" |
frequency | string | Yes | Charge frequency: a number from 1 to 999 followed by D (days), W (weeks), M (months), Q (quarters), or Y (years), for example "1M", "2W", or "30D" |
value | string | No | Your own alias value, maximum 128 characters; if omitted, dpay generates one automatically |
limit_amt | integer | No | Per-charge limit in grosz, for example 5999 = PLN 59.99 |
tot_limit_amt | integer | No | Total limit for all charges in grosz |
is_limit_amt_fixed | boolean | No | true: every charge must equal limit_amt exactly; false: limit_amt is the maximum amount |
expiration_date | string | No | Alias expiration date in YYYY-MM-DD format, later than the current date |
init_date | string | No | First charge date in YYYY-MM-DD format, equal to or later than the current date |
We recommend supplying your own value in register_blik_recurring_alias.value and storing it on
your server in association with the customer's account. You need it for subsequent charges and
alias-status checks. The value must be unique; an attempt to register a second active alias with the
same value is rejected.
Checksum generation
Generate the checksum exactly as for a standard payment:
sha256({service}|{SecretHash}|{value}|{url_success}|{url_fail}|{url_ipn})
The amount in the checksum is normalized to two decimal places. For registration with value: 0,
hash "0.00".
Example request
cURL
curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "blik_recurring",
"service": "abc123",
"value": 0,
"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",
"description": "Premium subscription",
"register_blik_recurring_alias": {
"label": "Premium subscription",
"type": "PAYID",
"value": "SUB-1234567890",
"model": "M",
"frequency": "1M",
"limit_amt": 5999,
"tot_limit_amt": 71988,
"is_limit_amt_fixed": false,
"expiration_date": "2027-07-01"
}
}'
PHP
<?php
$service = getenv('DPAY_SERVICE');
$secretHash = getenv('DPAY_SECRET_HASH');
$value = '0.00';
$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' => 'blik_recurring',
'service' => $service,
'value' => 0,
'url_success' => $urlSuccess,
'url_fail' => $urlFail,
'url_ipn' => $urlIpn,
'checksum' => $checksum,
'blik_code' => $_POST['blik_code'],
'user_ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'description' => 'Premium subscription',
'register_blik_recurring_alias' => [
'label' => 'Premium subscription',
'type' => 'PAYID',
'value' => 'SUB-' . $userId,
'model' => 'M',
'frequency' => '1M',
'limit_amt' => 5999,
'tot_limit_amt' => 71988,
'is_limit_amt_fixed' => false,
],
]);
$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);
API response
{
"error": false,
"msg": "Internal processing",
"status": true,
"transactionId": "abc-def-123-456"
}
The status field is a Boolean (true or false). After this response, the customer must approve
the subscription registration in their banking app. You receive the registration result through
IPN and can check the current alias status with the status endpoint described below.
Step 2: Create a recurring charge
Initiate subsequent payments from your server, without a BLIK code or customer interaction.
Request parameters
| Field | Type | Required | Description |
|---|---|---|---|
transactionType | string | Yes | "blik_recurring" |
service | string | Yes | Service name from the panel |
value | string | Yes | Charge amount in PLN; it must be greater than 0 |
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_alias | string | Yes | PAYID alias value from registration, maximum 128 characters |
no_delay | boolean | No | Defaults to true for an immediate response. With false, BLIK may hold the transaction for up to 72 hours while waiting for customer approval in their banking app (models M and O); prior dpay approval is required |
user_ip | string | No | IP address; optional for a server-to-server charge |
user_agent | string | No | User-Agent; optional for a server-to-server charge |
For a recurring charge, do not send blik_code, register_blik_alias, or
register_blik_recurring_alias. The blik_alias field is mutually exclusive with them.
Limit validation for model M
For aliases using model M, dpay validates every charge:
- With
limit_amtandis_limit_amt_fixed: true, the charge must be exactly equal to the limit - With
limit_amtandis_limit_amt_fixed: false, the charge cannot exceed the limit - With
tot_limit_amt, the sum of previous charges and the current charge cannot exceed the total limit
Exceeding a limit returns HTTP 400 with details in the message field.
Example request
curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "blik_recurring",
"service": "abc123",
"value": "59.99",
"url_success": "https://myshop.example/success",
"url_fail": "https://myshop.example/error",
"url_ipn": "https://myshop.example/api/ipn",
"checksum": "e3b0c44298fc1c149afb...",
"blik_alias": "SUB-1234567890"
}'
API response
{
"error": false,
"msg": "Internal processing",
"status": true,
"transactionId": "abc-def-123-456"
}
The charge result is sent through IPN to url_ipn.
Check alias status
Use the dedicated endpoint to retrieve the current PAYID alias status and its registration parameters:
POST https://api-payments.dpay.pl/api/v1_0/payments/blik/recurring/status
Content-Type: application/json
/api/v1_0/payments/blik/recurring/statusComplete contract: PAYID alias status and registration parameters.Full contract in the API Reference
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
service | string | Yes | Service name from the panel |
alias_value | string | Yes | PAYID alias value, maximum 128 characters |
checksum | string | Yes | SHA-256 checksum |
Checksum generation
sha256({service}|{SecretHash}|{alias_value})
Example request
curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/blik/recurring/status \
-H "Content-Type: application/json" \
-d '{
"service": "abc123",
"alias_value": "SUB-1234567890",
"checksum": "f5a3b2c1d0..."
}'
Response
{
"status": "success",
"data": {
"alias_value": "SUB-1234567890",
"alias_type": "PAYID",
"status": "ACTIVE",
"expiration_date": "2027-07-01",
"registration": {
"model": "M",
"frequency": "1M",
"limit_amt": 5999,
"tot_limit_amt": 71988,
"is_limit_amt_fixed": false,
"init_date": null,
"label": "Premium subscription",
"registered_at": "2026-07-01T12:00:00+02:00"
}
}
}
| Field | Type | Description |
|---|---|---|
alias_value | string | Alias value |
alias_type | string | Always "PAYID" |
status | string|null | Current alias status, such as "ACTIVE", "INACTIVE", "UNREGISTERED", or "EXPIRED" |
expiration_date | string|null | Alias expiration date |
registration | object|null | Registration parameters, including model, frequency, limits, label, and registration time |
Rate limits
| Endpoint | Limit |
|---|---|
POST /payments/register | 120 requests/min |
POST /blik/recurring/status | 60 requests/min |
Error handling
Business validation errors return HTTP 400 in the
{"status": "failed", "message": "...", "errors": {...}} format. Common messages include:
| Message | Cause |
|---|---|
BLIK Recurring is not enabled for this service. | BLIK recurring payments are not enabled for the service |
BLIK Recurring model M is not enabled for this service. | The selected renewal model is not enabled |
BLIK Recurring registration requires value=0. | Registration was attempted with an amount other than 0 |
An active BLIK Recurring alias with this value already exists. | Duplicate alias value |
BLIK Recurring payment requires value > 0. | A recurring charge was attempted with an amount of 0 |
No active BLIK Recurring alias found for this value. | The alias does not exist, has expired, or has been unregistered |
Setting no_delay=false requires merchant approval (...). Contact support. | no_delay: false was used without dpay approval |
A payment declined by BLIK returns
{"error": true, "msg": "Transaction canceled", "status": false, ...} with a code in
additionalInfo.error. The code list is shared by all BLIK payments; see
BLIK OneClick error handling.
If an alias expires or the customer withdraws consent in their banking app, register the alias again—Step 1—the next time you can, for example when renewing the subscription.