Bazaar: clear message when Bazaar is missing, old or not connected; reconnect before the next try

Android: buy() checks the Poolakey connection state and reconnects; BazaarNotFound / not supported / dynamic price
not supported become error "unavailable" (not_installed | old_bazaar | not_connected) instead of a generic failure.
assembleDebug green: com.tadrisino.math.app, versionCode 2, queries com.farsitel.bazaar. Not run on a phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-12 20:56:54 +03:30
parent 854149bb5d
commit 872e272ee1
3 changed files with 42 additions and 9 deletions

View File

@@ -30,9 +30,15 @@ import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import ir.cafebazaar.poolakey.Connection
import ir.cafebazaar.poolakey.ConnectionState
import ir.cafebazaar.poolakey.Payment
import ir.cafebazaar.poolakey.config.PaymentConfiguration
import ir.cafebazaar.poolakey.config.SecurityCheck
import ir.cafebazaar.poolakey.exception.BazaarNotFoundException
import ir.cafebazaar.poolakey.exception.BazaarNotSupportedException
import ir.cafebazaar.poolakey.exception.DynamicPriceNotSupportedException
import ir.cafebazaar.poolakey.exception.IAPNotSupportedException
import ir.cafebazaar.poolakey.exception.SubsNotSupportedException
import ir.cafebazaar.poolakey.request.PurchaseRequest
import org.json.JSONArray
import org.json.JSONObject
@@ -44,7 +50,7 @@ import org.json.JSONObject
* JS → اینجا TadrisinoAndroid.bazaarBuy(sku, orderId, dynamicPriceToken) (اشتراک)
* TadrisinoAndroid.bazaarPrices(skusJson) → رویدادِ «tadrisino:bazaar-prices»
* اینجا → JS window رویدادِ «tadrisino:bazaar» با { orderId, purchaseToken } یا
* { orderId, error: "canceled" | "failed", message }
* { orderId, error: "canceled" | "failed" | "unavailable", message }
*
* سمتِ سرور توکن را از خودِ بازار می‌سنجد (`POST /api/payment/bazaar/verify`)، پس این‌جا
* فقط واسطه است و به هیچ نتیجه‌ای اعتماد نمی‌کند.
@@ -209,6 +215,7 @@ class MainActivity : AppCompatActivity() {
val config = PaymentConfiguration(
localSecurityCheck = SecurityCheck.Enable(rsaPublicKey = BuildConfig.BAZAAR_RSA_KEY),
)
connection?.disconnect()
payment = Payment(context = this, config = config).also { p ->
connection = p.connect {
connectionSucceed { Log.i(TAG, "bazaar connected") }
@@ -225,8 +232,10 @@ class MainActivity : AppCompatActivity() {
*/
private fun buy(sku: String, orderId: String, dynamicPriceToken: String) {
val p = payment
if (p == null) {
send(orderId, error = "failed", message = "bazaar not connected")
if (p == null || connection?.getState() !is ConnectionState.Connected) {
// بازار نصب نیست، هنوز وصل نشده، یا اتصال افتاده؛ برای تلاشِ بعدی دوباره وصل می‌شویم.
connectToBazaar()
send(orderId, error = "unavailable", message = "not_connected")
return
}
val request = PurchaseRequest(
@@ -235,11 +244,27 @@ class MainActivity : AppCompatActivity() {
p.subscribeProduct(activityResultRegistry, request) {
purchaseSucceed { send(orderId, token = it.purchaseToken) }
purchaseCanceled { send(orderId, error = "canceled") }
purchaseFailed { recoverOwned(sku, orderId, it) }
failedToBeginFlow { recoverOwned(sku, orderId, it) }
purchaseFailed { failed(sku, orderId, it) }
failedToBeginFlow { failed(sku, orderId, it) }
}
}
/**
* بازارِ نصب‌نشده یا قدیمی (بی پشتیبانیِ اشتراک یا «تخفیف پویا») کاری است که کاربر باید بکند، نه خطای خرید؛
* فرانت برایش پیامِ جدا دارد. بقیه‌ی خطاها ممکن است خریدی باشند که پولش رفته، پس از بازار پرسیده می‌شوند.
*/
private fun failed(sku: String, orderId: String, cause: Throwable) {
val reason = when (cause) {
is BazaarNotFoundException -> "not_installed"
is BazaarNotSupportedException, is SubsNotSupportedException, is IAPNotSupportedException,
is DynamicPriceNotSupportedException -> "old_bazaar"
else -> null
}
Log.e(TAG, "bazaar purchase failed: ${reason ?: cause.javaClass.simpleName}", cause)
if (reason != null) send(orderId, error = "unavailable", message = reason)
else recoverOwned(sku, orderId, cause)
}
/**
* اشتراکِ همین محصول که پولش رفته ولی تأییدِ سرور نرسیده بود جلوی خریدِ دوباره را می‌گیرد. فرانت در تلاشِ
* بعدی همان سفارش را می‌فرستد؛ اگر اشتراکِ همان سفارش (payload) پیدا شد، توکنش برمی‌گردد تا سرور ثبتش کند.