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

Draft CustomPaymentMethod API #10379

Merged
merged 1 commit into from
Mar 17, 2025
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
32 changes: 32 additions & 0 deletions paymentsheet/api/paymentsheet.api
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,30 @@ public final class com/stripe/android/lpmfoundations/paymentmethod/link/LinkInli
public synthetic fun newArray (I)[Ljava/lang/Object;
}

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

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

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

public final class com/stripe/android/paymentelement/EmbeddedPaymentElement$Configuration$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/paymentelement/EmbeddedPaymentElement$Configuration;
Expand Down Expand Up @@ -1333,6 +1357,14 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$Configuration$Cr
public synthetic fun newArray (I)[Ljava/lang/Object;
}

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

public final class com/stripe/android/paymentsheet/PaymentSheet$CustomerAccessType$CustomerSession$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerAccessType$CustomerSession;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ internal object ConfigurationDefaults {
val externalPaymentMethods: List<String> = emptyList()
val paymentMethodLayout: PaymentMethodLayout = PaymentMethodLayout.Automatic
val cardBrandAcceptance: PaymentSheet.CardBrandAcceptance = PaymentSheet.CardBrandAcceptance.All
val customPaymentMethods: List<PaymentSheet.CustomPaymentMethod> = emptyList()

const val embeddedViewDisplaysMandateText: Boolean = true
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.stripe.android.paymentelement

import androidx.annotation.RestrictTo
import com.stripe.android.model.PaymentMethod
import com.stripe.android.paymentsheet.PaymentSheet

/**
* Handler to be used to confirm payment with a custom payment method.
*
* To learn more about custom payment methods, see "docs_url"
*/
@ExperimentalCustomPaymentMethodsApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun interface CustomPaymentMethodConfirmHandler {

/**
* Called when a user confirms payment or setup with a custom payment method.
*
* On completion, this should call [CustomPaymentMethodResultHandler.onCustomPaymentMethodResult] with the
* result of the custom payment method's confirmation.
*
* @param customPaymentMethod The custom payment method to confirm payment with
* @param billingDetails Any billing details you've configured Payment Element to collect
*/
fun confirmCustomPaymentMethod(
customPaymentMethod: PaymentSheet.CustomPaymentMethod,
billingDetails: PaymentMethod.BillingDetails,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.stripe.android.paymentelement

import android.content.Context
import android.os.Parcelable
import androidx.annotation.RestrictTo
import kotlinx.parcelize.Parcelize

/**
* Handler used to respond to custom payment method confirm results.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object CustomPaymentMethodResultHandler {

/**
* Updates the Payment Element UI to reflect the result of confirming a custom payment method.
*
* Should be called when [CustomPaymentMethodConfirmHandler.confirmCustomPaymentMethod] completes.
*/
@JvmStatic
fun onCustomPaymentMethodResult(context: Context, customPaymentMethodResult: CustomPaymentMethodResult) {
error(
"Not implemented! Should not called with context from " +
"${context.packageName} and $customPaymentMethodResult"
)
}
}

/**
* The result of an attempt to confirm a custom payment method.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
sealed class CustomPaymentMethodResult : Parcelable {
@Parcelize
internal data object Completed : CustomPaymentMethodResult()

@Parcelize
internal data object Canceled : CustomPaymentMethodResult()

@Parcelize
internal data class Failed(
val displayMessage: String?,
) : CustomPaymentMethodResult()

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
companion object {

/**
* The customer successfully completed the payment or setup.
*/
@JvmStatic
@ExperimentalCustomPaymentMethodsApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun completed(): CustomPaymentMethodResult {
return Completed
}

/**
* The customer canceled the payment or setup attempt.
*/
@JvmStatic
@ExperimentalCustomPaymentMethodsApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun canceled(): CustomPaymentMethodResult {
return Canceled
}

/**
* The payment or setup attempt failed.
*
* @param displayMessage Message to display to the user on failure. If null, will display Stripe's default
* error message.
*/
@JvmStatic
@JvmOverloads
@ExperimentalCustomPaymentMethodsApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun failed(displayMessage: String? = null): CustomPaymentMethodResult {
return Failed(displayMessage)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,25 @@ class EmbeddedPaymentElement @Inject internal constructor(
internal var externalPaymentMethodConfirmHandler: ExternalPaymentMethodConfirmHandler? = null
private set

@OptIn(ExperimentalCustomPaymentMethodsApi::class)
internal var customPaymentMethodConfirmHandler: CustomPaymentMethodConfirmHandler? = null
private set

/**
* Called when a user confirms payment for an external payment method.
*/
fun externalPaymentMethodConfirmHandler(handler: ExternalPaymentMethodConfirmHandler) = apply {
this.externalPaymentMethodConfirmHandler = handler
}

/**
* Called when a user confirms payment for a custom payment method.
*/
@ExperimentalCustomPaymentMethodsApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun customPaymentMethodConfirmHandler(handler: CustomPaymentMethodConfirmHandler) = apply {
this.customPaymentMethodConfirmHandler = handler
}
}

/** Configuration for [EmbeddedPaymentElement] **/
Expand All @@ -144,6 +157,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
internal val paymentMethodOrder: List<String>,
internal val externalPaymentMethods: List<String>,
internal val cardBrandAcceptance: PaymentSheet.CardBrandAcceptance,
internal val customPaymentMethods: List<PaymentSheet.CustomPaymentMethod>,
internal val embeddedViewDisplaysMandateText: Boolean,
) : Parcelable {
@Suppress("TooManyFunctions")
Expand Down Expand Up @@ -174,6 +188,8 @@ class EmbeddedPaymentElement @Inject internal constructor(
private var cardBrandAcceptance: PaymentSheet.CardBrandAcceptance =
ConfigurationDefaults.cardBrandAcceptance
private var embeddedViewDisplaysMandateText: Boolean = ConfigurationDefaults.embeddedViewDisplaysMandateText
private var customPaymentMethods: List<PaymentSheet.CustomPaymentMethod> =
ConfigurationDefaults.customPaymentMethods

/**
* If set, the customer can select a previously saved payment method.
Expand Down Expand Up @@ -330,6 +346,19 @@ class EmbeddedPaymentElement @Inject internal constructor(
this.cardBrandAcceptance = cardBrandAcceptance
}

/**
* Configuration related to custom payment methods.
*
* If set, Embedded Payment Element will display the defined list of custom payment methods in the UI.
*/
@ExperimentalCustomPaymentMethodsApi
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
fun customPaymentMethods(
customPaymentMethods: List<PaymentSheet.CustomPaymentMethod>,
) = apply {
this.customPaymentMethods = customPaymentMethods
}

/**
* Controls whether the view displays mandate text at the bottom for payment methods that require it.
*
Expand Down Expand Up @@ -360,6 +389,7 @@ class EmbeddedPaymentElement @Inject internal constructor(
paymentMethodOrder = paymentMethodOrder,
externalPaymentMethods = externalPaymentMethods,
cardBrandAcceptance = cardBrandAcceptance,
customPaymentMethods = customPaymentMethods,
embeddedViewDisplaysMandateText = embeddedViewDisplaysMandateText,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ fun rememberEmbeddedPaymentElement(
}

val callbacks = remember(builder) {
@OptIn(ExperimentalCustomPaymentMethodsApi::class)
PaymentElementCallbacks(
createIntentCallback = builder.createIntentCallback,
customPaymentMethodConfirmHandler = builder.customPaymentMethodConfirmHandler,
externalPaymentMethodConfirmHandler = builder.externalPaymentMethodConfirmHandler,
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.stripe.android.paymentelement

import androidx.annotation.RestrictTo

@RequiresOptIn(
level = RequiresOptIn.Level.ERROR,
message = "Custom payment methods support is beta. It may be changed in the future without notice."
)
@Retention(AnnotationRetention.BINARY)
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
annotation class ExperimentalCustomPaymentMethodsApi
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.stripe.android.paymentelement.callbacks

import com.stripe.android.paymentelement.CustomPaymentMethodConfirmHandler
import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
import com.stripe.android.paymentsheet.CreateIntentCallback
import com.stripe.android.paymentsheet.ExternalPaymentMethodConfirmHandler

@OptIn(ExperimentalCustomPaymentMethodsApi::class)
internal class PaymentElementCallbacks(
val createIntentCallback: CreateIntentCallback?,
val customPaymentMethodConfirmHandler: CustomPaymentMethodConfirmHandler?,
val externalPaymentMethodConfirmHandler: ExternalPaymentMethodConfirmHandler?,
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import com.stripe.android.common.ui.UpdateCallbacks
import com.stripe.android.paymentelement.CustomPaymentMethodConfirmHandler
import com.stripe.android.paymentelement.ExperimentalCustomPaymentMethodsApi
import com.stripe.android.paymentelement.callbacks.PaymentElementCallbacks
import com.stripe.android.paymentsheet.flowcontroller.FlowControllerFactory
import com.stripe.android.utils.rememberActivity
Expand All @@ -25,9 +27,11 @@ fun rememberPaymentSheetFlowController(
paymentOptionCallback: PaymentOptionCallback,
paymentResultCallback: PaymentSheetResultCallback,
): PaymentSheet.FlowController {
@OptIn(ExperimentalCustomPaymentMethodsApi::class)
return internalRememberPaymentSheetFlowController(
paymentOptionCallback = paymentOptionCallback,
paymentResultCallback = paymentResultCallback,
customPaymentMethodConfirmHandler = null,
createIntentCallback = null,
externalPaymentMethodConfirmHandler = null,
)
Expand All @@ -50,9 +54,11 @@ fun rememberPaymentSheetFlowController(
paymentOptionCallback: PaymentOptionCallback,
paymentResultCallback: PaymentSheetResultCallback,
): PaymentSheet.FlowController {
@OptIn(ExperimentalCustomPaymentMethodsApi::class)
return internalRememberPaymentSheetFlowController(
paymentOptionCallback = paymentOptionCallback,
paymentResultCallback = paymentResultCallback,
customPaymentMethodConfirmHandler = null,
createIntentCallback = createIntentCallback,
externalPaymentMethodConfirmHandler = null,
)
Expand All @@ -79,9 +85,11 @@ fun rememberPaymentSheetFlowController(
paymentOptionCallback: PaymentOptionCallback,
paymentResultCallback: PaymentSheetResultCallback,
): PaymentSheet.FlowController {
@OptIn(ExperimentalCustomPaymentMethodsApi::class)
return internalRememberPaymentSheetFlowController(
paymentOptionCallback = paymentOptionCallback,
paymentResultCallback = paymentResultCallback,
customPaymentMethodConfirmHandler = null,
createIntentCallback = createIntentCallback,
externalPaymentMethodConfirmHandler = externalPaymentMethodConfirmHandler
)
Expand Down Expand Up @@ -140,9 +148,11 @@ private fun internalRememberPaymentSheetFlowController(
}

@Composable
@OptIn(ExperimentalCustomPaymentMethodsApi::class)
internal fun internalRememberPaymentSheetFlowController(
createIntentCallback: CreateIntentCallback?,
externalPaymentMethodConfirmHandler: ExternalPaymentMethodConfirmHandler?,
customPaymentMethodConfirmHandler: CustomPaymentMethodConfirmHandler?,
paymentOptionCallback: PaymentOptionCallback,
paymentResultCallback: PaymentSheetResultCallback,
): PaymentSheet.FlowController {
Expand All @@ -153,6 +163,7 @@ internal fun internalRememberPaymentSheetFlowController(
val callbacks = remember(createIntentCallback, externalPaymentMethodConfirmHandler) {
PaymentElementCallbacks(
createIntentCallback = createIntentCallback,
customPaymentMethodConfirmHandler = customPaymentMethodConfirmHandler,
externalPaymentMethodConfirmHandler = externalPaymentMethodConfirmHandler,
)
}
Expand Down
Loading
Loading