Podstawy API
Idempotencja
Sieć bywa zawodna: żądanie mogło dojść, a odpowiedź nie. Nagłówek Idempotency-Key pozwala bezpiecznie powtórzyć POST albo DELETE: powtórka z tym samym kluczem i tą samą treścią w ciągu 24 godzin zwraca zapisany wynik pierwszego żądania zamiast tworzyć drugą płatność czy drugi zwrot.
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.
Jak to działa#
- 01Przed wysłaniem żądania nadaj mu klucz: dowolny tekst od 1 do 255 znaków, najlepiej UUID v4 albo identyfikator Twojej operacji (
zamowienie-10482,zwrot-10482-1). - 02Wyślij żądanie z nagłówkiem
Idempotency-Key. Serwer wykona je i zapisze kod HTTP oraz treść odpowiedzi pod tym kluczem. - 03Jeśli nie dostałeś odpowiedzi (timeout, zerwane połączenie), wyślij dokładnie to samo żądanie z tym samym kluczem. Dostaniesz zapisany wynik z nagłówkami
Idempotent-Replayed: trueiOriginal-Request: req_....
Klucz jest osobny dla konta i trybu (sk_test_, sk_live_), a wpisy starsze niż doba są kasowane. Przy GET nagłówek jest ignorowany, bo odczyt nic nie zmienia.
# pierwsze i powtórzone żądanie są identyczne; drugie wraca z Idempotent-Replayed: true
curl -i https://oplac.pl/api/v1/payments \
-H "Authorization: Bearer sk_test_TWOJ_KLUCZ" \
-H "Idempotency-Key: zamowienie-10482" \
-H "Content-Type: application/json" \
-d '{"amount": 12900, "currency": "PLN", "metadata": {"order_id": "10482"}}'
function utworzPlatnosc(array $dane, string $kluczIdempotencji): array
{
for ($proba = 1; $proba <= 3; $proba++) {
$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: ' . $kluczIdempotencji,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode($dane),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
]);
$odp = curl_exec($ch);
$kod = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($odp !== false && $kod < 500 && $kod !== 409 && $kod !== 429) {
return json_decode($odp, true);
}
usleep(500000 * $proba); // ten sam klucz: powtórka nie utworzy drugiej płatności
}
throw new RuntimeException('Brak odpowiedzi API');
}
$payment = utworzPlatnosc(['amount' => 12900, 'currency' => 'PLN'], 'zamowienie-10482');
async function utworzPlatnosc(dane, kluczIdempotencji) {
for (let proba = 1; proba <= 3; proba++) {
try {
const res = await fetch('https://oplac.pl/api/v1/payments', {
method: 'POST',
headers: {
Authorization: 'Bearer sk_test_TWOJ_KLUCZ',
'Idempotency-Key': kluczIdempotencji,
'Content-Type': 'application/json',
},
body: JSON.stringify(dane),
signal: AbortSignal.timeout(15000),
});
if (res.status < 500 && res.status !== 409 && res.status !== 429) return res.json();
} catch (e) {
// timeout albo zerwane połączenie: ten sam klucz, powtórka nie utworzy drugiej płatności
}
await new Promise((r) => setTimeout(r, 500 * proba));
}
throw new Error('Brak odpowiedzi API');
}
const payment = await utworzPlatnosc({ amount: 12900, currency: 'PLN' }, 'zamowienie-10482');
import time
import requests
def utworz_platnosc(dane, klucz_idempotencji):
for proba in range(1, 4):
try:
res = requests.post(
"https://oplac.pl/api/v1/payments",
headers={"Authorization": "Bearer sk_test_TWOJ_KLUCZ", "Idempotency-Key": klucz_idempotencji},
json=dane,
timeout=15,
)
if res.status_code < 500 and res.status_code not in (409, 429):
return res.json()
except requests.RequestException:
pass # ten sam klucz: powtórka nie utworzy drugiej płatności
time.sleep(0.5 * proba)
raise RuntimeError("Brak odpowiedzi API")
payment = utworz_platnosc({"amount": 12900, "currency": "PLN"}, "zamowienie-10482")
HTTP/1.1 201 Created
Idempotent-Replayed: true
Original-Request: req_7mK2pQ9rT4vX1bN8cD3fG6hJ
X-Request-Id: req_2aB4cD6eF8gH0iJ2kL4mN6oP
{
"id": "pay_Ab12Cd34Ef56Gh",
"object": "payment",
"amount": 12900,
"currency": "PLN",
"status": "pending",
"livemode": false
}
Co jest zapamiętywane, a co nie#
| Odpowiedź pierwszego żądania | Powtórka z tym samym kluczem |
|---|---|
| 2xx (utworzono, zmieniono, usunięto) | zapisany wynik, Idempotent-Replayed: true |
| 402, 403, 404, 422 (stan konta albo obiektu) | zapisany wynik, żeby powtórka nie dała innej odpowiedzi niż oryginał |
| 400 (walidacja), 401 (klucz), 429 (limit) | nie zapisane: poprawione żądanie może iść z tym samym kluczem |
| 5xx (awaria) | nie zapisane: ponów z tym samym kluczem, żądanie wykona się normalnie |
Ten sam klucz z inną treścią albo na innej trasie to 400 idempotency_key_reused: nowy zamiar (nowa płatność, nowy zwrot) ma mieć nowy klucz. Dwa równoległe żądania z jednym kluczem wykonują się po kolei: drugie czeka na wynik pierwszego i go dostaje; gdy pierwsze trwa dłużej niż blokada, drugie dostaje 409 idempotency_key_in_use i ma ponowić za chwilę.
Dobre praktyki#
- Klucz nadawaj po stronie serwera i zapisuj razem z operacją (np. w wierszu zamówienia), żeby po restarcie procesu dało się ponowić dokładnie to samo żądanie.
-
Dla operacji powtarzalnych buduj klucz z Twojego identyfikatora i numeru próby biznesowej:
zwrot-10482-1,zwrot-10482-2to dwa różne zwroty. - Nie używaj jednego klucza do różnych tras ani do różnych kont. Klucz działa w obrębie konta i trybu.
-
Każde zdarzenie webhooka ma pole
request.idempotency_keyz kluczem żądania, które je wywołało; ułatwia to dopasowanie zdarzeń do operacji w Twoim systemie.
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.