Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The `:shared` module is Kotlin Multiplatform and currently includes:
- Payment checkout state and PhonePe checkout URL preparation

Android still contains platform UI and integrations such as Compose screens,
Google Sign-In UI, PhonePe SDK/WebView handling, Firebase messaging, Android
Google Sign-In UI, PhonePe WebView handling, Firebase messaging, Android
navigation, and Android-specific sharing/image picking.

## Toolchain
Expand Down
10 changes: 1 addition & 9 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,22 +66,14 @@ dependencies {

//google sign in dependencies
implementation("com.google.android.gms:play-services-auth:20.7.0") // for Google Sign-In
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("androidx.datastore:datastore-preferences:1.0.0")

//coil dependency
implementation("io.coil-kt:coil-compose:2.5.0")

//logging dependency
implementation ("com.squareup.okhttp3:logging-interceptor:4.12.0")

//icon dependencies
implementation("androidx.compose.material:material-icons-extended:1.6.1")

//phonepe payment integration - Latest stable version
implementation("phonepe.intentsdk.android.release:IntentSDK:5.2.0")

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand All @@ -98,4 +90,4 @@ dependencies {
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)
}
}
277 changes: 0 additions & 277 deletions app/src/main/java/com/example/talkeys_new/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ package com.example.talkeys_new

import android.Manifest
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
Expand All @@ -19,15 +17,12 @@ import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat
import com.example.talkeys_new.navigation.AppNavigation
import com.example.talkeys_new.screens.authentication.TokenManager
import com.example.talkeys_new.ui.theme.Talkeys_NewTheme
import com.example.talkeys_new.utils.ConsentDialogHelper
import com.example.talkeys_new.utils.FCMInitializationManager
import com.example.talkeys_new.utils.FCMTokenManager
import com.example.talkeys_new.utils.PhonePePaymentManager
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.firebase.messaging.FirebaseMessaging
import kotlinx.coroutines.flow.first
// Koin is initialized in TalkeysApplication.onCreate()

Expand All @@ -46,25 +41,6 @@ class MainActivity : ComponentActivity() {
}
}

// PhonePe payment result launcher with enhanced handling
private val phonePePaymentLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
Log.d(TAG, "🔍 PhonePe payment result received")
Log.d(TAG, "📋 Result code: ${result.resultCode}")
Log.d(TAG, "📋 Result includes data=${result.data != null}")

// ✅ CRITICAL: Always verify payment status regardless of result code
// WebView JavaScript errors can cause wrong result codes even when payment succeeds
PhonePePaymentManager.handlePaymentResult(
resultCode = result.resultCode,
data = result.data
) { paymentResult ->
Log.d(TAG, "💳 Payment result processed: $paymentResult")
handlePhonePePaymentResult(paymentResult)
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

Expand Down Expand Up @@ -351,259 +327,6 @@ class MainActivity : ComponentActivity() {
}
}

/**
* Handle PhonePe payment result
* This method is called after the payment flow completes
*/
private fun handlePhonePePaymentResult(paymentResult: PhonePePaymentManager.PaymentResult) {
when (paymentResult) {
is PhonePePaymentManager.PaymentResult.Success -> {
Log.d(TAG, "Payment successful")
navigateToRegistrationSuccess()
}
is PhonePePaymentManager.PaymentResult.Failed -> {
Log.d(TAG, "Payment failed: ${paymentResult.message}")
showPaymentFailedMessage(paymentResult.message)
}
is PhonePePaymentManager.PaymentResult.Pending -> {
Log.d(TAG, "Payment pending: ${paymentResult.message}")
showPaymentPendingMessage(paymentResult.message)
}
is PhonePePaymentManager.PaymentResult.Completed -> {
Log.d(TAG, "Payment completed: ${paymentResult.message}")
// Legacy support - this should now be handled by automatic verification
navigateToRegistrationSuccess()
}
is PhonePePaymentManager.PaymentResult.Cancelled -> {
Log.d(TAG, "Payment cancelled: ${paymentResult.message}")
showPaymentCancelledMessage()
}
is PhonePePaymentManager.PaymentResult.Error -> {
Log.e(TAG, "Payment error: ${paymentResult.message}")
showPaymentErrorMessage(paymentResult.message)
}
}
}

/**
* Navigate to registration success screen
*/
private fun navigateToRegistrationSuccess() {
// Since we can't directly navigate from MainActivity, we'll use a different approach
// You might want to use a shared ViewModel or event bus for this
Log.d(TAG, "Payment successful - user should be navigated to success screen")

// For now, just log the success
// The actual navigation should be handled in the payment screen or through a callback
}

/**
* Initiate integrated payment flow (book ticket + PhonePe payment)
* This is the recommended method that handles the complete flow
*/
fun initiateIntegratedPayment(
eventId: String,
passType: String,
friends: List<com.talkeys.shared.data.payment.Friend>,
authToken: String? = null,
onResult: (PhonePePaymentManager.PaymentResult) -> Unit
) {
Log.d(TAG, "Initiating integrated payment for event: $eventId")

PhonePePaymentManager.bookTicketAndPay(
activity = this,
eventId = eventId,
passType = passType,
friends = friends,
activityResultLauncher = phonePePaymentLauncher,
authToken = authToken,
onResult = onResult
)
}

/**
* Legacy method: Initiate PhonePe payment with pre-obtained token
* Use initiateIntegratedPayment() for the complete flow
*
* @param token Payment token from your backend (Create Order API response)
* @param orderId Order ID from your backend (Create Order API response)
*/
fun initiatePhonePePayment(token: String, orderId: String) {
Log.d(TAG, "Initiating PhonePe payment")

PhonePePaymentManager.startCheckout(
activity = this,
token = token,
orderId = orderId,
activityResultLauncher = phonePePaymentLauncher
)
}

/**
* Check order status after payment completion
* This should call your backend's Order Status API
*/
private fun checkOrderStatus() {
Log.d(TAG, "Checking order status via backend API")

// Get the current merchant order ID (you'll need to store this during payment)
val merchantOrderId = getCurrentMerchantOrderId()

if (merchantOrderId != null) {
// Use PhonePePaymentManager to verify payment status
PhonePePaymentManager.verifyPaymentStatusOnServer(
merchantOrderId = merchantOrderId,
authToken = getCurrentAuthToken()
) { result ->
when (result) {
is PhonePePaymentManager.PaymentResult.Success -> {
Log.d(TAG, "✅ Payment verified as successful: ${result.message}")
handlePaymentSuccess(result.passId, result.passUUID)
}
is PhonePePaymentManager.PaymentResult.Failed -> {
Log.d(TAG, "❌ Payment verified as failed: ${result.message}")
handlePaymentFailure(result.message)
}
is PhonePePaymentManager.PaymentResult.Pending -> {
Log.d(TAG, "⏳ Payment still pending: ${result.message}")
handlePaymentPending(result.message)
}
is PhonePePaymentManager.PaymentResult.Error -> {
Log.e(TAG, "🚨 Error checking payment status: ${result.message}")
handlePaymentError(result.message)
}
else -> {
Log.w(TAG, "⚠️ Unknown payment result: $result")
handlePaymentError("Unknown payment status")
}
}
}
} else {
Log.e(TAG, "No merchant order ID found for status check")
handlePaymentError("No order ID available for verification")
}
}

/**
* Get current merchant order ID (implement based on how you store it)
*/
private fun getCurrentMerchantOrderId(): String? {
// TODO: Implement based on how you store the merchant order ID
// This could be from SharedPreferences, ViewModel, or passed as parameter

// Example implementation:
// return getSharedPreferences("payment", Context.MODE_PRIVATE)
// .getString("current_merchant_order_id", null)

// For testing, you can use your recent order ID:
return "TKT_1760870435366_hz7wihgz7" // Replace with actual implementation
}

/**
* Get current auth token (implement based on your auth system)
*/
private fun getCurrentAuthToken(): String? {
// TODO: Implement based on your authentication system
// This could be from TokenManager, SharedPreferences, etc.

// Example implementation:
// return TokenManager.getCurrentToken()

return null // Replace with actual token retrieval
}

/**
* Handle successful payment verification
*/
private fun handlePaymentSuccess(passId: String, passUUID: String?) {
Log.d(TAG, "Payment success confirmed")

// Navigate to success screen or show success message
// Example: navigateToRegistrationSuccess(passId, passUUID)

clearCurrentMerchantOrderId()
}

/**
* Handle failed payment verification
*/
private fun handlePaymentFailure(message: String) {
Log.d(TAG, "Payment failure confirmed: $message")

// Show failure message to user
// Example: showPaymentFailedDialog(message)

// Clear stored order ID
clearCurrentMerchantOrderId()
}

/**
* Handle pending payment verification
*/
private fun handlePaymentPending(message: String) {
Log.d(TAG, "Payment still pending: $message")

// Show pending message and maybe retry after delay
// Example: showPaymentPendingDialog(message)
}

/**
* Handle payment verification error
*/
private fun handlePaymentError(message: String) {
Log.e(TAG, "Payment verification error: $message")

// Show error message to user
// Example: showPaymentErrorDialog(message)

// Clear stored order ID
clearCurrentMerchantOrderId()
}

/**
* Clear stored merchant order ID
*/
private fun clearCurrentMerchantOrderId() {
// TODO: Implement based on how you store the merchant order ID
// Example:
// getSharedPreferences("payment", Context.MODE_PRIVATE)
// .edit()
// .remove("current_merchant_order_id")
// .apply()
}

/**
* Show payment cancelled message to user
*/
private fun showPaymentCancelledMessage() {
Log.d(TAG, "TODO: Show payment cancelled UI to user")
// TODO: Implement UI to show payment was cancelled
}

/**
* Show payment failed message to user
*/
private fun showPaymentFailedMessage(errorMessage: String) {
Log.e(TAG, "TODO: Show payment failed UI to user: $errorMessage")
// TODO: Implement UI to show payment failed
}

/**
* Show payment pending message to user
*/
private fun showPaymentPendingMessage(message: String) {
Log.d(TAG, "TODO: Show payment pending UI to user: $message")
// TODO: Implement UI to show payment is pending
}

/**
* Show payment error message to user
*/
private fun showPaymentErrorMessage(errorMessage: String) {
Log.e(TAG, "TODO: Show payment error UI to user: $errorMessage")
// TODO: Implement UI to show payment error
}

companion object {
private const val TAG = "MainActivity"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,8 @@ class MyFirebaseMessagingService : FirebaseMessagingService() {
// for the current user
Log.d(TAG, "TODO: send FCM token to backend (not yet implemented)")

// Example of what this would look like with a real API:
/*
val apiService = RetrofitClient.getApiService()
apiService.updateFCMToken(
userId = getCurrentUserId(),
token = token,
deviceInfo = getDeviceInfo()
).enqueue(object : Callback<ApiResponse> {
override fun onResponse(call: Call<ApiResponse>, response: Response<ApiResponse>) {
if (response.isSuccessful) {
Log.d(TAG, "✅ Token successfully sent to server")
} else {
Log.e(TAG, "❌ Failed to send token to server: ${response.code()}")
}
}

override fun onFailure(call: Call<ApiResponse>, t: Throwable) {
Log.e(TAG, "🌐 Network error sending token", t)
}
})
*/

// Add this through shared Ktor once the backend exposes an FCM-token
// registration endpoint.
}

private fun storeTokenMetadata(token: String, timestamp: String) {
Expand Down
Loading