diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/NativeAlternativePaymentRequestBody.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/NativeAlternativePaymentRequestBody.kt new file mode 100644 index 000000000..df0e117a8 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/NativeAlternativePaymentRequestBody.kt @@ -0,0 +1,18 @@ +package com.processout.sdk.api.model.request.napm.v2 + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +internal data class NativeAlternativePaymentRequestBody( + @Json(name = "gateway_configuration_id") + val gatewayConfigurationId: String, + @Json(name = "submit_data") + val submitData: SubmitData? +) { + + @JsonClass(generateAdapter = true) + data class SubmitData( + val parameters: Map + ) +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentAuthorizationRequest.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentAuthorizationRequest.kt index e20a9a308..f9c68d0c8 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentAuthorizationRequest.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentAuthorizationRequest.kt @@ -1,81 +1,18 @@ package com.processout.sdk.api.model.request.napm.v2 import com.processout.sdk.core.annotation.ProcessOutInternalApi -import com.squareup.moshi.Json -import com.squareup.moshi.JsonClass /** * Request parameters for native alternative payment authorization. * * @param[invoiceId] Invoice identifier. * @param[gatewayConfigurationId] Gateway configuration identifier. - * @param[parameters] Payment parameter values. + * @param[submitData] Payment payload. */ /** @suppress */ @ProcessOutInternalApi data class PONativeAlternativePaymentAuthorizationRequest( val invoiceId: String, val gatewayConfigurationId: String, - val parameters: Map? = null -) { - - /** - * Payment parameter value. - */ - data class Parameter internal constructor( - @ProcessOutInternalApi val value: Value - ) { - - companion object { - /** - * Arbitrary string value. - */ - fun string(value: String) = Parameter(Value.String(value)) - - /** - * Phone number value. - * - * @param[dialingCode] International dialing code. - * @param[number] The rest of the number without dialing code. - */ - fun phoneNumber( - dialingCode: String, - number: String - ) = Parameter( - Value.PhoneNumber( - dialingCode = dialingCode, - number = number - ) - ) - } - - /** @suppress */ - @ProcessOutInternalApi - sealed class Value { - data class String( - val value: kotlin.String - ) : Value() - - @JsonClass(generateAdapter = true) - data class PhoneNumber( - @Json(name = "dialing_code") - val dialingCode: kotlin.String, - val number: kotlin.String - ) : Value() - } - } -} - -@JsonClass(generateAdapter = true) -internal data class NativeAlternativePaymentAuthorizationRequestBody( - @Json(name = "gateway_configuration_id") - val gatewayConfigurationId: String, - @Json(name = "submit_data") - val submitData: SubmitData? -) { - - @JsonClass(generateAdapter = true) - data class SubmitData( - val parameters: Map - ) -} + val submitData: PONativeAlternativePaymentSubmitData? = null +) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentSubmitData.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentSubmitData.kt new file mode 100644 index 000000000..e72565ce3 --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentSubmitData.kt @@ -0,0 +1,63 @@ +package com.processout.sdk.api.model.request.napm.v2 + +import com.processout.sdk.core.annotation.ProcessOutInternalApi +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +/** + * Specifies native alternative payment payload. + * + * @param[parameters] Map of payment parameter values. + */ +/** @suppress */ +@ProcessOutInternalApi +data class PONativeAlternativePaymentSubmitData( + val parameters: Map +) { + + /** + * Payment parameter value. + */ + data class Parameter internal constructor( + @ProcessOutInternalApi val value: Value + ) { + + companion object { + /** + * Arbitrary string value. + */ + fun string(value: String) = Parameter(Value.String(value)) + + /** + * Phone number value. + * + * @param[dialingCode] International dialing code. + * @param[number] The rest of the number without dialing code. + */ + fun phoneNumber( + dialingCode: String, + number: String + ) = Parameter( + Value.PhoneNumber( + dialingCode = dialingCode, + number = number + ) + ) + } + + /** @suppress */ + @ProcessOutInternalApi + sealed class Value { + data class String( + val value: kotlin.String + ) : Value() + + @JsonClass(generateAdapter = true) + data class PhoneNumber( + @Json(name = "dialing_code") + val dialingCode: kotlin.String, + val number: kotlin.String + ) : Value() + } + } +} diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentTokenizationRequest.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentTokenizationRequest.kt new file mode 100644 index 000000000..ed4bb6a4e --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/request/napm/v2/PONativeAlternativePaymentTokenizationRequest.kt @@ -0,0 +1,20 @@ +package com.processout.sdk.api.model.request.napm.v2 + +import com.processout.sdk.core.annotation.ProcessOutInternalApi + +/** + * Request parameters for native alternative payment tokenization. + * + * @param[customerId] Customer identifier. + * @param[customerTokenId] Customer token identifier. + * @param[gatewayConfigurationId] Gateway configuration identifier. + * @param[submitData] Payment payload. + */ +/** @suppress */ +@ProcessOutInternalApi +data class PONativeAlternativePaymentTokenizationRequest( + val customerId: String, + val customerTokenId: String, + val gatewayConfigurationId: String, + val submitData: PONativeAlternativePaymentSubmitData? = null +) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentAuthorizationResponse.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentAuthorizationResponse.kt index 236b35523..0066bbab1 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentAuthorizationResponse.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentAuthorizationResponse.kt @@ -9,8 +9,7 @@ import com.squareup.moshi.JsonClass * @param[state] State of native alternative payment. * @param[invoice] Invoice details. * @param[paymentMethod] Payment method details. - * @param[nextStep] Next required step in the payment flow. - * @param[customerInstructions] Instructions for the customer that provide additional information and/or describe required actions. + * @param[elements] An ordered list of elements that needs to be rendered on the UI during native alternative payment flow. * @param[redirect] Indicates required redirect. */ /** @suppress */ diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentTokenizationResponse.kt b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentTokenizationResponse.kt new file mode 100644 index 000000000..4b99a844d --- /dev/null +++ b/sdk/src/main/kotlin/com/processout/sdk/api/model/response/napm/v2/PONativeAlternativePaymentTokenizationResponse.kt @@ -0,0 +1,30 @@ +package com.processout.sdk.api.model.response.napm.v2 + +import com.processout.sdk.core.annotation.ProcessOutInternalApi +import com.squareup.moshi.JsonClass + +/** + * Specifies details of native alternative payment. + * + * @param[state] State of native alternative payment. + * @param[paymentMethod] Payment method details. + * @param[elements] An ordered list of elements that needs to be rendered on the UI during native alternative payment flow. + * @param[redirect] Indicates required redirect. + */ +/** @suppress */ +@ProcessOutInternalApi +data class PONativeAlternativePaymentTokenizationResponse( + val state: PONativeAlternativePaymentState, +// val paymentMethod: PONativeAlternativePaymentMethodDetails, // TODO(v2): uncomment + val elements: List?, + val redirect: PONativeAlternativePaymentRedirect? +) + +@JsonClass(generateAdapter = true) +internal data class NativeAlternativePaymentTokenizationResponseBody( + val state: PONativeAlternativePaymentState, +// @Json(name = "payment_method") +// val paymentMethod: PONativeAlternativePaymentMethodDetails, // TODO(v2): uncomment + val elements: List?, + val redirect: PONativeAlternativePaymentRedirect? +) diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/network/CustomerTokensApi.kt b/sdk/src/main/kotlin/com/processout/sdk/api/network/CustomerTokensApi.kt index 964b993ae..2aa27853e 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/network/CustomerTokensApi.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/network/CustomerTokensApi.kt @@ -3,8 +3,10 @@ package com.processout.sdk.api.network import com.processout.sdk.api.model.request.AssignCustomerTokenRequestWithDeviceData import com.processout.sdk.api.model.request.POCreateCustomerRequest import com.processout.sdk.api.model.request.POCreateCustomerTokenRequestBody +import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentRequestBody import com.processout.sdk.api.model.response.CustomerResponse import com.processout.sdk.api.model.response.CustomerTokenResponse +import com.processout.sdk.api.model.response.napm.v2.NativeAlternativePaymentTokenizationResponseBody import com.processout.sdk.api.network.HeaderConstants.CLIENT_SECRET import retrofit2.Response import retrofit2.http.* @@ -18,6 +20,13 @@ internal interface CustomerTokensApi { @Body request: AssignCustomerTokenRequestWithDeviceData ): Response + @POST("/customers/{customer_id}/tokens/{token_id}/tokenize") + suspend fun tokenize( + @Path("customer_id") customerId: String, + @Path("token_id") tokenId: String, + @Body request: NativeAlternativePaymentRequestBody + ): Response + @DELETE("/customers/{customer_id}/tokens/{token_id}") suspend fun deleteCustomerToken( @Path("customer_id") customerId: String, diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/network/InvoicesApi.kt b/sdk/src/main/kotlin/com/processout/sdk/api/network/InvoicesApi.kt index 7552314a0..5fc41173f 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/network/InvoicesApi.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/network/InvoicesApi.kt @@ -4,7 +4,7 @@ import com.processout.sdk.api.model.request.InvoiceAuthorizationRequestWithDevic import com.processout.sdk.api.model.request.NativeAPMRequestBody import com.processout.sdk.api.model.request.NativeAlternativePaymentCaptureRequest import com.processout.sdk.api.model.request.POCreateInvoiceRequest -import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentAuthorizationRequestBody +import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentRequestBody import com.processout.sdk.api.model.response.* import com.processout.sdk.api.model.response.napm.v2.NativeAlternativePaymentAuthorizationResponseBody import com.processout.sdk.api.network.HeaderConstants.CLIENT_SECRET @@ -23,7 +23,7 @@ internal interface InvoicesApi { @POST("/invoices/{id}/apm-payment") suspend fun authorizeInvoice( @Path("id") invoiceId: String, - @Body request: NativeAlternativePaymentAuthorizationRequestBody + @Body request: NativeAlternativePaymentRequestBody ): Response @GET("/invoices/{invoiceId}/apm-payment/{gatewayConfigurationId}") diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/CustomerTokensRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/CustomerTokensRepository.kt index 1e9a4f940..6300c4cc8 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/repository/CustomerTokensRepository.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/repository/CustomerTokensRepository.kt @@ -4,9 +4,11 @@ import com.processout.sdk.api.model.request.POAssignCustomerTokenRequest import com.processout.sdk.api.model.request.POCreateCustomerRequest import com.processout.sdk.api.model.request.POCreateCustomerTokenRequest import com.processout.sdk.api.model.request.PODeleteCustomerTokenRequest +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentTokenizationRequest import com.processout.sdk.api.model.response.CustomerTokenResponse import com.processout.sdk.api.model.response.POCustomer import com.processout.sdk.api.model.response.POCustomerToken +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentTokenizationResponse import com.processout.sdk.core.ProcessOutResult import com.processout.sdk.core.annotation.ProcessOutInternalApi @@ -16,6 +18,10 @@ internal interface CustomerTokensRepository { request: POAssignCustomerTokenRequest ): ProcessOutResult + suspend fun tokenize( + request: PONativeAlternativePaymentTokenizationRequest + ): ProcessOutResult + suspend fun deleteCustomerToken( request: PODeleteCustomerTokenRequest ): ProcessOutResult diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultCustomerTokensRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultCustomerTokensRepository.kt index a9d9b83dc..d026b16a4 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultCustomerTokensRepository.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultCustomerTokensRepository.kt @@ -1,6 +1,15 @@ package com.processout.sdk.api.repository import com.processout.sdk.api.model.request.* +import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentRequestBody +import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentRequestBody.SubmitData +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData.Parameter +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData.Parameter.Value +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentTokenizationRequest +import com.processout.sdk.api.model.response.napm.v2.NativeAlternativePaymentElement +import com.processout.sdk.api.model.response.napm.v2.NativeAlternativePaymentTokenizationResponseBody +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentTokenizationResponse import com.processout.sdk.api.network.CustomerTokensApi import com.processout.sdk.core.POFailure import com.processout.sdk.core.ProcessOutResult @@ -23,6 +32,16 @@ internal class DefaultCustomerTokensRepository( ) } + override suspend fun tokenize( + request: PONativeAlternativePaymentTokenizationRequest + ) = apiCall { + api.tokenize( + customerId = request.customerId, + tokenId = request.customerTokenId, + request = request.toBody() + ) + }.map { it.toModel() } + override suspend fun deleteCustomerToken( request: PODeleteCustomerTokenRequest ) = apiCall { @@ -57,4 +76,44 @@ internal class DefaultCustomerTokensRepository( metadata = metadata, deviceData = contextGraph.deviceData ) + + private fun PONativeAlternativePaymentTokenizationRequest.toBody() = + NativeAlternativePaymentRequestBody( + gatewayConfigurationId = gatewayConfigurationId, + submitData = submitData?.let { SubmitData(parameters = it.parameters.map()) } + ) + + private fun Map.map() = + mapValues { (_, parameter) -> + when (val value = parameter.value) { + is Value.String -> value.value + is Value.PhoneNumber -> value + } + } + + private fun NativeAlternativePaymentTokenizationResponseBody.toModel() = + PONativeAlternativePaymentTokenizationResponse( + state = state, +// paymentMethod = paymentMethod, // TODO(v2): uncomment + elements = elements?.map { + when (it) { + is NativeAlternativePaymentElement.Form -> + PONativeAlternativePaymentElement.Form( + parameterDefinitions = it.parameters.parameterDefinitions + ) + is NativeAlternativePaymentElement.CustomerInstruction -> + PONativeAlternativePaymentElement.CustomerInstruction( + instruction = it.instruction + ) + is NativeAlternativePaymentElement.CustomerInstructionGroup -> + PONativeAlternativePaymentElement.CustomerInstructionGroup( + label = it.label, + instructions = it.instructions + ) + NativeAlternativePaymentElement.Unknown -> + PONativeAlternativePaymentElement.Unknown + } + }, + redirect = redirect + ) } diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultInvoicesRepository.kt b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultInvoicesRepository.kt index ae564f974..daea990e1 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultInvoicesRepository.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/repository/DefaultInvoicesRepository.kt @@ -1,12 +1,12 @@ package com.processout.sdk.api.repository import com.processout.sdk.api.model.request.* -import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentAuthorizationRequestBody -import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentAuthorizationRequestBody.SubmitData +import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentRequestBody +import com.processout.sdk.api.model.request.napm.v2.NativeAlternativePaymentRequestBody.SubmitData import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationDetailsRequest import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest -import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest.Parameter -import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest.Parameter.Value +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData.Parameter +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData.Parameter.Value import com.processout.sdk.api.model.response.* import com.processout.sdk.api.model.response.napm.v2.NativeAlternativePaymentAuthorizationResponseBody import com.processout.sdk.api.model.response.napm.v2.NativeAlternativePaymentElement @@ -156,9 +156,9 @@ internal class DefaultInvoicesRepository( ) private fun PONativeAlternativePaymentAuthorizationRequest.toBody() = - NativeAlternativePaymentAuthorizationRequestBody( + NativeAlternativePaymentRequestBody( gatewayConfigurationId = gatewayConfigurationId, - submitData = parameters?.let { SubmitData(parameters = it.map()) } + submitData = submitData?.let { SubmitData(parameters = it.parameters.map()) } ) private fun Map.map() = diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/service/DefaultCustomerTokensService.kt b/sdk/src/main/kotlin/com/processout/sdk/api/service/DefaultCustomerTokensService.kt index 230b3c572..4b4e74a3e 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/service/DefaultCustomerTokensService.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/service/DefaultCustomerTokensService.kt @@ -6,8 +6,10 @@ import com.processout.sdk.api.model.request.POAssignCustomerTokenRequest import com.processout.sdk.api.model.request.POCreateCustomerRequest import com.processout.sdk.api.model.request.POCreateCustomerTokenRequest import com.processout.sdk.api.model.request.PODeleteCustomerTokenRequest +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentTokenizationRequest import com.processout.sdk.api.model.response.POCustomer import com.processout.sdk.api.model.response.POCustomerToken +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentTokenizationResponse import com.processout.sdk.api.repository.CustomerTokensRepository import com.processout.sdk.core.POFailure.Code.Cancelled import com.processout.sdk.core.POFailure.Code.Internal @@ -208,6 +210,11 @@ internal class DefaultCustomerTokensService( } } + override suspend fun tokenize( + request: PONativeAlternativePaymentTokenizationRequest + ): ProcessOutResult = + repository.tokenize(request) + override suspend fun deleteCustomerToken( request: PODeleteCustomerTokenRequest ): ProcessOutResult = diff --git a/sdk/src/main/kotlin/com/processout/sdk/api/service/POCustomerTokensService.kt b/sdk/src/main/kotlin/com/processout/sdk/api/service/POCustomerTokensService.kt index 863313407..97e18d399 100644 --- a/sdk/src/main/kotlin/com/processout/sdk/api/service/POCustomerTokensService.kt +++ b/sdk/src/main/kotlin/com/processout/sdk/api/service/POCustomerTokensService.kt @@ -4,8 +4,10 @@ import com.processout.sdk.api.model.request.POAssignCustomerTokenRequest import com.processout.sdk.api.model.request.POCreateCustomerRequest import com.processout.sdk.api.model.request.POCreateCustomerTokenRequest import com.processout.sdk.api.model.request.PODeleteCustomerTokenRequest +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentTokenizationRequest import com.processout.sdk.api.model.response.POCustomer import com.processout.sdk.api.model.response.POCustomerToken +import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentTokenizationResponse import com.processout.sdk.core.ProcessOutResult import com.processout.sdk.core.annotation.ProcessOutInternalApi import kotlinx.coroutines.Job @@ -62,6 +64,15 @@ interface POCustomerTokensService { threeDSService: PO3DSService ): ProcessOutResult + /** + * Tokenize native alternative payment. + */ + /** @suppress */ + @ProcessOutInternalApi + suspend fun tokenize( + request: PONativeAlternativePaymentTokenizationRequest + ): ProcessOutResult + /** * Deletes customer token. */ diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt index 530776932..eadceb8ff 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/PONativeAlternativePaymentConfiguration.kt @@ -67,15 +67,15 @@ data class PONativeAlternativePaymentConfiguration( ) : Flow() /** - * @param[gatewayConfigurationId] Gateway configuration identifier. * @param[customerId] Customer identifier. * @param[customerTokenId] Customer token identifier. + * @param[gatewayConfigurationId] Gateway configuration identifier. */ @Parcelize data class Tokenization( - val gatewayConfigurationId: String, val customerId: String, - val customerTokenId: String + val customerTokenId: String, + val gatewayConfigurationId: String ) : Flow() } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt index e45ca2d0f..182d8e8ba 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentInteractor.kt @@ -19,17 +19,17 @@ import com.processout.sdk.R import com.processout.sdk.api.dispatcher.POEventDispatcher import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationDetailsRequest import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest -import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest.Parameter.Companion.phoneNumber -import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentAuthorizationRequest.Parameter.Companion.string -import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentAuthorizationResponse -import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentCustomerInstruction -import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData.Parameter.Companion.phoneNumber +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentSubmitData.Parameter.Companion.string +import com.processout.sdk.api.model.request.napm.v2.PONativeAlternativePaymentTokenizationRequest +import com.processout.sdk.api.model.response.napm.v2.* import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.CustomerInstruction import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.Form import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.Form.Parameter import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentElement.Form.Parameter.Otp.Subtype -import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentState import com.processout.sdk.api.model.response.napm.v2.PONativeAlternativePaymentState.* +import com.processout.sdk.api.service.POCustomerTokensService import com.processout.sdk.api.service.POInvoicesService import com.processout.sdk.core.POFailure.Code.* import com.processout.sdk.core.POFailure.InvalidField @@ -75,6 +75,7 @@ internal class NativeAlternativePaymentInteractor( private val app: Application, private var configuration: PONativeAlternativePaymentConfiguration, private val invoicesService: POInvoicesService, + private val customerTokensService: POCustomerTokensService, private val barcodeBitmapProvider: BarcodeBitmapProvider, private val mediaStorageProvider: MediaStorageProvider, private val captureRetryStrategy: PORetryStrategy, @@ -92,9 +93,9 @@ internal class NativeAlternativePaymentInteractor( POLogAttribute.GATEWAY_CONFIGURATION_ID to flow.gatewayConfigurationId ) is Tokenization -> mapOf( - POLogAttribute.GATEWAY_CONFIGURATION_ID to flow.gatewayConfigurationId, POLogAttribute.CUSTOMER_ID to flow.customerId, - POLogAttribute.CUSTOMER_TOKEN_ID to flow.customerTokenId + POLogAttribute.CUSTOMER_TOKEN_ID to flow.customerTokenId, + POLogAttribute.GATEWAY_CONFIGURATION_ID to flow.gatewayConfigurationId ) } } @@ -163,7 +164,7 @@ internal class NativeAlternativePaymentInteractor( invoicesService.nativeAlternativePayment(request) .onSuccess { response -> handlePaymentState( - stateValue = response.toUserInputStateValue(), + stateValue = initUserInputStateValue(), paymentState = response.state, elements = response.elements ) @@ -174,7 +175,22 @@ internal class NativeAlternativePaymentInteractor( } private suspend fun fetchTokenizationDetails(flow: Tokenization) { - TODO(reason = "v2") + val request = PONativeAlternativePaymentTokenizationRequest( + customerId = flow.customerId, + customerTokenId = flow.customerTokenId, + gatewayConfigurationId = flow.gatewayConfigurationId + ) + customerTokensService.tokenize(request) + .onSuccess { response -> + handlePaymentState( + stateValue = initUserInputStateValue(), + paymentState = response.state, + elements = response.elements + ) + }.onFailure { failure -> + POLogger.info("Failed to fetch tokenization details: %s", failure) + _completion.update { Failure(failure) } + } } private suspend fun handlePaymentState( @@ -200,18 +216,17 @@ internal class NativeAlternativePaymentInteractor( handleFormParameters(stateValue, parameters) } - private fun PONativeAlternativePaymentAuthorizationResponse.toUserInputStateValue() = - UserInputStateValue( - fields = emptyList(), - focusedFieldId = null, - primaryActionId = ActionId.SUBMIT, - secondaryAction = NativeAlternativePaymentInteractorState.Action( - id = ActionId.CANCEL, - enabled = false - ), - submitAllowed = true, - submitting = false - ) + private fun initUserInputStateValue() = UserInputStateValue( + fields = emptyList(), + focusedFieldId = null, + primaryActionId = ActionId.SUBMIT, + secondaryAction = NativeAlternativePaymentInteractorState.Action( + id = ActionId.CANCEL, + enabled = false + ), + submitAllowed = true, + submitting = false + ) private fun UserInputStateValue.toCaptureStateValue( instructions: List?, @@ -537,7 +552,10 @@ internal class NativeAlternativePaymentInteractor( ) ) } - initiatePayment() + when (val flow = configuration.flow) { + is Authorization -> authorize(flow) + is Tokenization -> tokenize(flow) + } } } @@ -602,20 +620,15 @@ internal class NativeAlternativePaymentInteractor( private fun UserInputStateValue.areAllFieldsValid() = fields.all { it.isValid } - private fun initiatePayment() { - when (val flow = configuration.flow) { - is Authorization -> initiatePayment(flow) - is Tokenization -> initiatePayment(flow) - } - } - - private fun initiatePayment(flow: Authorization) { + private fun authorize(flow: Authorization) { _state.whenUserInput { stateValue -> interactorScope.launch { val request = PONativeAlternativePaymentAuthorizationRequest( invoiceId = flow.invoiceId, gatewayConfigurationId = flow.gatewayConfigurationId, - parameters = stateValue.fields.values() + submitData = PONativeAlternativePaymentSubmitData( + parameters = stateValue.fields.values() + ) ) invoicesService.authorize(request) .onSuccess { response -> @@ -634,8 +647,32 @@ internal class NativeAlternativePaymentInteractor( } } - private fun initiatePayment(flow: Tokenization) { - TODO(reason = "v2") + private fun tokenize(flow: Tokenization) { + _state.whenUserInput { stateValue -> + interactorScope.launch { + val request = PONativeAlternativePaymentTokenizationRequest( + customerId = flow.customerId, + customerTokenId = flow.customerTokenId, + gatewayConfigurationId = flow.gatewayConfigurationId, + submitData = PONativeAlternativePaymentSubmitData( + parameters = stateValue.fields.values() + ) + ) + customerTokensService.tokenize(request) + .onSuccess { response -> + handlePaymentState( + stateValue = stateValue, + paymentState = response.state, + elements = response.elements + ) + }.onFailure { failure -> + handlePaymentFailure( + failure = failure, + replaceWithLocalMessage = true + ) + } + } + } } private fun List.values() = @@ -795,7 +832,13 @@ internal class NativeAlternativePaymentInteractor( gatewayConfigurationId = flow.gatewayConfigurationId ) ).map() - is Tokenization -> TODO(reason = "v2") + is Tokenization -> customerTokensService.tokenize( + request = PONativeAlternativePaymentTokenizationRequest( + customerId = flow.customerId, + customerTokenId = flow.customerTokenId, + gatewayConfigurationId = flow.gatewayConfigurationId + ) + ).map() } POLogger.debug("Attempted to capture the payment.") if (isCaptureRetryable(result)) { @@ -827,6 +870,7 @@ internal class NativeAlternativePaymentInteractor( } } + @JvmName(name = "mapFromAuthorizationResult") private fun ProcessOutResult.map() = fold( onSuccess = { @@ -840,6 +884,20 @@ internal class NativeAlternativePaymentInteractor( onFailure = { it } ) + @JvmName(name = "mapFromTokenizationResult") + private fun ProcessOutResult.map() = + fold( + onSuccess = { + ProcessOutResult.Success( + ProcessingResponse( + state = it.state, + elements = it.elements + ) + ) + }, + onFailure = { it } + ) + private fun isCaptureRetryable( result: ProcessOutResult ): Boolean = result.fold( diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt index 9492e13db..4c0bd514e 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/napm/v2/NativeAlternativePaymentViewModel.kt @@ -57,6 +57,7 @@ internal class NativeAlternativePaymentViewModel private constructor( app = app, configuration = configuration, invoicesService = ProcessOut.instance.invoices, + customerTokensService = ProcessOut.instance.customerTokens, barcodeBitmapProvider = BarcodeBitmapProvider(), mediaStorageProvider = MediaStorageProvider(app), captureRetryStrategy = Exponential(