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ıç

SDK'yı yalnızca birkaç satır kodla entegre edin:

UIKit

let token = try await BKMExpress.initialize(context: context)
BKMExpress.pay(token: token, data: paymentData, on: self) { result in ... }

SwiftUI

.bkmExpressPaymentSheet(token: $token, data: paymentData) { result in ... }

Kurulum

Swift Package Manager

Seçenek 1: Xcode üzerinden

Projenizin "Package Dependencies" sekmesinden aşağıdaki URL ve sürüm bilgisiyle paketi ekleyin:

https://github.com/BKMExpress/iOS-Full-SDK.git

Seçenek 2: Package.swift üzerinden (başka bir Swift Package içinde kullanılacaksa)

Package.swift dosyanızdaki dependencies listesine bağımlılığı ekleyin:

.package(url: "https://github.com/BKMExpress/iOS-Full-SDK.git", branch: "main")

Ardından bağımlılığı kullanmak istediğiniz target'ın dependencies listesine SDK'nin product'ını ekleyin:

"BKMExpressSDK"

Temel Entegrasyon

Adım 1: SDK'yı İmport Edin

import BKMExpressSDK

Adım 2: SDK'yı Başlatın

Kendi sunucularınızdan entegrasyon sonucunda elde edeceğiniz bilgilerle InitializationContext'i doldurun ve initialize(context:) çağrısı yapın:

let token = try await BKMExpress.initialize(
context: .init(
authToken: "your_auth_token", // Auth tokeniniz.
merchantID: "your_merchant_id", // Entegrasyon sonucunda size verilen merchant identifier.
merchantUserID: "your_user_id", // Kullanıcınızı tanımlamak için kullandığınız identifier.
gsmNo: .init("5XXXXXXXXX")!, // Kullanıcıya ait Türkiye telefon numarası.
currencyCode: "TRY", // ISO 4217 para birimi kodu.
mode: .test // Ortam modu: .test, .preprod veya .production.
)
)

Adım 3: Akışı Başlatın

Ödeme veya kart seçimi akışını başlatın:

// Ödeme
BKMExpress.pay(token: token, data: paymentData, on: self) { result in
// Sonuca göre aksiyon alın.
}

// Kart Seçimi
BKMExpress.selectCard(token: token, on: self) { result in
// Sonuca göre aksiyon alın.
}

SDK akışı herhangi bir nedenle tamamlandığında kendini otomatik olarak dismiss eder. Ekstra dismiss çağrısı yapılmasına gerek yoktur.


Tam Entegrasyon Örneği

UIKit — Ödeme

final class CheckoutViewController: UIViewController {

private func payButtonTapped() {
Task {
do {
// 1. SDK'yı başlatın.
let token = try await BKMExpress.initialize(
context: .init(
authToken: "your_auth_token",
merchantID: "your_merchant_id",
merchantUserID: "your_user_id",
gsmNo: .init("5XXXXXXXXX")!,
currencyCode: "TRY",
mode: .test
)
)

// 2. Ödeme verilerini hazırlayın.
let paymentData = BKMExpress.PaymentData(
amount: 99.90,
security: .tds,
installmentCount: .init(1)!,
orderID: "order_123",
successUrl: "https://merchant.example.com/payment/callback/success",
failUrl: "https://merchant.example.com/payment/callback/fail"
)

// 3. Ödeme akışını başlatın.
BKMExpress.pay(
token: token,
data: paymentData,
theme: .init(), // Opsiyonel: tema özelleştirmesi.
style: .sheet, // .sheet veya .fullScreen.
on: self
) { result in
switch result {
case .completed:
print("Ödeme başarılı.")
case .cancelled:
print("Ödeme iptal edildi.")
case .failed(let failure):
print("Ödeme başarısız: \(failure.message)")
}
}

} catch {
showAlert("BKM Express başlatılamadı: \(error.localizedDescription)")
}
}
}
}

UIKit — Kart Seçimi

final class CardSelectionViewController: UIViewController {

private func selectCardButtonTapped() {
Task {
do {
let token = try await BKMExpress.initialize(
context: .init(
authToken: "your_auth_token",
merchantID: "your_merchant_id",
merchantUserID: "your_user_id",
gsmNo: .init("5XXXXXXXXX")!,
currencyCode: "TRY",
mode: .test
)
)

BKMExpress.selectCard(
token: token,
theme: .init(),
style: .sheet,
on: self
) { result in
switch result {
case .selected(let card):
print("Seçilen kart: \(card.alias)")
case .cancelled:
print("Kart seçimi iptal edildi.")
case .failed(let failure):
print("Hata: \(failure.message)")
}
}

} catch {
showAlert("BKM Express başlatılamadı.")
}
}
}
}

SwiftUI — Ödeme

struct CheckoutScreen: View {
@State private var bkmExpressToken: BKMExpress.InitializationToken?

var body: some View {
VStack {
// View içeriğiniz.
Button("BKM Express ile Öde") {
Task { await startPayment() }
}
}
.bkmExpressPaymentSheet(
token: $bkmExpressToken,
data: BKMExpress.PaymentData(
amount: 99.90,
security: .tds,
installmentCount: .init(1)!,
orderID: "order_123",
successUrl: "https://merchant.example.com/payment/callback/success",
failUrl: "https://merchant.example.com/payment/callback/fail"
),
theme: { $0.buttonCornerRadius = .capsule }, // Opsiyonel.
style: .sheet
) { result in
switch result {
case .completed:
print("Ödeme başarılı.")
case .cancelled:
print("Ödeme iptal edildi.")
case .failed(let failure):
print("Ödeme başarısız: \(failure.message)")
}
}
}

private func startPayment() async {
do {
bkmExpressToken = try await BKMExpress.initialize(
context: .init(
authToken: "your_auth_token",
merchantID: "your_merchant_id",
merchantUserID: "your_user_id",
gsmNo: .init("5XXXXXXXXX")!,
currencyCode: "TRY",
mode: .test
)
)
} catch {
showAlert("BKM Express başlatılamadı.")
}
}
}

SwiftUI'da SDK akışı tamamlandığında token'a otomatik olarak nil set edilir. Ekstra bir aksiyona gerek yoktur.

SwiftUI — Kart Seçimi

struct CardSelectionScreen: View {
@State private var bkmExpressToken: BKMExpress.InitializationToken?

var body: some View {
VStack {
Button("BKM Express Kartınızı Eşleştirin") {
Task { await startCardSelection() }
}
}
.bkmExpressCardSelectionSheet(
token: $bkmExpressToken,
theme: { $0.buttonCornerRadius = .capsule }, // Opsiyonel.
style: .sheet
) { result in
switch result {
case .selected(let card):
print("Seçilen kart: \(card.alias)")
case .cancelled:
print("İptal edildi.")
case .failed(let failure):
print("Hata: \(failure.message)")
}
}
}

private func startCardSelection() async {
do {
bkmExpressToken = try await BKMExpress.initialize(
context: .init(
authToken: "your_auth_token",
merchantID: "your_merchant_id",
merchantUserID: "your_user_id",
gsmNo: .init("5XXXXXXXXX")!,
currencyCode: "TRY",
mode: .test
)
)
} catch {
showAlert("BKM Express başlatılamadı.")
}
}
}

API Referansı

BKMExpress

SDK'nın ana namespace'i.


initialize(context:)

SDK oturumu başlatır ve akışı başlatmak için gereken token'ı döner.

static func initialize(context: InitializationContext) async throws(Failure) -> InitializationToken

Parametreler:

  • context: Oturum için gerekli bilgileri içeren InitializationContext.

Döndürür: InitializationToken — akışı başlatmak için kullanılan token.

Fırlatır: BKMExpress.Failure — hata durumunda.


pay(token:data:theme:style:on:onFinished:)

Ödeme akışını başlatır (closure versiyonu).

static func pay(
token: InitializationToken,
data: PaymentData,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
onFinished: @escaping (PaymentResult) -> Void
)

Parametreler:

  • token: initialize(context:) metodundan elde edilen token.
  • data: Ödeme işlemine dair bilgileri içeren PaymentData.
  • theme: SDK görünümünü özelleştiren tema. Opsiyonel; varsayılan tema kullanılır.
  • style: Sunum biçimi. .sheet veya .fullScreen.
  • on: SDK'yı present edecek UIViewController.
  • onFinished: SDK tamamlandığında çağırılacak closure. PaymentResult iletilir.

pay(token:data:theme:style:on:delegate:)

Ödeme akışını başlatır (delegate versiyonu).

static func pay(
token: InitializationToken,
data: PaymentData,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
delegate: PaymentDelegate
)

Parametreler:

  • token: initialize(context:) metodundan elde edilen token.
  • data: Ödeme işlemine dair bilgileri içeren PaymentData.
  • theme: SDK görünümünü özelleştiren tema. Opsiyonel.
  • style: Sunum biçimi. .sheet veya .fullScreen.
  • on: SDK'yı present edecek UIViewController.
  • delegate: SDK tamamlandığında bkmExpressPaymentFinished(with:) metodu çağırılacak PaymentDelegate.

selectCard(token:theme:style:on:onFinished:)

Kart seçimi akışını başlatır (closure versiyonu).

static func selectCard(
token: InitializationToken,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
onFinished: @escaping (CardSelectionResult) -> Void
)

Parametreler:

  • token: initialize(context:) metodundan elde edilen token.
  • theme: SDK görünümünü özelleştiren tema. Opsiyonel.
  • style: Sunum biçimi. .sheet veya .fullScreen.
  • on: SDK'yı present edecek UIViewController.
  • onFinished: SDK tamamlandığında çağırılacak closure. CardSelectionResult iletilir.

selectCard(token:theme:style:on:delegate:)

Kart seçimi akışını başlatır (delegate versiyonu).

static func selectCard(
token: InitializationToken,
theme: Theme = .init(),
style: PresentationStyle = .sheet,
on controller: UIViewController,
delegate: CardSelectionDelegate
)

Parametreler:

  • token: initialize(context:) metodundan elde edilen token.
  • theme: SDK görünümünü özelleştiren tema. Opsiyonel.
  • style: Sunum biçimi. .sheet veya .fullScreen.
  • on: SDK'yı present edecek UIViewController.
  • delegate: SDK tamamlandığında bkmExpressCardSelectionFinished(with:) metodu çağırılacak CardSelectionDelegate.

InitializationContext

SDK oturumu başlatmak için gereken bilgileri içeren veri yapısıdır.

BKMExpress.InitializationContext(
authToken: String, // Auth tokeniniz.
merchantID: String, // Merchant identifier'ınız.
merchantUserID: String, // Kullanıcı identifier'ınız.
gsmNo: GSMNO, // Kullanıcıya ait telefon numarası.
currencyCode: String, // ISO 4217 para birimi kodu (örn. "TRY").
mode: Mode // Ortam modu.
)

Parametreler:

  • authToken: Kendi sunucularınızdan elde edeceğiniz auth tokeni.
  • merchantID: Entegrasyon sonucunda size verilen merchant identifier.
  • merchantUserID: Kullanıcınızı tanımlamak için kullandığınız identifier.
  • gsmNo: Kullanıcıya ait geçerli bir Türkiye telefon numarası (GSMNO instance'ı).
  • currencyCode: ISO 4217 standardında 3 haneli para birimi kodu. Yerleşik sabitler: BKMExpress.CurrencyCode.TRY, .USD, .EUR.
  • mode: Ortam modu. Test esnasında .test veya .preprod, üretimde .production kullanılmalıdır.

GSMNO

SDK tarafından desteklenen, geçerli bir Türkiye telefon numarasını temsil eden veri yapısıdır.

BKMExpress.GSMNO(_ rawValue: String)

Kurallar:

  • 10 haneden oluşmalıdır.
  • Yalnızca rakamlardan oluşmalıdır.
  • 5 rakamıyla başlamalıdır.

Koşullar sağlanmadığında init nil döner (InitializationError fırlatmaz). Failable init kullanımı:

guard let gsmNo = BKMExpress.GSMNO("5XXXXXXXXX") else {
// Geçersiz numara.
return
}

PaymentData

Ödeme işlemi için gerekli bilgileri içeren veri yapısıdır.

BKMExpress.PaymentData(
amount: Decimal,
security: PaymentSecurity,
installmentCount: InstallmentCount,
orderID: String,
successUrl: String,
failUrl: String
)

Parametreler:

  • amount: Ödeme tutarı.
  • security: Güvenlik mekanizması. .otp, .tds veya .none.
  • installmentCount: Taksit sayısı (InstallmentCount instance'ı, pozitif tam sayı).
  • orderID: Siparişinizi tanımlayan identifier.

Satıcı backend bildirim URL’leri: successUrl ve failUrl, ödeme işleminin sunucu tarafında tamamlanması veya başarısız kalması durumunda satıcı backend’inizin bilgilendirilmesi için kullanılır. SDK bu adresleri ödeme işlemi isteklerinde kullanır.

  • successUrl: Ödeme süreci başarıyla tamamlandığında satıcı backend’inize yönlendirilecek veya çağrılacak bildirim adresi. Tam, erişilebilir bir URL (ör. HTTPS) olmalıdır. Varsayılan: "" (boş string).
  • failUrl: Ödeme süreci başarısız olduğunda, iptal edildiğinde veya tamamlanamadığında satıcı backend’inize iletilecek bildirim adresi. Varsayılan: "" (boş string).

PaymentResult

Ödeme akışının nasıl tamamlandığını belirten enum'dur.

enum BKMExpress.PaymentResult {
case completed // Ödeme başarıyla tamamlandı.
case cancelled // Kullanıcı akışı iptal etti.
case failed(Failure) // Hata oluştu.
}

Örnek:

BKMExpress.pay(token: token, data: paymentData, on: self) { result in
switch result {
case .completed:
navigateToSuccessScreen()
case .cancelled:
print("Kullanıcı ödemeyi iptal etti.")
case .failed(let failure):
showError(failure.message)
}
}

CardSelectionResult

Kart seçimi akışının nasıl tamamlandığını belirten enum'dur.

enum BKMExpress.CardSelectionResult {
case selected(Card) // Kullanıcı bir kart seçti.
case cancelled // Kullanıcı akışı iptal etti.
case failed(Failure) // Hata oluştu.
}

Örnek:

BKMExpress.selectCard(token: token, on: self) { result in
switch result {
case .selected(let card):
print("Kart seçildi: \(card.alias), \(card.maskedCardNumber)")
case .cancelled:
print("Kart seçimi iptal edildi.")
case .failed(let failure):
showError(failure.message)
}
}

Failure

SDK'da oluşan bir hatayı temsil eden veri yapısıdır.

struct BKMExpress.Failure: LocalizedError {
let kind: Kind // Hata türü: .session, .encryption, .server(code:)
let message: String // Kullanıcıya gösterilebilecek hata mesajı.
let reason: String? // Teknik hata detayı. Opsiyonel.
var code: Int? // Sunucu hata kodu. Yalnızca .server türü hatalar için dolu gelir.
}

Hata Türleri:

  • .session: Oturum hatası.
  • .encryption: Şifreleme hatası.
  • .server(code:): Sunucu taraflı hata. code ile HTTP hata kodu erişilebilir.

PaymentDelegate

Ödeme akışı tamamlandığında bildirim almak için kullanılan protokoldür.

public protocol PaymentDelegate: AnyObject {
func bkmExpressPaymentFinished(with result: BKMExpress.PaymentResult)
}

Örnek:

extension CheckoutViewController: BKMExpress.PaymentDelegate {
func bkmExpressPaymentFinished(with result: BKMExpress.PaymentResult) {
switch result {
case .completed: navigateToSuccessScreen()
case .cancelled: break
case .failed(let failure): showError(failure.message)
}
}
}

CardSelectionDelegate

Kart seçimi akışı tamamlandığında bildirim almak için kullanılan protokoldür.

public protocol CardSelectionDelegate: AnyObject {
func bkmExpressCardSelectionFinished(with result: BKMExpress.CardSelectionResult)
}

Örnek:

extension MyViewController: BKMExpress.CardSelectionDelegate {
func bkmExpressCardSelectionFinished(with result: BKMExpress.CardSelectionResult) {
switch result {
case .selected(let card): useCard(card)
case .cancelled: break
case .failed(let failure): showError(failure.message)
}
}
}

Card

BKM Express'e kayıtlı bir kartı temsil eden veri yapısıdır. CardSelectionResult.selected ile iletilir.

struct BKMExpress.Card {
let id: ID // Benzersiz kart identifier'ı.
let alias: String // Kullanıcının karta verdiği ad.
let maskedCardNumber: String // Maskelenmiş kart numarası.
let imageURL: URL? // Kart görseli URL'i. Opsiyonel.
let bankInformation: BankInformation
let bin: String
let createTime: Date
}

struct BKMExpress.Card.BankInformation {
let bankShortName: String
let cardType: String
let cardScheme: String
let cardBrand: String
let bankCode: String
}

PresentationStyle

SDK'nın nasıl gösterileceğini belirler.

enum BKMExpress.PresentationStyle {
case sheet // Modal sheet olarak gösterir.
case fullScreen // Tam ekran olarak gösterir.
}

Mode

SDK'nın hangi ortama istek atacağını belirler.

enum BKMExpress.Mode {
case test // Test ortamı.
case preprod // Ön üretim ortamı.
case production // Üretim ortamı.
}

Tema Özelleştirme

Genel Bakış

SDK, tint rengi ve buton köşe yarıçapı özelleştirmesini destekler. Tüm özelleştirmeler opsiyoneldir; yapılmadığında SDK varsayılan teması kullanılır.

Temel Kullanım

var theme = BKMExpress.Theme()
theme.tint = UIColor(red: 0.12, green: 0.47, blue: 0.71, alpha: 1) // Marka renginiz.
theme.buttonCornerRadius = .custom(8) // Köşe yarıçapı.

Özelleştirme Seçenekleri

Tint Rengi

SDK genelinde kullanılan vurgu rengidir.

theme.tint = .systemBlue      // Sistem mavisi.
theme.tint = UIColor(...) // Özel renk.

Buton Köşe Yarıçapı

theme.buttonCornerRadius = .custom(12)  // Belirli bir piksel değeri (varsayılan: 12).
theme.buttonCornerRadius = .capsule // Tam yuvarlak (hap şekli).

SwiftUI Modifier ile Özelleştirme

SwiftUI modifier'larında tema bir closure aracılığıyla değiştirilir:

.bkmExpressPaymentSheet(
token: $token,
data: paymentData,
theme: { theme in
theme.tint = UIColor(red: 0.85, green: 0.11, blue: 0.11, alpha: 1)
theme.buttonCornerRadius = .capsule
}
) { result in ... }

Kısmi Özelleştirme

Yalnızca değiştirmek istediğiniz özellikleri belirtmeniz yeterlidir:

var theme = BKMExpress.Theme()
theme.tint = .red // Sadece tint değiştirilir; köşe yarıçapı varsayılan kalır.

Tam Örnek

// UIKit
var theme = BKMExpress.Theme()
theme.tint = UIColor(red: 0.85, green: 0.11, blue: 0.11, alpha: 1)
theme.buttonCornerRadius = .capsule

BKMExpress.pay(token: token, data: paymentData, theme: theme, style: .sheet, on: self) { result in
// ...
}

// SwiftUI
.bkmExpressPaymentSheet(
token: $token,
data: paymentData,
theme: { $0.buttonCornerRadius = .custom(8) }
) { result in ... }

Gelişmiş Kullanım

Hata Yönetimi Kalıpları

Ağ Hatasında Yeniden Deneme

BKMExpress.pay(token: token, data: paymentData, on: self) { result in
if case .failed(let failure) = result, failure.kind == .session {
showRetryDialog(message: failure.message) {
Task { await self.startPayment() }
}
}
}

Tüm Sonuç Türlerini Yönetme

BKMExpress.pay(token: token, data: paymentData, on: self) { result in
switch result {
case .completed:
navigateToSuccessScreen()
case .cancelled:
// Kullanıcı akıştan çıktı; ekstra aksiyon gerekmez.
break
case .failed(let failure):
switch failure.kind {
case .session:
showError("Oturum hatası oluştu.")
case .encryption:
showError("Şifreleme hatası oluştu.")
case .server(let code):
showError("Sunucu hatası: \(code)")
}
}
}

Analitik Takibi

BKMExpress.pay(token: token, data: paymentData, on: self) { result in
switch result {
case .completed:
Analytics.track("payment_success")
case .cancelled:
Analytics.track("payment_cancelled")
case .failed(let failure):
Analytics.track("payment_error", properties: [
"kind": "\(failure.kind)",
"message": failure.message
])
}
}

En İyi Pratikler

1. Token Güvenliği

Yapmayın — token'ı kaynak koduna gömmek:

let token = "sk_live_abc123..." // HATALI!

Yapın — kendi backend'inizden çekin:

let authToken = try await yourAPI.requestBKMExpressToken()

2. Doğru Ortamı Kullanın

Yapın — geliştirme ve test sırasında test ortamı kullanın:

mode: .test     // veya .preprod

Yapın — uygulamanızı App Store'a göndermeden önce production ortamına geçin:

mode: .production

3. Telefon Numarasını Doğrulayın

YapınGSMNO failable init'ini kullanın:

guard let gsmNo = BKMExpress.GSMNO(userInputPhoneNumber) else {
showError("Lütfen geçerli bir telefon numarası girin.")
return
}

4. Tüm Sonuçları Yönetin

Yapın — her zaman completed, cancelled ve failed durumlarını ele alın:

{ result in
switch result {
case .completed: /* ... */
case .cancelled: /* ... */
case .failed: /* ... */
}
}

Sorun Giderme

"BKM Express 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).