Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify logging config between Stripe and Stripe 3DS2 #1666

Merged
merged 1 commit into from
Oct 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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