Update CartViewModel.kt — payment in two phases
In the code column — the full CartViewModel.kt. It's grown since lesson 3: 3 → in-memory cart, 9 → placeOrder, 10 → the table. Now — payment in two phases. Note the order: startPayment() only fetches a clientSecret; the order (placeOrder) is recorded only in completeOrder(), once Stripe has confirmed the payment.
CartEvent gains payment events:
sealed interface CartEvent {
// ...earlier ones (AddItem, RemoveItem, ...)
data object Checkout : CartEvent // tapped "Pay"
data object PaymentSucceeded : CartEvent // Stripe: success
data object PaymentCanceled : CartEvent // user canceled
data class PaymentFailed(val message: String?) : CartEvent
data object ClearOrderPlaced : CartEvent
}
PicaApi gains the endpoint (the server sets the amount in cents):
@POST("create-payment-intent")
suspend fun createPaymentIntent(@Body request: PaymentIntentRequest): PaymentIntentResponse
@Serializable data class PaymentIntentRequest(@SerialName("amount_cents") val amountCents: Int)
@Serializable data class PaymentIntentResponse(@SerialName("client_secret") val clientSecret: String)
And Koin — CartViewModel now has three dependencies:
// cartModule
single { OrderRepository(get()) }
viewModel { CartViewModel(get(), get(), get()) } // OrderRepository, PicaApi, TableSession
PaymentConfiguration runs once in PicaApplication.onCreate (before any PaymentSheet):
PaymentConfiguration.init(applicationContext, "pk_test_...") // your Stripe publishable key
Tip. Why are
startPaymentandcompleteOrderseparate? Because between them there's a user action (entering the card inPaymentSheet) and a network round-trip to Stripe. The ViewModel can't "await" payment inline — it launchesPaymentSheet(viaclientSecret) and reacts to the result as a new event (PaymentSucceeded). A classic async UI flow.