All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ru.tinkoff.acquiring.sdk.AcquiringSdk.kt Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
/*
 * Copyright © 2020 Tinkoff Bank
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package ru.tinkoff.acquiring.sdk

import ru.tinkoff.acquiring.sdk.AcquiringSdk.Companion.tokenGenerator
import ru.tinkoff.acquiring.sdk.loggers.JavaLogger
import ru.tinkoff.acquiring.sdk.loggers.Logger
import ru.tinkoff.acquiring.sdk.requests.AcquiringRequest
import ru.tinkoff.acquiring.sdk.requests.AddCardRequest
import ru.tinkoff.acquiring.sdk.requests.AttachCardRequest
import ru.tinkoff.acquiring.sdk.requests.CancelRequest
import ru.tinkoff.acquiring.sdk.requests.ChargeRequest
import ru.tinkoff.acquiring.sdk.requests.Check3dsVersionRequest
import ru.tinkoff.acquiring.sdk.requests.ConfirmRequest
import ru.tinkoff.acquiring.sdk.requests.FinishAuthorizeRequest
import ru.tinkoff.acquiring.sdk.requests.GetAddCardStateRequest
import ru.tinkoff.acquiring.sdk.requests.GetCardListRequest
import ru.tinkoff.acquiring.sdk.requests.GetQrRequest
import ru.tinkoff.acquiring.sdk.requests.GetStateRequest
import ru.tinkoff.acquiring.sdk.requests.GetStaticQrRequest
import ru.tinkoff.acquiring.sdk.requests.GetTerminalPayMethodsRequest
import ru.tinkoff.acquiring.sdk.requests.InitRequest
import ru.tinkoff.acquiring.sdk.requests.MirPayLinkRequest
import ru.tinkoff.acquiring.sdk.requests.RemoveCardRequest
import ru.tinkoff.acquiring.sdk.requests.Submit3DSAuthorizationRequest
import ru.tinkoff.acquiring.sdk.requests.Submit3DSAuthorizationWebViewRequest
import ru.tinkoff.acquiring.sdk.requests.SubmitRandomAmountRequest
import ru.tinkoff.acquiring.sdk.requests.TinkoffPayLinkRequest
import ru.tinkoff.acquiring.sdk.requests.TinkoffPayStatusRequest
import ru.tinkoff.acquiring.sdk.utils.SampleAcquiringTokenGenerator
import ru.tinkoff.acquiring.sdk.utils.keycreators.KeyCreator
import ru.tinkoff.acquiring.sdk.utils.keycreators.StringKeyCreator
import java.security.MessageDigest
import java.security.PublicKey

/**
 * Класс позволяет конфигурировать SDK и осуществлять взаимодействие с T-Bank Acquiring API.
 * Методы осуществляют обращение к API.
 * Вызов методов выполняется синхронно
 *
 * Для корректного выполнения запросов также необходимо указать [tokenGenerator].
 *
 * @param terminalKey ключ терминала. Выдается после подключения к T-Bank Acquiring
 * @param publicKey   экземпляр PublicKey созданный из публичного ключа, выдаваемого вместе с
 *                    terminalKey
 *
 * @author Mariya Chernyadieva, Taras Nagorny
 */
class AcquiringSdk(
        private val terminalKey: String,
        private val publicKey: PublicKey
) {

    constructor(terminalKey: String, publicKey: String) :
            this(terminalKey, StringKeyCreator(publicKey))

    constructor(terminalKey: String, keyCreator: KeyCreator) :
            this(terminalKey, keyCreator.create())


    /**
     * Инициирует платежную сессию
     */
    fun init(request: InitRequest.() -> Unit): InitRequest {
        return InitRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Проверяет поддерживаемую версию 3DS протокола по карточным данным из входящих параметров
     */
    fun check3DsVersion(request: Check3dsVersionRequest.() -> Unit): Check3dsVersionRequest {
        return Check3dsVersionRequest().apply(request).apply {
            terminalKey = [email protected]
            publicKey = [email protected]
        }
    }

    /**
     * Подтверждает инициированный платеж передачей карточных данных
     */
    fun finishAuthorize(request: FinishAuthorizeRequest.() -> Unit): FinishAuthorizeRequest {
        return FinishAuthorizeRequest().apply(request).apply {
            terminalKey = [email protected]
            publicKey = [email protected]
        }
    }

    /**
     * Возвращает список привязанных карт
     */
    fun getCardList(request: GetCardListRequest.() -> Unit): GetCardListRequest {
        return GetCardListRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Осуществляет рекуррентный (повторный) платеж — безакцептное списание денежных средств со
     * счета банковской карты покупателя. Для возможности его использования покупатель должен
     * совершить хотя бы один платеж в пользу продавца, который должен быть указан как рекуррентный
     * (см. параметр [InitRequest.recurrent]), фактически являющийся первичным.
     *
     * Другими словами, для использования рекуррентных платежей необходима следующая
     * последовательность действий:
     * 1. Совершить родительский платеж путем вызова Init с указанием дополнительного параметра
     * recurrent=true
     * 2. Получить RebillId, предварительно вызвав метод GetCardList
     * 3. Для совершения рекуррентного платежа необходимо вызвать метод Init со стандартным
     * набором параметров (параметр Recurrent здесь не нужен).
     * 4. Получить в ответ на Init параметр PaymentId.
     * 5. Вызвать метод Charge c параметром RebillId полученным в п.2 и параметром PaymentId
     * полученным в п.4
     */
    fun charge(request: ChargeRequest.() -> Unit): ChargeRequest {
        return ChargeRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Метод подтверждает платеж и списывает ранее заблокированные средства.
     * Используется при двухстадийной оплате. При одностадийной оплате вызывается автоматически.
     * Применим к платежу только в статусе AUTHORIZED и только один раз.
     *
     * Сумма подтверждения не может быть больше заблокированной.
     * Если сумма подтверждения меньше заблокированной, будет выполнено частичное подтверждение
     */
    fun confirm(request: ConfirmRequest.() -> Unit): ConfirmRequest {
        return ConfirmRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Метод отменяет платеж
     */
    fun cancel(request: CancelRequest.() -> Unit): CancelRequest {
        return CancelRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Регистрирует QR и возвращает информацию о нем. Должен быть вызван после вызова метода Init
     */
    fun getQr(request: GetQrRequest.() -> Unit): GetQrRequest {
        return GetQrRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * При первом вызове регистрирует QR и возвращает информацию о нем при последующих вызовах вовзращает
     * информацию о ранее сгенерированном QR. Перерегистрация статического QR происходит только при смене
     * расчетного счета. Не привязан к конкретному платежу, может быть вызван в любое время
     * без предварительного вызова Init
     */
    fun getStaticQr(request: GetStaticQrRequest.() -> Unit): GetStaticQrRequest {
        return GetStaticQrRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Возвращает статус платежа
     */
    fun getState(request: GetStateRequest.() -> Unit): GetStateRequest {
        return GetStateRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Удаляет привязанную карту
     */
    fun removeCard(request: RemoveCardRequest.() -> Unit): RemoveCardRequest {
        return RemoveCardRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Метод подготовки для привязки карты, необходимо вызвать [AcquiringSdk.addCard]
     * перед методом [AcquiringSdk.attachCard]
     */
    fun addCard(request: AddCardRequest.() -> Unit): AddCardRequest {
        return AddCardRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Метод привязки карты, вызывается после [AcquiringSdk.addCard]
     */
    fun attachCard(request: AttachCardRequest.() -> Unit): AttachCardRequest {
        return AttachCardRequest().apply(request).apply {
            terminalKey = [email protected]
            publicKey = [email protected]
        }
    }

    /**
     * Метод проверки состояния привязки карты после 3D-Secure
     */
    fun getAddCardState(request: GetAddCardStateRequest.() -> Unit): GetAddCardStateRequest {
        return GetAddCardStateRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    /**
     * Метод подтверждения при [ru.tinkoff.acquiring.sdk.models.enums.CheckType.THREE_DS_HOLD]
     * привязки
     */
    fun submitRandomAmount(request: SubmitRandomAmountRequest.() -> Unit): SubmitRandomAmountRequest {
        return SubmitRandomAmountRequest().apply(request).apply {
            terminalKey = [email protected]
        }
    }

    fun tinkoffPayStatus(request: (TinkoffPayStatusRequest.() -> Unit)? = null): TinkoffPayStatusRequest {
        return TinkoffPayStatusRequest([email protected]).apply {
            request?.invoke(this)
        }
    }

    fun tinkoffPayLink(paymentId: Long, version: String, request: (TinkoffPayLinkRequest.() -> Unit)? = null): TinkoffPayLinkRequest {
        return TinkoffPayLinkRequest(paymentId.toString(), version).apply {
            terminalKey = [email protected]
            request?.invoke(this)
        }
    }

    /**
     * Метод получения Deeplink-a для оплаты с помощью MirPay
     */
    fun mirPayLink(paymentId: Long, request: (MirPayLinkRequest.() -> Unit)? = null): MirPayLinkRequest {
        return MirPayLinkRequest(paymentId.toString()).apply {
            terminalKey = [email protected]
            request?.invoke(this)
        }
    }

    fun getTerminalPayMethods() : GetTerminalPayMethodsRequest {
        return GetTerminalPayMethodsRequest(terminalKey)
    }

    fun submit3DSAuthorization(threeDSServerTransID: String, transStatus: String, request: (Submit3DSAuthorizationRequest.() -> Unit)? = null): Submit3DSAuthorizationRequest {
        return Submit3DSAuthorizationRequest().apply {
            terminalKey = [email protected]
            this.threeDSServerTransID = threeDSServerTransID
            this.transStatus = transStatus
            request?.invoke(this)
        }
    }

    fun submit3DSAuthorizationFromWebView(paymentId: String?): Submit3DSAuthorizationWebViewRequest {
        return Submit3DSAuthorizationWebViewRequest().apply {
            terminalKey = [email protected]
            this.paymentId = paymentId
        }
    }

    companion object {

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

        /**
         * Позволяет использовать свой логгер или заданный
         */
        var logger: Logger = JavaLogger()

        /**
         * Позволяет включить логирование. По-умолчанию выключен
         */
        var isDebug = false

        /**
         * Позволяет переключать SDK в режим разработчика и обратно. В режиме разработчика
         * используется тестовое окружение или окружение заданное параметром [customUrl].
         * По-умолчанию выключен
         */
        var isDeveloperMode = false

        /**
         * Позволяет переключать SDK на иной апи-контур, работает только при [isDeveloperMode]
         * равном true
         */
        var customUrl: String? = null

        /**
         * Таймаут сетевых запросов в секундах.
         * Работает только при [isDeveloperMode] равном true.
         * При [isDeveloperMode] равном false используется значение
         * [ru.tinkoff.acquiring.sdk.network.AcquiringApi.NETWORK_TIMEOUT_SECONDS]
         */
        var requestsTimeoutInterval: Long = ru.tinkoff.acquiring.sdk.network.AcquiringApi.NETWORK_TIMEOUT_SECONDS

        /**
         * Логирует сообщение
         */
        fun log(message: CharSequence) {
            if (isDebug) {
                logger.log(message)
            }
        }

        /**
         * Логирует ошибку/исключение
         */
        fun log(e: Throwable) {
            if (isDebug) {
                logger.log(e)
            }
        }
    }
}

/**
 * Объект, который будет использоваться для генерации токена при формировании
 * запросов к api ([документация по формированию токена](https://www.tinkoff.ru/kassa/dev/payments/index.html#section/Podpis-zaprosa)).
 * На вход принимает словарь параметров (объекты **Shops**, **Receipt** и **DATA** уже исключены из
 * этого словаря), на выходе должен вернуть строку, являющуюся токеном.
 *
 * Алгоритм формирования токена:
 * 1. Добавить в исходный словарь пароль терминала с ключом **Password**.
 * 2. Отсортировать словарь по ключам в алфавитном порядке.
 * 3. Конкатенировать значения всех пар.
 * 4. Для полученной строки вычислить хэш SHA-256.
 *
 * Полученный хэш и будет являться токеном. При возвращении *null* токен не будет добавляться к запросу.
 *
 * Пример реализации алгоритма генерации токена можно увидеть в [SampleAcquiringTokenGenerator].
 *
 * **Note:** Метод вызывается в фоновом потоке.
 */
fun interface AcquiringTokenGenerator {

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

    companion object {

        fun sha256hashString(source: String): String =
            MessageDigest.getInstance("SHA-256")
                .digest(source.toByteArray())
                .joinToString("") { "%02x".format(it) }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy