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

tech.carpentum.sdk.payment.PaymentContext.kt Maven / Gradle / Ivy

There is a newer version: 2.1021.0
Show newest version
package tech.carpentum.sdk.payment

import tech.carpentum.sdk.payment.PaymentContext.Builder
import tech.carpentum.sdk.payment.internal.api.EnhancedAuthApi
import tech.carpentum.sdk.payment.internal.api.PostAuthTokensErrorExceptionFactory
import tech.carpentum.sdk.payment.internal.api.ResponseExceptionUtils
import tech.carpentum.sdk.payment.model.AuthTokenRequest
import tech.carpentum.sdk.payment.model.AuthTokenResponse
import tech.carpentum.sdk.payment.model.Money
import tech.carpentum.sdk.payment.model.PostAuthTokensError
import java.io.InterruptedIOException
import java.time.Duration
import java.util.*
import java.util.function.Supplier

/**
 * The **root API class** used by any other individual APIs to call Payment RESTful API.
 * The class is thread safe. It is expected the context class is singleton in custom application.
 * Use [Builder] to create new instance of the class.
 *
 * The `PaymentContext` holds references and default values to call Payment RESTful API.
 * There are available overloaded methods `createAuthToken` to create new instance of Authorization token.
 * The context instance is as well used with individual API classes:
 *
 * - [AccountsApi]
 * - [IncomingPaymentsApi]
 * - [OutgoingPaymentsApi]
 * - [PaymentsApi]
 * - [MerchantInfoApi]
 */
class PaymentContext private constructor(
    val apiBaseUrl: String,
    val apiVersion: Int,
    val merchantCode: String,
    val brand: String?,
    val secret: String,
    /**
     * Default validity of Authorization token.
     * Used by [createAuthToken] method in case `tokenValidity` parameter is not specified.
     */
    val defaultTokenValidity: Duration,
    /**
     * Default request call timeout.
     */
    val defaultCallTimeout: Duration
) {
    private val authApi: EnhancedAuthApi = EnhancedAuthApi(apiBaseUrl, defaultCallTimeout)

    private fun validityInSeconds(tokenValidity: Duration?): Int =
        (tokenValidity ?: defaultTokenValidity).seconds.toInt()

    /**
     * Throws [PostAuthTokensErrorException] ("406" response) with one of defined
     * [PostAuthTokensError] business validation error code.
     * Throws [InterruptedIOException] in case of timeout.
     */
    @JvmOverloads
    @Throws(ResponseException::class, InterruptedIOException::class)
    // tag::userGuidePublicApi[]
    fun createAuthToken(
        operations: List,
        tokenValidity: Duration? = null,
        money: Optional = Optional.empty()
    ): AuthTokenResponse
    // end::userGuidePublicApi[]
    {
        val validityInSeconds = validityInSeconds(tokenValidity)
        val requestBodyBuilder = AuthTokenRequest.builder()
            .merchantCode(merchantCode)
            .secret(secret)
            .validitySecs(validityInSeconds)
            .operations(operations)
        money.ifPresent { moneyRequest -> requestBodyBuilder.money(moneyRequest) }

        val requestBody = requestBodyBuilder.build()
        return ResponseExceptionUtils.wrap(PostAuthTokensErrorExceptionFactory.instance) {
            authApi.createAuthToken(xAPIVersion = apiVersion, authTokenRequest = requestBody)
        }
    }

    /**
     * Throws [PostAuthTokensErrorException] ("406" response) with one of defined
     * [PostAuthTokensError] business validation error code.
     * Throws [InterruptedIOException] in case of timeout.
     */
    @JvmOverloads
    @Throws(ResponseException::class, InterruptedIOException::class)
    // tag::userGuidePublicApi[]
    fun createAuthToken(
        operationsSupplier: Supplier>,
        tokenValidity: Duration? = null,
        money: Optional = Optional.empty()
    ): AuthTokenResponse
    // end::userGuidePublicApi[]
    {
        return createAuthToken(operationsSupplier.get(), tokenValidity, money)
    }

    /**
     * Factory to create a new instance of [Builder] to build new instance of [PaymentContext].
     */
    companion object Factory {
        /**
         * Environment variable name for specifying default value for [Builder.domain] property.
         * One of [ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL] property or this property is mandatory.
         * Specified [ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL] property overrides [ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN] property.
         *
         * @see ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL
         */
        const val ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN = "TECH_CARPENTUM_SDK_PAYMENT_DOMAIN"

        /**
         * Environment variable name for specifying default value for [Builder.apiBaseUrl] property.
         * One of [ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN] property or this property is mandatory.
         * Specified [ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL] property overrides [ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN] property.
         *
         * @see ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN
         */
        const val ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL = "TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL"

        /**
         * Environment variable name for specifying default value for [Builder.merchantCode] property.
         */
        const val ENV_TECH_CARPENTUM_SDK_PAYMENT_MERCHANT_CODE = "TECH_CARPENTUM_SDK_PAYMENT_MERCHANT_CODE"

        /**
         * Environment variable name for specifying default value for [Builder.secret] property.
         */
        const val ENV_TECH_CARPENTUM_SDK_PAYMENT_SECRET = "TECH_CARPENTUM_SDK_PAYMENT_SECRET"

        /**
         * Environment variable name for specifying default value for [Builder.defaultCallTimeout] property.
         * The value is a number in seconds.
         */
        const val ENV_TECH_CARPENTUM_SDK_PAYMENT_CALL_TIMEOUT = "TECH_CARPENTUM_SDK_PAYMENT_CALL_TIMEOUT"

        private const val apiVersion: Int = 2
        private const val apiBaseUrlPrefix: String = "https://api."
        private val defaultTokenValidity: Duration = Duration.ofMinutes(1)
        private val defaultCallTimeout: Duration = Duration.ofSeconds(30)

        @JvmStatic
        fun builder(): Builder = Builder()
    }

    /**
     * Builder to create a new instance of [PaymentContext].
     *
     * Mandatory properties to be set:
     *
     * - [domain]
     * - [merchantCode]
     * - [secret]
     */
    class Builder {
        private var apiBaseUrl: String? =
            Optional.ofNullable(System.getenv(ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL))
                .orElseGet {
                    Optional.ofNullable(System.getenv(ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN))
                        .map(::formatApiBaseUrl)
                        .orElse(null)
                }

        private var merchantCode: String? = System.getenv(ENV_TECH_CARPENTUM_SDK_PAYMENT_MERCHANT_CODE)

        private var brand: String? = null

        private var secret: String? = System.getenv(ENV_TECH_CARPENTUM_SDK_PAYMENT_SECRET)

        private var defaultTokenValidity: Duration = Factory.defaultTokenValidity

        private var defaultCallTimeout: Duration =
            Optional.ofNullable(System.getenv(ENV_TECH_CARPENTUM_SDK_PAYMENT_CALL_TIMEOUT))
                .map {
                    try {
                        it.toLong()
                    } catch (ex: NumberFormatException) {
                        throw IllegalArgumentException("Environment variable $ENV_TECH_CARPENTUM_SDK_PAYMENT_CALL_TIMEOUT wrong number format: $it")
                    }
                }
                .map { Duration.ofSeconds(it) }
                .orElseGet { Factory.defaultCallTimeout }

        private fun formatApiBaseUrl(domain: String): String = apiBaseUrlPrefix + domain

        /**
         * Payment RESTful API domain, e.g. `carpentum.tech`.
         * Default value can be specified via [ENV_TECH_CARPENTUM_SDK_PAYMENT_DOMAIN] environment variable.
         *
         * @see [apiBaseUrl]
         */
        fun domain(domain: String) = apply { this.apiBaseUrl = formatApiBaseUrl(domain) }

        /**
         * Payment RESTful API domain, e.g. `carpentum.tech`.
         * Default value can be specified via [ENV_TECH_CARPENTUM_SDK_PAYMENT_API_BASE_URL] environment variable.
         *
         * @see [domain]
         */
        fun apiBaseUrl(apiBaseUrl: String) = apply { this.apiBaseUrl = apiBaseUrl }

        /**
         * Unique merchant code obtained during the merchant registration process.
         * Default value can be specified via [ENV_TECH_CARPENTUM_SDK_PAYMENT_MERCHANT_CODE] environment variable.
         */
        fun merchantCode(merchantCode: String) = apply { this.merchantCode = merchantCode }

        /**
         * Current payment system brand code.
         */
        fun brand(brand: String) = apply { this.brand = brand }

        /**
         * Merchant secret obtained upon merchant registration process.
         * Default value can be specified via [ENV_TECH_CARPENTUM_SDK_PAYMENT_SECRET] environment variable.
         */
        fun secret(secret: String) = apply { this.secret = secret }

        /**
         * Default auth token validity if not specified on every [PaymentContext.createAuthToken] call.
         * If not specified, `1 minute` by default.
         */
        fun defaultTokenValidity(tokenValidity: Duration) = apply { this.defaultTokenValidity = tokenValidity }

        /**
         * Default request call timeout if not specified while creating API instances.
         * Default value can be specified via [ENV_TECH_CARPENTUM_SDK_PAYMENT_CALL_TIMEOUT] environment variable.
         * If not specified, `30 seconds` by default.
         *
         * @see AccountsApi.Factory.create
         * @see IncomingPaymentsApi.Factory.create
         * @see OutgoingPaymentsApi.Factory.create
         * @see PaymentsApi.Factory.create
         * @see MerchantInfoApi.Factory.create
         */
        fun defaultCallTimeout(callTimeout: Duration) = apply { this.defaultCallTimeout = callTimeout }

        /**
         * Create new instance of [PaymentContext].
         */
        fun build(): PaymentContext = PaymentContext(
            apiBaseUrl = requireNotNull(apiBaseUrl) { "One of 'domain' or 'apiBaseUrl' properties is mandatory." },
            apiVersion = apiVersion,
            merchantCode = requireNotNull(merchantCode) { "Property 'merchantCode' is mandatory." },
            brand = brand,
            secret = requireNotNull(secret) { "Property 'secret' is mandatory." },
            defaultTokenValidity = defaultTokenValidity,
            defaultCallTimeout = defaultCallTimeout,
        )
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy