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://abby.rbsuat.com/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?) {
// installation of validators on the card information entry fields
cardNumberInput.setupValidator(cardNumberValidator)
cardExpiryInput.setupValidator(cardExpiryValidator)
cardCodeInput.setupValidator(cardCodeValidator)
cardHolderInput.setupValidator(cardHolderValidator)
mdOrderInput.setupValidator(orderNumberValidator)
// creation of an object and initialization of fields for a new card
val params = NewPaymentMethodCardParams(
pan = cardNumberInput.text.toString(),
cvc = cardCodeInput.text.toString(),
expiryMMYY = cardExpiryInput.text.toString(),
cardHolder = cardHolderInput.text.toString(),
pubKey = pubKeyInput.text.toString()
)
// method call to get the cryptogram for a new card
sdkCore.generateWithConfig(SDKCoreConfig(params))
// Creation of an object and initialization of fields for the linked card
val params = NewPaymentMethodStoredCardParams(
storedPaymentId = "storedPaymentMethodId",
cvc = "123",
pubKey = pubKeyInput.text.toString()
)
// method call to get the cryptogram for the linked card
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 | - | Нет | Токен как строка |
| errors | Map<ParamField, String> | - | Нет | Ошибка при генерации токена |
Ошибки валидации полей
| 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
Внешние зависимости
Для генерации токена необходимо установить открытый ключ.
val publicKey: String =
"-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoIITqh9xlGx4tWA+aucb0V0YuFC9aXzJb0epdioSkq3qzNdRSZIxe/dHqcbMN2SyhzvN6MRVl3xyjGAV+lwk8poD4BRW3VwPUkT8xG/P/YLzi5N8lY6ILlfw6WCtRPK5bKGGnERcX5dqL60LhOPRDSYT5NHbbp/J2eFWyLigdU9Sq7jvz9ixOLh6xD7pgNgHtnOJ3Cw0Gqy03r3+m3+CBZwrzcp7ZFs41bit7/t1nIqgx78BCTPugap88Gs+8ZjdfDvuDM+/3EwwK0UVTj0SQOv0E5KcEHENL9QQg3ujmEi+zAavulPqXH5907q21lwQeemzkTJH4o2RCCVeYO+YrQIDAQAB-----END PUBLIC KEY-----"Метод генерации токена
// 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-код вызывает нативные методы SDK на Android и iOS.
Platform channel связывает Dart и нативный код по имени канала: вы объявляете MethodChannel с одним и тем же именем на обеих сторонах, вызываете метод из Dart и получаете результат через Future. Имя канала — единственное, что связывает стороны, поэтому при несовпадении вызов завершится исключением MissingPluginException в рантайме, а не ошибкой компиляции.
Аргументы передаются как Map<String, dynamic> и разбираются на нативной стороне по именам ключей. Через канал проходят только значения, понятные стандартному кодеку — примитивы, списки и словари; объекты SDK создаются на нативной стороне и через канал не передаются.
iOS
-
Интегрируйте SDKCore в ваш проект в соответствии с разделом iOS-интеграция.
-
Проверьте, что для фреймворка
SDKCore.xcframeworkв колонке Embed выбрано Embed & Sign. Фреймворк внутри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 или выше