mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-06 16:28:21 +00:00
ios, android: show progress while a badge is bought, and say the payment is complete while it is issued
This commit is contained in:
+6
-12
@@ -88,10 +88,7 @@ data class BadgeStoreReceipt(
|
||||
// the token the badge service verifies with the Publisher API
|
||||
val token: String,
|
||||
val productId: String,
|
||||
val orderId: String?,
|
||||
val invoiceId: String?,
|
||||
// only set for test products - a real Play purchase has no environment to report
|
||||
val environment: String? = null
|
||||
val invoiceId: String?
|
||||
)
|
||||
|
||||
// TODO [badges] Play Billing has no offline product configuration. Set to true to price the screens
|
||||
@@ -224,15 +221,14 @@ object BadgeStore {
|
||||
}
|
||||
|
||||
// A one-time purchase has its core record before the store charges, and every store outcome reaches it.
|
||||
suspend fun purchase(level: BadgeLevel, period: BadgePeriod): Pair<BadgePurchaseOutcome, String> {
|
||||
suspend fun purchase(level: BadgeLevel, period: BadgePeriod): BadgePurchaseOutcome {
|
||||
val id = badgeStoreProductId(level, period)
|
||||
if (!products.value.containsKey(id)) throw BadgeStoreError.ProductUnavailable(id.productId)
|
||||
// a subscription is never sent to core, so nothing would ever finish it later
|
||||
if (id.productId !in badgeOneTimeProductIds) {
|
||||
val invoiceId = newBadgeInvoiceId()
|
||||
val outcome = storePurchase(id, invoiceId)
|
||||
val outcome = storePurchase(id, newBadgeInvoiceId())
|
||||
if (outcome is BadgePurchaseOutcome.Purchased) finish(outcome.receipt)
|
||||
return outcome to invoiceId
|
||||
return outcome
|
||||
}
|
||||
val rhId = chatModel.remoteHostId()
|
||||
val userId = chatModel.currentUser.value?.userId ?: throw BadgeStoreError.NoActiveProfile
|
||||
@@ -247,7 +243,7 @@ object BadgeStore {
|
||||
is BadgePurchaseOutcome.Cancelled -> closeInvoice(userId, invoiceId)
|
||||
is BadgePurchaseOutcome.Pending -> {}
|
||||
}
|
||||
return outcome to invoiceId
|
||||
return outcome
|
||||
} catch (e: Exception) {
|
||||
closeInvoice(userId, invoiceId)
|
||||
throw e
|
||||
@@ -262,9 +258,7 @@ object BadgeStore {
|
||||
BadgeStoreReceipt(
|
||||
token = "test-${UUID.randomUUID()}",
|
||||
productId = id.productId,
|
||||
orderId = null,
|
||||
invoiceId = invoiceId,
|
||||
environment = "test products"
|
||||
invoiceId = invoiceId
|
||||
)
|
||||
)
|
||||
} else {
|
||||
|
||||
+19
-26
@@ -1,6 +1,7 @@
|
||||
package chat.simplex.common.views.badges
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
@@ -48,6 +49,7 @@ fun BadgesCheckOrderView(level: BadgeLevel, period: BadgePeriod, modalManager: M
|
||||
.padding(top = 20.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(sectionCardColor())
|
||||
.border(1.dp, MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.92f), RoundedCornerShape(16.dp))
|
||||
.padding(horizontal = 24.dp, vertical = 12.dp)
|
||||
) {
|
||||
OrderRow(MR.strings.badges_your_badge, stringResource(level.title))
|
||||
@@ -63,6 +65,22 @@ fun BadgesCheckOrderView(level: BadgeLevel, period: BadgePeriod, modalManager: M
|
||||
BadgeBillingFooter(period)
|
||||
}
|
||||
}
|
||||
|
||||
if (purchasing.value) {
|
||||
Box(
|
||||
Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Surface(Modifier.size(50.dp), color = MaterialTheme.colors.background.copy(0.9f), contentColor = LocalContentColor.current, shape = RoundedCornerShape(50)){}
|
||||
CircularProgressIndicator(
|
||||
Modifier
|
||||
.padding(horizontal = 2.dp)
|
||||
.size(30.dp),
|
||||
color = MaterialTheme.colors.secondary,
|
||||
strokeWidth = 3.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -99,10 +117,7 @@ private fun purchase(level: BadgeLevel, period: BadgePeriod, purchasing: Mutable
|
||||
// not withBGApi: the purchase waits for the user in the Play sheet and would block chat API calls
|
||||
withLongRunningApi {
|
||||
try {
|
||||
val (outcome, invoiceId) = BadgeStore.purchase(level, period)
|
||||
if (outcome is BadgePurchaseOutcome.Purchased && outcome.receipt.productId !in badgeOneTimeProductIds) {
|
||||
showPurchasedAlert(outcome.receipt, invoiceId)
|
||||
}
|
||||
BadgeStore.purchase(level, period)
|
||||
purchasing.value = false
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "BadgesCheckOrderView.purchase: ${e.stackTraceToString()}")
|
||||
@@ -115,25 +130,3 @@ private fun purchase(level: BadgeLevel, period: BadgePeriod, purchasing: Mutable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO [badges] store integration diagnostics - replaced by the issued badge once subscriptions are delivered.
|
||||
private fun showPurchasedAlert(receipt: BadgeStoreReceipt, invoiceId: String) {
|
||||
val returnedInvoice = when (receipt.invoiceId) {
|
||||
null -> "none"
|
||||
invoiceId -> "yes"
|
||||
else -> "mismatch: ${receipt.invoiceId}"
|
||||
}
|
||||
val lines = mutableListOf(
|
||||
"Product: ${receipt.productId}",
|
||||
"Invoice: $invoiceId",
|
||||
"Invoice returned by Google: $returnedInvoice",
|
||||
"Order: ${receipt.orderId ?: "none"}"
|
||||
)
|
||||
if (receipt.environment != null) lines.add("Environment: ${receipt.environment}")
|
||||
val summary = lines.joinToString("\n")
|
||||
Log.d(TAG, "badge purchase succeeded\n$summary")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.badges_purchase_successful),
|
||||
text = summary
|
||||
)
|
||||
}
|
||||
|
||||
+1
-1
@@ -171,7 +171,7 @@ private fun PeriodCard(level: BadgeLevel, period: BadgePeriod, selectedPeriod: B
|
||||
Text(stringResource(period.label), style = MaterialTheme.typography.body1, color = textColor, textAlign = TextAlign.Center)
|
||||
Text(period.priceText(BadgeStore.price(level, period)), style = MaterialTheme.typography.h3, fontWeight = FontWeight.SemiBold, color = textColor, textAlign = TextAlign.Center)
|
||||
val percent = savingsPercent(level, period)
|
||||
if (percent != null) {
|
||||
if (forSale && percent != null) {
|
||||
Text(
|
||||
stringResource(MR.strings.badges_savings).format("${percent}%"),
|
||||
style = MaterialTheme.typography.body2,
|
||||
|
||||
@@ -3180,14 +3180,13 @@
|
||||
<string name="badges_banner_subtitle">Get badge + better files</string>
|
||||
<string name="badges_banner_dismiss_message">You can support SimpleX later in Settings.</string>
|
||||
<string name="badges_larger_files_longer">Support SimpleX to send larger files that stay available longer</string>
|
||||
<string name="badges_purchase_successful">Purchase successful</string>
|
||||
<string name="badges_purchase_error">Purchase error</string>
|
||||
<string name="badges_purchase_will_retry">The purchase will be retried, and the badge will arrive.</string>
|
||||
<string name="badges_check_your_order_title">Check your order</string>
|
||||
<string name="badges_order_duration">Duration</string>
|
||||
<string name="badges_order_total">Total</string>
|
||||
<string name="badges_issuing_title">Issuing your badge</string>
|
||||
<string name="badges_issuing_body">The store has taken the payment. Keeps trying if you close the app.</string>
|
||||
<string name="badges_issuing_body">Your payment is complete. The badge will be added to this profile.</string>
|
||||
<string name="badges_waiting_for_approval_title">Waiting for approval</string>
|
||||
<string name="badges_waiting_for_approval_body">Nothing has been charged.</string>
|
||||
<string name="badges_checking_title">Checking your purchase</string>
|
||||
|
||||
Reference in New Issue
Block a user