From dec472dea9ad3b21c985fa4cebc750bc558be6ef Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Sat, 16 May 2026 01:51:22 +0300 Subject: [PATCH] Update: 2026-05-16 01:51:22 --- .../autoride/SubscriptionActivity.kt | 127 +++++-- .../com/jordanbot/autoride/api/BackendApi.kt | 27 +- .../main/res/layout/activity_subscription.xml | 349 +++++++++++------- backend/api/subscription/check_payment.php | 50 +++ backend/api/subscription/init_payment.php | 50 +++ backend/api/subscription/setup_cliq_db.php | 22 ++ backend/api/subscription/webhook_sms.php | 91 +++++ 7 files changed, 550 insertions(+), 166 deletions(-) create mode 100644 backend/api/subscription/check_payment.php create mode 100644 backend/api/subscription/init_payment.php create mode 100644 backend/api/subscription/setup_cliq_db.php create mode 100644 backend/api/subscription/webhook_sms.php diff --git a/app/src/main/java/com/jordanbot/autoride/SubscriptionActivity.kt b/app/src/main/java/com/jordanbot/autoride/SubscriptionActivity.kt index bd60a05..b58d963 100644 --- a/app/src/main/java/com/jordanbot/autoride/SubscriptionActivity.kt +++ b/app/src/main/java/com/jordanbot/autoride/SubscriptionActivity.kt @@ -1,37 +1,56 @@ package com.jordanbot.autoride import android.os.Bundle -import android.widget.Button +import android.view.View +import android.widget.LinearLayout +import android.widget.ProgressBar import android.widget.TextView import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope -import com.jordanbot.autoride.api.ActivateSubscriptionRequest import com.jordanbot.autoride.api.ApiClient +import com.jordanbot.autoride.api.CheckPaymentRequest +import com.jordanbot.autoride.api.InitPaymentRequest import com.jordanbot.autoride.subscription.SubscriptionManager import com.jordanbot.autoride.utils.DeviceUtils -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* class SubscriptionActivity : AppCompatActivity() { private lateinit var tvStatus: TextView - private lateinit var btnBasic: Button private lateinit var btnPro: Button + private lateinit var btnAnnual: Button + + // Payment Overlay UI + private lateinit var layoutOverlay: LinearLayout + private lateinit var tvRefCode: TextView + private lateinit var tvTimer: TextView + private lateinit var btnCancel: Button + + private var pollingJob: Job? = null + private var timerJob: Job? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_subscription) tvStatus = findViewById(R.id.tv_current_status) - btnBasic = findViewById(R.id.btn_subscribe_basic) btnPro = findViewById(R.id.btn_subscribe_pro) + btnAnnual = findViewById(R.id.btn_subscribe_annual) + + layoutOverlay = findViewById(R.id.layout_payment_overlay) + tvRefCode = findViewById(R.id.tv_ref_code) + tvTimer = findViewById(R.id.tv_timer) + btnCancel = findViewById(R.id.btn_cancel_payment) updateStatusUI() - btnBasic.setOnClickListener { activatePlan("basic") } - btnPro.setOnClickListener { activatePlan("pro") } + btnPro.setOnClickListener { startPaymentFlow("pro", 10.0) } + btnAnnual.setOnClickListener { startPaymentFlow("annual", 80.0) } + + btnCancel.setOnClickListener { + stopPaymentFlow() + } } private fun updateStatusUI() { @@ -40,39 +59,99 @@ class SubscriptionActivity : AppCompatActivity() { val today = SubscriptionManager.ridesToday val planText = when(plan) { - "basic" -> "أساسي ($limit طلب / يوم)" "pro" -> "احترافي (لا محدود)" "annual" -> "سنوي (لا محدود)" else -> "مجاني (1 طلب / يوم)" } - tvStatus.text = "الخطة الحالية: $planText\nاستهلاك اليوم: $today" + tvStatus.text = "الخطة الحالية: $planText\nاستهلاك اليوم: $today / $limit" } - private fun activatePlan(plan: String) { - // In a real app, integrate payment gateway here. - // For demonstration, we just call the API directly. + private fun startPaymentFlow(plan: String, amount: Double) { val fingerprint = DeviceUtils.getDeviceFingerprint(this) lifecycleScope.launch { try { val response = withContext(Dispatchers.IO) { - ApiClient.service.activateSubscription( - ActivateSubscriptionRequest(fingerprint, plan, "DEMO_REF_123") - ) + ApiClient.service.initPayment(InitPaymentRequest(fingerprint, plan, amount)) } - if (response.success) { - Toast.makeText(this@SubscriptionActivity, "تم تفعيل الاشتراك بنجاح!", Toast.LENGTH_SHORT).show() - // Re-check subscription to update local cache - SubscriptionManager.checkSubscription(this@SubscriptionActivity) - updateStatusUI() + if (response.success && response.reference_code != null) { + showPaymentOverlay(response.reference_code, amount) + startPolling(response.reference_code) } else { - Toast.makeText(this@SubscriptionActivity, "فشل تفعيل الاشتراك: ${response.message}", Toast.LENGTH_SHORT).show() + Toast.makeText(this@SubscriptionActivity, "فشل إنشاء طلب الدفع", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { - Toast.makeText(this@SubscriptionActivity, "حدث خطأ في الاتصال", Toast.LENGTH_SHORT).show() + Toast.makeText(this@SubscriptionActivity, "حدث خطأ في الاتصال بالسيرفر", Toast.LENGTH_SHORT).show() } } } + + private fun showPaymentOverlay(refCode: String, amount: Double) { + tvRefCode.text = refCode + layoutOverlay.visibility = View.VISIBLE + + // Start 10 minute timer + var secondsLeft = 600 + timerJob?.cancel() + timerJob = lifecycleScope.launch { + while (secondsLeft > 0) { + val mins = secondsLeft / 60 + val secs = secondsLeft % 60 + tvTimer.text = "ننتظر وصول الدفعة... (${String.format("%02d:%02d", mins, secs)})" + delay(1000) + secondsLeft-- + } + stopPaymentFlow() + Toast.makeText(this@SubscriptionActivity, "انتهى وقت طلب الدفع", Toast.LENGTH_LONG).show() + } + } + + private fun startPolling(refCode: String) { + val fingerprint = DeviceUtils.getDeviceFingerprint(this) + pollingJob?.cancel() + pollingJob = lifecycleScope.launch { + while (isActive) { + delay(5000) // Poll every 5 seconds + try { + val response = withContext(Dispatchers.IO) { + ApiClient.service.checkPayment(CheckPaymentRequest(refCode, fingerprint)) + } + + if (response.status == "paid") { + onPaymentSuccess() + break + } else if (response.status == "expired") { + stopPaymentFlow() + Toast.makeText(this@SubscriptionActivity, "انتهت صلاحية الطلب", Toast.LENGTH_SHORT).show() + break + } + } catch (e: Exception) { + // Ignore connection errors during polling + } + } + } + } + + private fun onPaymentSuccess() { + stopPaymentFlow() + Toast.makeText(this, "✅ تم تفعيل الاشتراك بنجاح!", Toast.LENGTH_LONG).show() + lifecycleScope.launch { + SubscriptionManager.checkSubscription(this@SubscriptionActivity) + updateStatusUI() + } + } + + private fun stopPaymentFlow() { + pollingJob?.cancel() + timerJob?.cancel() + layoutOverlay.visibility = View.GONE + } + + override fun onDestroy() { + super.onDestroy() + pollingJob?.cancel() + timerJob?.cancel() + } } diff --git a/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt b/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt index 5e1e5fd..ade6d19 100644 --- a/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt +++ b/app/src/main/java/com/jordanbot/autoride/api/BackendApi.kt @@ -67,11 +67,24 @@ data class ActivateSubscriptionRequest( ) @Keep -data class ActivateSubscriptionResponse( +data class InitPaymentRequest(val fingerprint: String, val plan: String, val amount: Double) + +@Keep +data class InitPaymentResponse( val success: Boolean, - val message: String?, - val plan: String?, - val expires_at: String? + val reference_code: String?, + val amount: Double?, + val cliq_alias: String?, + val expires_in_minutes: Int? +) + +@Keep +data class CheckPaymentRequest(val reference_code: String, val fingerprint: String) + +@Keep +data class CheckPaymentResponse( + val success: Boolean, + val status: String // pending, paid, expired ) interface BackendApiService { @@ -86,6 +99,12 @@ interface BackendApiService { @POST("api/subscription/activate.php") suspend fun activateSubscription(@Body request: ActivateSubscriptionRequest): ActivateSubscriptionResponse + + @POST("api/subscription/init_payment.php") + suspend fun initPayment(@Body request: InitPaymentRequest): InitPaymentResponse + + @POST("api/subscription/check_payment.php") + suspend fun checkPayment(@Body request: CheckPaymentRequest): CheckPaymentResponse } object ApiClient { diff --git a/app/src/main/res/layout/activity_subscription.xml b/app/src/main/res/layout/activity_subscription.xml index c5aabff..ee16da6 100644 --- a/app/src/main/res/layout/activity_subscription.xml +++ b/app/src/main/res/layout/activity_subscription.xml @@ -1,163 +1,236 @@ - + android:background="#121222"> - + + + + + + + + + + + + + + + + + + + + + +