Skip to content

Commit

Permalink
Merge pull request #104 from Tinkoff/2.8.0
Browse files Browse the repository at this point in the history
2.8.0
  • Loading branch information
IlnarH authored Aug 26, 2022
2 parents 56c789a + 953cec6 commit d8b5623
Show file tree
Hide file tree
Showing 20 changed files with 148 additions and 73 deletions.
12 changes: 12 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
## 2.8.0

#### Fixed
Fixed proguard rules for inner dependencies to generate classes with unique package names
to prevent duplicate class errors
#### Changes
Removed `tokenGenerator` parameter from `TinkoffAcquiring` constructor; `tokenGenerator` can
be set via `AcquiringSdk.tokenGenerator`
Added `paymentId` as a second parameter to `PaymentListener.onError` ([migration](/migration.md))
#### Additions
Added `successURL` and `failURL` to `OrderOptions`

## 2.7.0

#### Fixed
Expand Down
16 changes: 13 additions & 3 deletions core/src/main/java/ru/tinkoff/acquiring/sdk/AcquiringSdk.kt
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,9 @@ class AcquiringSdk(

/**
* Объект, который будет использоваться для генерации токена при формировании запросов к api
* ([документация по формированию токена](https://www.tinkoff.ru/kassa/develop/api/request-sign/))
* ([документация по формированию токена](https://www.tinkoff.ru/kassa/develop/api/request-sign/)).
*
* Передача токена для SDK терминалов в общем случае не обязательна и зависит от настроек терминала.
*/
var tokenGenerator: AcquiringTokenGenerator? = null

Expand Down Expand Up @@ -304,15 +306,23 @@ class AcquiringSdk(
* 3. Конкатенировать значения всех пар.
* 4. Для полученной строки вычислить хэш SHA-256.
*
* Полученный хэш и будет являться токеном.
* Полученный хэш и будет являться токеном. При возвращении *null* токен не будет добавляться к запросу.
*
* Пример реализации алгоритма генерации токена можно увидеть в [SampleAcquiringTokenGenerator].
*
* **Note:** Метод вызывается в фоновом потоке.
*/
fun interface AcquiringTokenGenerator {

fun generateToken(request: AcquiringRequest<*>, params: MutableMap<String, Any>): String
/**
* @param request запрос, для которого будет гененрироваться токен
* @param params словарь параметров, используемый для формирования токена; объекты **Shops**,
* **Receipt** и **DATA** уже исключены из этого словаря
*
* @return токен, сформированный с использоваванием [params], который будет добавлен в параметры
* запроса к API; при возвращении *null* токен не будет добавляться к запросу
*/
fun generateToken(request: AcquiringRequest<*>, params: MutableMap<String, Any>): String?

companion object {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ abstract class AcquiringRequest<R : AcquiringResponse>(internal val apiMethod: S
const val DATA_TYPE = "DataType"
const val REDIRECT_DUE_DATE = "RedirectDueDate"
const val NOTIFICATION_URL = "NotificationURL"
const val SUCCESS_URL = "SuccessURL"
const val FAIL_URL = "FailURL"
const val IP = "IP"
const val CONNECTION_TYPE = "connection_type"
const val SDK_VERSION = "sdk_version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ class InitRequest : AcquiringRequest<InitResponse>(INIT_METHOD) {
*/
var notificationURL: String? = null

/**
* Страница успеха
*/
var successURL: String? = null

/**
* Страница ошибки
*/
var failURL: String? = null

var sdkVersion: String? = null

private var redirectDueDateFormat: String? = null
Expand All @@ -144,6 +154,8 @@ class InitRequest : AcquiringRequest<InitResponse>(INIT_METHOD) {
map.putIfNotNull(SHOPS, shops)
map.putIfNotNull(REDIRECT_DUE_DATE, redirectDueDateFormat)
map.putIfNotNull(NOTIFICATION_URL, notificationURL)
map.putIfNotNull(SUCCESS_URL, successURL)
map.putIfNotNull(FAIL_URL, failURL)
map.putDataIfNonNull(data)

return map
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ import ru.tinkoff.acquiring.sdk.requests.AcquiringRequest
import java.lang.StringBuilder

/**
* Пример реализации алгоритма генерации токена. Использование этой реализации в реальных
* Android-приложениях не рекомендуется.
*
* @param password пароль терминала, который будет использоваться при формировании токена.
* **В целях безопасности пароль не рекомендуется хранить в коде Android-приложения.**
*/
class SampleAcquiringTokenGenerator(private val password: String) : AcquiringTokenGenerator {

override fun generateToken(request: AcquiringRequest<*>, params: MutableMap<String, Any>): String {
override fun generateToken(request: AcquiringRequest<*>, params: MutableMap<String, Any>): String? {
params[AcquiringRequest.PASSWORD] = password
val sorted = params.toSortedMap()

Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION_NAME=2.7.0
VERSION_NAME=2.8.0
VERSION_CODE=15
GROUP=ru.tinkoff.acquiring

Expand Down
10 changes: 10 additions & 0 deletions migration.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
2.8.0

Конструктор `TinkoffAcquiring` больше не принимает `tokenGenerator`; при необходимости `tokenGenerator`
можно задать через `AcquiringSdk.tokenGenerator`.

`PaymentListener.onError(throwable: Throwable, paymentId: Long?)` теперь принимает `paymentId`
вторым параметром.

2.7.0

Большинство запросов теперь требует передачи токена.
Expand All @@ -9,6 +17,8 @@
При использовании зависимости только от `core`-модуля объект для генерации токена можно задать
через `AcquiringSdk.tokenGenerator`.

Поле `password` удалено из `AcquiringRequest`.

2.6.0

Добавление подтверждение 3DS по app-based flow при проведении платежа.
Expand Down
9 changes: 9 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ AcquiringSdk.isDeveloperMode = true // используется тестовый
AcquiringSdk.isDebug = true // включение логирования запросов
```

Кроме того, в некоторых случаях к запросам к API эквайринга необходимо добавлять некий токен (подпись
запроса). Передача токена для SDK терминалов в общем случае не обязательна и зависит от настроек
терминала. Задание способа генерации токена в случаях, когда это необходимо, может выглядеть следующим
образом (для более подробной информации см. kDoc `AcquiringSdk.tokenGenerator`):
```kotlin
AcquiringSdk.tokenGenerator = SampleAcquiringTokenGenerator(password) // генерация токена с использованием пароля
// В целях безопасности не рекомендуется хранить пароль в коде приложения
```

### Пример работы
Для проведения оплаты необходимо вызвать метод **TinkoffAcquiring**#_openPaymentScreen_. Метод запустит экран оплаты **PaymentActivity**. Активити должна быть настроена на обработку конкретного платежа, поэтому в метод необходимо передать настройки проведения оплаты, включающие в себя данные заказа, данные покупателя и опционально параметры кастомизации экрана оплаты.
Кроме того, можно указать тему, указать локализацию формы (или передать свою), а также указать модуль для сканирования (свой или **CameraCardIOScanner**).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ class SampleApplication : Application() {

val settings = SettingsSdkManager(this)
val params = SessionParams[settings.terminalKey]
tinkoffAcquiring = TinkoffAcquiring(this, params.terminalKey,
params.publicKey, SampleAcquiringTokenGenerator(params.password))
tinkoffAcquiring = TinkoffAcquiring(this, params.terminalKey, params.publicKey)
AcquiringSdk.isDeveloperMode = true
AcquiringSdk.isDebug = true
AcquiringSdk.tokenGenerator = SampleAcquiringTokenGenerator(params.password)
}

override fun onTerminate() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ open class PayableActivity : AppCompatActivity() {
title = this@PayableActivity.title
description = this@PayableActivity.description
recurrentPayment = settings.isRecurrentPayment
successURL = "https://www.google.com/search?q=success"
failURL = "https://www.google.com/search?q=fail"
additionalData = mutableMapOf(
"test_additional_data_key_1" to "test_additional_data_value_2",
"test_additional_data_key_2" to "test_additional_data_value_2")
}
customerOptions {
customerKey = sessionParams.customerKey
Expand Down Expand Up @@ -230,7 +235,7 @@ open class PayableActivity : AppCompatActivity() {
state)
}

override fun onError(throwable: Throwable) {
override fun onError(throwable: Throwable, paymentId: Long?) {
hideProgressDialog()
showErrorDialog()
SampleApplication.paymentProcess = null
Expand Down
Binary file modified threeds-sdk/emv-3ds-sdk-release.aar
Binary file not shown.
Binary file modified threeds-wrapper/threeds-wrapper-release.aar
Binary file not shown.
14 changes: 4 additions & 10 deletions ui/src/main/java/ru/tinkoff/acquiring/sdk/TinkoffAcquiring.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,20 @@ import ru.tinkoff.acquiring.sdk.ui.activities.ThreeDsActivity
/**
* Точка входа для взаимодействия с Acquiring SDK
*
* В некоторых случаях для формирования запросов к API может потребоваться генерация токена для
* подписи запроса, см. [AcquiringSdk.tokenGenerator].
*
* @param terminalKey ключ терминала. Выдается после подключения к Tinkoff Acquiring
* @param publicKey экземпляр PublicKey созданный из публичного ключа, выдаваемого вместе с
* terminalKey
* @param tokenGenerator объект, который будет использоваться для генерации токена при формировании
* запросов к api ([документация по формированию токена](https://www.tinkoff.ru/kassa/develop/api/request-sign/))
*
*
* @author Mariya Chernyadieva
*/
class TinkoffAcquiring(
private val applicationContext: Context,
private val terminalKey: String,
private val publicKey: String,
tokenGenerator: AcquiringTokenGenerator
private val publicKey: String
) {
init {
TinkoffAcquiring.tokenGenerator = tokenGenerator
}

val sdk = AcquiringSdk(terminalKey, publicKey)

Expand Down Expand Up @@ -461,7 +457,5 @@ class TinkoffAcquiring(
const val EXTRA_REBILL_ID = "extra_rebill_id"

const val EXTRA_CARD_LIST_CHANGED = "extra_cards_changed"

var tokenGenerator: AcquiringTokenGenerator? by AcquiringSdk.Companion::tokenGenerator
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ class OrderOptions() : Options(), Parcelable {
*/
var receipts: List<Receipt>? = null

/**
* Страница успеха
*/
var successURL: String? = null

/**
* Страница ошибки
*/
var failURL: String? = null

/**
* Объект содержащий дополнительные параметры в виде "ключ":"значение".
* Данные параметры будут переданы в запросе платежа/привязки карты.
Expand All @@ -92,6 +102,8 @@ class OrderOptions() : Options(), Parcelable {
receipt = readSerializable() as Receipt?
shops = readParcelList(Shop::class.java)
receipts = readParcelList(Receipt::class.java)
successURL = readString()
failURL = readString()
additionalData = readParcelMap(String::class.java)
}
}
Expand All @@ -106,6 +118,8 @@ class OrderOptions() : Options(), Parcelable {
writeSerializable(receipt)
writeList(shops)
writeList(receipts)
writeString(successURL)
writeString(failURL)
writeMap(additionalData)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ interface PaymentListener {
/**
* В процессе оплаты произошла ошибка
*/
fun onError(throwable: Throwable)
fun onError(throwable: Throwable, paymentId: Long?)

/**
* Событие изменения состояния процесса оплаты
Expand All @@ -65,7 +65,7 @@ abstract class PaymentListenerAdapter : PaymentListener {

override fun onUiNeeded(state: AsdkState) = Unit

override fun onError(throwable: Throwable) = Unit
override fun onError(throwable: Throwable, paymentId: Long?) = Unit

override fun onStatusChanged(state: PaymentState?) = Unit
}
49 changes: 26 additions & 23 deletions ui/src/main/java/ru/tinkoff/acquiring/sdk/payment/PaymentProcess.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ internal constructor(
paymentOptions.validateRequiredFields()

this.paymentSource = paymentSource
this.initRequest = configureInitRequest(paymentOptions)
this.initRequest = sdk.init { configure(paymentOptions) }
this.paymentType = CardPaymentType
this.email = email
this.sdkState = paymentOptions.asdkState
Expand Down Expand Up @@ -133,7 +133,7 @@ internal constructor(
fun createSbpPaymentProcess(paymentOptions: PaymentOptions): PaymentProcess {
paymentOptions.validateRequiredFields()

this.initRequest = configureInitRequest(paymentOptions)
this.initRequest = sdk.init { configure(paymentOptions) }
this.paymentType = SbpPaymentType

sendToListener(PaymentState.CREATED)
Expand All @@ -160,7 +160,7 @@ internal constructor(
* @return сконфигурированный объект для проведения оплаты
*/
fun createTinkoffPayPaymentProcess(paymentOptions: PaymentOptions, tinkoffPayVersion: String): PaymentProcess {
this.initRequest = configureInitRequest(paymentOptions).apply {
this.initRequest = sdk.init { configure(paymentOptions) }.apply {
data = mutableMapOf<String, String>().also { newData ->
data?.let { newData.putAll(it) }
newData["TinkoffPayWeb"] = "true"
Expand Down Expand Up @@ -216,7 +216,7 @@ internal constructor(
this.state = state
when (state) {
PaymentState.SUCCESS -> listener?.onSuccess(paymentResult!!.paymentId!!, paymentResult!!.cardId, paymentResult!!.rebillId)
PaymentState.ERROR -> listener?.onError(error!!)
PaymentState.ERROR -> listener?.onError(error!!, paymentId)
PaymentState.CHARGE_REJECTED, PaymentState.THREE_DS_NEEDED, PaymentState.BROWSE_SBP_BANK, PaymentState.OPEN_TINKOFF_PAY_BANK ->
listener?.onUiNeeded(sdkState!!)
PaymentState.THREE_DS_DATA_COLLECTING -> {
Expand Down Expand Up @@ -251,10 +251,12 @@ internal constructor(

coroutine.call(request,
onSuccess = {
paymentId = it.paymentId
when (paymentType) {
CardPaymentType -> finishPayment(it.paymentId!!, paymentSource, email)
SbpPaymentType -> callGetQr(it.paymentId!!)
TinkoffPayPaymentType -> callTinkoffPayLinkRequest(it.paymentId!!, tinkoffPayVersion!!)
else -> Unit
}
})
}
Expand Down Expand Up @@ -447,25 +449,6 @@ internal constructor(
}
}

private fun configureInitRequest(paymentOptions: PaymentOptions): InitRequest {
val order = paymentOptions.order

return sdk.init {
orderId = order.orderId
amount = order.amount.coins
description = order.description
chargeFlag = order.recurrentPayment
recurrent = order.recurrentPayment
receipt = order.receipt
receipts = order.receipts
shops = order.shops
data = order.additionalData
customerKey = paymentOptions.customer.customerKey
language = AsdkLocalization.language.name
sdkVersion = BuildConfig.ASDK_VERSION_NAME
}
}

private fun modifyRejectedData(request: InitRequest): Map<String, String> {
val map = HashMap<String, String>()
map[RECURRING_TYPE_KEY] = RECURRING_TYPE_VALUE
Expand All @@ -476,5 +459,25 @@ internal constructor(

return data.toMap()
}

companion object {

fun InitRequest.configure(paymentOptions: PaymentOptions) = apply {
orderId = paymentOptions.order.orderId
amount = paymentOptions.order.amount.coins
description = paymentOptions.order.description
chargeFlag = paymentOptions.order.recurrentPayment
recurrent = paymentOptions.order.recurrentPayment
receipt = paymentOptions.order.receipt
receipts = paymentOptions.order.receipts
shops = paymentOptions.order.shops
successURL = paymentOptions.order.successURL
failURL = paymentOptions.order.failURL
data = paymentOptions.order.additionalData
customerKey = paymentOptions.customer.customerKey
language = AsdkLocalization.language.name
sdkVersion = BuildConfig.ASDK_VERSION_NAME
}
}
}

Loading

0 comments on commit d8b5623

Please sign in to comment.