Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support to manage non-Google purchases in Customer Center #2067

Merged
merged 11 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.revenuecat.paywallstester.ui.theme.PaywallTesterAndroidTheme
import com.revenuecat.purchases.Offering
Expand All @@ -22,8 +23,8 @@ class MainActivity : ComponentActivity(), PaywallResultHandler {
super.onCreate(savedInstanceState)
paywallActivityLauncher = PaywallActivityLauncher(this, this)
setContent {
PaywallTesterAndroidTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
PaywallTesterAndroidTheme(dynamicColor = false) {
Box(modifier = Modifier.fillMaxSize().background(color = MaterialTheme.colorScheme.background)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed this because I was facing some issues and thought this could be the reason, but in any case, I think it's better to use a Box here

PaywallTesterApp()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Divider
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material3.Button
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@ private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80,
surface = PurpleGrey80,
)

private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40,
surface = PurpleGrey80,

/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ data class CustomerCenterConfigData(
@SerialName("purchases_not_recovered")
PURCHASES_NOT_RECOVERED,

@SerialName("manage_subscription")
MANAGE_SUBSCRIPTION,

@SerialName("you_have_promo")
YOU_HAVE_PROMO,

Expand Down Expand Up @@ -199,6 +202,7 @@ data class CustomerCenterConfigData(
PURCHASES_NOT_RECOVERED ->
"We couldn't find any additional purchases under this account. " +
"Contact support for assistance if you think this is an error."
MANAGE_SUBSCRIPTION -> "Manage your subscription"
YOU_HAVE_PROMO -> "You've been granted a subscription that doesn’t renew"
YOU_HAVE_LIFETIME -> "Your active lifetime subscription"
WEB_SUBSCRIPTION_MANAGE ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class BackendGetCustomerCenterConfigTest {
"You can manage your subscription by visiting your account.",
"free" to "Free",
"never" to "Never",
"manage_subscription" to "Manage your subscription",
)
),
support = CustomerCenterConfigData.Support(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"you_have_lifetime": "Your active lifetime subscription",
"web_subscription_manage": "You have an active subscription that was created on the web. You can manage your subscription by visiting your account.",
"free": "Free",
"never": "Never"
"never": "Never",
"manage_subscription": "Manage your subscription"
}
},
"screens":{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ internal fun InternalCustomerCenter(

is CustomerCenterAction.DismissRestoreDialog -> viewModel.dismissRestoreDialog()
is CustomerCenterAction.ContactSupport -> viewModel.contactSupport(context, action.email)
is CustomerCenterAction.OpenURL -> viewModel.openURL(context, action.url)
is CustomerCenterAction.NavigationButtonPressed -> {
val buttonType = state.navigationButtonType
viewModel.onNavigationButtonPressed()
Expand Down Expand Up @@ -227,9 +228,8 @@ private fun MainScreen(
screen = managementScreen,
localization = configuration.localization,
purchaseInformation = state.purchaseInformation,
onPathButtonPress = { path ->
onAction(CustomerCenterAction.PathButtonPressed(path))
},
support = configuration.support,
onAction = onAction,
)
} ?: run {
// Handle missing management screen
Expand All @@ -240,9 +240,8 @@ private fun MainScreen(
ManageSubscriptionsView(
screen = noActiveScreen,
localization = configuration.localization,
onPathButtonPress = { path ->
onAction(CustomerCenterAction.PathButtonPressed(path))
},
support = configuration.support,
onAction = onAction,
)
} ?: run {
// Fallback with a restore button
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.revenuecat.purchases.ui.revenuecatui.customercenter

import android.net.Uri
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
Expand Down Expand Up @@ -205,6 +206,8 @@ private val PaddingHorizontal = 8.dp
private val PaddingVertical = 8.dp
private const val SizeIconDp = 22

private const val MANAGEMENT_URL = "https://play.google.com/store/account/subscriptions"

private class SubscriptionInformationProvider : PreviewParameterProvider<PurchaseInformation> {
override val values: Sequence<PurchaseInformation> = sequenceOf(
PurchaseInformation(
Expand All @@ -219,6 +222,7 @@ private class SubscriptionInformationProvider : PreviewParameterProvider<Purchas
ExpirationOrRenewal.Date.DateString("June 1st, 2024"),
),
store = Store.PLAY_STORE,
managementURL = Uri.parse(MANAGEMENT_URL),
),
PurchaseInformation(
title = "Basic",
Expand All @@ -232,6 +236,7 @@ private class SubscriptionInformationProvider : PreviewParameterProvider<Purchas
ExpirationOrRenewal.Date.DateString("June 1st, 2024"),
),
store = Store.PLAY_STORE,
managementURL = Uri.parse(MANAGEMENT_URL),
),
PurchaseInformation(
title = "Basic",
Expand All @@ -245,6 +250,7 @@ private class SubscriptionInformationProvider : PreviewParameterProvider<Purchas
ExpirationOrRenewal.Date.DateString("June 1st, 2024"),
),
store = Store.PLAY_STORE,
managementURL = Uri.parse(MANAGEMENT_URL),
),
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.revenuecat.purchases.ui.revenuecatui.customercenter.actions

import android.net.Uri
import com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI
import com.revenuecat.purchases.customercenter.CustomerCenterConfigData

Expand All @@ -9,5 +10,6 @@ internal sealed class CustomerCenterAction {
object PerformRestore : CustomerCenterAction()
object DismissRestoreDialog : CustomerCenterAction()
data class ContactSupport(val email: String) : CustomerCenterAction()
data class OpenURL(val url: Uri) : CustomerCenterAction()
object NavigationButtonPressed : CustomerCenterAction()
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.revenuecat.purchases.ui.revenuecatui.customercenter.data

import android.net.Uri
import com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI
import com.revenuecat.purchases.Store
import com.revenuecat.purchases.customercenter.CustomerCenterConfigData
Expand Down Expand Up @@ -122,6 +123,7 @@ internal object CustomerCenterConfigTestData {
),
productIdentifier = "monthly_product_id",
store = Store.PLAY_STORE,
managementURL = Uri.parse("https://play.google.com/store/account/subscriptions"),
)

val purchaseInformationYearlyExpiring = PurchaseInformation(
Expand All @@ -136,5 +138,6 @@ internal object CustomerCenterConfigTestData {
),
productIdentifier = "yearly_product_id",
store = Store.PLAY_STORE,
managementURL = Uri.parse("https://play.google.com/store/account/subscriptions"),
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.revenuecat.purchases.ui.revenuecatui.customercenter.data

import android.net.Uri
import com.revenuecat.purchases.EntitlementInfo
import com.revenuecat.purchases.Store
import com.revenuecat.purchases.models.StoreProduct
Expand All @@ -18,12 +19,14 @@ internal class PurchaseInformation(
val expirationOrRenewal: ExpirationOrRenewal?,
val productIdentifier: String,
val store: Store,
val managementURL: Uri?,
) {

constructor(
entitlementInfo: EntitlementInfo? = null,
subscribedProduct: StoreProduct? = null,
transaction: TransactionDetails,
managementURL: Uri?,
dateFormatter: DateFormatter = DefaultDateFormatter(),
locale: Locale,
) : this(
Expand Down Expand Up @@ -73,6 +76,7 @@ internal class PurchaseInformation(
} else {
subscribedProduct?.let { PriceDetails.Paid(it.price.formatted) } ?: PriceDetails.Unknown
},
managementURL = managementURL,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.revenuecat.purchases.CacheFetchPolicy
Expand All @@ -15,11 +16,13 @@ import com.revenuecat.purchases.Store
import com.revenuecat.purchases.SubscriptionInfo
import com.revenuecat.purchases.customercenter.CustomerCenterConfigData
import com.revenuecat.purchases.models.Transaction
import com.revenuecat.purchases.ui.revenuecatui.R
import com.revenuecat.purchases.ui.revenuecatui.customercenter.data.CustomerCenterState
import com.revenuecat.purchases.ui.revenuecatui.customercenter.data.FeedbackSurveyData
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.openUriOrElse
import com.revenuecat.purchases.ui.revenuecatui.helpers.Logger
import com.revenuecat.purchases.ui.revenuecatui.utils.DateFormatter
import com.revenuecat.purchases.ui.revenuecatui.utils.DefaultDateFormatter
Expand All @@ -42,6 +45,7 @@ internal interface CustomerCenterViewModel {
fun contactSupport(context: Context, supportEmail: String)
fun onNavigationButtonPressed()
suspend fun loadCustomerCenter()
fun openURL(context: Context, url: Uri)
}

internal sealed class TransactionDetails(
Expand Down Expand Up @@ -203,7 +207,13 @@ internal class CustomerCenterViewModelImpl(
val entitlement = customerInfo.entitlements.all.values
.firstOrNull { it.productIdentifier == activeTransactionDetails.productIdentifier }

return createPurchaseInformation(activeTransactionDetails, entitlement, dateFormatter, locale)
return createPurchaseInformation(
activeTransactionDetails,
entitlement,
customerInfo.managementURL,
dateFormatter,
locale,
)
} else {
Logger.w("Could not find subscription information")
}
Expand Down Expand Up @@ -248,6 +258,7 @@ internal class CustomerCenterViewModelImpl(
private suspend fun createPurchaseInformation(
transaction: TransactionDetails,
entitlement: EntitlementInfo?,
managementURL: Uri?,
dateFormatter: DateFormatter,
locale: Locale,
): PurchaseInformation {
Expand All @@ -268,6 +279,7 @@ internal class CustomerCenterViewModelImpl(
entitlementInfo = entitlement,
subscribedProduct = product,
transaction = transaction,
managementURL = managementURL,
dateFormatter = dateFormatter,
locale = locale,
)
Expand All @@ -282,6 +294,19 @@ internal class CustomerCenterViewModelImpl(
context.startActivity(Intent.createChooser(intent, "Contact Support"))
}

@SuppressWarnings("ForbiddenComment")
override fun openURL(context: Context, url: Uri) {
context.openUriOrElse(url.toString()) { exception ->
val msg = if (exception is ActivityNotFoundException) {
context.getString(R.string.no_browser_cannot_open_link)
} else {
context.getString(R.string.cannot_open_link)
}
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
Logger.w(msg)
}
}

override fun onNavigationButtonPressed() {
_state.update { currentState ->
if (currentState is CustomerCenterState.Success &&
Expand Down
Loading