首先要赞扬上面@lincollincol提供的答案,非常详细和高效。
但是,从Stripe SDK版本"20.26.0"开始,通过接受的答案中的两种方法
不建议使用,也不需要在
compose中这样做。
因为
PaymentSheetContract
和
PaymentLauncher
已经
被弃用,并将在将来从SDK中删除。
相反,现在我们可以直接在Compose方法中使用
rememberPaymentSheet()
。
以下是具体步骤:
步骤1:在我的Compose屏幕中初始化"PaymentSheet"并设置Checkout ViewModel
@Composable
fun CheckoutScreen(
checkoutVM: CheckoutVM = hiltViewModel(),
) {
val context = LocalContext.current
val checkoutState = checkoutVM.checkoutState.collectAsStateWithLifecycle()
val paymentSheet =
rememberPaymentSheet(paymentResultCallback = checkoutVM::onPaymentSheetResult)
LaunchedEffect(key1 = checkoutState.value.paymentSheetResult, block = {
when (checkoutState.value.paymentSheetResult) {
is PaymentSheetResult.Canceled -> {}
is PaymentSheetResult.Completed -> {}
is PaymentSheetResult.Failed -> {}
null -> {}
}
})
LaunchedEffect(key1 = checkoutState.value.checkoutSessionDM, block = {
checkoutState.value.checkoutSessionDM?.run {
checkoutVM.onEvent(CheckoutEvent.OnCheckOut(paymentSheet))
}
})
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(modifier = Modifier.clickable {
checkoutVM.onEvent(CheckoutEvent.OnInitPayment(paymentSheet))
}, text = "Checkout Screen")
}
}
在上面的代码中,我通过获取支付表格。
val paymentSheet =
rememberPaymentSheet(paymentResultCallback = checkoutVM::onPaymentSheetResult)
回调方法:
对于回调,我传递了在我的Checkout ViewModel中定义的一个方法,名为onPaymentSheetResult(paymentSheetResult: PaymentSheetResult)
引用:
回调也可以在可组合中处理,使用LaunchedEffect并观察状态变化,如上所示。
您可以选择适合您的代码架构的方法
所有事件都在我的Hilt ViewModel中处理,名为"CheckoutVM"。
在点击TextComp时,我调用我的服务器端API来获取付款详情或所需的付款表参数值
例如clientSecret或publishable key等。
我使用onEvent()方法将paymentSheet实例传递给checkoutVM viewModel。
这是我的CheckoutVM的代码。
@HiltViewModel
class CheckoutVM @Inject constructor(
private val checkoutRepoDec: CheckoutRepoDec,
) : ViewModel() {
private val _checkoutState = MutableStateFlow(CheckoutState())
val checkoutState: StateFlow<CheckoutState> = _checkoutState
fun onEvent(event: CheckoutEvent) {
when (event) {
is CheckoutEvent.OnInitPayment -> {
getPaymentSessionDetails(event)
}
is CheckoutEvent.OnCheckOut -> {
viewModelScope.launch {
checkoutRepoDec.presentPaymentSheet(event.paymentSheet)
}
}
}
}
private fun getPaymentSessionDetails(event: CheckoutEvent.OnInitPayment) {
viewModelScope.launch {
checkoutRepoDec.initPaymentSheet(event.paymentSheet)?.collect { result ->
when (result) {
is Resource.Error -> {
_checkoutState.update {
it.copy(
checkoutSessionDM = null,
checkoutSessionError = result.error
)
}
}
is Resource.Loading -> {
_checkoutState.update {
it.copy(isLoading = result.isLoading)
}
}
is Resource.Success -> {
_checkoutState.update {
it.copy(
checkoutSessionDM = result.data,
checkoutSessionError = null
)
}
}
}
}
}
}
fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {
_checkoutState.update {
it.copy(
paymentSheetResult = paymentSheetResult
)
}
when (paymentSheetResult) {
is PaymentSheetResult.Canceled -> {
showLog(TAG, "paymentSheetResult - Canceled")
}
is PaymentSheetResult.Failed -> {
showLog(TAG, "paymentSheetResult - Error: ${paymentSheetResult.error}")
}
is PaymentSheetResult.Completed -> {
showLog(TAG, "paymentSheetResult - Completed")
}
}
}
}
第二步:根据
Stripe文档,我将通过执行服务器端请求获取付款详情。
我在我的
CheckoutVM
视图模型中使用Retrofit执行服务器端请求,在我的
CheckoutRepo
中返回Checkout Result,该结果以数据类
CheckoutSessionDM的形式返回。
data class CheckoutSessionDM(
val customer: String,
val ephemeralKey: String,
val paymentIntent: String,
val paymentIntentId: String,
val publishableKey: String
)
使用Retrofit执行网络请求,并在我的ViewModel中更新我的
CheckoutState
数据变量。
data class CheckoutState(
val isLoading: Boolean? = null,
val isCompleted: Boolean? = null,
val isCanceled: Boolean? = null,
val isFailed: Boolean? = null,
val paymentSheetResult: PaymentSheetResult? = null,
val checkoutSessionDM: CheckoutSessionDM? = null,
val checkoutSessionError: String? = null,
)
注意:要获取
CheckoutSessionDM
值,您需要在API服务器上设置一个Stripe的端点,如Stripe文档中所述
Add-Server-Endpoint。
通过您自己的方法执行API请求,使用"App API Server"请求来获取
CheckoutSessionDM
数据。我从
CheckoutRepo
返回一个流到我的
CheckoutVM
viewModel,如下所示。
我使用一个资源类作为我的结果包装器,您可以自由地使用自己的方法将响应返回给您的视图模型并跳过此包装器类。
以下是我的包装器类的代码,如果有人想使用它,请将其放在一个名为
Resource.kt的文件中。
sealed class Resource<T>(
val data: T? = null,
val error: String? = "Unable to display error!"
) {
class Success<T>(data: T?) : Resource<T>(data)
class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
class Loading<T>(val isLoading: Boolean = true) : Resource<T>()
}
调用API -
suspend fun initPaymentSheet(paymentSheet: PaymentSheet): Flow<Resource<CheckoutSessionDM>>? {
return flow {
try {
val result = api.createSession("100", "INR")
result?.body()?.run {
showLog(TAG, "result - $this")
val checkoutSessionDto = this
paymentIntentClientSecret = checkoutSessionDto.paymentIntent
customerConfig = PaymentSheet.CustomerConfiguration(
checkoutSessionDto.customer,
checkoutSessionDto.ephemeralKey
)
val publishableKey = checkoutSessionDto.publishableKey
PaymentConfiguration.init(context, publishableKey)
emit(
Resource.Success(
CheckoutSessionDM(
customer = checkoutSessionDto.customer,
ephemeralKey = checkoutSessionDto.ephemeralKey,
paymentIntent = checkoutSessionDto.paymentIntent,
paymentIntentId = checkoutSessionDto.paymentIntentId,
publishableKey = checkoutSessionDto.publishableKey
)
)
)
} ?: run {
emit(
Resource.Error(
data = null,
message = "Server Error"
)
)
}
} catch (e: Exception) {
if (e is CancellationException) {
showLog(TAG, "initPaymentSheet() CancellationException $e")
throw e
} else if (e is HttpException) {
showLog(TAG, "initPaymentSheet() HttpException $e")
}
emit(
Resource.Error(
data = null,
message = "App Error : $e"
)
)
return@flow
}
}
}
API的响应保存在`CheckoutSessionDto`数据类中,该数据类与`CheckoutSessionDM`相同。使用两个相同的类只是为了保持干净的架构。
步骤3:展示Stripe支付表单
一旦我们在`CheckoutVM`的`getPaymentSessionDetails()`方法中收到`Resource.Success`,我们就可以调用我们的`presentPaymentSheet()`方法。
它可以从两个地方调用:
1. 在我们的`getPaymentSessionDetails()`方法中收到`Resource.Success`时,可以在`CheckoutVM`自身调用。
2. 通过观察`CheckoutState`数据类中的`CheckoutSessionDM`的值,可以在`CheckoutScreen`组合中调用。
在我的示例中,我使用了第二种方法,如
步骤1所示。
在我的
CheckoutScreen
组合中,我观察到
CheckoutState
中的
CheckoutSessionDM
值的变化。
当
CheckoutSessionDM
变为非空时,我在我的
CheckoutVM
viewModel中调用
OnCheckOut
事件。
LaunchedEffect(key1 = checkoutState.value.checkoutSessionDM, block = {
checkoutState.value.checkoutSessionDM?.run {
checkoutVM.onEvent(CheckoutEvent.OnCheckOut(paymentSheet))
}
})
CheckoutVM
进一步调用了我存储库类中的
presentPaymentSheet()
方法。
override suspend fun presentPaymentSheet(paymentSheet: PaymentSheet?) {
paymentSheet?.presentWithPaymentIntent(
paymentIntentClientSecret,
PaymentSheet.Configuration(
merchantDisplayName = "Display Name",
customer = customerConfig,
allowsDelayedPaymentMethods = true
)
)
}
最后,Payment Sheet的响应可以在我们的回调方法
onPaymentSheetResult()
中处理,该方法位于
CheckoutVM
的viewmodel中。
或者
在CheckoutScreen
组合中使用LaunchedEffect
块来处理checkoutState.value.paymentSheetResult
。
希望对某人有所帮助 :)