mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-13 06:37:45 +00:00
ios, android: tell the buyer of a refusal without relying on the store echoing the invoice
This commit is contained in:
@@ -66,6 +66,7 @@ struct BadgeStoreReceipt {
|
||||
// the signed token the badge service verifies - never transaction.jsonRepresentation
|
||||
let jws: String
|
||||
let productId: String
|
||||
let transactionId: UInt64
|
||||
let invoiceId: UUID?
|
||||
let signatureVerified: Bool
|
||||
let transaction: Transaction
|
||||
@@ -111,8 +112,8 @@ final class BadgeStore: ObservableObject {
|
||||
// set when the first sweep has returned, which is after every transaction the store holds was handed
|
||||
// to core - until then a purchase made while the app was not running is unknown
|
||||
@Published private var reconciledOnce = false
|
||||
// invoices of the purchases the user started this run, whose refusal is theirs to be told of
|
||||
private var awaitedInvoices: Set<String> = []
|
||||
// purchases the user started this run, by transaction: the store is not relied on to echo the invoice back
|
||||
private var awaitedTransactions: Set<UInt64> = []
|
||||
private var transactionUpdates: Task<Void, Never>? = nil
|
||||
|
||||
private init() {}
|
||||
@@ -208,7 +209,7 @@ final class BadgeStore: ObservableObject {
|
||||
do {
|
||||
let outcome = try await storePurchase(product, invoiceId)
|
||||
if case let .purchased(receipt) = outcome, receipt.signatureVerified {
|
||||
await MainActor.run { _ = awaitedInvoices.insert(invoice) }
|
||||
await MainActor.run { _ = awaitedTransactions.insert(receipt.transactionId) }
|
||||
await handOver(receipt)
|
||||
}
|
||||
await MainActor.run { buying = false }
|
||||
@@ -263,7 +264,7 @@ final class BadgeStore: ObservableObject {
|
||||
|
||||
private func settle(_ receipt: BadgeStoreReceipt, refusal: Error?) async {
|
||||
await receipt.transaction.finish()
|
||||
let awaited = await MainActor.run { receipt.echoedInvoiceId.map { awaitedInvoices.remove($0) != nil } ?? false }
|
||||
let awaited = await MainActor.run { awaitedTransactions.remove(receipt.transactionId) != nil }
|
||||
if awaited, let refusal {
|
||||
await MainActor.run { showAlert(NSLocalizedString("Purchase error", comment: "alert title"), message: redeemErrorText(refusal, purchase: true)) }
|
||||
}
|
||||
@@ -332,6 +333,7 @@ private func storeReceipt(_ verification: VerificationResult<Transaction>) -> Ba
|
||||
return BadgeStoreReceipt(
|
||||
jws: verification.jwsRepresentation,
|
||||
productId: t.productID,
|
||||
transactionId: t.id,
|
||||
invoiceId: t.appAccountToken,
|
||||
signatureVerified: signatureVerified,
|
||||
transaction: t
|
||||
|
||||
+4
-5
@@ -159,9 +159,8 @@ object BadgeStore {
|
||||
// set when the first sweep has returned or failed, which is after every purchase the store holds was handed
|
||||
// to core - until then a purchase made while the app was not running is unknown
|
||||
private val reconciledOnce = mutableStateOf(false)
|
||||
// invoices of the purchases the user started this run, whose refusal is theirs to be told of;
|
||||
// read and written on the main thread only
|
||||
private val awaitedInvoices = mutableSetOf<String>()
|
||||
// purchases the user started this run, by token: the store is not relied on to echo the invoice back
|
||||
private val awaitedTransactions = mutableSetOf<String>()
|
||||
|
||||
fun purchaseState(userId: Long?): BadgePurchaseState? {
|
||||
if (!badgeStoreAvailable) return null
|
||||
@@ -247,7 +246,7 @@ object BadgeStore {
|
||||
try {
|
||||
val outcome = storePurchase(id, invoiceId)
|
||||
if (outcome is BadgePurchaseOutcome.Purchased) {
|
||||
withContext(Dispatchers.Main) { awaitedInvoices += invoiceId }
|
||||
withContext(Dispatchers.Main) { awaitedTransactions += outcome.receipt.token }
|
||||
handOver(outcome.receipt)
|
||||
}
|
||||
return outcome
|
||||
@@ -313,7 +312,7 @@ object BadgeStore {
|
||||
|
||||
private suspend fun settle(receipt: BadgeStoreReceipt, refusal: ChatError?) {
|
||||
finish(receipt)
|
||||
val awaited = withContext(Dispatchers.Main) { receipt.invoiceId?.let { awaitedInvoices.remove(it) } == true }
|
||||
val awaited = withContext(Dispatchers.Main) { awaitedTransactions.remove(receipt.token) }
|
||||
if (awaited && refusal != null) {
|
||||
AlertManager.shared.showAlertMsg(title = generalGetString(MR.strings.badges_purchase_error), text = chatModel.controller.redeemErrorText(refusal, purchase = true))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user