Skip to content

Commit

Permalink
Add Cancel subscriptions support (#2008)
Browse files Browse the repository at this point in the history
  • Loading branch information
vegaro authored Dec 26, 2024
1 parent afd8114 commit 842e85f
Showing 7 changed files with 58 additions and 9 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ activity-compose = "1.9.3"
fragment = "1.6.1"
hamcrest = "1.3"
emergeGradlePlugin = "4.0.0"
emergeSnapshots = "1.2.0"
emergeSnapshots = "1.3.0"

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Original file line number Diff line number Diff line change
@@ -50,14 +50,16 @@ internal fun InternalCustomerCenter(
when (action) {
is CustomerCenterAction.DetermineFlow -> {
coroutineScope.launch {
viewModel.pathButtonPressed(action.path)
viewModel.pathButtonPressed(context, action.path)
}
}

is CustomerCenterAction.PerformRestore -> {
coroutineScope.launch {
viewModel.restorePurchases()
}
}

is CustomerCenterAction.DismissRestoreDialog -> viewModel.dismissRestoreDialog()
is CustomerCenterAction.ContactSupport -> viewModel.contactSupport(context, action.email)
}
Original file line number Diff line number Diff line change
@@ -148,6 +148,7 @@ private class SubscriptionInformationProvider : PreviewParameterProvider<Purchas
expirationDateString = "June 1st, 2024",
willRenew = true,
active = true,
productId = "basic_monthly",
),
PurchaseInformation(
title = "Basic",
@@ -156,6 +157,7 @@ private class SubscriptionInformationProvider : PreviewParameterProvider<Purchas
expirationDateString = "June 1st, 2024",
willRenew = false,
active = true,
productId = "basic_yearly",
),
PurchaseInformation(
title = "Basic",
@@ -164,6 +166,7 @@ private class SubscriptionInformationProvider : PreviewParameterProvider<Purchas
expirationDateString = "June 1st, 2024",
willRenew = false,
active = false,
productId = "basic_weekly",
),
)
}
Original file line number Diff line number Diff line change
@@ -116,6 +116,7 @@ internal object CustomerCenterConfigTestData {
expirationDateString = "June 1st, 2024",
willRenew = true,
active = true,
productId = "monthly_product_id",
)

val purchaseInformationYearlyExpiring = PurchaseInformation(
@@ -125,5 +126,6 @@ internal object CustomerCenterConfigTestData {
expirationDateString = "June 1st, 2025",
willRenew = false,
active = true,
productId = "yearly_product_id",
)
}
Original file line number Diff line number Diff line change
@@ -7,4 +7,5 @@ internal data class PurchaseInformation(
val expirationDateString: String?,
val willRenew: Boolean,
val active: Boolean,
val productId: String,
)
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package com.revenuecat.purchases.ui.revenuecatui.customercenter.viewmodel

import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.content.ContextCompat.startActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.revenuecat.purchases.CacheFetchPolicy
import com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI
import com.revenuecat.purchases.PurchasesException
import com.revenuecat.purchases.customercenter.CustomerCenterConfigData
import com.revenuecat.purchases.ui.revenuecatui.customercenter.data.CustomerCenterConfigTestData
import com.revenuecat.purchases.ui.revenuecatui.customercenter.data.CustomerCenterState
import com.revenuecat.purchases.ui.revenuecatui.customercenter.data.PurchaseInformation
import com.revenuecat.purchases.ui.revenuecatui.customercenter.dialogs.RestorePurchasesState
import com.revenuecat.purchases.ui.revenuecatui.data.PurchasesType
import com.revenuecat.purchases.ui.revenuecatui.extensions.localizedPeriod
import com.revenuecat.purchases.ui.revenuecatui.helpers.Logger
import com.revenuecat.purchases.utils.getDefaultLocales
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -25,11 +28,12 @@ import kotlinx.coroutines.flow.update
@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
internal interface CustomerCenterViewModel {
val state: StateFlow<CustomerCenterState>
suspend fun pathButtonPressed(path: CustomerCenterConfigData.HelpPath)
suspend fun pathButtonPressed(context: Context, path: CustomerCenterConfigData.HelpPath)
fun dismissRestoreDialog()
suspend fun restorePurchases()
fun contactSupport(context: Context, supportEmail: String)
fun openAppStore(context: Context)
fun showManageSubscriptions(context: Context, productId: String)
}

@OptIn(ExperimentalPreviewRevenueCatPurchasesAPI::class)
@@ -57,7 +61,7 @@ internal class CustomerCenterViewModelImpl(
initialValue = CustomerCenterState.Loading,
)

override suspend fun pathButtonPressed(path: CustomerCenterConfigData.HelpPath) {
override suspend fun pathButtonPressed(context: Context, path: CustomerCenterConfigData.HelpPath) {
when (path.type) {
CustomerCenterConfigData.HelpPath.PathType.MISSING_PURCHASE -> {
_state.update { currentState ->
@@ -71,7 +75,15 @@ internal class CustomerCenterViewModelImpl(
}

CustomerCenterConfigData.HelpPath.PathType.CANCEL -> {
// Customer Center WIP
when (val currentState = _state.value) {
is CustomerCenterState.Success -> {
currentState.purchaseInformation?.productId?.let {
showManageSubscriptions(context, it)
}
}

else -> {}
}
}

else -> {
@@ -122,9 +134,20 @@ internal class CustomerCenterViewModelImpl(
val customerInfo = purchases.awaitCustomerInfo(fetchPolicy = CacheFetchPolicy.FETCH_CURRENT)

// Customer Center WIP: update when we have subscription information in CustomerInfo
val activeEntitlement = customerInfo.entitlements.active.isEmpty()
if (activeEntitlement) {
return CustomerCenterConfigTestData.purchaseInformationMonthlyRenewing
val activeEntitlement = customerInfo.entitlements.active.values.firstOrNull()
if (activeEntitlement != null) {
val product = purchases.awaitGetProduct(activeEntitlement.productIdentifier).first()
val locale = getDefaultLocales().first()
val purchaseInformation = PurchaseInformation(
title = product.description,
durationTitle = product.period?.localizedPeriod(locale) ?: "",
price = product.price.formatted,
expirationDateString = activeEntitlement.expirationDate.toString(),
willRenew = activeEntitlement.willRenew,
active = activeEntitlement.isActive,
productId = activeEntitlement.productIdentifier,
)
return purchaseInformation
}

return null
@@ -145,4 +168,14 @@ internal class CustomerCenterViewModelImpl(
}
context.startActivity(intent)
}

override fun showManageSubscriptions(context: Context, productId: String) {
try {
val packageName = context.packageName
val uri = "https://play.google.com/store/account/subscriptions?sku=$productId&package=$packageName"
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(uri)))
} catch (e: ActivityNotFoundException) {
Logger.e("Error opening manage subscriptions", e)
}
}
}
Original file line number Diff line number Diff line change
@@ -10,10 +10,12 @@ import com.revenuecat.purchases.Purchases
import com.revenuecat.purchases.PurchasesAreCompletedBy
import com.revenuecat.purchases.awaitCustomerCenterConfigData
import com.revenuecat.purchases.awaitCustomerInfo
import com.revenuecat.purchases.awaitGetProducts
import com.revenuecat.purchases.awaitOfferings
import com.revenuecat.purchases.awaitPurchase
import com.revenuecat.purchases.awaitRestore
import com.revenuecat.purchases.customercenter.CustomerCenterConfigData
import com.revenuecat.purchases.models.StoreProduct
import com.revenuecat.purchases.paywalls.events.PaywallEvent

/**
@@ -33,6 +35,8 @@ internal interface PurchasesType {

suspend fun awaitCustomerCenterConfigData(): CustomerCenterConfigData

suspend fun awaitGetProduct(productId: String): List<StoreProduct>

fun track(event: PaywallEvent)

val purchasesAreCompletedBy: PurchasesAreCompletedBy
@@ -64,6 +68,10 @@ internal class PurchasesImpl(private val purchases: Purchases = Purchases.shared
return purchases.awaitCustomerCenterConfigData()
}

override suspend fun awaitGetProduct(productId: String): List<StoreProduct> {
return purchases.awaitGetProducts(listOf(productId))
}

override fun track(event: PaywallEvent) {
purchases.track(event)
}

0 comments on commit 842e85f

Please sign in to comment.