I can't figure it out, how to impalement Stripe API integration into an Compose app
Here is Stripe provided snippet of code
class CheckoutActivity : AppCompatActivity() {
lateinit var paymentSheet: PaymentSheet
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
paymentSheet = PaymentSheet(this, ::onPaymentSheetResult)
}
fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {
// implemented in the next steps
}
}
In my case I am out of ideas where to put paymentSheet = PaymentSheet(this, ::onPaymentSheetResult) in compose code as it shows that: None of the following functions can be called with the arguments supplied.
(ComponentActivity, PaymentSheetResultCallback) defined in com.stripe.android.paymentsheet.PaymentSheet
(Fragment, PaymentSheetResultCallback) defined in com.stripe.android.paymentsheet.PaymentSheet
class MainActivity : ComponentActivity() {
lateinit var paymentSheet: PaymentSheet
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PayTheme {
LoginUi()
}
}
fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {
// implemented in the next steps
}
}
}
First of all, you can check the stripe compose sample on github stripe-android (ComposeExampleActivity.kt).
implementation "com.stripe:stripe-android:20.17.0"
PaymentConfiguration
in the Application class@HiltAndroidApp
class BookstoreApplication : Application() {
override fun onCreate() {
super.onCreate()
PaymentConfiguration.init(applicationContext, BuildConfig.STRIPE_PUBLISHABLE_KEY)
}
}
Stripe provides many ways to implement payments in the app. Let's consider payment confirmation using PaymentSheetContract
and PaymentLauncher
.
PaymentSheetContract
rememberLauncherForActivityResult()
with PaymentSheetContract()
to launch stripe payment form.
@Composable
fun PaymentScreen(
viewModel: PaymentViewModel = hiltViewModel()
) {
val stripeLauncher = rememberLauncherForActivityResult(
contract = PaymentSheetContract(),
onResult = {
viewModel.handlePaymentResult(it)
}
)
val clientSecret by viewModel.clientSecret.collectAsStateWithLifecycle()
clientSecret?.let {
val args = PaymentSheetContract.Args.createPaymentIntentArgs(it)
stripeLauncher.launch(args)
viewModel.onPaymentLaunched()
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Button(
onClick = {
viewModel.makePayment()
}
) {
Text(text = "Confirm payment")
}
}
}
PaymentViewModel.kt
@HiltViewModel
class PaymentViewModel @Inject constructor(
private val repository: PaymentRepository
) : ViewModel() {
private val _clientSecret = MutableStateFlow<String?>(null)
val clientSecret = _clientSecret.asStateFlow()
fun makePayment() {
val paymentIntent = repository.createPaymentIntent()
_clientSecret.update { paymentIntent.clientSecret }
}
fun onPaymentLaunched() {
_clientSecret.update { null }
}
fun handlePaymentResult(result: PaymentSheetResult) {
when(result) {
PaymentSheetResult.Canceled -> TODO()
PaymentSheetResult.Completed -> TODO()
is PaymentSheetResult.Failed -> TODO()
}
}
}
PaymentLauncher
rememberLauncherForActivityResult()
with PaymentSheetContract()
to launch stripe payment form.
@Composable
fun PaymentScreen(
viewModel: PaymentViewModel = hiltViewModel()
) {
val paymentLauncher = rememberPaymentLauncher(viewModel::handlePaymentResult)
val confirmPaymentParams by viewModel.confirmPaymentParams.collectAsStateWithLifecycle()
confirmPaymentParams?.let { payment ->
paymentLauncher.confirm(payment)
viewModel.onPaymentLaunched()
}
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Button(
onClick = {
viewModel.makePayment()
}
) {
Text(text = "Confirm payment")
}
}
}
@Composable
fun rememberPaymentLauncher(
callback: PaymentLauncher.PaymentResultCallback
): PaymentLauncher {
val config = PaymentConfiguration.getInstance(LocalContext.current)
return PaymentLauncher.rememberLauncher(
publishableKey = config.publishableKey,
stripeAccountId = config.stripeAccountId,
callback = callback
)
}
PaymentViewModel.kt
@HiltViewModel
class PaymentViewModel @Inject constructor(
private val repository: PaymentRepository
) : ViewModel() {
private val _confirmPaymentParams = MutableStateFlow<ConfirmPaymentIntentParams?>(null)
val confirmPaymentParams = _confirmPaymentParams.asStateFlow()
fun makePayment() {
val paymentIntent = repository.createPaymentIntent()
// For example, pay with hardcoded test card
val configuration = ConfirmPaymentIntentParams.createWithPaymentMethodCreateParams(
paymentMethodCreateParams = PaymentMethodCreateParams.create(
card = PaymentMethodCreateParams.Card(
number = "4242424242424242",
expiryMonth = 1,
expiryYear = 24,
cvc = "111"
)
),
clientSecret = paymentIntent.clientSecret
)
_confirmPaymentParams.update { configuration }
}
fun onPaymentLaunched() {
_confirmPaymentParams.update { null }
}
fun handlePaymentResult(result: PaymentResult) {
when(result) {
PaymentResult.Canceled -> TODO()
PaymentResult.Completed -> TODO()
is PaymentResult.Failed -> TODO()
}
}
}
client_secret
for example).
Please, read stripe Accept a payment doc to understand better.
You can also watch youtube video: How to integrate Stripe in Android Studio 2022.
PaymentRepository.kt
class PaymentRepository @Inject constructor(
private val stripeApiService: StripeApiService,
private val paymentDao: PaymentDao
) {
/*
Create customer before payment (attach to app user)
*/
suspend fun createCustomer() = withContext(Dispatchers.IO) {
val customer = stripeApiService.createCustomer()
// save customer in the database or preferences
// customerId required to confirm payment
paymentDao.insertCustomer(customer)
}
suspend fun refreshCustomerEphemeralKey() = withContext(Dispatchers.IO) {
val customer = paymentDao.getCustomer()
val key = stripeApiService.createEphemeralKey(customer.customerId)
paymentDao.insertEphemeralKey(key)
}
suspend fun createPaymentIntent() = withContext(Dispatchers.IO) {
val customer = paymentDao.getCustomer()
refreshCustomerEphemeralKey()
val paymentIntent = stripeApiService.createPaymentIntent(
customerId = customer.customerId,
amount = 1000,
currency = "usd", // or your currency
autoPaymentMethodsEnable = true
)
return@withContext paymentIntent
}
}
StripeApiService.kt
private const val SECRET = BuildConfig.STRIPE_SECRET_KEY
interface StripeApiService {
@Headers(
"Authorization: Bearer $SECRET",
"Stripe-Version: 2022-08-01"
)
@POST("v1/customers")
suspend fun createCustomer() : CustomerApiModel
@Headers(
"Authorization: Bearer $SECRET",
"Stripe-Version: 2022-08-01"
)
@POST("v1/ephemeral_keys")
suspend fun createEphemeralKey(
@Query("customer") customerId: String
): EphemeralKeyApiModel
@Headers(
"Authorization: Bearer $SECRET"
)
@POST("v1/payment_intents")
suspend fun createPaymentIntent(
@Query("customer") customerId: String,
@Query("amount") amount: Int,
@Query("currency") currency: String,
@Query("automatic_payment_methods[enabled]") autoPaymentMethodsEnable: Boolean,
): PaymentIntentApiModel
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With