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

Add feature flag and LinkConsumerIncentive struct #9605

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
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 @@ -45,6 +45,7 @@ data class ElementsSession(
val linkMode: LinkMode?,
val linkFlags: Map<String, Boolean>,
val disableLinkSignup: Boolean,
val linkConsumerIncentive: LinkConsumerIncentive?,
) : StripeModel

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.stripe.android.model.parsers
import com.stripe.android.core.model.StripeJsonUtils
import com.stripe.android.core.model.parsers.ModelJsonParser
import com.stripe.android.core.model.parsers.ModelJsonParser.Companion.jsonArrayToList
import com.stripe.android.core.utils.FeatureFlags
import com.stripe.android.model.DeferredIntentParams
import com.stripe.android.model.ElementsSession
import com.stripe.android.model.ElementsSessionParams
Expand Down Expand Up @@ -148,12 +149,15 @@ internal class ElementsSessionJsonParser(
parseLinkFlags(linkSettingsJson)
} ?: emptyMap()

val linkConsumerIncentive = json?.let { LinkConsumerIncentiveJsonParser.parse(it) }

return ElementsSession.LinkSettings(
linkFundingSources = jsonArrayToList(linkFundingSources),
linkPassthroughModeEnabled = linkPassthroughModeEnabled,
linkMode = linkMode,
linkFlags = linkFlags,
disableLinkSignup = disableLinkSignup,
linkConsumerIncentive = linkConsumerIncentive.takeIf { FeatureFlags.instantDebitsIncentives.isEnabled },
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,82 @@ internal object ElementsSessionFixtures {
""".trimIndent()
)

val EXPANDED_PAYMENT_INTENT_WITH_LINK_INCENTIVE_JSON = JSONObject(
"""
{
"business_name": "Mybusiness",
"link_settings": {
"link_bank_enabled": false,
"link_bank_onboarding_enabled": false,
"link_consumer_incentive": {
campaign: "bankaccountsignup",
incentive_params: {
amount_flat: 500,
amount_percent: null,
currency: "usd",
payment_method: "link_instant_debits"
}
}
},
"merchant_country": "US",
"payment_method_preference": {
"object": "payment_method_preference",
"country_code": "US",
"ordered_payment_method_types": [
"card",
"ideal",
"sepa_debit",
"bancontact",
"sofort"
],
"payment_intent": {
"id": "pi_3JTDhYIyGgrkZxL71IDUGKps",
"object": "payment_intent",
"amount": 973,
"canceled_at": null,
"cancellation_reason": null,
"capture_method": "automatic",
"client_secret": "pi_3JTDhYIyGgrkZxL71IDUGKps_secret_aWuzwD4JvF1HM8XJTdUsXG6Za",
"confirmation_method": "automatic",
"created": 1630103948,
"currency": "eur",
"description": null,
"last_payment_error": null,
"livemode": false,
"next_action": null,
"payment_method": null,
"payment_method_types": [
"bancontact",
"card",
"sepa_debit",
"sofort",
"ideal"
],
"receipt_email": null,
"setup_future_usage": null,
"shipping": {
"address": {
"city": "San Francisco",
"country": "US",
"line1": "510 Townsend St",
"line2": null,
"postal_code": "94102",
"state": "California"
},
"carrier": null,
"name": "Bruno",
"phone": null,
"tracking_number": null
},
"source": null,
"status": "requires_payment_method"
},
"type": "payment_intent"
}
}
""".trimIndent()
)

val EXPANDED_SETUP_INTENT_JSON = JSONObject(
"""
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,31 @@ package com.stripe.android.model.parsers

import com.google.common.truth.Truth.assertThat
import com.stripe.android.core.model.parsers.ModelJsonParser
import com.stripe.android.core.utils.FeatureFlags
import com.stripe.android.model.CardBrand
import com.stripe.android.model.DeferredIntentParams
import com.stripe.android.model.ElementsSession
import com.stripe.android.model.ElementsSessionFixtures
import com.stripe.android.model.ElementsSessionFixtures.createPaymentIntentWithCustomerSession
import com.stripe.android.model.ElementsSessionParams
import com.stripe.android.model.LinkConsumerIncentive
import com.stripe.android.model.PaymentIntent
import com.stripe.android.model.PaymentMethod
import com.stripe.android.model.SetupIntent
import com.stripe.android.model.StripeIntent
import com.stripe.android.testing.FeatureFlagTestRule
import org.json.JSONObject
import org.junit.Rule
import org.junit.Test

class ElementsSessionJsonParserTest {

@get:Rule
val incentivesFeatureFlagRule = FeatureFlagTestRule(
featureFlag = FeatureFlags.instantDebitsIncentives,
isEnabled = false,
)

@Test
fun parsePaymentIntent_shouldCreateObjectWithOrderedPaymentMethods() {
val elementsSession = ElementsSessionJsonParser(
Expand Down Expand Up @@ -702,6 +712,50 @@ class ElementsSessionJsonParserTest {
assertThat(elementsSession?.stripeIntent?.unactivatedPaymentMethods).containsExactly("au_becs_debit")
}

@Test
fun `Parses Link consumer incentives if feature flag is enabled`() {
incentivesFeatureFlagRule.setEnabled(true)

val elementsSession = ElementsSessionJsonParser(
ElementsSessionParams.PaymentIntentType(
clientSecret = "secret",
externalPaymentMethods = emptyList(),
),
isLiveMode = true,
).parse(
ElementsSessionFixtures.EXPANDED_PAYMENT_INTENT_WITH_LINK_INCENTIVE_JSON
)

assertThat(elementsSession?.linkSettings?.linkConsumerIncentive).isEqualTo(
LinkConsumerIncentive(
campaign = "bankaccountsignup",
incentiveParams = LinkConsumerIncentive.IncentiveParams(
amountFlat = 500,
amountPercent = null,
currency = "usd",
paymentMethod = "link_instant_debits",
)
)
)
}

@Test
fun `Does not parse Link consumer incentives if feature flag is disabled`() {
incentivesFeatureFlagRule.setEnabled(false)

val elementsSession = ElementsSessionJsonParser(
ElementsSessionParams.PaymentIntentType(
clientSecret = "secret",
externalPaymentMethods = emptyList(),
),
isLiveMode = true,
).parse(
ElementsSessionFixtures.EXPANDED_PAYMENT_INTENT_WITH_LINK_INCENTIVE_JSON
)

assertThat(elementsSession?.linkSettings?.linkConsumerIncentive).isNull()
}

private fun allowRedisplayTest(
rawAllowRedisplayValue: String?,
allowRedisplay: PaymentMethod.AllowRedisplay?,
Expand Down
36 changes: 36 additions & 0 deletions payments-model/api/payments-model.api
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,42 @@ public final class com/stripe/android/model/ConsumerSessionSignup$Creator : andr
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/stripe/android/model/LinkConsumerIncentive$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/model/LinkConsumerIncentive;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lcom/stripe/android/model/LinkConsumerIncentive;
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/stripe/android/model/LinkConsumerIncentive$IncentiveParams : com/stripe/android/core/model/StripeModel {
public static final field CREATOR Landroid/os/Parcelable$Creator;
public fun <init> (Ljava/lang/Long;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;)V
public final fun component1 ()Ljava/lang/Long;
public final fun component2 ()Ljava/lang/Float;
public final fun component3 ()Ljava/lang/String;
public final fun component4 ()Ljava/lang/String;
public final fun copy (Ljava/lang/Long;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;)Lcom/stripe/android/model/LinkConsumerIncentive$IncentiveParams;
public static synthetic fun copy$default (Lcom/stripe/android/model/LinkConsumerIncentive$IncentiveParams;Ljava/lang/Long;Ljava/lang/Float;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/stripe/android/model/LinkConsumerIncentive$IncentiveParams;
public final fun describeContents ()I
public fun equals (Ljava/lang/Object;)Z
public final fun getAmountFlat ()Ljava/lang/Long;
public final fun getAmountPercent ()Ljava/lang/Float;
public final fun getCurrency ()Ljava/lang/String;
public final fun getPaymentMethod ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
public final fun writeToParcel (Landroid/os/Parcel;I)V
}

public final class com/stripe/android/model/LinkConsumerIncentive$IncentiveParams$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/model/LinkConsumerIncentive$IncentiveParams;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lcom/stripe/android/model/LinkConsumerIncentive$IncentiveParams;
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/stripe/android/model/SharePaymentDetails$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/model/SharePaymentDetails;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.stripe.android.model

import androidx.annotation.RestrictTo
import com.stripe.android.core.model.StripeModel
import kotlinx.parcelize.Parcelize

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Parcelize
data class LinkConsumerIncentive(
val campaign: String,
val incentiveParams: IncentiveParams,
) : StripeModel {

@Parcelize
data class IncentiveParams(
val amountFlat: Long?,
val amountPercent: Float?,
val currency: String?,
val paymentMethod: String,
) : StripeModel
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.stripe.android.model.parsers

import androidx.annotation.RestrictTo
import com.stripe.android.core.model.StripeJsonUtils.optDouble
import com.stripe.android.core.model.StripeJsonUtils.optLong
import com.stripe.android.core.model.StripeJsonUtils.optString
import com.stripe.android.core.model.parsers.ModelJsonParser
import com.stripe.android.model.LinkConsumerIncentive
import org.json.JSONObject

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object LinkConsumerIncentiveJsonParser : ModelJsonParser<LinkConsumerIncentive> {

override fun parse(json: JSONObject): LinkConsumerIncentive? {
return json.optJSONObject("link_consumer_incentive")?.let { incentive ->
val incentiveParams = buildIncentiveParams(
json = incentive.getJSONObject("incentive_params"),
)

LinkConsumerIncentive(
campaign = incentive.optString("campaign"),
incentiveParams = incentiveParams,
)
}
}

private fun buildIncentiveParams(json: JSONObject): LinkConsumerIncentive.IncentiveParams {
val amountFlat = optLong(json, "amount_flat")
val amountPercent = optDouble(json, "amount_percent")?.toFloat()
val currency = optString(json, "currency")
val paymentMethod = optString(json, "payment_method")

return LinkConsumerIncentive.IncentiveParams(
amountFlat = amountFlat,
amountPercent = amountPercent,
currency = currency,
paymentMethod = paymentMethod.orEmpty(),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ internal class PlaygroundSettings private constructor(
CardBrandAcceptanceSettingsDefinition,
FeatureFlagSettingsDefinition(FeatureFlags.useNewUpdateCardScreen),
FeatureFlagSettingsDefinition(FeatureFlags.instantDebitsDeferredIntent),
FeatureFlagSettingsDefinition(FeatureFlags.instantDebitsIncentives),
)

private val nonUiSettingDefinitions: List<PlaygroundSettingDefinition<*>> = listOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ object StripeJsonUtils {
}
}

@JvmSynthetic
fun optDouble(
jsonObject: JSONObject,
@Size(min = 1) fieldName: String
): Double? {
return jsonObject.optDouble(fieldName).takeUnless { it.isNaN() }
}

/**
* Calls through to [JSONObject.optString] while safely
* converting the raw string "null" and the empty string to `null`. Will not throw
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ object FeatureFlags {
val nativeLinkEnabled = FeatureFlag("Native Link")
val instantDebitsDeferredIntent = FeatureFlag("IBP Deferred")
val useNewUpdateCardScreen = FeatureFlag("Enable new update card screen")
val instantDebitsIncentives = FeatureFlag("IBP Incentives")
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Expand Down
Loading