Update: 2026-05-16 01:51:22
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,163 +1,236 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#1A1A2E"
|
||||
android:fillViewport="true">
|
||||
android:background="#121222">
|
||||
|
||||
<LinearLayout
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="💎 باقات التميّز"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="28sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="سرّع عملك وضاعف دخلك اليوم"
|
||||
android:textColor="#AAAAAA"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- Current Status -->
|
||||
<TextView
|
||||
android:id="@+id/tv_current_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:background="@drawable/bg_card"
|
||||
android:padding="12dp"
|
||||
android:text="الحالة: جاري جلب البيانات..."
|
||||
android:textAlignment="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<!-- Pro Plan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_card"
|
||||
android:elevation="4dp"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="الخطة الاحترافية"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="10.00 JOD / شهر"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginVertical="12dp"
|
||||
android:background="#33FFFFFF" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="✅ طلبات غير محدودة (Unlimited)\n✅ أولوية قصوى في القبول\n✅ دعم جميع التطبيقات\n✅ فلاتر السعر والمسافة المتقدمة"
|
||||
android:textColor="#CCCCCC"
|
||||
android:textSize="13sp"
|
||||
android:lineSpacingExtra="4dp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_subscribe_pro"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="54dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_button_primary"
|
||||
android:text="تفعيل عبر CliQ"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Annual Plan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="الخطة السنوية (الأكثر توفيراً)"
|
||||
android:textColor="#FFD700"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="80.00 JOD / سنة"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:text="وفر 40 دينار سنوياً مع تفعيل دائم\nشامل جميع التحديثات القادمة"
|
||||
android:textColor="#AAAAAA"
|
||||
android:textSize="13sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_subscribe_annual"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="54dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_button"
|
||||
android:text="تفعيل سنوي عبر CliQ"
|
||||
android:textColor="#FFFFFF" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Payment Overlay (Hidden by default) -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_payment_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#F2121222"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
android:padding="32dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="💳 الاشتراكات"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="32sp"
|
||||
android:text="انتظار الدفع عبر CliQ"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="32dp"
|
||||
android:text="اختر الخطة المناسبة لعملك"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="يرجى إرسال المبلغ إلى الاسم المستعار التالي:"
|
||||
android:textColor="#AAAAAA"
|
||||
android:textSize="16sp" />
|
||||
|
||||
android:textAlignment="center" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_current_status"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/tv_cliq_alias"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:text="الخطة الحالية: مجاني (1 طلب / يوم)"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="JordanBot"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="22sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="24dp"
|
||||
android:text="الرقم المرجعي (هام جداً):"
|
||||
android:textColor="#FFFFFF" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_ref_code"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#3300D4AA"
|
||||
android:padding="12dp"
|
||||
android:text="JB-XXXXXX"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="32sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="8dp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="⚠️ يجب كتابة الرقم أعلاه في خانة الملاحظات داخل تطبيق البنك لضمان التفعيل الآلي."
|
||||
android:textColor="#FF5252"
|
||||
android:textAlignment="center"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="16sp" />
|
||||
android:textSize="12sp" />
|
||||
|
||||
<!-- Free Plan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<ProgressBar
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="32dp"
|
||||
android:indeterminateTint="#00D4AA" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_timer"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="ننتظر وصول الدفعة... (10:00)"
|
||||
android:textColor="#AAAAAA" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="المجانية"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0 JOD"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="• طلب واحد يومياً\n• تجربة الأساسيات"
|
||||
android:textColor="#AAAAAA"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Basic Plan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
<Button
|
||||
android:id="@+id/btn_cancel_payment"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="الأساسية"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="1 JOD / شهر"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="• 10 طلبات يومياً\n• إحصائيات أساسية\n• فلاتر متقدمة"
|
||||
android:textColor="#AAAAAA"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_subscribe_basic"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:backgroundTint="#00D4AA"
|
||||
android:textColor="#FFFFFF"
|
||||
android:text="اشترك الآن" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Pro Plan -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_card"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="الاحترافية"
|
||||
android:textColor="#FFFFFF"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="2.5 JOD / شهر"
|
||||
android:textColor="#00D4AA"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="• طلبات غير محدودة\n• أولوية القبول\n• دعم فني مخصص"
|
||||
android:textColor="#AAAAAA"
|
||||
android:textSize="14sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_subscribe_pro"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:backgroundTint="#FF9800"
|
||||
android:textColor="#FFFFFF"
|
||||
android:text="اشترك الآن" />
|
||||
</LinearLayout>
|
||||
android:layout_marginTop="32dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:text="إلغاء الطلب"
|
||||
android:textColor="#888888" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
50
backend/api/subscription/check_payment.php
Normal file
50
backend/api/subscription/check_payment.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../config/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Method Not Allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$referenceCode = $input['reference_code'] ?? null;
|
||||
$fingerprint = $input['fingerprint'] ?? null;
|
||||
|
||||
if (!$referenceCode || !$fingerprint) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing reference_code or fingerprint']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT status FROM cliq_payments WHERE reference_code = :ref AND fingerprint = :fingerprint LIMIT 1");
|
||||
$stmt->execute([':ref' => $referenceCode, ':fingerprint' => $fingerprint]);
|
||||
$payment = $stmt->fetch();
|
||||
|
||||
if ($payment) {
|
||||
// If it's still pending but older than 15 minutes, mark it as expired
|
||||
if ($payment['status'] === 'pending') {
|
||||
$stmtDate = $pdo->prepare("UPDATE cliq_payments SET status = 'expired' WHERE reference_code = :ref AND created_at < NOW() - INTERVAL 15 MINUTE");
|
||||
$stmtDate->execute([':ref' => $referenceCode]);
|
||||
|
||||
// Re-fetch if we just expired it
|
||||
if ($stmtDate->rowCount() > 0) {
|
||||
$payment['status'] = 'expired';
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'status' => $payment['status'] // 'pending', 'paid', or 'expired'
|
||||
]);
|
||||
} else {
|
||||
http_response_code(404);
|
||||
echo json_encode(['success' => false, 'message' => 'Payment not found']);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
50
backend/api/subscription/init_payment.php
Normal file
50
backend/api/subscription/init_payment.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../config/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Method Not Allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$fingerprint = $input['fingerprint'] ?? null;
|
||||
$plan = $input['plan'] ?? null;
|
||||
$amount = $input['amount'] ?? null;
|
||||
|
||||
if (!$fingerprint || !$plan || !$amount) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing required fields']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Expire old pending payments for this user to avoid confusion
|
||||
try {
|
||||
$stmt = $pdo->prepare("UPDATE cliq_payments SET status = 'expired' WHERE fingerprint = :fingerprint AND status = 'pending'");
|
||||
$stmt->execute([':fingerprint' => $fingerprint]);
|
||||
|
||||
// Generate a unique 6-character reference code (e.g. JB-1A2B3C)
|
||||
$refCode = 'JB-' . strtoupper(substr(md5(uniqid(rand(), true)), 0, 6));
|
||||
|
||||
// Insert new pending payment
|
||||
$stmt = $pdo->prepare("INSERT INTO cliq_payments (fingerprint, reference_code, amount, plan, status) VALUES (:fingerprint, :refCode, :amount, :plan, 'pending')");
|
||||
$stmt->execute([
|
||||
':fingerprint' => $fingerprint,
|
||||
':refCode' => $refCode,
|
||||
':amount' => $amount,
|
||||
':plan' => $plan
|
||||
]);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'reference_code' => $refCode,
|
||||
'amount' => $amount,
|
||||
'cliq_alias' => 'JordanBot', // Change this to the actual CliQ alias
|
||||
'expires_in_minutes' => 10
|
||||
]);
|
||||
} catch (PDOException $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
22
backend/api/subscription/setup_cliq_db.php
Normal file
22
backend/api/subscription/setup_cliq_db.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../config/db.php';
|
||||
|
||||
try {
|
||||
$sql = "CREATE TABLE IF NOT EXISTS cliq_payments (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
fingerprint VARCHAR(255) NOT NULL,
|
||||
reference_code VARCHAR(20) NOT NULL UNIQUE,
|
||||
amount DECIMAL(10,2) NOT NULL,
|
||||
plan VARCHAR(50) NOT NULL,
|
||||
status ENUM('pending', 'paid', 'expired') DEFAULT 'pending',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX (reference_code),
|
||||
INDEX (fingerprint)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
|
||||
|
||||
$pdo->exec($sql);
|
||||
echo "Table 'cliq_payments' created or already exists successfully.";
|
||||
} catch (PDOException $e) {
|
||||
echo "Error creating table: " . $e->getMessage();
|
||||
}
|
||||
91
backend/api/subscription/webhook_sms.php
Normal file
91
backend/api/subscription/webhook_sms.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../config/db.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// This webhook is called by your SMS Bot application
|
||||
// Expected fields: sender (e.g., Arab Bank), message (SMS text), timestamp
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$message = $input['message'] ?? '';
|
||||
$sender = $input['sender'] ?? '';
|
||||
|
||||
if (empty($message)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Empty message']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 1. Log the incoming SMS for debugging
|
||||
error_log("JordanBot SMS Received: Sender: [$sender], Content: [$message]");
|
||||
|
||||
// 2. Extract Reference Code (Pattern: JB-XXXXXX)
|
||||
// Matches JB- followed by 6 alphanumeric characters
|
||||
preg_match('/JB-([A-Z0-9]{6})/', strtoupper($message), $matches);
|
||||
$refCode = isset($matches[0]) ? $matches[0] : null;
|
||||
|
||||
// 3. Extract Amount (Pattern: finds decimal numbers)
|
||||
// Note: Jordan uses 'JOD' or 'دينار'
|
||||
preg_match('/([0-9]+(\.[0-9]{2})?)/', $message, $amtMatches);
|
||||
$amountReceived = isset($amtMatches[0]) ? floatval($amtMatches[0]) : 0;
|
||||
|
||||
if (!$refCode) {
|
||||
echo json_encode(['success' => false, 'message' => 'No Reference Code found in SMS']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. Find the pending payment
|
||||
$stmt = $pdo->prepare("SELECT * FROM cliq_payments WHERE reference_code = :ref AND status = 'pending' LIMIT 1");
|
||||
$stmt->execute([':ref' => $refCode]);
|
||||
$payment = $stmt->fetch();
|
||||
|
||||
if ($payment) {
|
||||
// Optional: Verify amount match (allowing for minor differences or currency symbols)
|
||||
if (abs($payment['amount'] - $amountReceived) > 0.05) {
|
||||
error_log("JordanBot: Amount mismatch for $refCode. Expected: {$payment['amount']}, Received: $amountReceived");
|
||||
// We can still proceed or mark for manual review
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 5. Update payment status
|
||||
$stmt = $pdo->prepare("UPDATE cliq_payments SET status = 'paid' WHERE id = :id");
|
||||
$stmt->execute([':id' => $payment['id']]);
|
||||
|
||||
// 6. Activate/Extend Subscription
|
||||
$fingerprint = $payment['fingerprint'];
|
||||
$plan = $payment['plan'];
|
||||
|
||||
// Calculate expiration (e.g. basic=30 days, annual=365 days)
|
||||
$days = ($plan === 'annual') ? 365 : 30;
|
||||
$expiresAt = date('Y-m-d H:i:s', strtotime("+$days days"));
|
||||
|
||||
// Check if user already has a subscription to extend it
|
||||
$stmtCheck = $pdo->prepare("SELECT id, expires_at FROM subscriptions WHERE fingerprint = :fingerprint AND is_active = 1 LIMIT 1");
|
||||
$stmtCheck->execute([':fingerprint' => $fingerprint]);
|
||||
$existing = $stmtCheck->fetch();
|
||||
|
||||
if ($existing) {
|
||||
// Extend existing
|
||||
$newExpiry = date('Y-m-d H:i:s', strtotime($existing['expires_at'] . " +$days days"));
|
||||
$stmtUpdate = $pdo->prepare("UPDATE subscriptions SET expires_at = :expiry, plan = :plan WHERE id = :id");
|
||||
$stmtUpdate->execute([':expiry' => $newExpiry, ':plan' => $plan, ':id' => $existing['id']]);
|
||||
} else {
|
||||
// Create new
|
||||
$stmtInsert = $pdo->prepare("INSERT INTO subscriptions (fingerprint, plan, expires_at, is_active) VALUES (:fingerprint, :plan, :expiry, 1)");
|
||||
$stmtInsert->execute([':fingerprint' => $fingerprint, ':plan' => $plan, ':expiry' => $expiresAt]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
error_log("JordanBot: Subscription activated for $fingerprint via $refCode ($plan)");
|
||||
echo json_encode(['success' => true, 'message' => "Subscription activated for $refCode"]);
|
||||
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Reference code not found or already processed']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
Reference in New Issue
Block a user