Skip to content

Commit

Permalink
Unify logging config between Stripe and Stripe 3DS2 (#1666)
Browse files Browse the repository at this point in the history
Use the value for `enableLogging` passed in the `Stripe` constructor
to determine whether to enable logging in the Stripe 3DS2 SDK.
  • Loading branch information
mshafrir-stripe authored Oct 7, 2019
1 parent 3f55643 commit d1f9dd6
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,12 @@ class PaymentAuthActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_payment_auth)

val uiCustomization = PaymentAuthConfig.Stripe3ds2UiCustomization.Builder().build()
val uiCustomization =
PaymentAuthConfig.Stripe3ds2UiCustomization.Builder().build()
PaymentAuthConfig.init(PaymentAuthConfig.Builder()
.set3ds2Config(PaymentAuthConfig.Stripe3ds2Config.Builder()
.setTimeout(6)
.setUiCustomization(uiCustomization)
.setEnableLogging(true)
.build())
.build())

Expand All @@ -62,11 +62,10 @@ class PaymentAuthActivity : AppCompatActivity() {

backendApi = RetrofitFactory.instance.create(BackendApi::class.java)
val publishableKey = PaymentConfiguration.getInstance(this).publishableKey
stripe = if (stripeAccountId != null) {
Stripe(this, publishableKey, stripeAccountId)
} else {
Stripe(this, publishableKey)
}
stripe = Stripe(this, publishableKey,
stripeAccountId = stripeAccountId,
enableLogging = true
)

buy_3ds1_button.setOnClickListener {
createPaymentIntent(stripeAccountId, AuthType.ThreeDS1)
Expand Down Expand Up @@ -103,10 +102,10 @@ class PaymentAuthActivity : AppCompatActivity() {
progress_bar.visibility = View.VISIBLE
statusTextView.append("\n\nPayment authentication completed, getting result")

val isPaymentResult = stripe.onPaymentResult(requestCode, data, AuthResultListener(this))

val isPaymentResult =
stripe.onPaymentResult(requestCode, data, AuthResultListener(this))
if (!isPaymentResult) {
val isSetupResult = stripe.onSetupResult(requestCode, data, SetupAuthResultListener(this))
stripe.onSetupResult(requestCode, data, SetupAuthResultListener(this))
}
}

Expand All @@ -130,7 +129,9 @@ class PaymentAuthActivity : AppCompatActivity() {
authType: AuthType
) {
compositeSubscription.add(
backendApi.createPaymentIntent(createPaymentIntentParams(stripeAccountId))
backendApi.createPaymentIntent(
createPaymentIntentParams(stripeAccountId).toMutableMap()
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
Expand Down Expand Up @@ -196,16 +197,17 @@ class PaymentAuthActivity : AppCompatActivity() {
progress_bar.visibility = View.INVISIBLE
}

private fun createPaymentIntentParams(stripeAccountId: String?): HashMap<String, Any> {
val params = hashMapOf(
private fun createPaymentIntentParams(stripeAccountId: String?): Map<String, Any> {
return mapOf(
"payment_method_types[]" to "card",
"amount" to 1000,
"currency" to "usd"
)
if (stripeAccountId != null) {
params["stripe_account"] = stripeAccountId
}
return params
.plus(
stripeAccountId?.let {
mapOf("stripe_account" to it)
}.orEmpty()
)
}

private class AuthResultListener constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ interface BackendApi {

@FormUrlEncoded
@POST("create_intent")
fun createPaymentIntent(@FieldMap params: HashMap<String, Any>): Observable<ResponseBody>
fun createPaymentIntent(@FieldMap params: MutableMap<String, Any>): Observable<ResponseBody>

@FormUrlEncoded
@POST("create_setup_intent")
Expand Down
16 changes: 0 additions & 16 deletions stripe/src/main/java/com/stripe/android/PaymentAuthConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,10 @@ public static final class Stripe3ds2Config {

final int timeout;
@NonNull final Stripe3ds2UiCustomization uiCustomization;
final boolean enableLogging;

private Stripe3ds2Config(@NonNull Builder builder) {
timeout = checkValidTimeout(builder.mTimeout);
uiCustomization = Objects.requireNonNull(builder.mUiCustomization);
enableLogging = builder.mEnableLogging;
}

private int checkValidTimeout(int timeout) {
Expand All @@ -93,7 +91,6 @@ public static final class Builder implements ObjectBuilder<Stripe3ds2Config> {
private int mTimeout = DEFAULT_TIMEOUT;
private Stripe3ds2UiCustomization mUiCustomization =
new Stripe3ds2UiCustomization.Builder().build();
private boolean mEnableLogging = false;

@NonNull
public Builder setTimeout(@IntRange(from = 5, to = 99) int timeout) {
Expand All @@ -107,19 +104,6 @@ public Builder setUiCustomization(@NonNull Stripe3ds2UiCustomization uiCustomiza
return this;
}

/**
* Enable logging in the Stripe 3DS2 SDK; disabled by default.
* It is recommended to disable logging in production.
*
* <p>Logs can be accessed from the command line using
* <code>adb logcat -s Stripe3ds2</code>.</p>
*/
@NonNull
public Builder setEnableLogging(boolean enableLogging) {
this.mEnableLogging = enableLogging;
return this;
}

@NonNull
public Stripe3ds2Config build() {
return new Stripe3ds2Config(this);
Expand Down
13 changes: 9 additions & 4 deletions stripe/src/main/java/com/stripe/android/PaymentController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ import java.util.concurrent.TimeUnit
internal open class PaymentController @VisibleForTesting constructor(
context: Context,
private val stripeRepository: StripeRepository,
enableLogging: Boolean = false,
private val messageVersionRegistry: MessageVersionRegistry =
MessageVersionRegistry(),
private val config: PaymentAuthConfig =
PaymentAuthConfig.get(),
private val threeDs2Service: StripeThreeDs2Service =
StripeThreeDs2ServiceImpl(context, StripeSSLSocketFactory(),
config.stripe3ds2Config.enableLogging),
StripeThreeDs2ServiceImpl(context, StripeSSLSocketFactory(), enableLogging),
private val analyticsRequestExecutor: FireAndForgetRequestExecutor =
StripeFireAndForgetRequestExecutor(),
private val analyticsDataFactory: AnalyticsDataFactory =
Expand Down Expand Up @@ -765,9 +765,14 @@ internal open class PaymentController @VisibleForTesting constructor(
.start(PaymentRelayStarter.Data.create(exception))
}

@JvmOverloads
@JvmStatic
fun create(context: Context, stripeRepository: StripeRepository): PaymentController {
return PaymentController(context.applicationContext, stripeRepository)
fun create(
context: Context,
stripeRepository: StripeRepository,
enableLogging: Boolean = false
): PaymentController {
return PaymentController(context.applicationContext, stripeRepository, enableLogging)
}
}
}
11 changes: 8 additions & 3 deletions stripe/src/main/java/com/stripe/android/Stripe.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class Stripe internal constructor(
* @param context Activity or application context
* @param publishableKey the client's publishable key
* @param stripeAccountId optional, the Stripe Connect account id to attach to [Stripe API requests](https://stripe.com/docs/connect/authentication#authentication-via-the-stripe-account-header)
* @param enableLogging enable logging in the Stripe and Stripe 3DS2 SDKs; disabled by default.
* It is recommended to disable logging in production. Logs can be accessed from the command line using
* `adb logcat -s StripeSdk`
*/
@JvmOverloads
constructor(
Expand All @@ -75,19 +78,21 @@ class Stripe internal constructor(
),
StripeNetworkUtils(context.applicationContext),
ApiKeyValidator.get().requireValid(publishableKey),
stripeAccountId
stripeAccountId,
enableLogging
)

private constructor(
context: Context,
stripeRepository: StripeRepository,
stripeNetworkUtils: StripeNetworkUtils,
publishableKey: String,
stripeAccountId: String?
stripeAccountId: String?,
enableLogging: Boolean
) : this(
stripeRepository,
stripeNetworkUtils,
PaymentController.create(context.applicationContext, stripeRepository),
PaymentController.create(context.applicationContext, stripeRepository, enableLogging),
publishableKey,
stripeAccountId
)
Expand Down
38 changes: 20 additions & 18 deletions stripe/src/test/java/com/stripe/android/PaymentControllerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ class PaymentControllerTest {
controller = PaymentController(
ApplicationProvider.getApplicationContext<Context>(),
FakeStripeRepository(),
false,
MessageVersionRegistry(),
CONFIG,
threeDs2Service,
Expand All @@ -116,7 +117,9 @@ class PaymentControllerTest {
@Throws(CertificateException::class)
fun handleNextAction_withMastercardAnd3ds2_shouldStart3ds2ChallengeFlow() {
val paymentIntent = PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2
val dsPublicKey = Stripe3ds2Fingerprint.create(paymentIntent.stripeSdkData!!)
val dsPublicKey = Stripe3ds2Fingerprint.create(
requireNotNull(paymentIntent.stripeSdkData)
)
.directoryServerEncryption
.directoryServerPublicKey
`when`(threeDs2Service.createTransaction(
Expand Down Expand Up @@ -145,7 +148,7 @@ class PaymentControllerTest {

verify<FireAndForgetRequestExecutor>(fireAndForgetRequestExecutor)
.executeAsync(apiRequestArgumentCaptor.capture())
val analyticsParams = apiRequestArgumentCaptor.firstValue.params!!
val analyticsParams = requireNotNull(apiRequestArgumentCaptor.firstValue.params)
assertEquals("stripe_android.3ds2_fingerprint",
analyticsParams[AnalyticsDataFactory.FIELD_EVENT])
assertEquals(PaymentIntentFixtures.PI_REQUIRES_MASTERCARD_3DS2.id,
Expand Down Expand Up @@ -196,7 +199,7 @@ class PaymentControllerTest {
verify<FireAndForgetRequestExecutor>(fireAndForgetRequestExecutor)
.executeAsync(apiRequestArgumentCaptor.capture())
assertEquals("stripe_android.3ds1_sdk",
apiRequestArgumentCaptor.firstValue.params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(apiRequestArgumentCaptor.firstValue.params)[AnalyticsDataFactory.FIELD_EVENT])
}

@Test
Expand All @@ -216,7 +219,7 @@ class PaymentControllerTest {

verify<FireAndForgetRequestExecutor>(fireAndForgetRequestExecutor)
.executeAsync(apiRequestArgumentCaptor.capture())
val analyticsParams = apiRequestArgumentCaptor.firstValue.params!!
val analyticsParams = requireNotNull(apiRequestArgumentCaptor.firstValue.params)
assertEquals("stripe_android.url_redirect_next_action",
analyticsParams[AnalyticsDataFactory.FIELD_EVENT])
assertEquals("pi_1EZlvVCRMbs6FrXfKpq2xMmy",
Expand Down Expand Up @@ -276,9 +279,9 @@ class PaymentControllerTest {
val analyticsRequests = apiRequestArgumentCaptor.allValues

assertEquals("stripe_android.3ds2_challenge_flow_completed",
analyticsRequests[0].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[0].params)[AnalyticsDataFactory.FIELD_EVENT])

val analyticsParamsSecond = analyticsRequests[1].params!!
val analyticsParamsSecond = requireNotNull(analyticsRequests[1].params)
assertEquals("stripe_android.3ds2_challenge_flow_presented",
analyticsParamsSecond[AnalyticsDataFactory.FIELD_EVENT])
assertEquals("oob",
Expand All @@ -298,10 +301,10 @@ class PaymentControllerTest {
val analyticsRequests = apiRequestArgumentCaptor.allValues

assertEquals("stripe_android.3ds2_challenge_flow_timed_out",
analyticsRequests[0].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[0].params)[AnalyticsDataFactory.FIELD_EVENT])

assertEquals("stripe_android.3ds2_challenge_flow_presented",
analyticsRequests[1].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[1].params)[AnalyticsDataFactory.FIELD_EVENT])
}

@Test
Expand All @@ -318,10 +321,10 @@ class PaymentControllerTest {
val analyticsRequests = apiRequestArgumentCaptor.allValues

assertEquals("stripe_android.3ds2_challenge_flow_canceled",
analyticsRequests[0].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[0].params)[AnalyticsDataFactory.FIELD_EVENT])

assertEquals("stripe_android.3ds2_challenge_flow_presented",
analyticsRequests[1].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[1].params)[AnalyticsDataFactory.FIELD_EVENT])
}

@Test
Expand All @@ -342,7 +345,7 @@ class PaymentControllerTest {
.executeAsync(apiRequestArgumentCaptor.capture())
val analyticsRequests = apiRequestArgumentCaptor.allValues

val analyticsParamsFirst = analyticsRequests[0].params!!
val analyticsParamsFirst = requireNotNull(analyticsRequests[0].params)
assertEquals("stripe_android.3ds2_challenge_flow_errored",
analyticsParamsFirst[AnalyticsDataFactory.FIELD_EVENT])

Expand All @@ -353,7 +356,7 @@ class PaymentControllerTest {
assertEquals("Resource not found", errorData["error_message"])

assertEquals("stripe_android.3ds2_challenge_flow_presented",
analyticsRequests[1].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[1].params)[AnalyticsDataFactory.FIELD_EVENT])
}

@Test
Expand All @@ -380,7 +383,7 @@ class PaymentControllerTest {
.executeAsync(apiRequestArgumentCaptor.capture())
val analyticsRequests = apiRequestArgumentCaptor.allValues

val analyticsParamsFirst = analyticsRequests[0].params!!
val analyticsParamsFirst = requireNotNull(analyticsRequests[0].params)
assertEquals("stripe_android.3ds2_challenge_flow_errored",
analyticsParamsFirst[AnalyticsDataFactory.FIELD_EVENT])

Expand All @@ -390,7 +393,7 @@ class PaymentControllerTest {
assertEquals("201", errorData["error_code"])

assertEquals("stripe_android.3ds2_challenge_flow_presented",
analyticsRequests[1].params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequests[1].params)[AnalyticsDataFactory.FIELD_EVENT])
}

@Test
Expand Down Expand Up @@ -488,7 +491,7 @@ class PaymentControllerTest {

verify(fireAndForgetRequestExecutor).executeAsync(apiRequestArgumentCaptor.capture())
val analyticsRequest = apiRequestArgumentCaptor.firstValue
val analyticsParams = analyticsRequest.params!!
val analyticsParams = requireNotNull(analyticsRequest.params)
assertEquals("stripe_android.3ds2_frictionless_flow",
analyticsParams[AnalyticsDataFactory.FIELD_EVENT])
assertEquals("pi_1ExkUeAWhjPjYwPiXph9ouXa",
Expand Down Expand Up @@ -516,7 +519,7 @@ class PaymentControllerTest {
verify(fireAndForgetRequestExecutor).executeAsync(apiRequestArgumentCaptor.capture())
val analyticsRequest = apiRequestArgumentCaptor.firstValue
assertEquals("stripe_android.3ds2_fallback",
analyticsRequest.params!![AnalyticsDataFactory.FIELD_EVENT])
requireNotNull(analyticsRequest.params)[AnalyticsDataFactory.FIELD_EVENT])
}

@Test
Expand All @@ -529,12 +532,11 @@ class PaymentControllerTest {
)
authCallback.onSuccess(Stripe3ds2AuthResultFixtures.ERROR)
verify(paymentRelayStarter).start(relayStarterDataArgumentCaptor.capture())
val exception = relayStarterDataArgumentCaptor.firstValue.exception!!
assertEquals("Error encountered during 3DS2 authentication request. " +
"Code: 302, Detail: null, " +
"Description: Data could not be decrypted by the receiving system due to " +
"technical or other reason., Component: D",
exception.message)
relayStarterDataArgumentCaptor.firstValue.exception?.message)
}

private class FakeStripeRepository : AbsFakeStripeRepository() {
Expand Down

0 comments on commit d1f9dd6

Please sign in to comment.