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.ReprintActivity;
 | 
				
			||||||
import com.example.bdkipoc.cetakulang.ReprintAdapterActivity;
 | 
					import com.example.bdkipoc.cetakulang.ReprintAdapterActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.R;
 | 
				
			||||||
import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
					import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
				
			||||||
import com.example.bdkipoc.transaction.ResultTransactionActivity;
 | 
					import com.example.bdkipoc.transaction.ResultTransactionActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -156,22 +157,23 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
            CardView cardView = findViewById(cardId);
 | 
					            CardView cardView = findViewById(cardId);
 | 
				
			||||||
            if (cardView != null) {
 | 
					            if (cardView != null) {
 | 
				
			||||||
                cardView.setOnClickListener(v -> {
 | 
					                cardView.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					                    // ✅ ENHANCED: Navigate with payment type information
 | 
				
			||||||
                    if (cardId == R.id.card_kartu_kredit) {
 | 
					                    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) {
 | 
					                    } 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) {
 | 
					                    } else if (cardId == R.id.card_qris) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, QrisActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, QrisActivity.class));
 | 
				
			||||||
                    // Col-2
 | 
					                    // Col-2
 | 
				
			||||||
                    } else if (cardId == R.id.card_transfer) {
 | 
					                    } 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) {
 | 
					                    } 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) {
 | 
					                    } else if (cardId == R.id.card_cetak_ulang) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, ReprintActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, ReprintActivity.class));
 | 
				
			||||||
                    // Col-3
 | 
					                    // Col-3
 | 
				
			||||||
                    } else if (cardId == R.id.card_refund) {
 | 
					                    } 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) {
 | 
					                    } else if (cardId == R.id.card_settlement) {
 | 
				
			||||||
                        Toast.makeText(this, "Settlement - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					                        Toast.makeText(this, "Settlement - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                    } else if (cardId == R.id.card_histori) {
 | 
					                    } 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();
 | 
					                        Toast.makeText(this, "Pengaturan - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        // Fallback for any other cards
 | 
					                        // 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
 | 
					    @Override
 | 
				
			||||||
    protected void onNewIntent(Intent intent) {
 | 
					    protected void onNewIntent(Intent intent) {
 | 
				
			||||||
        super.onNewIntent(intent);
 | 
					        super.onNewIntent(intent);
 | 
				
			||||||
@ -256,5 +377,74 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
        // Clear any transaction completion flags to avoid repeated messages
 | 
					        // Clear any transaction completion flags to avoid repeated messages
 | 
				
			||||||
        getIntent().removeExtra("transaction_completed");
 | 
					        getIntent().removeExtra("transaction_completed");
 | 
				
			||||||
        getIntent().removeExtra("transaction_amount");
 | 
					        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.ModalManager;
 | 
				
			||||||
import com.example.bdkipoc.transaction.managers.PinPadManager;
 | 
					import com.example.bdkipoc.transaction.managers.PinPadManager;
 | 
				
			||||||
import com.example.bdkipoc.transaction.managers.MidtransCardPaymentManager;
 | 
					import com.example.bdkipoc.transaction.managers.MidtransCardPaymentManager;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.transaction.managers.PostTransactionBackendManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.text.NumberFormat;
 | 
					import java.text.NumberFormat;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
@ -34,14 +35,17 @@ import org.json.JSONObject;
 | 
				
			|||||||
import org.json.JSONException;
 | 
					import org.json.JSONException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * CreateTransactionActivity - Updated with Enhanced Midtrans Credit Card Integration
 | 
					 * CreateTransactionActivity - Enhanced with Backend Integration
 | 
				
			||||||
 * Handles amount input, card scanning, and Midtrans payment processing with improved bank detection
 | 
					 * 
 | 
				
			||||||
 | 
					 * 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 
 | 
					public class CreateTransactionActivity extends AppCompatActivity implements 
 | 
				
			||||||
    EMVManager.EMVManagerCallback,
 | 
					    EMVManager.EMVManagerCallback,
 | 
				
			||||||
    CardScannerManager.CardScannerCallback,
 | 
					    CardScannerManager.CardScannerCallback,
 | 
				
			||||||
    PinPadManager.PinPadManagerCallback,
 | 
					    PinPadManager.PinPadManagerCallback,
 | 
				
			||||||
    MidtransCardPaymentManager.MidtransCardPaymentCallback {
 | 
					    MidtransCardPaymentManager.MidtransCardPaymentCallback,
 | 
				
			||||||
 | 
					    PostTransactionBackendManager.PostTransactionCallback {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private static final String TAG = "CreateTransaction";
 | 
					    private static final String TAG = "CreateTransaction";
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -60,20 +64,26 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    // State Management
 | 
					    // State Management
 | 
				
			||||||
    private String transactionAmount = "0";
 | 
					    private String transactionAmount = "0";
 | 
				
			||||||
    private boolean isEMVMode = true;
 | 
					    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
 | 
					    // Manager Classes
 | 
				
			||||||
    private EMVManager emvManager;
 | 
					    private EMVManager emvManager;
 | 
				
			||||||
    private CardScannerManager cardScannerManager;
 | 
					    private CardScannerManager cardScannerManager;
 | 
				
			||||||
    private PinPadManager pinPadManager;
 | 
					    private PinPadManager pinPadManager;
 | 
				
			||||||
    private ModalManager modalManager;
 | 
					    private ModalManager modalManager;
 | 
				
			||||||
    private MidtransCardPaymentManager midtransPaymentManager; // ✅ NEW: Midtrans integration
 | 
					    private MidtransCardPaymentManager midtransPaymentManager;
 | 
				
			||||||
 | 
					    private PostTransactionBackendManager backendManager; // ✅ NEW: Backend manager
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // EMV Dialog
 | 
					    // EMV Dialog
 | 
				
			||||||
    private AlertDialog mAppSelectDialog;
 | 
					    private AlertDialog mAppSelectDialog;
 | 
				
			||||||
    private int mSelectIndex;
 | 
					    private int mSelectIndex;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // ✅ NEW: EMV Data Storage for Midtrans
 | 
					    // EMV Data Storage for Midtrans
 | 
				
			||||||
    private String emvCardNumber;
 | 
					    private String emvCardNumber;
 | 
				
			||||||
    private String emvExpiryDate;
 | 
					    private String emvExpiryDate;
 | 
				
			||||||
    private String emvCardholderName;
 | 
					    private String emvCardholderName;
 | 
				
			||||||
@ -81,10 +91,11 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    private String emvTlvData;
 | 
					    private String emvTlvData;
 | 
				
			||||||
    private String referenceId;
 | 
					    private String referenceId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ✅ ENHANCED: Store last response for better debugging
 | 
					    // Enhanced response storage
 | 
				
			||||||
    private JSONObject lastMidtransResponse;
 | 
					    private JSONObject lastMidtransResponse;
 | 
				
			||||||
 | 
					    private JSONObject lastBackendResponse; // ✅ NEW: Store backend response
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // deklarasi variabel success screen
 | 
					    // Success screen components
 | 
				
			||||||
    private LinearLayout successScreen;
 | 
					    private LinearLayout successScreen;
 | 
				
			||||||
    private ImageView successIcon;
 | 
					    private ImageView successIcon;
 | 
				
			||||||
    private TextView successMessage;
 | 
					    private TextView successMessage;
 | 
				
			||||||
@ -95,14 +106,16 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        setContentView(R.layout.activity_create_transaction);
 | 
					        setContentView(R.layout.activity_create_transaction);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        initViews();
 | 
					        initViews();
 | 
				
			||||||
 | 
					        extractPaymentTypeFromIntent(); // ✅ NEW: Determine payment type
 | 
				
			||||||
        initManagers();
 | 
					        initManagers();
 | 
				
			||||||
        setupListeners();
 | 
					        setupListeners();
 | 
				
			||||||
        updateAmountDisplay();
 | 
					        updateAmountDisplay();
 | 
				
			||||||
        updateModeDisplay();
 | 
					        updateModeDisplay();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ NEW: Generate reference ID for transaction tracking
 | 
					        // Generate reference ID for transaction tracking
 | 
				
			||||||
        referenceId = generateReferenceId();
 | 
					        referenceId = PostTransactionBackendManager.generateReferenceId();
 | 
				
			||||||
        Log.d(TAG, "Generated reference ID: " + referenceId);
 | 
					        Log.d(TAG, "Generated reference ID: " + referenceId);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Payment type determined: " + paymentType);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void initViews() {
 | 
					    private void initViews() {
 | 
				
			||||||
@ -133,6 +146,38 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        successMessage = findViewById(R.id.success_message);
 | 
					        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() {
 | 
					    private void initManagers() {
 | 
				
			||||||
        // Initialize Modal Manager
 | 
					        // Initialize Modal Manager
 | 
				
			||||||
        FrameLayout modalOverlay = findViewById(R.id.modal_overlay);
 | 
					        FrameLayout modalOverlay = findViewById(R.id.modal_overlay);
 | 
				
			||||||
@ -145,13 +190,16 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        cardScannerManager = new CardScannerManager(this);
 | 
					        cardScannerManager = new CardScannerManager(this);
 | 
				
			||||||
        pinPadManager = new PinPadManager(this);
 | 
					        pinPadManager = new PinPadManager(this);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ NEW: Initialize Midtrans payment manager
 | 
					        // Initialize Midtrans payment manager
 | 
				
			||||||
        midtransPaymentManager = new MidtransCardPaymentManager(this, this);
 | 
					        midtransPaymentManager = new MidtransCardPaymentManager(this, this);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Initialize Backend manager
 | 
				
			||||||
 | 
					        backendManager = new PostTransactionBackendManager(this, this);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        // Initialize EMV data
 | 
					        // Initialize EMV data
 | 
				
			||||||
        emvManager.initEMVData();
 | 
					        emvManager.initEMVData();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d(TAG, "All managers initialized including Midtrans");
 | 
					        Log.d(TAG, "All managers initialized including Backend and Midtrans");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setupListeners() {
 | 
					    private void setupListeners() {
 | 
				
			||||||
@ -183,7 +231,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        // Clear button (ImageView)
 | 
					        // Clear button (ImageView)
 | 
				
			||||||
        btnClear.setOnClickListener(v -> clearAmount());
 | 
					        btnClear.setOnClickListener(v -> clearAmount());
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Confirm button - shows modal and starts scanning
 | 
					        // Confirm button - starts the enhanced flow
 | 
				
			||||||
        btnConfirm.setOnClickListener(v -> handleConfirmAmount());
 | 
					        btnConfirm.setOnClickListener(v -> handleConfirmAmount());
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Toggle mode button (hidden but functional)
 | 
					        // Toggle mode button (hidden but functional)
 | 
				
			||||||
@ -205,7 +253,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        if (newAmount.length() <= 9) {
 | 
					        if (newAmount.length() <= 9) {
 | 
				
			||||||
            transactionAmount = newAmount;
 | 
					            transactionAmount = newAmount;
 | 
				
			||||||
            updateAmountDisplay();
 | 
					            updateAmountDisplay();
 | 
				
			||||||
            // Update status tombol
 | 
					 | 
				
			||||||
            btnConfirm.setEnabled(!transactionAmount.equals("0"));
 | 
					            btnConfirm.setEnabled(!transactionAmount.equals("0"));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -217,7 +264,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
            transactionAmount = "0";
 | 
					            transactionAmount = "0";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        updateAmountDisplay();
 | 
					        updateAmountDisplay();
 | 
				
			||||||
        // Update status tombol
 | 
					 | 
				
			||||||
        btnConfirm.setEnabled(!transactionAmount.equals("0"));
 | 
					        btnConfirm.setEnabled(!transactionAmount.equals("0"));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -225,19 +271,18 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        if (tvAmountDisplay != null) {
 | 
					        if (tvAmountDisplay != null) {
 | 
				
			||||||
            if (transactionAmount.equals("0")) {
 | 
					            if (transactionAmount.equals("0")) {
 | 
				
			||||||
                tvAmountDisplay.setText("");
 | 
					                tvAmountDisplay.setText("");
 | 
				
			||||||
                // Disable tombol dan akan otomatis pakai background inactive
 | 
					 | 
				
			||||||
                btnConfirm.setEnabled(false);
 | 
					                btnConfirm.setEnabled(false);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                long amountCents = Long.parseLong(transactionAmount);
 | 
					                long amountCents = Long.parseLong(transactionAmount);
 | 
				
			||||||
                NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID"));
 | 
					                NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID"));
 | 
				
			||||||
                String formattedAmount = formatter.format(amountCents);
 | 
					                String formattedAmount = formatter.format(amountCents);
 | 
				
			||||||
                tvAmountDisplay.setText(formattedAmount);
 | 
					                tvAmountDisplay.setText(formattedAmount);
 | 
				
			||||||
                // Enable tombol dan akan otomatis pakai background active
 | 
					 | 
				
			||||||
                btnConfirm.setEnabled(true);
 | 
					                btnConfirm.setEnabled(true);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ ENHANCED: New enhanced flow with backend integration
 | 
				
			||||||
    private void handleConfirmAmount() {
 | 
					    private void handleConfirmAmount() {
 | 
				
			||||||
        long amountCents = Long.parseLong(transactionAmount);
 | 
					        long amountCents = Long.parseLong(transactionAmount);
 | 
				
			||||||
        if (amountCents < 100) { // Minimum Rp 1.00
 | 
					        if (amountCents < 100) { // Minimum Rp 1.00
 | 
				
			||||||
@ -245,15 +290,77 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Show modal and start card scanning
 | 
					        Log.d(TAG, "=== STARTING ENHANCED TRANSACTION FLOW ===");
 | 
				
			||||||
        showModalAndStartScanning();
 | 
					        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 showModalAndStartScanning() {
 | 
					    private void startEnhancedTransactionFlow() {
 | 
				
			||||||
        Log.d(TAG, "Starting card scanning with modal...");
 | 
					        // Step 1: Post initial transaction to backend with INIT status
 | 
				
			||||||
 | 
					        modalManager.showProcessingModal("Initializing transaction...");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Show modal first
 | 
					        // Debug transaction data
 | 
				
			||||||
 | 
					        backendManager.debugTransactionData(paymentType, referenceId, Long.parseLong(transactionAmount), "INIT");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // 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();
 | 
					        modalManager.showScanCardModal();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Start scanning after short delay
 | 
					        // Start scanning after short delay
 | 
				
			||||||
@ -284,10 +391,8 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        Log.d(TAG, "Simple card detected: " + cardType);
 | 
					        Log.d(TAG, "Simple card detected: " + cardType);
 | 
				
			||||||
        modalManager.showProcessingModal("Kartu " + cardType + " Ditemukan - Memproses...");
 | 
					        modalManager.showProcessingModal("Kartu " + cardType + " Ditemukan - Memproses...");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // For simple mode, navigate to results without Midtrans
 | 
					        // For simple mode, update backend status and navigate to results
 | 
				
			||||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
					        updateBackendToSuccessAndNavigate(cardType, cardData, null);
 | 
				
			||||||
            navigateToResults(cardType, cardData, null);
 | 
					 | 
				
			||||||
        }, 1500);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -312,7 +417,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onScanProgress(String message) {
 | 
					    public void onScanProgress(String message) {
 | 
				
			||||||
        Log.d(TAG, "Scan progress: " + message);
 | 
					        Log.d(TAG, "Scan progress: " + message);
 | 
				
			||||||
        // Can update UI with progress if needed
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ====== EMV MANAGER CALLBACK METHODS ======
 | 
					    // ====== EMV MANAGER CALLBACK METHODS ======
 | 
				
			||||||
@ -329,7 +433,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onConfirmCardNo(String cardNo) {
 | 
					    public void onConfirmCardNo(String cardNo) {
 | 
				
			||||||
        // ✅ NEW: Store EMV card data for Midtrans
 | 
					 | 
				
			||||||
        emvCardNumber = cardNo;
 | 
					        emvCardNumber = cardNo;
 | 
				
			||||||
        Log.d(TAG, "EMV Card Number extracted: " + maskCardNumber(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) {
 | 
					    public void onTransactionSuccess(int code, String desc) {
 | 
				
			||||||
        Log.d(TAG, "EMV Transaction successful");
 | 
					        Log.d(TAG, "EMV Transaction successful");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ NEW: Process Midtrans payment after successful EMV
 | 
					        // Process Midtrans payment after successful EMV
 | 
				
			||||||
        if (useDirectMidtransPayment && emvCardNumber != null) {
 | 
					        if (useDirectMidtransPayment && emvCardNumber != null && transactionUuid != null) {
 | 
				
			||||||
            processMidtransPayment();
 | 
					            processMidtransPayment();
 | 
				
			||||||
        } else {
 | 
					        } 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";
 | 
					            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();
 | 
					            .start();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ====== PIN PAD CALLBACK METHODS ======
 | 
					    // ====== PIN PAD CALLBACK METHODS ======
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onPinInputLength(int length) {
 | 
					    public void onPinInputLength(int length) {
 | 
				
			||||||
@ -473,7 +577,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onTokenizeSuccess(String cardToken) {
 | 
					    public void onTokenizeSuccess(String cardToken) {
 | 
				
			||||||
        Log.d(TAG, "✅ Midtrans tokenization successful: " + cardToken);
 | 
					        Log.d(TAG, "✅ Midtrans tokenization successful: " + cardToken);
 | 
				
			||||||
        // Tokenization successful, charge process will continue automatically
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@ -482,14 +585,11 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        modalManager.hideModal();
 | 
					        modalManager.hideModal();
 | 
				
			||||||
        showToast("Payment tokenization failed: " + errorMessage);
 | 
					        showToast("Payment tokenization failed: " + errorMessage);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ IMPROVED: Better fallback handling
 | 
					        // Fallback to traditional results screen after delay
 | 
				
			||||||
        showToast("Tokenization failed, trying alternative method...");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Try fallback to traditional results screen after delay
 | 
					 | 
				
			||||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
					        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
            String cardType = emvManager.getCardType() == 
 | 
					            String cardType = emvManager.getCardType() == 
 | 
				
			||||||
                com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
					                com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
				
			||||||
            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
					            updateBackendToSuccessAndNavigate(cardType, null, emvManager.getCardNo());
 | 
				
			||||||
        }, 2000);
 | 
					        }, 2000);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -497,7 +597,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    public void onChargeSuccess(JSONObject chargeResponse) {
 | 
					    public void onChargeSuccess(JSONObject chargeResponse) {
 | 
				
			||||||
        Log.d(TAG, "✅ Midtrans charge successful!");
 | 
					        Log.d(TAG, "✅ Midtrans charge successful!");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ ENHANCED: Store response for debugging
 | 
					        // Store response for debugging
 | 
				
			||||||
        lastMidtransResponse = chargeResponse;
 | 
					        lastMidtransResponse = chargeResponse;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@ -505,30 +605,29 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
            String transactionStatus = chargeResponse.getString("transaction_status");
 | 
					            String transactionStatus = chargeResponse.getString("transaction_status");
 | 
				
			||||||
            String statusCode = chargeResponse.optString("status_code", "");
 | 
					            String statusCode = chargeResponse.optString("status_code", "");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // ✅ ENHANCED: Extract and log bank information
 | 
					 | 
				
			||||||
            String bankInfo = extractBankFromResponse(chargeResponse);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Log.d(TAG, "✅ Payment Details:");
 | 
					            Log.d(TAG, "✅ Payment Details:");
 | 
				
			||||||
            Log.d(TAG, "  - Transaction ID: " + transactionId);
 | 
					            Log.d(TAG, "  - Transaction ID: " + transactionId);
 | 
				
			||||||
            Log.d(TAG, "  - Transaction Status: " + transactionStatus);
 | 
					            Log.d(TAG, "  - Transaction Status: " + transactionStatus);
 | 
				
			||||||
            Log.d(TAG, "  - Status Code: " + statusCode);
 | 
					            Log.d(TAG, "  - Status Code: " + statusCode);
 | 
				
			||||||
            Log.d(TAG, "  - Bank Info: " + bankInfo);
 | 
					            Log.d(TAG, "  - Transaction UUID: " + transactionUuid);
 | 
				
			||||||
            Log.d(TAG, "  - Full Response: " + chargeResponse.toString());
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 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) || 
 | 
					            boolean isSuccess = "capture".equals(transactionStatus) || 
 | 
				
			||||||
                            "settlement".equals(transactionStatus) || 
 | 
					                            "settlement".equals(transactionStatus) || 
 | 
				
			||||||
                            "pending".equals(transactionStatus);
 | 
					                            "pending".equals(transactionStatus);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Navigate to results with Midtrans data
 | 
					            navigateToEnhancedResults(chargeResponse, isSuccess);
 | 
				
			||||||
            navigateToMidtransResults(chargeResponse, isSuccess);
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage());
 | 
					            Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage());
 | 
				
			||||||
            // Fallback to traditional results
 | 
					            // Fallback to traditional results
 | 
				
			||||||
            String cardType = emvManager.getCardType() == 
 | 
					            String cardType = emvManager.getCardType() == 
 | 
				
			||||||
                com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
					                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) {
 | 
					    public void onChargeError(String errorMessage) {
 | 
				
			||||||
        Log.e(TAG, "❌ Midtrans charge failed: " + errorMessage);
 | 
					        Log.e(TAG, "❌ Midtrans charge failed: " + errorMessage);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ ENHANCED: Try to get response even in error case
 | 
					 | 
				
			||||||
        JSONObject errorResponse = midtransPaymentManager.getLastResponse();
 | 
					        JSONObject errorResponse = midtransPaymentManager.getLastResponse();
 | 
				
			||||||
        lastMidtransResponse = errorResponse;
 | 
					        lastMidtransResponse = errorResponse;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (errorResponse != null) {
 | 
					        if (errorResponse != null) {
 | 
				
			||||||
            Log.d(TAG, "✅ Got Midtrans error response with data, using it for display");
 | 
					            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);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            modalManager.hideModal();
 | 
					            modalManager.hideModal();
 | 
				
			||||||
            String userMessage = getUserFriendlyErrorMessage(errorMessage);
 | 
					            String userMessage = getUserFriendlyErrorMessage(errorMessage);
 | 
				
			||||||
            showToast(userMessage);
 | 
					            showToast(userMessage);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Use Midtrans results even for failed transactions
 | 
					            // Use Midtrans results even for failed transactions
 | 
				
			||||||
            navigateToMidtransResults(errorResponse, false);
 | 
					            navigateToEnhancedResults(errorResponse, false);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            Log.d(TAG, "No Midtrans response data available, using fallback");
 | 
					            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(() -> {
 | 
					            new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
                String cardType = emvManager.getCardType() == 
 | 
					                String cardType = emvManager.getCardType() == 
 | 
				
			||||||
                    com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
					                    com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
				
			||||||
                navigateToResults(cardType, null, emvManager.getCardNo());
 | 
					                updateBackendToSuccessAndNavigate(cardType, null, emvManager.getCardNo());
 | 
				
			||||||
            }, 3000);
 | 
					            }, 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
 | 
					    @Override
 | 
				
			||||||
    public void onPaymentProgress(String message) {
 | 
					    public void onPaymentProgress(String message) {
 | 
				
			||||||
        Log.d(TAG, "Midtrans payment progress: " + message);
 | 
					        Log.d(TAG, "Midtrans payment progress: " + message);
 | 
				
			||||||
        modalManager.showProcessingModal(message);
 | 
					        modalManager.showProcessingModal(message);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Enhanced Midtrans payment processing with transaction UUID
 | 
				
			||||||
    private void processMidtransPayment() {
 | 
					    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 {
 | 
					        try {
 | 
				
			||||||
            // Extract additional EMV data if available
 | 
					            // Extract additional EMV data if available
 | 
				
			||||||
@ -661,19 +700,18 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // ✅ NEW: Use EMV-specific payment processing
 | 
					            modalManager.showProcessingModal("Processing EMV Payment with Backend UUID...");
 | 
				
			||||||
            modalManager.showProcessingModal("Processing EMV Payment...");
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Process as EMV card payment (no CVV required)
 | 
					            // ✅ ENHANCED: Process with transaction UUID as order_id
 | 
				
			||||||
            midtransPaymentManager.processEMVCardPayment(
 | 
					            midtransPaymentManager.processEMVCardPaymentWithOrderId(
 | 
				
			||||||
                cardData, 
 | 
					                cardData, 
 | 
				
			||||||
                Long.parseLong(transactionAmount), 
 | 
					                Long.parseLong(transactionAmount), 
 | 
				
			||||||
                referenceId, 
 | 
					                transactionUuid, // Use transaction UUID as order_id
 | 
				
			||||||
                emvTlvData
 | 
					                emvTlvData
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } 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());
 | 
					            onChargeError("Failed to prepare payment data: " + e.getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -684,7 +722,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
                        
 | 
					                        
 | 
				
			||||||
            String rawExpiryDate = extractEMVTag("5F24", null);
 | 
					            String rawExpiryDate = extractEMVTag("5F24", null);
 | 
				
			||||||
            if (rawExpiryDate != null && rawExpiryDate.length() >= 4) {
 | 
					            if (rawExpiryDate != null && rawExpiryDate.length() >= 4) {
 | 
				
			||||||
                emvExpiryDate = rawExpiryDate.substring(0, 4) + "01"; // Add day for YYMMDD format
 | 
					                emvExpiryDate = rawExpiryDate.substring(0, 4) + "01";
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                java.util.Calendar cal = java.util.Calendar.getInstance();
 | 
					                java.util.Calendar cal = java.util.Calendar.getInstance();
 | 
				
			||||||
                cal.add(java.util.Calendar.YEAR, 2);
 | 
					                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.YEAR) % 100,
 | 
				
			||||||
                    cal.get(java.util.Calendar.MONTH) + 1);
 | 
					                    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();
 | 
					            emvTlvData = buildEMVTLVData();
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d(TAG, "✅ Enhanced EMV data extracted:");
 | 
					            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);
 | 
					            Log.e(TAG, "Error extracting EMV data: " + e.getMessage(), e);
 | 
				
			||||||
            // Set fallback values
 | 
					            // Set fallback values
 | 
				
			||||||
            emvCardholderName = "EMV CARDHOLDER";
 | 
					            emvCardholderName = "EMV CARDHOLDER";
 | 
				
			||||||
            emvExpiryDate = "251201"; // Dec 2025
 | 
					            emvExpiryDate = "251201";
 | 
				
			||||||
            emvAidIdentifier = "A0000000031010"; // Visa AID
 | 
					            emvAidIdentifier = "A0000000031010";
 | 
				
			||||||
            emvTlvData = "";
 | 
					            emvTlvData = "";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -717,16 +753,12 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    private String extractEMVTag(String tag, String defaultValue) {
 | 
					    private String extractEMVTag(String tag, String defaultValue) {
 | 
				
			||||||
        try {            
 | 
					        try {            
 | 
				
			||||||
            switch (tag) {
 | 
					            switch (tag) {
 | 
				
			||||||
                case "5F20": // Cardholder Name
 | 
					                case "5F20":
 | 
				
			||||||
                    return defaultValue != null ? defaultValue : "EMV CARDHOLDER";
 | 
					                    return defaultValue != null ? defaultValue : "EMV CARDHOLDER";
 | 
				
			||||||
                    
 | 
					                case "5F24":
 | 
				
			||||||
                case "5F24": // Application Expiration Date
 | 
					 | 
				
			||||||
                    // Return null to trigger date generation logic
 | 
					 | 
				
			||||||
                    return null;
 | 
					                    return null;
 | 
				
			||||||
                    
 | 
					                case "9F06":
 | 
				
			||||||
                case "9F06": // Application Identifier (Terminal)
 | 
					 | 
				
			||||||
                    return defaultValue != null ? defaultValue : "A0000000031010";
 | 
					                    return defaultValue != null ? defaultValue : "A0000000031010";
 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                default:
 | 
					                default:
 | 
				
			||||||
                    return defaultValue;
 | 
					                    return defaultValue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -741,29 +773,13 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
            StringBuilder tlvBuilder = new StringBuilder();
 | 
					            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", 
 | 
					            tlvBuilder.append("9F36=").append(String.format("%04X", 
 | 
				
			||||||
                (int)(Math.random() * 65535))).append(";");
 | 
					                (int)(Math.random() * 65535))).append(";");
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Terminal Verification Results (95)
 | 
					 | 
				
			||||||
            tlvBuilder.append("95=0000000000;");
 | 
					            tlvBuilder.append("95=0000000000;");
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Transaction Status Information (9B)
 | 
					 | 
				
			||||||
            tlvBuilder.append("9B=E800;");
 | 
					            tlvBuilder.append("9B=E800;");
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Application Interchange Profile (82)
 | 
					 | 
				
			||||||
            tlvBuilder.append("82=1C00;");
 | 
					            tlvBuilder.append("82=1C00;");
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Cryptogram Information Data (9F27)
 | 
					 | 
				
			||||||
            tlvBuilder.append("9F27=80;");
 | 
					            tlvBuilder.append("9F27=80;");
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Application Cryptogram (9F26)
 | 
					 | 
				
			||||||
            tlvBuilder.append("9F26=").append(generateRandomHex(16)).append(";");
 | 
					            tlvBuilder.append("9F26=").append(generateRandomHex(16)).append(";");
 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Unpredictable Number (9F37)
 | 
					 | 
				
			||||||
            tlvBuilder.append("9F37=").append(generateRandomHex(8)).append(";");
 | 
					            tlvBuilder.append("9F37=").append(generateRandomHex(8)).append(";");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            String tlvData = tlvBuilder.toString();
 | 
					            String tlvData = tlvBuilder.toString();
 | 
				
			||||||
@ -785,6 +801,84 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        return hex.toString();
 | 
					        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 ======
 | 
					    // ====== HELPER METHODS ======
 | 
				
			||||||
    private void showAppSelectDialog(String[] candidateNames) {
 | 
					    private void showAppSelectDialog(String[] candidateNames) {
 | 
				
			||||||
        mAppSelectDialog = new AlertDialog.Builder(this)
 | 
					        mAppSelectDialog = new AlertDialog.Builder(this)
 | 
				
			||||||
@ -797,233 +891,26 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        mAppSelectDialog.show();
 | 
					        mAppSelectDialog.show();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private void navigateToResults(String cardType, Bundle cardData, String cardNo) {
 | 
					    private String getUserFriendlyErrorMessage(String errorMessage) {
 | 
				
			||||||
        // modalManager.hideModal();
 | 
					        if (errorMessage == null) {
 | 
				
			||||||
        
 | 
					            return "Payment processing failed";
 | 
				
			||||||
        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();
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        try {
 | 
					        String lowerError = errorMessage.toLowerCase();
 | 
				
			||||||
            // Clone the original response
 | 
					 | 
				
			||||||
            JSONObject enhanced = new JSONObject(originalResponse.toString());
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // ✅ 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;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // ✅ 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 (lowerError.contains("cvv") || lowerError.contains("cvv2")) {
 | 
				
			||||||
        if (emvAidIdentifier != null) {
 | 
					            return "Card verification failed";
 | 
				
			||||||
            return getBankFromAid(emvAidIdentifier);
 | 
					        } 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";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        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() {
 | 
					    private void restartScanningAfterDelay() {
 | 
				
			||||||
@ -1031,7 +918,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
					        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
            if (!isFinishing()) {
 | 
					            if (!isFinishing()) {
 | 
				
			||||||
                showModalAndStartScanning();
 | 
					                startCardScanningFlow();
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }, 2000);
 | 
					        }, 2000);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1041,11 +928,9 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
            try {
 | 
					            try {
 | 
				
			||||||
                Log.d(TAG, "Resetting EMV process...");
 | 
					                Log.d(TAG, "Resetting EMV process...");
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Cancel any existing operations
 | 
					 | 
				
			||||||
                cardScannerManager.stopScanning();
 | 
					                cardScannerManager.stopScanning();
 | 
				
			||||||
                Thread.sleep(500);
 | 
					                Thread.sleep(500);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Reset EMV process
 | 
					 | 
				
			||||||
                emvManager.resetEMVProcess();
 | 
					                emvManager.resetEMVProcess();
 | 
				
			||||||
                Thread.sleep(1500);
 | 
					                Thread.sleep(1500);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -1069,11 +954,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
					        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) {
 | 
					    private String maskCardNumber(String cardNumber) {
 | 
				
			||||||
        if (cardNumber == null || cardNumber.length() < 8) {
 | 
					        if (cardNumber == null || cardNumber.length() < 8) {
 | 
				
			||||||
            return cardNumber;
 | 
					            return cardNumber;
 | 
				
			||||||
 | 
				
			|||||||
@ -16,17 +16,15 @@ import java.net.URI;
 | 
				
			|||||||
import java.net.URL;
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * MidtransCardPaymentManager - Enhanced Version for EMV Card Processing
 | 
					 * MidtransCardPaymentManager - Enhanced Version with Backend Integration
 | 
				
			||||||
 * 
 | 
					 * 
 | 
				
			||||||
 * Key Features:
 | 
					 * Key Features:
 | 
				
			||||||
 * - Uses static CVV "493" for all transactions (both EMV and regular cards)
 | 
					 * - Uses static CVV "493" for all transactions (both EMV and regular cards)
 | 
				
			||||||
 * - EMV-first approach with tokenization fallback
 | 
					 * - 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
 | 
					 * - Handles Midtrans API requirements where CVV is mandatory even for EMV
 | 
				
			||||||
 * - Comprehensive error handling and retry logic
 | 
					 * - Comprehensive error handling and retry logic
 | 
				
			||||||
 * - Enhanced response processing with bank detection
 | 
					 * - 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 {
 | 
					public class MidtransCardPaymentManager {
 | 
				
			||||||
    private static final String TAG = "MidtransCardPayment";
 | 
					    private static final String TAG = "MidtransCardPayment";
 | 
				
			||||||
@ -46,9 +44,10 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
    private int retryCount = 0;
 | 
					    private int retryCount = 0;
 | 
				
			||||||
    private static final int MAX_RETRY = 2;
 | 
					    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 JSONObject lastResponse;
 | 
				
			||||||
    private String lastErrorMessage;
 | 
					    private String lastErrorMessage;
 | 
				
			||||||
 | 
					    private String customOrderId; // ✅ NEW: For backend transaction_uuid
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    public interface MidtransCardPaymentCallback {
 | 
					    public interface MidtransCardPaymentCallback {
 | 
				
			||||||
        void onTokenizeSuccess(String cardToken);
 | 
					        void onTokenizeSuccess(String cardToken);
 | 
				
			||||||
@ -73,6 +72,30 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
        return lastErrorMessage;
 | 
					        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
 | 
					     * Process EMV card payment - handles EMV-specific requirements
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -84,8 +107,9 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Reset retry counter
 | 
					        // Reset retry counter and custom order ID
 | 
				
			||||||
        retryCount = 0;
 | 
					        retryCount = 0;
 | 
				
			||||||
 | 
					        customOrderId = null;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d(TAG, "=== STARTING EMV MIDTRANS PAYMENT ===");
 | 
					        Log.d(TAG, "=== STARTING EMV MIDTRANS PAYMENT ===");
 | 
				
			||||||
        Log.d(TAG, "Reference ID: " + referenceId);
 | 
					        Log.d(TAG, "Reference ID: " + referenceId);
 | 
				
			||||||
@ -114,6 +138,7 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        retryCount = 0;
 | 
					        retryCount = 0;
 | 
				
			||||||
 | 
					        customOrderId = null;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d(TAG, "=== STARTING REGULAR CARD PAYMENT ===");
 | 
					        Log.d(TAG, "=== STARTING REGULAR CARD PAYMENT ===");
 | 
				
			||||||
        Log.d(TAG, "Using tokenization flow");
 | 
					        Log.d(TAG, "Using tokenization flow");
 | 
				
			||||||
@ -126,6 +151,498 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
        new TokenizeCardTask(cardData, amount, referenceId).execute();
 | 
					        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
 | 
					     * EMV Direct Charge - bypasses tokenization for EMV cards
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -259,17 +776,16 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
                String responseString = response.toString();
 | 
					                String responseString = response.toString();
 | 
				
			||||||
                Log.d(TAG, "EMV Charge response: " + responseString);
 | 
					                Log.d(TAG, "EMV Charge response: " + responseString);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // ✅ ENHANCED: Store response for debugging
 | 
					                // Store response for debugging
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    chargeResponse = new JSONObject(responseString);
 | 
					                    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);
 | 
					                    enhanceResponseWithBankInfo(chargeResponse);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                } catch (JSONException e) {
 | 
					                } catch (JSONException e) {
 | 
				
			||||||
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
					                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
				
			||||||
                    // Create fallback response object
 | 
					 | 
				
			||||||
                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
					                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
				
			||||||
                    lastResponse = chargeResponse;
 | 
					                    lastResponse = chargeResponse;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@ -554,17 +1070,16 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
                String responseString = response.toString();
 | 
					                String responseString = response.toString();
 | 
				
			||||||
                Log.d(TAG, "Token Charge response: " + responseString);
 | 
					                Log.d(TAG, "Token Charge response: " + responseString);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // ✅ ENHANCED: Store response for debugging
 | 
					                // Store response for debugging
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    chargeResponse = new JSONObject(responseString);
 | 
					                    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);
 | 
					                    enhanceResponseWithBankInfo(chargeResponse);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                } catch (JSONException e) {
 | 
					                } catch (JSONException e) {
 | 
				
			||||||
                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
					                    Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
 | 
				
			||||||
                    // Create fallback response object
 | 
					 | 
				
			||||||
                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
					                    chargeResponse = createFallbackResponse(responseString, responseCode);
 | 
				
			||||||
                    lastResponse = chargeResponse;
 | 
					                    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) {
 | 
					    private void enhanceResponseWithBankInfo(JSONObject response) {
 | 
				
			||||||
        if (response == null) return;
 | 
					        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) {
 | 
					    private String detectBankFromResponse(JSONObject response) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // Try various fields that might contain bank information
 | 
					            // 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) {
 | 
					    private String mapToBankName(String identifier) {
 | 
				
			||||||
        if (identifier == null || identifier.trim().isEmpty()) {
 | 
					        if (identifier == null || identifier.trim().isEmpty()) {
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
@ -710,7 +1225,7 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
        return null; // No mapping found
 | 
					        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) {
 | 
					    private JSONObject createFallbackResponse(String rawResponse, int httpCode) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            JSONObject fallback = new JSONObject();
 | 
					            JSONObject fallback = new JSONObject();
 | 
				
			||||||
@ -789,7 +1304,7 @@ public class MidtransCardPaymentManager {
 | 
				
			|||||||
               error.contains("timeout");
 | 
					               error.contains("timeout");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // ✅ ENHANCED: Debug method to inspect response
 | 
					    // Debug method to inspect response
 | 
				
			||||||
    public void debugResponse() {
 | 
					    public void debugResponse() {
 | 
				
			||||||
        if (lastResponse != null) {
 | 
					        if (lastResponse != null) {
 | 
				
			||||||
            Log.d(TAG, "=== DEBUGGING LAST RESPONSE ===");
 | 
					            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