Skip to content

Commit

Permalink
feat(POM-447): (Dynamic Checkout) Delete saved payment methods (#255)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalii-vanziak-cko authored Jan 30, 2025
1 parent f6b3177 commit 7b33eb9
Show file tree
Hide file tree
Showing 82 changed files with 2,488 additions and 520 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

buildscript {
ext {
androidGradlePluginVersion = '8.7.3'
androidGradlePluginVersion = '8.8.0'
kotlinVersion = '2.1.0'
kspVersion = '2.1.0-1.0.29'
dokkaVersion = '1.9.20'
Expand Down Expand Up @@ -46,7 +46,7 @@ ext {
androidxSwipeRefreshLayoutVersion = '1.1.0'
androidxBrowserVersion = '1.8.0'

androidxComposeBOMVersion = '2024.12.01'
androidxComposeBOMVersion = '2025.01.01'
composeGooglePayButtonVersion = '1.0.0'

materialVersion = '1.12.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.processout.sdk.core.ProcessOutActivityResult
import com.processout.sdk.core.onFailure
import com.processout.sdk.core.onSuccess
import com.processout.sdk.ui.card.tokenization.POCardTokenizationConfiguration
import com.processout.sdk.ui.card.tokenization.POCardTokenizationConfiguration.SubmitButton
import com.processout.sdk.ui.card.tokenization.POCardTokenizationConfiguration.Button
import com.processout.sdk.ui.card.tokenization.POCardTokenizationLauncher
import com.processout.sdk.ui.shared.view.dialog.POAlertDialog
import com.processout.sdk.ui.threeds.PO3DSRedirectCustomTabLauncher
Expand Down Expand Up @@ -100,7 +100,7 @@ class CardPaymentFragment : BaseFragment<FragmentCardPaymentBinding>(
launcher.launch(
POCardTokenizationConfiguration(
savingAllowed = true,
submitButton = SubmitButton()
submitButton = Button()
)
)
}
Expand Down Expand Up @@ -179,7 +179,7 @@ class CardPaymentFragment : BaseFragment<FragmentCardPaymentBinding>(

private fun handleControls(uiState: CardPaymentUiState) {
when (uiState) {
Initial -> enableControls(true)
Initial, is Failure -> enableControls(true)
else -> enableControls(false)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ import com.processout.sdk.core.onFailure
import com.processout.sdk.core.onSuccess
import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration
import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.AlternativePaymentConfiguration
import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.AlternativePaymentConfiguration.PaymentConfirmationConfiguration
import com.processout.sdk.ui.checkout.PODynamicCheckoutConfiguration.SubmitButton
import com.processout.sdk.ui.checkout.PODynamicCheckoutLauncher
import com.processout.sdk.ui.shared.view.dialog.POAlertDialog
import com.processout.sdk.ui.threeds.PO3DSRedirectCustomTabLauncher
Expand Down Expand Up @@ -97,10 +95,7 @@ class DynamicCheckoutFragment : BaseFragment<FragmentDynamicCheckoutBinding>(
clientSecret = uiModel.clientSecret
),
alternativePayment = AlternativePaymentConfiguration(
returnUrl = Constants.RETURN_URL,
paymentConfirmation = PaymentConfirmationConfiguration(
confirmButton = SubmitButton()
)
returnUrl = Constants.RETURN_URL
)
)
)
Expand Down Expand Up @@ -142,7 +137,7 @@ class DynamicCheckoutFragment : BaseFragment<FragmentDynamicCheckoutBinding>(

private fun handleControls(uiState: DynamicCheckoutUiState) {
when (uiState) {
Initial -> enableControls(true)
Initial, is Failure -> enableControls(true)
else -> enableControls(false)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import com.processout.sdk.api.model.response.POCard
import com.processout.sdk.api.model.response.POGooglePayCardTokenizationData
import com.processout.sdk.core.*
import com.processout.sdk.ui.card.update.POCardUpdateConfiguration
import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.*
import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.CardInformation
import com.processout.sdk.ui.card.update.POCardUpdateLauncher
import com.processout.sdk.ui.googlepay.POGooglePayCardTokenizationLauncher
import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration
Expand Down Expand Up @@ -91,19 +91,16 @@ class FeaturesFragment : BaseFragment<FragmentFeaturesBinding>(
cardUpdateLauncher.launch(
POCardUpdateConfiguration(
cardId = card?.id ?: String(),
options = Options(
cardInformation = CardInformation(
maskedNumber = maskedNumber,
iin = card?.iin,
scheme = card?.scheme,
preferredScheme = card?.coScheme
),
submitButton = SubmitButton(),
cancellation = POCancellationConfiguration(
backPressed = true,
dragDown = true,
touchOutside = false
)
cardInformation = CardInformation(
maskedNumber = maskedNumber,
iin = card?.iin,
scheme = card?.scheme,
preferredScheme = card?.coScheme
),
cancellation = POCancellationConfiguration(
backPressed = true,
dragDown = true,
touchOutside = false
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.processout.example.ui.screen.nativeapm.NativeApmUiState.*
import com.processout.sdk.core.onFailure
import com.processout.sdk.core.onSuccess
import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration
import com.processout.sdk.ui.napm.PONativeAlternativePaymentConfiguration.Button
import com.processout.sdk.ui.napm.PONativeAlternativePaymentLauncher
import com.processout.sdk.ui.nativeapm.PONativeAlternativePaymentMethodConfiguration
import com.processout.sdk.ui.nativeapm.PONativeAlternativePaymentMethodLauncher
Expand Down Expand Up @@ -96,7 +97,8 @@ class NativeApmFragment : BaseFragment<FragmentNativeApmBinding>(
launcherCompose.launch(
PONativeAlternativePaymentConfiguration(
invoiceId = uiModel.invoiceId,
gatewayConfigurationId = uiModel.gatewayConfigurationId
gatewayConfigurationId = uiModel.gatewayConfigurationId,
submitButton = Button()
)
)
} else {
Expand All @@ -112,7 +114,7 @@ class NativeApmFragment : BaseFragment<FragmentNativeApmBinding>(

private fun handleControls(uiState: NativeApmUiState) {
when (uiState) {
Initial -> enableControls(true)
Initial, is Failure -> enableControls(true)
else -> enableControls(false)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.processout.sdk.api.model.event

import com.processout.sdk.core.ProcessOutResult
import com.processout.sdk.core.annotation.ProcessOutInternalApi

/**
* Defines saved payment methods lifecycle events.
*/
/** @suppress */
@ProcessOutInternalApi
sealed class POSavedPaymentMethodsEvent {

/**
* Initial event that is sent prior any other event.
*/
data object WillStart : POSavedPaymentMethodsEvent()

/**
* Event indicates that initialization is complete.
*/
data object DidStart : POSavedPaymentMethodsEvent()

/**
* Event is sent when customer token has been deleted.
*/
data class DidDeleteCustomerToken(
val customerId: String,
val tokenId: String
) : POSavedPaymentMethodsEvent()

/**
* Event is sent when unretryable error occurs. This is a final event.
*/
data class DidFail(
val failure: ProcessOutResult.Failure
) : POSavedPaymentMethodsEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ data class POCreateInvoiceRequest(
val customerId: String? = null,
@Json(name = "return_url")
val returnUrl: String? = null,
val device: Map<String, String> = mapOf("channel" to "android")
val device: Map<String, String> = mapOf("channel" to "android"),
val metadata: Map<String, String>? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.processout.sdk.api.model.request

/**
* Request to delete a customer token.
*
* @param[customerId] ID of the customer.
* @param[tokenId] Token ID that belong to the customer.
* @param[clientSecret] Client secret is a value of __X-ProcessOut-Client-Secret__ header of the invoice.
*/
data class PODeleteCustomerTokenRequest(
val customerId: String,
val tokenId: String,
val clientSecret: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import java.util.Date
* @param captureAmount Amount of money to capture when partial captures are available. Note that this only applies if you are also using the [autoCaptureAt] option.
* @param authorizeOnly Boolean value indicating whether should only authorize the invoice or also capture it. Default value is _true_.
* @param allowFallbackToSale Boolean value indicating whether should fallback to sale if the gateway does not support separation between authorization and capture. Default value is _false_.
* @param clientSecret Client secret is a value of __x-processout-client-secret__ header of the invoice.
* @param clientSecret Client secret is a value of __X-ProcessOut-Client-Secret__ header of the invoice.
* @param metadata Additional metadata.
*/
data class POInvoiceAuthorizationRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import kotlinx.parcelize.Parcelize
* Request to get single invoice details.
*
* @param[invoiceId] Requested invoice ID.
* @param[clientSecret] Client secret is a value of __x-processout-client-secret__ header of the invoice.
* @param[clientSecret] Client secret is a value of __X-ProcessOut-Client-Secret__ header of the invoice.
* When provided payment methods saved by the customer will be included in the response if the invoice has assigned customer ID.
*/
@Parcelize
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal data class InvoiceResponseBody(
val currency: String,
@Json(name = "return_url")
val returnUrl: String?,
@Json(name = "customer_id")
val customerId: String?,
val transaction: POTransaction?,
@Json(name = "payment_methods")
val paymentMethods: List<PODynamicCheckoutPaymentMethod>?
Expand All @@ -28,15 +30,17 @@ internal data class InvoiceResponseBody(
* @param[amount] Invoice amount.
* @param[currency] Invoice currency.
* @param[returnUrl] Return URL or deep link for web based operations.
* @param[customerId] Customer identifier.
* @param[transaction] Transaction details.
* @param[paymentMethods] Dynamic checkout configuration.
* @param[clientSecret] Client secret is a value of __x-processout-client-secret__ header of the invoice.
* @param[clientSecret] Client secret is a value of __X-ProcessOut-Client-Secret__ header of the invoice.
*/
data class POInvoice(
val id: String,
val amount: String = String(),
val currency: String = String(),
val returnUrl: String? = null,
val customerId: String? = null,
@ProcessOutInternalApi val transaction: POTransaction? = null,
@ProcessOutInternalApi val paymentMethods: List<PODynamicCheckoutPaymentMethod>? = null,
@ProcessOutInternalApi val clientSecret: String? = null
Expand Down Expand Up @@ -244,7 +248,9 @@ sealed class PODynamicCheckoutPaymentMethod {
@Json(name = "customer_token_id")
val customerTokenId: String,
@Json(name = "redirect_url")
val redirectUrl: String?
val redirectUrl: String?,
@Json(name = "deleting_allowed")
val deletingAllowed: Boolean
)

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@ import com.processout.sdk.api.model.request.POCreateCustomerRequest
import com.processout.sdk.api.model.request.POCreateCustomerTokenRequestBody
import com.processout.sdk.api.model.response.CustomerResponse
import com.processout.sdk.api.model.response.CustomerTokenResponse
import com.processout.sdk.api.network.HeaderConstants.CLIENT_SECRET
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.*

internal interface CustomerTokensApi {

Expand All @@ -20,12 +18,21 @@ internal interface CustomerTokensApi {
@Body request: AssignCustomerTokenRequestWithDeviceData
): Response<CustomerTokenResponse>

@DELETE("/customers/{customer_id}/tokens/{token_id}")
suspend fun deleteCustomerToken(
@Path("customer_id") customerId: String,
@Path("token_id") tokenId: String,
@Header(CLIENT_SECRET) clientSecret: String
): Response<Unit>

@POST("/customers/{customer_id}/tokens")
suspend fun createCustomerToken(
@Path("customer_id") customerId: String,
@Body request: POCreateCustomerTokenRequestBody
): Response<CustomerTokenResponse>

@POST("/customers")
suspend fun createCustomer(@Body request: POCreateCustomerRequest): Response<CustomerResponse>
suspend fun createCustomer(
@Body request: POCreateCustomerRequest
): Response<CustomerResponse>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.processout.sdk.api.network

internal object HeaderConstants {
const val CLIENT_SECRET = "X-ProcessOut-Client-Secret"
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,17 @@ 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.response.*
import com.processout.sdk.api.network.HeaderConstants.CLIENT_SECRET
import retrofit2.Response
import retrofit2.http.*

internal interface InvoicesApi {

companion object {
const val HEADER_CLIENT_SECRET = "x-processout-client-secret"
}

@POST("/invoices/{id}/authorize")
suspend fun authorizeInvoice(
@Path("id") invoiceId: String,
@Body request: InvoiceAuthorizationRequestWithDeviceData,
@Header(HEADER_CLIENT_SECRET) clientSecret: String?
@Header(CLIENT_SECRET) clientSecret: String?
): Response<InvoiceAuthorizationResponse>

@POST("/invoices/{id}/native-payment")
Expand All @@ -42,7 +39,7 @@ internal interface InvoicesApi {
@GET("/invoices/{id}?expand=transaction")
suspend fun invoice(
@Path("id") invoiceId: String,
@Header(HEADER_CLIENT_SECRET) clientSecret: String?
@Header(CLIENT_SECRET) clientSecret: String?
): Response<InvoiceResponse>

@POST("/invoices")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal class RetryInterceptor(

private fun Request.addIdempotencyKey(): Request =
when (method) {
"POST" -> newBuilder()
"POST", "PUT", "DELETE" -> newBuilder()
.header("Idempotency-Key", UUID.randomUUID().toString())
.build()
else -> this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.processout.sdk.api.repository
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.response.CustomerTokenResponse
import com.processout.sdk.api.model.response.POCustomer
import com.processout.sdk.api.model.response.POCustomerToken
Expand All @@ -15,6 +16,10 @@ internal interface CustomerTokensRepository {
request: POAssignCustomerTokenRequest
): ProcessOutResult<CustomerTokenResponse>

suspend fun deleteCustomerToken(
request: PODeleteCustomerTokenRequest
): ProcessOutResult<Unit>

/** @suppress */
@ProcessOutInternalApi
suspend fun createCustomerToken(
Expand All @@ -23,5 +28,7 @@ internal interface CustomerTokensRepository {

/** @suppress */
@ProcessOutInternalApi
suspend fun createCustomer(request: POCreateCustomerRequest): ProcessOutResult<POCustomer>
suspend fun createCustomer(
request: POCreateCustomerRequest
): ProcessOutResult<POCustomer>
}
Loading

0 comments on commit 7b33eb9

Please sign in to comment.