Files
soroush.asadi cfed2950b2 Add ZarinPal sandbox payments for buying coins (config-driven merchant)
- ZarinpalService (request/verify) + /api/coins/pay/request (JWT) and
  /api/coins/pay/callback (verify → credit via ProfileService.BuyCoins → redirect
  back with ?pay=success); merchant id from config (sandbox default)
- Client buyCoins (live) returns the StartPay redirect URL; BuyCoinsScreen
  redirects; page.tsx handles the ?pay return (notify + refresh)
- Verified: sandbox request returns a real StartPay URL
- Documented Cafe Bazaar (Poolakey) / Myket IAB as the required store payment path

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 17:59:30 +03:30

3.4 KiB

برگ وسط — Android (Capacitor) build

The app is packaged with Capacitor for Cafe Bazaar / Myket.

Layout

  • next.config.tsoutput: "export" (static site in out/)
  • capacitor.config.ts → appId com.bargevasat.app, appName «برگ وسط», webDir out
  • android/ → generated native project (committed; build outputs are git-ignored)
  • Hardware back button handled by src/components/CapacitorBack.tsx (@capacitor/app)

Build the web + sync native

npm run cap:sync          # next build (export) + cap sync android

Build the APK

npm run android:apk       # → android/app/build/outputs/apk/debug/app-debug.apk
# or open in Android Studio:
npm run android:open

Point the app at the live server at build time: NEXT_PUBLIC_USE_SERVER=1 NEXT_PUBLIC_SERVER_URL=https://api.example.com npm run cap:sync

⚠️ Maven mirror (Iran) — VERIFIED WORKING

Gradle pulls the Android Gradle Plugin from dl.google.com + Maven Central, blocked in Iran. The Myket mirror proxies both — at the root (no /maven2):

https://maven.myket.ir

Copy android/gradle-mirror.init.gradle.exampleandroid/gradle-mirror.init.gradle, then build with the init script. (The gradlew.bat wrapper has a CLASSPATH quirk on this setup, so invoke the wrapper jar with java directly):

cd android
java -classpath gradle\wrapper\gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain \
  assembleDebug --init-script gradle-mirror.init.gradle --no-daemon
# → app/build/outputs/apk/debug/app-debug.apk   (≈4.5 MB)

The init script also pins build-tools 36.0.0 and Java 17 compatibility, which are the versions installed here (Capacitor 8 defaults to build-tools 35 / JDK 21). On a CI box with JDK 21 + build-tools 35 you can drop those overrides.

For Nexus instead of Myket directly: create a maven2 (proxy) repo with Remote storage URL https://maven.myket.ir, add it to a maven2 (group), and point MIRROR at the group URL.

💳 Payments — ZarinPal (web) vs store billing (Android)

  • Web / PWA: buying coins uses ZarinPal (sandbox now). Flow: POST /api/coins/pay/request → redirect to StartPay → ZarinPal → GET /api/coins/pay/callback (server verifies + credits) → back to the app with ?pay=success. Merchant id is config-driven (Zarinpal:MerchantId, swap in the admin panel / appsettings; Sandbox: true).
  • Cafe Bazaar / Myket (APK): app stores in Iran require their own in-app billing — do NOT use ZarinPal inside the store build. Use:
    • Cafe Bazaar: Poolakey (ir.cafebazaar.poolakey) — define in-app products in the Bazaar panel.
    • Myket: Myket IAB SDK — define products in the Myket panel. Wire a Capacitor plugin that detects the store build and routes buyCoins to the store SDK; verify the purchase token on the server, then credit coins via the same ProfileService.BuyCoins. (TODO — needs store accounts + product SKUs.)

Release (Cafe Bazaar / Myket)

  1. Generate a keystore: keytool -genkey -v -keystore bargevasat.keystore -alias bargevasat -keyalg RSA -keysize 2048 -validity 10000
  2. Configure signing in android/app/build.gradle (release signingConfig).
  3. gradlew.bat assembleRelease (or bundleRelease for AAB) → upload to Cafe Bazaar / Myket.
  4. App icon/splash: replace android/app/src/main/res/** (use @capacitor/assets to generate).