Przejdź do głównej zawartości

Karty - płatności cykliczne (MIT)

Płatności cykliczne kartą umożliwiają obciążanie zapisanej karty klienta bez jego obecności - w modelu MIT (Merchant Initiated Transactions). Po jednorazowej zgodzie klienta (ustanowienie mandatu) merchant inicjuje kolejne pobrania samodzielnie, np. przy odnowieniu subskrypcji.

Model pull

Kolejne pobrania inicjuje merchant (model pull) - wołając endpoint /register w wybranym momencie cyklu. dpay.pl nie planuje pobrań automatycznie; to merchant decyduje kiedy i o jaką kwotę obciążyć kartę (w granicach ustalonych limitów).

POST/api/v1_0/payments/registerZarejestruj mandat kartowy i inicjuj pobrania cykliczne (MIT).Pełny kontrakt w API Reference POST/api/v1_0/cards/payment/{transactionId}/pay/card-otpAktywacja mandatu: pierwsza płatność kartą z 3D Secure.Pełny kontrakt w API Reference

Korzyści

  • Subskrypcje i płatności abonamentowe - automatyczne odnowienia bez udziału klienta
  • Mniejsza rezygnacja - brak konieczności ponownego wpisywania danych karty
  • Kontrola limitów - sztywne lub maksymalne kwoty pobrań ustalane per mandat

Jak działa?

Proces składa się z trzech kroków:

  1. Rejestracja mandatu - merchant wywołuje /register z obiektem register_card_recurring. dpay.pl zwraca card_recurring_alias.
  2. Aktywacja - klient wykonuje pierwszą płatność kartą (3DS). Po jej powodzeniu mandat zostaje aktywowany.
  3. Pobrania cykliczne (MIT) - merchant obciąża zapisaną kartę, podając card_recurring_alias (server-to-server, bez udziału klienta).
Zachowaj alias

card_recurring_alias zwracany przy rejestracji jest jedynym identyfikatorem mandatu potrzebnym do kolejnych pobrań. Zapisz go po stronie serwera, powiązany z klientem/subskrypcją.

Wymagania

  • Aktywny Punkt Płatności z włączoną obsługą płatności cyklicznych kartą (skontaktuj się z dpay.pl, aby ją włączyć)
  • Wdrożona integracja kartowa server-to-server - patrz Karty S2S (krok aktywacji wykorzystuje ten sam przepływ płatności kartą)
  • Możliwość przechowywania card_recurring_alias po stronie serwera

Endpoint

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

Krok 1. Rejestracja mandatu

Rejestracja tworzy mandat i zwraca jego alias. Pierwsza płatność jednocześnie ustanawia mandat - dlatego kwota value musi być większa od zera. Mandat staje się aktywny dopiero po udanej pierwszej płatności kartą (krok 2).

Kwota rejestracji

Domyślny przepływ rejestracji (jednym wywołaniem, opłacanym przez klienta) wymaga value > 0. Jeśli chcesz zapisać kartę bez obciążenia i ustanowić zgodę osobno (card-on-file), użyj trójetapowej tokenizacji przez pole card_recurring_operation - add_card przyjmuje value=0.

Parametry zapytania

PoleTypWymaganeOpis
transactionTypestringTak"card_recurring"
servicestringTakNazwa serwisu z panelu
valuestringTakKwota pierwszej płatności w PLN (musi być > 0)
creditcardintegerNie1 - płatność kartą. Pole opcjonalne - dla card_recurring flagi kartowe wymuszane są automatycznie po stronie dpay
url_successstringTakURL po udanej płatności
url_failstringTakURL po nieudanej płatności
url_ipnstringTakURL do powiadomień IPN
checksumstringTakSuma kontrolna SHA-256
emailstringNieE-mail klienta
client_namestringNieImię klienta
client_surnamestringNieNazwisko klienta
register_card_recurringobjectTakDane mandatu do rejestracji

Obiekt register_card_recurring

PoleTypWymaganeOpis
labelstringTakEtykieta mandatu (max 50 znaków)
frequencystringNieOpcjonalna częstotliwość cyklu - wartość enum: DAILY, WEEKLY, BIWEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL.
limit_amtintegerNieLimit pojedynczego pobrania w groszach
tot_limit_amtintegerNieLimit łączny wszystkich pobrań w groszach
is_limit_amt_fixedbooleanNietrue - kwota pobrania musi być dokładnie równa limit_amt; false - limit_amt jest kwotą maksymalną
expiration_datestringNieData wygaśnięcia mandatu (YYYY-MM-DD, po dacie pobrania są odrzucane)
init_datestringNieData rozpoczęcia cyklu (YYYY-MM-DD)
Jednostki: grosze vs PLN

Limity (limit_amt, tot_limit_amt) podawane są w groszach (np. 5999 = 59,99 PLN), natomiast value podawane jest w PLN (np. "59.99"). To częsta pułapka - zwróć na nią uwagę.

Alias generowany automatycznie

card_recurring_alias jest zawsze generowany przez dpay.pl (format DPAY.CARD.{id}.{losowy}). Nie podawaj własnego aliasu - zostanie odrzucony.

Generowanie checksum

Checksum generowany jest identycznie jak dla standardowej płatności:

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

Przykład zapytania

cURL

curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "card_recurring",
"service": "abc123",
"value": "19.99",
"creditcard": 1,
"url_success": "https://mojsklep.pl/sukces",
"url_fail": "https://mojsklep.pl/blad",
"url_ipn": "https://mojsklep.pl/api/ipn",
"checksum": "e3b0c44298fc1c149afb...",
"email": "klient@example.com",
"register_card_recurring": {
"label": "Subskrypcja Premium",
"frequency": "MONTHLY",
"limit_amt": 5999,
"tot_limit_amt": 71988,
"is_limit_amt_fixed": false,
"expiration_date": "2027-06-30"
}
}'

PHP

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

$value = '19.99'; // pierwsza płatność ustanawiająca mandat (musi być > 0)
$urlSuccess = 'https://mojsklep.pl/sukces';
$urlFail = 'https://mojsklep.pl/blad';
$urlIpn = 'https://mojsklep.pl/api/ipn';

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

$payload = json_encode([
'transactionType' => 'card_recurring',
'service' => $service,
'value' => $value,
'creditcard' => 1,
'url_success' => $urlSuccess,
'url_fail' => $urlFail,
'url_ipn' => $urlIpn,
'checksum' => $checksum,
'email' => 'klient@example.com',
'register_card_recurring' => [
'label' => 'Subskrypcja Premium',
'frequency' => 'MONTHLY',
'limit_amt' => 5999, // grosze
'tot_limit_amt' => 71988, // grosze
'is_limit_amt_fixed' => false,
'expiration_date' => '2027-06-30',
],
]);

$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);
// Zapisz $result['additionalInfo']['card_recurring_alias'] po stronie serwera

JavaScript (Node.js / Express)

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

async function registerCardMandate() {
const service = process.env.DPAY_SERVICE;
const secretHash = process.env.DPAY_SECRET_HASH;

const value = '19.99'; // pierwsza płatność ustanawiająca mandat (musi być > 0)
const urlSuccess = 'https://mojsklep.pl/sukces';
const urlFail = 'https://mojsklep.pl/blad';
const urlIpn = 'https://mojsklep.pl/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: 'card_recurring',
service,
value,
creditcard: 1,
url_success: urlSuccess,
url_fail: urlFail,
url_ipn: urlIpn,
checksum,
email: 'klient@example.com',
register_card_recurring: {
label: 'Subskrypcja Premium',
frequency: 'MONTHLY',
limit_amt: 5999, // grosze
tot_limit_amt: 71988, // grosze
is_limit_amt_fixed: false,
expiration_date: '2027-06-30',
},
}
);

// Zapisz response.data.additionalInfo.card_recurring_alias
return response.data;
}

Odpowiedź API

{
"error": false,
"status": true,
"msg": "Card recurring mandate registered, awaiting initial customer payment",
"transactionId": "abc-def-123-456",
"additionalInfo": {
"card_recurring_alias": "DPAY.CARD.123.a1b2c3d4",
"operation": "mandate_registration"
}
}

Zapisz additionalInfo.card_recurring_alias - będzie potrzebny do kolejnych pobrań. transactionId to identyfikator transakcji rejestracyjnej, którą klient opłaca w kroku 2.

Krok 2. Aktywacja mandatu

Klient finalizuje pierwszą płatność kartą dla transakcji transactionId z kroku 1 - dokładnie tak jak w zwykłej płatności kartą server-to-server (patrz Karty S2S), wraz z uwierzytelnieniem 3DS.

Po udanej pierwszej płatności mandat zostaje automatycznie aktywowany. Dopiero aktywny mandat pozwala na pobrania cykliczne. Wynik pierwszej płatności otrzymasz przez IPN, tak jak dla każdej transakcji kartowej.

informacja

Dopóki klient nie sfinalizuje pierwszej płatności, mandat pozostaje nieaktywny i próby pobrania zwrócą błąd No active card recurring mandate found for this alias.

Krok 3. Pobranie cykliczne (MIT)

Aby obciążyć zapisaną kartę, wywołaj /register z card_recurring_alias, transactionType=card_recurring oraz dodatnią kwotą value. Wywołanie jest server-to-server - klient nie bierze w nim udziału i nie jest wymagane 3DS.

Parametry zapytania

PoleTypWymaganeOpis
transactionTypestringTak"card_recurring"
servicestringTakNazwa serwisu z panelu
valuestringTakKwota pobrania w PLN (musi być > 0)
card_recurring_aliasstringTakAlias mandatu zwrócony przy rejestracji (max 128 znaków)
url_successstringTakURL po udanej płatności
url_failstringTakURL po nieudanej płatności
url_ipnstringTakURL do powiadomień IPN
checksumstringTakSuma kontrolna SHA-256
authorize_onlybooleanNiePreautoryzacja na tokenie zamiast obciążenia - blokuje środki; przechwyć lub anuluj później (patrz Krok 3b). Wymaga włączonej preautoryzacji.
Wzajemne wykluczanie

Pola register_card_recurring i card_recurring_alias wzajemnie się wykluczają. Przy pobraniu przekazuj wyłącznie card_recurring_alias.

Przykład zapytania

cURL

curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "card_recurring",
"service": "abc123",
"value": "59.99",
"card_recurring_alias": "DPAY.CARD.123.a1b2c3d4",
"url_success": "https://mojsklep.pl/sukces",
"url_fail": "https://mojsklep.pl/blad",
"url_ipn": "https://mojsklep.pl/api/ipn",
"checksum": "e3b0c44298fc1c149afb..."
}'

PHP

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

$value = '59.99';
$urlSuccess = 'https://mojsklep.pl/sukces';
$urlFail = 'https://mojsklep.pl/blad';
$urlIpn = 'https://mojsklep.pl/api/ipn';

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

// $aliasValue - zapisany wcześniej alias mandatu klienta
$payload = json_encode([
'transactionType' => 'card_recurring',
'service' => $service,
'value' => $value,
'card_recurring_alias' => $aliasValue,
'url_success' => $urlSuccess,
'url_fail' => $urlFail,
'url_ipn' => $urlIpn,
'checksum' => $checksum,
]);

$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);

Odpowiedź API

{
"error": false,
"status": true,
"msg": "Internal processing",
"transactionId": "ghi-jkl-789-012"
}

Odpowiedź potwierdza przyjęcie pobrania. Kształt pól msg i status transakcji zależą od tego, czy w momencie odpowiedzi obciążenie rozliczyło się od razu, czy jest jeszcze przetwarzane:

  • rozliczenie synchroniczne - msg: "Transaction paid", transakcja od razu ze statusem paid (w additionalInfo.transaction);
  • obciążenie w toku (lub preautoryzacja authorize_only) - msg: "Internal processing", status jeszcze niefinalny (jak w przykładzie powyżej).

Oba kształty są poprawne. Ostateczny wynik zawsze potwierdza IPN dla transakcji transactionId, tak jak dla każdej płatności. Opieraj logikę integracji na statusie transakcji i IPN, a nie na dokładnej treści msg - patrz statusy transakcji.

Idempotentność i ponawianie

Endpoint pobrania nie jest idempotentny - każde wywołanie tworzy nową próbę obciążenia. Po timeoucie sieciowym nie ponawiaj wywołania w ciemno - najpierw sprawdź status poprzedniej transakcji (IPN lub odpytanie statusu). Równoległe wywołania dla tego samego aliasu są blokowane (Card recurring charge already in progress for this alias).

Krok 3b. Preautoryzacja na tokenie (authorize_only)

Zamiast od razu obciążać zapisaną kartę, możesz jedynie zablokować środki na tokenie mandatu. Wystarczy dodać opcjonalne pole "authorize_only": true do wywołania pobrania z kroku 3 (transactionType=card_recurring + card_recurring_alias + dodatnia value). Wtedy zamiast obciążenia nastąpi preautoryzacja (blokada środków) na zapisanym tokenie.

Zwrócona transakcja (transactionId) to numer autoryzacji. Następnie:

  • przechwytujesz zablokowaną kwotę - w całości lub niższą - przez endpoint przechwycenia (capture),
  • albo zwalniasz blokadę przez endpoint anulowania (cancellation).

Oba endpointy (przechwycenie i anulowanie) są te same, które opisano na stronie Karty - pre-autoryzacja i przechwycenie.

Wymaga włączonej preautoryzacji

Preautoryzacja na tokenie wymaga włączonej preautoryzacji na Punkcie Płatności (skontaktuj się z dpay.pl). Bez pola authorize_only (lub gdy false) pobranie działa jak zwykłe obciążenie - bez zmian.

Przykład zapytania

cURL

curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "card_recurring",
"service": "abc123",
"value": "59.99",
"card_recurring_alias": "DPAY.CARD.123.a1b2c3d4",
"authorize_only": true,
"url_success": "https://mojsklep.pl/sukces",
"url_fail": "https://mojsklep.pl/blad",
"url_ipn": "https://mojsklep.pl/api/ipn",
"checksum": "e3b0c44298fc1c149afb..."
}'

Zablokowaną kwotę przechwycisz (w całości lub niższą) albo zwolnisz zgodnie z instrukcją na stronie Karty - pre-autoryzacja i przechwycenie.

Tokenizacja trójetapowa (card-on-file)

Domyślnie rejestracja mandatu (Krok 1) zapisuje kartę i ustanawia zgodę na pobrania jednym przepływem opłacanym przez klienta. Jeśli Twoja aplikacja rozdziela zapisanie karty od ustanowienia zgody (typowo: karta dodawana raz przy zakładaniu konta, zgoda potwierdzana osobno), użyj opcjonalnego pola card_recurring_operation, które rozdziela cały cykl na trzy niezależne wywołania /register:

Dwie rzeczy są tu kluczowe:

  • Kolejność jest sztywna: add_card to para wywołań /register + /pay. Mandat jest gotowy do cof_initial dopiero po ukończeniu /pay add_card (przechodzi ze stanu INACTIVE w TOKENIZED). Sam /register add_card nie wystarczy - alias już istnieje, ale mandat nie jest jeszcze stokenizowany. Wywołanie cof_initial przed /pay add_card kończy się błędem No tokenized card mandate found for this alias. Complete the add_card tokenization (/pay) before cof_initial.
  • Gdzie pojawia się formularz 3DS: w cof_initial zwraca go dopiero drugie wywołanie (/pay) - /register w tym kroku zwraca wyłącznie transactionId.
OperacjaWywołanieCo robi
add_cardregister_card_recurring + value=0Zapisuje kartę (tokenizacja). Zwraca card_recurring_alias. Bez obciążenia, bez 3DS, bez zgody.
cof_initialcard_recurring_alias (mandat po add_card)Ustanawia zgodę card-on-file na zapisanej karcie. /register zwraca nowy transactionId (osobny od transakcji add_card, ten sam alias) - formularz jeszcze się nie pojawia. Challenge 3-D Secure zwraca dopiero następujący /pay na tym transactionId (jak zwykła płatność kartą - patrz Karty S2S). Po jego potwierdzeniu przez klienta mandat staje się gotowy do pobrań. value=0 dla samej weryfikacji konta (bez obciążenia) lub value>0 dla pierwszego realnego obciążenia.
charge (lub pominięte)card_recurring_alias + value>0Pobranie cykliczne (MIT) - jak Krok 3. Domyślne zachowanie, gdy pole nie jest wysłane.
Wymaga włączonego card-on-file

Trójetapowy przepływ wymaga włączonego card-on-file na Punkcie Płatności (skontaktuj się z dpay.pl). Bez pola card_recurring_operation rejestracja i pobranie działają jak w Krokach 1-3 - bez zmian.

Każda z trzech operacji to para wywołań: /register (z odpowiednim card_recurring_operation) i - dla add_card oraz cof_initial - następujące po niej /pay (jak w Karty S2S). Różnica dotyczy uwierzytelnienia: add_card przebiega bez 3DS, cof_initial z pełnym challenge 3DS.

add_card - zapisanie karty (bez 3DS)

Rejestrujesz operację add_card z value=0, a następnie finalizujesz /pay z zaszyfrowanymi danymi karty (jak w Karty S2S). Karta zostaje zapisana bez obciążenia i bez 3DS; zwrócony card_recurring_alias reprezentuje zapisaną kartę.

curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "card_recurring",
"service": "abc123",
"value": "0",
"card_recurring_operation": "add_card",
"url_success": "https://mojsklep.pl/sukces",
"url_fail": "https://mojsklep.pl/blad",
"url_ipn": "https://mojsklep.pl/api/ipn",
"checksum": "e3b0c44298fc1c149afb...",
"register_card_recurring": { "label": "Karta klienta" }
}'

Odpowiedź /register

{
"error": false,
"msg": "Card registered, awaiting tokenization via /pay (required before cof_initial).",
"status": true,
"transactionId": "8f3c1e20-4a7b-4c11-9e33-add0000000",
"additionalInfo": {
"card_recurring_alias": "DPAY.CARD.123.a1b2c3d4",
"operation": "mandate_registration"
}
}

Zapisz additionalInfo.card_recurring_alias - to on identyfikuje zapisaną kartę w kolejnych operacjach. transactionId to transakcja tokenizacji, którą finalizujesz przez /pay.

Po /register add_card musi nastąpić /pay

Ta odpowiedź to dopiero pierwsze z dwóch wywołań add_card. Mandat jest w stanie INACTIVE i nie nadaje się jeszcze do cof_initial (przy value=0 nie ma żadnej płatności klienta - "awaiting" dotyczy tokenizacji, nie płatności). Najpierw ukończ /pay poniżej (tokenizacja S2S); dopiero to przełącza mandat w TOKENIZED. Nie przechodź prosto do cof_initial.

Odpowiedź /pay

Finalizujesz /pay zaszyfrowanymi danymi karty - server-to-server, bez 3DS (patrz Karty S2S):

{
"success": true,
"status": "success",
"message": {
"redirectText": "",
"redirectType": "SUCCESS",
"dccOffer": null
}
}

redirectType: "SUCCESS" oznacza, że karta została zapisana, a mandat przeszedł w stan TOKENIZED - dopiero teraz jest gotowy do cof_initial. Sama transakcja tokenizacji (transactionId z /register) kończy się jako anulowana - to nie jest realna płatność, więc nie podlega rozliczeniu.

cof_initial - ustanowienie zgody card-on-file (z 3DS)

Na zapisanej karcie (card_recurring_alias z add_card) ustanawiasz zgodę card-on-file. Rejestrujesz operację cof_initial, a następnie finalizujesz /pay - tu pojawia się challenge 3D Secure, który potwierdza zgodę klienta.

Najpierw ukończ /pay dla add_card

cof_initial wymaga mandatu w stanie TOKENIZED, czyli po ukończonym /pay dla add_card. Jeśli wywołasz cof_initial zaraz po /register add_card (pomijając /pay), dostaniesz No tokenized card mandate found for this alias. Complete the add_card tokenization (/pay) before cof_initial. Kolejność jest sztywna: register add_card/pay add_carddopiero potem register cof_initial/pay cof_initial.

curl -X POST https://api-payments.dpay.pl/api/v1_0/payments/register \
-H "Content-Type: application/json" \
-d '{
"transactionType": "card_recurring",
"service": "abc123",
"value": "0",
"card_recurring_operation": "cof_initial",
"card_recurring_alias": "DPAY.CARD.123.a1b2c3d4",
"url_success": "https://mojsklep.pl/sukces",
"url_fail": "https://mojsklep.pl/blad",
"url_ipn": "https://mojsklep.pl/api/ipn",
"checksum": "e3b0c44298fc1c149afb..."
}'

Odpowiedź /register

{
"error": false,
"msg": "Card-on-file initial registered, awaiting 3DS confirmation",
"status": true,
"transactionId": "b7d40c9a-2f18-4d6e-8a51-cof0000000",
"additionalInfo": {
"card_recurring_alias": "DPAY.CARD.123.a1b2c3d4",
"operation": "cof_initial"
}
}
Nowy transactionId - to jeszcze nie formularz

/register dla cof_initial zwraca nowy, osobny transactionId (inny niż transakcja tokenizacji z add_card), ale ten sam card_recurring_alias. To na tym nowym transactionId wołasz kolejny /pay - dopiero on zwraca formularz 3DS.

value=0 ustanawia zgodę bez obciążenia (weryfikacja konta); value>0 łączy ją z pierwszym realnym obciążeniem.

Odpowiedź /pay

Finalizujesz /pay na transactionId z odpowiedzi /register powyżej (jak w Karty S2S). Jeśli issuer wymaga uwierzytelnienia (typowy przypadek), odpowiedź zawiera formularz challenge:

{
"success": true,
"status": "success",
"message": {
"redirectText": "<Base64 HTML z formularzem auto-submit>",
"redirectType": "FORM",
"dccOffer": null
}
}

Jeśli issuer nie wymaga challenge (płatność frictionless), /pay zwraca od razu redirectType: "SUCCESS" - bez formularza.

Obsługa 3DS w cof_initial

Krok cof_initial zwraca challenge 3DS dokładnie tak jak zwykła płatność kartą. Finalizując /pay otrzymasz odpowiedź z redirectType: "FORM" i formularzem HTML (Base64) w redirectText. Obsłuż go identycznie jak opisano w Karty S2S - obsługa 3D Secure: wyrenderuj formularz, a po ukończeniu challenge przez klienta wywołaj /pay ponownie z threeDsConfirmed: true. Dopiero po potwierdzeniu challenge zgoda card-on-file zostaje ustanowiona, a mandat staje się gotowy do pobrań MIT.

charge - pobranie cykliczne (MIT)

Po ustanowieniu zgody (cof_initial) kolejne pobrania wykonujesz dokładnie jak w Kroku 3 - card_recurring_alias + dodatnia value (opcjonalnie authorize_only). Pole card_recurring_operation możesz pominąć albo podać charge - efekt jest ten sam: samodzielne wywołanie /register, bez /pay, bez 3DS.

Odpowiedź /register

{
"error": false,
"msg": "Transaction paid",
"status": true,
"transactionId": "c2a19f5b-7e04-48d3-b1a2-mit0000000",
"additionalInfo": {
"transaction": {
"payment_id": "c2a19f5b-...",
"value": 35,
"status": "paid"
}
}
}

Powyższy przykład pokazuje wariant rozliczony synchronicznie (msg: "Transaction paid", status transakcji paid). Dokładnie tak samo jak w Kroku 3 odpowiedź może też przyjąć postać msg: "Internal processing" ze statusem niefinalnym, gdy obciążenie jest jeszcze przetwarzane - oba kształty są poprawne. Zasady są identyczne jak w Kroku 3: opieraj logikę na statusie transakcji i IPN, a nie na treści msg; endpoint nie jest idempotentny.

Limity

Limity walidowane są po stronie dpay.pl przed obciążeniem karty:

LimitPoleDziałanie
Pojedyncze pobranie (sztywne)limit_amt + is_limit_amt_fixed: trueKwota pobrania musi być dokładnie równa limit_amt
Pojedyncze pobranie (maksymalne)limit_amt + is_limit_amt_fixed: falseKwota pobrania nie może przekroczyć limit_amt
Łącznytot_limit_amtSuma wszystkich rozliczonych pobrań + bieżące nie może przekroczyć tot_limit_amt
Wygaśnięcieexpiration_datePo tej dacie pobrania są odrzucane

Limity podawane są w groszach. Przekroczenie limitu zwraca błąd jeszcze przed wywołaniem operatora kartowego.

Wyrejestrowanie mandatu

Aby zakończyć subskrypcję, po prostu przestań wywoływać endpoint pobrania - dpay.pl nie wykona żadnego obciążenia bez Twojego zapytania. Mandat wygasa automatycznie po expiration_date. Operator dpay.pl może również ręcznie dezaktywować mandat w panelu administracyjnym (status UNREGISTERED), po czym kolejne pobrania będą odrzucane.

Walidacja i ograniczenia

RegułaOpis
transactionTypeMusi być "card_recurring"
Wzajemne wykluczanieregister_card_recurring i card_recurring_alias nie mogą wystąpić jednocześnie
register_card_recurring.labelMaksymalnie 50 znaków
register_card_recurring.frequencyOpcjonalna; wartość enum: DAILY, WEEKLY, BIWEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL
card_recurring_aliasMaksymalnie 128 znaków; przy pobraniu wymaga value > 0
Alias mandatuZawsze generowany przez dpay.pl - nie można podać własnego
authorize_onlyOpcjonalne; z card_recurring_alias - preautoryzacja zamiast obciążenia. Wymaga włączonej preautoryzacji na Punkcie Płatności
Limitylimit_amt, tot_limit_amt w groszach

Obsługa błędów

KomunikatPrzyczynaDziałanie
Invalid checksumNieprawidłowa suma kontrolnaSprawdź kolejność i wartości pól w checksum
Card Recurring is not enabled for this service.Brak włączonej obsługi na Punkcie PłatnościSkontaktuj się z dpay.pl
No active card recurring mandate found for this alias.Mandat nie istnieje lub nie został aktywowany (klient nie opłacił pierwszej płatności)Upewnij się, że krok 2 zakończył się sukcesem
Card recurring mandate has expired; please register a new mandate.Mandat po expiration_dateZarejestruj nowy mandat
Card Recurring charge requires value > 0.Pobranie z kwotą 0Podaj dodatnią value
Card Recurring: payment amount ... exceeds single payment limit ...Przekroczony limit_amtDostosuj kwotę do limitu
Card Recurring: total spent ... exceeds total limit ...Przekroczony tot_limit_amtLimit łączny mandatu wyczerpany
No tokenized card mandate found for this alias. Complete the add_card tokenization (/pay) before cof_initial.Wywołano cof_initial zanim ukończono /pay dla add_card (mandat nie jest jeszcze stokenizowany), albo alias jest błędnyUkończ krok /pay dla add_card przed cof_initial; sprawdź, że używasz aliasu zwróconego przez add_card
Card already registered.Karta jest już zapisana dla tego serwisu (przy add_card)Użyj istniejącego card_recurring_alias zamiast zapisywać kartę ponownie
Card is not available for recurring charges. Please re-add the card.Zapisana karta nie ma poprawnej referencji do obciążeń cyklicznychZapisz kartę ponownie (add_card → nowy alias)
Card recurring charge already in progress for this alias...Trwa równoległe pobranie dla tego aliasuNie ponawiaj w ciemno - sprawdź status poprzedniej próby

Odmowy obciążenia i błędy danych karty (np. Payment was declined by the card issuer., Invalid card details.) opisuje wspólna sekcja Komunikaty przetwarzania płatności. Treść pola message bywa różna w zależności od sytuacji - dopasowuj logikę luźno, a nieznane wartości traktuj jak ogólny błąd.

wskazówka

Jeśli pobrania zaczną być trwale odrzucane (np. karta klienta wygasła) lub mandat wygasł, zarejestruj nowy mandat - klient ponownie poda dane karty i wyrazi zgodę.