Skip to content

Instantly share code, notes, and snippets.

@flopshot
Last active May 5, 2020 14:50
Show Gist options
  • Save flopshot/d4f754fde5f48b858e23d6707e7eddcd to your computer and use it in GitHub Desktop.
Save flopshot/d4f754fde5f48b858e23d6707e7eddcd to your computer and use it in GitHub Desktop.
Example of a local-version request code for Stripe.confirmSetupIntent
// AddCardUseCase designed to be injected into any view-layer presenter,
// regardless of whether it is an activity or fragemnt. In our case, we are
// injecting this AddCardUseCase into two fragments, possibly contained in the same Activity
class AddCardUseCase(
private val accountRepo: accountRepository,
private val setupIntentRelay: SetupIntentRelay,
private val stripeManager: StripeManager,
private val containingActivity: Activity // <- injected here just for demonstration
) {
fun addCard(params: PaymentMethodCreateParams): Flow<AddCardState> {
val confirmSetupIntentId = UUID.randomUUID().toString()
accountRepository.createSetupIntentFromOurBackend(accountRepo.userId)
.flatmapLatest { setupIntent ->
val confirmParams = ConfirmSetupIntentParams.create(params, setupIntent.clientSecret)
stripe.confirmSetupIntent(
containingActivity = Activity,
confirmSetupIntentParams = confirmParams,
clientRequestCode = confirmSetupIntentId // <- I wish I could have a local-version request code
)
return@flatMapLatest setupIntentRelay.setupIntentResultFlow()
.filter{ codeToPaymentIntentState: Pair<String, SetupIntentState> ->
codeToSetupIntentState.first == confirmSetupIntentId
}.take(1)
}.flatMapLatest { codeToSetupIntentState: Pair<String, SetupIntentState> ->
when (val state = codeToSetupIntentState.second) {
PaymentIntentState.Success -> {
accountRepository.addCreditCardToUserInOurBackend(
state.second.setupIntent,
)
}
else -> throw IllegalStateException()
}
}
.catch { t ->
emit(AddCreditCartState.Error(t))
}
}
}
class ContainingActivity: Activity {
@Inject lateinit var setupIntentRelay: SetupIntentRelay
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val weakActivity = WeakReference<Activity>(this)
// Handle the result of stripe.confirmSetup
stripe.onSetupResult(requestCode, data, object : ApiResultCallback<SetupIntentResult> {
override fun onSuccess(result: SetupIntentResult) {
val setupIntent = result.intent
val clientRequestCode = result.clientRequestCode // <- Having this returned by the result means that I can completly decouple stripe from my UI logic
val status = paymentIntent.status
if (status == StripeIntent.Status.Succeeded) {
setupIntentRelay.postToSetupIntentResultFlow(
clientRequestCode to SetupIntentState.Success(paymentIntent)
)
} else if (status == StripeIntent.Status.RequiresPaymentMethod) {
setupIntentRelay.postToSetupIntentResultFlow(
clientRequestCode to SetupIntentState.Error(IllegalStateException(
"Payment failed", paymentIntent.lastPaymentError?.message ?: ""
)
)
}
}
override fun onError(e: Exception) {
setupIntentRelay.postToSetupIntentResultFlow(
clientRequestCode to SetupIntentState.Error(e)
)
}
})
}
}
sealed class SetupIntentState {
data class Error(val t: Throwable): SetupIntentState()
data class Success(val paymentIntent: PaymentIntent): SetupIntentState()
}
data class SetupIntent(
val stripeSetupIntentId: String,
val clientSecret: String
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment