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:
@@ -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) پیدا شد، توکنش برمیگردد تا سرور ثبتش کند.
|
||||
|
||||
@@ -15,7 +15,8 @@ import { store } from './storage';
|
||||
* و detailِ { prices: { sku: «۸۹۸,۰۰۰ تومان» } } یا { error }
|
||||
* اندروید → JS رویدادِ window به نامِ «tadrisino:bazaar» با detail:
|
||||
* { orderId, purchaseToken } خرید انجام شد
|
||||
* { orderId, error: 'canceled' | 'failed', message? }
|
||||
* { orderId, error: 'canceled' | 'failed' | 'unavailable', message? }
|
||||
* («unavailable»: بازار نصب نیست/وصل نشد یا قدیمی است؛ message: not_installed | old_bazaar | not_connected)
|
||||
*
|
||||
* `bazaarBuy` از APKهای پس از ۲۰۲۶-۱۰-۱۲ است. APKهای قبلی کلیدِ RSA نداشتند و Poolakey در آنها خاموش بود، پس از
|
||||
* بازار هیچ خریدی نکردهاند؛ در آنها کاشیِ بازار غیرفعال است و «اپ را بهروز کن» میگوید.
|
||||
@@ -39,10 +40,10 @@ declare global {
|
||||
|
||||
type BridgeResult =
|
||||
| { orderId: string; purchaseToken: string }
|
||||
| { orderId: string; error: 'canceled' | 'failed'; message?: string };
|
||||
| { orderId: string; error: 'canceled' | 'failed' | 'unavailable'; message?: string };
|
||||
|
||||
export class BazaarPurchaseError extends Error {
|
||||
constructor(readonly kind: 'canceled' | 'failed' | 'timeout', message = '') {
|
||||
constructor(readonly kind: 'canceled' | 'failed' | 'unavailable' | 'timeout', message = '') {
|
||||
super(message || kind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,14 @@ function CheckoutSheet({ plan, storePrices, onClose, onPaid }: { plan: Plan | nu
|
||||
const outcome = await payWithBazaar(plan);
|
||||
if (outcome) setDone(outcome);
|
||||
} catch (err) {
|
||||
setError(friendlyError(err, 'خرید از بازار تأیید نشد. اگه پولی کم شده، دوباره «پرداخت» رو بزن تا همون خرید ثبت بشه.'));
|
||||
if (err instanceof BazaarPurchaseError && err.kind === 'unavailable') {
|
||||
// کارِ کاربر است، نه خطای خرید: پولی جابهجا نشده.
|
||||
setError(err.message === 'old_bazaar'
|
||||
? 'کافهبازارِ گوشیت قدیمیه؛ از خودِ بازار بهروزش کن و دوباره بزن، یا با کارتِ بانکی پرداخت کن.'
|
||||
: 'کافهبازار روی گوشیت پیدا نشد یا وصل نشد. مطمئن شو نصبه و وارد حسابت شدی، بعد دوباره بزن؛ یا با کارتِ بانکی پرداخت کن.');
|
||||
} else {
|
||||
setError(friendlyError(err, 'خرید از بازار تأیید نشد. اگه پولی کم شده، دوباره «پرداخت» رو بزن تا همون خرید ثبت بشه.'));
|
||||
}
|
||||
}
|
||||
setBusy(false);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user