bex Full SDK — Entegrasyon Kılavuzu
Bu doküman, iOS uygulamanıza bex Full SDK’nın entegrasyonu için hazırlanmıştır.
Gereksinimler
- Min. iOS: 15.0
- Dil: Swift
- Framework'ler: UIKit ve SwiftUI desteklenmektedir.
- Cihazlar: iPhone ve iPad.
- Dağıtım: Yalnızca Swift Package Manager.
Hızlı Başlangıç
bex SDK iki farklı akış sunar:
- Kart seçimi (
selectCard): Kullanıcı kayıtlı kartlarından birini seçer, SDK ödeme başlatmaz; seçilen kart uygulamanıza döner (hibrit entegrasyon). - Ödeme (
pay): SDK kart seçiminden ödeme tamamlanmasına kadar tüm akışı (3DS/OTP dahil) uçtan uca yönetir.
Her iki akış da önce BKMExpress.initialize(context:) ile elde edilen bir InitializationToken
gerektirir.
1) Token oluşturma
import BKMExpressSDK
let gsmNo = try BKMExpress.GSMNO("5551234567")
let context = BKMExpress.InitializationContext(
authToken: "your_initial_auth_token",
merchantID: "MERCHANT_123",
transactionID: UUID(),
gsmNo: gsmNo,
merchantUserID: "user_456",
paymentData: BKMExpress.PaymentData(currencyCode: "TRY"),
mode: .test // Ortam modu: .test, .preprod veya .production.
)
let token = try await BKMExpress.initialize(context: context)
2) Yalnızca kart seçimi (selectCard)
BKMExpress.selectCard(
token: token,
style: .sheet,
on: self
) { result in
switch result {
case let .selected(card):
// Seçilen kart ile kendi ödeme akışınızı başlatın
print("Seçilen kart: \(card.maskedCardNumber)")
case let .failed(failure):
print("Hata: \(failure.message)")
case .cancelled:
print("İptal edildi")
}
}
3) Uçtan uca ödeme (pay)
let paymentData = BKMExpress.PaymentData(
amount: 100.0,
security: .tds,
currencyCode: "TRY",
installmentCount: BKMExpress.InstallmentCount(1)!,
transactionType: .sale,
orderID: "ORDER_123",
successUrl: "https://merchant.example.com/payment/callback/success",
failUrl: "https://merchant.example.com/payment/callback/fail"
)
BKMExpress.pay(
token: token,
data: paymentData,
style: .sheet,
on: self
) { result in
switch result {
case .completed:
print("Ödeme başarılı")
case let .failed(failure):
print("Hata: \(failure.message)")
case .cancelled:
print("Ödeme iptal edildi")
}
}
Kurulum
Swift Package Manager
Package.swift dosyanıza bağımlılığı ekleyin:
dependencies: [
.package(url: "https://github.com/BKMExpress/iOS-Full-SDK.git", branch: "main")
]
Ya da Xcode üzerinden: File → Add Package Dependencies… ile paket URL'sini ekleyip
BKMExpressSDK kütüphanesini hedefinize (target) bağlayın.
İzinler / Ayarlar
SDK internet erişimi gerektirir; App Transport Security ayarlarınızın kullanacağınız HTTPS
uçlarına izin verdiğinden emin olun. 3DS ekranı web içeriği gösterebileceğinden ekstra bir
Info.plist izni gerekmez; yalnızca uygulamanızın minimum iOS sürümünün SDK gereksinimini
karşıladığından emin olun.
Temel Entegrasyon
SDK sınıflarının import edilmesi
import BKMExpressSDK
Tüm public tipler BKMExpress enum namespace'i altında yer alır (ör. BKMExpress.PaymentData,
BKMExpress.InitializationToken).
Adım 1: GSM numarası ve context oluşturma
GSMNO, yalnızca geçerli bir Türkiye cep telefonu numarasını (10 hane, 5 ile başlayan) kabul
eder:
do {
let gsmNo = try BKMExpress.GSMNO("5551234567")
} catch {
// .invalidCharacters / .short / .doesNotStartWithFive
}
InitializationContext, SDK oturumunu başlatmak için gereken tüm bilgileri taşır:
let context = BKMExpress.InitializationContext(
authToken: "your_initial_auth_token",
merchantID: "your_merchant_id",
transactionID: UUID(),
gsmNo: gsmNo,
merchantUserID: "your_user_id",
paymentData: BKMExpress.PaymentData(currencyCode: "TRY"),
mode: .production // Ortam modu: .test, .preprod veya .production.
)
Adım 2: Token alma
do {
let token = try await BKMExpress.initialize(context: context)
// token, selectCard(...) veya pay(...) çağrılarında kullanılır
} catch {
// error bir BKMExpress.Failure'dır
print(error.message)
}
InitializationTokenyalnızcainitialize(context:)ile elde edilebilir; doğrudan oluşturulamaz.
Adım 3: Akışı başlatma
İki farklı kullanım biçimi desteklenir: delegate tabanlı ve closure (completion) tabanlı. Ayrıca SwiftUI için hazır view modifier'ları mevcuttur.
Delegate tabanlı — Kart seçimi
final class CardSelectionViewController: UIViewController, BKMExpress.CardSelectionDelegate {
func startCardSelection(token: BKMExpress.InitializationToken) {
BKMExpress.selectCard(
token: token,
style: .sheet,
on: self,
delegate: self
)
}
func bkmExpressCardSelectionFinished(with result: BKMExpress.CardSelectionResult) {
switch result {
case let .selected(card):
// Seçilen kart ile kendi ödeme akışınızı başlatın
break
case let .failed(failure):
break
case .cancelled:
break
}
}
}
Closure tabanlı — Ödeme
final class CheckoutViewController: UIViewController {
func startPayment(token: BKMExpress.InitializationToken) {
let data = BKMExpress.PaymentData(
amount: 100.0,
security: .tds,
currencyCode: "TRY",
installmentCount: BKMExpress.InstallmentCount(1)!,
transactionType: .sale,
orderID: "ORDER_123",
successUrl: "https://merchant.example.com/payment/callback/success",
failUrl: "https://merchant.example.com/payment/callback/fail"
)
BKMExpress.pay(
token: token,
data: data,
style: .fullScreen,
on: self
) { [weak self] result in
switch result {
case .completed:
self?.navigateToOrderConfirmation()
case let .failed(failure):
self?.showError(failure.message)
case .cancelled:
break
}
}
}
}
SwiftUI örneği
import SwiftUI
import BKMExpressSDK
struct CheckoutView: View {
@State private var token: BKMExpress.InitializationToken?
var body: some View {
Button("Öde") {
Task {
let context = BKMExpress.InitializationContext(
authToken: "your_initial_auth_token",
merchantID: "your_merchant_id",
transactionID: UUID(),
gsmNo: try! BKMExpress.GSMNO("5551234567"),
merchantUserID: "your_user_id",
paymentData: BKMExpress.PaymentData(currencyCode: "TRY"),
mode: .production // Ortam modu: .test, .preprod veya .production.
)
token = try? await BKMExpress.initialize(context: context)
}
}
.bkmExpressPaymentSheet(
token: $token,
data: .init(
amount: 100.0,
security: .tds,
currencyCode: "TRY",
installmentCount: BKMExpress.InstallmentCount(1)!,
transactionType: .sale,
orderID: "ORDER_123",
successUrl: "https://merchant.example.com/payment/callback/success",
failUrl: "https://merchant.example.com/payment/callback/fail"
)
) { result in
// result: BKMExpress.PaymentResult
}
}
}
Kart seçiminin SwiftUI karşılığı bkmExpressCardSelectionSheet(token:theme:style:onFinished:)
modifier'ıdır. Her iki modifier de, token binding nil'den bir değere değiştiğinde akışı
otomatik başlatır; nil olduğunda açık olan sheet'i kapatır. Akış tamamlandığında (onFinished
çağrıldıktan sonra) token otomatik olarak nil'e döner.
Örnek Kullanım
- Kart seçimi:
selectCard(...)kullanılır, sonuçCardSelectionResultile döner (.selected,.failed,.cancelled). - Ödeme:
pay(...)kullanılır, sonuçPaymentResultile döner (.completed,.failed,.cancelled).
Yukarıdaki "Temel Entegrasyon" bölümündeki delegate, closure ve SwiftUI örnekleri doğrudan
kullanılabilir; her iki akış da aynı InitializationToken'ı paylaşabilir.
API Referansı
BKMExpress.initialize(context:)
public static func initialize(
context: InitializationContext
) async throws(Failure) -> InitializationToken
Verilen InitializationContext ile SDK oturumunu başlatır: başlangıç token'ını oturum token'ı
ile değiştirir, kullanıcı cüzdan durumunu kontrol eder ve bir InitializationToken döner. Hata
durumunda Failure fırlatır (typed throw).
BKMExpress.selectCard
// Delegate tabanlı
public static func selectCard(
token: InitializationToken,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
delegate: CardSelectionDelegate
)
// Closure tabanlı
public static func selectCard(
token: InitializationToken,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
onFinished: @escaping (CardSelectionResult) -> Void
)
Yalnızca kart seçimi akışını başlatır; SDK ödeme başlatmaz. Sonuç CardSelectionResult ile
döner.
BKMExpress.pay
// Delegate tabanlı
public static func pay(
token: InitializationToken,
data: PaymentData,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
delegate: PaymentDelegate
)
// Closure tabanlı
public static func pay(
token: InitializationToken,
data: PaymentData,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
onFinished: @escaping (PaymentResult) -> Void
)
Uçtan uca ödeme akışını başlatır (kart seçimi + 3DS/OTP + sonuç ekranı). Sonuç PaymentResult
ile döner.
Parametreler (ortak):
token:initialize(context:)'den elde edilenInitializationToken.theme(isteğe bağlı):Theme— tint rengi ve buton köşe yuvarlaklığı. Varsayılan:Theme().style(isteğe bağlı):PresentationStyle—.sheetveya.fullScreen. Varsayılan:.sheet.on: SDK akışının üzerinde sunulacağıUIViewController.delegate/onFinished: Sonuç geri bildirimi; ikisinden yalnızca biri kullanılır.
SwiftUI Modifier'ları
func bkmExpressCardSelectionSheet(
token: Binding<InitializationToken?>,
theme modify: (inout Theme) -> Void = { _ in },
style: PresentationStyle = .sheet,
onFinished: @escaping (CardSelectionResult) -> Void
) -> some View
func bkmExpressPaymentSheet(
token: Binding<InitializationToken?>,
data: PaymentData,
theme modify: (inout Theme) -> Void = { _ in },
style: PresentationStyle = .sheet,
onFinished: @escaping (PaymentResult) -> Void
) -> some View
token binding bir değere ayarlandığında akış otomatik olarak sunulur; nil'e ayarlandığında
açık sunum kapatılır.
InitializationContext
public struct InitializationContext {
public let authToken: String
public let merchantID: String
public let transactionID: UUID
public let gsmNo: GSMNO
public let merchantUserID: String
public let paymentData: PaymentData
let mode: Mode
}
authToken: Backend'den alınan başlangıç token'ı.merchantID: Satıcı tanımlayıcı.transactionID: İşlem kimliği; oluşturduğunuzUUIDdeğeri akış boyuncaInitializationTokenüzerinden taşınır.gsmNo: Kullanıcı GSM numarası (bkz. GSMNO).merchantUserID: Satıcı tarafındaki kullanıcı tanımlayıcı.paymentData: Ödeme işlemine dair bilgiler (bkz. PaymentData) — para birimi kodu, işlem tipi ve taksit sayısı artık bu yapı üzerinden verilir.mode: SDK'nın bağlanacağı ortam (bkz. Mode).
Token akışı: SDK, verdiğiniz başlangıç token'ı ile /sdk/init çağrısı yapar; backend oturum
token'ı döner ve SDK bunu güvenli şekilde saklar. Sonraki tüm API çağrıları otomatik olarak bu
oturum token'ı ile yapılır.
InitializationToken
public struct InitializationToken: Equatable, CustomReflectable
Yalnızca initialize(context:) ile elde edilebilen, oturum bilgilerini taşıyan opak (mirror'ı
boş) bir veri yapısıdır. Taşıdığı bilgiler arasında:
initialState: Kullanıcının cüzdan durumu — bağlı kartlar (linked), bağlı değil (unlinked), kayıtsız (unregistered) veya doğrulama gerekli (verificationRequired).authToken,linkInfo,encryptionKey: Sonraki API çağrıları için gerekli oturum bilgileri.appStoreURL: Ödeme sonucu ekranındaki banner için mağaza yönlendirme adresi.agreements: Kullanıcıya gösterilecek sözleşme listesi.currencySymbol:PaymentData.amountile birlikte tutarın gösteriminde kullanılır (ör. «₺ 100,00»); para birimi kodu iseInitializationContext.paymentData.currencyCodeüzerinden verdiğiniz değerdir.
PaymentData
public struct PaymentData {
public init(
amount: Decimal = 0.0,
orderID: String = "",
transactionDate: Date = Date(),
security: PaymentSecurity = .none,
currencyCode: String,
installmentCount: InstallmentCount = InstallmentCount(1)!,
transactionType: TransactionType = .sale,
successUrl: String = "",
failUrl: String = ""
)
}
amount: Ödeme tutarı; verilmezse0.0.orderID: Satıcı tarafındaki sipariş kimliği; verilmezse "".transactionDate: İşlem tarihi; verilmezseDate()(şimdi) kullanılır.security: Ödeme güvenlik mekanizması (bkz. PaymentSecurity); verilmezse.none.currencyCode: Para birimi kodu (ör."TRY"). Zorunludur, varsayılan değeri yoktur.installmentCount: Taksit sayısı (bkz. InstallmentCount); verilmezse1.transactionType: İşlem tipi (bkz. TransactionType); verilmezse.sale.successUrl/failUrl: Ödeme tamamlandığında veya başarısız olduğunda satıcı backend'inizin bilgilendirilmesi için kullanılan bildirim adresleri. SDK bu adresleri işlem başlatma (initPayment) isteğinin gövdesine ekler; bildirim, uygulama içionFinished/delegategeri çağrılarından bağımsız olarak backend'inize iletilir. Verilmezse "".
Bu URL'ler istemci tarafındaki
PaymentResultsonucunun yerine geçmez; mobil uygulama deneyimi içinonFinished/PaymentDelegatekullanılmaya devam edilir.
PaymentSecurity
public enum PaymentSecurity {
case otp
case tds
case none
}
tds: Ödeme 3-D Secure (3DS) ile gerçekleşir.otp: Ödeme Non-3D + OTP ile gerçekleşir.none: Ödemede ek bir güvenlik doğrulaması kullanılmaz.
Bu yalnızca istemci tarafı bir tercihtir; gerçek akış (3DS/OTP/doğrudan sonuç) her zaman API yanıtına göre belirlenir.
TransactionType
public enum TransactionType: String, Sendable, CaseIterable {
case sale
case preAuth
case recurring
}
sale: Standart satış işlemi.preAuth: Ön provizyon.recurring: Tekrarlayan ödeme.
InstallmentCount
public struct InstallmentCount: Sendable {
public init?(_ rawValue: Int)
}
Geçerli bir taksit sayısını temsil eder; rawValue 0'dan büyük olmalıdır, aksi halde init?
nil döner:
guard let installmentCount = BKMExpress.InstallmentCount(1) else {
// geçersiz taksit sayısı
return
}
GSMNO
public struct GSMNO: Sendable, Equatable {
public enum InitializationError: Error {
case invalidCharacters
case short
case doesNotStartWithFive
}
public init(_ value: String) throws(InitializationError)
}
Geçerli bir Türkiye cep telefonu numarasını temsil eder:
- 10 haneden oluşmalıdır.
- Sadece rakamlardan oluşmalıdır.
5ile başlamalıdır.
Hatalı bir değer verilirse InitializationError fırlatılır (invalidCharacters, short,
doesNotStartWithFive).
Mode
public enum Mode {
case test
case preprod
case production
}
İsteklerin hangi API ortamına gönderileceğini belirler.
PresentationStyle
public enum PresentationStyle {
case sheet
case fullScreen
}
sheet: SDK akışı modal bir sayfa (sheet) olarak sunulur; kaydırarak kapatılamaz (isModalInPresentation = true), yalnızca SDK içindeki kapatma butonu ile çıkılabilir.fullScreen: SDK akışı tam ekran (modalPresentationStyle = .fullScreen) olarak sunulur.
Her iki sunum şeklinde de aynı işlevsellik ve geri çağrılar kullanılır.
CardSelectionDelegate / PaymentDelegate
public protocol CardSelectionDelegate: AnyObject {
func bkmExpressCardSelectionFinished(with result: CardSelectionResult)
}
public protocol PaymentDelegate: AnyObject {
func bkmExpressPaymentFinished(with result: PaymentResult)
}
Delegate tabanlı kullanım için sonuç geri çağrı arayüzleridir. Delegate, retain cycle'ı önlemek
için SDK tarafından weak olarak tutulur.
CardSelectionResult
public enum CardSelectionResult {
case selected(Card)
case failed(Failure)
case cancelled
}
selected(Card): Kullanıcı kayıtlı kartlarından birini seçti (bkz. Card).failed(Failure): SDK bir hata ile tamamlandı (bkz. Failure).cancelled: Kullanıcı akışı iptal etti.
PaymentResult
public enum PaymentResult {
case completed
case failed(Failure)
case cancelled
}
completed: Ödeme başarıyla tamamlandı.failed(Failure): Ödeme bir hata ile sonuçlandı.cancelled: Kullanıcı ödemeyi iptal etti.
.completedyalnızca bir işaretçidir; işlem detayları doğrudan bu sonuç içinde dönmez. Sipariş sonrası akışınız içinPaymentData.orderID/transactionIDdeğerlerini kendi tarafınızda saklamanız önerilir.
Card
public struct Card: Identifiable, Equatable, Hashable {
public let id: ID
public let alias: String
public let maskedCardNumber: String
public let imageURL: URL?
public let bankInformation: BankInformation
public let bin: String
public let createTime: Date
}
id: Kartın benzersiz kimliği.alias: Kartın takma adı.maskedCardNumber: Maskelenmiş kart numarası.imageURL: Kart görseli adresi.bankInformation: Banka/kart bilgileri:bankShortName: Banka kısa adı.cardType: Kart tipi.cardScheme: Kart şeması.cardBrand: Kart markası (ör.Troy).bankCode: Banka kodu.
bin: Kart BIN değeri.createTime: Kartın eklendiği tarih.
Failure
public struct Failure: LocalizedError {
public enum Kind {
case session
case cancelled
case invalidPaymentData
case encryption
case server(code: Int)
}
public let kind: Kind
public let message: String
public let reason: String?
public var isCancelled: Bool
public var errorDescription: String? { message }
public var code: Int?
}
kind: Hatanın türü:session: Oturum/ağ hatası veya zaman aşımı.cancelled: Kullanıcı işlemi iptal etti.invalidPaymentData: Verilen ödeme bilgileri (PaymentData) geçersiz veya eksik.encryption: Şifreleme/tokenizasyon hatası (ör. public key oluşturulamadı).server(code:): Sunucudan dönen iş kuralı hatası;codealanı ile birlikte gelir.
message: Kullanıcıya gösterilebilecek hata mesajı.reason: Teknik/loglama amaçlı ek açıklama; kullanıcıya gösterilmemelidir.isCancelled:kind == .cancelledolup olmadığını kontrol eden yardımcı özellik.code: Yalnızcakind,.server(code:)olduğunda dolu olan hata kodu; diğer durumlardanil.
Sunum Modları
1. Sheet (varsayılan)
BKMExpress.pay(token: token, data: data, style: .sheet, on: self, onFinished: onFinished)
Modal bir sayfa olarak sunulur; kaydırarak kapatma devre dışıdır, yalnızca SDK içindeki kapatma butonu (ve onay uyarısı) ile çıkılabilir.
2. Tam ekran
BKMExpress.pay(token: token, data: data, style: .fullScreen, on: self, onFinished: onFinished)
Aynı işlevsellik ve geri çağrılar .sheet ile birebir aynıdır; yalnızca sunum biçimi değişir.
Tema Özelleştirme
SDK, vurgu (tint) rengi ve buton köşe yuvarlaklığını özelleştirmeyi destekler.
public struct Theme {
public enum ButtonCornerRadius {
case custom(CGFloat)
case capsule
}
public var tint: UIColor
public var buttonCornerRadius: ButtonCornerRadius
}
Temel kullanım
var theme = BKMExpress.Theme()
theme.tint = .systemBlue
theme.buttonCornerRadius = .custom(8)
BKMExpress.pay(
token: token,
data: paymentData,
theme: theme,
style: .sheet,
on: self,
onFinished: onFinished
)
SwiftUI'da tema, modify closure'ı ile ayarlanır:
.bkmExpressPaymentSheet(
token: $token,
data: paymentData,
theme: { theme in
theme.tint = .systemBlue
theme.buttonCornerRadius = .capsule
}
) { result in
// ...
}
Sorun Giderme
"bex başlatılamadı" Hatası
Problem: initialize(context:) çağrısı hata fırlattı.
Çözümler:
authTokendeğerinin boş veya süresi dolmuş olmadığını kontrol edin.merchantIDvemerchantUserIDdeğerlerinin doğru olduğunu doğrulayın.- Cihazın internet bağlantısı olduğunu kontrol edin.
modeparametresinin kullandığınız token ortamıyla eşleştiğinden emin olun.
Akış Başlamıyor
Problem: pay veya selectCard çağrıldı ancak SDK görünmüyor.
Çözümler:
initialize(context:)çağrısının başarıyla tamamlandığından emin olun.on:parametresine iletilenUIViewController'ın aktif olduğunu kontrol edin.on:parametresine iletilen controller'ın zaten başka bir modal sunup sunmadığını kontrol edin.
SwiftUI'da Akış Başlamıyor
Problem: Token state'e atandı ancak SDK görünmüyor.
Çözümler:
tokenbinding'inin@Stateya da@Publishedolarak tanımlandığından emin olun.- Modifier'ın doğru view üzerinde uygulandığını kontrol edin.
- Token'ın
nil'den geçerli bir değere set edildiğini,nil'e değil, doğrulayın.
Derleme Hatası: 'BKMExpressSDK' Bulunamadı
Çözümler:
- SDK bağımlılığının
Package.swiftya da Xcode "Package Dependencies" ekranına eklendiğini doğrulayın. - Paketi resolve edin (Xcode: File → Packages → Resolve Package Versions).
- Projeyi temizleyip yeniden derleyin (Xcode: Product → Clean Build Folder).