Przejdź do treści
Oplac

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.

Tryb testowy

Jak to działa#

  1. 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).
  2. 02Wyślij żądanie z nagłówkiem Idempotency-Key. Serwer wykona je i zapisze kod HTTP oraz treść odpowiedzi pod tym kluczem.
  3. 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: true i Original-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")
Odpowiedź
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 żądaniaPowtó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-2 to 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_key z 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.

Zamów integrację za darmo

BLIK, karty, subskrypcje i KSeF w jednym panelu.