Repository navigation
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ized Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Repeated authentication responses can cause an unbounded loop that never returns a merchant result and leaves the in-flight guard blocking subsequent calls.
4 open issues
What changed in this PR
Adds Cardinal-backed 3D Secure device data collection to PaymentActions, allowing its existing drive loop to handle authentication setup internally while leaving other customer actions as handoff boundaries.
Changes:
- Introduces a provider interface, result types, and Cardinal implementation.
- Wires collection and server signaling into the handler and client.
- Adds the Cardinal dependency, consumer ProGuard rules, and unit tests.
| File | Description |
|---|---|
PaymentActions/src/test/java/com/braintreepayments/api/paymentactions/PaymentActionsClientUnitTest.kt |
Tests automatic collection during payment submission. |
PaymentActions/src/test/java/com/braintreepayments/api/paymentactions/NextActionHandlerUnitTest.kt |
Tests collection, signaling, response handling, and failures. |
PaymentActions/src/test/java/com/braintreepayments/api/paymentactions/CardinalThreeDSecureProviderUnitTest.kt |
Tests Cardinal configuration, callbacks, and setup errors. |
PaymentActions/src/main/java/com/braintreepayments/api/paymentactions/ThreeDSecureProvider.kt |
Defines the provider boundary and collection results. |
PaymentActions/src/main/java/com/braintreepayments/api/paymentactions/PaymentActionsClient.kt |
Constructs and injects the provider; updates behavior documentation. |
PaymentActions/src/main/java/com/braintreepayments/api/paymentactions/NextActionHandler.kt |
Collects device data and continues from the signal response. |
PaymentActions/src/main/java/com/braintreepayments/api/paymentactions/CardinalThreeDSecureProvider.kt |
Implements Cardinal session setup and callback adaptation. |
PaymentActions/proguard.pro |
Adds Cardinal warning suppression. |
PaymentActions/build.gradle |
Adds Cardinal and registers consumer ProGuard rules. |
Review effort: Balanced
|
|
||
| implementation libs.androidx.annotation | ||
| implementation libs.coroutines.core | ||
| implementation libs.cardinal |
Comment on lines
+138
to
+139
| * 3D Secure device data collection is performed internally when the Payment Action requires | ||
| * it. Any other customer action returns [PaymentActionResult.CustomerActionRequired]. |
Base automatically changed from
payment-actions-drive-loop
to
payment-actions-m3-feature
November 9, 2026 17:05
… payment-actions-3ds-wrapper
| } | ||
| // The signal's response is the updated Payment Action, so the next turn uses it | ||
| // instead of calling getPaymentAction() again. | ||
| passedInResult = collectDeviceDataAndSignal() |
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
The new automatic PaymentActions 3DS device-data collection lacks an unreleased changelog entry, and DEPENDENCIES.md omits the module’s Cardinal runtime dependency.
3 open issues
3 resolved since last review
Review effort: Balanced
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary of changes
:PaymentActions, plus a consumer ProGuard rule (-dontwarn com.cardinalcommerce**) matching:ThreeDSecure. As with the legacy module, apps using:PaymentActionsneed the Cardinal JFrog repository.ThreeDSecureProvider, an internal provider-neutral interface the drive loop depends on.collectDeviceData()returns a sealedDeviceDataResult(Collected(dfReferenceId)orFailed(error)), so failures never throw through the loop. Switching 3DS providers later means a new implementation plus one line inPaymentActionsClient; the loop and public API stay the same.CardinalThreeDSecureProvider, the only class that touches Cardinal. It configures Cardinal from the Braintree configuration and bridges Cardinal'sinitcallback into a suspend call.NextActionHandlernow resolves 3DS device data collection (DDC). OnREQUIRES_CUSTOMER_ACTIONwith aThreeDSecureAuthenticationnext action, it collects device data, sendssignalCustomerActionComplete(DeviceDataCollection(dfReferenceId)), and continues on the signal's response instead of re-fetching. A frictionless flow therefore ends at confirm or complete with no extra request. Any other next action, including a challenge, still returnsCustomerActionRequired.PaymentActionsClientbuilds the Cardinal provider in a private constructor. The public constructor is unchanged, so merchants call nothing new.payment-actions-drive-loop. The iOS equivalent is Cardinal mobile integration braintree_ios#1904. The 3DS challenge, public launch options and analytics are not part of this PR.Cardinal configuration
production, otherwise STAGINGrequestTimeoutisEnableDFSynconSetupCompletedwaits until device data has reached Cardinal, so the server never gets adfReferenceIdwhose data is still in flightisEnableLoggingchallengeTimeoutuiTypeWhy it's shaped this way
cardinalAuthenticationJwtreturns aFailure, as on iOS. There is no separate "3DS enabled" check.CardinalSessionthat has to span DDC and the challenge. Android's Cardinal is a process-wide singleton, so the provider holds no state.AtomicBooleanlets only the first callback resume it.PaymentActionServiceResult.Failure, so it takes the same exit as a failed fetch or signal.Testing
CardinalThreeDSecureProviderUnitTest: configuration per environment, 2-arginitordering, success, missing and empty JWT, setup validation failure,configure/initthrowing, configuration fetch failure, and duplicate callbacks.NextActionHandlerUnitTest: DDC then signal then continue (confirm, complete, PROCESSING polling, challenge hand-off), DDC failure with no signal sent, signal failure, a version decrease on the signal response, and next actions that never reach the provider.PaymentActionsClientUnitTest:submitForPaymentActiongoes through DDC.:PaymentActions:testReleaseUnitTest,detekt,lintRelease,:Demo:assembleDebug.AI Usage
Which AI Agent Was Used?
How was AI used?
Claude helped plan the design against the Android 3DS spike and the iOS implementation, wrote the provider, handler changes and unit tests, and drafted this description. I reviewed the design choices and the diff.
Estimated AI Code Contribution
Checklist
Authors
🤖 Generated with Claude Code