Files
GridFlow/app/src/main/java/sh/sar/gridflow/PaymentReviewActivity.kt

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()
}
}
}