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 CustomerSheet playground UI #8524

Merged
merged 2 commits into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.stripe.android.customersheet.CustomerSheet
import com.stripe.android.customersheet.CustomerSheetResult
import com.stripe.android.customersheet.ExperimentalCustomerSheetApi
import com.stripe.android.customersheet.rememberCustomerSheet
import com.stripe.android.model.PaymentMethod
Expand All @@ -55,9 +57,12 @@ import com.stripe.android.paymentsheet.example.playground.settings.SettingsUi
import com.stripe.android.paymentsheet.example.samples.ui.shared.BuyButton
import com.stripe.android.paymentsheet.example.samples.ui.shared.CHECKOUT_TEST_TAG
import com.stripe.android.paymentsheet.example.samples.ui.shared.PaymentMethodSelector
import com.stripe.android.paymentsheet.model.PaymentOption
import com.stripe.android.paymentsheet.rememberPaymentSheet
import com.stripe.android.paymentsheet.rememberPaymentSheetFlowController
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.withContext

internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPaymentMethodConfirmHandler {
companion object {
Expand All @@ -76,6 +81,7 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
)
}

@OptIn(ExperimentalCustomerSheetApi::class)
@Suppress("LongMethod")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -101,6 +107,14 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay

val playgroundState by viewModel.state.collectAsState()

val customerSheet = playgroundState?.asCustomerState()?.let { customerPlaygroundState ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want this to be unconditionally created like the others. Is that not possible with customer sheet? If not this integration shape is probably wrong :/

rememberCustomerSheet(
configuration = customerPlaygroundState.customerSheetConfiguration(),
customerAdapter = customerPlaygroundState.adapter,
callback = viewModel::onCustomerSheetCallback
)
}

PlaygroundTheme(
content = {
playgroundState?.asPaymentState()?.stripeIntentId?.let { stripeIntentId ->
Expand Down Expand Up @@ -130,6 +144,7 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
playgroundState = playgroundState,
paymentSheet = paymentSheet,
flowController = flowController,
customerSheet = customerSheet,
addressLauncher = addressLauncher,
)
}
Expand Down Expand Up @@ -208,11 +223,13 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
}
}

@OptIn(ExperimentalCustomerSheetApi::class)
@Composable
private fun PlaygroundStateUi(
playgroundState: PlaygroundState?,
paymentSheet: PaymentSheet,
flowController: PaymentSheet.FlowController,
customerSheet: CustomerSheet?,
addressLauncher: AddressLauncher
) {
if (playgroundState == null) {
Expand Down Expand Up @@ -243,15 +260,8 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
else -> Unit
}
}
is PlaygroundState.Customer -> {
val customerSheetState by viewModel.customerSheetState.collectAsState()

customerSheetState?.let { state ->
CustomerSheetUi(
customerSheetState = state,
playgroundState = playgroundState
)
}
is PlaygroundState.Customer -> customerSheet?.run {
CustomerSheetUi(customerSheet = this)
}
}
}
Expand Down Expand Up @@ -318,29 +328,41 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
@OptIn(ExperimentalCustomerSheetApi::class)
@Composable
fun CustomerSheetUi(
playgroundState: PlaygroundState.Customer,
customerSheetState: CustomerSheetState,
customerSheet: CustomerSheet,
) {
val customerSheet = rememberCustomerSheet(
configuration = playgroundState.customerSheetConfiguration(),
customerAdapter = playgroundState.adapter,
callback = viewModel::onCustomerSheetCallback
)
val customerSheetState by viewModel.customerSheetState.collectAsState()

customerSheetState?.let { state ->
LaunchedEffect(state) {
if (state.shouldFetchPaymentOption) {
fetchOption(customerSheet).onSuccess { option ->
viewModel.customerSheetState.emit(
CustomerSheetState(
selectedPaymentOption = option,
shouldFetchPaymentOption = false
)
)
}.onFailure { exception ->
viewModel.status.emit(
StatusMessage(
message = "Failed to retrieve payment options:\n${exception.message}"
)
)
}
}
}

LaunchedEffect(customerSheet) {
viewModel.fetchOption(customerSheet)
}
if (state.shouldFetchPaymentOption) {
return
}

if (customerSheetState.shouldFetchPaymentOption) {
return
PaymentMethodSelector(
isEnabled = true,
paymentMethodLabel = customerSheetState.paymentMethodLabel(),
paymentMethodPainter = customerSheetState.paymentMethodPainter(),
onClick = customerSheet::present
)
}

PaymentMethodSelector(
isEnabled = true,
paymentMethodLabel = customerSheetState.paymentMethodLabel(),
paymentMethodPainter = customerSheetState.paymentMethodPainter(),
onClick = customerSheet::present
)
}

@Composable
Expand Down Expand Up @@ -419,6 +441,17 @@ internal class PaymentSheetPlaygroundActivity : AppCompatActivity(), ExternalPay
}
}

@OptIn(ExperimentalCustomerSheetApi::class)
private suspend fun fetchOption(
customerSheet: CustomerSheet
): Result<PaymentOption?> = withContext(Dispatchers.IO) {
when (val result = customerSheet.retrievePaymentOptionSelection()) {
is CustomerSheetResult.Selected -> Result.success(result.selection?.paymentOption)
is CustomerSheetResult.Failed -> Result.failure(result.exception)
is CustomerSheetResult.Canceled -> Result.success(null)
}
}

override fun confirmExternalPaymentMethod(
externalPaymentMethodType: String,
billingDetails: PaymentMethod.BillingDetails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import com.github.kittinunf.result.Result
import com.stripe.android.PaymentConfiguration
import com.stripe.android.customersheet.CustomerAdapter
import com.stripe.android.customersheet.CustomerEphemeralKey
import com.stripe.android.customersheet.CustomerSheet
import com.stripe.android.customersheet.CustomerSheetResult
import com.stripe.android.customersheet.ExperimentalCustomerSheetApi
import com.stripe.android.model.PaymentMethod
Expand Down Expand Up @@ -304,42 +303,6 @@ internal class PaymentSheetPlaygroundViewModel(
status.value = StatusMessage(statusMessage)
}

@OptIn(ExperimentalCustomerSheetApi::class)
fun fetchOption(customerSheet: CustomerSheet) {
viewModelScope.launch(Dispatchers.IO) {
when (val result = customerSheet.retrievePaymentOptionSelection()) {
is CustomerSheetResult.Selected -> {
customerSheetState.update { existingState ->
existingState?.copy(
selectedPaymentOption = result.selection?.paymentOption,
shouldFetchPaymentOption = false,
)
}
}
is CustomerSheetResult.Failed -> {
customerSheetState.update { existingState ->
existingState?.copy(
shouldFetchPaymentOption = false,
)
}

status.emit(
StatusMessage(
message = "Failed to retrieve payment options:\n${result.exception.message}"
)
)
}
is CustomerSheetResult.Canceled -> {
customerSheetState.update { existingState ->
existingState?.copy(
shouldFetchPaymentOption = false,
)
}
}
}
}
}

@OptIn(ExperimentalCustomerSheetApi::class)
fun onCustomerSheetCallback(result: CustomerSheetResult) {
val statusMessage = when (result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ internal sealed interface PlaygroundState {
return this as? Payment
}

fun asCustomerState(): Customer? {
return this as? Customer
}

companion object {
fun CheckoutResponse.asPlaygroundState(
snapshot: PlaygroundSettings.Snapshot,
Expand Down