Przejdź do głównej zawartości

PHP SDK

Dostępne - wersja 0.1.1

Oficjalna biblioteka PHP do integracji z API dpay.pl. Automatyzuje generowanie sum kontrolnych, weryfikację powiadomień IPN oraz wywołania API: rejestrację płatności, szczegóły transakcji, zwroty, listę banków, aliasy BLIK, płatności kartowe server-to-server i wypłaty 1:1. Kwoty są reprezentowane obiektem Money (grosze jako int), a odpowiedzi API mapowane na typowane obiekty.


Wymagania

  • PHP 7.4 lub nowszy (testowane do PHP 8.5)
  • Rozszerzenia: curl, json, openssl
  • Composer

Biblioteka nie ma żadnych zależności runtime poza rozszerzeniami PHP.


Instalacja

composer require dpayglobal/dpay-php-sdk

Pakiet jest publikowany na Packagist, a kod źródłowy dostępny na GitHub.


Konfiguracja

Utwórz instancję klienta DPay\DPayClient, podając dane z Panelu dpay.pl:

use DPay\DPayClient;

$dpay = new DPayClient([
'service' => 'nazwa_serwisu',
'secret_hash' => 'twoj_secret_hash',
]);
OpcjaTypOpis
servicestringNazwa serwisu (Punktu Płatności) z panel.dpay.pl (wymagane)
secret_hashstringKlucz Hash (Secret Hash) do generowania sum kontrolnych (wymagane)
timeoutintTimeout żądań HTTP w sekundach (domyślnie 30)
http_clientHttpClientInterfaceWłasny klient HTTP (testy, proxy)
base_urlsarrayNadpisanie hostów API (klucze: api_payments, panel, gateway)

Klient udostępnia serwisy jako publiczne właściwości:

SerwisZakres
$dpay->paymentsRejestracja płatności, szczegóły transakcji
$dpay->refundsZwroty i sprawdzanie dostępności zwrotu
$dpay->banksLista banków pay-by-link
$dpay->blikAliasy BLIK OneClick i Recurring
$dpay->cardsPłatności kartowe server-to-server
$dpay->payoutsSzczegóły wypłat 1:1

Kwoty - obiekt Money

Wszystkie kwoty w SDK to obiekt DPay\Money, wewnętrznie przechowujący grosze jako int. SDK sam dba o właściwy format kwoty dla każdego endpointu (kwota dziesiętna albo grosze), więc nie musisz pamiętać, który endpoint oczekuje którego formatu.

use DPay\Money;

Money::pln(1050); // 10.50 PLN
Money::of(500, 'EUR'); // 5.00 EUR
Money::fromDecimal('10.50', 'PLN');

$money->getMinor(); // 1050 (grosze)
$money->toDecimal(); // "10.50"

Rejestracja płatności

Żądanie budujesz obiektem RegisterPaymentRequest - pola wymagane w create(), opcjonalne przez settery with*(). Suma kontrolna i pole transactionType są dokładane automatycznie:

use DPay\DPayClient;
use DPay\Money;
use DPay\Payment\Payer;
use DPay\Payment\RegisterPaymentRequest;
use DPay\Payment\ReturnUrls;
use DPay\Payment\TransactionType;

$dpay = new DPayClient([
'service' => 'nazwa_serwisu',
'secret_hash' => 'twoj_secret_hash',
]);

$payment = $dpay->payments->register(
RegisterPaymentRequest::create(
Money::pln(1050),
TransactionType::TRANSFERS,
new ReturnUrls(
'https://twojsklep.pl/sukces',
'https://twojsklep.pl/blad',
'https://twojsklep.pl/ipn'
)
)
->withDescription('Zamówienie #1234')
->withCustom('order-1234')
->withPayer(Payer::create()->withEmail('klient@example.com'))
);

if ($payment->getRedirectUrl() !== null) {
header('Location: ' . $payment->getRedirectUrl());
exit;
}

if ($payment->isPaid()) {
// płatność rozliczona inline (np. BLIK Level 0)
}

Typy transakcji (TransactionType): TRANSFERS, DCB_GATEWAY, CARD_AUTH, MB_WAY_DIRECT, BIZUM_DIRECT, BLIK_RECURRING, CARD_RECURRING.

Ważniejsze settery RegisterPaymentRequest

MetodaOpis
withDescription(string)Opis transakcji widoczny dla klienta
withCustom(string)Własne dane identyfikujące zamówienie (wracają w IPN)
withPayer(Payer)E-mail oraz imię i nazwisko klienta
withChannel(string)Płatność bezpośrednia wskazanym kanałem banku
withCreditCard(bool) / withPaypal(bool) / withPaysafecard(bool) / withInstallment(bool) / withBlik(bool)Włączenie lub wyłączenie metod na bramce
withNoBanks(bool)Ukrycie listy banków
withBlikCode(string $code, string $userAgent, string $userIp)Płatność BLIK Level 0 (kod 6-cyfrowy)
withBlikAlias(string $alias, string $userAgent, string $userIp)Płatność aliasem BLIK OneClick
withRegisterBlikAlias(BlikAliasRegistration)Rejestracja aliasu OneClick przy płatności
withCardRecurring(CardRecurringRegistration)Rejestracja mandatu card recurring
withCardRecurringAlias(string)Obciążenie zapisanej karty (MIT)
withPayout(PayoutInstruction)Instrukcja wypłaty 1:1
withEfaktura(?InvoiceDetails)Płatność za eFakturę (KSeF)
withPhoneNumber(string $phone, string $currency)MB WAY

Obsługa IPN

Powiadomienia IPN weryfikujesz przez IpnVerifier::constructEvent() - nieprawidłowy podpis rzuca SignatureVerificationException:

use DPay\Exception\SignatureVerificationException;
use DPay\Ipn\IpnEvent;
use DPay\Ipn\IpnVerifier;

try {
$event = IpnVerifier::constructEvent(
(string) file_get_contents('php://input'),
'twoj_secret_hash'
);

if ($event->isTransfer()) {
markOrderAsPaid($event->getId(), $event->getAmount());
}

if ($event->isCapture()) {
markOrderAsCaptured($event->getId(), $event->getAmount(), $event->getCapturePaymentId());
}

http_response_code(200);
echo IpnEvent::ACK;
} catch (SignatureVerificationException $e) {
http_response_code(400);
echo 'Invalid signature';
}
Odpowiedź musi być dokładnie OK

dpay.pl uznaje IPN za dostarczony wyłącznie, gdy treść odpowiedzi to dokładnie OK (stała IpnEvent::ACK). Kod HTTP nie jest sprawdzany. Upewnij się, że framework nie dokleja niczego do body.

informacja

Zawsze porównaj $event->getAmount() (string, np. "10.00") z kwotą zamówienia w Twojej bazie i przetwarzaj każdą transakcję tylko raz (IPN może przyjść wielokrotnie).


Szczegóły transakcji

$transaction = $dpay->payments->details('identyfikator-transakcji');

$transaction->getStatus(); // 'paid', 'created', 'processing', 'expired', 'captured'
$transaction->isPaid(); // true dla paid i captured
$transaction->getValue()->toDecimal(); // "29.99"
$transaction->getRefundedAmount(); // Money
$transaction->getAvailableRefundAmount(); // Money
$transaction->isFullyRefunded(); // bool
$transaction->getRefunds(); // TransactionRefund[]

Zwroty

use DPay\Money;

// zwrot pełny
$refund = $dpay->refunds->create('identyfikator-transakcji');

// zwrot częściowy z powodem
$refund = $dpay->refunds->create('identyfikator-transakcji', Money::pln(500), 'reklamacja');

$refund->isAccepted();

Przed zwrotem możesz sprawdzić jego dostępność - metoda zwraca wynik biznesowy (nie rzuca wyjątku), także gdy zwrot jest niemożliwy:

$availability = $dpay->refunds->checkAvailability('identyfikator-transakcji', Money::pln(500));

if (!$availability->isAvailable()) {
$availability->getMessage(); // np. "Transakcja nie została opłacona"
$availability->getHttpStatus(); // stabilny kod przyczyny, patrz tabela
}
Kod HTTPPrzyczyna odmowy
400Kwota przekracza dostępną kwotę zwrotu
401Kanał płatności nie obsługuje zwrotów
402Transakcja nie została opłacona
406Niewystarczające saldo na pokrycie zwrotu
409Wniosek o zwrot został już złożony
410Transakcja została już zwrócona
411Transakcje obciążeniowe nie podlegają zwrotom

Banki

$banks = $dpay->banks->all(); // wszystkie banki dpay.pl
$banks = $dpay->banks->forService(); // banki dostępne dla Twojego serwisu

$banks[0]->getId();
$banks[0]->getName();

BLIK - aliasy OneClick i Recurring

$alias = $dpay->blik->alias('DPAY.UID.123456.abc12345');
$alias->isActive();
$alias->getApps();

$dpay->blik->unregisterAlias('DPAY.UID.123456.abc12345');

$status = $dpay->blik->recurringStatus('PAYID-...');
$status->getRegistration();

Rejestracja aliasu i płatność aliasem odbywają się przez rejestrację płatności (withBlikCode + withRegisterBlikAlias, płatność przez withBlikAlias).


Karty server-to-server

Dane karty szyfrujesz kluczem RSA pobieranym przed każdą próbą płatności:

use DPay\Card\CardData;
use DPay\Card\CardEncryptor;
use DPay\Card\CardPaymentRequest;
use DPay\Card\DccDecision;
use DPay\Payment\DeviceInfo;

$publicKey = $dpay->cards->publicKey();

$encrypted = (new CardEncryptor())->encrypt(
new CardData('4111111111111111', '123', '12/30'),
$transactionId,
$publicKey
);

$deviceInfo = DeviceInfo::create(/* dane przeglądarki płatnika */);

$result = $dpay->cards->payOtp($transactionId, CardPaymentRequest::create($deviceInfo)
->withEncryptedCardData($encrypted)
->withCardHolder('Jan', 'Kowalski')
->withChannelId(31));

if ($result->isSuccess()) {
// płatność przechwycona
} elseif ($result->requiresThreeDsForm()) {
$html = $result->getThreeDsFormHtml(); // wyrenderuj w przeglądarce płatnika
} elseif ($result->hasDccOffer()) {
$offer = $result->getDccOffer(); // pokaż ofertę DCC płatnikowi
// decyzję odeślij ponownym payOtp z ->withDccDecision(DccDecision::ACCEPT)
}

Dostępne są także: preAuth() (pre-autoryzacja), capture() i cancel() (przechwycenie i anulowanie), googlePay() oraz applePay(). Odrzucenie płatności przy HTTP 200 rzuca CardPaymentException z kodem błędu.

informacja

Karty S2S wymagają zgodności PCI-DSS po stronie merchanta. Szczegóły przepływu znajdziesz w dokumentacji kart S2S.


Wypłaty 1:1

$details = $dpay->payouts->details(12345);

$details->isProcessed();
$details->getNet()->toDecimal();
$details->getReceiver();

Obsługa błędów

Wszystkie wyjątki SDK implementują DPay\Exception\ExceptionInterface:

WyjątekKiedy
TransportExceptionBłąd sieci - status płatności nieznany, użyj payments->details()
AuthenticationException401 - błędny checksum lub secret hash
InvalidRequestException400/422 - błędy walidacji (getFieldErrors())
NotFoundException404 - zasób nie istnieje
RateLimitException429 - limit żądań (getRetryAfter())
PaymentRejectedExceptionRejestracja odrzucona (np. błędny kod BLIK; getTransactionId())
CardPaymentExceptionOdrzucenie płatności kartowej (getErrorCode())
SignatureVerificationExceptionNieprawidłowy podpis IPN
ApiServerException5xx lub niepoprawna odpowiedź API
use DPay\Exception\ApiErrorException;
use DPay\Exception\InvalidRequestException;
use DPay\Exception\TransportException;

try {
$payment = $dpay->payments->register($request);
} catch (InvalidRequestException $e) {
$e->getFieldErrors(); // array<string, string[]>
} catch (ApiErrorException $e) {
$e->getHttpStatus();
$e->getErrorCode();
} catch (TransportException $e) {
// nie wiadomo, czy żądanie dotarło - zweryfikuj przez payments->details()
}

Więcej informacji