Installed APK sequence was system splash -> white WebView -> web splash -> home. The activity now holds the system splash (core-splashscreen) until the page calls TadrisinoAndroid.appReady(), after its first non-boot screen; released after 10s or on a main-frame load error so a dead site shows the WebView error, not an endless splash. Older APKs have no appReady and are unaffected. Debug build compiles; not run on a device. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
اپِ اندرویدِ تدریسینو (پوستهی WebView + پرداختِ بازار)
همان سایتِ math.tadrisino2.ir داخلِ یک WebView. تنها کدِ بومی پرداختِ درونبرنامهایِ
کافهبازار است (کتابخانهی Poolakey)، چون بازار برای اپی که در آن منتشر میشود پرداختِ
محصولِ دیجیتال را فقط از راهِ خودش میپذیرد. باقیِ اپ همان فرانتِ وب است و با دیپلویِ
فرانت بهروز میشود، بیآنکه نسخهی تازهی اپ لازم باشد.
ساخت و امضا با همتیمی است (MH-W90، m.hassanabadi) با روشِ امضای خودش (
my-release-key.keystore+ propertyهای Gradle)؛ تصمیمِ مالک این است که همان روش بماند. پیش از دست زدن به امضا از مالک بپرس و هرگز keystore را commit نکن.applicationIdواقعیcom.tadrisino.math.appاست (از APKِ ساختهشده با aapt2). هیچ خریدِ واقعیِ بازار هنوز آزموده نشده.
نسخهی بعدیِ APK — چه چیزی در کد هست و در اپِ نصبشده نیست
اینها در MainActivity.kt نوشته شدهاند (۱۴۰۵-۰۷، 468f5b2 و 737414f) ولی هیچکدام build نشدهاند؛ اپی که الان روی
گوشیهاست بدونِ آنهاست. فرانت با هر دو نسخه کار میکند (اگر پل نبود، راهِ قدیم را میرود).
| تغییر | چرا | بعد از build روی گوشی بسنج |
|---|---|---|
TadrisinoAndroid.vibrate(patternJson) با Vibratorِ گوشی |
navigator.vibrate در WebView همیشه کار نمیکند؛ مالک گفت اپ لرزش ندارد |
انتخابِ گزینهی آزمون، جوابِ درست/غلط، پایانِ جزیره و صفحهی «روزِ پیاپی» باید بلرزند؛ با خاموش کردنِ «صداها» در پروفایل نه |
mediaPlaybackRequiresUserGesture = false |
صداهای رابط بعد از پاسخِ سرور پخش میشوند، نه در لحظهی لمس | صدای جوابِ درست/غلط و پایانِ تمرین |
isPaymentPage + paysInApp() |
درگاهِ زرینپال داخلِ خودِ اپ باز میشود. اپِ فعلی آن را به مرورگرِ گوشی میدهد که Referer نمیفرستد و زرینپال بیReferer «دسترسی از این دامنه مجاز نمیباشد» میدهد؛ فعلاً فرانت از راهِ pay.html (با http://) دورش میزند |
«پرداخت» با کارتِ بانکی: صفحهی زرینپال و بانک داخلِ اپ، و بعد از پرداخت برگشت به اپ با پیامِ «پرداخت موفق بود» (PaymentResult). دکمهی برگشت وسطِ صفحهی بانک چه میکند را هم ببین |
پیشنهادهایی که هنوز نوشته نشدهاند:
- «برای خروج دوباره بزن»: دکمهی برگشتِ گوشی حالا قدمبهقدم در خودِ وب عقب میرود (
src/nav/Navigator.tsx) و از خانه یکراست بیرون میرود. اگر تأییدِ خروج خواستید، درhandleOnBackPressedوقتی!webView.canGoBack()یک Toast بدهید و فقط برگشتِ دوم در دو ثانیهfinish()کند. در وب شدنی نیست (کروم ورودیِ history بیلمس را رد میکند). versionCode/versionNameرا برای هر نسخه بالا ببرید؛ هنوز ۱ و 1.0.0 است.
قانونِ بازار: داخلِ اپِ بازار حالا پرداخت با درگاه هم پیشنهاد میشود (خواستِ مالک). بازار پرداختِ محتوای دیجیتال بیرون از پرداختِ درونبرنامهای را محدود میکند؛ پیش از انتشار در بازار این را با بازار یا مالک روشن کنید.
ساخت روی مکِ مالک: SDK در ~/Library/Android/sdk، JDK 17 (/opt/homebrew/opt/openjdk@17)، و چون Google Maven از
ایران بیVPN ۴۰۴ میدهد، ~/.gradle/init.d/google-mirror.gradle آینهی Aliyun را اضافه میکند؛ ساختِ اول ~۹ دقیقه.
پیش از build — سه چیز از پیشخانِ بازار (سنیور / حسابِ شرکت)
| چه | کجا گذاشته میشود | وضعیت |
|---|---|---|
| اسمِ پکیج | applicationId در app/build.gradle.kts |
com.tadrisino.math.app (APKِ ساختهشده)؛ ثبتش در پیشخان تأیید نشده |
| کلیدِ RSAِ عمومیِ اپ | BAZAAR_RSA_KEY در app/build.gradle.kts |
ندارد؛ بدونِ آن خرید شروع نمیشود |
| SKUِ هر بسته (محصولِ مصرفشدنی) | پنلِ ادمینِ بکاند: /admin/subscription/plan/ ← «Bazaar sku» |
هیچکدام تعریف نشده |
قیمتِ هر محصول در پیشخانِ بازار باید با قیمتِ همان بسته در ادمین یکی باشد؛ سرور مبلغ را از بازار نمیخواند.
build
- Android Studio ← Open ← همین پوشهی
android/. Studio خودش Gradle wrapper را میسازد. Build ← Generate Signed App Bundle / APK، با کلیدِ امضای شرکت. کلیدِ امضا را گم نکنید: بدونِ همان کلید هیچ نسخهی بعدی روی بازار بهروزرسانی نمیشود.- نصب روی گوشیای که کافهبازار دارد و با حسابِ تست وارد شده.
جریانِ خرید (برای عیبیابی)
فرانت POST /api/subscription/purchase {planId, paymentMethod:"bazaar"} → orderId, bazaarSku
فرانت TadrisinoAndroid.bazaarPurchase(sku, orderId)
اپ Poolakey.purchaseProduct(sku, payload=orderId) → purchaseToken
اپ رویدادِ «tadrisino:bazaar» {orderId, purchaseToken}
فرانت POST /api/payment/bazaar/verify {orderId, purchaseToken} ← سرور از بازار میپرسد
فرانت TadrisinoAndroid.bazaarConsume(token) ← تا همان بسته دوباره خریدنی باشد
اگر پول رفت ولی تأییدِ سرور نرسید، کاربر دوباره «پرداخت» میزند: فرانت همان سفارش را میفرستد
و اپ توکنِ ماندهٔ همان سفارش را از getPurchasedProducts پیدا میکند.
قرارداد سمتِ وب: src/lib/bazaar.ts. سمتِ سرور: payment/bazaar.py و docs/operations.mdِ بکاند.