Mobile SDK Core

Това е по-скоро библиотека, отколкото SDK. Тя се използва за криптиране на данни от карта в seToken, който може безопасно да се предаде и съхрани във всяка система (например на сървър на търговец). Това е добро решение за търговци, които искат максимална гъвкавост или не искат да се занимават с данни от карти на своите сървъри.

Начини на плащане, поддържани от този SDK:

Начин на плащане Поддръжка
Банкова карта Да
Запазени данни за достъп/карта Да
Apple Pay Не се изисква
Google Pay Не се изисква

Процес на плащане SDK Core

Web View за 3DS

На диаграмата по-долу е показан процесът на плащане SDK Core с пренасочване 3DS чрез Web View.

sequenceDiagram participant MA as Мобилно приложение participant MS as Мобилен сървър participant SDK as SDK participant PG as Платежен шлюз participant 3DS as 3DSS/ACS/DS MA ->> MS: 1. клиент създава поръчка MS ->> PG: 2. регистрация на поръчка чрез API PG -->> MS: 3. уникален номер на поръчка (mdOrder) MA ->> MA: 4. клиент въвежда данни MA -->> SDK: 5. генериране на seToken MA ->> MS: 6. изпращане на seToken на сървър MS ->> PG: 7. извикване на платежен API alt Плащането е завършено PG -->> MS: 8. отговор със статус на плащането (Преход към 16) else изисква се 3DS2 PG -->> MS: 9. отговор с пренасочване към 3DS MS ->> MA: 10. отваряне на Web View за 3DS MA ->> ACS: 11. клиент въвежда парола ACS -->> PG: 12. пренасочване към платежен шлюз PG ->> PG: 13. плащане PG -->> MA: 14. пренасочване към returnUrl MA ->> MA: 15. затваряне на Web View end opt Callback-уведомления са настроени PG -->> MS: 16. callback-уведомление end MS ->> PG: 17. проверка на статус на плащането MS ->> MA: 18. показване на резултат от плащането на клиента
  1. Клиент създава поръчка
  2. Мобилен сървър регистрира тази поръчка в платежния шлюз чрез register.do. Използвайте параметър returnUrl като маркер за затваряне на Web View след пренасочване от ACS на Стъпка 14.
  3. Мобилен сървър получава в отговора уникален номер на поръчка mdOrder.
  4. Клиент попълва платежни данни в мобилното приложение.
  5. Мобилното приложение извиква SDK за създаване на seToken. (Android: sdkCore.generateWithCard; iOS: CKCToken.generateWithCard).

    Публичният ключ, който се изисква в съответния метод, трябва да се взема от online ресурс https://uat.dskbank.bg/payment/se/keys.do. Ако по тази връзка са достъпни няколко ключа, трябва да се използва първият ключ. (Имайте предвид, че за тестова и работна среда се използват различни ключове.)

  6. Мобилното приложение изпраща seToken на мобилния сървър.

  7. Мобилен сървър използва този seToken за извършване на плащане чрез paymentorder.do.

    • Използвайте seToken вместо pan, cvc и дата на изтичане на срока на валидност.
    • Не забравяйте да укажете име на притежател на карта в поле TEXT. Ако не събирате име на притежател на карта, просто изпратете стойност CARDHOLDER.
  8. Мобилният сървър получава отговор без пренасочване към ACS. Това означава, че плащането е завършено и трябва да преминем към Стъпка 16.

  9. Мобилният сървър получава отговор с пренасочване към ACS.

  10. Мобилното приложение отваря Web View с данните за пренасочване към ACS.

  11. Клиентът въвежда своята еднократна парола във формата на ACS.

  12. ACS пренасочва клиента към платежната порта.

  13. Платежната порта осъществява плащането.

  14. Платежната порта пренасочва клиента към returnUrl, което може да се използва като маркер за затваряне на Web View.

  15. Мобилното приложение затваря Web View.

  16. Платежната порта изпраща известие за обратно повикване към сървъра на търговеца, ако е конфигурирано за търговеца.

  17. Мобилният сървър проверява окончателния статус на плащането чрез getOrderStatusExtended.do.

  18. Мобилното приложение показва резултата от плащането на клиента.

IOS

iOS-интеграция

Интеграция SDKCore.framework

Можете да интегрирате SDKCore.framework, като го добавите ръчно.

SDKCore.framework

Фигура 1. Добавяне на файла SDKCore.framework

Фигура 2. Промяна на свойствата на SDKCore.framework

След това импортирайте framework-а във файла 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.

...
    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(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() {
    // инициализация на валидатори за полетата за въвеждане на информация за карта
    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()
        )
        // 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 Грешка Описание
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)

React Native

За реализация на интеграцията на Mobile SDK Core в React Native-приложение е необходимо да се настрои мост — обвивка на Kotlin/Java или Swift/Objective-C, която ще приема извиквания от JavaScript и ще ги предава в нативния код на SDK.

Мостовете в React Native позволяват на вашия JS-код да извиква методи на нативни библиотеки толкова просто, колкото обикновените асинхронни функции: вие декларирате нативен модул с методи, които връщат резултат чрез Promise, React Native автоматично свързва тези методи с JavaScript, и след това работите с тях в приложението без задълбочени знания за платформените API.

IOS

  1. Интегрирайте SDKCore във вашия проект в съответствие с раздела iOS-интеграция.

  2. Проверете, че за фреймуорка SDKCore.xcframework в колоната Embed е избрано Embed & Sign.

  3. Създайте 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
        }
    }
  4. Реализирайте 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

  1. Изтеглете файловете sdk_core-release.aar и sdk_logs-release.aar и ги копирайте в папката android/app/libs/ съгласно раздела Android-интеграция.

  2. Добавете плосък репозиторий и зависимости във файла 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

  1. Създайте файл 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)
            }
        }
    }
  2. Създайте пакет 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()
    }
  3. Регистрирайте създадения пакет в 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()
            }
        }
    }

Използване в React Native

Използвайте методите от 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.

Mobile SDK Source

iOS

iOS изходни кодове в GitHub

iOS издания в GitHub

Изисквания: iOS 10.0 или по-висока версия

Android

Android изходни кодове в GitHub

Android издания в GitHub

Изисквания: Android 5.0 или по-висока версия

:
eCommerce SDK