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.
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).
/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:
- Rejestracja mandatu - merchant wywołuje
/registerz obiektemregister_card_recurring. dpay.pl zwracacard_recurring_alias. - Aktywacja - klient wykonuje pierwszą płatność kartą (3DS). Po jej powodzeniu mandat zostaje aktywowany.
- Pobrania cykliczne (MIT) - merchant obciąża zapisaną kartę, podając
card_recurring_alias(server-to-server, bez udziału klienta).
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_aliaspo 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).
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
| Pole | Typ | Wymagane | Opis |
|---|---|---|---|
transactionType | string | Tak | "card_recurring" |
service | string | Tak | Nazwa serwisu z panelu |
value | string | Tak | Kwota pierwszej płatności w PLN (musi być > 0) |
creditcard | integer | Nie | 1 - płatność kartą. Pole opcjonalne - dla card_recurring flagi kartowe wymuszane są automatycznie po stronie dpay |
url_success | string | Tak | URL po udanej płatności |
url_fail | string | Tak | URL po nieudanej płatności |
url_ipn | string | Tak | URL do powiadomień IPN |
checksum | string | Tak | Suma kontrolna SHA-256 |
email | string | Nie | E-mail klienta |
client_name | string | Nie | Imię klienta |
client_surname | string | Nie | Nazwisko klienta |
register_card_recurring | object | Tak | Dane mandatu do rejestracji |
Obiekt register_card_recurring
| Pole | Typ | Wymagane | Opis |
|---|---|---|---|
label | string | Tak | Etykieta mandatu (max 50 znaków) |
frequency | string | Nie | Opcjonalna częstotliwość cyklu - wartość enum: DAILY, WEEKLY, BIWEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL. |
limit_amt | integer | Nie | Limit pojedynczego pobrania w groszach |
tot_limit_amt | integer | Nie | Limit łączny wszystkich pobrań w groszach |
is_limit_amt_fixed | boolean | Nie | true - kwota pobrania musi być dokładnie równa limit_amt; false - limit_amt jest kwotą maksymalną |
expiration_date | string | Nie | Data wygaśnięcia mandatu (YYYY-MM-DD, po dacie pobrania są odrzucane) |
init_date | string | Nie | Data rozpoczęcia cyklu (YYYY-MM-DD) |
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ę.
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.
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
| Pole | Typ | Wymagane | Opis |
|---|---|---|---|
transactionType | string | Tak | "card_recurring" |
service | string | Tak | Nazwa serwisu z panelu |
value | string | Tak | Kwota pobrania w PLN (musi być > 0) |
card_recurring_alias | string | Tak | Alias mandatu zwrócony przy rejestracji (max 128 znaków) |
url_success | string | Tak | URL po udanej płatności |
url_fail | string | Tak | URL po nieudanej płatności |
url_ipn | string | Tak | URL do powiadomień IPN |
checksum | string | Tak | Suma kontrolna SHA-256 |
authorize_only | boolean | Nie | Preautoryzacja na tokenie zamiast obciążenia - blokuje środki; przechwyć lub anuluj później (patrz Krok 3b). Wymaga włączonej preautoryzacji. |
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 statusempaid(wadditionalInfo.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.
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.
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_cardto para wywołań/register+/pay. Mandat jest gotowy docof_initialdopiero po ukończeniu/payadd_card (przechodzi ze stanuINACTIVEwTOKENIZED). Sam/register add_cardnie wystarczy - alias już istnieje, ale mandat nie jest jeszcze stokenizowany. Wywołaniecof_initialprzed/payadd_card kończy się błędemNo tokenized card mandate found for this alias. Complete the add_card tokenization (/pay) before cof_initial. - Gdzie pojawia się formularz 3DS: w
cof_initialzwraca go dopiero drugie wywołanie (/pay) -/registerw tym kroku zwraca wyłącznietransactionId.
| Operacja | Wywołanie | Co robi |
|---|---|---|
add_card | register_card_recurring + value=0 | Zapisuje kartę (tokenizacja). Zwraca card_recurring_alias. Bez obciążenia, bez 3DS, bez zgody. |
cof_initial | card_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>0 | Pobranie cykliczne (MIT) - jak Krok 3. Domyślne zachowanie, gdy pole nie jest wysłane. |
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.
/register add_card musi nastąpić /payTa 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.
/pay dla add_cardcof_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_card → dopiero 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"
}
}
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.
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:
| Limit | Pole | Działanie |
|---|---|---|
| Pojedyncze pobranie (sztywne) | limit_amt + is_limit_amt_fixed: true | Kwota pobrania musi być dokładnie równa limit_amt |
| Pojedyncze pobranie (maksymalne) | limit_amt + is_limit_amt_fixed: false | Kwota pobrania nie może przekroczyć limit_amt |
| Łączny | tot_limit_amt | Suma wszystkich rozliczonych pobrań + bieżące nie może przekroczyć tot_limit_amt |
| Wygaśnięcie | expiration_date | Po 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ła | Opis |
|---|---|
transactionType | Musi być "card_recurring" |
| Wzajemne wykluczanie | register_card_recurring i card_recurring_alias nie mogą wystąpić jednocześnie |
register_card_recurring.label | Maksymalnie 50 znaków |
register_card_recurring.frequency | Opcjonalna; wartość enum: DAILY, WEEKLY, BIWEEKLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL |
card_recurring_alias | Maksymalnie 128 znaków; przy pobraniu wymaga value > 0 |
| Alias mandatu | Zawsze generowany przez dpay.pl - nie można podać własnego |
authorize_only | Opcjonalne; z card_recurring_alias - preautoryzacja zamiast obciążenia. Wymaga włączonej preautoryzacji na Punkcie Płatności |
| Limity | limit_amt, tot_limit_amt w groszach |
Obsługa błędów
| Komunikat | Przyczyna | Działanie |
|---|---|---|
Invalid checksum | Nieprawidłowa suma kontrolna | Sprawdź 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ści | Skontaktuj 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_date | Zarejestruj nowy mandat |
Card Recurring charge requires value > 0. | Pobranie z kwotą 0 | Podaj dodatnią value |
Card Recurring: payment amount ... exceeds single payment limit ... | Przekroczony limit_amt | Dostosuj kwotę do limitu |
Card Recurring: total spent ... exceeds total limit ... | Przekroczony tot_limit_amt | Limit łą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łędny | Ukoń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ń cyklicznych | Zapisz kartę ponownie (add_card → nowy alias) |
Card recurring charge already in progress for this alias... | Trwa równoległe pobranie dla tego aliasu | Nie 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.
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ę.