Files
MathApp_frontend/android
AliReza 5327fed1dd Android: reproducible signed build (keystore and Bazaar RSA key read from outside the repo), Gradle wrapper, build-apk.sh
First time the project has been built: debug and release both produce an APK.
Release refuses to build without keystore or RSA key.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 14:05:48 +03:30
..

اپِ اندرویدِ تدریسینو (پوسته‌ی WebView + پرداختِ بازار)

همان سایتِ math.tadrisino2.ir داخلِ یک WebView. تنها کدِ بومی پرداختِ درون‌برنامه‌ایِ کافه‌بازار است (کتابخانه‌ی Poolakey)، چون بازار برای اپی که در آن منتشر می‌شود پرداختِ محصولِ دیجیتال را فقط از راهِ خودش می‌پذیرد. باقیِ اپ همان فرانتِ وب است و با دیپلویِ فرانت به‌روز می‌شود، بی‌آن‌که نسخه‌ی تازه‌ی اپ لازم باشد.

⚠️ APIِ Poolakey از README خودِ کتابخانه است و خرید روی گوشی هنوز آزموده نشده.

پیش از build — سه چیز از پیشخانِ بازار (سنیور / حسابِ شرکت)

چه کجا گذاشته می‌شود وضعیت
اسمِ پکیج applicationId در app/build.gradle.kts com.tadrisino.app حدس است؛ تأیید نشده
کلیدِ RSAِ عمومیِ اپ bazaarRsaKey در فایلِ تنظیماتِ بیرون از ریپو (پایین) ندارد؛ بدونِ آن build نوعِ release نمی‌شود
SKUِ هر بسته (محصولِ مصرف‌شدنی) پنلِ ادمینِ بک‌اند: /admin/subscription/plan/ ← «Bazaar sku» هیچ‌کدام تعریف نشده

قیمتِ هر محصول در پیشخانِ بازار باید با قیمتِ همان بسته در ادمین یکی باشد؛ سرور مبلغ را از بازار نمی‌خواند.

تنظیماتِ بیرون از ریپو

keystore و کلیدِ RSA هرگز داخلِ ریپو نمی‌آیند. یک فایل با دسترسیِ ۶۰۰ بسازید: ~/.tadrisino/android.properties (یا مسیرِ دلخواه در TADRISINO_ANDROID_CONFIG):

storeFile=/مسیر/کامل/tadrisino-release.jks
storePassword=...
keyAlias=...
keyPassword=...
bazaarRsaKey=<کلیدِ RSA از پیشخانِ بازار، یک خط>
مقدار از کجا اگر نبود
storeFile تا keyPassword keystoreِ شرکت. همان فایلِ APKِ فعلی را امضا کرده؛ نسخه‌ی تازه با کلیدِ دیگر روی بازار آپدیت نمی‌شود. دو نسخه‌ی پشتیبان در دو جای جدا build نوعِ release با پیامِ روشن می‌ایستد
bazaarRsaKey پیشخان ← اپ ← پرداخت درون‌برنامه‌ای build نوعِ release می‌ایستد (نه اپِ بی‌خرید)

build

نیازها (یک بار): JDK 17 (brew install openjdk@17) و Android SDK با platforms;android-34 و build-tools;34.0.0 در ~/Library/Android/sdk.

cd android && ./build-apk.sh

خروجی android/build-out/Tadrisino-<نسخه>-<versionCode>.apk است. پیش از هر انتشار versionCode را در app/build.gradle.kts بالا ببرید (بازار نسخه‌ی با شماره‌ی تکراری را نمی‌پذیرد). اسکریپت امضا را می‌سنجد و اثرانگشتِ گواهی را چاپ می‌کند؛ باید با APKِ قبلی یکی باشد. APKِ آزمایشی بی‌نیاز از کلید: ./gradlew assembleDebug.

جریانِ خرید (برای عیب‌یابی)

فرانت  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ِ بک‌اند.