Karetní data
Karetní platby probíhají prostřednictvím zabezpečených vstupních komponent — Secure Fields. Tyto komponenty zajišťují bezpečné zadávání, validaci a ochranu karetních údajů. Hostitelská aplikace nikdy nemá přístup k citlivým údajům v nechráněné podobě.
Secure Fields
Knihovna poskytuje čtyři komponenty pro zadávání údajů v platebním formuláři:
SecurePanField
Pole pro zadání čísla karty (PAN — Primary Account Number).
- Přijímá pouze číslice
- Automaticky detekuje karetní síť (Visa, Mastercard)
- Formátuje číslo karty s mezerami dle karetní sítě (např.
4111 1111 1111 1111) - Omezuje délku vstupu podle detekované karetní sítě
- Validuje číslo karty pomocí Luhnova algoritmu
- Zpřístupňuje vlastnost
last4— poslední 4 číslice karty (bezpečné pro zobrazení v UI)
import SwiftUI
import ComgateSDK
@StateObject private var panState = SecurePanFieldState()
var body: some View {
SecurePanField(state: panState)
}
SecureExpiryField
Pole pro zadání data expirace karty.
- Přijímá pouze číslice
- Automaticky formátuje vstup ve tvaru MM/YY
- Validuje měsíc (1–12) a kontroluje, zda karta není expirovaná
@StateObject private var expiryState = SecureExpiryFieldState()
SecureExpiryField(state: expiryState)
SecureCvvField
Pole pro zadání bezpečnostního kódu karty (CVV/CVC).
- Přijímá pouze číslice
- Délka je 3 číslice
- Validuje minimální požadovanou délku
@StateObject private var cvvState = SecureCvvFieldState()
SecureCvvField(state: cvvState)
SecureFullNameField
Pole pro zadání jména a příjmení plátce.
- Přijímá textový vstup (včetně mezer)
- Je validní při neprázdné hodnotě
- Po připojení k
ComgateSecureSessionse jeho hodnota automaticky použije jakofullName
@StateObject private var fullNameState = SecureFullNameFieldState()
SecureFullNameField(state: fullNameState)
.onAppear { fullNameState.attachTo(session) }
.onDisappear { fullNameState.detachFrom(session) }
PaymentParams.fullName má vždy přednost před hodnotou z SecureFullNameField — pokud je fullName v PaymentParams vyplněný, pole SecureFullNameField není vyžadováno. Pokud je fullName prázdný, použije se hodnota z připojeného pole. Je-li prázdné obojí, platba selže s chybou MISSING_CARDHOLDER_NAME.
Společné vlastnosti
Všechny stavy Secure Fields (SecurePanFieldState, SecureExpiryFieldState, SecureCvvFieldState, SecureFullNameFieldState) sdílejí tyto vlastnosti a metody:
| Vlastnost / Metoda | Typ | Popis |
|---|---|---|
isValid | Bool | Aktuální stav validace pole (read-only, @Published). |
errorText | String? | Aktuální chybová zpráva — lokalizovaná dle translation. |
translation | Translation | Překlady použité pro placeholder a chybové zprávy. Nastavte na session.translation pro shodu se session. |
onFieldError | ((String?) -> Void)? | Callback volaný při změně chybového stavu. |
clear() | — | Vymaže obsah pole a resetuje validaci. |
requestFocus() | @discardableResult Bool | Programaticky zaměří pole. |
Labely vstupních polí
Každé pole má label nad vstupem. Výchozí text labelu se bere z translation. Pro vlastní label nastavte odpovídající klíč v Translation:
panState.translation = Translation(panLabel: "Číslo platební karty")
SecureCardDataCollector
SecureCardDataCollector propojuje tři samostatná Secure Fields a sleduje jejich celkový validační stav. Je vyžadován pro zpracování platby. Vytvořte ho jednou pomocí factory funkce secureCardDataCollector(pan:expiry:cvv:) a uchovejte v @StateObject ObservableObject obalu, aby přežil rerendery view:
@MainActor
final class CollectorHolder: ObservableObject {
let collector: SecureCardDataCollector
init(pan: SecurePanFieldState, expiry: SecureExpiryFieldState, cvv: SecureCvvFieldState) {
self.collector = secureCardDataCollector(pan: pan, expiry: expiry, cvv: cvv)
}
}
struct PaymentForm: View {
@StateObject private var panState: SecurePanFieldState
@StateObject private var expiryState: SecureExpiryFieldState
@StateObject private var cvvState: SecureCvvFieldState
@StateObject private var holder: CollectorHolder
init() {
let pan = SecurePanFieldState()
let expiry = SecureExpiryFieldState()
let cvv = SecureCvvFieldState()
_panState = StateObject(wrappedValue: pan)
_expiryState = StateObject(wrappedValue: expiry)
_cvvState = StateObject(wrappedValue: cvv)
_holder = StateObject(wrappedValue: CollectorHolder(pan: pan, expiry: expiry, cvv: cvv))
}
private var collector: SecureCardDataCollector { holder.collector }
}
Nepoužívejte var collector: SecureCardDataCollector { secureCardDataCollector(...) } jako computed property — vytvářel by nový collector při každém renderu, čímž by se ztratil focus advance i validační stav.
| Vlastnost / Metoda | Typ | Popis |
|---|---|---|
isValid | Bool | true pokud jsou všechna tři pole validní. |
autoAdvanceFocus | Bool | Zapne/vypne automatický přesun focusu mezi poli (PAN → Expirace → CVV). Výchozí: true. |
onValidationChanged | ((Bool) -> Void)? | Callback volaný při změně validačního stavu kteréhokoliv pole. |
Předávání focusu mezi poli
Kolektor ve výchozím stavu automaticky předává focus mezi poli v pořadí:
PAN → Expirace → CVV
K přesunu dojde ve chvíli, kdy je aktuální pole kompletně vyplněné a validní. Pokud chcete focus řídit sami, vypněte automatiku:
let collector = secureCardDataCollector(pan: panState, expiry: expiryState, cvv: cvvState)
collector.autoAdvanceFocus = false
SecurePayButton
SecurePayButton je předpřipravené platební tlačítko, které se automaticky integruje se session a kolektorem:
- Automaticky se aktivuje/deaktivuje podle validačního stavu karetních polí
- Tlačítko zůstane deaktivované, dokud není session úspěšně inicializována (
session.state == .ready) - Po kliknutí zahájí zpracování platby prostřednictvím
ComgateSecureSession - Během zpracování zobrazuje shimmer animaci (lze vypnout v
PayButtonStyle) - Vrací výsledek platby přes
onResultcallback
Nastavení tlačítka
SecurePayButton(
session: session,
collector: collector,
paymentParams: {
try! PaymentParams(
email: "zakaznik@example.com",
price: 100,
curr: "CZK",
country: "CZ",
label: "Název platby",
refId: "ref-123",
fullName: "Jan Novák",
billingAddrCity: "Hradec Králové",
billingAddrStreet: "Jiráskova 115",
billingAddrPostalCode: "50304",
billingAddrCountry: "CZ"
)
},
onResult: { result in
// Zpracování výsledku platby
}
)
Parametr paymentParams je closure, která je zavolána v okamžiku kliknutí na tlačítko. Díky tomu lze dynamicky číst aktuální hodnoty z UI (např. částku z textového pole).
PaymentParams
| Parametr | Typ | Povinný | Popis |
|---|---|---|---|
email | String | Ano | E-mailová adresa plátce. |
price | Int | Ano | Částka platby v haléřích/centech (např. 10000 = 100,00 CZK). Musí být kladné celé číslo. |
curr | String | Ano | Kód měny — ISO 4217 (např. "CZK", "EUR"). Viz PaymentParams.supportedCurrencies pro úplný seznam. |
label | String | Ano | Krátký popis produktu (1–16 znaků). |
refId | String | Ano | Variabilní symbol nebo číslo objednávky (vaše interní ID). |
fullName | String | Podmíněně | Jméno a příjmení plátce. Pokud je neprázdný, má přednost před hodnotou z SecureFullNameField. Pokud je prázdný, použije se hodnota z připojeného SecureFullNameField (tehdy povinného). |
country | String | Ne | Kód země dle ISO 3166-1 alpha-2 (např. "CZ", "SK"). Výchozí: "CZ". |
account | String? | Ne | Identifikátor bankovního účtu klienta v systému Comgate. |
name | String? | Ne | Identifikátor produktu (zobrazí se v denním CSV jako „Produkt"). |
preauth | Bool? | Ne | Označí platbu jako předautorizaci. Nelze kombinovat s initRecurring = true. |
initRecurring | Bool? | Ne | Označí platbu jako první v sérii opakovaných plateb. Nelze kombinovat s preauth = true. |
billingAddrCity | String? | Ne | Fakturační adresa — město. |
billingAddrStreet | String? | Ne | Fakturační adresa — ulice. |
billingAddrPostalCode | String? | Ne | Fakturační adresa — PSČ. |
billingAddrCountry | String? | Ne | Fakturační adresa — kód země (ISO 3166-1 alpha-2). |
delivery | String? | Ne | Způsob doručení ("HOME_DELIVERY", "PICKUP", "ELECTRONIC_DELIVERY"). |
homeDeliveryCity | String? | Ne | Doručovací adresa — město (jen při delivery = "HOME_DELIVERY"). |
homeDeliveryStreet | String? | Ne | Doručovací adresa — ulice (jen při delivery = "HOME_DELIVERY"). |
homeDeliveryPostalCode | String? | Ne | Doručovací adresa — PSČ (jen při delivery = "HOME_DELIVERY"). |
homeDeliveryCountry | String? | Ne | Doručovací adresa — kód země (jen při delivery = "HOME_DELIVERY"). |
category | String? | Ne | Kategorie produktu ("PHYSICAL_GOODS_ONLY", "OTHER"). |
require3ds | Bool? | Ne | Pouze v dev režimu (devMode = true). Vynucuje typ 3DS průběhu. V produkci ignorováno. |
errorReason | ErrorReason? | Ne | Pouze v dev režimu. Simuluje konkrétní důvod zamítnutí. V produkci ignorováno. Viz ErrorReason. |
Konstruktor PaymentParams vyhodí ComgateError.invalidPrice, pokud není price kladné celé číslo, nebo ComgateError.conflictingPaymentOptions při kombinaci initRecurring = true a preauth = true.
Podporované měny a země
Podporované měny (PaymentParams.supportedCurrencies)
PaymentParams.supportedCurrencies vrací seznam ISO 4217 kódů měn přijímaných platební bránou Comgate. Tento seznam lze využít například k naplnění výběru měny v UI.
BGN, CHF, CZK, DKK, EUR, GBP, HUF, NOK, PLN, RON, SEK, USD
Podporované země (PaymentParams.supportedCountries)
PaymentParams.supportedCountries vrací seznam ISO 3166-1 alpha-2 kódů zemí přijímaných pro parametr country. Hodnota "ALL" reprezentuje žádné omezení na konkrétní zemi.
ALL, AT, BE, CY, CZ, DE, EE, EL, ES, FI, FR, GB, HR, HU, IE, IT, LT, LU, LV, MT, NL, NO, PL, PT, RO, SE, SI, SK, US
Příklad použití pro naplnění výběru měny a země
// Dostupné měny pro výběr v UI
let currencies = PaymentParams.supportedCurrencies // ["BGN", "CHF", "CZK", ...]
// Dostupné země pro výběr v UI
let countries = PaymentParams.supportedCountries // ["ALL", "AT", "BE", "CY", "CZ", ...]
let params = try PaymentParams(
email: "zakaznik@example.com",
price: 10000,
curr: selectedCurrency,
country: selectedCountry,
label: "Objednávka",
refId: "order-123",
fullName: "Jan Novák"
)
Opakované platby a předautorizace
initRecurring
Parametr initRecurring = true označí platbu jako první (iniciační) transakci v sérii opakovaných plateb. Banka tím dostane signál, že v budoucnu budou probíhat další automatické platby (např. předplatné, pravidelné poplatky).
let params = try PaymentParams(
email: "zakaznik@example.com",
price: 9900,
curr: "CZK",
label: "Předplatné",
refId: "sub-001",
fullName: "Jan Novák",
initRecurring: true
)
Parametr initRecurring nelze kombinovat s preauth = true.
preauth
Parametr preauth = true označí platbu jako předautorizaci — banka dočasně rezervuje požadovanou částku na kartě plátce, ale prostředky nejsou ihned strženy. K zachycení (stržení) dochází až při potvrzení transakce na straně backendu.
let params = try PaymentParams(
email: "zakaznik@example.com",
price: 50000,
curr: "CZK",
label: "Rezervace",
refId: "reservation-42",
fullName: "Jan Novák",
preauth: true
)
Parametr preauth nelze kombinovat s initRecurring = true.
Vlastní tlačítko a přímé volání processPayment
Pokud vám předpřipravené SecurePayButton nevyhovuje, můžete platbu spustit přímo voláním metody session.processPayment().
Metoda processPayment šifruje karetní data, odesílá je na platební bránu a v případě potřeby automaticky provede 3D Secure autentizaci. Citlivé údaje nikdy neopustí knihovnu v nechráněné podobě.
Signatura metody
public func processPayment(
collector: SecureCardDataCollector,
params: PaymentParams,
presenter: UIViewController? = nil
) async -> PaymentResult
| Parametr | Typ | Popis |
|---|---|---|
collector | SecureCardDataCollector | Kolektor propojující Secure Fields (PAN, expirace, CVV). |
params | PaymentParams | Parametry platby (cena, měna, popis, refId, jméno plátce aj.). |
presenter | UIViewController? | Volitelný presenter pro 3DS challenge UI. Pokud nil, knihovna použije aktuální top view controller. |
Příklad implementace
struct CustomPaymentScreen: View {
@ObservedObject var session: ComgateSecureSession
@StateObject private var panState: SecurePanFieldState
@StateObject private var expiryState: SecureExpiryFieldState
@StateObject private var cvvState: SecureCvvFieldState
@StateObject private var holder: CollectorHolder
@State private var isProcessing = false
@State private var resultText = ""
init(session: ComgateSecureSession) {
self._session = ObservedObject(wrappedValue: session)
let pan = SecurePanFieldState()
let expiry = SecureExpiryFieldState()
let cvv = SecureCvvFieldState()
_panState = StateObject(wrappedValue: pan)
_expiryState = StateObject(wrappedValue: expiry)
_cvvState = StateObject(wrappedValue: cvv)
_holder = StateObject(wrappedValue: CollectorHolder(pan: pan, expiry: expiry, cvv: cvv))
}
private var collector: SecureCardDataCollector { holder.collector }
var body: some View {
VStack(spacing: 12) {
SecurePanField(state: panState)
SecureExpiryField(state: expiryState)
SecureCvvField(state: cvvState)
Button(isProcessing ? "Zpracování…" : "Zaplatit") {
Task {
isProcessing = true
let params = try! PaymentParams(
email: "zakaznik@example.com",
price: 100,
curr: "CZK",
country: "CZ",
label: "Objednávka #123",
refId: "order-123",
fullName: "Jan Novák"
)
let result = await session.processPayment(collector: collector, params: params)
isProcessing = false
switch result {
case .paid(let t): resultText = "Zaplaceno (\(t))"
case .authorized(let t): resultText = "Autorizováno (\(t))"
case .pending(let t): resultText = "Čeká (\(t))"
case .cancelled(let reason, _): resultText = "Zrušeno: \(reason ?? "-")"
case .failed(let err): resultText = "Chyba: \(err.message)"
}
}
}
.disabled(!collector.isValid || isProcessing || session.state != .ready)
Text(resultText)
}
.padding()
}
}
Před voláním processPayment se ujistěte, že:
- Session je úspěšně inicializovaná (
session.state == .ready). - Karetní data jsou validní (
collector.isValid == true).
Pokud tyto podmínky nejsou splněny, metoda okamžitě vrátí .failed(.sessionNotInitialized) nebo .failed(.invalidCardData).
3D Secure
Knihovna poskytuje kompletní podporu 3D Secure autentizace. Pokud je 3DS nakonfigurováno, knihovna automaticky:
- Připraví autentizační parametry při zpracování platby
- Vyhodnotí odpověď serveru (frictionless / challenge)
- V případě potřeby zobrazí challenge obrazovku
- Vrátí výsledek autentizace prostřednictvím
PaymentResult
Konfigurace
3DS se konfiguruje prostřednictvím struktury ThreeDSConfig, která se předává do konstruktoru ComgateSecureSession:
let threeDSConfig = ThreeDSConfig(
uiCustomization: threeDSUi,
defaultMessageVersion: "2.2.0",
challengeTimeoutMinutes: 5,
challengeWindowCornerRadiusDp: 16
)
let session = ComgateSecureSession(
checkoutId: "váš-checkout-id",
threeDSConfig: threeDSConfig
)
Parametry ThreeDSConfig
| Parametr | Typ | Výchozí | Popis |
|---|---|---|---|
uiCustomization | ThreeDSUiCustomization? | nil | Přizpůsobení vzhledu challenge obrazovky. Pokud je nil, použijí se výchozí styly. |
defaultMessageVersion | String | "2.2.0" | Verze 3DS protokolu. Podporované hodnoty: viz ThreeDSConfig.supportedMessageVersions. |
challengeTimeoutMinutes | Int | 5 | Maximální doba čekání na dokončení challenge v minutách (1–30). |
challengeWindowCornerRadiusDp | Int? | nil | Zaoblení rohů challenge okna (0–64). Pokud je nil, použije se výchozí hodnota. |
Přizpůsobení vzhledu challenge obrazovky
Struktura ThreeDSUiCustomization umožňuje detailní přizpůsobení vzhledu 3DS challenge obrazovky:
let threeDSUi = ThreeDSUiCustomization(
buttons: [
.submit: ThreeDSButtonStyle(
backgroundColor: "#4287F5",
textColor: "#FFFFFF",
textFontSize: 16,
cornerRadius: 48
),
.continue: ThreeDSButtonStyle(
backgroundColor: "#4287F5",
textColor: "#FFFFFF",
textFontSize: 16,
cornerRadius: 48
),
.resend: ThreeDSButtonStyle(
backgroundColor: "#F0F0F0",
textColor: "#4287F5",
textFontSize: 16,
cornerRadius: 48
)
],
labelStyle: ThreeDSLabelStyle(
headingTextColor: "#4287F5",
textColor: "#333333"
),
textBoxStyle: ThreeDSTextBoxStyle(
borderColor: "#4287F5",
borderWidth: 4,
cornerRadius: 16,
textColor: "#333333"
),
toolbarStyle: ThreeDSToolbarStyle(
headerText: "Ověření platby",
buttonText: "Zrušit",
backgroundColor: "#F0F0F0",
textColor: "#333333"
)
)
ThreeDSButtonStyle
Přizpůsobení tlačítek na challenge obrazovce.
| Parametr | Typ | Popis |
|---|---|---|
backgroundColor | String? | Barva pozadí ve formátu #RRGGBB nebo #AARRGGBB. Výchozí "#4287F5". |
textColor | String? | Barva textu ve formátu #RRGGBB nebo #AARRGGBB. Výchozí "#FFFFFF". |
textFontSize | Int? | Velikost písma (8–48). |
cornerRadius | Int? | Zaoblení rohů tlačítka (0–64). |
Tlačítka se konfigurují v dictionary klíčovaném typem:
| Typ tlačítka | Popis |
|---|---|
.submit | Tlačítko pro odeslání (potvrzení). |
.continue | Tlačítko pro pokračování. |
.next | Tlačítko pro další krok. |
.cancel | Tlačítko pro zrušení. |
.resend | Tlačítko pro opětovné odeslání kódu. |
ThreeDSLabelStyle
Přizpůsobení textových popisků.
| Parametr | Typ | Popis |
|---|---|---|
headingTextColor | String? | Barva nadpisu. |
headingTextFontSize | Int? | Velikost nadpisu (8–48). |
textColor | String? | Barva běžného textu. |
textFontSize | Int? | Velikost běžného textu (8–48). |
ThreeDSTextBoxStyle
Přizpůsobení vstupních polí na challenge obrazovce.
| Parametr | Typ | Popis |
|---|---|---|
borderColor | String? | Barva ohraničení. |
borderWidth | Int? | Šířka ohraničení (0–16). |
cornerRadius | Int? | Zaoblení rohů (0–64). |
textColor | String? | Barva textu. |
textFontSize | Int? | Velikost textu (8–48). |
ThreeDSToolbarStyle
Přizpůsobení toolbaru challenge obrazovky.
| Parametr | Typ | Popis |
|---|---|---|
headerText | String? | Text v hlavičce toolbaru. |
buttonText | String? | Text tlačítka v toolbaru (typicky „Zrušit"). |
backgroundColor | String? | Barva pozadí toolbaru. |
textColor | String? | Barva textu. |
textFontSize | Int? | Velikost textu (8–48). |
Barvy se zadávají jako řetězce ve formátu #RRGGBB nebo #AARRGGBB. Neplatné formáty způsobí runtime chybu validace ThreeDSConfigurationError.invalidHexColor.
Testování 3DS plateb
Pro usnadnění vývoje a testování nabízí knihovna v dev režimu (devMode = true) možnost simulovat různé průběhy 3DS autentizace bez nutnosti použít skutečnou bankovní kartu.
Testovací chování se řídí parametrem require3ds v PaymentParams:
Hodnota require3ds | Průběh platby |
|---|---|
true | Server vrátí challenge — zobrazí se 3DS challenge obrazovka, uživatel musí zadat OTP nebo provést ověření. |
false | Server vrátí frictionless výsledek — platba proběhne bez zobrazení challenge obrazovky. |
nil (výchozí) | Server rozhodne sám; v produkci standardní chování. |
Parametr require3ds je funkční výhradně v dev režimu (devMode = true). V produkci (devMode = false) je hodnota tohoto parametru ignorována a platba proběhne standardním způsobem.
Simulace 3DS challenge
Nastavte require3ds = true. Knihovna zobrazí 3DS challenge obrazovku, kde uživatel provede ověření. Výsledek platby bude záviset na akci uživatele:
- Dokončení ověření →
.paid(transId:) - Zrušení challenge →
.failed(.threeDSChallengeCancelled) - Vypršení časového limitu →
.failed(.threeDSChallengeTimeout)
let params = try PaymentParams(
email: "zakaznik@example.com",
price: 100,
curr: "CZK",
label: "Testovací platba",
refId: "test-001",
fullName: "Jan Novák",
require3ds: true
)
Simulace frictionless průběhu
Nastavte require3ds = false. Platba proběhne bez zobrazení challenge obrazovky.
let params = try PaymentParams(
email: "zakaznik@example.com",
price: 100,
curr: "CZK",
label: "Testovací platba",
refId: "test-001",
fullName: "Jan Novák",
require3ds: false
)
Simulace chybového důvodu — ErrorReason
V dev režimu (devMode = true) lze pomocí parametru errorReason v PaymentParams simulovat konkrétní důvod zamítnutí nebo selhání platby.
let params = try PaymentParams(
email: "zakaznik@example.com",
price: 100,
curr: "CZK",
label: "Testovací platba",
refId: "test-001",
fullName: "Jan Novák",
errorReason: .noFunds
)
Parametr errorReason je funkční výhradně v dev režimu (devMode = true). V produkci je tento parametr zcela ignorován.
Dostupné hodnoty výčtu ErrorReason:
| Hodnota | Raw value | Popis |
|---|---|---|
.customerClick | CUSTOMER_CLICK | Zrušeno plátcem. |
.fraudSuspected | FRAUD_SUSPECTED | Podezření na podvod. |
.eshopCancelled | ESHOP_CANCELLED | Zrušeno obchodníkem. |
.providerReport | PROVIDER_REPORT | Zrušeno providerem. |
.providerTimeout | PROVIDER_TIMEOUT | Vypršel časový limit poskytovatele. |
.customerTimeout | CUSTOMER_TIMEOUT | Vypršel časový limit platby. |
.acsTimeout | ACS_TIMEOUT | Vypršel časový limit pro ověření. |
.invalidCardnoExpiry | INVALID_CARDNO_EXPIRY | Chybně zadané číslo karty nebo datum platnosti karty. |
.invalidCvc | INVALID_CVC | Chybně zadaný CVC / CVV kód. |
.limitExceeded | LIMIT_EXCEEDED | Limit karty byl překročen. |
.noFunds | NO_FUNDS | Na účtu není dostatečný zůstatek. |
.rejectedByBank | REJECTED_BY_BANK | Platba byla zamítnuta bankou. |
.threeDSAuthFail | 3DS_AUTH_FAIL | Ověření 3DS nebylo úspěšné. |
.notSpecified | NOT_SPECIFIED | Nespecifikováno. |
Kompletní příklad
Následující příklad ukazuje kompletní implementaci karetní platby s 3D Secure od inicializace po zpracování výsledku:
import SwiftUI
import ComgateSDK
@main
struct CardPaymentApp: App {
@StateObject private var session = ComgateSecureSession(
checkoutId: "váš-checkout-id",
threeDSConfig: ThreeDSConfig(
uiCustomization: ThreeDSUiCustomization(
toolbarStyle: ThreeDSToolbarStyle(
headerText: "Ověření platby",
buttonText: "Zrušit"
)
),
challengeTimeoutMinutes: 5
)
)
var body: some Scene {
WindowGroup {
PaymentScreen(session: session)
.task {
if case .notInitialized = session.state {
try? await session.initialize()
}
}
}
}
}
struct PaymentScreen: View {
@ObservedObject var session: ComgateSecureSession
@StateObject private var panState: SecurePanFieldState
@StateObject private var expiryState: SecureExpiryFieldState
@StateObject private var cvvState: SecureCvvFieldState
@StateObject private var nameState: SecureFullNameFieldState
@StateObject private var statusState = PaymentStatusState()
@StateObject private var holder: CollectorHolder
@State private var cardInfo = "Vyplňte údaje karty"
init(session: ComgateSecureSession) {
self._session = ObservedObject(wrappedValue: session)
let pan = SecurePanFieldState()
let expiry = SecureExpiryFieldState()
let cvv = SecureCvvFieldState()
let name = SecureFullNameFieldState()
_panState = StateObject(wrappedValue: pan)
_expiryState = StateObject(wrappedValue: expiry)
_cvvState = StateObject(wrappedValue: cvv)
_nameState = StateObject(wrappedValue: name)
_holder = StateObject(wrappedValue: CollectorHolder(pan: pan, expiry: expiry, cvv: cvv))
}
private var collector: SecureCardDataCollector { holder.collector }
var body: some View {
ScrollView {
VStack(spacing: 12) {
Text(cardInfo)
SecureFullNameField(state: nameState)
SecurePanField(state: panState)
SecureExpiryField(state: expiryState)
SecureCvvField(state: cvvState)
SecurePayButton(
session: session,
collector: collector,
paymentParams: {
try! PaymentParams(
email: "zakaznik@example.com",
price: 100,
curr: "CZK",
country: "CZ",
label: "Objednávka #123",
refId: "order-123",
fullName: "Jan Novák",
billingAddrCity: "Hradec Králové",
billingAddrStreet: "Jiráskova 115",
billingAddrPostalCode: "50304",
billingAddrCountry: "CZ"
)
},
onResult: { result in
statusState.translation = session.translation
statusState.show(result: result)
}
)
SecurePaymentStatusView(state: statusState)
}
.padding()
}
.onChange(of: panState.isValid) { isValid in
cardInfo = isValid ? "✓ Karta končící na \(panState.last4)" : "Vyplňte údaje karty"
}
.onAppear {
panState.translation = session.translation
expiryState.translation = session.translation
cvvState.translation = session.translation
nameState.translation = session.translation
nameState.attachTo(session)
}
.onDisappear { nameState.detachFrom(session) }
.secureLoadingOverlay(session: session)
}
}