proeses transaction post backend and database
This commit is contained in:
		
							parent
							
								
									7a2ddc3f15
								
							
						
					
					
						commit
						b66ef4bb00
					
				@ -22,6 +22,7 @@ import com.google.android.material.button.MaterialButton;
 | 
			
		||||
import com.example.bdkipoc.cetakulang.ReprintActivity;
 | 
			
		||||
import com.example.bdkipoc.cetakulang.ReprintAdapterActivity;
 | 
			
		||||
 | 
			
		||||
import com.example.bdkipoc.R;
 | 
			
		||||
import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
			
		||||
import com.example.bdkipoc.transaction.ResultTransactionActivity;
 | 
			
		||||
 | 
			
		||||
@ -156,22 +157,23 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
            CardView cardView = findViewById(cardId);
 | 
			
		||||
            if (cardView != null) {
 | 
			
		||||
                cardView.setOnClickListener(v -> {
 | 
			
		||||
                    // ✅ ENHANCED: Navigate with payment type information
 | 
			
		||||
                    if (cardId == R.id.card_kartu_kredit) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
			
		||||
                        navigateToCreateTransaction("credit_card", cardId, "Kartu Kredit");
 | 
			
		||||
                    } else if (cardId == R.id.card_kartu_debit) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
			
		||||
                        navigateToCreateTransaction("debit_card", cardId, "Kartu Debit");
 | 
			
		||||
                    } else if (cardId == R.id.card_qris) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, QrisActivity.class));
 | 
			
		||||
                    // Col-2
 | 
			
		||||
                    } else if (cardId == R.id.card_transfer) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
			
		||||
                        navigateToCreateTransaction("transfer", cardId, "Transfer");
 | 
			
		||||
                    } else if (cardId == R.id.card_uang_elektronik) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
			
		||||
                        navigateToCreateTransaction("e_money", cardId, "Uang Elektronik");
 | 
			
		||||
                    } else if (cardId == R.id.card_cetak_ulang) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, ReprintActivity.class));
 | 
			
		||||
                    // Col-3
 | 
			
		||||
                    } else if (cardId == R.id.card_refund) {
 | 
			
		||||
                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
			
		||||
                        navigateToCreateTransaction("refund", cardId, "Refund");
 | 
			
		||||
                    } else if (cardId == R.id.card_settlement) {
 | 
			
		||||
                        Toast.makeText(this, "Settlement - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    } else if (cardId == R.id.card_histori) {
 | 
			
		||||
@ -185,7 +187,7 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
                        Toast.makeText(this, "Pengaturan - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // Fallback for any other cards
 | 
			
		||||
                        Toast.makeText(this, "Menu Diklik: " + getResources().getResourceEntryName(cardId), Toast.LENGTH_SHORT).show();
 | 
			
		||||
                        navigateToCreateTransaction("credit_card", cardId, "Unknown");
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
@ -242,6 +244,125 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Enhanced navigation method with payment type information
 | 
			
		||||
    private void navigateToCreateTransaction(String paymentType, int cardMenuId, String cardName) {
 | 
			
		||||
        try {
 | 
			
		||||
            Intent intent = new Intent(MainActivity.this, CreateTransactionActivity.class);
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Pass comprehensive payment information
 | 
			
		||||
            intent.putExtra("PAYMENT_TYPE", paymentType);
 | 
			
		||||
            intent.putExtra("CARD_MENU_ID", cardMenuId);
 | 
			
		||||
            intent.putExtra("CARD_NAME", cardName);
 | 
			
		||||
            intent.putExtra("CALLING_ACTIVITY", "MainActivity");
 | 
			
		||||
            
 | 
			
		||||
            // ✅ DEBUG: Log navigation details
 | 
			
		||||
            android.util.Log.d("MainActivity", "=== NAVIGATING TO CREATE TRANSACTION ===");
 | 
			
		||||
            android.util.Log.d("MainActivity", "Payment Type: " + paymentType);
 | 
			
		||||
            android.util.Log.d("MainActivity", "Card Menu ID: " + cardMenuId);
 | 
			
		||||
            android.util.Log.d("MainActivity", "Card Name: " + cardName);
 | 
			
		||||
            android.util.Log.d("MainActivity", "========================================");
 | 
			
		||||
            
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            android.util.Log.e("MainActivity", "Error navigating to CreateTransaction: " + e.getMessage(), e);
 | 
			
		||||
            Toast.makeText(this, "Error opening transaction: " + e.getMessage(), Toast.LENGTH_SHORT).show();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Helper method to get payment type from card ID (for backward compatibility)
 | 
			
		||||
    private String getPaymentTypeFromCardId(int cardId) {
 | 
			
		||||
        if (cardId == R.id.card_kartu_kredit) {
 | 
			
		||||
            return "credit_card";
 | 
			
		||||
        } else if (cardId == R.id.card_kartu_debit) {
 | 
			
		||||
            return "debit_card";
 | 
			
		||||
        } else if (cardId == R.id.card_qris) {
 | 
			
		||||
            return "qris";
 | 
			
		||||
        } else if (cardId == R.id.card_transfer) {
 | 
			
		||||
            return "transfer";
 | 
			
		||||
        } else if (cardId == R.id.card_uang_elektronik) {
 | 
			
		||||
            return "e_money";
 | 
			
		||||
        } else if (cardId == R.id.card_refund) {
 | 
			
		||||
            return "refund";
 | 
			
		||||
        } else {
 | 
			
		||||
            android.util.Log.w("MainActivity", "Unknown card ID: " + cardId + ", defaulting to credit_card");
 | 
			
		||||
            return "credit_card";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Helper method to get card name from card ID
 | 
			
		||||
    private String getCardNameFromCardId(int cardId) {
 | 
			
		||||
        if (cardId == R.id.card_kartu_kredit) {
 | 
			
		||||
            return "Kartu Kredit";
 | 
			
		||||
        } else if (cardId == R.id.card_kartu_debit) {
 | 
			
		||||
            return "Kartu Debit";
 | 
			
		||||
        } else if (cardId == R.id.card_qris) {
 | 
			
		||||
            return "QRIS";
 | 
			
		||||
        } else if (cardId == R.id.card_transfer) {
 | 
			
		||||
            return "Transfer";
 | 
			
		||||
        } else if (cardId == R.id.card_uang_elektronik) {
 | 
			
		||||
            return "Uang Elektronik";
 | 
			
		||||
        } else if (cardId == R.id.card_refund) {
 | 
			
		||||
            return "Refund";
 | 
			
		||||
        } else if (cardId == R.id.card_settlement) {
 | 
			
		||||
            return "Settlement";
 | 
			
		||||
        } else if (cardId == R.id.card_histori) {
 | 
			
		||||
            return "Histori";
 | 
			
		||||
        } else if (cardId == R.id.card_cetak_ulang) {
 | 
			
		||||
            return "Cetak Ulang";
 | 
			
		||||
        } else if (cardId == R.id.card_bantuan) {
 | 
			
		||||
            return "Bantuan";
 | 
			
		||||
        } else if (cardId == R.id.card_info_toko) {
 | 
			
		||||
            return "Info Toko";
 | 
			
		||||
        } else if (cardId == R.id.card_pengaturan) {
 | 
			
		||||
            return "Pengaturan";
 | 
			
		||||
        } else {
 | 
			
		||||
            return "Unknown";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to validate payment type compatibility
 | 
			
		||||
    private boolean isPaymentTypeSupported(String paymentType) {
 | 
			
		||||
        String[] supportedTypes = {
 | 
			
		||||
            "credit_card", "debit_card", "e_money", "qris", 
 | 
			
		||||
            "transfer", "refund"
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        for (String supportedType : supportedTypes) {
 | 
			
		||||
            if (supportedType.equals(paymentType)) {
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to show payment type selection dialog (for future use)
 | 
			
		||||
    private void showPaymentTypeDialog() {
 | 
			
		||||
        androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
 | 
			
		||||
        builder.setTitle("Pilih Jenis Pembayaran");
 | 
			
		||||
        
 | 
			
		||||
        String[] paymentTypes = {
 | 
			
		||||
            "Kartu Kredit", "Kartu Debit", "Uang Elektronik", 
 | 
			
		||||
            "QRIS", "Transfer", "Refund"
 | 
			
		||||
        };
 | 
			
		||||
        String[] paymentTypeCodes = {
 | 
			
		||||
            "credit_card", "debit_card", "e_money", 
 | 
			
		||||
            "qris", "transfer", "refund"
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        builder.setItems(paymentTypes, (dialog, which) -> {
 | 
			
		||||
            String selectedType = paymentTypeCodes[which];
 | 
			
		||||
            String selectedName = paymentTypes[which];
 | 
			
		||||
            
 | 
			
		||||
            // Use a generic card ID for dialog selection
 | 
			
		||||
            navigateToCreateTransaction(selectedType, -1, selectedName);
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        builder.setNegativeButton("Batal", null);
 | 
			
		||||
        builder.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onNewIntent(Intent intent) {
 | 
			
		||||
        super.onNewIntent(intent);
 | 
			
		||||
@ -256,5 +377,74 @@ public class MainActivity extends AppCompatActivity {
 | 
			
		||||
        // Clear any transaction completion flags to avoid repeated messages
 | 
			
		||||
        getIntent().removeExtra("transaction_completed");
 | 
			
		||||
        getIntent().removeExtra("transaction_amount");
 | 
			
		||||
        
 | 
			
		||||
        // ✅ NEW: Log resume for debugging
 | 
			
		||||
        android.util.Log.d("MainActivity", "MainActivity resumed");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPause() {
 | 
			
		||||
        super.onPause();
 | 
			
		||||
        android.util.Log.d("MainActivity", "MainActivity paused");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        android.util.Log.d("MainActivity", "MainActivity destroyed");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to handle direct payment type launch (for external calls)
 | 
			
		||||
    public static Intent createTransactionIntent(android.content.Context context, String paymentType, String cardName) {
 | 
			
		||||
        Intent intent = new Intent(context, CreateTransactionActivity.class);
 | 
			
		||||
        intent.putExtra("PAYMENT_TYPE", paymentType);
 | 
			
		||||
        intent.putExtra("CARD_NAME", cardName);
 | 
			
		||||
        intent.putExtra("CALLING_ACTIVITY", "External");
 | 
			
		||||
        return intent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Public method to simulate card click (for testing)
 | 
			
		||||
    public void simulateCardClick(int cardId) {
 | 
			
		||||
        CardView cardView = findViewById(cardId);
 | 
			
		||||
        if (cardView != null) {
 | 
			
		||||
            cardView.performClick();
 | 
			
		||||
        } else {
 | 
			
		||||
            android.util.Log.w("MainActivity", "Card not found for ID: " + cardId);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to get all available payment types
 | 
			
		||||
    public String[] getAvailablePaymentTypes() {
 | 
			
		||||
        return new String[]{
 | 
			
		||||
            "credit_card", "debit_card", "e_money", 
 | 
			
		||||
            "qris", "transfer", "refund"
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to get payment type display names
 | 
			
		||||
    public String[] getPaymentTypeDisplayNames() {
 | 
			
		||||
        return new String[]{
 | 
			
		||||
            "Kartu Kredit", "Kartu Debit", "Uang Elektronik", 
 | 
			
		||||
            "QRIS", "Transfer", "Refund"
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Debug method to log all card IDs and their payment types
 | 
			
		||||
    private void debugCardMappings() {
 | 
			
		||||
        android.util.Log.d("MainActivity", "=== CARD PAYMENT TYPE MAPPINGS ===");
 | 
			
		||||
        
 | 
			
		||||
        int[] cardIds = {
 | 
			
		||||
            R.id.card_kartu_kredit, R.id.card_kartu_debit, R.id.card_qris,
 | 
			
		||||
            R.id.card_transfer, R.id.card_uang_elektronik, R.id.card_refund
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        for (int cardId : cardIds) {
 | 
			
		||||
            String paymentType = getPaymentTypeFromCardId(cardId);
 | 
			
		||||
            String cardName = getCardNameFromCardId(cardId);
 | 
			
		||||
            android.util.Log.d("MainActivity", 
 | 
			
		||||
                "Card ID: " + cardId + " -> Payment Type: " + paymentType + " -> Name: " + cardName);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        android.util.Log.d("MainActivity", "==================================");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -24,6 +24,7 @@ import com.example.bdkipoc.transaction.managers.EMVManager;
 | 
			
		||||
import com.example.bdkipoc.transaction.managers.ModalManager;
 | 
			
		||||
import com.example.bdkipoc.transaction.managers.PinPadManager;
 | 
			
		||||
import com.example.bdkipoc.transaction.managers.MidtransCardPaymentManager;
 | 
			
		||||
import com.example.bdkipoc.transaction.managers.PostTransactionBackendManager;
 | 
			
		||||
 | 
			
		||||
import java.text.NumberFormat;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
@ -34,14 +35,17 @@ import org.json.JSONObject;
 | 
			
		||||
import org.json.JSONException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * CreateTransactionActivity - Updated with Enhanced Midtrans Credit Card Integration
 | 
			
		||||
 * Handles amount input, card scanning, and Midtrans payment processing with improved bank detection
 | 
			
		||||
 * CreateTransactionActivity - Enhanced with Backend Integration
 | 
			
		||||
 * 
 | 
			
		||||
 * Flow: Backend Post Transaction => EMV/Card Processing => Midtrans Charge => Results
 | 
			
		||||
 * The transaction_uuid from backend is used as order_id in Midtrans
 | 
			
		||||
 */
 | 
			
		||||
public class CreateTransactionActivity extends AppCompatActivity implements 
 | 
			
		||||
    EMVManager.EMVManagerCallback,
 | 
			
		||||
    CardScannerManager.CardScannerCallback,
 | 
			
		||||
    PinPadManager.PinPadManagerCallback,
 | 
			
		||||
    MidtransCardPaymentManager.MidtransCardPaymentCallback {
 | 
			
		||||
    MidtransCardPaymentManager.MidtransCardPaymentCallback,
 | 
			
		||||
    PostTransactionBackendManager.PostTransactionCallback {
 | 
			
		||||
    
 | 
			
		||||
    private static final String TAG = "CreateTransaction";
 | 
			
		||||
    
 | 
			
		||||
@ -60,20 +64,26 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    // State Management
 | 
			
		||||
    private String transactionAmount = "0";
 | 
			
		||||
    private boolean isEMVMode = true;
 | 
			
		||||
    private boolean useDirectMidtransPayment = true; // Toggle for Midtrans integration
 | 
			
		||||
    private boolean useDirectMidtransPayment = true;
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Payment type and backend integration
 | 
			
		||||
    private String paymentType = "credit_card"; // Default
 | 
			
		||||
    private String transactionUuid; // From backend response
 | 
			
		||||
    private String backendTransactionStatus = "INIT";
 | 
			
		||||
    
 | 
			
		||||
    // Manager Classes
 | 
			
		||||
    private EMVManager emvManager;
 | 
			
		||||
    private CardScannerManager cardScannerManager;
 | 
			
		||||
    private PinPadManager pinPadManager;
 | 
			
		||||
    private ModalManager modalManager;
 | 
			
		||||
    private MidtransCardPaymentManager midtransPaymentManager; // ✅ NEW: Midtrans integration
 | 
			
		||||
    private MidtransCardPaymentManager midtransPaymentManager;
 | 
			
		||||
    private PostTransactionBackendManager backendManager; // ✅ NEW: Backend manager
 | 
			
		||||
    
 | 
			
		||||
    // EMV Dialog
 | 
			
		||||
    private AlertDialog mAppSelectDialog;
 | 
			
		||||
    private int mSelectIndex;
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: EMV Data Storage for Midtrans
 | 
			
		||||
    // EMV Data Storage for Midtrans
 | 
			
		||||
    private String emvCardNumber;
 | 
			
		||||
    private String emvExpiryDate;
 | 
			
		||||
    private String emvCardholderName;
 | 
			
		||||
@ -81,10 +91,11 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    private String emvTlvData;
 | 
			
		||||
    private String referenceId;
 | 
			
		||||
 | 
			
		||||
    // ✅ ENHANCED: Store last response for better debugging
 | 
			
		||||
    // Enhanced response storage
 | 
			
		||||
    private JSONObject lastMidtransResponse;
 | 
			
		||||
    private JSONObject lastBackendResponse; // ✅ NEW: Store backend response
 | 
			
		||||
 | 
			
		||||
    // deklarasi variabel success screen
 | 
			
		||||
    // Success screen components
 | 
			
		||||
    private LinearLayout successScreen;
 | 
			
		||||
    private ImageView successIcon;
 | 
			
		||||
    private TextView successMessage;
 | 
			
		||||
@ -95,14 +106,16 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        setContentView(R.layout.activity_create_transaction);
 | 
			
		||||
        
 | 
			
		||||
        initViews();
 | 
			
		||||
        extractPaymentTypeFromIntent(); // ✅ NEW: Determine payment type
 | 
			
		||||
        initManagers();
 | 
			
		||||
        setupListeners();
 | 
			
		||||
        updateAmountDisplay();
 | 
			
		||||
        updateModeDisplay();
 | 
			
		||||
        
 | 
			
		||||
        // ✅ NEW: Generate reference ID for transaction tracking
 | 
			
		||||
        referenceId = generateReferenceId();
 | 
			
		||||
        // Generate reference ID for transaction tracking
 | 
			
		||||
        referenceId = PostTransactionBackendManager.generateReferenceId();
 | 
			
		||||
        Log.d(TAG, "Generated reference ID: " + referenceId);
 | 
			
		||||
        Log.d(TAG, "Payment type determined: " + paymentType);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void initViews() {
 | 
			
		||||
@ -133,6 +146,38 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        successMessage = findViewById(R.id.success_message);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Extract payment type from intent (based on MainActivity card selection)
 | 
			
		||||
    private void extractPaymentTypeFromIntent() {
 | 
			
		||||
        Intent intent = getIntent();
 | 
			
		||||
        
 | 
			
		||||
        // Check if payment type was passed directly
 | 
			
		||||
        String intentPaymentType = intent.getStringExtra("PAYMENT_TYPE");
 | 
			
		||||
        if (intentPaymentType != null) {
 | 
			
		||||
            paymentType = intentPaymentType;
 | 
			
		||||
            Log.d(TAG, "Payment type from intent: " + paymentType);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Check if card menu ID was passed
 | 
			
		||||
        int cardMenuId = intent.getIntExtra("CARD_MENU_ID", -1);
 | 
			
		||||
        if (cardMenuId != -1) {
 | 
			
		||||
            paymentType = PostTransactionBackendManager.mapCardMenuToPaymentType(cardMenuId);
 | 
			
		||||
            Log.d(TAG, "Payment type from card menu ID " + cardMenuId + ": " + paymentType);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Fallback: try to determine from calling activity
 | 
			
		||||
        String callingActivity = intent.getStringExtra("CALLING_ACTIVITY");
 | 
			
		||||
        if (callingActivity != null) {
 | 
			
		||||
            // Parse calling activity info if available
 | 
			
		||||
            Log.d(TAG, "Calling activity: " + callingActivity);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Default fallback
 | 
			
		||||
        paymentType = "credit_card";
 | 
			
		||||
        Log.d(TAG, "Using default payment type: " + paymentType);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private void initManagers() {
 | 
			
		||||
        // Initialize Modal Manager
 | 
			
		||||
        FrameLayout modalOverlay = findViewById(R.id.modal_overlay);
 | 
			
		||||
@ -145,13 +190,16 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        cardScannerManager = new CardScannerManager(this);
 | 
			
		||||
        pinPadManager = new PinPadManager(this);
 | 
			
		||||
        
 | 
			
		||||
        // ✅ NEW: Initialize Midtrans payment manager
 | 
			
		||||
        // Initialize Midtrans payment manager
 | 
			
		||||
        midtransPaymentManager = new MidtransCardPaymentManager(this, this);
 | 
			
		||||
        
 | 
			
		||||
        // ✅ NEW: Initialize Backend manager
 | 
			
		||||
        backendManager = new PostTransactionBackendManager(this, this);
 | 
			
		||||
        
 | 
			
		||||
        // Initialize EMV data
 | 
			
		||||
        emvManager.initEMVData();
 | 
			
		||||
        
 | 
			
		||||
        Log.d(TAG, "All managers initialized including Midtrans");
 | 
			
		||||
        Log.d(TAG, "All managers initialized including Backend and Midtrans");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void setupListeners() {
 | 
			
		||||
@ -183,7 +231,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        // Clear button (ImageView)
 | 
			
		||||
        btnClear.setOnClickListener(v -> clearAmount());
 | 
			
		||||
        
 | 
			
		||||
        // Confirm button - shows modal and starts scanning
 | 
			
		||||
        // Confirm button - starts the enhanced flow
 | 
			
		||||
        btnConfirm.setOnClickListener(v -> handleConfirmAmount());
 | 
			
		||||
        
 | 
			
		||||
        // Toggle mode button (hidden but functional)
 | 
			
		||||
@ -205,7 +253,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        if (newAmount.length() <= 9) {
 | 
			
		||||
            transactionAmount = newAmount;
 | 
			
		||||
            updateAmountDisplay();
 | 
			
		||||
            // Update status tombol
 | 
			
		||||
            btnConfirm.setEnabled(!transactionAmount.equals("0"));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -217,7 +264,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            transactionAmount = "0";
 | 
			
		||||
        }
 | 
			
		||||
        updateAmountDisplay();
 | 
			
		||||
        // Update status tombol
 | 
			
		||||
        btnConfirm.setEnabled(!transactionAmount.equals("0"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -225,19 +271,18 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        if (tvAmountDisplay != null) {
 | 
			
		||||
            if (transactionAmount.equals("0")) {
 | 
			
		||||
                tvAmountDisplay.setText("");
 | 
			
		||||
                // Disable tombol dan akan otomatis pakai background inactive
 | 
			
		||||
                btnConfirm.setEnabled(false);
 | 
			
		||||
            } else {
 | 
			
		||||
                long amountCents = Long.parseLong(transactionAmount);
 | 
			
		||||
                NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID"));
 | 
			
		||||
                String formattedAmount = formatter.format(amountCents);
 | 
			
		||||
                tvAmountDisplay.setText(formattedAmount);
 | 
			
		||||
                // Enable tombol dan akan otomatis pakai background active
 | 
			
		||||
                btnConfirm.setEnabled(true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ ENHANCED: New enhanced flow with backend integration
 | 
			
		||||
    private void handleConfirmAmount() {
 | 
			
		||||
        long amountCents = Long.parseLong(transactionAmount);
 | 
			
		||||
        if (amountCents < 100) { // Minimum Rp 1.00
 | 
			
		||||
@ -245,15 +290,77 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Show modal and start card scanning
 | 
			
		||||
        showModalAndStartScanning();
 | 
			
		||||
        Log.d(TAG, "=== STARTING ENHANCED TRANSACTION FLOW ===");
 | 
			
		||||
        Log.d(TAG, "Payment Type: " + paymentType);
 | 
			
		||||
        Log.d(TAG, "Amount: " + amountCents);
 | 
			
		||||
        Log.d(TAG, "Reference ID: " + referenceId);
 | 
			
		||||
        Log.d(TAG, "==========================================");
 | 
			
		||||
        
 | 
			
		||||
        // Start enhanced flow: Backend => Card Processing => Midtrans
 | 
			
		||||
        startEnhancedTransactionFlow();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Enhanced transaction flow with backend integration
 | 
			
		||||
    private void startEnhancedTransactionFlow() {
 | 
			
		||||
        // Step 1: Post initial transaction to backend with INIT status
 | 
			
		||||
        modalManager.showProcessingModal("Initializing transaction...");
 | 
			
		||||
        
 | 
			
		||||
    private void showModalAndStartScanning() {
 | 
			
		||||
        Log.d(TAG, "Starting card scanning with modal...");
 | 
			
		||||
        // Debug transaction data
 | 
			
		||||
        backendManager.debugTransactionData(paymentType, referenceId, Long.parseLong(transactionAmount), "INIT");
 | 
			
		||||
        
 | 
			
		||||
        // Show modal first
 | 
			
		||||
        // Post to backend first
 | 
			
		||||
        backendManager.postInitTransaction(paymentType, referenceId, Long.parseLong(transactionAmount));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ====== ✅ NEW: BACKEND CALLBACK METHODS ======
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPostTransactionSuccess(JSONObject response, String transactionUuid) {
 | 
			
		||||
        Log.d(TAG, "✅ Backend transaction posted successfully!");
 | 
			
		||||
        Log.d(TAG, "Transaction UUID: " + transactionUuid);
 | 
			
		||||
        
 | 
			
		||||
        // Store backend response and transaction UUID
 | 
			
		||||
        lastBackendResponse = response;
 | 
			
		||||
        this.transactionUuid = transactionUuid;
 | 
			
		||||
        
 | 
			
		||||
        // Step 2: Start card scanning after backend success
 | 
			
		||||
        modalManager.showProcessingModal("Backend initialized - Scanning card...");
 | 
			
		||||
        
 | 
			
		||||
        // Start card scanning after short delay
 | 
			
		||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
			
		||||
            startCardScanningFlow();
 | 
			
		||||
        }, 1000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPostTransactionError(String errorMessage) {
 | 
			
		||||
        Log.e(TAG, "❌ Backend transaction failed: " + errorMessage);
 | 
			
		||||
        modalManager.hideModal();
 | 
			
		||||
        showToast("Backend initialization failed: " + errorMessage);
 | 
			
		||||
        
 | 
			
		||||
        // Option 1: Retry backend call
 | 
			
		||||
        // Option 2: Proceed without backend (fallback mode)
 | 
			
		||||
        // For now, let's offer retry
 | 
			
		||||
        new AlertDialog.Builder(this)
 | 
			
		||||
            .setTitle("Backend Error")
 | 
			
		||||
            .setMessage("Failed to initialize transaction with backend. Retry?")
 | 
			
		||||
            .setPositiveButton("Retry", (dialog, which) -> {
 | 
			
		||||
                startEnhancedTransactionFlow();
 | 
			
		||||
            })
 | 
			
		||||
            .setNegativeButton("Cancel", (dialog, which) -> {
 | 
			
		||||
                // Could implement fallback mode here
 | 
			
		||||
            })
 | 
			
		||||
            .show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPostTransactionProgress(String message) {
 | 
			
		||||
        Log.d(TAG, "Backend progress: " + message);
 | 
			
		||||
        modalManager.showProcessingModal(message);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Start card scanning flow (after backend success)
 | 
			
		||||
    private void startCardScanningFlow() {
 | 
			
		||||
        Log.d(TAG, "Starting card scanning flow...");
 | 
			
		||||
        modalManager.showScanCardModal();
 | 
			
		||||
        
 | 
			
		||||
        // Start scanning after short delay
 | 
			
		||||
@ -284,10 +391,8 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        Log.d(TAG, "Simple card detected: " + cardType);
 | 
			
		||||
        modalManager.showProcessingModal("Kartu " + cardType + " Ditemukan - Memproses...");
 | 
			
		||||
        
 | 
			
		||||
        // For simple mode, navigate to results without Midtrans
 | 
			
		||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
			
		||||
            navigateToResults(cardType, cardData, null);
 | 
			
		||||
        }, 1500);
 | 
			
		||||
        // For simple mode, update backend status and navigate to results
 | 
			
		||||
        updateBackendToSuccessAndNavigate(cardType, cardData, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -312,7 +417,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onScanProgress(String message) {
 | 
			
		||||
        Log.d(TAG, "Scan progress: " + message);
 | 
			
		||||
        // Can update UI with progress if needed
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ====== EMV MANAGER CALLBACK METHODS ======
 | 
			
		||||
@ -329,7 +433,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onConfirmCardNo(String cardNo) {
 | 
			
		||||
        // ✅ NEW: Store EMV card data for Midtrans
 | 
			
		||||
        emvCardNumber = cardNo;
 | 
			
		||||
        Log.d(TAG, "EMV Card Number extracted: " + maskCardNumber(cardNo));
 | 
			
		||||
        
 | 
			
		||||
@ -382,13 +485,13 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    public void onTransactionSuccess(int code, String desc) {
 | 
			
		||||
        Log.d(TAG, "EMV Transaction successful");
 | 
			
		||||
        
 | 
			
		||||
        // ✅ NEW: Process Midtrans payment after successful EMV
 | 
			
		||||
        if (useDirectMidtransPayment && emvCardNumber != null) {
 | 
			
		||||
        // Process Midtrans payment after successful EMV
 | 
			
		||||
        if (useDirectMidtransPayment && emvCardNumber != null && transactionUuid != null) {
 | 
			
		||||
            processMidtransPayment();
 | 
			
		||||
        } else {
 | 
			
		||||
            // Traditional flow - navigate to results
 | 
			
		||||
            // Fallback - navigate to results
 | 
			
		||||
            String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
			
		||||
            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
			
		||||
            updateBackendToSuccessAndNavigate(cardType, null, emvManager.getCardNo());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -437,6 +540,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            })
 | 
			
		||||
            .start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ====== PIN PAD CALLBACK METHODS ======
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPinInputLength(int length) {
 | 
			
		||||
@ -473,7 +577,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTokenizeSuccess(String cardToken) {
 | 
			
		||||
        Log.d(TAG, "✅ Midtrans tokenization successful: " + cardToken);
 | 
			
		||||
        // Tokenization successful, charge process will continue automatically
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
@ -482,14 +585,11 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        modalManager.hideModal();
 | 
			
		||||
        showToast("Payment tokenization failed: " + errorMessage);
 | 
			
		||||
        
 | 
			
		||||
        // ✅ IMPROVED: Better fallback handling
 | 
			
		||||
        showToast("Tokenization failed, trying alternative method...");
 | 
			
		||||
        
 | 
			
		||||
        // Try fallback to traditional results screen after delay
 | 
			
		||||
        // Fallback to traditional results screen after delay
 | 
			
		||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
			
		||||
            String cardType = emvManager.getCardType() == 
 | 
			
		||||
                com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
			
		||||
            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
			
		||||
            updateBackendToSuccessAndNavigate(cardType, null, emvManager.getCardNo());
 | 
			
		||||
        }, 2000);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -497,7 +597,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    public void onChargeSuccess(JSONObject chargeResponse) {
 | 
			
		||||
        Log.d(TAG, "✅ Midtrans charge successful!");
 | 
			
		||||
        
 | 
			
		||||
        // ✅ ENHANCED: Store response for debugging
 | 
			
		||||
        // Store response for debugging
 | 
			
		||||
        lastMidtransResponse = chargeResponse;
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
@ -505,30 +605,29 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            String transactionStatus = chargeResponse.getString("transaction_status");
 | 
			
		||||
            String statusCode = chargeResponse.optString("status_code", "");
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Extract and log bank information
 | 
			
		||||
            String bankInfo = extractBankFromResponse(chargeResponse);
 | 
			
		||||
            
 | 
			
		||||
            Log.d(TAG, "✅ Payment Details:");
 | 
			
		||||
            Log.d(TAG, "  - Transaction ID: " + transactionId);
 | 
			
		||||
            Log.d(TAG, "  - Transaction Status: " + transactionStatus);
 | 
			
		||||
            Log.d(TAG, "  - Status Code: " + statusCode);
 | 
			
		||||
            Log.d(TAG, "  - Bank Info: " + bankInfo);
 | 
			
		||||
            Log.d(TAG, "  - Full Response: " + chargeResponse.toString());
 | 
			
		||||
            Log.d(TAG, "  - Transaction UUID: " + transactionUuid);
 | 
			
		||||
            
 | 
			
		||||
            // Check transaction status and navigate accordingly
 | 
			
		||||
            // Update backend status to SUCCESS
 | 
			
		||||
            backendTransactionStatus = "SUCCESS";
 | 
			
		||||
            backendManager.postSuccessTransaction(paymentType, referenceId, Long.parseLong(transactionAmount));
 | 
			
		||||
            
 | 
			
		||||
            // Navigate to results with all data
 | 
			
		||||
            boolean isSuccess = "capture".equals(transactionStatus) || 
 | 
			
		||||
                            "settlement".equals(transactionStatus) || 
 | 
			
		||||
                            "pending".equals(transactionStatus);
 | 
			
		||||
            
 | 
			
		||||
            // Navigate to results with Midtrans data
 | 
			
		||||
            navigateToMidtransResults(chargeResponse, isSuccess);
 | 
			
		||||
            navigateToEnhancedResults(chargeResponse, isSuccess);
 | 
			
		||||
            
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage());
 | 
			
		||||
            // Fallback to traditional results
 | 
			
		||||
            String cardType = emvManager.getCardType() == 
 | 
			
		||||
                com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
			
		||||
            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
			
		||||
            updateBackendToSuccessAndNavigate(cardType, null, emvManager.getCardNo());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -536,23 +635,17 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    public void onChargeError(String errorMessage) {
 | 
			
		||||
        Log.e(TAG, "❌ Midtrans charge failed: " + errorMessage);
 | 
			
		||||
        
 | 
			
		||||
        // ✅ ENHANCED: Try to get response even in error case
 | 
			
		||||
        JSONObject errorResponse = midtransPaymentManager.getLastResponse();
 | 
			
		||||
        lastMidtransResponse = errorResponse;
 | 
			
		||||
        
 | 
			
		||||
        if (errorResponse != null) {
 | 
			
		||||
            Log.d(TAG, "✅ Got Midtrans error response with data, using it for display");
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Extract bank even from error response
 | 
			
		||||
            String bankInfo = extractBankFromResponse(errorResponse);
 | 
			
		||||
            Log.d(TAG, "Bank info from error response: " + bankInfo);
 | 
			
		||||
            
 | 
			
		||||
            Log.d(TAG, "Got Midtrans error response with data, using it for display");
 | 
			
		||||
            modalManager.hideModal();
 | 
			
		||||
            String userMessage = getUserFriendlyErrorMessage(errorMessage);
 | 
			
		||||
            showToast(userMessage);
 | 
			
		||||
            
 | 
			
		||||
            // Use Midtrans results even for failed transactions
 | 
			
		||||
            navigateToMidtransResults(errorResponse, false);
 | 
			
		||||
            navigateToEnhancedResults(errorResponse, false);
 | 
			
		||||
            
 | 
			
		||||
        } else {
 | 
			
		||||
            Log.d(TAG, "No Midtrans response data available, using fallback");
 | 
			
		||||
@ -565,75 +658,21 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
			
		||||
                String cardType = emvManager.getCardType() == 
 | 
			
		||||
                    com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
			
		||||
                navigateToResults(cardType, null, emvManager.getCardNo());
 | 
			
		||||
                updateBackendToSuccessAndNavigate(cardType, null, emvManager.getCardNo());
 | 
			
		||||
            }, 3000);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to extract bank information from Midtrans response
 | 
			
		||||
    private String extractBankFromResponse(JSONObject response) {
 | 
			
		||||
        if (response == null) {
 | 
			
		||||
            return "Unknown";
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            // Try different possible bank field names
 | 
			
		||||
            String[] possibleBankFields = {"bank", "issuer", "acquiring_bank", "card_type"};
 | 
			
		||||
            
 | 
			
		||||
            for (String field : possibleBankFields) {
 | 
			
		||||
                if (response.has(field)) {
 | 
			
		||||
                    String value = response.getString(field);
 | 
			
		||||
                    if (value != null && !value.trim().isEmpty()) {
 | 
			
		||||
                        Log.d(TAG, "Found bank info in field '" + field + "': " + value);
 | 
			
		||||
                        return value;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // If no bank field found, try to extract from card details
 | 
			
		||||
            if (response.has("payment_method")) {
 | 
			
		||||
                String paymentMethod = response.getString("payment_method");
 | 
			
		||||
                Log.d(TAG, "Payment method: " + paymentMethod);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            return "Unknown";
 | 
			
		||||
            
 | 
			
		||||
        } catch (JSONException e) {
 | 
			
		||||
            Log.e(TAG, "Error extracting bank from response: " + e.getMessage());
 | 
			
		||||
            return "Unknown";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String getUserFriendlyErrorMessage(String errorMessage) {
 | 
			
		||||
        if (errorMessage == null) {
 | 
			
		||||
            return "Payment processing failed";
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        String lowerError = errorMessage.toLowerCase();
 | 
			
		||||
        
 | 
			
		||||
        if (lowerError.contains("cvv") || lowerError.contains("cvv2")) {
 | 
			
		||||
            return "Card verification failed";
 | 
			
		||||
        } else if (lowerError.contains("token expired")) {
 | 
			
		||||
            return "Card session expired, please try again";
 | 
			
		||||
        } else if (lowerError.contains("network") || lowerError.contains("timeout")) {
 | 
			
		||||
            return "Network connection issue, please try again";
 | 
			
		||||
        } else if (lowerError.contains("decline") || lowerError.contains("deny")) {
 | 
			
		||||
            return "Transaction declined by bank";
 | 
			
		||||
        } else if (lowerError.contains("invalid")) {
 | 
			
		||||
            return "Invalid card information";
 | 
			
		||||
        } else {
 | 
			
		||||
            return "Payment processing failed, please try again";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onPaymentProgress(String message) {
 | 
			
		||||
        Log.d(TAG, "Midtrans payment progress: " + message);
 | 
			
		||||
        modalManager.showProcessingModal(message);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Enhanced Midtrans payment processing with transaction UUID
 | 
			
		||||
    private void processMidtransPayment() {
 | 
			
		||||
        Log.d(TAG, "=== STARTING ENHANCED MIDTRANS PAYMENT PROCESS ===");
 | 
			
		||||
        Log.d(TAG, "=== STARTING ENHANCED MIDTRANS PAYMENT ===");
 | 
			
		||||
        Log.d(TAG, "Using transaction UUID as order_id: " + transactionUuid);
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            // Extract additional EMV data if available
 | 
			
		||||
@ -661,19 +700,18 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // ✅ NEW: Use EMV-specific payment processing
 | 
			
		||||
            modalManager.showProcessingModal("Processing EMV Payment...");
 | 
			
		||||
            modalManager.showProcessingModal("Processing EMV Payment with Backend UUID...");
 | 
			
		||||
            
 | 
			
		||||
            // Process as EMV card payment (no CVV required)
 | 
			
		||||
            midtransPaymentManager.processEMVCardPayment(
 | 
			
		||||
            // ✅ ENHANCED: Process with transaction UUID as order_id
 | 
			
		||||
            midtransPaymentManager.processEMVCardPaymentWithOrderId(
 | 
			
		||||
                cardData, 
 | 
			
		||||
                Long.parseLong(transactionAmount), 
 | 
			
		||||
                referenceId, 
 | 
			
		||||
                transactionUuid, // Use transaction UUID as order_id
 | 
			
		||||
                emvTlvData
 | 
			
		||||
            );
 | 
			
		||||
            
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.e(TAG, "Error preparing Midtrans payment: " + e.getMessage(), e);
 | 
			
		||||
            Log.e(TAG, "Error preparing enhanced Midtrans payment: " + e.getMessage(), e);
 | 
			
		||||
            onChargeError("Failed to prepare payment data: " + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -684,7 +722,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
                        
 | 
			
		||||
            String rawExpiryDate = extractEMVTag("5F24", null);
 | 
			
		||||
            if (rawExpiryDate != null && rawExpiryDate.length() >= 4) {
 | 
			
		||||
                emvExpiryDate = rawExpiryDate.substring(0, 4) + "01"; // Add day for YYMMDD format
 | 
			
		||||
                emvExpiryDate = rawExpiryDate.substring(0, 4) + "01";
 | 
			
		||||
            } else {
 | 
			
		||||
                java.util.Calendar cal = java.util.Calendar.getInstance();
 | 
			
		||||
                cal.add(java.util.Calendar.YEAR, 2);
 | 
			
		||||
@ -692,10 +730,8 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
                    cal.get(java.util.Calendar.YEAR) % 100,
 | 
			
		||||
                    cal.get(java.util.Calendar.MONTH) + 1);
 | 
			
		||||
            }            
 | 
			
		||||
            // Extract AID from EMV tag 9F06 (Application Identifier - Terminal)
 | 
			
		||||
            emvAidIdentifier = extractEMVTag("9F06", "A0000000031010"); // Default to Visa AID
 | 
			
		||||
            
 | 
			
		||||
            // ✅ NEW: Build comprehensive TLV data for EMV processing
 | 
			
		||||
            emvAidIdentifier = extractEMVTag("9F06", "A0000000031010");
 | 
			
		||||
            emvTlvData = buildEMVTLVData();
 | 
			
		||||
            
 | 
			
		||||
            Log.d(TAG, "✅ Enhanced EMV data extracted:");
 | 
			
		||||
@ -708,8 +744,8 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            Log.e(TAG, "Error extracting EMV data: " + e.getMessage(), e);
 | 
			
		||||
            // Set fallback values
 | 
			
		||||
            emvCardholderName = "EMV CARDHOLDER";
 | 
			
		||||
            emvExpiryDate = "251201"; // Dec 2025
 | 
			
		||||
            emvAidIdentifier = "A0000000031010"; // Visa AID
 | 
			
		||||
            emvExpiryDate = "251201";
 | 
			
		||||
            emvAidIdentifier = "A0000000031010";
 | 
			
		||||
            emvTlvData = "";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -717,16 +753,12 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
    private String extractEMVTag(String tag, String defaultValue) {
 | 
			
		||||
        try {            
 | 
			
		||||
            switch (tag) {
 | 
			
		||||
                case "5F20": // Cardholder Name
 | 
			
		||||
                case "5F20":
 | 
			
		||||
                    return defaultValue != null ? defaultValue : "EMV CARDHOLDER";
 | 
			
		||||
                    
 | 
			
		||||
                case "5F24": // Application Expiration Date
 | 
			
		||||
                    // Return null to trigger date generation logic
 | 
			
		||||
                case "5F24":
 | 
			
		||||
                    return null;
 | 
			
		||||
                    
 | 
			
		||||
                case "9F06": // Application Identifier (Terminal)
 | 
			
		||||
                case "9F06":
 | 
			
		||||
                    return defaultValue != null ? defaultValue : "A0000000031010";
 | 
			
		||||
                    
 | 
			
		||||
                default:
 | 
			
		||||
                    return defaultValue;
 | 
			
		||||
            }
 | 
			
		||||
@ -741,29 +773,13 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        try {
 | 
			
		||||
            StringBuilder tlvBuilder = new StringBuilder();
 | 
			
		||||
            
 | 
			
		||||
            // Add key EMV tags that might be useful for Midtrans
 | 
			
		||||
            // Format: TAG=VALUE;TAG=VALUE;...
 | 
			
		||||
            
 | 
			
		||||
            // Application Transaction Counter (9F36)
 | 
			
		||||
            tlvBuilder.append("9F36=").append(String.format("%04X", 
 | 
			
		||||
                (int)(Math.random() * 65535))).append(";");
 | 
			
		||||
            
 | 
			
		||||
            // Terminal Verification Results (95)
 | 
			
		||||
            tlvBuilder.append("95=0000000000;");
 | 
			
		||||
            
 | 
			
		||||
            // Transaction Status Information (9B)
 | 
			
		||||
            tlvBuilder.append("9B=E800;");
 | 
			
		||||
            
 | 
			
		||||
            // Application Interchange Profile (82)
 | 
			
		||||
            tlvBuilder.append("82=1C00;");
 | 
			
		||||
            
 | 
			
		||||
            // Cryptogram Information Data (9F27)
 | 
			
		||||
            tlvBuilder.append("9F27=80;");
 | 
			
		||||
            
 | 
			
		||||
            // Application Cryptogram (9F26)
 | 
			
		||||
            tlvBuilder.append("9F26=").append(generateRandomHex(16)).append(";");
 | 
			
		||||
            
 | 
			
		||||
            // Unpredictable Number (9F37)
 | 
			
		||||
            tlvBuilder.append("9F37=").append(generateRandomHex(8)).append(";");
 | 
			
		||||
            
 | 
			
		||||
            String tlvData = tlvBuilder.toString();
 | 
			
		||||
@ -785,6 +801,84 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        return hex.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Update backend to SUCCESS and navigate
 | 
			
		||||
    private void updateBackendToSuccessAndNavigate(String cardType, Bundle cardData, String cardNo) {
 | 
			
		||||
        // Update backend status to SUCCESS
 | 
			
		||||
        backendTransactionStatus = "SUCCESS";
 | 
			
		||||
        modalManager.showProcessingModal("Finalizing transaction...");
 | 
			
		||||
        
 | 
			
		||||
        // Update backend status
 | 
			
		||||
        backendManager.postSuccessTransaction(paymentType, referenceId, Long.parseLong(transactionAmount));
 | 
			
		||||
        
 | 
			
		||||
        // Navigate after delay
 | 
			
		||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
			
		||||
            navigateToResults(cardType, cardData, cardNo);
 | 
			
		||||
        }, 1500);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ====== NAVIGATION METHODS ======
 | 
			
		||||
    private void navigateToResults(String cardType, Bundle cardData, String cardNo) {
 | 
			
		||||
        showSuccessScreen(() -> {
 | 
			
		||||
            Intent intent = new Intent(this, ResultTransactionActivity.class);
 | 
			
		||||
            intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
			
		||||
            intent.putExtra("CARD_TYPE", cardType);
 | 
			
		||||
            intent.putExtra("EMV_MODE", isEMVMode);
 | 
			
		||||
            intent.putExtra("REFERENCE_ID", referenceId);
 | 
			
		||||
            intent.putExtra("PAYMENT_TYPE", paymentType); // ✅ NEW: Include payment type
 | 
			
		||||
            intent.putExtra("TRANSACTION_UUID", transactionUuid); // ✅ NEW: Include transaction UUID
 | 
			
		||||
            
 | 
			
		||||
            if (cardData != null) {
 | 
			
		||||
                intent.putExtra("CARD_DATA", cardData);
 | 
			
		||||
            }
 | 
			
		||||
            if (cardNo != null) {
 | 
			
		||||
                intent.putExtra("CARD_NO", cardNo);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // ✅ NEW: Include backend response
 | 
			
		||||
            if (lastBackendResponse != null) {
 | 
			
		||||
                intent.putExtra("BACKEND_RESPONSE", lastBackendResponse.toString());
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            finish();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Enhanced navigation with comprehensive data
 | 
			
		||||
    private void navigateToEnhancedResults(JSONObject midtransResponse, boolean paymentSuccess) {
 | 
			
		||||
        Log.d(TAG, "=== NAVIGATING TO ENHANCED RESULTS ===");
 | 
			
		||||
        Log.d(TAG, "Payment Success: " + paymentSuccess);
 | 
			
		||||
        Log.d(TAG, "Transaction UUID: " + transactionUuid);
 | 
			
		||||
        
 | 
			
		||||
        showSuccessScreen(() -> {
 | 
			
		||||
            Intent intent = new Intent(this, ResultTransactionActivity.class);
 | 
			
		||||
            intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
			
		||||
            intent.putExtra("CARD_TYPE", "EMV_MIDTRANS_BACKEND");
 | 
			
		||||
            intent.putExtra("EMV_MODE", true);
 | 
			
		||||
            intent.putExtra("REFERENCE_ID", referenceId);
 | 
			
		||||
            intent.putExtra("CARD_NO", emvCardNumber);
 | 
			
		||||
            intent.putExtra("PAYMENT_SUCCESS", paymentSuccess);
 | 
			
		||||
            intent.putExtra("PAYMENT_TYPE", paymentType); // ✅ NEW
 | 
			
		||||
            intent.putExtra("TRANSACTION_UUID", transactionUuid); // ✅ NEW
 | 
			
		||||
            
 | 
			
		||||
            // Enhanced data
 | 
			
		||||
            if (midtransResponse != null) {
 | 
			
		||||
                intent.putExtra("MIDTRANS_RESPONSE", midtransResponse.toString());
 | 
			
		||||
            }
 | 
			
		||||
            if (lastBackendResponse != null) {
 | 
			
		||||
                intent.putExtra("BACKEND_RESPONSE", lastBackendResponse.toString());
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Add additional EMV data
 | 
			
		||||
            intent.putExtra("EMV_CARDHOLDER_NAME", emvCardholderName);
 | 
			
		||||
            intent.putExtra("EMV_AID", emvAidIdentifier);
 | 
			
		||||
            intent.putExtra("EMV_EXPIRY", emvExpiryDate);
 | 
			
		||||
            
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            finish();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ====== HELPER METHODS ======
 | 
			
		||||
    private void showAppSelectDialog(String[] candidateNames) {
 | 
			
		||||
        mAppSelectDialog = new AlertDialog.Builder(this)
 | 
			
		||||
@ -797,241 +891,34 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        mAppSelectDialog.show();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private void navigateToResults(String cardType, Bundle cardData, String cardNo) {
 | 
			
		||||
        // modalManager.hideModal();
 | 
			
		||||
        
 | 
			
		||||
        showSuccessScreen(() -> {
 | 
			
		||||
            Intent intent = new Intent(this, ResultTransactionActivity.class);
 | 
			
		||||
            intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
			
		||||
            intent.putExtra("CARD_TYPE", cardType);
 | 
			
		||||
            intent.putExtra("EMV_MODE", isEMVMode);
 | 
			
		||||
            intent.putExtra("REFERENCE_ID", referenceId);
 | 
			
		||||
            
 | 
			
		||||
            if (cardData != null) {
 | 
			
		||||
                intent.putExtra("CARD_DATA", cardData);
 | 
			
		||||
            }
 | 
			
		||||
            if (cardNo != null) {
 | 
			
		||||
                intent.putExtra("CARD_NO", cardNo);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            finish();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Better navigation with comprehensive data
 | 
			
		||||
    private void navigateToMidtransResults(JSONObject midtransResponse, boolean paymentSuccess) {
 | 
			
		||||
        Log.d(TAG, "=== NAVIGATING TO MIDTRANS RESULTS ===");
 | 
			
		||||
        Log.d(TAG, "Payment Success: " + paymentSuccess);
 | 
			
		||||
        Log.d(TAG, "Response Length: " + (midtransResponse != null ? midtransResponse.length() : 0));
 | 
			
		||||
        
 | 
			
		||||
        // ✅ ENHANCED: Validate and enhance response data
 | 
			
		||||
        JSONObject enhancedResponse = enhanceMidtransResponse(midtransResponse);
 | 
			
		||||
        
 | 
			
		||||
        showSuccessScreen(() -> {
 | 
			
		||||
            Intent intent = new Intent(this, ResultTransactionActivity.class);
 | 
			
		||||
            intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
			
		||||
            intent.putExtra("CARD_TYPE", "EMV_MIDTRANS");
 | 
			
		||||
            intent.putExtra("EMV_MODE", true);
 | 
			
		||||
            intent.putExtra("REFERENCE_ID", referenceId);
 | 
			
		||||
            intent.putExtra("CARD_NO", emvCardNumber);
 | 
			
		||||
            intent.putExtra("PAYMENT_SUCCESS", paymentSuccess);
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Send enhanced response
 | 
			
		||||
            if (enhancedResponse != null) {
 | 
			
		||||
                String responseString = enhancedResponse.toString();
 | 
			
		||||
                intent.putExtra("MIDTRANS_RESPONSE", responseString);
 | 
			
		||||
                Log.d(TAG, "✅ Sending Midtrans response: " + responseString);
 | 
			
		||||
            } else {
 | 
			
		||||
                Log.w(TAG, "⚠️ No Midtrans response to send");
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Add additional EMV data for receipt
 | 
			
		||||
            intent.putExtra("EMV_CARDHOLDER_NAME", emvCardholderName);
 | 
			
		||||
            intent.putExtra("EMV_AID", emvAidIdentifier);
 | 
			
		||||
            intent.putExtra("EMV_EXPIRY", emvExpiryDate);
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Add debugging extras
 | 
			
		||||
            intent.putExtra("DEBUG_BANK_SOURCE", "midtrans_response");
 | 
			
		||||
            
 | 
			
		||||
            startActivity(intent);
 | 
			
		||||
            finish();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Method to enhance Midtrans response with additional data
 | 
			
		||||
    private JSONObject enhanceMidtransResponse(JSONObject originalResponse) {
 | 
			
		||||
        if (originalResponse == null) {
 | 
			
		||||
            Log.w(TAG, "Original response is null, creating fallback response");
 | 
			
		||||
            return createFallbackMidtransResponse();
 | 
			
		||||
    private String getUserFriendlyErrorMessage(String errorMessage) {
 | 
			
		||||
        if (errorMessage == null) {
 | 
			
		||||
            return "Payment processing failed";
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        try {
 | 
			
		||||
            // Clone the original response
 | 
			
		||||
            JSONObject enhanced = new JSONObject(originalResponse.toString());
 | 
			
		||||
        String lowerError = errorMessage.toLowerCase();
 | 
			
		||||
        
 | 
			
		||||
            // ✅ ENHANCED: Add bank information if missing
 | 
			
		||||
            if (!enhanced.has("bank") || enhanced.getString("bank").trim().isEmpty()) {
 | 
			
		||||
                String bankFromCard = determineBankFromCard();
 | 
			
		||||
                if (bankFromCard != null) {
 | 
			
		||||
                    enhanced.put("bank", bankFromCard);
 | 
			
		||||
                    Log.d(TAG, "✅ Added bank to response: " + bankFromCard);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Add EMV data if available
 | 
			
		||||
            if (emvCardNumber != null) {
 | 
			
		||||
                enhanced.put("emv_card_number", maskCardNumber(emvCardNumber));
 | 
			
		||||
            }
 | 
			
		||||
            if (emvCardholderName != null) {
 | 
			
		||||
                enhanced.put("emv_cardholder_name", emvCardholderName);
 | 
			
		||||
            }
 | 
			
		||||
            if (emvAidIdentifier != null) {
 | 
			
		||||
                enhanced.put("emv_aid", emvAidIdentifier);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // Add transaction metadata
 | 
			
		||||
            enhanced.put("processing_timestamp", System.currentTimeMillis());
 | 
			
		||||
            enhanced.put("emv_mode", true);
 | 
			
		||||
            
 | 
			
		||||
            Log.d(TAG, "✅ Enhanced response created with " + enhanced.length() + " fields");
 | 
			
		||||
            return enhanced;
 | 
			
		||||
            
 | 
			
		||||
        } catch (JSONException e) {
 | 
			
		||||
            Log.e(TAG, "Error enhancing response: " + e.getMessage());
 | 
			
		||||
            return originalResponse;
 | 
			
		||||
        if (lowerError.contains("cvv") || lowerError.contains("cvv2")) {
 | 
			
		||||
            return "Card verification failed";
 | 
			
		||||
        } else if (lowerError.contains("token expired")) {
 | 
			
		||||
            return "Card session expired, please try again";
 | 
			
		||||
        } else if (lowerError.contains("network") || lowerError.contains("timeout")) {
 | 
			
		||||
            return "Network connection issue, please try again";
 | 
			
		||||
        } else if (lowerError.contains("decline") || lowerError.contains("deny")) {
 | 
			
		||||
            return "Transaction declined by bank";
 | 
			
		||||
        } else if (lowerError.contains("invalid")) {
 | 
			
		||||
            return "Invalid card information";
 | 
			
		||||
        } else {
 | 
			
		||||
            return "Payment processing failed, please try again";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Create fallback response when Midtrans response is unavailable
 | 
			
		||||
    private JSONObject createFallbackMidtransResponse() {
 | 
			
		||||
        try {
 | 
			
		||||
            JSONObject fallback = new JSONObject();
 | 
			
		||||
            
 | 
			
		||||
            // Basic transaction info
 | 
			
		||||
            fallback.put("transaction_id", "FALLBACK_" + System.currentTimeMillis());
 | 
			
		||||
            fallback.put("order_id", "ORDER_" + System.currentTimeMillis());
 | 
			
		||||
            fallback.put("gross_amount", transactionAmount);
 | 
			
		||||
            fallback.put("transaction_status", "processed");
 | 
			
		||||
            fallback.put("status_code", "200");
 | 
			
		||||
            fallback.put("status_message", "Success");
 | 
			
		||||
            
 | 
			
		||||
            // ✅ ENHANCED: Determine bank from available data
 | 
			
		||||
            String bankFromCard = determineBankFromCard();
 | 
			
		||||
            if (bankFromCard != null) {
 | 
			
		||||
                fallback.put("bank", bankFromCard);
 | 
			
		||||
            } else {
 | 
			
		||||
                fallback.put("bank", "BCA"); // Default
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            // EMV data
 | 
			
		||||
            if (emvCardNumber != null) {
 | 
			
		||||
                fallback.put("emv_card_number", maskCardNumber(emvCardNumber));
 | 
			
		||||
            }
 | 
			
		||||
            if (emvCardholderName != null) {
 | 
			
		||||
                fallback.put("emv_cardholder_name", emvCardholderName);
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            fallback.put("payment_type", "credit_card");
 | 
			
		||||
            fallback.put("transaction_time", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));
 | 
			
		||||
            fallback.put("is_fallback", true);
 | 
			
		||||
            
 | 
			
		||||
            Log.d(TAG, "✅ Created fallback response: " + fallback.toString());
 | 
			
		||||
            return fallback;
 | 
			
		||||
            
 | 
			
		||||
        } catch (JSONException e) {
 | 
			
		||||
            Log.e(TAG, "Error creating fallback response: " + e.getMessage());
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Determine bank from card number or EMV data
 | 
			
		||||
    private String determineBankFromCard() {
 | 
			
		||||
        // Priority 1: Use card BIN
 | 
			
		||||
        if (emvCardNumber != null && emvCardNumber.length() >= 6) {
 | 
			
		||||
            String bin = emvCardNumber.substring(0, 6);
 | 
			
		||||
            return getBankFromBin(bin);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Priority 2: Use EMV AID
 | 
			
		||||
        if (emvAidIdentifier != null) {
 | 
			
		||||
            return getBankFromAid(emvAidIdentifier);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ ENHANCED: Better BIN to bank mapping
 | 
			
		||||
    private String getBankFromBin(String bin) {
 | 
			
		||||
        if (bin == null || bin.length() < 4) {
 | 
			
		||||
            return "BCA"; // Default
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Get first 4 digits for matching
 | 
			
		||||
        String bin4 = bin.substring(0, 4);
 | 
			
		||||
        
 | 
			
		||||
        // Indonesian Bank BIN mapping
 | 
			
		||||
        switch (bin4) {
 | 
			
		||||
            // BCA
 | 
			
		||||
            case "4621": case "4699": case "5221": case "6277": 
 | 
			
		||||
                return "BCA";
 | 
			
		||||
                
 | 
			
		||||
            // MANDIRI
 | 
			
		||||
            case "4313": case "5573": case "6011": case "6234":
 | 
			
		||||
            case "5406": case "4097":
 | 
			
		||||
                return "MANDIRI";
 | 
			
		||||
                
 | 
			
		||||
            // BNI  
 | 
			
		||||
            case "4603": case "1946": case "5264":
 | 
			
		||||
                return "BNI";
 | 
			
		||||
                
 | 
			
		||||
            // BRI
 | 
			
		||||
            case "4578": case "4479": case "5208": case "4486":
 | 
			
		||||
                return "BRI";
 | 
			
		||||
                
 | 
			
		||||
            // CIMB NIAGA
 | 
			
		||||
            case "4599": case "5249": case "4543":
 | 
			
		||||
                return "CIMB NIAGA";
 | 
			
		||||
                
 | 
			
		||||
            // DANAMON
 | 
			
		||||
            case "4055": case "5108": case "4631":
 | 
			
		||||
                return "DANAMON";
 | 
			
		||||
                
 | 
			
		||||
            default:
 | 
			
		||||
                // Try 6-digit BIN patterns
 | 
			
		||||
                if (bin.length() >= 6) {
 | 
			
		||||
                    String bin6 = bin.substring(0, 6);
 | 
			
		||||
                    // Add more specific 6-digit patterns here if needed
 | 
			
		||||
                    Log.d(TAG, "Unknown BIN pattern: " + bin6);
 | 
			
		||||
                }
 | 
			
		||||
                return "BCA"; // Default fallback
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String getBankFromAid(String aid) {
 | 
			
		||||
        if (aid == null) return "BCA";
 | 
			
		||||
        
 | 
			
		||||
        // AID patterns for Indonesian banks
 | 
			
		||||
        if (aid.contains("A0000000031010")) {
 | 
			
		||||
            return "BCA"; // Common for VISA
 | 
			
		||||
        } else if (aid.contains("A0000000041010")) {
 | 
			
		||||
            return "MANDIRI"; // Common for Mastercard
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        return "BCA"; // Default
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ✅ NEW: Add getter for last response (for debugging)
 | 
			
		||||
    public JSONObject getLastMidtransResponse() {
 | 
			
		||||
        return lastMidtransResponse;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private void restartScanningAfterDelay() {
 | 
			
		||||
        Log.d(TAG, "Restarting scanning after delay...");
 | 
			
		||||
        
 | 
			
		||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
			
		||||
            if (!isFinishing()) {
 | 
			
		||||
                showModalAndStartScanning();
 | 
			
		||||
                startCardScanningFlow();
 | 
			
		||||
            }
 | 
			
		||||
        }, 2000);
 | 
			
		||||
    }
 | 
			
		||||
@ -1041,11 +928,9 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
            try {
 | 
			
		||||
                Log.d(TAG, "Resetting EMV process...");
 | 
			
		||||
                
 | 
			
		||||
                // Cancel any existing operations
 | 
			
		||||
                cardScannerManager.stopScanning();
 | 
			
		||||
                Thread.sleep(500);
 | 
			
		||||
                
 | 
			
		||||
                // Reset EMV process
 | 
			
		||||
                emvManager.resetEMVProcess();
 | 
			
		||||
                Thread.sleep(1500);
 | 
			
		||||
                
 | 
			
		||||
@ -1069,11 +954,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
			
		||||
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Generate reference ID for transaction tracking
 | 
			
		||||
    private String generateReferenceId() {
 | 
			
		||||
        return "ref-" + System.currentTimeMillis() + "-" + (int)(Math.random() * 10000);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    private String maskCardNumber(String cardNumber) {
 | 
			
		||||
        if (cardNumber == null || cardNumber.length() < 8) {
 | 
			
		||||
            return cardNumber;
 | 
			
		||||
 | 
			
		||||
@ -16,17 +16,15 @@ import java.net.URI;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * MidtransCardPaymentManager - Enhanced Version for EMV Card Processing
 | 
			
		||||
 * MidtransCardPaymentManager - Enhanced Version with Backend Integration
 | 
			
		||||
 * 
 | 
			
		||||
 * Key Features:
 | 
			
		||||
 * - Uses static CVV "493" for all transactions (both EMV and regular cards)
 | 
			
		||||
 * - EMV-first approach with tokenization fallback
 | 
			
		||||
 * - Supports custom order_id from backend transaction_uuid
 | 
			
		||||
 * - Handles Midtrans API requirements where CVV is mandatory even for EMV
 | 
			
		||||
 * - Comprehensive error handling and retry logic
 | 
			
		||||
 * - Enhanced response processing with bank detection
 | 
			
		||||
 * 
 | 
			
		||||
 * Note: Midtrans sandbox environment requires CVV even for EMV chip transactions
 | 
			
		||||
 * during tokenization, so we use static CVV "493" as per curl example.
 | 
			
		||||
 */
 | 
			
		||||
public class MidtransCardPaymentManager {
 | 
			
		||||
    private static final String TAG = "MidtransCardPayment";
 | 
			
		||||
@ -46,9 +44,10 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
    private int retryCount = 0;
 | 
			
		||||
    private static final int MAX_RETRY = 2;
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Store last response for debugging
 | 
			
		||||
    // ✅ ENHANCED: Store last response and custom order ID
 | 
			
		||||
    private JSONObject lastResponse;
 | 
			
		||||
    private String lastErrorMessage;
 | 
			
		||||
    private String customOrderId; // ✅ NEW: For backend transaction_uuid
 | 
			
		||||
    
 | 
			
		||||
    public interface MidtransCardPaymentCallback {
 | 
			
		||||
        void onTokenizeSuccess(String cardToken);
 | 
			
		||||
@ -73,6 +72,30 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        return lastErrorMessage;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * ✅ NEW: Process EMV card payment with custom order ID (transaction_uuid from backend)
 | 
			
		||||
     */
 | 
			
		||||
    public void processEMVCardPaymentWithOrderId(CardData cardData, long amount, String orderId, String emvData) {
 | 
			
		||||
        this.customOrderId = orderId; // Store custom order ID
 | 
			
		||||
        
 | 
			
		||||
        Log.d(TAG, "=== PROCESSING EMV PAYMENT WITH CUSTOM ORDER ID ===");
 | 
			
		||||
        Log.d(TAG, "Custom Order ID (Transaction UUID): " + orderId);
 | 
			
		||||
        Log.d(TAG, "Amount: " + amount);
 | 
			
		||||
        Log.d(TAG, "Card PAN: " + maskCardNumber(cardData.getPan()));
 | 
			
		||||
        Log.d(TAG, "Payment Mode: EMV with Backend Integration");
 | 
			
		||||
        Log.d(TAG, "================================================");
 | 
			
		||||
        
 | 
			
		||||
        // Reset retry counter
 | 
			
		||||
        retryCount = 0;
 | 
			
		||||
        
 | 
			
		||||
        if (callback != null) {
 | 
			
		||||
            callback.onPaymentProgress("Processing EMV payment with backend UUID...");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Use direct charge with custom order ID
 | 
			
		||||
        new EMVDirectChargeWithOrderIdTask(cardData, amount, orderId, emvData).execute();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Process EMV card payment - handles EMV-specific requirements
 | 
			
		||||
     */
 | 
			
		||||
@ -84,8 +107,9 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Reset retry counter
 | 
			
		||||
        // Reset retry counter and custom order ID
 | 
			
		||||
        retryCount = 0;
 | 
			
		||||
        customOrderId = null;
 | 
			
		||||
        
 | 
			
		||||
        Log.d(TAG, "=== STARTING EMV MIDTRANS PAYMENT ===");
 | 
			
		||||
        Log.d(TAG, "Reference ID: " + referenceId);
 | 
			
		||||
@ -114,6 +138,7 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        retryCount = 0;
 | 
			
		||||
        customOrderId = null;
 | 
			
		||||
        
 | 
			
		||||
        Log.d(TAG, "=== STARTING REGULAR CARD PAYMENT ===");
 | 
			
		||||
        Log.d(TAG, "Using tokenization flow");
 | 
			
		||||
@ -126,6 +151,498 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        new TokenizeCardTask(cardData, amount, referenceId).execute();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * ✅ NEW: EMV Direct Charge with Custom Order ID - uses backend transaction_uuid
 | 
			
		||||
     */
 | 
			
		||||
    private class EMVDirectChargeWithOrderIdTask extends AsyncTask<Void, Void, Boolean> {
 | 
			
		||||
        private CardData cardData;
 | 
			
		||||
        private long amount;
 | 
			
		||||
        private String orderId;
 | 
			
		||||
        private String emvData;
 | 
			
		||||
        private String errorMessage;
 | 
			
		||||
        private JSONObject chargeResponse;
 | 
			
		||||
        
 | 
			
		||||
        public EMVDirectChargeWithOrderIdTask(CardData cardData, long amount, String orderId, String emvData) {
 | 
			
		||||
            this.cardData = cardData;
 | 
			
		||||
            this.amount = amount;
 | 
			
		||||
            this.orderId = orderId;
 | 
			
		||||
            this.emvData = emvData;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Boolean doInBackground(Void... voids) {
 | 
			
		||||
            try {
 | 
			
		||||
                // Build EMV-specific charge payload with custom order ID
 | 
			
		||||
                JSONObject payload = new JSONObject();
 | 
			
		||||
                payload.put("payment_type", "credit_card");
 | 
			
		||||
                
 | 
			
		||||
                // Transaction details with custom order ID
 | 
			
		||||
                JSONObject transactionDetails = new JSONObject();
 | 
			
		||||
                transactionDetails.put("order_id", orderId); // ✅ Use backend transaction_uuid
 | 
			
		||||
                transactionDetails.put("gross_amount", amount);
 | 
			
		||||
                payload.put("transaction_details", transactionDetails);
 | 
			
		||||
                
 | 
			
		||||
                // EMV Credit card data (no tokenization)
 | 
			
		||||
                JSONObject creditCard = new JSONObject();
 | 
			
		||||
                creditCard.put("card_number", cardData.getPan());
 | 
			
		||||
                creditCard.put("card_exp_month", cardData.getExpiryMonth());
 | 
			
		||||
                creditCard.put("card_exp_year", cardData.getExpiryYear());
 | 
			
		||||
                
 | 
			
		||||
                // Include static CVV even for EMV (Midtrans may require it)
 | 
			
		||||
                creditCard.put("card_cvv", STATIC_CVV);
 | 
			
		||||
                Log.d(TAG, "EMV Transaction: Including static CVV (" + STATIC_CVV + ") for Midtrans compatibility");
 | 
			
		||||
                
 | 
			
		||||
                // Add EMV data if available
 | 
			
		||||
                if (emvData != null && !emvData.isEmpty()) {
 | 
			
		||||
                    creditCard.put("emv_data", emvData);
 | 
			
		||||
                    creditCard.put("authentication_mode", "chip");
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                payload.put("credit_card", creditCard);
 | 
			
		||||
                
 | 
			
		||||
                // Item details
 | 
			
		||||
                JSONArray itemDetails = new JSONArray();
 | 
			
		||||
                JSONObject item = new JSONObject();
 | 
			
		||||
                item.put("id", "emv_backend1");
 | 
			
		||||
                item.put("price", amount);
 | 
			
		||||
                item.put("quantity", 1);
 | 
			
		||||
                item.put("name", "EMV Backend Transaction");
 | 
			
		||||
                item.put("brand", "EMV Backend Payment");
 | 
			
		||||
                item.put("category", "Backend Transaction");
 | 
			
		||||
                item.put("merchant_name", "EDC-Store-Backend");
 | 
			
		||||
                itemDetails.put(item);
 | 
			
		||||
                payload.put("item_details", itemDetails);
 | 
			
		||||
                
 | 
			
		||||
                // Customer details (same as curl example)
 | 
			
		||||
                addCustomerDetails(payload);
 | 
			
		||||
                
 | 
			
		||||
                Log.d(TAG, "=== EMV BACKEND DIRECT CHARGE ===");
 | 
			
		||||
                Log.d(TAG, "Order ID (Backend UUID): " + orderId);
 | 
			
		||||
                Log.d(TAG, "Amount: " + amount);
 | 
			
		||||
                Log.d(TAG, "Card: " + maskCardNumber(cardData.getPan()));
 | 
			
		||||
                Log.d(TAG, "Mode: EMV Backend Direct (No Token)");
 | 
			
		||||
                Log.d(TAG, "=================================");
 | 
			
		||||
                
 | 
			
		||||
                // Make charge request
 | 
			
		||||
                return makeChargeRequest(payload);
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "EMV Backend Direct Charge exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "EMV backend payment error: " + e.getMessage();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(Boolean success) {
 | 
			
		||||
            if (success && chargeResponse != null && callback != null) {
 | 
			
		||||
                Log.d(TAG, "✅ EMV Backend charge successful!");
 | 
			
		||||
                callback.onChargeSuccess(chargeResponse);
 | 
			
		||||
            } else if (callback != null) {
 | 
			
		||||
                // Fallback to tokenization if direct charge fails
 | 
			
		||||
                Log.w(TAG, "EMV backend direct charge failed, trying tokenization fallback...");
 | 
			
		||||
                callback.onPaymentProgress("Retrying with tokenization...");
 | 
			
		||||
                new TokenizeCardWithOrderIdTask(cardData, amount, orderId).execute();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private Boolean makeChargeRequest(JSONObject payload) {
 | 
			
		||||
            try {
 | 
			
		||||
                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
 | 
			
		||||
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
			
		||||
                conn.setRequestMethod("POST");
 | 
			
		||||
                conn.setRequestProperty("Accept", "application/json");
 | 
			
		||||
                conn.setRequestProperty("Content-Type", "application/json");
 | 
			
		||||
                conn.setRequestProperty("Authorization", MIDTRANS_SERVER_AUTH);
 | 
			
		||||
                conn.setDoOutput(true);
 | 
			
		||||
                conn.setConnectTimeout(30000);
 | 
			
		||||
                conn.setReadTimeout(30000);
 | 
			
		||||
                
 | 
			
		||||
                try (OutputStream os = conn.getOutputStream()) {
 | 
			
		||||
                    byte[] input = payload.toString().getBytes("utf-8");
 | 
			
		||||
                    os.write(input, 0, input.length);
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                int responseCode = conn.getResponseCode();
 | 
			
		||||
                Log.d(TAG, "EMV Backend Charge response code: " + responseCode);
 | 
			
		||||
                
 | 
			
		||||
                BufferedReader br;
 | 
			
		||||
                StringBuilder response = new StringBuilder();
 | 
			
		||||
                String responseLine;
 | 
			
		||||
                
 | 
			
		||||
                if (responseCode == 200 || responseCode == 201) {
 | 
			
		||||
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
			
		||||
                } else {
 | 
			
		||||
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                while ((responseLine = br.readLine()) != null) {
 | 
			
		||||
                    response.append(responseLine.trim());
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                String responseString = response.toString();
 | 
			
		||||
                Log.d(TAG, "EMV Backend Charge response: " + responseString);
 | 
			
		||||
                
 | 
			
		||||
                // Store response for debugging
 | 
			
		||||
                try {
 | 
			
		||||
                    chargeResponse = new JSONObject(responseString);
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                    
 | 
			
		||||
                    // Enhanced: Add bank detection if missing
 | 
			
		||||
                    enhanceResponseWithBankInfo(chargeResponse);
 | 
			
		||||
                    
 | 
			
		||||
                } catch (JSONException e) {
 | 
			
		||||
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
			
		||||
                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                return processChargeResponse(chargeResponse, responseCode);
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "EMV Backend Charge request exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Network error: " + e.getMessage();
 | 
			
		||||
                lastErrorMessage = errorMessage;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private Boolean processChargeResponse(JSONObject response, int httpCode) {
 | 
			
		||||
            try {
 | 
			
		||||
                String statusCode = response.optString("status_code", "");
 | 
			
		||||
                String statusMessage = response.optString("status_message", "");
 | 
			
		||||
                String transactionStatus = response.optString("transaction_status", "");
 | 
			
		||||
                String fraudStatus = response.optString("fraud_status", "");
 | 
			
		||||
                
 | 
			
		||||
                Log.d(TAG, "=== BACKEND CHARGE RESPONSE ANALYSIS ===");
 | 
			
		||||
                Log.d(TAG, "HTTP Code: " + httpCode);
 | 
			
		||||
                Log.d(TAG, "Status Code: " + statusCode);
 | 
			
		||||
                Log.d(TAG, "Status Message: " + statusMessage);
 | 
			
		||||
                Log.d(TAG, "Transaction Status: " + transactionStatus);
 | 
			
		||||
                Log.d(TAG, "Fraud Status: " + fraudStatus);
 | 
			
		||||
                Log.d(TAG, "Order ID: " + response.optString("order_id", ""));
 | 
			
		||||
                Log.d(TAG, "========================================");
 | 
			
		||||
                
 | 
			
		||||
                // Handle specific error cases
 | 
			
		||||
                if ("411".equals(statusCode)) {
 | 
			
		||||
                    errorMessage = "Token expired: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if ("400".equals(statusCode)) {
 | 
			
		||||
                    errorMessage = "Bad request: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if ("202".equals(statusCode) && "deny".equals(transactionStatus)) {
 | 
			
		||||
                    errorMessage = "Transaction denied: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if (httpCode != 200 && httpCode != 201) {
 | 
			
		||||
                    errorMessage = "HTTP error: " + httpCode + " - " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Success conditions
 | 
			
		||||
                if ("200".equals(statusCode) && 
 | 
			
		||||
                    ("capture".equals(transactionStatus) || 
 | 
			
		||||
                     "settlement".equals(transactionStatus) || 
 | 
			
		||||
                     "pending".equals(transactionStatus))) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else if ("201".equals(statusCode)) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    errorMessage = "Transaction failed: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Error processing backend charge response: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Response processing error: " + e.getMessage();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * ✅ NEW: Tokenize Card with Custom Order ID - fallback method
 | 
			
		||||
     */
 | 
			
		||||
    private class TokenizeCardWithOrderIdTask extends AsyncTask<Void, Void, String> {
 | 
			
		||||
        private CardData cardData;
 | 
			
		||||
        private long amount;
 | 
			
		||||
        private String orderId;
 | 
			
		||||
        private String errorMessage;
 | 
			
		||||
        
 | 
			
		||||
        public TokenizeCardWithOrderIdTask(CardData cardData, long amount, String orderId) {
 | 
			
		||||
            this.cardData = cardData;
 | 
			
		||||
            this.amount = amount;
 | 
			
		||||
            this.orderId = orderId;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected String doInBackground(Void... voids) {
 | 
			
		||||
            try {
 | 
			
		||||
                StringBuilder urlBuilder = new StringBuilder(MIDTRANS_TOKEN_URL);
 | 
			
		||||
                urlBuilder.append("?card_number=").append(cardData.getPan());
 | 
			
		||||
                urlBuilder.append("&card_exp_month=").append(cardData.getExpiryMonth());
 | 
			
		||||
                urlBuilder.append("&card_exp_year=").append(cardData.getExpiryYear());
 | 
			
		||||
                
 | 
			
		||||
                // Always include CVV for tokenization
 | 
			
		||||
                String cvvToUse = determineCVV(cardData);
 | 
			
		||||
                urlBuilder.append("&card_cvv=").append(cvvToUse);
 | 
			
		||||
                Log.d(TAG, "Using CVV " + cvvToUse + " for backend tokenization");
 | 
			
		||||
                
 | 
			
		||||
                urlBuilder.append("&client_key=").append(MIDTRANS_CLIENT_KEY);
 | 
			
		||||
                
 | 
			
		||||
                Log.d(TAG, "Backend Tokenization URL: " + maskUrl(urlBuilder.toString()));
 | 
			
		||||
                
 | 
			
		||||
                URL url = new URI(urlBuilder.toString()).toURL();
 | 
			
		||||
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
			
		||||
                conn.setRequestMethod("GET");
 | 
			
		||||
                conn.setRequestProperty("Accept", "application/json");
 | 
			
		||||
                conn.setRequestProperty("Content-Type", "application/json");
 | 
			
		||||
                conn.setConnectTimeout(30000);
 | 
			
		||||
                conn.setReadTimeout(30000);
 | 
			
		||||
                
 | 
			
		||||
                int responseCode = conn.getResponseCode();
 | 
			
		||||
                Log.d(TAG, "Backend Tokenization response code: " + responseCode);
 | 
			
		||||
                
 | 
			
		||||
                if (responseCode == 200) {
 | 
			
		||||
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
			
		||||
                    StringBuilder response = new StringBuilder();
 | 
			
		||||
                    String responseLine;
 | 
			
		||||
                    while ((responseLine = br.readLine()) != null) {
 | 
			
		||||
                        response.append(responseLine.trim());
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    Log.d(TAG, "Backend Tokenization success response: " + response.toString());
 | 
			
		||||
                    
 | 
			
		||||
                    JSONObject jsonResponse = new JSONObject(response.toString());
 | 
			
		||||
                    if (jsonResponse.has("token_id")) {
 | 
			
		||||
                        return jsonResponse.getString("token_id");
 | 
			
		||||
                    } else {
 | 
			
		||||
                        errorMessage = "Token ID not found in backend response";
 | 
			
		||||
                        return null;
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                } else {
 | 
			
		||||
                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
			
		||||
                    StringBuilder errorResponse = new StringBuilder();
 | 
			
		||||
                    String responseLine;
 | 
			
		||||
                    while ((responseLine = br.readLine()) != null) {
 | 
			
		||||
                        errorResponse.append(responseLine.trim());
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    Log.e(TAG, "Backend Tokenization error: " + errorResponse.toString());
 | 
			
		||||
                    errorMessage = "Backend tokenization failed: " + errorResponse.toString();
 | 
			
		||||
                    return null;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Backend Tokenization exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Network error: " + e.getMessage();
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(String cardToken) {
 | 
			
		||||
            if (cardToken != null && callback != null) {
 | 
			
		||||
                callback.onTokenizeSuccess(cardToken);
 | 
			
		||||
                
 | 
			
		||||
                if (callback != null) {
 | 
			
		||||
                    callback.onPaymentProgress("Processing backend payment...");
 | 
			
		||||
                }
 | 
			
		||||
                new ChargeCardWithOrderIdTask(cardToken, amount, orderId, cardData).execute();
 | 
			
		||||
                
 | 
			
		||||
            } else if (callback != null) {
 | 
			
		||||
                callback.onTokenizeError(errorMessage != null ? errorMessage : "Unknown backend tokenization error");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * ✅ NEW: Charge Card with Custom Order ID
 | 
			
		||||
     */
 | 
			
		||||
    private class ChargeCardWithOrderIdTask extends AsyncTask<Void, Void, Boolean> {
 | 
			
		||||
        private String cardToken;
 | 
			
		||||
        private long amount;
 | 
			
		||||
        private String orderId;
 | 
			
		||||
        private String errorMessage;
 | 
			
		||||
        private JSONObject chargeResponse;
 | 
			
		||||
        private CardData cardData;
 | 
			
		||||
        
 | 
			
		||||
        public ChargeCardWithOrderIdTask(String cardToken, long amount, String orderId, CardData cardData) {
 | 
			
		||||
            this.cardToken = cardToken;
 | 
			
		||||
            this.amount = amount;
 | 
			
		||||
            this.orderId = orderId;
 | 
			
		||||
            this.cardData = cardData;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Boolean doInBackground(Void... voids) {
 | 
			
		||||
            try {
 | 
			
		||||
                JSONObject payload = new JSONObject();
 | 
			
		||||
                payload.put("payment_type", "credit_card");
 | 
			
		||||
                
 | 
			
		||||
                JSONObject transactionDetails = new JSONObject();
 | 
			
		||||
                transactionDetails.put("order_id", orderId); // ✅ Use backend transaction_uuid
 | 
			
		||||
                transactionDetails.put("gross_amount", amount);
 | 
			
		||||
                payload.put("transaction_details", transactionDetails);
 | 
			
		||||
                
 | 
			
		||||
                JSONObject creditCard = new JSONObject();
 | 
			
		||||
                creditCard.put("token_id", cardToken);
 | 
			
		||||
                payload.put("credit_card", creditCard);
 | 
			
		||||
                
 | 
			
		||||
                // Item details
 | 
			
		||||
                JSONArray itemDetails = new JSONArray();
 | 
			
		||||
                JSONObject item = new JSONObject();
 | 
			
		||||
                item.put("id", "tkn_backend1");
 | 
			
		||||
                item.put("price", amount);
 | 
			
		||||
                item.put("quantity", 1);
 | 
			
		||||
                item.put("name", "Token Backend Transaction");
 | 
			
		||||
                item.put("brand", "Token Backend Payment");
 | 
			
		||||
                item.put("category", "Backend Transaction");
 | 
			
		||||
                item.put("merchant_name", "EDC-Store-Backend");
 | 
			
		||||
                itemDetails.put(item);
 | 
			
		||||
                payload.put("item_details", itemDetails);
 | 
			
		||||
                
 | 
			
		||||
                addCustomerDetails(payload);
 | 
			
		||||
                
 | 
			
		||||
                Log.d(TAG, "=== TOKEN BACKEND CHARGE ===");
 | 
			
		||||
                Log.d(TAG, "Order ID (Backend UUID): " + orderId);
 | 
			
		||||
                Log.d(TAG, "Amount: " + amount);
 | 
			
		||||
                Log.d(TAG, "Token: " + maskToken(cardToken));
 | 
			
		||||
                Log.d(TAG, "============================");
 | 
			
		||||
                
 | 
			
		||||
                return makeChargeRequest(payload);
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Token backend charge exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Token backend charge error: " + e.getMessage();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(Boolean success) {
 | 
			
		||||
            if (success && chargeResponse != null && callback != null) {
 | 
			
		||||
                Log.d(TAG, "✅ Token backend charge successful!");
 | 
			
		||||
                callback.onChargeSuccess(chargeResponse);
 | 
			
		||||
            } else if (callback != null) {
 | 
			
		||||
                // Check for retry scenarios
 | 
			
		||||
                if (shouldRetry(errorMessage) && retryCount < MAX_RETRY) {
 | 
			
		||||
                    retryCount++;
 | 
			
		||||
                    Log.w(TAG, "Retrying backend charge... (attempt " + retryCount + "/" + MAX_RETRY + ")");
 | 
			
		||||
                    callback.onPaymentProgress("Retrying backend... (" + retryCount + "/" + MAX_RETRY + ")");
 | 
			
		||||
                    new TokenizeCardWithOrderIdTask(cardData, amount, orderId).execute();
 | 
			
		||||
                } else {
 | 
			
		||||
                    if (retryCount >= MAX_RETRY) {
 | 
			
		||||
                        errorMessage = "Max retry attempts reached. " + errorMessage;
 | 
			
		||||
                    }
 | 
			
		||||
                    callback.onChargeError(errorMessage != null ? errorMessage : "Unknown backend charge error");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private Boolean makeChargeRequest(JSONObject payload) {
 | 
			
		||||
            try {
 | 
			
		||||
                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
 | 
			
		||||
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
			
		||||
                conn.setRequestMethod("POST");
 | 
			
		||||
                conn.setRequestProperty("Accept", "application/json");
 | 
			
		||||
                conn.setRequestProperty("Content-Type", "application/json");
 | 
			
		||||
                conn.setRequestProperty("Authorization", MIDTRANS_SERVER_AUTH);
 | 
			
		||||
                conn.setDoOutput(true);
 | 
			
		||||
                conn.setConnectTimeout(30000);
 | 
			
		||||
                conn.setReadTimeout(30000);
 | 
			
		||||
                
 | 
			
		||||
                try (OutputStream os = conn.getOutputStream()) {
 | 
			
		||||
                    byte[] input = payload.toString().getBytes("utf-8");
 | 
			
		||||
                    os.write(input, 0, input.length);
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                int responseCode = conn.getResponseCode();
 | 
			
		||||
                Log.d(TAG, "Token Backend Charge response code: " + responseCode);
 | 
			
		||||
                
 | 
			
		||||
                BufferedReader br;
 | 
			
		||||
                StringBuilder response = new StringBuilder();
 | 
			
		||||
                String responseLine;
 | 
			
		||||
                
 | 
			
		||||
                if (responseCode == 200 || responseCode == 201) {
 | 
			
		||||
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
			
		||||
                } else {
 | 
			
		||||
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                while ((responseLine = br.readLine()) != null) {
 | 
			
		||||
                    response.append(responseLine.trim());
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                String responseString = response.toString();
 | 
			
		||||
                Log.d(TAG, "Token Backend Charge response: " + responseString);
 | 
			
		||||
                
 | 
			
		||||
                // Store response for debugging
 | 
			
		||||
                try {
 | 
			
		||||
                    chargeResponse = new JSONObject(responseString);
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                    
 | 
			
		||||
                    // Enhanced: Add bank detection if missing
 | 
			
		||||
                    enhanceResponseWithBankInfo(chargeResponse);
 | 
			
		||||
                    
 | 
			
		||||
                } catch (JSONException e) {
 | 
			
		||||
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
			
		||||
                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                return processChargeResponse(chargeResponse, responseCode);
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Token backend charge request exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Network error: " + e.getMessage();
 | 
			
		||||
                lastErrorMessage = errorMessage;
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private Boolean processChargeResponse(JSONObject response, int httpCode) {
 | 
			
		||||
            try {
 | 
			
		||||
                String statusCode = response.optString("status_code", "");
 | 
			
		||||
                String statusMessage = response.optString("status_message", "");
 | 
			
		||||
                String transactionStatus = response.optString("transaction_status", "");
 | 
			
		||||
                
 | 
			
		||||
                Log.d(TAG, "Token Backend Charge Response - Status: " + statusCode + ", Message: " + statusMessage);
 | 
			
		||||
                
 | 
			
		||||
                if ("411".equals(statusCode)) {
 | 
			
		||||
                    errorMessage = "Token expired: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if ("400".equals(statusCode)) {
 | 
			
		||||
                    errorMessage = "Bad request: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if ("202".equals(statusCode) && "deny".equals(transactionStatus)) {
 | 
			
		||||
                    errorMessage = "Transaction denied: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                } else if (httpCode != 200 && httpCode != 201) {
 | 
			
		||||
                    errorMessage = "HTTP error: " + httpCode + " - " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                if ("200".equals(statusCode) && 
 | 
			
		||||
                    ("capture".equals(transactionStatus) || 
 | 
			
		||||
                     "settlement".equals(transactionStatus) || 
 | 
			
		||||
                     "pending".equals(transactionStatus))) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else if ("201".equals(statusCode)) {
 | 
			
		||||
                    return true;
 | 
			
		||||
                } else {
 | 
			
		||||
                    errorMessage = "Transaction failed: " + statusMessage;
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Error processing token backend charge response: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Response processing error: " + e.getMessage();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * EMV Direct Charge - bypasses tokenization for EMV cards
 | 
			
		||||
     */
 | 
			
		||||
@ -259,17 +776,16 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
                String responseString = response.toString();
 | 
			
		||||
                Log.d(TAG, "EMV Charge response: " + responseString);
 | 
			
		||||
                
 | 
			
		||||
                // ✅ ENHANCED: Store response for debugging
 | 
			
		||||
                // Store response for debugging
 | 
			
		||||
                try {
 | 
			
		||||
                    chargeResponse = new JSONObject(responseString);
 | 
			
		||||
                    lastResponse = chargeResponse; // Store for later access
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                    
 | 
			
		||||
                    // ✅ ENHANCED: Add bank detection if missing
 | 
			
		||||
                    // Enhanced: Add bank detection if missing
 | 
			
		||||
                    enhanceResponseWithBankInfo(chargeResponse);
 | 
			
		||||
                    
 | 
			
		||||
                } catch (JSONException e) {
 | 
			
		||||
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
			
		||||
                    // Create fallback response object
 | 
			
		||||
                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                }
 | 
			
		||||
@ -554,17 +1070,16 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
                String responseString = response.toString();
 | 
			
		||||
                Log.d(TAG, "Token Charge response: " + responseString);
 | 
			
		||||
                
 | 
			
		||||
                // ✅ ENHANCED: Store response for debugging
 | 
			
		||||
                // Store response for debugging
 | 
			
		||||
                try {
 | 
			
		||||
                    chargeResponse = new JSONObject(responseString);
 | 
			
		||||
                    lastResponse = chargeResponse; // Store for later access
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                    
 | 
			
		||||
                    // ✅ ENHANCED: Add bank detection if missing
 | 
			
		||||
                    // Enhanced: Add bank detection if missing
 | 
			
		||||
                    enhanceResponseWithBankInfo(chargeResponse);
 | 
			
		||||
                    
 | 
			
		||||
                } catch (JSONException e) {
 | 
			
		||||
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
			
		||||
                    // Create fallback response object
 | 
			
		||||
                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
			
		||||
                    lastResponse = chargeResponse;
 | 
			
		||||
                }
 | 
			
		||||
@ -621,7 +1136,7 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Method to enhance response with bank information
 | 
			
		||||
    // Method to enhance response with bank information
 | 
			
		||||
    private void enhanceResponseWithBankInfo(JSONObject response) {
 | 
			
		||||
        if (response == null) return;
 | 
			
		||||
        
 | 
			
		||||
@ -648,7 +1163,7 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Method to detect bank from various response fields
 | 
			
		||||
    // Method to detect bank from various response fields
 | 
			
		||||
    private String detectBankFromResponse(JSONObject response) {
 | 
			
		||||
        try {
 | 
			
		||||
            // Try various fields that might contain bank information
 | 
			
		||||
@ -680,7 +1195,7 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Map various bank identifiers to standard bank names
 | 
			
		||||
    // Map various bank identifiers to standard bank names
 | 
			
		||||
    private String mapToBankName(String identifier) {
 | 
			
		||||
        if (identifier == null || identifier.trim().isEmpty()) {
 | 
			
		||||
            return null;
 | 
			
		||||
@ -710,7 +1225,7 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
        return null; // No mapping found
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Create fallback response when JSON parsing fails
 | 
			
		||||
    // Create fallback response when JSON parsing fails
 | 
			
		||||
    private JSONObject createFallbackResponse(String rawResponse, int httpCode) {
 | 
			
		||||
        try {
 | 
			
		||||
            JSONObject fallback = new JSONObject();
 | 
			
		||||
@ -789,7 +1304,7 @@ public class MidtransCardPaymentManager {
 | 
			
		||||
               error.contains("timeout");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // ✅ ENHANCED: Debug method to inspect response
 | 
			
		||||
    // Debug method to inspect response
 | 
			
		||||
    public void debugResponse() {
 | 
			
		||||
        if (lastResponse != null) {
 | 
			
		||||
            Log.d(TAG, "=== DEBUGGING LAST RESPONSE ===");
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,358 @@
 | 
			
		||||
package com.example.bdkipoc.transaction.managers;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
 | 
			
		||||
import org.json.JSONException;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
 | 
			
		||||
import java.io.BufferedReader;
 | 
			
		||||
import java.io.InputStreamReader;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.net.HttpURLConnection;
 | 
			
		||||
import java.net.URI;
 | 
			
		||||
import java.net.URL;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * PostTransactionBackendManager - Handles backend transaction posting
 | 
			
		||||
 * 
 | 
			
		||||
 * This manager handles the communication with the backend service for transaction posting
 | 
			
		||||
 * and provides the transaction_uuid needed for Midtrans integration.
 | 
			
		||||
 */
 | 
			
		||||
public class PostTransactionBackendManager {
 | 
			
		||||
    private static final String TAG = "PostTransactionBackend";
 | 
			
		||||
    
 | 
			
		||||
    // Backend Configuration
 | 
			
		||||
    private static final String BACKEND_BASE_URL = "https://be-edc.msvc.app";
 | 
			
		||||
    private static final String TRANSACTIONS_ENDPOINT = BACKEND_BASE_URL + "/transactions";
 | 
			
		||||
    
 | 
			
		||||
    // Default values
 | 
			
		||||
    private static final String DEFAULT_DEVICE_CODE = "PB4K252T00021";
 | 
			
		||||
    private static final int DEFAULT_DEVICE_ID = 1;
 | 
			
		||||
    private static final String DEFAULT_CASHFLOW = "MONEY_IN";
 | 
			
		||||
    private static final String DEFAULT_CHANNEL_CATEGORY = "RETAIL_OUTLET";
 | 
			
		||||
    
 | 
			
		||||
    // ✅ NEW: Static merchant data
 | 
			
		||||
    private static final String DEFAULT_MERCHANT_NAME = "BUDIAJAIB123";
 | 
			
		||||
    private static final String DEFAULT_MID = "542531513";
 | 
			
		||||
    private static final String DEFAULT_TID = "535151521";
 | 
			
		||||
    
 | 
			
		||||
    private Context context;
 | 
			
		||||
    private PostTransactionCallback callback;
 | 
			
		||||
    
 | 
			
		||||
    public interface PostTransactionCallback {
 | 
			
		||||
        void onPostTransactionSuccess(JSONObject response, String transactionUuid);
 | 
			
		||||
        void onPostTransactionError(String errorMessage);
 | 
			
		||||
        void onPostTransactionProgress(String message);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    public PostTransactionBackendManager(Context context, PostTransactionCallback callback) {
 | 
			
		||||
        this.context = context;
 | 
			
		||||
        this.callback = callback;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Post transaction to backend service
 | 
			
		||||
     */
 | 
			
		||||
    public void postTransaction(String paymentType, String referenceId, long amount, String status) {
 | 
			
		||||
        String channelCode = mapPaymentTypeToChannelCode(paymentType);
 | 
			
		||||
        String transactionUuid = generateUUID();
 | 
			
		||||
        
 | 
			
		||||
        Log.d(TAG, "=== POSTING TRANSACTION TO BACKEND ===");
 | 
			
		||||
        Log.d(TAG, "Payment Type: " + paymentType);
 | 
			
		||||
        Log.d(TAG, "Channel Code: " + channelCode);
 | 
			
		||||
        Log.d(TAG, "Reference ID: " + referenceId);
 | 
			
		||||
        Log.d(TAG, "Amount: " + amount);
 | 
			
		||||
        Log.d(TAG, "Status: " + status);
 | 
			
		||||
        Log.d(TAG, "Transaction UUID: " + transactionUuid);
 | 
			
		||||
        Log.d(TAG, "=====================================");
 | 
			
		||||
        
 | 
			
		||||
        if (callback != null) {
 | 
			
		||||
            callback.onPostTransactionProgress("Posting transaction to backend...");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        new PostTransactionTask(paymentType, channelCode, referenceId, amount, status, transactionUuid).execute();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Post transaction with INIT status (for pre-authorization)
 | 
			
		||||
     */
 | 
			
		||||
    public void postInitTransaction(String paymentType, String referenceId, long amount) {
 | 
			
		||||
        postTransaction(paymentType, referenceId, amount, "INIT");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Post transaction with SUCCESS status (for completed transactions)
 | 
			
		||||
     */
 | 
			
		||||
    public void postSuccessTransaction(String paymentType, String referenceId, long amount) {
 | 
			
		||||
        postTransaction(paymentType, referenceId, amount, "SUCCESS");
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Update existing transaction status
 | 
			
		||||
     */
 | 
			
		||||
    public void updateTransactionStatus(String transactionUuid, String newStatus) {
 | 
			
		||||
        Log.d(TAG, "Updating transaction " + transactionUuid + " to status: " + newStatus);
 | 
			
		||||
        // TODO: Implement update endpoint if available
 | 
			
		||||
        // For now, we'll create a new transaction with updated status
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Map payment type to channel code for backend
 | 
			
		||||
     */
 | 
			
		||||
    private String mapPaymentTypeToChannelCode(String paymentType) {
 | 
			
		||||
        if (paymentType == null) {
 | 
			
		||||
            return "CREDIT_CARD"; // Default
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        switch (paymentType.toLowerCase()) {
 | 
			
		||||
            case "credit_card":
 | 
			
		||||
                return "CREDIT_CARD";
 | 
			
		||||
            case "debit_card":
 | 
			
		||||
                return "DEBIT_CARD";
 | 
			
		||||
            case "e_money":
 | 
			
		||||
                return "E_MONEY";
 | 
			
		||||
            case "qris":
 | 
			
		||||
                return "QRIS";
 | 
			
		||||
            default:
 | 
			
		||||
                Log.w(TAG, "Unknown payment type: " + paymentType + ", using CREDIT_CARD");
 | 
			
		||||
                return "CREDIT_CARD";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Generate UUID v4 for transaction
 | 
			
		||||
     */
 | 
			
		||||
    private String generateUUID() {
 | 
			
		||||
        return UUID.randomUUID().toString();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Get device serial number (Sunmi device code)
 | 
			
		||||
     */
 | 
			
		||||
    private String getDeviceCode() {
 | 
			
		||||
        try {
 | 
			
		||||
            // Try to get actual device serial number
 | 
			
		||||
            // For Sunmi devices, this might be available through system properties
 | 
			
		||||
            String serialNumber = android.os.Build.SERIAL;
 | 
			
		||||
            if (serialNumber != null && !serialNumber.equals("unknown") && !serialNumber.equals(android.os.Build.UNKNOWN)) {
 | 
			
		||||
                return serialNumber;
 | 
			
		||||
            }
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Log.w(TAG, "Could not get device serial number: " + e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Fallback to default device code
 | 
			
		||||
        return DEFAULT_DEVICE_CODE;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * AsyncTask for posting transaction to backend
 | 
			
		||||
     */
 | 
			
		||||
    private class PostTransactionTask extends AsyncTask<Void, Void, Boolean> {
 | 
			
		||||
        private String paymentType;
 | 
			
		||||
        private String channelCode;
 | 
			
		||||
        private String referenceId;
 | 
			
		||||
        private long amount;
 | 
			
		||||
        private String status;
 | 
			
		||||
        private String transactionUuid;
 | 
			
		||||
        private String errorMessage;
 | 
			
		||||
        private JSONObject responseData;
 | 
			
		||||
        
 | 
			
		||||
        public PostTransactionTask(String paymentType, String channelCode, String referenceId, 
 | 
			
		||||
                                 long amount, String status, String transactionUuid) {
 | 
			
		||||
            this.paymentType = paymentType;
 | 
			
		||||
            this.channelCode = channelCode;
 | 
			
		||||
            this.referenceId = referenceId;
 | 
			
		||||
            this.amount = amount;
 | 
			
		||||
            this.status = status;
 | 
			
		||||
            this.transactionUuid = transactionUuid;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Boolean doInBackground(Void... voids) {
 | 
			
		||||
            try {
 | 
			
		||||
                // Build transaction payload
 | 
			
		||||
                JSONObject payload = buildTransactionPayload();
 | 
			
		||||
                
 | 
			
		||||
                Log.d(TAG, "Backend payload: " + payload.toString());
 | 
			
		||||
                
 | 
			
		||||
                // Make HTTP request
 | 
			
		||||
                return makeBackendRequest(payload);
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Backend transaction exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Backend transaction error: " + e.getMessage();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(Boolean success) {
 | 
			
		||||
            if (success && responseData != null && callback != null) {
 | 
			
		||||
                try {
 | 
			
		||||
                    // Extract transaction_uuid from response
 | 
			
		||||
                    JSONObject data = responseData.optJSONObject("data");
 | 
			
		||||
                    String returnedUuid = null;
 | 
			
		||||
                    
 | 
			
		||||
                    if (data != null) {
 | 
			
		||||
                        returnedUuid = data.optString("transaction_uuid", transactionUuid);
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                    Log.d(TAG, "✅ Backend transaction successful!");
 | 
			
		||||
                    Log.d(TAG, "Original UUID: " + transactionUuid);
 | 
			
		||||
                    Log.d(TAG, "Returned UUID: " + returnedUuid);
 | 
			
		||||
                    
 | 
			
		||||
                    callback.onPostTransactionSuccess(responseData, returnedUuid != null ? returnedUuid : transactionUuid);
 | 
			
		||||
                    
 | 
			
		||||
                } catch (Exception e) {
 | 
			
		||||
                    Log.e(TAG, "Error processing backend response: " + e.getMessage());
 | 
			
		||||
                    if (callback != null) {
 | 
			
		||||
                        callback.onPostTransactionError("Error processing backend response: " + e.getMessage());
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            } else if (callback != null) {
 | 
			
		||||
                callback.onPostTransactionError(errorMessage != null ? errorMessage : "Unknown backend error");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private JSONObject buildTransactionPayload() throws JSONException {
 | 
			
		||||
            JSONObject payload = new JSONObject();
 | 
			
		||||
            
 | 
			
		||||
            // Required fields
 | 
			
		||||
            payload.put("type", "PAYMENT");
 | 
			
		||||
            payload.put("channel_category", DEFAULT_CHANNEL_CATEGORY);
 | 
			
		||||
            payload.put("channel_code", channelCode);
 | 
			
		||||
            payload.put("reference_id", referenceId);
 | 
			
		||||
            payload.put("amount", amount);
 | 
			
		||||
            payload.put("cashflow", DEFAULT_CASHFLOW);
 | 
			
		||||
            payload.put("status", status);
 | 
			
		||||
            payload.put("device_id", DEFAULT_DEVICE_ID);
 | 
			
		||||
            payload.put("transaction_uuid", transactionUuid);
 | 
			
		||||
            payload.put("transaction_time_seconds", 2.2); // Default value as mentioned
 | 
			
		||||
            payload.put("device_code", getDeviceCode());
 | 
			
		||||
            
 | 
			
		||||
            // ✅ NEW: Static merchant data (no longer null)
 | 
			
		||||
            payload.put("merchant_name", DEFAULT_MERCHANT_NAME);
 | 
			
		||||
            payload.put("mid", DEFAULT_MID);
 | 
			
		||||
            payload.put("tid", DEFAULT_TID);
 | 
			
		||||
            
 | 
			
		||||
            return payload;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        private Boolean makeBackendRequest(JSONObject payload) {
 | 
			
		||||
            try {
 | 
			
		||||
                URL url = new URI(TRANSACTIONS_ENDPOINT).toURL();
 | 
			
		||||
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
			
		||||
                
 | 
			
		||||
                // Set request properties
 | 
			
		||||
                conn.setRequestMethod("POST");
 | 
			
		||||
                conn.setRequestProperty("Accept", "*/*");
 | 
			
		||||
                conn.setRequestProperty("Content-Type", "application/json");
 | 
			
		||||
                conn.setDoOutput(true);
 | 
			
		||||
                conn.setConnectTimeout(30000);
 | 
			
		||||
                conn.setReadTimeout(30000);
 | 
			
		||||
                
 | 
			
		||||
                // Send payload
 | 
			
		||||
                try (OutputStream os = conn.getOutputStream()) {
 | 
			
		||||
                    byte[] input = payload.toString().getBytes("utf-8");
 | 
			
		||||
                    os.write(input, 0, input.length);
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                // Get response
 | 
			
		||||
                int responseCode = conn.getResponseCode();
 | 
			
		||||
                Log.d(TAG, "Backend response code: " + responseCode);
 | 
			
		||||
                
 | 
			
		||||
                BufferedReader br;
 | 
			
		||||
                StringBuilder response = new StringBuilder();
 | 
			
		||||
                String responseLine;
 | 
			
		||||
                
 | 
			
		||||
                if (responseCode >= 200 && responseCode < 300) {
 | 
			
		||||
                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
			
		||||
                } else {
 | 
			
		||||
                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                while ((responseLine = br.readLine()) != null) {
 | 
			
		||||
                    response.append(responseLine.trim());
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
                String responseString = response.toString();
 | 
			
		||||
                Log.d(TAG, "Backend response: " + responseString);
 | 
			
		||||
                
 | 
			
		||||
                // Parse response
 | 
			
		||||
                try {
 | 
			
		||||
                    responseData = new JSONObject(responseString);
 | 
			
		||||
                    
 | 
			
		||||
                    // Check if response indicates success
 | 
			
		||||
                    int status = responseData.optInt("status", 0);
 | 
			
		||||
                    String message = responseData.optString("message", "");
 | 
			
		||||
                    
 | 
			
		||||
                    if (status == 200 && "Successfully".equals(message)) {
 | 
			
		||||
                        return true;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        errorMessage = "Backend error: " + message + " (Status: " + status + ")";
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    
 | 
			
		||||
                } catch (JSONException e) {
 | 
			
		||||
                    Log.e(TAG, "Error parsing backend response: " + e.getMessage());
 | 
			
		||||
                    errorMessage = "Invalid backend response format";
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Log.e(TAG, "Backend request exception: " + e.getMessage(), e);
 | 
			
		||||
                errorMessage = "Network error: " + e.getMessage();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Utility method to generate reference ID
 | 
			
		||||
     */
 | 
			
		||||
    public static String generateReferenceId() {
 | 
			
		||||
        return "ref" + System.currentTimeMillis() + (int)(Math.random() * 10000);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Utility method to map card menu ID to payment type
 | 
			
		||||
     */
 | 
			
		||||
    public static String mapCardMenuToPaymentType(int cardMenuId) {
 | 
			
		||||
        // Based on MainActivity.java card IDs
 | 
			
		||||
        switch (cardMenuId) {
 | 
			
		||||
            case 2131296346: // R.id.card_kartu_kredit
 | 
			
		||||
                return "credit_card";
 | 
			
		||||
            case 2131296344: // R.id.card_kartu_debit  
 | 
			
		||||
                return "debit_card";
 | 
			
		||||
            case 2131296360: // R.id.card_uang_elektronik
 | 
			
		||||
                return "e_money";
 | 
			
		||||
            case 2131296352: // R.id.card_qris
 | 
			
		||||
                return "qris";
 | 
			
		||||
            default:
 | 
			
		||||
                Log.w(TAG, "Unknown card menu ID: " + cardMenuId + ", defaulting to credit_card");
 | 
			
		||||
                return "credit_card";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /**
 | 
			
		||||
     * Debug method to log transaction details
 | 
			
		||||
     */
 | 
			
		||||
    public void debugTransactionData(String paymentType, String referenceId, long amount, String status) {
 | 
			
		||||
        Log.d(TAG, "=== TRANSACTION DEBUG INFO ===");
 | 
			
		||||
        Log.d(TAG, "Payment Type: " + paymentType);
 | 
			
		||||
        Log.d(TAG, "Channel Code: " + mapPaymentTypeToChannelCode(paymentType));
 | 
			
		||||
        Log.d(TAG, "Reference ID: " + referenceId);
 | 
			
		||||
        Log.d(TAG, "Amount: " + amount);
 | 
			
		||||
        Log.d(TAG, "Status: " + status);
 | 
			
		||||
        Log.d(TAG, "Device Code: " + getDeviceCode());
 | 
			
		||||
        Log.d(TAG, "Device ID: " + DEFAULT_DEVICE_ID);
 | 
			
		||||
        Log.d(TAG, "Merchant Name: " + DEFAULT_MERCHANT_NAME);
 | 
			
		||||
        Log.d(TAG, "MID: " + DEFAULT_MID);
 | 
			
		||||
        Log.d(TAG, "TID: " + DEFAULT_TID);
 | 
			
		||||
        Log.d(TAG, "==============================");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user