Fix: inject moto=true for user-key integrations with saved payment method ID 🪿✨ - #2564
Draft
joyceqin-stripe wants to merge 9 commits into
Draft
Fix: inject moto=true for user-key integrations with saved payment method ID 🪿✨#2564joyceqin-stripe wants to merge 9 commits into
joyceqin-stripe wants to merge 9 commits into
Conversation
…saved payment method ID When the Stripe SDK is initialized with a user key (uk_), the MOTO flag was only injected when creating a new card inline. Confirming with a pre-existing payment method ID skipped this logic, causing unexpected 3DS challenges for MOTO payments made with saved cards. Android: In PaymentMethodCreateParamsFactory.createCardStripeIntentParams, detect the uk_ key prefix and set moto=true on PaymentMethodOptionsParams.Card when a paymentMethodId is provided. iOS: In PaymentMethodFactory.createCardPaymentMethodOptions, set moto=true via STPConfirmCardOptions.additionalAPIParameters when the key starts with uk_. Also apply createOptions in the confirmSetupIntent paymentMethodId branch, which previously skipped it entirely. Committed-By-Agent: goose
Covers all four affected paths: - Android confirmPayment (PaymentIntent) + saved paymentMethodId - Android confirmSetupIntent (SetupIntent) + saved paymentMethodId - iOS createCardPaymentMethodOptions with uk_ key (used by both confirmPayment and confirmSetupIntent) Each path is tested for both uk_ (moto=true expected) and pk_ (no moto) keys, with and without a CVC. Committed-By-Agent: goose
…remove internal property access in test STPSetupIntentConfirmParams does not expose a paymentMethodOptions property, so the moto injection for iOS confirmSetupIntent with a saved payment method ID is not possible via this API. Revert that part of the fix and update the changelog to reflect accurate platform/intent coverage. Also remove access to ConfirmSetupIntentParams.paymentMethodId in the Android unit test, which is an internal property not accessible from outside the stripe-android module. Committed-By-Agent: goose
ConfirmSetupIntentParams.paymentMethodId is declared as
`@get:JvmSynthetic internal val` in the primary constructor.
Using it as a named argument from outside the module causes a
compile error even though the constructor itself is accessible
(via @SuppressLint("RestrictedApi") for the @RestrictTo lint check).
Switch to create().copy(paymentMethodOptions = ...) instead:
- create(paymentMethodId, clientSecret) is the public factory method
- copy() is @RestrictTo but already covered by the function-level
@SuppressLint("RestrictedApi")
- paymentMethodOptions in copy() is a regular function parameter,
not an internal property, so named argument use is unrestricted
Committed-By-Agent: goose
STPSetupIntentConfirmParams does not have a paymentMethodOptions property. Instead, it conforms to STPFormEncodable and exposes additionalAPIParameters, which are merged into the API request body. Use this to inject payment_method_options[card][moto]=true for user-key integrations. Committed-By-Agent: goose
…ntent The moto flag only applies to card payments. Add a paymentMethodType == .card guard so non-card types (iDEAL, SEPADebit, USBankAccount, etc.) do not receive spurious card payment method options in the API request. Committed-By-Agent: goose
…set paymentMethodOptions The copy() function on ConfirmSetupIntentParams does not include paymentMethodOptions as a named parameter, causing a compile error. Use the @RestrictTo primary constructor directly instead. Committed-By-Agent: goose
…ParamMap()
PaymentMethodOptionsParams.toParamMap() returns a nested map keyed
by payment method type code, e.g. {"card": {"moto": true, "cvc": "123"}}.
The assertions were incorrectly reading top-level keys; fix them to
read from the nested card params map.
Committed-By-Agent: goose
…int rule Committed-By-Agent: goose
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.
Minion run
Summary
When the SDK is initialized with a user key (
uk_),moto=truewas only injected into card confirmation params when creating a new payment method inline. Confirming with a pre-existingpaymentMethodId(saved card) bypassed the injection entirely on both Android and iOS, causing 3DS challenges to surface unexpectedly for MOTO payments.Motivation
Android:
PaymentMethodCreateParamsFactory.createCardStripeIntentParamsbranches onpaymentMethodId != null. In that branch it builtPaymentMethodOptionsParams.Cardwith only a CVC (if present), never settingmoto. The Android SDK'smaybeForDashboardauto-injection only fires whenpaymentMethodCreateParams != null, so it is never reached when a pre-existing ID is used. Additionally, theconfirmSetupIntentpath calledConfirmSetupIntentParams.create(paymentMethodId, clientSecret)— a public overload with nopaymentMethodOptionsparameter — silently discarding the options that were computed.iOS:
PaymentMethodFactory.createCardPaymentMethodOptions()only set CVC. AftercreateOptions()returns,StripeSdkImpl.confirmSetupIntentapplied options only in the inline-create branch; thepaymentMethodIdbranch skippedcreateOptionsentirely.Changes:
PaymentMethodCreateParamsFactory.kt: AcceptpublishableKeyin the constructor. WhenpaymentMethodId != nulland the key starts withuk_, setmoto = trueonPaymentMethodOptionsParams.Card. For theconfirmSetupIntentpath, use the@RestrictToprimary constructor ofConfirmSetupIntentParamsdirectly sopaymentMethodOptionsis passed through.StripeSdkModule.kt: PasspublishableKeyto all threePaymentMethodCreateParamsFactoryinstantiations.PaymentMethodFactory.swift: AcceptpublishableKeyininit. IncreateCardPaymentMethodOptions(), setcardOptions.additionalAPIParameters["moto"] = truewhen the key starts withuk_.StripeSdkImpl.swift: PassSTPAPIClient.shared.publishableKeyto all threePaymentMethodFactory.initcalls. InconfirmSetupIntent, callfactory.createOptionsin thepaymentMethodIdbranch, which previously skipped it.Testing
Documentation
Select one: