Mobile SDK Core
Това е по-скоро библиотека, отколкото SDK. Тя се използва за шифроване на данните на картата в seToken, който може безопасно да се предаде и съхрани в която и да е система (например на сървъра на продавача). Това е добро решение за продавачи, които искат максимална гъвкавост или не искат да се занимават с данните на картата на своите сървъри.
Начини на плащане, поддържани от този SDK:
| Начин на плащане | Поддръжка |
|---|---|
| Банкова карта | Да |
| Съхранени потребителски данни/карта | Да |
| Apple Pay | Не се изисква |
| Google Pay | Не се изисква |
Процес на плащане SDK Core
Web View за 3DS
На приведената по-долу диаграма е показан процесът на плащане SDK Core с пренасочване 3DS чрез Web View.
- Клиентът създава поръчка
- Мобилният сървър регистрира тази поръчка в платежния шлюз чрез register.do. Използвайте параметъра
returnUrlкато маркер за затваряне на Web View след пренасочването от ACS на Стъпка 14. - Мобилният сървър получава в отговора уникален номер на поръчка
mdOrder. - Клиентът попълва платежните данни в мобилното приложение.
-
Мобилното приложение извиква SDK за създаване на seToken (secure token). (Android: sdkCore.generateWithCard; iOS: CKCToken.generateWithCard).
Публичният ключ, който се изисква в съответния метод, трябва да се взема от online ресурса https://uat.dskbank.bg/payment/se/keys.do. Ако на този линк са достъпни няколко ключа, следва да се използва първият ключ. (Имайте предвид, че за тестовата и работната среда се използват различни ключове.)
Мобилното приложение изпраща seToken на мобилния сървър.
-
Мобилният сървър използва този seToken за извършване на плащане чрез paymentorder.do.
- Използвайте seToken вместо pan, cvc и дата на изтичане.
- Не забравяйте да посочите името на притежателя на картата в полето
TEXT. Ако не събирате името на притежателя на картата, просто изпратете стойносттаCARDHOLDER.
Мобилният сървър получава отговор без пренасочване към ACS. Това означава, че плащането е завършено и трябва да преминем към Стъпка 16.
Мобилният сървър получава отговор с пренасочване към ACS.
Мобилното приложение отваря Web View с данните за пренасочване към ACS.
Клиентът въвежда своята еднократна парола във формата на ACS.
ACS пренасочва клиента към платежния шлюз.
Платежният шлюз осъществява плащането.
Платежният шлюз пренасочва клиента към
returnUrl, който може да се използва като маркер за затваряне на Web View.Мобилното приложение затваря Web View.
Платежният шлюз изпраща известие за обратна връзка на сървъра на продавача, ако е настроено за продавача.
Мобилният сървър проверява окончателния статус на плащането чрез getOrderStatusExtended.do.
Мобилното приложение показва резултата от плащането на клиента.
IOS
iOS-интеграция
Интеграция SDKCore.framework
Можете да интегрирате SDKCore.framework, като го добавите ръчно.
SDKCore.framework
Изтеглете последната версия на фреймуорка тук.
Изберете файл
SDKCore.frameworkи го добавете в папката на проекта.
- Отворете страницата Targets -> General -> Frameworks, Libraries, and Embedded Content. За
SDKCore.frameworkв колона Embed заменетеDo not EmbedсEmbed & Sign.
След това импортирайте фреймуорка във файла
ViewController.swift .
//ViewController.swift
...
import SDKCore
...Работа с API V1
Външни зависимости
За генериране на токен е необходимо да се зададе открит ключ.
let publicKey: String =
"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIITqh9xlGx4tWA+aucb0V0YuFC9aXzJb0epdioSkq3qzNdRSZIxe/dHqcbMN2SyhzvN6MRVl3xyjGAV+lwk8poD4BRW3VwPUkT8xG/P/YLzi5N8lY6ILlfw6WCtRPK5bKGGnERcX5dqL60LhOPRDSYT5NHbbp/J2eFWyLigdU9Sq7jvz9ixOLh6xD7pgNgHtnOJ3Cw0Gqy03r3+m3+CBZwrzcp7ZFs41bit7/t1nIqgx78BCTPugap88Gs+8ZjdfDvuDM+/3EwwK0UVTj0SQOv0E5KcEHENL9QQg3ujmEi+zAavulPqXH5907q21lwQeemzkTJH4o2RCCVeYO+YrQIDAQAB-----END PUBLIC KEY-----"Метод за генериране на токен
let sdkCore = SdkCore()
let cardParams = CardParams(
pan: "4111111111111111",
cvc: "123",
expiryMMYY: "12/28",
cardholder: "TEST CARDHOLDER",
mdOrder: "mdOrder",
pubKey: publicKey
)
let cardParamsConfig = SDKCoreConfig(
paymentMethodParams: .cardParams(params: cardParams)
)
let tokenResult = sdkCore.generateWithConfig(config: cardParamsConfig)
let bindignParams = BindingParams(
pubKey: publicKey,
bindingId: "das",
cvc: "123",
mdOrder: "mdOrder"
)
let bindingParamsConfig = SDKCoreConfig(
paymentMethodParams: .bindingParams(params: bindignParams)
)
let tokenResult = sdkCore.generateWithConfig(config: bindingParamsConfig)Модели
CardParams
| Име на свойство | Тип данни | Стойност по подразбиране | Незадължително | Описание |
|---|---|---|---|---|
| mdOrder | String | - | Не | Номер на поръчката |
| pan | String | - | Не | Номер на картата |
| cvc | String | - | Не | Таен код на картата |
| expiryMMYY | String | - | Не | Срок на валидност на картата |
| cardHolder | String | - | Да | Име и фамилия на притежателя на картата |
| pubKey | String | - | Не | Публичен ключ |
BindingParams
| Име на свойство | Тип данни | Стойност по подразбиране | Незадължително | Описание |
|---|---|---|---|---|
| mdOrder | String | - | Не | Номер на поръчката |
| bindingId | String | - | Не | Номер на запазените платежни данни за карта |
| cvc | String | - | Да | Таен код на картата |
| pubKey | String | - | Не | Публичен ключ |
Грешки при валидация на полетата
| ParamField | Грешка | Описание |
|---|---|---|
| UNKNOWN | - | Неизвестна грешка |
| PAN | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| invalid-format | Използват се недопустими символи. Достъпни са само цифри. | |
| CVC | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| EXPIRY | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| invalid-format | Форматът не съответства на шаблона MM/YY | |
| CARDHOLDER | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| invalid-format | Използват се недопустими символи. Достъпни са само букви и интервали. | |
| BINDING_ID | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| MD_ORDER | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| PUB_KEY | required | Посочено е празно поле |
Android
Android-интеграция
Свързване към Gradle проект чрез добавяне на .aar файлове на библиотеката
Необходимо е да добавите файла на библиотеката sdk_core-release.aar в папката libs, а след това да посочите зависимостта на добавената библиотека.
build.gradle.kts
allprojects {
repositories {
// ...
flatDir {
dirs("libs")
}
}
}
dependencies {
// dependency is mandatory to add
implementation(group = "", name = "sdk_core-release", ext = "aar")
}build.gradle
allprojects {
repositories {
// ...
flatDir {
dirs 'libs'
}
}
}
dependencies {
// dependency is mandatory to add
implementation(group: '', name: 'sdk_core-release', ext: 'aar')
}Конфигурация за Android
Логиране
Вътрешните процеси се логират с таг SDK-Core.
Можете също така да логирате собствените си процеси.
Логирането е достъпно чрез обекта Logger.
-
За добавяне на log- интерфейси е необходимо да се извика
Logger-методътaddLogInterface().Пример за логиране в LogCat:
...
Logger.addLogInterface(object : LogInterface {
override fun log(classMethod: Class<Any>, tag: String, message: String, exception: Exception?) {
Log.i(tag, "$classMethod: $message", exception)
}
})
... По подразбиране се използва тагът SDK-Core. Можете да зададете свой собствен, ако желаете.
-
За логиране на собствени събития е необходимо да се извика
Logger-методътlog().Пример:
...
Logger.log(this.javaClass, "MyTag", "My process...", null)
...Пример Kotlin_core (без графичен интерфейс)
Пример за формиране на криптограма
import net.payrdr.mobile.payment.sdk.core.SDKCore
import net.payrdr.mobile.payment.sdk.core.TokenResult
import net.payrdr.mobile.payment.sdk.core.model.BindingParams
import net.payrdr.mobile.payment.sdk.core.model.CardParams
import net.payrdr.mobile.payment.sdk.core.validation.BaseValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardCodeValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardExpiryValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardHolderValidator
import net.payrdr.mobile.payment.sdk.core.validation.CardNumberValidator
import net.payrdr.mobile.payment.sdk.core.validation.OrderNumberValidator
class MainActivity : AppCompatActivity() {
// initialization of validators for card information entry fields
private val cardNumberValidator by lazy { CardNumberValidator(this) }
private val cardExpiryValidator by lazy { CardExpiryValidator(this) }
private val cardCodeValidator by lazy { CardCodeValidator(this) }
private val cardHolderValidator by lazy { CardHolderValidator(this) }
private val orderNumberValidator by lazy { OrderNumberValidator(this) }
private val sdkCore by lazy { SDKCore(context = this) }
override fun onCreate(savedInstanceState: Bundle?) {
// инсталиране на валидатори на полетата за въвеждане на информация за карта
cardNumberInput.setupValidator(cardNumberValidator)
cardExpiryInput.setupValidator(cardExpiryValidator)
cardCodeInput.setupValidator(cardCodeValidator)
cardHolderInput.setupValidator(cardHolderValidator)
mdOrderInput.setupValidator(orderNumberValidator)
// създаване на обект и инициализация на полета за нова карта
val params = NewPaymentMethodCardParams(
pan = cardNumberInput.text.toString(),
cvc = cardCodeInput.text.toString(),
expiryMMYY = cardExpiryInput.text.toString(),
cardHolder = cardHolderInput.text.toString(),
pubKey = pubKeyInput.text.toString()
)
// извикване на метод за получаване на криптограма за нова карта
sdkCore.generateWithConfig(SDKCoreConfig(params))
// Създаване на обект и инициализация на полета за свързана карта
val params = NewPaymentMethodStoredCardParams(
storedPaymentId = "storedPaymentMethodId",
cvc = "123",
pubKey = pubKeyInput.text.toString()
)
// извикване на метод за получаване на криптограма за свързаната карта
sdkCore.generateWithConfig(SDKCoreConfig(params))
}
}Модели
NewPaymentMethodCardParams
| Име на свойството | Тип данни | Стойност по подразбиране | Незадължително | Описание |
|---|---|---|---|---|
| pan | String | - | Не | Номер на карта |
| cvc | String | - | Не | Таен код на карта |
| expiryMMYY | String | - | Не | Срок на валидност на карта |
| cardHolder | String | - | Не | Име и фамилия на притежателя на карта |
| pubKey | String | - | Не | Публичен ключ |
NewPaymentMethodStoredCardParams
| Име на свойството | Тип данни | Стойност по подразбиране | Незадължително | Описание |
|---|---|---|---|---|
| storedPaymentId | String | - | Не | Номер на съхранени платежни данни за карта |
| cvc | String | - | Не | Таен код на карта |
| pubKey | String | - | Не | Публичен ключ |
TokenResult
| Име на свойството | Тип данни | Стойност по подразбиране | Незадължително | Описание |
|---|---|---|---|---|
| token | String | - | Не | Token като низ |
| errors | Map |
- | Не | Грешка при генериране на token |
Грешки при валидиране на полета
| ParamField | Грешка | Описание |
|---|---|---|
| PAN | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| invalid-format | Използват се недопустими символи. Достъпни са само цифри. | |
| CVC | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| EXPIRY | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| invalid-format | Форматът не съответства на шаблона MM/YY. | |
| CARDHOLDER | required | Посочено е празно поле |
| invalid | Некоректна стойност | |
| invalid-format | Използват се недопустими символи. Достъпни са само букви и интервали. | |
| PUB_KEY | required | Посочено е празно поле |
| STORED_PAYMENT_ID | requrired | Посочено е празно поле |
| invalid | Некоректна стойност |
Работа с API V1
Външни зависимости
За генериране на token е необходимо да се зададе публичен ключ.
val publicKey: String =
"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIITqh9xlGx4tWA+aucb0V0YuFC9aXzJb0epdioSkq3qzNdRSZIxe/dHqcbMN2SyhzvN6MRVl3xyjGAV+lwk8poD4BRW3VwPUkT8xG/P/YLzi5N8lY6ILlfw6WCtRPK5bKGGnERcX5dqL60LhOPRDSYT5NHbbp/J2eFWyLigdU9Sq7jvz9ixOLh6xD7pgNgHtnOJ3Cw0Gqy03r3+m3+CBZwrzcp7ZFs41bit7/t1nIqgx78BCTPugap88Gs+8ZjdfDvuDM+/3EwwK0UVTj0SQOv0E5KcEHENL9QQg3ujmEi+zAavulPqXH5907q21lwQeemzkTJH4o2RCCVeYO+YrQIDAQAB-----END PUBLIC KEY-----"Метод за генериране на token
// TokenResult with CardParams
val cardParams: CardParams = CardParams(
mdOrder = "mdOrder",
pan = "4111111111111111",
cvc = "123",
expiryMMYY = "12/28",
cardHolder = "TEST CARDHOLDER",
pubKey = "publicKey"
)
val tokenResult = sdkCore.generationWithConfig(paymentCardParams = cardParams)
// TokenResult with BindingParams
val bindingParams: BindingParams = BindingParams(
mdOrder = "mdOrder",
bindingID = "das",
cvc = "123",
pubKey = "publicKey"
)
val tokenResult = sdkCore.generationWithConfig(paymentCardParams = bindingParams)Кросплатформени фреймуърци
Mobile SDK Core може да се използва не само в нативни приложения: извикванията на SDK се прехвърлят в нативния код чрез мост, който декларирате от страната на приложението.
React Native
За реализацията на интеграция на Mobile SDK Core в React Native приложение е необходимо да настроите мост — обвивка на Kotlin/Java или Swift/Objective-C, която ще приема извиквания от JavaScript и ще ги предава в нативния код на SDK.
Мостовете в React Native позволяват на вашия JS код да извиква методи на нативни библиотеки толкова просто, колкото обикновените асинхронни функции: вие декларирате нативен модул с методи, които връщат резултат чрез Promise, React Native автоматично свързва тези методи с JavaScript, и след това работите с тях в приложението без дълбоки познания за платформените API.
IOS
Интегрирайте SDKCore във вашия проект в съответствие с раздел iOS-интеграция.
Проверете, че за фреймуорка
SDKCore.xcframeworkв колоната Embed е избрано Embed & Sign.-
Създайте Swift модул
RadarSdkBridge.swiftсъс следното съдържание:import Foundation import React import SDKCore @objc(RadarSdk) class RadarSdkBridge: NSObject { private let sdkCore = SdkCore() @objc func generateNewCardToken( _ pan: String, cvc: String, expiryMMYY: String, cardHolder: String, mdOrder: String, pubKey: String, resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock ) { do { let params = CardParams( pan: pan, cvc: cvc, expiryMMYY: expiryMMYY, cardholder: cardHolder, mdOrder: mdOrder, pubKey: pubKey ) let config = SDKCoreConfig(paymentMethodParams: .cardParams(params: params)) let result = try sdkCore.generateWithConfig(config: config) resolve(result.token) } catch { reject("TOKEN_ERROR", error.localizedDescription, error) } } @objc func generateStoredCardToken( _ storedPaymentId: String, cvc: String, pubKey: String, mdOrder: String, resolver resolve: RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock ) { do { let params = BindingParams( pubKey: pubKey, bindingId: storedPaymentId, cvc: cvc, mdOrder: mdOrder ) let config = SDKCoreConfig(paymentMethodParams: .bindingParams(params: params)) let result = try sdkCore.generateWithConfig(config: config) resolve(result.token) } catch { reject("TOKEN_ERROR", error.localizedDescription, error) } } @objc static func requiresMainQueueSetup() -> Bool { return false } } -
Реализирайте Objective-C мост в файла
RadarSdkBridge.m:#import <React/RCTBridgeModule.h> @interface RCT_EXTERN_MODULE(RadarSdk, NSObject) RCT_EXTERN_METHOD(generateNewCardToken: (NSString *)pan cvc:(NSString *)cvc expiryMMYY:(NSString *)expiryMMYY cardHolder:(NSString *)cardHolder mdOrder:(NSString *)mdOrder pubKey:(NSString *)pubKey resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject ) RCT_EXTERN_METHOD(generateStoredCardToken: (NSString *)storedPaymentId cvc:(NSString *)cvc pubKey:(NSString *)pubKey mdOrder:(NSString *)mdOrder resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject ) @end
Android
Изтеглете файловете
sdk_core-release.aarиsdk_logs-release.aarи ги копирайте в папкатаandroid/app/libs/според раздел Android-интеграция.-
Добавете плоския репозиторий и зависимостите във файла
android/app/build.gradle:android { // … останалата конфигурация на вашия проект … } repositories { flatDir { dirs 'libs' } } dependencies { // … други зависимости … implementation(name: 'sdk_core-release', ext: 'aar') implementation(name: 'sdk_logs-release', ext: 'aar') }
Реализация на Native Module за Kotlin
-
Създайте файла
RadarSdkModule.ktв директориятаandroid/app/src/main/java/.../radar/:package com.mobilesdkdemo.radar import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import net.payrdr.mobile.payment.sdk.core.SDKCore import net.payrdr.mobile.payment.sdk.core.model.SDKCoreConfig import net.payrdr.mobile.payment.sdk.core.model.CardParams import net.payrdr.mobile.payment.sdk.core.model.BindingParams class RadarSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { override fun getName(): String = "RadarSdk" private val sdkCore by lazy { SDKCore(context = reactApplicationContext) } @ReactMethod fun generateNewCardToken( pan: String, cvc: String, expiryMMYY: String, cardHolder: String, mdOrder: String, pubKey: String, promise: Promise ) { try { val params = CardParams( pan = pan, cvc = cvc, expiryMMYY = expiryMMYY, cardHolder = cardHolder, mdOrder = mdOrder, pubKey = pubKey ) val config = SDKCoreConfig(paymentCardParams = params) val result = sdkCore.generateWithConfig(config) promise.resolve(result.token) } catch (e: Exception) { promise.reject("TOKEN_ERROR", e) } } @ReactMethod fun generateStoredCardToken( storedPaymentId: String, cvc: String, pubKey: String, mdOrder: String, promise: Promise ) { try { val params = BindingParams( bindingID = storedPaymentId, mdOrder = mdOrder, cvc = cvc, pubKey = pubKey ) val config = SDKCoreConfig(paymentCardParams = params) val result = sdkCore.generateWithConfig(config) promise.resolve(result.token) } catch (e: Exception) { promise.reject("TOKEN_ERROR", e) } } } -
Създайте пакета
RadarSdkPackage.ktв същата директория:package com.mobilesdkdemo.radar import com.facebook.react.ReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.uimanager.ViewManager class RadarSdkPackage : ReactPackage { override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> = listOf(RadarSdkModule(reactContext)) override fun createViewManagers( reactContext: ReactApplicationContext ): List<ViewManager<*, *>> = emptyList() } -
Регистрирайте създадения пакет в
MainApplication.kt:package com.mobilesdkdemo import android.app.Application import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactNativeHost import com.facebook.react.ReactHost import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.soloader.SoLoader import com.mobilesdkdemo.radar.RadarSdkPackage class MainApplication : Application(), ReactApplication { override val reactNativeHost: ReactNativeHost = object : DefaultReactNativeHost(this) { override fun getPackages(): List<ReactPackage> = PackageList(this).packages.apply { add(RadarSdkPackage()) // Регистрираме пакета } override fun getJSMainModuleName(): String = "index" override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED } override val reactHost: ReactHost get() = getReactNativeHost(applicationContext, reactNativeHost) override fun onCreate() { super.onCreate() SoLoader.init(this, false) if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { load() } } }
Използване
Използвайте методите от JavaScript/TypeScript по следния начин:
import { NativeModules } from 'react-native';
const { RadarSdk } = NativeModules;
// Генериране на токен за нова карта
const token = await RadarSdk.generateNewCardToken(
'4111111111111111', // PAN
'123', // CVC
'12/25', // Expiry MM/YY
'CARDHOLDER NAME', // Име на притежателя на картата
'mdOrder', // Номер, получен при регистрация на поръчката
'-----BEGIN PUBLIC KEY-----...' // Публичен ключ от /payment/se/keys.do
);
// Генериране на токен за свързана карта
const bindToken = await RadarSdk.generateStoredCardToken(
'bindingId', // ID на запазената карта
'123', // CVC
'-----BEGIN PUBLIC KEY-----…', // Публичен ключ от /payment/se/keys.do
'mdOrder' // Номер, получен при регистрация на поръчката
);Демо приложение
Пример за интеграция на Mobile Sdk Core в приложение на React Native може да се види на GitHub.
Flutter
За реализиране на интеграцията на Mobile SDK Core в Flutter приложение е необходимо да настроите platform channel — канал, чрез който Dart кодът извиква native методи на SDK на Android и iOS.
Platform channel свързва Dart и native кода по име на канала: вие декларирате MethodChannel с едно и също име на двете страни, извиквате метод от Dart и получавате резултата чрез Future. Името на канала е единственото, което свързва страните, затова при несъвпадение извикването ще завърши с изключение MissingPluginException в runtime, а не с грешка при компилация.
Аргументите се предават като Map<String, dynamic> и се разбират на native страната по имена на ключовете. Чрез канала преминават само стойности, разбираеми от стандартния кодек — примитиви, списъци и речници; обектите на SDK се създават на native страната и не се предават чрез канала.
iOS
Интегрирайте SDKCore във вашия проект в съответствие с раздел iOS-интеграция.
Проверете, че за framework
SDKCore.xcframeworkв колоната Embed е избрано Embed & Sign. Framework-ът вътре вxcframeworkе динамична библиотека: ако само се свърже, но не се вгради, проектът ще се компилира, а приложението ще се срине при стартиране.-
Създайте файл
RadarSdkBridge.swiftв targetRunner:import Flutter import Foundation import SDKCore final class RadarSdkBridge: NSObject { static let channelName = "radar_sdk" private let channel: FlutterMethodChannel private let sdkCore = SdkCore() init(messenger: FlutterBinaryMessenger) { channel = FlutterMethodChannel(name: RadarSdkBridge.channelName, binaryMessenger: messenger) super.init() channel.setMethodCallHandler { [weak self] call, result in self?.handle(call, result: result) } } private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "generateNewCardToken": generateNewCardToken(call, result: result) case "generateStoredCardToken": generateStoredCardToken(call, result: result) default: result(FlutterMethodNotImplemented) } } private func generateNewCardToken(_ call: FlutterMethodCall, result: @escaping FlutterResult) { guard let args = call.arguments as? [String: Any], let pan = args["pan"] as? String, let cvc = args["cvc"] as? String, let expiryMMYY = args["expiryMMYY"] as? String, let cardHolder = args["cardHolder"] as? String, let mdOrder = args["mdOrder"] as? String, let pubKey = args["pubKey"] as? String else { result(FlutterError(code: "TOKEN_ERROR", message: "Invalid arguments", details: nil)) return } do { let params = CardParams( pan: pan, cvc: cvc, expiryMMYY: expiryMMYY, cardholder: cardHolder, mdOrder: mdOrder, pubKey: pubKey ) let config = SDKCoreConfig(paymentMethodParams: .cardParams(params: params)) let tokenResult = try sdkCore.generateWithConfig(config: config) result(tokenResult.token) } catch { result(FlutterError(code: "TOKEN_ERROR", message: error.localizedDescription, details: nil)) } } private func generateStoredCardToken(_ call: FlutterMethodCall, result: @escaping FlutterResult) { guard let args = call.arguments as? [String: Any], let storedPaymentId = args["storedPaymentId"] as? String, let cvc = args["cvc"] as? String, let pubKey = args["pubKey"] as? String, let mdOrder = args["mdOrder"] as? String else { result(FlutterError(code: "TOKEN_ERROR", message: "Invalid arguments", details: nil)) return } do { let params = BindingParams( pubKey: pubKey, bindingId: storedPaymentId, cvc: cvc, mdOrder: mdOrder ) let config = SDKCoreConfig(paymentMethodParams: .bindingParams(params: params)) let tokenResult = try sdkCore.generateWithConfig(config: config) result(tokenResult.token) } catch { result(FlutterError(code: "TOKEN_ERROR", message: error.localizedDescription, details: nil)) } } } -
Регистрирайте моста в
AppDelegate.swift:import Flutter import UIKit @main @objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { // Мостът трябва да се пази в свойство: обект, създаден в локална // променлива, се освобождава и мълчаливо престава да отговаря на извиквания от Dart. private var radarSdkBridge: RadarSdkBridge? func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) if let messenger = engineBridge.pluginRegistry.registrar(forPlugin: "RadarSdkBridge")?.messenger() { radarSdkBridge = RadarSdkBridge(messenger: messenger) } } }
Android
Свалете файловете
sdk_core-release.aarиsdk_logs-release.aarи ги копирайте в папкатаandroid/app/libs/според раздел Android-интеграция.-
Добавете зависимостите в файла
android/app/build.gradle.kts:android { // … останалата конфигурация на вашия проект … } dependencies { // … други зависимости … implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar")))) } -
Създайте файл
RadarSdkBridge.ktв директориятаandroid/app/src/main/kotlin/.../:package com.mobilesdkdemo import android.content.Context import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import net.payrdr.mobile.payment.sdk.core.SDKCore import net.payrdr.mobile.payment.sdk.core.model.BindingParams import net.payrdr.mobile.payment.sdk.core.model.CardParams import net.payrdr.mobile.payment.sdk.core.model.SDKCoreConfig private const val CHANNEL_NAME = "radar_sdk" class RadarSdkBridge(private val context: Context) : MethodChannel.MethodCallHandler { private val sdkCore by lazy { SDKCore(context = context) } fun register(messenger: BinaryMessenger) { MethodChannel(messenger, CHANNEL_NAME).setMethodCallHandler(this) } override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { "generateNewCardToken" -> generateNewCardToken(call, result) "generateStoredCardToken" -> generateStoredCardToken(call, result) else -> result.notImplemented() } } private fun generateNewCardToken(call: MethodCall, result: MethodChannel.Result) { try { val params = CardParams( pan = call.argument<String>("pan")!!, cvc = call.argument<String>("cvc")!!, expiryMMYY = call.argument<String>("expiryMMYY")!!, cardHolder = call.argument<String>("cardHolder")!!, mdOrder = call.argument<String>("mdOrder")!!, pubKey = call.argument<String>("pubKey")!! ) val config = SDKCoreConfig(paymentCardParams = params) val token = sdkCore.generateWithConfig(config).token result.success(token) } catch (e: Exception) { result.error("TOKEN_ERROR", e.message, null) } } private fun generateStoredCardToken(call: MethodCall, result: MethodChannel.Result) { try { val params = BindingParams( bindingID = call.argument<String>("storedPaymentId")!!, mdOrder = call.argument<String>("mdOrder")!!, cvc = call.argument<String>("cvc")!!, pubKey = call.argument<String>("pubKey")!! ) val config = SDKCoreConfig(paymentCardParams = params) val token = sdkCore.generateWithConfig(config).token result.success(token) } catch (e: Exception) { result.error("TOKEN_ERROR", e.message, null) } } } -
Регистрирайте моста в
MainActivity.kt:package com.mobilesdkdemo import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) RadarSdkBridge(applicationContext).register(flutterEngine.dartExecutor.binaryMessenger) } }
Използване
-
Създайте обвивка над канала в
lib/radar_sdk.dart. Името на канала и имената на ключовете в аргументите трябва да съвпадат с нативната страна:import 'package:flutter/services.dart'; class RadarSdk { const RadarSdk._(); static const MethodChannel _channel = MethodChannel('radar_sdk'); static Future<String?> generateNewCardToken({ required String pan, required String cvc, required String expiryMMYY, required String cardHolder, required String mdOrder, required String pubKey, }) { return _channel.invokeMethod<String>('generateNewCardToken', { 'pan': pan, 'cvc': cvc, 'expiryMMYY': expiryMMYY, 'cardHolder': cardHolder, 'mdOrder': mdOrder, 'pubKey': pubKey, }); } static Future<String?> generateStoredCardToken({ required String storedPaymentId, required String cvc, required String pubKey, required String mdOrder, }) { return _channel.invokeMethod<String>('generateStoredCardToken', { 'storedPaymentId': storedPaymentId, 'cvc': cvc, 'pubKey': pubKey, 'mdOrder': mdOrder, }); } } -
Използвайте методите от Dart по следния начин:
// Генериране на токен за нова карта final token = await RadarSdk.generateNewCardToken( pan: '4111111111111111', // PAN cvc: '123', // CVC expiryMMYY: '12/25', // Срок на валидност MM/YY cardHolder: 'CARDHOLDER NAME', // Име на притежателя на картата mdOrder: 'mdOrder', // Номер, получен при регистрация на поръчката pubKey: '-----BEGIN PUBLIC KEY-----...', // Публичен ключ от /payment/se/keys.do ); // Генериране на токен за свързана карта final bindToken = await RadarSdk.generateStoredCardToken( storedPaymentId: 'bindingId', // ID на съхранената карта cvc: '123', // CVC pubKey: '-----BEGIN PUBLIC KEY-----…', // Публичен ключ от /payment/se/keys.do mdOrder: 'mdOrder', // Номер, получен при регистрация на поръчката );
Грешките от нативната страна идват в Dart като PlatformException: при неуспешно генериране на токен мостът отговаря с код TOKEN_ERROR, а в полето message се предава текстът на грешката от SDK.
try {
final token = await RadarSdk.generateNewCardToken(/* … */);
} on PlatformException catch (e) {
// e.code == 'TOKEN_ERROR', e.message — текст на грешката от SDK
}Демо приложение
Пример за интеграция на Mobile SDK Core в приложение на Flutter може да се види в GitHub.
Mobile SDK Source
iOS
Изисквания: iOS 10.0 или по-висока версия
Android
Изисквания: Android 5.0 или по-висока версия