Przejdź do głównej zawartości

Dynamic Currency Conversion (DCC)

DCC to mechanizm pozwalający posiadaczowi karty zagranicznej zapłacić w walucie własnej karty zamiast w walucie sklepu. Bramka oblicza ofertę z aktualnym kursem (zawierającym marżę sieci kartowej, np. 6% Mastercard) i przekazuje ją do prezentacji klientowi przed zatwierdzeniem płatności.

DCC jest dostępne wyłącznie w integracji Server-to-Server (S2S). Integracja przez iframe SDK obsługuje DCC automatycznie wewnątrz ramki - merchant nie musi nic dodatkowo implementować.

Aktywacja DCC

DCC wymaga osobnej aktywacji per serwis płatniczy. Jeśli chcesz włączyć ten mechanizm na swoim koncie, skontaktuj się z dpay.

Kiedy klient zobaczy ofertę DCC?

Oferta DCC pojawia się dopiero po spełnieniu wszystkich poniższych warunków:

  1. Serwis płatniczy ma aktywne DCC w panelu dpay.
  2. Karta klienta jest wydana w walucie innej niż waluta sklepu.
  3. Bramka stwierdziła, że transakcja kwalifikuje się do konwersji.

Jeśli któryś z warunków nie jest spełniony, transakcja przebiega standardowo - frontend dostaje od razu SUCCESS lub FORM (3DS), tak jak dotychczas. Nie musisz nic zmieniać w obsłudze "klasycznego" flow kart.

Schemat działania (state machine)

1. POST /pay/card-otp (encryptedCardData)

├── SUCCESS ──────────────────────────────────► koniec (transakcja zaksięgowana)

├── FORM (3DS) ──► render HTML ──► powrót z ACS
│ POST /pay/card-otp (threeDsConfirmed: true) ──► SUCCESS

└── DCC_OFFER ──► UI wyboru waluty ──► decyzja klienta
POST /pay/card-otp (dccDecision: "accept" | "reject")

├── SUCCESS ───────────────────────────────► koniec

└── FORM (3DS po DCC) ──► render HTML ──► powrót z ACS
POST /pay/card-otp (threeDsConfirmed: true) ──► SUCCESS

Pierwsze wywołanie - rozpoznanie odpowiedzi

Pierwsze wywołanie POST /api/v1_0/cards/payment/{transactionId}/pay/card-otp jest takie samo jak dotychczas. W odpowiedzi może pojawić się nowy redirectType:

{
"success": true,
"status": "success",
"message": {
"redirectType": "DCC_OFFER",
"redirectText": null,
"dccOffer": {
"currencyConversionId": "00509166251006151007",
"originalAmount": 3.00,
"originalCurrency": "EUR",
"convertedAmount": 13.52,
"convertedCurrency": "PLN",
"exchangeRate": 4.507968,
"validUntil": "2026-05-03T17:40:07+00:00",
"declarationText": "Make sure you understand the costs of currency conversions...",
"markup": [
{ "rate": 6.0, "additionalInfo": "Mastercard" }
],
"europeanEconomicArea": true
}
}
}

Pola dccOffer

PoleTypOpis
currencyConversionIdstringIdentyfikator oferty (przyda się przy korelacji ze wsparciem dpay)
originalAmountfloatKwota w walucie sklepu
originalCurrencystring (ISO 4217)Waluta sklepu
convertedAmountfloatKwota w walucie karty (z wbudowaną marżą)
convertedCurrencystring (ISO 4217)Waluta karty
exchangeRatefloatKurs konwersji (zawiera marżę)
validUntilstring (ISO 8601)Moment wygaśnięcia oferty
declarationTextstringTekst regulacyjny PSD2 - pokaż klientowi dosłownie, nie tłumacz
markuparrayTablica obiektów { rate, additionalInfo } - marża sieci kartowej
europeanEconomicAreaboolCzy karta wydana w EEA

Wymagania UI - co musisz pokazać klientowi

Prezentacja oferty DCC jest regulowana przez PSD2 i CBPR2. Niezbędne elementy:

  1. Dwie kwoty side-by-side - originalAmount + originalCurrency vs convertedAmount + convertedCurrency.
  2. declarationText w pełnej formie - tekst regulacyjny przekazany przez bramkę. Nie skracaj i nie tłumacz.
  3. Marża sieci kartowej (markup) - np. "Konwersja zawiera marżę 6% (Mastercard)". Pole jest tablicą - obsłuż wiele wpisów.
  4. Countdown do validUntil - po wygaśnięciu zablokuj przyciski lub wymuś powrót na ekran metody płatności.
  5. Dwa przyciski wyboru:
    • "Zapłać w {originalCurrency}" → wyśle dccDecision: "reject"
    • "Zapłać w {convertedCurrency}" → wyśle dccDecision: "accept"
Nie podejmuj decyzji za klienta

Decyzję musi świadomie wybrać posiadacz karty - regulator wymaga, aby klient widział obie kwoty i jawnie wybrał walutę przed dokończeniem płatności.

Drugie wywołanie - wysłanie decyzji

Po kliknięciu wybranej waluty wykonaj ponownie ten sam endpoint z dotychczasowym payloadem + nowe pole dccDecision:

POST /api/v1_0/cards/payment/{transactionId}/pay/card-otp
Content-Type: application/json

{
"channelId": 31,
"encryptedCardData": "<ten sam payload co przy 1. wywołaniu>",
"deviceInfo": { ... },
"email": "...",
"dccDecision": "accept"
}
PoleTypWartościOpis
dccDecisionstring"accept" / "reject"Decyzja posiadacza karty
encryptedCardDatastring-Ten sam zaszyfrowany payload co w pierwszym wywołaniu
encryptedCardData między wywołaniami

Frontend trzyma encryptedCardData w pamięci między 1. a 2. wywołaniem - dokładnie tak samo jak przy challenge 3DS, gdzie po powrocie z ACS wysyłasz threeDsConfirmed: true z tym samym zaszyfrowanym payloadem.

Możliwe odpowiedzi po decyzji

  • SUCCESS - transakcja zaksięgowana (najczęstszy przypadek)
  • FORM - bramka wymaga jeszcze 3DS challenge po DCC. Wyrenderuj HTML, po powrocie z ACS wywołaj endpoint z threeDsConfirmed: true + ten sam encryptedCardData

Błędy biznesowe DCC

Komunikat (message)HTTPPrzyczynaAkcja frontu
DCC_OFFER_EXPIRED400Decyzja wysłana po validUntilPokaż "Oferta wygasła, spróbuj ponownie", wróć do ekranu metody płatności (nowy flow od początku)
INVALID_FLOW_STATE400dccDecision wysłane bez wcześniejszej oferty (np. na zfinalizowanej transakcji)Błąd techniczny - log + redirect na error screen
DCC_PROVIDER_ERROR502Błąd procesora kart przy aktualizacji decyzji DCCBłąd techniczny - error screen

Pełny przykład - obsługa wszystkich gałęzi (Node.js)

async function handleCardPaymentResponse(response, retryWith) {
const { redirectType, redirectText, dccOffer } = response.message;

switch (redirectType) {
case 'SUCCESS':
window.location.href = '/sukces';
return;

case 'FORM':
renderHtmlAndAutoSubmit(atob(redirectText));
return;

case 'DCC_OFFER':
const decision = await showDccOfferUI(dccOffer);
const next = await retryWith({ dccDecision: decision });
return handleCardPaymentResponse(next, retryWith);

case 'URL':
window.location.href = redirectText;
return;

default:
throw new Error(`Unknown redirectType: ${redirectType}`);
}
}

Testowanie

Pełna lista testowych PAN-ów do scenariuszy DCC dostępna na stronie Środowisko testowe. W skrócie:

PANScenariusz
5346930000008110Standardowa oferta DCC (EUR→PLN, ważność 30 minut)
5346930000008128Karta nie kwalifikuje się do DCC - frontend dostaje od razu SUCCESS
5346930000008136Oferta DCC z ważnością 60 sekund - test wygaśnięcia
5346930000008144Oferta DCC z dodatkowym 3DS po decyzji - kaskada DCC_OFFERFORMSUCCESS

Co się NIE zmienia

  • Standardowy success path (karta lokalna, bez DCC, bez 3DS) - bez zmian, działa jak dotychczas
  • 3DS challenge bez DCC - bez zmian, ten sam flow, ten sam shape FORM
  • encryptedCardData, deviceInfo, email, channelId - bez zmian
  • Endpoint, autoryzacja, format kluczy RSA - bez zmian