forked from LibreMV/GridFlow
319 lines
13 KiB
Kotlin
319 lines
13 KiB
Kotlin
package sh.sar.gridflow
|
|
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.os.Bundle
|
|
import android.util.Log
|
|
import android.view.View
|
|
import android.widget.Toast
|
|
import androidx.appcompat.app.AppCompatActivity
|
|
import androidx.appcompat.app.AppCompatDelegate
|
|
import androidx.lifecycle.lifecycleScope
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import kotlinx.coroutines.launch
|
|
import sh.sar.gridflow.data.OutstandingBill
|
|
import sh.sar.gridflow.data.PaymentDetails
|
|
import sh.sar.gridflow.data.PaymentGateway
|
|
import sh.sar.gridflow.data.PaymentItem
|
|
import sh.sar.gridflow.data.PaymentRequest
|
|
import sh.sar.gridflow.databinding.ActivityPaymentReviewBinding
|
|
import sh.sar.gridflow.network.ApiResult
|
|
import sh.sar.gridflow.network.FenakaApiService
|
|
import sh.sar.gridflow.ui.payment.BillSummaryAdapter
|
|
import sh.sar.gridflow.ui.payment.PaymentConfirmationDialog
|
|
import sh.sar.gridflow.ui.payment.PaymentMethodAdapter
|
|
import sh.sar.gridflow.utils.SecureStorage
|
|
|
|
class PaymentReviewActivity : AppCompatActivity() {
|
|
|
|
private lateinit var binding: ActivityPaymentReviewBinding
|
|
private lateinit var secureStorage: SecureStorage
|
|
private lateinit var apiService: FenakaApiService
|
|
private lateinit var billSummaryAdapter: BillSummaryAdapter
|
|
private lateinit var paymentMethodAdapter: PaymentMethodAdapter
|
|
|
|
private var outstandingBills: List<OutstandingBill> = emptyList()
|
|
private var isSingleBillMode: Boolean = false
|
|
private var singleBillData: SingleBillData? = null
|
|
|
|
data class SingleBillData(
|
|
val billId: Long,
|
|
val amount: String,
|
|
val billNumber: String,
|
|
val subscriptionId: Long
|
|
)
|
|
|
|
companion object {
|
|
private const val TAG = "PaymentReviewActivity"
|
|
}
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
|
|
// Force system theme (follows device dark mode setting)
|
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
|
|
|
binding = ActivityPaymentReviewBinding.inflate(layoutInflater)
|
|
setContentView(binding.root)
|
|
|
|
secureStorage = SecureStorage(this)
|
|
apiService = FenakaApiService()
|
|
|
|
// Check if this is single bill mode
|
|
checkForSingleBillMode()
|
|
|
|
setupViews()
|
|
loadData()
|
|
}
|
|
|
|
private fun checkForSingleBillMode() {
|
|
val billId = intent.getLongExtra("SINGLE_BILL_ID", -1L)
|
|
if (billId != -1L) {
|
|
isSingleBillMode = true
|
|
singleBillData = SingleBillData(
|
|
billId = billId,
|
|
amount = intent.getStringExtra("SINGLE_BILL_AMOUNT") ?: "0.00",
|
|
billNumber = intent.getStringExtra("SINGLE_BILL_NUMBER") ?: "",
|
|
subscriptionId = intent.getLongExtra("SINGLE_BILL_SUBSCRIPTION_ID", -1L)
|
|
)
|
|
Log.d(TAG, "Single bill mode activated for bill: ${singleBillData?.billNumber}")
|
|
}
|
|
}
|
|
|
|
private fun setupViews() {
|
|
// Setup bills RecyclerView
|
|
billSummaryAdapter = BillSummaryAdapter()
|
|
binding.recyclerBills.apply {
|
|
layoutManager = LinearLayoutManager(this@PaymentReviewActivity)
|
|
adapter = billSummaryAdapter
|
|
}
|
|
|
|
// Setup payment methods RecyclerView
|
|
paymentMethodAdapter = PaymentMethodAdapter { paymentMethod ->
|
|
onPaymentMethodSelected(paymentMethod)
|
|
}
|
|
binding.recyclerPaymentMethods.apply {
|
|
layoutManager = LinearLayoutManager(this@PaymentReviewActivity)
|
|
adapter = paymentMethodAdapter
|
|
}
|
|
}
|
|
|
|
private fun loadData() {
|
|
val cookie = secureStorage.getCookie()
|
|
if (cookie == null) {
|
|
Toast.makeText(this, "Authentication required", Toast.LENGTH_SHORT).show()
|
|
finish()
|
|
return
|
|
}
|
|
|
|
if (isSingleBillMode) {
|
|
loadSingleBillMode(cookie)
|
|
} else {
|
|
loadOutstandingBillsMode(cookie)
|
|
}
|
|
}
|
|
|
|
private fun loadSingleBillMode(cookie: String) {
|
|
singleBillData?.let { billData ->
|
|
// Create fake OutstandingBill for single bill mode
|
|
val fakeOutstandingBill = OutstandingBill(
|
|
id = billData.billId,
|
|
branchName = "",
|
|
subscriptionId = billData.subscriptionId,
|
|
subscriptionStatus = "active",
|
|
subscriptionNumber = billData.billNumber,
|
|
outstandingBillCount = "1",
|
|
billAmount = billData.amount,
|
|
paidAmount = "0",
|
|
outstandingAmount = billData.amount
|
|
)
|
|
outstandingBills = listOf(fakeOutstandingBill)
|
|
updateBillsUI()
|
|
}
|
|
|
|
showLoading(true)
|
|
lifecycleScope.launch {
|
|
// Load payment methods
|
|
when (val gatewaysResult = apiService.getPaymentGateways("connect.sid=$cookie")) {
|
|
is ApiResult.Success -> {
|
|
paymentMethodAdapter.updatePaymentMethods(gatewaysResult.data)
|
|
showLoading(false)
|
|
}
|
|
is ApiResult.Error -> {
|
|
Log.e(TAG, "Failed to load payment gateways: ${gatewaysResult.message}")
|
|
showLoading(false)
|
|
Toast.makeText(this@PaymentReviewActivity, "Failed to load payment methods", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun loadOutstandingBillsMode(cookie: String) {
|
|
showLoading(true)
|
|
|
|
lifecycleScope.launch {
|
|
// Load outstanding bills first
|
|
when (val billsResult = apiService.getOutstandingBills("connect.sid=$cookie")) {
|
|
is ApiResult.Success -> {
|
|
outstandingBills = billsResult.data
|
|
updateBillsUI()
|
|
|
|
// Then load payment methods
|
|
when (val gatewaysResult = apiService.getPaymentGateways("connect.sid=$cookie")) {
|
|
is ApiResult.Success -> {
|
|
paymentMethodAdapter.updatePaymentMethods(gatewaysResult.data)
|
|
showLoading(false)
|
|
}
|
|
is ApiResult.Error -> {
|
|
Log.e(TAG, "Failed to load payment gateways: ${gatewaysResult.message}")
|
|
showLoading(false)
|
|
Toast.makeText(this@PaymentReviewActivity, "Failed to load payment methods", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
is ApiResult.Error -> {
|
|
Log.e(TAG, "Failed to load outstanding bills: ${billsResult.message}")
|
|
showLoading(false)
|
|
Toast.makeText(this@PaymentReviewActivity, "Failed to load bills", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateBillsUI() {
|
|
billSummaryAdapter.updateBills(outstandingBills)
|
|
|
|
// Calculate total bills count
|
|
val totalBillCount = outstandingBills.sumOf { bill ->
|
|
try {
|
|
bill.outstandingBillCount.toInt()
|
|
} catch (e: NumberFormatException) {
|
|
0
|
|
}
|
|
}
|
|
|
|
// Calculate total amount
|
|
val totalAmount = outstandingBills.sumOf { bill ->
|
|
try {
|
|
bill.outstandingAmount.toDouble()
|
|
} catch (e: NumberFormatException) {
|
|
0.0
|
|
}
|
|
}
|
|
|
|
binding.textBillCount.text = "You are paying a total of $totalBillCount bills"
|
|
binding.textTotalAmount.text = "MVR ${String.format("%.2f", totalAmount)}"
|
|
}
|
|
|
|
private fun showLoading(show: Boolean) {
|
|
binding.layoutLoading.visibility = if (show) View.VISIBLE else View.GONE
|
|
binding.recyclerPaymentMethods.visibility = if (show) View.GONE else View.VISIBLE
|
|
}
|
|
|
|
private fun onPaymentMethodSelected(paymentMethod: PaymentGateway) {
|
|
Log.d(TAG, "Payment method selected: ${paymentMethod.name} (${paymentMethod.code})")
|
|
|
|
// Show payment confirmation dialog
|
|
PaymentConfirmationDialog.show(
|
|
context = this,
|
|
paymentGateway = paymentMethod,
|
|
onContinue = { dialog ->
|
|
if (paymentMethod.code == "bml") {
|
|
processBmlPayment(dialog)
|
|
} else {
|
|
Toast.makeText(this, "Not yet implemented.. use BML", Toast.LENGTH_SHORT).show()
|
|
dialog.dismiss()
|
|
}
|
|
},
|
|
onCancel = {
|
|
Log.d(TAG, "Payment cancelled by user")
|
|
}
|
|
)
|
|
}
|
|
|
|
private fun processBmlPayment(dialog: PaymentConfirmationDialog) {
|
|
val cookie = secureStorage.getCookie()
|
|
if (cookie == null) {
|
|
Toast.makeText(this, "Authentication required", Toast.LENGTH_SHORT).show()
|
|
dialog.dismiss()
|
|
return
|
|
}
|
|
|
|
Log.d(TAG, "Processing BML payment...")
|
|
dialog.showLoading(true)
|
|
|
|
lifecycleScope.launch {
|
|
try {
|
|
// Create payment items from outstanding bills
|
|
val paymentItems = outstandingBills.map { bill ->
|
|
PaymentItem(
|
|
id = bill.subscriptionId,
|
|
type = "subscription",
|
|
amount = bill.outstandingAmount.toDoubleOrNull() ?: 0.0
|
|
)
|
|
}
|
|
|
|
val paymentRequest = PaymentRequest(
|
|
gateway = "bml",
|
|
details = PaymentDetails(items = paymentItems)
|
|
)
|
|
|
|
// Step 1: Initiate payment
|
|
when (val paymentResult = apiService.initiatePayment(paymentRequest, "connect.sid=$cookie")) {
|
|
is ApiResult.Success -> {
|
|
val paymentResponse = paymentResult.data
|
|
Log.d(TAG, "Payment initiated successfully. ID: ${paymentResponse.id}")
|
|
|
|
// Calculate total amount
|
|
val totalAmount = paymentItems.sumOf { it.amount }
|
|
|
|
// Step 2: Get redirect URL
|
|
when (val redirectResult = apiService.getPaymentRedirectUrl(
|
|
gateway = "bml",
|
|
reference = paymentResponse.id,
|
|
amount = totalAmount,
|
|
cookie = "connect.sid=$cookie"
|
|
)) {
|
|
is ApiResult.Success -> {
|
|
val redirectUrl = redirectResult.data
|
|
Log.d(TAG, "Got redirect URL: $redirectUrl")
|
|
|
|
// Open browser with redirect URL
|
|
openBrowser(redirectUrl)
|
|
dialog.dismiss()
|
|
|
|
// Take user back (finish activity)
|
|
finish()
|
|
}
|
|
is ApiResult.Error -> {
|
|
Log.e(TAG, "Failed to get redirect URL: ${redirectResult.message}")
|
|
dialog.showLoading(false)
|
|
Toast.makeText(this@PaymentReviewActivity, "Failed to get payment URL", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
is ApiResult.Error -> {
|
|
Log.e(TAG, "Failed to initiate payment: ${paymentResult.message}")
|
|
dialog.showLoading(false)
|
|
Toast.makeText(this@PaymentReviewActivity, "Failed to initiate payment", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Error processing BML payment", e)
|
|
dialog.showLoading(false)
|
|
Toast.makeText(this@PaymentReviewActivity, "Payment processing error", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun openBrowser(url: String) {
|
|
try {
|
|
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
|
startActivity(intent)
|
|
Log.d(TAG, "Opened browser with URL: $url")
|
|
} catch (e: Exception) {
|
|
Log.e(TAG, "Failed to open browser", e)
|
|
Toast.makeText(this, "Failed to open browser", Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
} |