Skip to main content

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:

  1. Register a PAYID alias by creating a transaction with an amount of 0, a BLIK code, and a register_blik_recurring_alias object. The customer approves recurring payments in their banking app.
  2. Create recurring charges by sending subsequent payments with transactionType: "blik_recurring" and the blik_alias field. 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:

ModelDescription
AThe customer's bank controls limits and renewals through its banking app
MThe merchant controls them; dpay validates every charge against the registration limits (limit_amt, tot_limit_amt)
ONo 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

FieldTypeRequiredDescription
transactionTypestringYes"blik_recurring"
servicestringYesService name from the panel
valuestringYesMust be 0 for registration without a charge
url_successstringYesURL used after successful registration
url_failstringYesURL used after failed registration
url_ipnstringYesURL for IPN notifications
checksumstringYesSHA-256 checksum
blik_codestringYesSix-digit BLIK code
user_ipstringYesCustomer's IP address
user_agentstringYesCustomer's User-Agent header
register_blik_recurring_aliasobjectYesPAYID alias parameters described below
alias_ipn_urlstringNoURL for alias-status notifications, maximum 500 characters; if omitted, notifications are sent to url_ipn
descriptionstringNoSubscription description

register_blik_recurring_alias object

FieldTypeRequiredDescription
labelstringYesSubscription label visible to the customer, maximum 50 characters
typestringYesAlias type; it must be "PAYID"
modelstringYesRenewal model: "A", "M", or "O"
frequencystringYesCharge 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"
valuestringNoYour own alias value, maximum 128 characters; if omitted, dpay generates one automatically
limit_amtintegerNoPer-charge limit in grosz, for example 5999 = PLN 59.99
tot_limit_amtintegerNoTotal limit for all charges in grosz
is_limit_amt_fixedbooleanNotrue: every charge must equal limit_amt exactly; false: limit_amt is the maximum amount
expiration_datestringNoAlias expiration date in YYYY-MM-DD format, later than the current date
init_datestringNoFirst charge date in YYYY-MM-DD format, equal to or later than the current date
Recommendation: provide your own alias value

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})
Amount format in the checksum

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

FieldTypeRequiredDescription
transactionTypestringYes"blik_recurring"
servicestringYesService name from the panel
valuestringYesCharge amount in PLN; it must be greater than 0
url_successstringYesURL used after a successful payment
url_failstringYesURL used after a failed payment
url_ipnstringYesURL for IPN notifications
checksumstringYesSHA-256 checksum
blik_aliasstringYesPAYID alias value from registration, maximum 128 characters
no_delaybooleanNoDefaults 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_ipstringNoIP address; optional for a server-to-server charge
user_agentstringNoUser-Agent; optional for a server-to-server charge
Mutually exclusive fields

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_amt and is_limit_amt_fixed: true, the charge must be exactly equal to the limit
  • With limit_amt and is_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
POST/api/v1_0/payments/blik/recurring/statusComplete contract: PAYID alias status and registration parameters.Full contract in the API Reference

Parameters

FieldTypeRequiredDescription
servicestringYesService name from the panel
alias_valuestringYesPAYID alias value, maximum 128 characters
checksumstringYesSHA-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"
}
}
}
FieldTypeDescription
alias_valuestringAlias value
alias_typestringAlways "PAYID"
statusstring|nullCurrent alias status, such as "ACTIVE", "INACTIVE", "UNREGISTERED", or "EXPIRED"
expiration_datestring|nullAlias expiration date
registrationobject|nullRegistration parameters, including model, frequency, limits, label, and registration time

Rate limits

EndpointLimit
POST /payments/register120 requests/min
POST /blik/recurring/status60 requests/min

Error handling

Business validation errors return HTTP 400 in the {"status": "failed", "message": "...", "errors": {...}} format. Common messages include:

MessageCause
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.

tip

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.