I am developing an application in Kotlin with Jetpack Compose and implementing the code to retrieve subscription offers, which include three different plans.
I have uploaded the app to Google Play Store in production to test its functionality on a real device. However, when trying to fetch the products, an error occurs, even though the configuration appears to be correct.
@HiltViewModel
class PurchasesViewModel @Inject constructor() : ViewModel() {
var informationState: InformationUiState by mutableStateOf(InformationUiState.Hidden())
private set
var offeringState: Offering? by mutableStateOf(null)
private set
var selectedPackageState: Package? by mutableStateOf(null)
private set
init {
loadOfferings()
}
private fun loadOfferings() {
Purchases.sharedInstance.getOfferingsWith(
onSuccess = { offerings ->
val offering: Offering? = offerings.current
offeringState = offering
selectedPackageState = offering?.availablePackages?.first()
},
onError = { error ->
informationState = InformationUiState.Error(
onDismiss = { informationState = InformationUiState.Hidden() },
message = UiText.StringResource(R.string.error_loading_subscriptions)
)
Log.d("PurchasesViewModel", "${error.underlyingErrorMessage}")
}
)
}
private fun purchaseOffering(
activity: Activity,
packageToPurchase: Package
) {
Purchases.sharedInstance.purchaseWith(
PurchaseParams.Builder(activity, packageToPurchase).build(),
onError = { error, userCancelled ->
if (userCancelled) {
informationState = InformationUiState.Success(
message = UiText.StringResource(R.string.success_purchase_cancelled)
)
Log.d("PurchasesViewModel", "User cancelled purchase")
} else {
with(error) {
informationState = InformationUiState.Error(
onDismiss = { informationState = InformationUiState.Hidden() },
message =
when (code) {
PurchasesErrorCode.PurchaseNotAllowedError -> UiText.StringResource(R.string.error_purchase_not_allowed)
PurchasesErrorCode.PurchaseInvalidError -> UiText.StringResource(R.string.error_purchase_invalid)
else -> UiText.StringResource(R.string.error_purchasing_subscription)
}
)
}
Log.d("PurchasesViewModel", "${error.underlyingErrorMessage}")
}
},
onSuccess = { storeTransaction, customerInfo ->
if (customerInfo.entitlements["my_entitlement"]?.isActive == true) {
informationState = InformationUiState.Success(message = UiText.StringResource(R.string.success_purchase_completed))
Log.d("PurchasesViewModel", "Purchase successful: $storeTransaction")
}
}
)
}
private fun restorePurchases() {
Purchases.sharedInstance.restorePurchasesWith() { customerInfo ->
if (customerInfo.entitlements["entlb0f322e33f"]?.isActive == true) {
informationState = InformationUiState.Success(
message = UiText.StringResource(R.string.success_restoring_purchases)
)
Log.d("PurchasesViewModel", "Restoration successful: $customerInfo")
} else {
informationState = InformationUiState.Error(
onDismiss = { informationState = InformationUiState.Hidden() },
message = UiText.StringResource(R.string.error_restoring_purchases)
)
Log.d("PurchasesViewModel", "Restoration failed: $customerInfo")
}
}
}
fun onPurchasesEvent(event: PurchasesEvent) {
when(event) {
is PurchasesEvent.OnPurchasePackage -> {
val activity: Activity? = event.activity
val pkg: Package? = event.packageToPurchase
if (activity != null && pkg != null) {
purchaseOffering(
activity = activity,
packageToPurchase = pkg
)
} else {
informationState = InformationUiState.Error(
onDismiss = { informationState = InformationUiState.Hidden() },
message = UiText.StringResource(R.string.error_purchasing_subscription)
)
Log.d("PurchasesViewModel", "Activity or Package is null, cannot proceed with purchase")
}
}
is PurchasesEvent.OnPurchasesRestore -> {
restorePurchases()
}
is PurchasesEvent.OnPackageSelected -> {
selectedPackageState = event.pkg
}
else -> {}
}
}
}

