BEX Lite SDK — Entegrasyon Kılavuzu
Ne işe yarar?
BKM Express SDK'si, kartların güvenli ve kolayca saklandığı BKM Express altyapısını kullanmak için geliştirilen bir framework'tür.
Nasıl Çalışır?
- Entegrasyon talebi açılır ve token üretilecek servisin entegrasyonu sağlanır.
- Kullanıcının telefon numarası ile 'init' isteği atılır ve API objesi elde edilir.
- API objesi uzerinden SDK istekleri atılır.
- Kart ekleme işlevleri için metin komponentleri kullanılarak elde
Gereksinimler
- iOS 15.0+ sürümler ve Swift programlama dili desteklenmektedir.
- SwiftUI ve UIKit frameworkleri için komponentler mevcuttur, ancak SwiftUI'da UIKit desteklenmeyen platformlar kullanılamaz (macOS).
Ortamlar
SDK Production, Preprod ve Test olmak üzere üç modu desteklemektedir. Mod (Mode) parametresi 'init' işlemi esnasında context içinde belirtilmelidir. Testlerin güvenlik ve çeşitlilik için Test ortamında yapılması önerilir.
Entegrasyon
Sadece Swift Package Manager ile dağıtım mevcuttur.
- Projenizin "Package Dependencies" kısmından gerekli sürüm bilgisini girerek
** veya **
- Başka bir Swift Package içinde kullanılacaksa:
-
Package.swift dosyanızdaki "dependencies" parametresi içerisine bağımlılığı ekledikten sonra
.package(url: "https://github.com/BKMExpress/BKMExpressLiteSDK.git", exact: "2.0.0")
-
Bağımlılığı kullanmak istediğiniz target'in "dependencies" kısmına SDK'nin product'ını ekleyerek
"BKMExpressLiteSDK"
-
SDK'yi kullanabilirsiniz.
Kullanım
1. API Objesi Oluşturma
- BKM Express SDK'sı import edilir
import BKMExpressLiteSDK
- Kendi sunucularınızdan entegrasyon sonucunda elde edeceğiniz tokenle birlikte 'InitializationContext' içerisinde istenen tüm bilgiler doldurulur ve 'init' çağrısı yapılır.
private func pairButtonTapped() async {
do {
let api = try await BKMExpress.initialize(
context: .init(
authToken: "", // -> Auth tokeniniz.
merchantID: "", // -> Entegrasyon sonucunda size verilen merchant identifier'ınız.
merchantUserID: "", // -> Kullanıcınızı tanımlamak için kullandığınız identifier.
gsmNo: .init("5555555555") // -> Kullanıcınıza ait telefon numarası. Ayrı bir struct olduğu için kendi kurallarını işletir ve sadece verilen telefon numarası şartları sağladığında bir instance'ı yaratılabilir.
mode: .development // -> SDK'nın modunu belirler. Test yaparken test veya preprod, uygulama kullanılırken production kullanılmalıdır.
)
)
} catch {
showAlert("BKM Express başlatılamadı.")
}
}
- 'init' çağrısından elde edilen API objesi istenen yerlere paslanarak istenen işlemle ilgili endpointler çağırılabilir.
⚠️ Optional checkleri yapmamak için API'ın kullanılacağı ekrandan bir önceki ekranda oluşturulması ve kullanacak ekrana tercih edilen dependency injection mekanizmasıyla iletilmesi tavsiye edilir.
private func pairButtonTapped() async {
do {
let api = try await BKMExpress.initialize(
context: .init(
authToken: "", // -> Auth tokeniniz.
merchantID: "", // -> Entegrasyon sonucunda size verilen merchant identifier'ınız.
merchantUserID: "", // -> Kullanıcınızı tanımlamak için kullandığınız identifier.
gsmNo: .init("5555555555") // -> Kullanıcınıza ait telefon numarası. Ayrı bir struct olduğu için kendi kurallarını işletir ve sadece verilen telefon numarası şartları sağladığında bir instance'ı yaratılabilir.
mode: .development // -> SDK'nın modunu belirler. Test yaparken test veya preprod, uygulama kullanılırken production kullanılmalıdır.
)
)
// API ile ilgili aksiyon alınır. Örn:
// delegate?.bkmExpressInitialized(api)
// self.onBKMExpressInitialized?(api)
} catch {
showAlert("BKM Express başlatılamadı.")
}
}
2. Kart Numarası Alma
BKM Express'te kullanılabilecek geçerli bir kart numarası sadece BKM Express'in sunduğu kart numarası komponentlerinden elde edilebilir.
⚠️ Kart numarası komponentleri mantık olarak her yerden yaratılabilseler de, geçerli kart numaraları üretebilmeleri için SDK init çağrısının yapılmış olması gerekmektedir.
a. UIKit
UIKit'te kullanılan komponentin adı BKMExpressCardNumberField'dır.
Bu komponent bir UITextField subclassıdır ve subclass edilebilir. Ancak "text" veya "attributedText" gibi endpointler güvenlik sebebiyle kapatılmıştır.
Komponentte yapılan özelleştirmeler sebebiyle komponentin orijinal delegate'i (UITextFieldDelegate) kullanılamamaktadır. Onun yerine bkmExpressDelegate property'si aracılığıyla custom delegate'in kullanılması gerekmektedir.
Zorunlu olan metodlar protokole conform edildiğinde compiler tarafından eklenmektedir. Diğer metodlar protokolün tanımından görülebilir ve ihtiyaca göre conformance eklenebilir.
⚠️ Interface builder kullanılması durumunda "bkmExpressDelegate" ataması koddan yapılmalıdır.
Örnek Kullanım (Code Layout)
// !!! bu controller oluşturulmadan önce başarılı "init" çağrısının yapıldığından emin olunuz.
class Controller: UIViewController, BKMExpressCardNumberFieldDelegate {
let field = BKMExpressCardNumberField()
// diğer UI komponentleriniz
override func viewDidLoad() {
super.viewDidLoad()
field.bkmExpressDelegate = self
}
func bkmExpressCardNumberField(_ field: BKMExpressCardNumberField, didReceiveValidCardNumber cardNumber: BKMExpress.CardNumber) {
// geçerli bir kart numarası elde edildiğinde bu metod çağırılır.
}
}
b. SwiftUI
SwiftUI'da kullanılan komponentin adı BKMExpressCardNumberView'dır.
Aslında UIKit komponentini wrap eden bu view tam anlamıyla bir SwiftUI TextField'ı olmadığı için TextField ile ilgili modifierlar bu view'a etki etmez (textFieldStyle vb.). Bu sebeple, eğer view'ın size sunmuş olduğu özelleştirme imkanları ekranlarınız için yetersiz kalırsa, UIKit komponentini bir UIViewRepresentable ile wrap edebilirsiniz.
Komponentin ihtiyaç duyduğu tüm bilgiler initializer'da istenmektedir.
⚠️ SwiftUI fontları yapıları bakımından UIKit'teki karşılıklarından farklıdır ve iOS 26 öncesinde birbirlerine temiz bir şekilde dönüştürülmesi mümkün değildir. Dolayısıyla komponent iOS 26 öncesinde initializer'ında bir UIFont parametresi gerektirmektedir. iOS 26 sonrasında ise komponente font parametresi vermeden .font() view modifier'ıyla komponentin fontunu değiştirebilir ve SwiftUI fontlarınızı kullanabilirsiniz.
Örnek Kullanım
// !!! bu view oluşturulmadan önce başarılı "init" çağrısının yapıldığından emin olunuz.
struct Screen: View {
@State var number: BKMExpress.CardNumber?
var body: some View {
BKMExpressCardNumberView(
font: .systemFont(ofSize: 15),
placeholder: "Kart Numarası",
number: $number
)
// UI'ınızın geri kalanı
}
}
Komponentin attributedPlaceholder kabul eden bir versiyonu daha bulunmaktadır.
API arayüzünün kullanımı
checkStatus Servisi
Bu servis SDK'nın başlatıldığı telefon numarasına ait kullanıcının durumunu sorgulamak için kullanılır.
func checkBKMExpressStatus() async {
do {
let response = try await api.checkStatus()
switch response {
case let .linked(cards):
// kullanıcı var, hesabı bağlı ve kartları paylaşıldığı gibi.
case .unlinked:
// kullanıcı var ama hesabı bağlı değil.
case .unregistered:
// kullanıcı kayıtlı değil
}
} catch {
logger.log("hata: \(error.message)")
}
}
linkAccount Servisi
Bu servis SDK'nın başlatıldığı telefon numarasına ait kayıtlı kullanıcı olup olmadığını sorgulamak için kullanılan servistir.
func linkBKMExpress() async {
do {
let response = try await api.linkAccount()
switch response {
case .recheck:
// kullanıcının hesabı bağlandı, ancak kart listesinin
// alınması için check isteğinin yeniden atılması gerekiyor.
case let .verificationRequired(otp):
// otp doğrulama gerekiyor.
}
} catch {
logger.log("hata: \(error.message)")
}
}
storeCard Servisi
SDK'nın başlatıldığı telefon numarasına kart eklemek için kullanılan servistir.
func storeCardInBKMExpress() async {
do {
let response = try await api.storeCard(
context: ... // kart ekleme işlemi için gerekli olan bilgileri içerir.
)
switch response {
case let .added(card):
// kart eklendi ve kart eklendiği haliyle kullanıcının kartları paylaşıldığı gibidir.
case let .verificationRequired(otp):
// otp doğrulama gerekiyor.
}
} catch {
logger.log("hata: \(error.message)")
}
}
deleteCard Servisi
Kullanıcının herhangi bir kartını silmek için kullanılan servistir.
func deleteCardFromBKMExpress() async {
do {
let response = try await api.deleteCard(
id: ... // SDK'dan dönen geçerli bir kart idsi
)
switch response {
case let .cardDeleted(card):
// kart silindi ve kartın silindiği haliyle kullanıcının kartları paylaşıldığı gibidir.
case .accountDeleted:
// kullanıcının son kartı ve BKM express hesabı silindi.
}
} catch {
logger.log("hata: \(error.message)")
}
}
register Servisi
SDK'nın başlatıldığı telefon numarasını ilgili kart bilgisiyle "kısmi" kayıt etmek için kullanılan servistir.
func registerToBKMExpress() async {
do {
let response = try await api.register(
context: ... // Kayıt olma işlemi için gerekli olan bilgileri içerir.
)
switch response {
case let .verificationRequired(otp):
// otp doğrulama gerekiyor
}
} catch {
logger.log("hata: \(error.message)")
}
}
controlPayment Servisi
Doğrulama adımı geçildikten sonra ödeme sonucunu kontrol etmek için kullanılan servistir.
func controlBKMExpressPayment() async {
do {
let response = try await api.controlPayment(
token: ... // sadece SDK üzerinden geçerli bir ödeme başlatıldığında elde edilen token.
)
switch response {
case let .verificationRequired(otp):
// otp doğrulama gerekiyor
}
} catch {
logger.log("hata: \(error.message)")
}
}
startPayment Servisi
Kayıtlı bir kartla ödeme işlemi başlatmak için kullanılır.
func startBKMExpressPayment() async {
do {
let response = try await api.startPayment(
context: ... // ödemeyle ilgili bilgileri içeren veri yapısı.
)
switch response {
case let .tds(tds):
// 3ds doğrulama gerekiyor.
case let .otp(otp):
// otp doğrulama gerekiyor.
case let .control(token):
// ödeme sonucu token ile kontrol edilmelidir.
}
} catch {
logger.log("hata: \(error.message)")
}
}
startPaymentWithRegisteredCard Servisi
Kayıtlı olmayan bir kartla ödeme başlatmak ve kartı kullanıcının hesabına kaydetmek için kullanılır.
func startBKMExpressPayment() async {
do {
let response = try await api.startPaymentWithRegisteredCard(
context: ... // ödemeyle ilgili bilgileri içeren veri yapısı.
)
switch response {
case let .tds(tds):
// 3ds doğrulama gerekiyor.
case let .otp(otp):
// otp doğrulama gerekiyor.
case let .control(token):
// ödeme sonucu token ile kontrol edilmelidir.
}
} catch {
logger.log("hata: \(error.message)")
}
}
OTP Doğrulama
OTP doğrulama gerektiren her çağrı, içerisinden onaylama ve yeniden gönderim yapılabilecek bir OTP objesi döndürür.
"verify(code:)" metoduyla OTP'lerinizi onaylayabilir, "resend" metoduyla yeniden kod gönderimi sağlayabilirsiniz.
"resend" çağrısı sonucunda elde edilen OTPInfo objesi OTP'nin objesinin içinde saklanmakta ve çağrı sonucu döndürülmektedir. Mimarinize uygun olan metodu kullanabilirsiniz.
func resendOTP() async {
do {
let info = try await otp.resend()
// UI güncelleme mantıkları
// istenirse "otp.info" ile de elde edilebilir.
} catch {
logger.log("hata: \(error.message)")
}
}
func verifyOTP() async {
do {
let response = try await otp.verify(
code: ... // otp doğrulama için kullanıcıdan alınan kod.
)
// delegate mantıklarınız:
// onOTPConfirmed(response)
// delegate?.otpConfirmed(response)
} catch {
logger.log("hata: \(error.message)")
}
}
Sözleşmeler
Kart ekleme ve kayıt olma esnasında kullanıcıya gösterilmesi gereken sözleşme bilgileri, yaratılan API objesi üzerinden pendingAgreements metoduyla elde edilmelidir.
Elde edilen sözleşmeleri onaylayabilmek için SDK tarafından sağlanan checkbox komponentinin kullanılması gerekmektedir.
⚠️ Checkbox komponentinin UI hiyerarşinizde sadece bir adet bulunduğundan emin olun. Aksi takdirde onay mekanizması beklendiği şekilde çalışmayabilir.
** a. UIKit **
UIKit'te kullanılan komponentin adı BKMExpressAgreementCheckbox'tır ve bir UIControl subclass'ıdır.
Komponentin seçili olup olmadığını anlamak için ve durumunu değiştirmek için isSelected property'si kullanılır. Seçili değerin değişmesi halinde .valueChanged UIControl.Event'i tetiklenir. Bu event'i isterseniz selector ile, isterseniz iOS 14.0+ UIAction API'ı ile dinleyebilirsiniz.
⚠️ Komponenti interface builder uzerinden ekleyebilirsiniz ancak özelleştirmelerin koddan yapılması gerekmektedir.
Örnek Kullanım (Code Layout)
// !!! bu controller oluşturulmadan önce başarılı "init" çağrısının yapıldığından emin olunuz.
class Controller: UIViewController {
let checkbox = BKMExpressAgreementCheckbox()
// diğer UI komponentleriniz
override func viewDidLoad() {
super.viewDidLoad()
// ...
checkbox.addTarget(self, action: #selector(checkboxValueChanged), for: .valueChanged)
}
@objc func checkboxValueChanged(_ checkbox: BKMExpressAgreementCheckbox) {
print("checkbox value is now \(checkbox.isSelected)")
}
}
** b. SwiftUI **
SwiftUI'da kullanılan komponentin adı BKMExpressAgreementCheckboxView'dır.
Komponentin ihtiyaç duyduğu tüm bilgiler initializer'da istenmektedir.
** Örnek Kullanım **
// !!! bu view oluşturulmadan önce başarılı "init" çağrısının yapıldığından emin olunuz.
struct Screen: View {
@State var checboxSelected: Bool = false
var body: some View {
BKMExpressCheckboxView(
isSelected: $checkboxSelected
)
// UI'ınızın geri kalanı
}
}
Hata Yapısı
API
API'den döndürülen hataların tümü BKMExpress.Failure tipinde ve "typed throws" özelliği kullanılarak fırlatılır.
⚠️ BKMExpress.Failure objesi LocalizedError'a conform ettiği için "localizedDescription" değişkeniyle kullanıcıya gösterilebilecek metin elde edilebilir ancak metinler Türkçedir. "localizedDescription" değişkeni "message" değişkeninin değeriyle aynıdır.
Kart Numarası Komponentleri
Komponentlerden kart numarası elde etmeye çalışırken dönen hatalar BKMExpress.CardNumberFailure tipinde "typed throws" özelliği kullanılarak fırlatılır.
⚠️ Bu hatalar özellikle lokalize edilmemiştir, dolayısıyla son kullanıcıya "localizedDescription" parametresiyle gösterim yapılmamalıdır. Enumeration'un her bir değeri için kendi belirlediğiniz mesajları, mimarinize uygun error handling mekanizmanızla kullanıcıya göstermeniz gerekmektedir.