Zacznij
Szybki start z pierwszą płatnością w 5 minut
Pięć kroków od założenia konta do działającego webhooka. Każdy krok ma orientacyjny czas i gotowe żądanie w czterech językach. Pracujesz na kluczu testowym sk_test_, więc nic nie kosztuje i nie rusza pieniędzy. Na końcu zamieniasz klucz na produkcyjny i reszta zostaje bez zmian.
Klucz testowy w przykładach
Wklej klucz sk_test_ z panelu (zakładka Deweloperzy), a każdy przykład będzie gotowy do uruchomienia. Klucz zostaje w tej karcie przeglądarki, nie wysyłamy go nigdzie.
Przykłady używają Twojego klucza testowego. Produkcyjnego klucza nie przyjmujemy.
Zanim zaczniesz#
Potrzebujesz adresu e-mail, terminala z curl albo dowolnego języka z klientem HTTP i serwera, który przyjmie żądanie POST z publicznego internetu (do webhooka). Do testów bez publicznego adresu zamiast kroku 4 użyj zdarzenia próbnego i podglądu zdarzeń w panelu.
-
Adres API:
https://oplac.pl/api, wszystkie trasy pod/v1. - Treść żądań i odpowiedzi to JSON. Kwoty w groszach, czas jako znacznik Unix. Reszta konwencji w Konwencje.
-
Nie ma biblioteki SDK do zainstalowania. Przykłady używają wbudowanego
curlw PHP,fetchw Node irequestsw Pythonie.
Krok 1. Klucz testowy (1 minuta)#
Kliknij Zacznij przyjmować płatności, podaj adres e-mail i wpisz kod z maila. Otwiera się panel w piaskownicy. W zakładce Deweloperzy kliknij Utwórz klucz przy kluczu testowym i skopiuj wartość sk_test_... od razu: pełny klucz widać tylko raz, w bazie zostaje wyłącznie jego skrót.
Wklej klucz w karcie Klucz testowy w przykładach na górze tej strony. Od tej chwili każdy przykład w dokumentacji jest gotowy do uruchomienia. Klucz zostaje w Twojej przeglądarce i nigdzie go nie wysyłamy.
Sprawdź klucz pierwszym wywołaniem: GET /v1/account zwraca plan, moduły i to, czy konto przyjmuje płatności.
curl https://oplac.pl/api/v1/account \
-H "Authorization: Bearer sk_test_TWOJ_KLUCZ"
$ch = curl_init('https://oplac.pl/api/v1/account');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_test_TWOJ_KLUCZ'],
CURLOPT_RETURNTRANSFER => true,
]);
$account = json_decode(curl_exec($ch), true);
const res = await fetch('https://oplac.pl/api/v1/account', {
headers: { Authorization: 'Bearer sk_test_TWOJ_KLUCZ' },
});
const account = await res.json();
import requests
res = requests.get(
"https://oplac.pl/api/v1/account",
headers={"Authorization": "Bearer sk_test_TWOJ_KLUCZ"},
)
account = res.json()
{
"id": "acct_Ab12Cd34Ef56Gh",
"object": "account",
"name": "Sklep Testowy",
"plan": "demo",
"modules": ["accept", "api", "plan"],
"currencies": ["PLN"],
"charges_enabled": true,
"provider_connected": false,
"webhook_configured": false,
"livemode": false,
"created": 1760000000
}
Krok 2. Utwórz płatność (1 minuta)#
Jedno żądanie POST /v1/payments. Wymagane są tylko amount w groszach i currency. Podaj też return_url, na który klient wróci po zapłacie, i metadata z numerem zamówienia, żeby webhook dało się dopasować do Twojej bazy. Nagłówek Idempotency-Key sprawia, że ponowione żądanie (timeout, błąd sieci) nie utworzy drugiej płatności.
W odpowiedzi dostajesz obiekt payment ze statusem pending i polem checkout_url. To hostowana strona płatności: w piaskownicy u nas, z testowymi kodami BLIK, w produkcji u operatora płatności Twojego konta.
curl https://oplac.pl/api/v1/payments \
-H "Authorization: Bearer sk_test_TWOJ_KLUCZ" \
-H "Idempotency-Key: 5f0c2a4e-7d1b-4e8a-9b2f-1c3d5e7f9a0b" \
-H "Content-Type: application/json" \
-d '{
"amount": 12900,
"currency": "PLN",
"description": "Zamówienie 10482",
"return_url": "https://sklep.example.com/powrot",
"metadata": {"order_id": "10482"}
}'
$ch = curl_init('https://oplac.pl/api/v1/payments');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer sk_test_TWOJ_KLUCZ',
'Idempotency-Key: 5f0c2a4e-7d1b-4e8a-9b2f-1c3d5e7f9a0b',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'amount' => 12900,
'currency' => 'PLN',
'description' => 'Zamówienie 10482',
'return_url' => 'https://sklep.example.com/powrot',
'metadata' => ['order_id' => '10482'],
]),
CURLOPT_RETURNTRANSFER => true,
]);
$payment = json_decode(curl_exec($ch), true);
header('Location: ' . $payment['checkout_url'], true, 302);
const res = await fetch('https://oplac.pl/api/v1/payments', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_test_TWOJ_KLUCZ',
'Idempotency-Key': '5f0c2a4e-7d1b-4e8a-9b2f-1c3d5e7f9a0b',
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 12900,
currency: 'PLN',
description: 'Zamówienie 10482',
return_url: 'https://sklep.example.com/powrot',
metadata: { order_id: '10482' },
}),
});
const payment = await res.json();
reply.redirect(payment.checkout_url);
import requests
res = requests.post(
"https://oplac.pl/api/v1/payments",
headers={
"Authorization": "Bearer sk_test_TWOJ_KLUCZ",
"Idempotency-Key": "5f0c2a4e-7d1b-4e8a-9b2f-1c3d5e7f9a0b",
},
json={
"amount": 12900,
"currency": "PLN",
"description": "Zamówienie 10482",
"return_url": "https://sklep.example.com/powrot",
"metadata": {"order_id": "10482"},
},
)
payment = res.json()
return redirect(payment["checkout_url"], code=302)
HTTP/1.1 201 Created
X-Request-Id: req_7mK2pQ9rT4vX1bN8cD3fG6hJ
{
"id": "pay_Ab12Cd34Ef56Gh",
"object": "payment",
"amount": 12900,
"currency": "PLN",
"status": "pending",
"description": "Zamówienie 10482",
"checkout_url": "https://oplac.pl/p/Ab12Cd34Ef56Gh",
"return_url": "https://sklep.example.com/powrot",
"metadata": {"order_id": "10482"},
"livemode": false,
"expires_at": 1760086400,
"created": 1760000000
}
Krok 3. Zapłać w piaskownicy (1 minuta)#
Otwórz checkout_url z odpowiedzi w przeglądarce. Strona piaskownicy wygląda jak strona płatności klienta: wybierz BLIK i wpisz kod 777 123. Płatność przechodzi w status succeeded, a przeglądarka wraca na return_url z parametrem ?payment=pay_.... Kod 777 000 daje odmowę (failed, blik_rejected), 777 408 zły kod. Przy karcie zamiast numeru są dwa przyciski wyniku.
Nie chcesz klikać? Ten sam wynik ustawisz żądaniem POST /v1/test_helpers/payments/{id}/confirm. Uruchamia dokładnie te same zdarzenia i webhooki co zapłata na stronie.
Po powrocie klienta nigdy nie ufaj samemu adresowi: potwierdź stan żądaniem GET /v1/payments/{id} (status succeeded), a zamówienie oznaczaj dopiero po zdarzeniu z webhooka z kroku 4.
curl https://oplac.pl/api/v1/test_helpers/payments/pay_Ab12Cd34Ef56Gh/confirm \
-H "Authorization: Bearer sk_test_TWOJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{"blik_code": "777123"}'
$ch = curl_init('https://oplac.pl/api/v1/test_helpers/payments/pay_Ab12Cd34Ef56Gh/confirm');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_test_TWOJ_KLUCZ', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['blik_code' => '777123']),
CURLOPT_RETURNTRANSFER => true,
]);
$payment = json_decode(curl_exec($ch), true);
const res = await fetch('https://oplac.pl/api/v1/test_helpers/payments/pay_Ab12Cd34Ef56Gh/confirm', {
method: 'POST',
headers: { Authorization: 'Bearer sk_test_TWOJ_KLUCZ', 'Content-Type': 'application/json' },
body: JSON.stringify({ blik_code: '777123' }),
});
const payment = await res.json();
import requests
res = requests.post(
"https://oplac.pl/api/v1/test_helpers/payments/pay_Ab12Cd34Ef56Gh/confirm",
headers={"Authorization": "Bearer sk_test_TWOJ_KLUCZ"},
json={"blik_code": "777123"},
)
payment = res.json()
HTTP/1.1 200 OK
{
"id": "pay_Ab12Cd34Ef56Gh",
"object": "payment",
"amount": 12900,
"currency": "PLN",
"status": "succeeded",
"method": "blik",
"paid_at": 1760000040,
"metadata": {"order_id": "10482"},
"livemode": false
}
Krok 4. Odbierz webhook (2 minuty)#
Zapisz adres webhooka żądaniem POST /v1/webhook_endpoints (albo w panelu, w zakładce Deweloperzy). Odpowiedź zawiera sekret whsec_, który wraca tylko raz: zapisz go w konfiguracji serwera. Od tej chwili każde zdarzenie z listy enabled_events przychodzi jako POST z JSON obiektu event i nagłówkiem Oplac-Signature.
Twój odbiorca robi trzy rzeczy: sprawdza podpis HMAC SHA-256 (sekret, ciąg <t>.<surowa treść>), odpowiada 200 w ciągu 10 sekund i obsługuje zdarzenie idempotentnie po id, bo to samo zdarzenie może przyjść więcej niż raz. Gotowe funkcje w PHP, Node i Pythonie są w przewodniku Webhooki.
Przy payment.succeeded odczytaj data.object.metadata.order_id i oznacz zamówienie jako opłacone. Teraz powtórz krok 3: webhook powinien dotrzeć w ciągu sekundy, a historia dostarczeń jest w panelu i pod GET /v1/events.
curl https://oplac.pl/api/v1/webhook_endpoints \
-H "Authorization: Bearer sk_test_TWOJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{
"url": "https://sklep.example.com/oplac/webhook",
"enabled_events": ["payment.succeeded", "payment.failed", "refund.succeeded"]
}'
$ch = curl_init('https://oplac.pl/api/v1/webhook_endpoints');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer sk_test_TWOJ_KLUCZ', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://sklep.example.com/oplac/webhook',
'enabled_events' => ['payment.succeeded', 'payment.failed', 'refund.succeeded'],
]),
CURLOPT_RETURNTRANSFER => true,
]);
$endpoint = json_decode(curl_exec($ch), true);
// $endpoint['secret'] zapisz w konfiguracji: nie wraca w GET
const res = await fetch('https://oplac.pl/api/v1/webhook_endpoints', {
method: 'POST',
headers: { Authorization: 'Bearer sk_test_TWOJ_KLUCZ', 'Content-Type': 'application/json' },
body: JSON.stringify({
url: 'https://sklep.example.com/oplac/webhook',
enabled_events: ['payment.succeeded', 'payment.failed', 'refund.succeeded'],
}),
});
const endpoint = await res.json();
// endpoint.secret zapisz w konfiguracji: nie wraca w GET
import requests
res = requests.post(
"https://oplac.pl/api/v1/webhook_endpoints",
headers={"Authorization": "Bearer sk_test_TWOJ_KLUCZ"},
json={
"url": "https://sklep.example.com/oplac/webhook",
"enabled_events": ["payment.succeeded", "payment.failed", "refund.succeeded"],
},
)
endpoint = res.json()
# endpoint["secret"] zapisz w konfiguracji: nie wraca w GET
HTTP/1.1 201 Created
{
"id": "we_test_Ab12Cd34Ef56Gh",
"object": "webhook_endpoint",
"url": "https://sklep.example.com/oplac/webhook",
"enabled_events": ["payment.succeeded", "payment.failed", "refund.succeeded"],
"status": "enabled",
"secret": "whsec_Ab12Cd34Ef56Gh78Ij90Kl12Mn34Op56Qr78St90",
"livemode": false,
"created": 1760000000
}
Krok 5. Produkcja (bez zmian w kodzie)#
Produkcję włączasz sam w panelu: wybierasz plan z cennika, w zakładce Deweloperzy wklejasz klucz operatora płatności swojego konta (sekcja Dostawca) i tworzysz klucz sk_live_. Zapisz też produkcyjny adres webhooka, bo adres i sekret są osobne dla każdego trybu.
W kodzie zmieniasz wyłącznie klucz. Trasy, pola, zdarzenia i obiekty są identyczne, a livemode: true w odpowiedziach mówi, że pieniądze są prawdziwe. Obiekty testowe i produkcyjne nie widzą się nawzajem: sk_live_ nie znajdzie płatności pay_ utworzonej w piaskownicy.
- Strona płatności w produkcji działa u operatora płatności Twojego konta; Oplac nie przyjmuje ani nie przechowuje środków.
-
Pomocnicy testowi (
/v1/test_helpers) z kluczemsk_live_zwracają403 test_helpers_live_mode. - Limit zapytań rośnie z 25 do 100 na sekundę. Szczegóły w Limity zapytań.
Co dalej#
-
Hostowana strona płatności: pełny przepływ z
checkout_url,return_urli wygasaniem. - Webhooki: weryfikacja podpisu w PHP, Node i Pythonie, ponowienia, obsługa idempotentna.
-
Błędy: jeden obiekt błędu i katalog kodów, do którego prowadzi
doc_url. - Referencja API: każdy zasób i każda operacja.
Powiązane
Klucz testowy dostajesz od razu po zapisie
Adres e-mail, kod z maila i piaskownica z kodami BLIK. Produkcję włączasz sam w panelu: plan, dostawca płatności, klucz sk_live_. Wolisz, żeby wdrożenie w sklepie zrobił nasz zespół? To też jest za darmo.