Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
8e4f337
feature: creating 3ds
capaixao Jul 23, 2025
f805c12
feature: implement agnostic 3DS authentication module
capaixao Jul 24, 2025
093f983
feature: adjusted ktlint
capaixao Jul 25, 2025
21609ec
feature: adjustments in provider and challenge error
capaixao Jul 25, 2025
765b769
feature: adjust mock call response
capaixao Jul 25, 2025
99976b8
feature: remove compose in 3ds module
capaixao Jul 25, 2025
a58786d
feature: changed implementation with master card sdk
capaixao Jul 25, 2025
3af0192
feature: added gradle config
capaixao Jul 25, 2025
8e01a84
feature: commit
capaixao Jul 25, 2025
be79649
feature: ktlint changes
capaixao Jul 25, 2025
c019ff3
feature: several changes and ajustments to clean code
capaixao Jul 25, 2025
716182d
feature: several adjustment to clean code
capaixao Jul 28, 2025
83dfb95
feature: MPThreeDS initialize changes
capaixao Jul 28, 2025
3de7d36
feature: added unit tests
capaixao Jul 28, 2025
4d95408
feature: removed unused unit tests
capaixao Jul 28, 2025
2414b7b
feature: added unit tests
capaixao Aug 4, 2025
b231845
several changes
capaixao Sep 4, 2025
40fc5d1
several changes
capaixao Sep 8, 2025
04584e0
deleted tests
capaixao Sep 8, 2025
de410cb
changed implementation and initialization
capaixao Sep 8, 2025
44b4825
feature: changed proguard rules
capaixao Sep 19, 2025
28e1880
feature: create unit test
capaixao Sep 19, 2025
9fbe3b2
feature: added documentation
capaixao Sep 19, 2025
c7731a2
feature: removed comments
capaixao Sep 19, 2025
e5b7a1d
feature: lint fix
capaixao Sep 19, 2025
34cb993
feature: changed test case
capaixao Sep 19, 2025
ded6d65
Merge branch 'main' into feature/2962/checkout-implementation
capaixao Sep 22, 2025
9a16d22
feature: usdk implementation
capaixao Nov 3, 2025
6c27d5d
feature: added new maven repository url
capaixao Nov 4, 2025
c5cee6c
feature: change function to private
capaixao Nov 4, 2025
8d92f45
feature: ktlintformat
capaixao Nov 4, 2025
e2ad755
feature: change secret file
capaixao Nov 4, 2025
f21c776
feature: changed threeds to java 1.8
capaixao Nov 4, 2025
de4ea5a
feature: code review changes
capaixao Nov 7, 2025
17d3c4b
feature: code review changes
capaixao Nov 7, 2025
b70d98f
feature: code review changes
capaixao Nov 7, 2025
f619f40
feature: code review changes
capaixao Nov 7, 2025
cccce48
feature: changed methods assignature
capaixao Nov 11, 2025
c14a3bd
feature: threeds implementation changes
capaixao Nov 24, 2025
0319853
feature: threeDS initialization changes
capaixao Nov 24, 2025
b730979
feature: threeDS implementation changes
capaixao Dec 1, 2025
8e2fa26
feature: threeDS provider implementation
capaixao Dec 1, 2025
9401c05
feature: threeDS coremethods extension
capaixao Dec 2, 2025
f2147ce
feature: ktlint and deteckt adjusts
capaixao Dec 2, 2025
7f29e37
feature: added threeds in bom
capaixao Dec 3, 2025
7a19f46
feature: changed circle ci config
capaixao Dec 3, 2025
931a9f0
feature: added 3DS implementation to testimplementation
capaixao Dec 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ jobs:
sudo apt-get install git-lfs
git lfs install --skip-smudge
- checkout
- run: git lfs fetch --all
# - run: git lfs fetch --all
- run:
name: Set Secrets File
command: echo "$SECRETS_FILE" | base64 --decode > secrets.properties
command: echo "$SECRET_FILE" | base64 --decode > secrets.properties
- run:
name: Check JDK exists
command: javac -version
Expand Down
1 change: 1 addition & 0 deletions core-methods/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ ksp {
}

dependencies {

implementation(projects.core)
implementation(projects.sdkAndroid)
implementation(projects.analytics)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ class CoreMethods internal constructor(
securityCode = securityCodeState.input,
buyerIdentification = buyerIdentification,
)

when (result) {
is Result.Error -> {
when (result.error) {
Expand Down Expand Up @@ -219,7 +218,10 @@ class CoreMethods internal constructor(

is Result.Success -> {
MPAnalytics.getInstance().trackMetric(
metricGenerateCardTokenCallSuccess(isSavedCard = true, identityType = buyerIdentification.type),
metricGenerateCardTokenCallSuccess(
isSavedCard = true,
identityType = buyerIdentification.type,
),
)
}
}
Expand Down Expand Up @@ -333,7 +335,6 @@ class CoreMethods internal constructor(
*/
suspend fun getIdentificationTypes(): Result<List<IdentificationType>, ResultError> {
val result = koin.get<GetIdentificationTypesUseCase>().invoke()

when (result) {
is Result.Error -> {
when (result.error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package com.mercadopago.sdk.android.coremethods.domain.interactor

import android.app.Activity
import com.mercadopago.sdk.android.coremethods.domain.model.CardToken
import com.mercadopago.sdk.android.coremethods.domain.model.ResultError
import com.mercadopago.sdk.android.coremethods.domain.provider.ThreeDSProvider
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSAuthenticationModel
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSChallengeResult
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSWarning
import com.mercadopago.sdk.android.coremethods.domain.utils.Result

/**
* Private property to hold the 3DS provider instance.
* This is set by the client application through setThreeDSProvider method.
*/
private var threeDSProvider: ThreeDSProvider? = null

/**
* Sets the 3DS provider implementation for this CoreMethods instance.
* This method should be called once during application initialization to enable 3DS functionality.
*
* @param provider The ThreeDSProvider implementation to use for 3DS operations
*
* Example:
* ```kotlin
* val coreMethods = MercadoPagoSDK.getInstance().coreMethods
* val threeDS = MPThreeDS.getInstance(context)
* coreMethods.setThreeDSProvider(MPThreeDSProviderAdapter(threeDS))
* ```
*/
fun CoreMethods.setThreeDSProvider(provider: ThreeDSProvider) {
threeDSProvider = provider
}

/**
* Checks if a 3DS provider is currently configured and available.
*
* This internal helper method is used to verify that a [ThreeDSProvider]
* has been set via [setThreeDSProvider] before attempting any 3DS operations.
*
* @return `true` if a provider is set and available, `false` otherwise
*/
private fun CoreMethods.hasThreeDSProvider(): Boolean = threeDSProvider != null

/**
* Retrieves the list of security warnings from the 3DS SDK.
* These warnings indicate potential security issues or configuration problems
* that may affect the 3DS authentication process.
*
* This method requires a 3DS provider to be set via [setThreeDSProvider].
*
* @return [Result.Success] with a list of [ThreeDSWarning] if the operation completed successfully,
* [Result.Error] with [ResultError] if the provider is not available or an error occurred
*
* Example:
* ```kotlin
* val result = coreMethods.getWarnings()
* when (result) {
* is Result.Success -> {
* val warnings = result.data
* warnings.forEach { warning ->
* Log.w("3DS", "Warning: ${warning.message}")
* }
* }
* is Result.Error -> {
* // Handle error
* }
* }
* ```
*/
fun CoreMethods.getWarnings(): Result<List<ThreeDSWarning>, ResultError> {
if (!hasThreeDSProvider()) {
return Result.Error(
ResultError.Validation(
message = "3DS provider not available. Please set the provider using setThreeDSProvider() method.",
),
)
}
return runCatching { threeDSProvider?.getWarnings() }
.fold(
onSuccess = { warnings ->
warnings?.let { Result.Success(it) } ?: Result.Error(
ResultError.Request(
code = "",
message = "Failed to get 3DS warnings.",
),
)
},
onFailure = { throwable ->
Result.Error(
ResultError.Request(
code = "",
message = "Error getting warnings: ${throwable.message}",
),
)
},
)
}

/**
* Starts the 3DS challenge flow with the provided authentication response.
* This method requires a 3DS provider to be set via setThreeDSProvider.
*
* @param activity The activity context for displaying the challenge UI
* @param authentication The authentication response received from your backend
* @param timeout The challenge timeout in minutes (default: 10)
* @return [Result.Success] with ThreeDSChallengeResult if the challenge completed,
* [Result.Error] with ResultError if the provider is not available or an error occurred
*
* Example:
* ```kotlin
* val result = coreMethods.startThreeDSChallenge(
* activity = this,
* authentication = authenticationModel,
* timeout = 10
* )
* when (result) {
* is Result.Success -> {
* when (val challengeResult = result.data) {
* is ThreeDSChallengeResult.OnSuccess -> {
* // Challenge completed successfully
* val authenticated = challengeResult.result
* }
* is ThreeDSChallengeResult.OnError -> {
* // Challenge failed
* }
* is ThreeDSChallengeResult.OnCancel -> {
* // User cancelled
* }
* is ThreeDSChallengeResult.OnTimedOut -> {
* // Challenge timed out
* }
* }
* }
* is Result.Error -> {
* // Provider not available
* }
* }
* ```
*/
suspend fun CoreMethods.startChallenge(
activity: Activity,
authentication: ThreeDSAuthenticationModel,
timeout: Int = 10,
): Result<ThreeDSChallengeResult, ResultError> {
if (!hasThreeDSProvider()) {
return Result.Error(
ResultError.Validation(
message = "3DS provider not available. Please set the provider using setThreeDSProvider() method.",
),
)
}
return runCatching {
threeDSProvider?.doChallenge(
activity = activity,
authentication = authentication,
timeout = timeout,
)
}.fold(
onSuccess = { challengeResult ->
challengeResult?.let { Result.Success(it) } ?: Result.Error(
ResultError.Request(
code = "",
message = "Failed to execute 3DS challenge.",
),
)
},
onFailure = { throwable ->
Result.Error(
ResultError.Request(
code = "",
message = "Error during 3DS challenge: ${throwable.message}",
),
)
},
)
}

/**
* Closes the current 3DS transaction and releases associated resources.
* This method should be called after the 3DS authentication flow is complete,
* regardless of whether it succeeded or failed.
*
* Calling this method ensures proper cleanup of the 3DS SDK resources
* and prepares the provider for a new transaction if needed.
*
* @return [Result.Success] with a confirmation message if the transaction was closed successfully,
* [Result.Error] with [ResultError] if an error occurred during cleanup
*
* Example:
* ```kotlin
* val result = coreMethods.close()
* when (result) {
* is Result.Success -> {
* Log.d("3DS", "Transaction closed successfully")
* }
* is Result.Error -> {
* Log.e("3DS", "Failed to close transaction: ${result.error.message}")
* }
* }
* ```
*/
fun CoreMethods.close(): Result<String, ResultError> {
if (!hasThreeDSProvider()) {
return Result.Error(
ResultError.Validation(
message = "3DS provider not available. Please set the provider using setThreeDSProvider() method.",
),
)
}
return runCatching { threeDSProvider?.close() }
.fold(
onSuccess = { Result.Success("Transaction closed") },
onFailure = { throwable ->
Result.Error(
ResultError.Validation(
message = "Failed to close transaction: ${throwable.message}",
),
)
},
)
}

/**
* Creates a new 3DS transaction using the provided card token.
* This method initializes the 3DS authentication process for the specified card.
*
* This method requires a 3DS provider to be set via [setThreeDSProvider].
* The transaction must be created before calling [startChallenge] to execute
* the 3DS challenge flow.
*
* @param cardToken The [CardToken] containing the tokenized card information
* @return [Result.Success] with a confirmation message if the transaction was created successfully,
* [Result.Error] with [ResultError] if the provider is not available or an error occurred
*
* Example:
* ```kotlin
* val cardToken = CardToken(token = "card_token_123")
* val result = coreMethods.createTransaction(cardToken)
* when (result) {
* is Result.Success -> {
* Log.d("3DS", "Transaction created successfully")
* // Proceed with 3DS authentication
* }
* is Result.Error -> {
* Log.e("3DS", "Failed to create transaction: ${result.error.message}")
* }
* }
* ```
*/
fun CoreMethods.createTransaction(cardToken: CardToken): Result<String, ResultError> {
if (!hasThreeDSProvider()) {
return Result.Error(
ResultError.Validation(
message = "3DS provider not available. Please set the provider using setThreeDSProvider() method.",
),
)
}
return runCatching { threeDSProvider?.createTransaction(cardToken.token) }
.fold(
onSuccess = { Result.Success("Transaction created") },
onFailure = { throwable ->
Result.Error(
ResultError.Validation(
message = "Failed to create transaction: ${throwable.message}",
),
)
},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.mercadopago.sdk.android.coremethods.domain.provider

import android.app.Activity
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSAuthenticationModel
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSChallengeResult
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSRequestParams
import com.mercadopago.sdk.android.coremethods.domain.provider.models.ThreeDSWarning

/**
* Interface defining the contract for 3DS (3-D Secure) operations.
* This interface allows CoreMethods to interact with 3DS functionality
* without directly depending on the threeds module.
*
* Implementations of this interface should handle:
* - Transaction creation
* - Authentication parameter generation
* - Challenge flow execution
* - SDK warnings retrieval
* - Resource cleanup
*
* @see ThreeDSRequestParams
* @see ThreeDSAuthenticationModel
* @see ThreeDSChallengeResult
* @see ThreeDSWarning
*/
interface ThreeDSProvider {
/**
* Creates a 3DS transaction for the specified payment method.
*
* @param paymentMethodId The payment method ID to create the transaction for (e.g., "visa", "mastercard")
*/
fun createTransaction(paymentMethodId: String)

/**
* Retrieves the authentication request parameters for the current transaction.
* These parameters should be sent to the backend for 3DS authentication.
*
* @return The authentication request parameters, or null if no transaction is active
*/
fun getAuthenticationRequestParameters(): ThreeDSRequestParams?

/**
* Performs the 3DS challenge flow with the provided authentication response.
* This is a suspending function that will display the challenge UI and wait for completion.
*
* @param activity The activity context for displaying the challenge UI
* @param authentication The authentication response received from the backend
* @param timeout The challenge timeout in minutes (default: 10)
* @return The result of the challenge flow
*/
suspend fun doChallenge(
activity: Activity,
authentication: ThreeDSAuthenticationModel,
timeout: Int,
): ThreeDSChallengeResult

/**
* Closes the current 3DS transaction and releases associated resources.
* This should be called when the 3DS flow is complete or cancelled.
*/
fun close()

/**
* Retrieves warnings generated by the 3DS SDK during initialization or operation.
*
* @return A list of warnings, or an empty list if no warnings were generated
*/
fun getWarnings(): List<ThreeDSWarning>
}
Loading