Ana içeriğe geç

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)
}

InitializationToken yalnızca initialize(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ç CardSelectionResult ile döner (.selected, .failed, .cancelled).
  • Ödeme: pay(...) kullanılır, sonuç PaymentResult ile 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 edilen InitializationToken.
  • theme (isteğe bağlı): Theme — tint rengi ve buton köşe yuvarlaklığı. Varsayılan: Theme().
  • style (isteğe bağlı): PresentationStyle.sheet veya .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ğunuz UUID değeri akış boyunca InitializationToken ü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.amount ile birlikte tutarın gösteriminde kullanılır (ör. «₺ 100,00»); para birimi kodu ise InitializationContext.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ı; verilmezse 0.0.
  • orderID: Satıcı tarafındaki sipariş kimliği; verilmezse "".
  • transactionDate: İşlem tarihi; verilmezse Date() (ş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); verilmezse 1.
  • 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çi onFinished/delegate geri çağrılarından bağımsız olarak backend'inize iletilir. Verilmezse "".

Bu URL'ler istemci tarafındaki PaymentResult sonucunun yerine geçmez; mobil uygulama deneyimi için onFinished/PaymentDelegate kullanı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:

  1. 10 haneden oluşmalıdır.
  2. Sadece rakamlardan oluşmalıdır.
  3. 5 ile 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.

.completed yalnızca bir işaretçidir; işlem detayları doğrudan bu sonuç içinde dönmez. Sipariş sonrası akışınız için PaymentData.orderID / transactionID değ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ı; code alanı 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 == .cancelled olup olmadığını kontrol eden yardımcı özellik.
  • code: Yalnızca kind, .server(code:) olduğunda dolu olan hata kodu; diğer durumlarda nil.

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:

  1. authToken değerinin boş veya süresi dolmuş olmadığını kontrol edin.
  2. merchantID ve merchantUserID değerlerinin doğru olduğunu doğrulayın.
  3. Cihazın internet bağlantısı olduğunu kontrol edin.
  4. mode parametresinin 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:

  1. initialize(context:) çağrısının başarıyla tamamlandığından emin olun.
  2. on: parametresine iletilen UIViewController'ın aktif olduğunu kontrol edin.
  3. 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:

  1. token binding'inin @State ya da @Published olarak tanımlandığından emin olun.
  2. Modifier'ın doğru view üzerinde uygulandığını kontrol edin.
  3. 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:

  1. SDK bağımlılığının Package.swift ya da Xcode "Package Dependencies" ekranına eklendiğini doğrulayın.
  2. Paketi resolve edin (Xcode: File → Packages → Resolve Package Versions).
  3. Projeyi temizleyip yeniden derleyin (Xcode: Product → Clean Build Folder).