Skip to main content

dpay Python SDK

Oficjalna biblioteka Pythona do integracji z API płatności dpay.pl.

Wymagania

  • Python 3.10 lub nowszy
  • Brak zależności runtime dla klienta synchronicznego

Instalacja

pip install dpay-python-sdk

Klient asynchroniczny wymaga dodatkowej zależności:

pip install "dpay-python-sdk[async]"

Uwaga na nazwę pakietu. Instalujesz dpay-python-sdk, a importujesz dpay. Nie instaluj pakietu o nazwie dpay z PyPI - to niepowiązany projekt, który zajmuje ten sam moduł najwyższego poziomu i przykryłby to SDK.

Szybki start

from dpay import DPayClient, Money, RegisterPaymentRequest, ReturnUrls, TransactionType

dpay = DPayClient(service="nazwa_serwisu", secret_hash="twoj_secret_hash")

payment = dpay.payments.register(
    RegisterPaymentRequest.create(
        Money.pln(1050),
        TransactionType.TRANSFERS,
        ReturnUrls(
            "https://twojsklep.pl/sukces",
            "https://twojsklep.pl/blad",
            "https://twojsklep.pl/ipn",
        ),
    )
    .with_description("Zamówienie #1234")
    .with_custom("order-1234")
)

if payment.redirect_url is not None:
    return redirect(payment.redirect_url)

Klient asynchroniczny

Identyczne API, metody korutynowe:

from dpay.aio import AsyncDPayClient

async with AsyncDPayClient(service="nazwa_serwisu", secret_hash="twoj_secret_hash") as dpay:
    payment = await dpay.payments.register(request)
    transaction = await dpay.payments.details(payment.transaction_id)

Płatności cykliczne

Rejestracja idzie razem z płatnością kodem BLIK klienta (kwota 0 - sama zgoda, więcej - opłata inicjalna). Kolejne obciążenia wysyła Twój serwer, bez kodu.

from dpay import Money, RecurringRegistration, RegisterPaymentRequest, ReturnUrls, TransactionType

urls = ReturnUrls("https://twojsklep.pl/sukces", "https://twojsklep.pl/blad")

registration = dpay.payments.register(
    RegisterPaymentRequest.create(Money.pln(0), TransactionType.TRANSFERS, urls)
    .with_blik_code(kod_blik, request.headers["User-Agent"], adres_ip_klienta)
    .with_recurring_registration(
        RecurringRegistration.create(
            "Abonament Premium", RecurringRegistration.MODEL_O, "https://twojsklep.pl/regulamin"
        ).with_alias("SUB-1234")
    )
)

charge = dpay.payments.register(
    RegisterPaymentRequest.create(Money.pln(4999), TransactionType.TRANSFERS, urls)
    .with_recurring_alias("SUB-1234")
    .with_description("Abonament Premium 10/2026")
)

status = dpay.recurring.status("SUB-1234")           # ACTIVE, INACTIVE, UNREGISTERED, EXPIRED, DECLINED
retry = dpay.recurring.retry(charge.transaction_id)  # po odmowie, np. INSUFFICIENT_FUNDS
dpay.recurring.cancel("SUB-1234", "Rezygnacja klienta")

Obciążenie wiąże alias z sumą kontrolną, a anulowanie ma własną sumę - SDK liczy obie. Limity API: status do 60, retry i cancel do 30 zapytań na minutę (licznik wspólny z resztą API płatności z tego adresu IP) - nie odpytuj statusu w pętli, wynik przychodzi webhookiem.

Webhooki

Zdarzenia (payment.succeeded, refund.failed, recurring_payment.canceled i inne) są podpisane. Weryfikuj je na surowym body, przed parsowaniem JSON:

from dpay import SignatureVerificationError, WebhookVerifier

def webhook_view(request):
    try:
        event = WebhookVerifier.construct_event(
            request.body,     # surowe bajty żądania
            request.headers,  # dowolny mapping, wielkość liter nazw nie ma znaczenia
            "whsec_...",      # sekret endpointu z panelu; w czasie rotacji lista sekretów
        )
    except SignatureVerificationError:
        return HttpResponse(status=400)

    if event.type == "payment.succeeded":
        payment = event.object  # kwoty w groszach
    return HttpResponse(status=200)

Deduplikuj zdarzenia po event.id. Historię zdarzeń (np. po awarii endpointu) pobierzesz przez dpay.events.iterate(types=["payment.succeeded"]).

Własny adres zdarzeń jednej płatności: .with_webhook(WebhookTarget.create("https://twojsklep.pl/webhooks")) (podpisywany sekretem webhooków serwisu).

Obsługa IPN

IPN przychodzi tylko wtedy, gdy podasz adres IPN w ReturnUrls.

dpay.pl uznaje IPN za dostarczony wyłącznie, gdy body odpowiedzi to dokładnie OK. Kod HTTP nie jest sprawdzany. Zawsze weryfikuj kwotę z własnym zamówieniem.

from dpay import IpnEvent, IpnVerifier, SignatureVerificationError

def ipn_view(request):
    try:
        event = IpnVerifier.construct_event(request.body, "twoj_secret_hash")
    except SignatureVerificationError:
        return HttpResponse("Invalid signature", status=400)

    if event.is_transfer:
        mark_order_as_paid(event.id, event.amount)

    return HttpResponse(IpnEvent.ACK)

event.amount to surowy string dziesiętny - payload IPN nie niesie waluty, więc porównaj go z kwotą własnego zamówienia.

Zwroty

from dpay import Money, WebhookTarget

dpay.refunds.create("identyfikator-transakcji")
dpay.refunds.create("identyfikator-transakcji", Money.pln(500), "reklamacja")

# Odpowiedź oznacza przyjęcie zwrotu - wynik przychodzi zdarzeniem refund.succeeded / refund.failed
dpay.refunds.create(
    "identyfikator-transakcji",
    Money.pln(500),
    webhook=WebhookTarget.create(
        "https://twojsklep.pl/webhooks/zwroty", ["refund.succeeded", "refund.failed"]
    ),
)

availability = dpay.refunds.check_availability("identyfikator-transakcji")
if availability.is_available:
    ...

Szczegóły transakcji i banki

transaction = dpay.payments.details("identyfikator-transakcji")
transaction.is_paid
transaction.available_refund_amount.to_decimal()
transaction.refunds

banks = dpay.banks.for_service()

Karty S2S

from dpay import CardData, CardEncryptor, CardPaymentRequest, DeviceInfo

public_key = dpay.cards.public_key()
encrypted = CardEncryptor().encrypt(
    CardData("4111111111111111", "123", "12/28"),
    transaction_id,
    public_key,
)

result = dpay.cards.pay_otp(
    transaction_id,
    CardPaymentRequest.create(device_info).with_encrypted_card_data(encrypted),
)

if result.requires_three_ds_form:
    return HttpResponse(result.three_ds_form_html)
if result.has_dcc_offer:
    offer = result.dcc_offer

Klucz publiczny jest rotowany - pobieraj go przed każdą próbą płatności.

dpay.cards.capture() i dpay.cards.cancel() wysyłają sumę kontrolną operacji wyliczaną przez SDK; capture() przyjmuje też WebhookTarget dla zdarzenia payment.captured.

Obsługa błędów

Wszystkie wyjątki SDK dziedziczą po DPayError.

from dpay import ApiError, DPayError, InvalidRequestError, TransportError

try:
    payment = dpay.payments.register(request)
except InvalidRequestError as error:
    error.field_errors
except ApiError as error:
    error.http_status
    error.error_code  # np. CHECKSUM_REQUIRED, WEBHOOK_URL_INVALID
    error.reason      # szczegół obok kodu, np. https_required
except TransportError:
    ...  # błąd sieci - status płatności nieznany, użyj payments.details()
Wyjątek Kiedy
AuthenticationError 401 - niepoprawny checksum
InvalidRequestError 400, 422
AccessDeniedError 403
NotFoundError 404
RateLimitError 429
ApiServerError 5xx
PaymentRejectedError rejestracja odrzucona przy HTTP 200
CardPaymentError płatność kartą odrzucona przy HTTP 200
SignatureVerificationError niepoprawny podpis IPN albo webhooka
TransportError awaria sieci
DPayValueError niepoprawny argument (dziedziczy też po ValueError)

Konfiguracja

Opcja Typ Opis
service str Nazwa Punktu Płatności z panel.dpay.pl (wymagane)
secret_hash str Klucz Secret Hash (wymagane)
timeout int Timeout HTTP w sekundach (domyślnie 30)
http_client HttpClient Własny transport (proxy, retry, testy)
base_urls dict[str, str] Nadpisanie hostów API

Testowanie integracji

from dpay import DPayClient
from dpay.testing import MockHttpClient

transport = MockHttpClient()
transport.queue_json(200, {"transactionId": "tx-1", "msg": "https://secure.dpay.pl/pay/1"})

dpay = DPayClient(service="test", secret_hash="test", http_client=transport)
payment = dpay.payments.register(request)

assert transport.last_request_body["value"] == "10.50"

Licencja

Apache-2.0

Metadata

Release files for dpay-python-sdk 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dpay-python-sdk 0.2.0
File Size Uploaded Provenance
dpay_python_sdk-0.2.0.tar.gz 87.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for dpay-python-sdk 0.2.0
File Interpreter ABI Platform
dpay_python_sdk-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 157.9 kB

Release files / dpay_python_sdk-0.2.0.tar.gz

Download URL dpay_python_sdk-0.2.0.tar.gz
Size 87.8 kB
Tags Source
SHA-256 checksum
How to use checksums
61b2cb6c840d505ca4c43692f4952e6cb630fddd5590a25a75378d35197c3e85
BLAKE2b-256 checksum
How to use checksums
79457216d9f9ea229644b6173d5e197dfd72523c4182855d7b6ef9f310c253df
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / dpay_python_sdk-0.2.0-py3-none-any.whl

Download URL dpay_python_sdk-0.2.0-py3-none-any.whl
Size 70.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
450d5449fe9c54e29b1cd1a05fa28eb83c9c35e0e89e562a4bdfd8a5bd061493
BLAKE2b-256 checksum
How to use checksums
cbaf7d900e463cdba2d39ac0c50a4ab4a553f698f7fbfa701eb1d7e238e4407c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Security for dpay-python-sdk 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for dpay_python_sdk-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page