From 53964211c2d58c9d7c80cb9d7dac32e60010eb3c Mon Sep 17 00:00:00 2001 From: riz081 Date: Fri, 27 Jun 2025 17:01:05 +0700 Subject: [PATCH] Result Transaction dan QRIS --- .../com/example/bdkipoc/ReceiptActivity.java | 564 ++++++++++++------ .../bdkipoc/qris/QrisResultActivity.java | 14 +- .../ResultTransactionActivity.java | 493 +++++---------- app/src/main/res/layout/activity_receipt.xml | 45 +- 4 files changed, 579 insertions(+), 537 deletions(-) diff --git a/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java b/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java index 0ec5251..b4521ea 100644 --- a/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java +++ b/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java @@ -23,6 +23,8 @@ import java.net.HttpURLConnection; import java.io.OutputStream; import java.net.URL; import java.net.URI; +import java.util.HashMap; +import java.util.Map; import com.example.bdkipoc.cetakulang.ReprintActivity; @@ -52,6 +54,28 @@ public class ReceiptActivity extends AppCompatActivity { private LinearLayout emailButton; private Button finishButton; + // ✅ ENHANCED: Mapping dari technical issuer ke display name + private static final Map ISSUER_DISPLAY_MAP = new HashMap() {{ + put("airpay shopee", "ShopeePay"); + put("shopeepay", "ShopeePay"); + put("shopee", "ShopeePay"); + put("linkaja", "LinkAja"); + put("link aja", "LinkAja"); + put("dana", "DANA"); + put("ovo", "OVO"); + put("gopay", "GoPay"); + put("jenius", "Jenius"); + put("sakuku", "Sakuku"); + put("bni", "BNI"); + put("bca", "BCA"); + put("mandiri", "Mandiri"); + put("bri", "BRI"); + put("cimb", "CIMB Niaga"); + put("permata", "Permata"); + put("maybank", "Maybank"); + put("qris", "QRIS"); + }}; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -118,10 +142,27 @@ public class ReceiptActivity extends AppCompatActivity { finishButton.setOnClickListener(v -> handleFinish()); } + /** + * ✅ ENHANCED: Load transaction data with support for both EMV/Card and QRIS + */ private void loadTransactionData() { Intent intent = getIntent(); if (intent != null) { - Log.d("ReceiptActivity", "=== LOADING TRANSACTION DATA ==="); + Log.d("ReceiptActivity", "=== LOADING ENHANCED TRANSACTION DATA ==="); + + // ✅ DETECT TRANSACTION TYPE + String callingActivity = intent.getStringExtra("calling_activity"); + String channelCategory = intent.getStringExtra("channel_category"); + String channelCode = intent.getStringExtra("channel_code"); + boolean isEmvTransaction = "EMV_CARD".equals(channelCategory) || "ResultTransactionActivity".equals(callingActivity); + boolean isQrisTransaction = "QRIS".equalsIgnoreCase(channelCode) || "QrisResultActivity".equals(callingActivity); + + Log.d("ReceiptActivity", "🔍 TRANSACTION TYPE DETECTION:"); + Log.d("ReceiptActivity", " Calling Activity: " + callingActivity); + Log.d("ReceiptActivity", " Channel Category: " + channelCategory); + Log.d("ReceiptActivity", " Channel Code: " + channelCode); + Log.d("ReceiptActivity", " Is EMV Transaction: " + isEmvTransaction); + Log.d("ReceiptActivity", " Is QRIS Transaction: " + isQrisTransaction); // Get all available data from intent String amount = intent.getStringExtra("transaction_amount"); @@ -135,99 +176,154 @@ public class ReceiptActivity extends AppCompatActivity { String createdAt = intent.getStringExtra("created_at"); String paymentMethodStr = intent.getStringExtra("payment_method"); String cardTypeStr = intent.getStringExtra("card_type"); - String channelCode = intent.getStringExtra("channel_code"); - String channelCategory = intent.getStringExtra("channel_category"); String acquirer = intent.getStringExtra("acquirer"); String mid = intent.getStringExtra("mid"); String tid = intent.getStringExtra("tid"); + // ✅ EMV SPECIFIC DATA + String emvCardholderName = intent.getStringExtra("emv_cardholder_name"); + String emvAid = intent.getStringExtra("emv_aid"); + String emvExpiry = intent.getStringExtra("emv_expiry"); + String cardNumber = intent.getStringExtra("card_number"); + String midtransResponse = intent.getStringExtra("midtrans_response"); + boolean emvMode = intent.getBooleanExtra("emv_mode", false); + + // ✅ QRIS SPECIFIC DATA + String qrString = intent.getStringExtra("qr_string"); + // Log received data for debugging Log.d("ReceiptActivity", "🔍 RECEIVED DATA:"); Log.d("ReceiptActivity", " amount: " + amount); Log.d("ReceiptActivity", " referenceId: " + referenceId); Log.d("ReceiptActivity", " orderId: " + orderId); - Log.d("ReceiptActivity", " channelCode: " + channelCode); Log.d("ReceiptActivity", " acquirer (from intent): " + acquirer); Log.d("ReceiptActivity", " createdAt: " + createdAt); + Log.d("ReceiptActivity", " EMV Mode: " + emvMode); + Log.d("ReceiptActivity", " EMV Cardholder: " + emvCardholderName); + Log.d("ReceiptActivity", " Card Number: " + (cardNumber != null ? cardNumber : "N/A")); + Log.d("ReceiptActivity", " QR String Available: " + (qrString != null && !qrString.isEmpty())); // 1. Set merchant data with defaults - merchantName.setText(merchantNameStr != null ? merchantNameStr : "Marcel Panjaitan"); - merchantLocation.setText(merchantLocationStr != null ? merchantLocationStr : "Jakarta, Indonesia"); + merchantName.setText(merchantNameStr != null ? merchantNameStr : "TOKO KLONTONG PAK EKO"); + merchantLocation.setText(merchantLocationStr != null ? merchantLocationStr : "Ciputat Baru, Tangsel"); // 2. Set MID and TID - midText.setText(mid != null ? mid : "71000026521"); - tidText.setText(tid != null ? tid : "73001500"); + midText.setText("MID: " + (mid != null ? mid : "123456789901")); + tidText.setText("TID: " + (tid != null ? tid : "123456789901")); // 3. Set transaction number - String displayTransactionNumber = null; - if (referenceId != null && !referenceId.isEmpty()) { - displayTransactionNumber = referenceId; - } else if (transactionId != null && !transactionId.isEmpty()) { - displayTransactionNumber = transactionId; - } else if (orderId != null && !orderId.isEmpty()) { - displayTransactionNumber = orderId; - } - transactionNumber.setText(displayTransactionNumber != null ? displayTransactionNumber : "N/A"); + String displayTransactionNumber = getDisplayTransactionNumber(referenceId, transactionId, orderId); + transactionNumber.setText(displayTransactionNumber); // 4. Set transaction date - String displayDate = null; - if (createdAt != null && !createdAt.isEmpty()) { - displayDate = formatDateFromCreatedAt(createdAt); - } else if (transactionDateStr != null && !transactionDateStr.isEmpty()) { - displayDate = transactionDateStr; - } else { - displayDate = getCurrentDateTime(); - } + String displayDate = getDisplayTransactionDate(createdAt, transactionDateStr, isEmvTransaction); transactionDate.setText(displayDate); - // 5. Set payment method - String displayPaymentMethod = getPaymentMethodFromChannelCode(channelCode, paymentMethodStr); + // 5. ✅ ENHANCED: Set payment method based on transaction type + String displayPaymentMethod = getDisplayPaymentMethod(channelCode, paymentMethodStr, isEmvTransaction, emvMode); paymentMethod.setText(displayPaymentMethod); - // 6. ✅ IMPROVED: Enhanced card type detection for QRIS - String displayCardType = null; - - if (channelCode != null && channelCode.equalsIgnoreCase("QRIS")) { - Log.d("ReceiptActivity", "🔍 QRIS transaction detected - searching for real acquirer"); - - // For QRIS, try to get real acquirer from webhook data - if (referenceId != null && !referenceId.isEmpty()) { - String realAcquirer = fetchRealAcquirerSync(referenceId); - if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) { - displayCardType = getCardTypeFromAcquirer(realAcquirer, null, null); - Log.d("ReceiptActivity", "✅ QRIS real acquirer found: " + realAcquirer + " -> " + displayCardType); - } else { - Log.w("ReceiptActivity", "⚠️ QRIS real acquirer not found, using generic QRIS"); - displayCardType = "QRIS"; - - // Start async search for better results - fetchRealAcquirerFromWebhook(referenceId); - } - } else { - displayCardType = "QRIS"; - } - } else { - // Non-QRIS transaction - displayCardType = getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr); - } - + // 6. ✅ ENHANCED: Set card type with EMV and QRIS priority + String displayCardType = getDisplayCardType(cardTypeStr, acquirer, channelCode, isEmvTransaction, + isQrisTransaction, midtransResponse, referenceId); cardType.setText(displayCardType); - Log.d("ReceiptActivity", "💳 FINAL CARD TYPE: " + displayCardType); - // 7. Format and set amounts - setAmountData(amount, grossAmount); + // 7. ✅ Format and set amounts with proper calculation + setAmountDataEnhanced(amount, grossAmount, isEmvTransaction, isQrisTransaction); - Log.d("ReceiptActivity", "=== TRANSACTION DATA LOADED ==="); + Log.d("ReceiptActivity", "💳 FINAL DISPLAY VALUES:"); + Log.d("ReceiptActivity", " Payment Method: " + displayPaymentMethod); + Log.d("ReceiptActivity", " Card Type: " + displayCardType); + Log.d("ReceiptActivity", " Transaction Number: " + displayTransactionNumber); + Log.d("ReceiptActivity", "=== ENHANCED TRANSACTION DATA LOADED ==="); } } - + + /** + * Get display transaction number with priority + */ + private String getDisplayTransactionNumber(String referenceId, String transactionId, String orderId) { + if (referenceId != null && !referenceId.isEmpty()) { + return referenceId; + } else if (transactionId != null && !transactionId.isEmpty()) { + // For long transaction IDs, show last 10 characters + return transactionId.length() > 10 ? + transactionId.substring(transactionId.length() - 10) : transactionId; + } else if (orderId != null && !orderId.isEmpty()) { + return orderId.length() > 10 ? + orderId.substring(orderId.length() - 10) : orderId; + } + return String.valueOf(System.currentTimeMillis() % 10000000000L); + } + + /** + * Get display transaction date with proper formatting + */ + private String getDisplayTransactionDate(String createdAt, String transactionDateStr, boolean isEmvTransaction) { + String dateToFormat = null; + + if (createdAt != null && !createdAt.isEmpty()) { + dateToFormat = createdAt; + } else if (transactionDateStr != null && !transactionDateStr.isEmpty()) { + dateToFormat = transactionDateStr; + } + + if (dateToFormat != null) { + if (isEmvTransaction) { + return formatDateForEmvTransaction(dateToFormat); + } else { + return formatDateFromCreatedAt(dateToFormat); + } + } + + return getCurrentDateTime(); + } + + /** + * Format date specifically for EMV transactions + */ + private String formatDateForEmvTransaction(String dateString) { + try { + // EMV transactions might use different date formats + String[] inputFormats = { + "dd MMMM yyyy HH:mm", + "yyyy-MM-dd HH:mm:ss", + "yyyy-MM-dd'T'HH:mm:ss'Z'", + "dd/MM/yyyy HH:mm" + }; + + SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID")); + + for (String format : inputFormats) { + try { + SimpleDateFormat inputFormat = new SimpleDateFormat(format, + format.contains("MMMM") ? new Locale("id", "ID") : Locale.getDefault()); + Date date = inputFormat.parse(dateString); + String formatted = outputFormat.format(date); + Log.d("ReceiptActivity", "EMV Date formatting: '" + dateString + "' -> '" + formatted + "'"); + return formatted; + } catch (Exception ignored) { + // Try next format + } + } + + // If all formats fail, return as-is + Log.w("ReceiptActivity", "Could not format EMV date: " + dateString); + return dateString; + + } catch (Exception e) { + Log.e("ReceiptActivity", "Error formatting EMV date: " + dateString, e); + return dateString; + } + } + private String formatDateFromCreatedAt(String createdAt) { try { // Input format from database: "yyyy-MM-dd HH:mm:ss" SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); // Output format for receipt: "dd/MM/yyyy HH:mm" - SimpleDateFormat outputFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm", new Locale("id", "ID")); + SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID")); Date date = inputFormat.parse(createdAt); String formatted = outputFormat.format(date); @@ -242,6 +338,39 @@ public class ReceiptActivity extends AppCompatActivity { } } + /** + * Get display payment method with EMV support + */ + private String getDisplayPaymentMethod(String channelCode, String fallbackPaymentMethod, + boolean isEmvTransaction, boolean emvMode) { + if (isEmvTransaction) { + // For EMV transactions, be more specific + if (emvMode) { + if (channelCode != null) { + switch (channelCode.toUpperCase()) { + case "CREDIT": return "Kartu Kredit (EMV)"; + case "DEBIT": return "Kartu Debit (EMV)"; + default: return "Kartu Kredit (EMV)"; + } + } + return "Kartu Kredit (EMV)"; + } else { + // Magnetic stripe + if (channelCode != null) { + switch (channelCode.toUpperCase()) { + case "CREDIT": return "Kartu Kredit"; + case "DEBIT": return "Kartu Debit"; + default: return "Kartu Kredit"; + } + } + return "Kartu Kredit"; + } + } else { + // For QRIS and other transactions, use existing logic + return getPaymentMethodFromChannelCode(channelCode, fallbackPaymentMethod); + } + } + /** * Get payment method name from channel_code with comprehensive mapping */ @@ -291,6 +420,78 @@ public class ReceiptActivity extends AppCompatActivity { return fallbackPaymentMethod != null ? fallbackPaymentMethod : "QRIS"; } + /** + * ✅ ENHANCED: Card type detection with EMV and QRIS priority + */ + private String getDisplayCardType(String cardTypeStr, String acquirer, String channelCode, + boolean isEmvTransaction, boolean isQrisTransaction, + String midtransResponse, String referenceId) { + + Log.d("ReceiptActivity", "🔍 ENHANCED CARD TYPE DETECTION:"); + Log.d("ReceiptActivity", " Input Card Type: " + cardTypeStr); + Log.d("ReceiptActivity", " Input Acquirer: " + acquirer); + Log.d("ReceiptActivity", " Is EMV Transaction: " + isEmvTransaction); + Log.d("ReceiptActivity", " Is QRIS Transaction: " + isQrisTransaction); + + if (isEmvTransaction) { + // ✅ FOR EMV TRANSACTIONS: Priority to cardTypeStr from ResultTransactionActivity + if (cardTypeStr != null && !cardTypeStr.isEmpty() && !cardTypeStr.equalsIgnoreCase("unknown")) { + Log.d("ReceiptActivity", "✅ EMV: Using provided card type: " + cardTypeStr); + return cardTypeStr; + } + + // ✅ FALLBACK: Try to extract from Midtrans response + if (midtransResponse != null && !midtransResponse.isEmpty()) { + String bankFromMidtrans = extractBankFromMidtransResponse(midtransResponse); + if (bankFromMidtrans != null && !bankFromMidtrans.isEmpty()) { + Log.d("ReceiptActivity", "✅ EMV: Using bank from Midtrans response: " + bankFromMidtrans); + return bankFromMidtrans; + } + } + + // ✅ FALLBACK: Use acquirer + if (acquirer != null && !acquirer.isEmpty() && !acquirer.equalsIgnoreCase("qris")) { + String mappedAcquirer = getCardTypeFromAcquirer(acquirer, channelCode, null); + Log.d("ReceiptActivity", "✅ EMV: Using mapped acquirer: " + mappedAcquirer); + return mappedAcquirer; + } + + Log.d("ReceiptActivity", "⚠️ EMV: Using default fallback: BCA"); + return "BCA"; // Default for EMV + + } else if (isQrisTransaction) { + // ✅ FOR QRIS TRANSACTIONS: Enhanced detection + Log.d("ReceiptActivity", "🔍 QRIS transaction detected - searching for real acquirer"); + + // Priority 1: Use provided cardTypeStr if it's not generic + if (cardTypeStr != null && !cardTypeStr.isEmpty() && + !cardTypeStr.equalsIgnoreCase("qris") && !cardTypeStr.equalsIgnoreCase("unknown")) { + Log.d("ReceiptActivity", "✅ QRIS: Using provided specific card type: " + cardTypeStr); + return cardTypeStr; + } + + // Priority 2: Search webhook logs for real acquirer + if (referenceId != null && !referenceId.isEmpty()) { + String realAcquirer = fetchRealAcquirerSync(referenceId); + if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) { + String mappedQrisAcquirer = getCardTypeFromAcquirer(realAcquirer, null, null); + Log.d("ReceiptActivity", "✅ QRIS real acquirer found: " + realAcquirer + " -> " + mappedQrisAcquirer); + return mappedQrisAcquirer; + } else { + Log.w("ReceiptActivity", "⚠️ QRIS real acquirer not found, using generic QRIS"); + // Start async search for better results + fetchRealAcquirerFromWebhook(referenceId); + return "QRIS"; + } + } else { + return "QRIS"; + } + } else { + // ✅ FOR OTHER TRANSACTIONS: Use standard logic + return getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr); + } + } + private String getCardTypeFromAcquirer(String acquirer, String channelCode, String fallbackCardType) { // STEP 1: If we have a valid acquirer that's not generic "qris", use it if (acquirer != null && !acquirer.isEmpty() && !acquirer.equalsIgnoreCase("qris")) { @@ -299,88 +500,14 @@ public class ReceiptActivity extends AppCompatActivity { Log.d("ReceiptActivity", "🔍 Mapping acquirer: '" + acquirer + "' -> '" + acq + "'"); // ✅ COMPREHENSIVE acquirer mapping (case-insensitive) + String mappedName = ISSUER_DISPLAY_MAP.get(acq); + if (mappedName != null) { + Log.d("ReceiptActivity", "✅ Mapped acquirer: " + acquirer + " -> " + mappedName); + return mappedName; + } + + // Additional mapping for variations not in the map switch (acq) { - // E-Wallet acquirers (most common for QRIS) - case "gopay": - case "go-pay": - case "gojek": return "GoPay"; - - case "shopeepay": - case "shopee_pay": - case "shopee": return "ShopeePay"; - - case "ovo": return "OVO"; - - case "dana": return "DANA"; - - case "linkaja": - case "link_aja": - case "tcash": return "LinkAja"; - - case "jenius": - case "btpn": return "Jenius"; - - case "kaspro": - case "kas_pro": return "KasPro"; - - case "sakuku": - case "saku_ku": return "SakuKu"; - - case "doku": - case "doku_wallet": return "DOKU"; - - case "paymi": - case "pay_mi": return "PayMi"; - - case "isaku": - case "i_saku": return "i.Saku"; - - // Bank acquirers - case "bca": - case "bank_bca": return "BCA"; - - case "mandiri": - case "bank_mandiri": - case "mandiri_bill": return "Mandiri"; - - case "bni": - case "bank_bni": - case "bni_va": return "BNI"; - - case "bri": - case "bank_bri": - case "bri_va": return "BRI"; - - case "permata": - case "bank_permata": - case "permata_va": return "Permata"; - - case "cimb": - case "cimb_niaga": - case "bank_cimb": - case "cimb_va": return "CIMB Niaga"; - - case "danamon": - case "bank_danamon": - case "danamon_va": return "Danamon"; - - case "bsi": - case "bank_bsi": - case "bsi_va": - case "syariah_indonesia": return "BSI"; - - case "maybank": - case "bank_maybank": return "Maybank"; - - case "bca_digital": - case "blu": return "BCA Digital"; - - case "jago": - case "bank_jago": return "Bank Jago"; - - case "seabank": - case "sea_bank": return "SeaBank"; - // Credit card acquirers case "visa": return "Visa"; case "mastercard": @@ -740,6 +867,78 @@ public class ReceiptActivity extends AppCompatActivity { return null; // No acquirer found for specified criteria } + /** + * Extract bank from Midtrans response JSON string + */ + private String extractBankFromMidtransResponse(String midtransResponse) { + try { + org.json.JSONObject response = new org.json.JSONObject(midtransResponse); + + // Try different possible bank fields + String[] bankFields = {"bank", "issuer", "acquiring_bank", "issuer_bank"}; + + for (String field : bankFields) { + if (response.has(field)) { + String bankValue = response.getString(field); + if (bankValue != null && !bankValue.trim().isEmpty() && !bankValue.equalsIgnoreCase("qris")) { + Log.d("ReceiptActivity", "Found bank in Midtrans response (" + field + "): " + bankValue); + return formatBankNameForReceipt(bankValue); + } + } + } + + Log.w("ReceiptActivity", "No valid bank found in Midtrans response"); + return null; + + } catch (Exception e) { + Log.e("ReceiptActivity", "Error extracting bank from Midtrans response: " + e.getMessage()); + return null; + } + } + + /** + * Format bank name specifically for receipt display + */ + private String formatBankNameForReceipt(String bankName) { + if (bankName == null || bankName.trim().isEmpty()) { + return "BCA"; // Default + } + + String formatted = bankName.trim(); + + // Common bank name mappings for receipt display + switch (formatted.toUpperCase()) { + case "BCA": + case "BANK BCA": + case "BANK CENTRAL ASIA": return "BCA"; + + case "MANDIRI": + case "BANK MANDIRI": return "Mandiri"; + + case "BNI": + case "BANK BNI": + case "BANK NEGARA INDONESIA": return "BNI"; + + case "BRI": + case "BANK BRI": + case "BANK RAKYAT INDONESIA": return "BRI"; + + case "CIMB": + case "CIMB NIAGA": + case "BANK CIMB NIAGA": return "CIMB Niaga"; + + case "DANAMON": + case "BANK DANAMON": return "Danamon"; + + case "PERMATA": + case "BANK PERMATA": return "Permata"; + + default: + // Return capitalized version + return capitalizeFirstLetter(formatted); + } + } + private String capitalizeFirstLetter(String input) { if (input == null || input.isEmpty()) { return input; @@ -758,51 +957,79 @@ public class ReceiptActivity extends AppCompatActivity { return cleaned.substring(0, 1).toUpperCase() + cleaned.substring(1).toLowerCase(); } - private void setAmountData(String amount, String grossAmount) { - // Prioritize 'amount' over 'grossAmount' for transaction data + /** + * ✅ ENHANCED: Set amount data with EMV and QRIS support + */ + private void setAmountDataEnhanced(String amount, String grossAmount, boolean isEmvTransaction, boolean isQrisTransaction) { String amountToUse = amount != null ? amount : grossAmount; - Log.d("ReceiptActivity", "Setting amount data - amount: " + amount + - ", grossAmount: " + grossAmount + ", using: " + amountToUse); + Log.d("ReceiptActivity", "Setting enhanced amount data - amount: " + amount + + ", grossAmount: " + grossAmount + ", using: " + amountToUse + + ", isEMV: " + isEmvTransaction + ", isQRIS: " + isQrisTransaction); if (amountToUse != null) { try { - // Clean and parse the amount String cleanAmount = cleanAmountString(amountToUse); Log.d("ReceiptActivity", "Cleaned amount: " + cleanAmount); - // Parse as long integer (Indonesian Rupiah doesn't use decimal cents) long amountLong = Long.parseLong(cleanAmount); // Set transaction total - transactionTotal.setText("Rp " + formatCurrency(amountLong)); + transactionTotal.setText(formatCurrency(amountLong)); - // Calculate tax and service fee (for QRIS, typically no additional fees) - long tax = 0; // QRIS usually doesn't have tax - long serviceFeeValue = 0; // QRIS usually doesn't have service fee - long total = amountLong + tax + serviceFeeValue; + // ✅ CALCULATE FEES BASED ON TRANSACTION TYPE + long tax = 0; + long serviceFeeValue = 0; + long total = amountLong; + + if (isEmvTransaction) { + // For EMV transactions, check if gross amount includes additional fees + if (grossAmount != null && !grossAmount.equals(amount)) { + try { + long grossAmountLong = Long.parseLong(cleanAmountString(grossAmount)); + long difference = grossAmountLong - amountLong; + + if (difference > 0) { + // Assume 11% tax and 500 service fee (adjust based on your business logic) + tax = Math.round(amountLong * 0.11); + serviceFeeValue = 500; + total = grossAmountLong; + + Log.d("ReceiptActivity", "EMV: Calculated tax=" + tax + ", service=" + serviceFeeValue + ", total=" + total); + } + } catch (Exception e) { + Log.w("ReceiptActivity", "Could not parse gross amount for EMV: " + grossAmount); + } + } + } else if (isQrisTransaction) { + // For QRIS, typically no additional fees (tax=0, service=0) + tax = 0; + serviceFeeValue = 0; + total = amountLong; + Log.d("ReceiptActivity", "QRIS: No additional fees - total=" + total); + } // Set calculated values - taxPercentage.setText("Rp 0"); - serviceFee.setText("Rp 0"); - finalTotal.setText("Rp " + formatCurrency(total)); + taxPercentage.setText(tax > 0 ? "11%" : "0%"); + serviceFee.setText(formatCurrency(serviceFeeValue)); + finalTotal.setText(formatCurrency(total)); - Log.d("ReceiptActivity", "Amount formatting successful: " + amountLong + " -> Rp " + formatCurrency(total)); + Log.d("ReceiptActivity", "Enhanced amount formatting successful: " + amountLong + " -> " + formatCurrency(total)); } catch (NumberFormatException e) { - Log.e("ReceiptActivity", "Error parsing amount: " + amountToUse, e); + Log.e("ReceiptActivity", "Error parsing enhanced amount: " + amountToUse, e); // Fallback if parsing fails - transactionTotal.setText("Rp " + amountToUse); - taxPercentage.setText("Rp 0"); - serviceFee.setText("Rp 0"); - finalTotal.setText("Rp " + amountToUse); + transactionTotal.setText(amountToUse); + taxPercentage.setText("0%"); + serviceFee.setText("0"); + finalTotal.setText(amountToUse); } } else { // Default values if no amount provided - transactionTotal.setText("Rp 0"); - taxPercentage.setText("Rp 0"); - serviceFee.setText("Rp 0"); - finalTotal.setText("Rp 0"); + transactionTotal.setText("0"); + taxPercentage.setText("0%"); + serviceFee.setText("0"); + finalTotal.setText("0"); } } @@ -876,7 +1103,7 @@ public class ReceiptActivity extends AppCompatActivity { } private String getCurrentDateTime() { - SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm", new Locale("id", "ID")); + SimpleDateFormat sdf = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID")); return sdf.format(new Date()); } @@ -916,6 +1143,11 @@ public class ReceiptActivity extends AppCompatActivity { navigateToHomePage(); break; + case "ResultTransactionActivity": + // ✅ NEW: Handle back from ResultTransactionActivity + navigateToHomePage(); + break; + case "PaymentActivity": case "QrisActivity": // Go back to payment/qris activity diff --git a/app/src/main/java/com/example/bdkipoc/qris/QrisResultActivity.java b/app/src/main/java/com/example/bdkipoc/qris/QrisResultActivity.java index 1ba75f6..2d46142 100644 --- a/app/src/main/java/com/example/bdkipoc/qris/QrisResultActivity.java +++ b/app/src/main/java/com/example/bdkipoc/qris/QrisResultActivity.java @@ -78,15 +78,15 @@ public class QrisResultActivity extends AppCompatActivity { private String currentQrImageUrl; private int originalAmount; - // ✅ ADD: QR String untuk validasi QRIS + // ✅ QR String untuk validasi QRIS private String currentQrString = ""; private String qrStringFromMidtrans = ""; - // ✅ FIXED: Store actual issuer/acquirer from Midtrans response + // ✅ Store actual issuer/acquirer from Midtrans response private String actualIssuerFromMidtrans = ""; private String actualAcquirerFromMidtrans = ""; - // ✅ ADD: Track QR refresh transaction for payment monitoring + // ✅ Track QR refresh transaction for payment monitoring private String currentQrTransactionId = ""; private boolean isMonitoringQrRefreshTransaction = false; @@ -97,7 +97,7 @@ public class QrisResultActivity extends AppCompatActivity { private static final String MIDTRANS_AUTH = "Basic U0ItTWlkLXNlcnZlci1PM2t1bXkwVDl4M1VvYnVvVTc3NW5QbXc="; private static final String MIDTRANS_CHARGE_URL = "https://api.sandbox.midtrans.com/v2/charge"; - // ✅ ADD: Mapping dari technical issuer ke display name + // ✅ Mapping dari technical issuer ke display name private static final Map ISSUER_DISPLAY_MAP = new HashMap() {{ put("airpay shopee", "ShopeePay"); put("shopeepay", "ShopeePay"); @@ -125,6 +125,8 @@ public class QrisResultActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + // ✅ TETAP MENGGUNAKAN LAYOUT ASLI UNTUK QR DISPLAY setContentView(R.layout.activity_qris_result); // Initialize views @@ -409,7 +411,7 @@ public class QrisResultActivity extends AppCompatActivity { } } - // ✅ FIXED: Return both QR URL and QR String + // ✅ ENHANCED: Return both QR URL and QR String private QrRefreshResult generateNewQrCode() { try { Log.d("QrisResultFlow", "🔧 Refreshing QR code for existing transaction"); @@ -807,7 +809,7 @@ public class QrisResultActivity extends AppCompatActivity { return fallbackName; } - // ✅ COMPLETELY FIXED: Use actual issuer from Midtrans response + // ✅ ENHANCED: Use actual issuer from Midtrans response private void syncTransactionStatusToBackend(String finalStatus) { Log.d("QrisResultFlow", "🔄 Syncing status '" + finalStatus + "' to backend for reference: " + referenceId); diff --git a/app/src/main/java/com/example/bdkipoc/transaction/ResultTransactionActivity.java b/app/src/main/java/com/example/bdkipoc/transaction/ResultTransactionActivity.java index a5e96ca..8e53734 100644 --- a/app/src/main/java/com/example/bdkipoc/transaction/ResultTransactionActivity.java +++ b/app/src/main/java/com/example/bdkipoc/transaction/ResultTransactionActivity.java @@ -6,6 +6,8 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.View; +import android.widget.Button; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -14,7 +16,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import com.example.bdkipoc.R; -import com.google.android.material.button.MaterialButton; import org.json.JSONException; import org.json.JSONObject; @@ -25,30 +26,39 @@ import java.util.Date; import java.util.Locale; /** - * ResultTransactionActivity - Enhanced Receipt-style Display with Better Bank Detection - * Shows transaction results in a professional receipt format with improved debugging + * ResultTransactionActivity - Enhanced Receipt-style Display using activity_receipt.xml + * Shows EMV/Card transaction results using the same layout as QRIS receipts */ public class ResultTransactionActivity extends AppCompatActivity { private static final String TAG = "ResultTransaction"; - // UI Components - New Receipt Layout - private TextView tvMerchantName, tvMerchantLocation; - private TextView tvTid, tvTransactionNumber, tvTransactionDate; - private TextView tvPaymentMethodDetail, tvCardType; - private TextView tvSubtotal, tvTax, tvServiceFee, tvFinalTotal; - private LinearLayout btnPrint, btnEmail; - private MaterialButton btnFinish; + // ✅ UI Components using activity_receipt.xml IDs private LinearLayout backNavigation; + private ImageView backArrow; + private TextView toolbarTitle; - // Hidden compatibility components - private TextView tvAmount, tvStatus, tvReference, tvCardInfo; - private TextView tvPaymentMethod, tvTransactionId, tvOrderId, tvTimestamp; - private TextView tvResponseData, tvErrorDetails; - private LinearLayout layoutErrorDetails; + // Receipt details + private TextView merchantName; + private TextView merchantLocation; + private TextView midText; + private TextView tidText; + private TextView transactionNumber; + private TextView transactionDate; + private TextView paymentMethod; + private TextView cardType; + private TextView transactionTotal; + private TextView taxPercentage; + private TextView serviceFee; + private TextView finalTotal; + + // Action buttons + private LinearLayout printButton; + private LinearLayout emailButton; + private Button finishButton; // Data from intent private String transactionAmount; - private String cardType; + private String cardTypeFromIntent; private boolean emvMode; private String referenceId; private String cardNo; @@ -66,18 +76,21 @@ public class ResultTransactionActivity extends AppCompatActivity { private long subtotalAmount = 0; private long taxAmount = 0; private long serviceFeeAmount = 500; // Default service fee - private double taxPercentage = 0.11; // 11% tax + private double taxPercentageValue = 0.11; // 11% tax @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_result_transaction); + + // ✅ CRITICAL: Use the same layout as ReceiptActivity + setContentView(R.layout.activity_receipt); Log.d(TAG, "=== RESULT TRANSACTION ACTIVITY STARTED ==="); + Log.d(TAG, "✅ Using activity_receipt.xml layout"); initViews(); extractIntentData(); - debugAllDataSources(); // ✅ ENHANCED: Debug all available data + debugAllDataSources(); setupListeners(); calculateAmounts(); displayReceiptData(); @@ -86,47 +99,40 @@ public class ResultTransactionActivity extends AppCompatActivity { } private void initViews() { + // ✅ Initialize views using activity_receipt.xml IDs + // Navigation backNavigation = findViewById(R.id.back_navigation); + backArrow = findViewById(R.id.backArrow); + toolbarTitle = findViewById(R.id.toolbarTitle); - // New Receipt Layout Components - tvMerchantName = findViewById(R.id.tv_merchant_name); - tvMerchantLocation = findViewById(R.id.tv_merchant_location); - tvTid = findViewById(R.id.tv_tid); - tvTransactionNumber = findViewById(R.id.tv_transaction_number); - tvTransactionDate = findViewById(R.id.tv_transaction_date); - tvPaymentMethodDetail = findViewById(R.id.tv_payment_method_detail); - tvCardType = findViewById(R.id.tv_card_type); - tvSubtotal = findViewById(R.id.tv_subtotal); - tvTax = findViewById(R.id.tv_tax); - tvServiceFee = findViewById(R.id.tv_service_fee); - tvFinalTotal = findViewById(R.id.tv_final_total); + // Receipt details + merchantName = findViewById(R.id.merchant_name); + merchantLocation = findViewById(R.id.merchant_location); + midText = findViewById(R.id.mid_text); + tidText = findViewById(R.id.tid_text); + transactionNumber = findViewById(R.id.transaction_number); + transactionDate = findViewById(R.id.transaction_date); + paymentMethod = findViewById(R.id.payment_method); + cardType = findViewById(R.id.card_type); + transactionTotal = findViewById(R.id.transaction_total); + taxPercentage = findViewById(R.id.tax_percentage); + serviceFee = findViewById(R.id.service_fee); + finalTotal = findViewById(R.id.final_total); // Action buttons - btnPrint = findViewById(R.id.btn_print); - btnEmail = findViewById(R.id.btn_email); - btnFinish = findViewById(R.id.btn_finish); + printButton = findViewById(R.id.print_button); + emailButton = findViewById(R.id.email_button); + finishButton = findViewById(R.id.finish_button); - // Hidden compatibility components - tvAmount = findViewById(R.id.tv_amount); - tvStatus = findViewById(R.id.tv_status); - tvReference = findViewById(R.id.tv_reference); - tvCardInfo = findViewById(R.id.tv_card_info); - tvPaymentMethod = findViewById(R.id.tv_payment_method); - tvTransactionId = findViewById(R.id.tv_transaction_id); - tvOrderId = findViewById(R.id.tv_order_id); - tvTimestamp = findViewById(R.id.tv_timestamp); - tvResponseData = findViewById(R.id.tv_response_data); - tvErrorDetails = findViewById(R.id.tv_error_details); - layoutErrorDetails = findViewById(R.id.layout_error_details); + Log.d(TAG, "✅ All views initialized using activity_receipt.xml"); } - // ✅ ENHANCED: Better intent data extraction with comprehensive debugging private void extractIntentData() { Intent intent = getIntent(); transactionAmount = intent.getStringExtra("TRANSACTION_AMOUNT"); - cardType = intent.getStringExtra("CARD_TYPE"); + cardTypeFromIntent = intent.getStringExtra("CARD_TYPE"); emvMode = intent.getBooleanExtra("EMV_MODE", false); referenceId = intent.getStringExtra("REFERENCE_ID"); cardNo = intent.getStringExtra("CARD_NO"); @@ -136,41 +142,29 @@ public class ResultTransactionActivity extends AppCompatActivity { emvAid = intent.getStringExtra("EMV_AID"); emvExpiry = intent.getStringExtra("EMV_EXPIRY"); - // ✅ ENHANCED: Better debugging for Midtrans response Log.d(TAG, "=== EXTRACTING INTENT DATA ==="); - Log.d(TAG, "Card Type: " + cardType); + Log.d(TAG, "Card Type: " + cardTypeFromIntent); Log.d(TAG, "EMV Mode: " + emvMode); - Log.d(TAG, "Midtrans Response Raw: " + midtransResponse); + Log.d(TAG, "Transaction Amount: " + transactionAmount); + Log.d(TAG, "Reference ID: " + referenceId); Log.d(TAG, "Midtrans Response Length: " + (midtransResponse != null ? midtransResponse.length() : 0)); // Parse Midtrans response if available if (midtransResponse != null && !midtransResponse.isEmpty()) { try { responseJsonData = new JSONObject(midtransResponse); - - // ✅ ENHANCED: Debug all fields in response Log.d(TAG, "✅ Midtrans Response parsed successfully!"); - Log.d(TAG, "Response keys: " + responseJsonData.keys().toString()); // Check for bank field specifically if (responseJsonData.has("bank")) { String bankValue = responseJsonData.getString("bank"); - Log.d(TAG, "✅ Bank field found: '" + bankValue + "' (length: " + bankValue.length() + ")"); + Log.d(TAG, "✅ Bank field found: '" + bankValue + "'"); } else { Log.w(TAG, "⚠️ No 'bank' field in Midtrans response"); - - // Check for alternative bank-related fields - String[] possibleBankFields = {"issuer", "card_type", "payment_method", "acquiring_bank"}; - for (String field : possibleBankFields) { - if (responseJsonData.has(field)) { - Log.d(TAG, "Alternative field '" + field + "': " + responseJsonData.optString(field)); - } - } } } catch (JSONException e) { Log.e(TAG, "❌ Error parsing Midtrans response: " + e.getMessage()); - Log.e(TAG, "Raw response causing error: " + midtransResponse); responseJsonData = null; } } else { @@ -181,17 +175,6 @@ public class ResultTransactionActivity extends AppCompatActivity { Log.d(TAG, "==============================="); } - private void logTransactionDetails() { - Log.d(TAG, "=== RECEIPT DETAILS ==="); - Log.d(TAG, "Reference ID: " + referenceId); - Log.d(TAG, "Card Number: " + (cardNo != null ? maskCardNumber(cardNo) : "N/A")); - Log.d(TAG, "Subtotal: " + subtotalAmount); - Log.d(TAG, "Tax: " + taxAmount); - Log.d(TAG, "Service Fee: " + serviceFeeAmount); - Log.d(TAG, "Final Total: " + (subtotalAmount + taxAmount + serviceFeeAmount)); - Log.d(TAG, "======================"); - } - private void setupListeners() { // Back navigation backNavigation.setOnClickListener(v -> { @@ -199,25 +182,35 @@ public class ResultTransactionActivity extends AppCompatActivity { navigateBack(); }); + backArrow.setOnClickListener(v -> { + if (isNavigating) return; + navigateBack(); + }); + + toolbarTitle.setOnClickListener(v -> { + if (isNavigating) return; + navigateBack(); + }); + // Print button - btnPrint.setOnClickListener(v -> { + printButton.setOnClickListener(v -> { showToast("Mencetak struk..."); - // TODO: Implement print functionality printReceipt(); }); // Email button - btnEmail.setOnClickListener(v -> { + emailButton.setOnClickListener(v -> { showToast("Mengirim email..."); - // TODO: Implement email functionality emailReceipt(); }); - // Finish button - btnFinish.setOnClickListener(v -> { + // ✅ Finish button - Navigate to new transaction + finishButton.setOnClickListener(v -> { if (isNavigating) return; navigateToNewTransaction(); }); + + Log.d(TAG, "✅ All click listeners setup"); } private void calculateAmounts() { @@ -229,7 +222,7 @@ public class ResultTransactionActivity extends AppCompatActivity { } // Calculate tax (11%) - taxAmount = Math.round(subtotalAmount * taxPercentage); + taxAmount = Math.round(subtotalAmount * taxPercentageValue); // Service fee is fixed serviceFeeAmount = 500; @@ -247,48 +240,50 @@ public class ResultTransactionActivity extends AppCompatActivity { } private void displayReceiptData() { - // ✅ ENHANCED: Add debugging at start Log.d(TAG, "=== DISPLAYING RECEIPT DATA ==="); debugAllDataSources(); - // Merchant Information - tvMerchantName.setText("TOKO KLONTONG PAK EKO"); - tvMerchantLocation.setText("Ciputat Baru, Tangsel"); + // ✅ 1. Set merchant data + merchantName.setText("TOKO KLONTONG PAK EKO"); + merchantLocation.setText("Ciputat Baru, Tangsel"); - // Generate or extract transaction details + // ✅ 2. Set MID and TID String tid = extractTidFromResponse(); - String transactionNumber = extractTransactionNumberFromResponse(); - String transactionDate = formatTransactionDate(); + midText.setText("MID: " + tid); + tidText.setText("TID: " + tid); - tvTid.setText("TID: " + tid); - tvTransactionNumber.setText(transactionNumber); - tvTransactionDate.setText(transactionDate); + // ✅ 3. Set transaction number + String displayTransactionNumber = extractTransactionNumberFromResponse(); + transactionNumber.setText(displayTransactionNumber); - // Payment method details - String paymentMethod = getPaymentMethodDisplay(); - String cardTypeDisplay = getCardTypeDisplay(); - - // ✅ ENHANCED: Add debugging for final values - Log.d(TAG, "Payment Method: " + paymentMethod); - Log.d(TAG, "Final Card Type Display: " + cardTypeDisplay); - Log.d(TAG, "================================"); + // ✅ 4. Set transaction date + String displayDate = formatTransactionDate(); + transactionDate.setText(displayDate); - tvPaymentMethodDetail.setText(paymentMethod); - tvCardType.setText(cardTypeDisplay); + // ✅ 5. Set payment method + String displayPaymentMethod = getPaymentMethodDisplay(); + paymentMethod.setText(displayPaymentMethod); - // Amount details + // ✅ 6. ENHANCED: Set card type with comprehensive detection + String displayCardType = getCardTypeDisplay(); + cardType.setText(displayCardType); + + // ✅ 7. Set amount details NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID")); - tvSubtotal.setText(formatter.format(subtotalAmount)); - tvTax.setText(Math.round(taxPercentage * 100) + "%"); - tvServiceFee.setText(formatter.format(serviceFeeAmount)); + transactionTotal.setText(formatter.format(subtotalAmount)); + taxPercentage.setText(Math.round(taxPercentageValue * 100) + "%"); + serviceFee.setText(formatter.format(serviceFeeAmount)); // Final total - long finalTotal = subtotalAmount + taxAmount + serviceFeeAmount; - tvFinalTotal.setText(formatter.format(finalTotal)); + long finalTotalAmount = subtotalAmount + taxAmount + serviceFeeAmount; + finalTotal.setText(formatter.format(finalTotalAmount)); - // Update hidden compatibility components for backward compatibility - updateCompatibilityComponents(); + Log.d(TAG, "✅ Receipt data displayed successfully"); + Log.d(TAG, " Payment Method: " + displayPaymentMethod); + Log.d(TAG, " Card Type: " + displayCardType); + Log.d(TAG, " Final Total: " + formatter.format(finalTotalAmount)); + Log.d(TAG, "================================"); } private String extractTidFromResponse() { @@ -331,7 +326,6 @@ public class ResultTransactionActivity extends AppCompatActivity { try { if (responseJsonData.has("transaction_time")) { String transactionTime = responseJsonData.getString("transaction_time"); - // Parse and reformat if needed return formatDateForDisplay(transactionTime); } } catch (JSONException e) { @@ -345,7 +339,6 @@ public class ResultTransactionActivity extends AppCompatActivity { private String formatDateForDisplay(String dateString) { try { - // Try to parse the input date string and reformat SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID")); Date date = inputFormat.parse(dateString); @@ -362,32 +355,31 @@ public class ResultTransactionActivity extends AppCompatActivity { } private String getPaymentMethodDisplay() { - if (cardType == null) return "Kartu Kredit"; + if (cardTypeFromIntent == null) return "Kartu Kredit"; - switch (cardType.toUpperCase()) { + switch (cardTypeFromIntent.toUpperCase()) { case "EMV_MIDTRANS": case "IC": case "NFC": - return "Kartu Kredit"; + return emvMode ? "Kartu Kredit (EMV)" : "Kartu Kredit"; case "DEBIT": - return "Kartu Debit"; + return emvMode ? "Kartu Debit (EMV)" : "Kartu Debit"; case "MAGNETIC": - return "Kartu Magnetic"; + return "Kartu Kredit"; default: return "Kartu Kredit"; } } - // ✅ ENHANCED: Comprehensive bank detection with multiple fallbacks + // ✅ ENHANCED: Comprehensive bank detection for EMV transactions private String getCardTypeDisplay() { - Log.d(TAG, "=== DETERMINING CARD TYPE DISPLAY ==="); + Log.d(TAG, "=== DETERMINING CARD TYPE DISPLAY (EMV) ==="); // Priority 1: Get bank from Midtrans response (most accurate) if (responseJsonData != null) { Log.d(TAG, "✅ Midtrans response available, checking for bank..."); try { - // ✅ ENHANCED: Check multiple possible bank fields String bankFromResponse = null; if (responseJsonData.has("bank")) { @@ -405,15 +397,11 @@ public class ResultTransactionActivity extends AppCompatActivity { String formattedBank = formatBankName(bankFromResponse); Log.d(TAG, "✅ Bank from Midtrans response: '" + bankFromResponse + "' -> '" + formattedBank + "'"); return formattedBank; - } else { - Log.w(TAG, "⚠️ Bank field exists but is empty/null"); } } catch (JSONException e) { Log.e(TAG, "❌ Error extracting bank from response: " + e.getMessage()); } - } else { - Log.w(TAG, "⚠️ No Midtrans response data available"); } // Priority 2: EMV AID detection @@ -424,9 +412,6 @@ public class ResultTransactionActivity extends AppCompatActivity { Log.d(TAG, "✅ Bank from EMV AID: " + bankFromAid); return bankFromAid; } - Log.d(TAG, "EMV AID resulted in default bank"); - } else { - Log.d(TAG, "No EMV AID available"); } // Priority 3: Card BIN detection @@ -436,226 +421,118 @@ public class ResultTransactionActivity extends AppCompatActivity { String bankFromBin = getBankFromComprehensiveBin(cardBin); Log.d(TAG, "✅ Bank from BIN (" + cardBin + "): " + bankFromBin); return bankFromBin; - } else { - Log.d(TAG, "Card number too short for BIN detection: " + - (cardNo != null ? cardNo.length() : 0) + " digits"); } Log.d(TAG, "⚠️ Using default bank: BCA"); Log.d(TAG, "===================================="); return "BCA"; // Default fallback } - - // ✅ ENHANCED: Better bank name formatting with more variations + + // ✅ ENHANCED: Better bank name formatting private String formatBankName(String bankName) { if (bankName == null || bankName.trim().isEmpty()) { - Log.d(TAG, "Bank name is null/empty, using default BCA"); return "BCA"; // Default } - // Clean and normalize input String formatted = bankName.trim().toUpperCase(); - Log.d(TAG, "Formatting bank name: '" + bankName + "' -> '" + formatted + "'"); // Handle common bank name variations switch (formatted) { - // BCA variations case "BCA": case "BANK BCA": case "BANK CENTRAL ASIA": - case "PT BANK CENTRAL ASIA TBK": return "BCA"; - // MANDIRI variations case "MANDIRI": case "BANK MANDIRI": - case "PT BANK MANDIRI (PERSERO) TBK": - case "MANDIRI BANK": - return "MANDIRI"; + return "Mandiri"; - // BNI variations case "BNI": case "BANK BNI": case "BANK NEGARA INDONESIA": - case "PT BANK NEGARA INDONESIA (PERSERO) TBK": return "BNI"; - // BRI variations case "BRI": case "BANK BRI": case "BANK RAKYAT INDONESIA": - case "PT BANK RAKYAT INDONESIA (PERSERO) TBK": return "BRI"; - // CIMB variations case "CIMB": case "CIMB NIAGA": case "BANK CIMB NIAGA": - case "PT BANK CIMB NIAGA TBK": - return "CIMB NIAGA"; + return "CIMB Niaga"; - // DANAMON variations case "DANAMON": case "BANK DANAMON": - case "PT BANK DANAMON INDONESIA TBK": - return "DANAMON"; + return "Danamon"; - // PERMATA variations case "PERMATA": case "BANK PERMATA": - case "PT BANK PERMATA TBK": - return "PERMATA"; - - // MEGA variations - case "MEGA": - case "BANK MEGA": - case "PT BANK MEGA TBK": - return "MEGA"; - - // BTN variations - case "BTN": - case "BANK BTN": - case "BANK TABUNGAN NEGARA": - return "BTN"; - - // MAYBANK variations - case "MAYBANK": - case "MAYBANK INDONESIA": - case "PT BANK MAYBANK INDONESIA TBK": - return "MAYBANK"; + return "Permata"; default: - // For unknown banks, return as-is but log it - Log.d(TAG, "Unknown bank name, returning as-is: " + formatted); - return formatted; + return capitalizeFirstLetter(bankName); } } - private String getBankFromBin(String bin) { - // Indonesian Bank BIN mapping - return bank names - // BCA - if (bin.startsWith("4621") || bin.startsWith("4699") || - bin.startsWith("5221") || bin.startsWith("6277")) return "BCA"; - - // MANDIRI - if (bin.startsWith("4313") || bin.startsWith("5573") || - bin.startsWith("6011") || bin.startsWith("6234")) return "MANDIRI"; - - // BNI - if (bin.startsWith("4603") || bin.startsWith("1946") || - bin.startsWith("5264")) return "BNI"; - - // BRI - if (bin.startsWith("4578") || bin.startsWith("4479") || - bin.startsWith("5208")) return "BRI"; - - // CIMB NIAGA - if (bin.startsWith("4599") || bin.startsWith("5249")) return "CIMB NIAGA"; - - // DANAMON - if (bin.startsWith("4055") || bin.startsWith("5108")) return "DANAMON"; - - // Generic fallback based on first digit - if (bin.startsWith("4") || bin.startsWith("5")) { - // Check more specific patterns if needed - return "BCA"; // Default for unknown Visa/Mastercard - } - - return "BCA"; // Ultimate fallback - } - private String getBankFromAid(String aid) { // AID to Indonesian bank mapping - // Note: AID identifies card scheme, but we want bank name - // This is best-effort mapping, real bank detection needs BIN - if (aid.contains("A0000000031010")) { // VISA - check if we have card number for better detection if (cardNo != null && cardNo.length() >= 6) { - return getBankFromBin(cardNo.substring(0, 6)); + return getBankFromComprehensiveBin(cardNo.substring(0, 6)); } return "BCA"; // Default for VISA } if (aid.contains("A0000000041010")) { - // MASTERCARD - check if we have card number for better detection + // MASTERCARD if (cardNo != null && cardNo.length() >= 6) { - return getBankFromBin(cardNo.substring(0, 6)); + return getBankFromComprehensiveBin(cardNo.substring(0, 6)); } - return "MANDIRI"; // Default for Mastercard + return "Mandiri"; // Default for Mastercard } return "BCA"; // Ultimate fallback } - - // ✅ ENHANCED: More comprehensive Indonesian bank BIN mapping + + // ✅ ENHANCED: Comprehensive Indonesian bank BIN mapping private String getBankFromComprehensiveBin(String bin) { if (bin == null || bin.length() < 4) { return "BCA"; // Default } - // More comprehensive Indonesian bank BIN mapping String bin4 = bin.substring(0, 4); String bin6 = bin.length() >= 6 ? bin.substring(0, 6) : bin4; // BCA patterns - if (bin4.equals("4621") || bin4.equals("4699") || bin4.equals("5221") || bin4.equals("6277") || - bin6.startsWith("462117") || bin6.startsWith("469929") || bin6.startsWith("522156")) { + if (bin4.equals("4621") || bin4.equals("4699") || bin4.equals("5221") || bin4.equals("6277")) { return "BCA"; } // MANDIRI patterns - if (bin4.equals("4313") || bin4.equals("5573") || bin4.equals("6011") || bin4.equals("6234") || - bin4.equals("5406") || bin4.equals("4097") || bin6.startsWith("431360") || bin6.startsWith("557347")) { - return "MANDIRI"; + if (bin4.equals("4313") || bin4.equals("5573") || bin4.equals("6011") || bin4.equals("6234")) { + return "Mandiri"; } // BNI patterns - if (bin4.equals("4603") || bin4.equals("1946") || bin4.equals("5264") || - bin6.startsWith("460347") || bin6.startsWith("194637") || bin6.startsWith("526435")) { + if (bin4.equals("4603") || bin4.equals("1946") || bin4.equals("5264")) { return "BNI"; } // BRI patterns - if (bin4.equals("4578") || bin4.equals("4479") || bin4.equals("5208") || bin4.equals("4486") || - bin6.startsWith("457865") || bin6.startsWith("447935") || bin6.startsWith("520834")) { + if (bin4.equals("4578") || bin4.equals("4479") || bin4.equals("5208")) { return "BRI"; } // CIMB NIAGA patterns - if (bin4.equals("4599") || bin4.equals("5249") || bin4.equals("4543") || - bin6.startsWith("459923") || bin6.startsWith("524901") || bin6.startsWith("454347")) { - return "CIMB NIAGA"; + if (bin4.equals("4599") || bin4.equals("5249")) { + return "CIMB Niaga"; } // DANAMON patterns - if (bin4.equals("4055") || bin4.equals("5108") || bin4.equals("4631") || - bin6.startsWith("405512") || bin6.startsWith("510834") || bin6.startsWith("463178")) { - return "DANAMON"; - } - - // PERMATA patterns - if (bin4.equals("5399") || bin4.equals("4723") || - bin6.startsWith("539923") || bin6.startsWith("472345")) { - return "PERMATA"; - } - - // MEGA patterns - if (bin4.equals("5221") || bin4.equals("4128") || - bin6.startsWith("522145") || bin6.startsWith("412834")) { - return "MEGA"; - } - - // BTN patterns - if (bin4.equals("4058") || bin4.equals("5258") || - bin6.startsWith("405834") || bin6.startsWith("525823")) { - return "BTN"; - } - - // MAYBANK patterns - if (bin4.equals("5108") || bin4.equals("4055") || - bin6.startsWith("510856") || bin6.startsWith("405523")) { - return "MAYBANK"; + if (bin4.equals("4055") || bin4.equals("5108")) { + return "Danamon"; } // Default fallback @@ -663,47 +540,20 @@ public class ResultTransactionActivity extends AppCompatActivity { return "BCA"; } - // Update hidden components for backward compatibility - private void updateCompatibilityComponents() { - if (tvAmount != null) { - NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID")); - tvAmount.setText("Rp " + formatter.format(subtotalAmount + taxAmount + serviceFeeAmount)); - } - - if (tvStatus != null) { - tvStatus.setText(paymentSuccess ? "SUCCESS" : "FAILED"); - } - - if (tvReference != null) { - tvReference.setText(referenceId != null ? referenceId : "N/A"); - } - - if (tvCardInfo != null) { - tvCardInfo.setText(cardNo != null ? maskCardNumber(cardNo) : "N/A"); - } - - if (tvPaymentMethod != null) { - tvPaymentMethod.setText(getPaymentMethodDisplay()); - } - - if (tvTransactionId != null) { - tvTransactionId.setText(extractTransactionNumberFromResponse()); - } - - if (tvTimestamp != null) { - tvTimestamp.setText(formatTransactionDate()); + private String capitalizeFirstLetter(String input) { + if (input == null || input.isEmpty()) { + return input; } + return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase(); } - // ✅ ENHANCED: Add method to debug all data sources + // ✅ Debug methods private void debugAllDataSources() { Log.d(TAG, "=== DEBUGGING ALL DATA SOURCES ==="); - // Check Midtrans response if (responseJsonData != null) { Log.d(TAG, "Midtrans Response Available:"); try { - // Log all keys and their values java.util.Iterator keys = responseJsonData.keys(); while (keys.hasNext()) { String key = keys.next(); @@ -717,63 +567,35 @@ public class ResultTransactionActivity extends AppCompatActivity { Log.d(TAG, "❌ No Midtrans Response Data"); } - // Check EMV data Log.d(TAG, "EMV Data:"); Log.d(TAG, " Card Number: " + (cardNo != null ? maskCardNumber(cardNo) : "null")); Log.d(TAG, " EMV AID: " + emvAid); Log.d(TAG, " EMV Cardholder: " + emvCardholderName); - Log.d(TAG, " EMV Expiry: " + emvExpiry); - - // Check card type - Log.d(TAG, "Card Type: " + cardType); - Log.d(TAG, "EMV Mode: " + emvMode); + Log.d(TAG, " EMV Mode: " + emvMode); Log.d(TAG, "=================================="); } - - // ✅ ENHANCED: Method to manually set bank for testing - public void debugSetBank(String bankName) { - try { - if (responseJsonData == null) { - responseJsonData = new JSONObject(); - } - responseJsonData.put("bank", bankName); - Log.d(TAG, "Debug: Manually set bank to: " + bankName); - - // Refresh display - displayReceiptData(); - } catch (JSONException e) { - Log.e(TAG, "Error setting debug bank: " + e.getMessage()); - } - } - - // ✅ ENHANCED: Method to test different bank displays - private void testBankDisplay() { - // Test different bank values - String[] testBanks = {"BCA", "MANDIRI", "BNI", "BRI", "CIMB NIAGA"}; - - for (String bank : testBanks) { - debugSetBank(bank); - Log.d(TAG, "Test bank '" + bank + "' -> Display: " + getCardTypeDisplay()); - } - } - - // ✅ ENHANCED: Get last response for debugging - public JSONObject getLastMidtransResponse() { - return responseJsonData; + + private void logTransactionDetails() { + Log.d(TAG, "=== RECEIPT DETAILS ==="); + Log.d(TAG, "Reference ID: " + referenceId); + Log.d(TAG, "Card Number: " + (cardNo != null ? maskCardNumber(cardNo) : "N/A")); + Log.d(TAG, "Subtotal: " + subtotalAmount); + Log.d(TAG, "Tax: " + taxAmount); + Log.d(TAG, "Service Fee: " + serviceFeeAmount); + Log.d(TAG, "Final Total: " + (subtotalAmount + taxAmount + serviceFeeAmount)); + Log.d(TAG, "======================"); } // Action Methods private void printReceipt() { Log.d(TAG, "Print receipt requested"); - // TODO: Implement actual printing logic showToast("Fitur cetak akan segera tersedia"); } private void emailReceipt() { Log.d(TAG, "Email receipt requested"); - // Create email intent Intent emailIntent = new Intent(Intent.ACTION_SEND); emailIntent.setType("text/plain"); emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Struk Pembayaran - " + extractTransactionNumberFromResponse()); @@ -791,9 +613,8 @@ public class ResultTransactionActivity extends AppCompatActivity { StringBuilder content = new StringBuilder(); NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID")); - content.append("STRUK PEMBAYARAN\n"); - content.append("================\n\n"); - content.append("Payvora PRO\n"); + content.append("STRUK PEMBAYARAN EMV/CARD\n"); + content.append("==========================\n\n"); content.append("TOKO KLONTONG PAK EKO\n"); content.append("Ciputat Baru, Tangsel\n\n"); content.append("TID: ").append(extractTidFromResponse()).append("\n"); @@ -801,6 +622,13 @@ public class ResultTransactionActivity extends AppCompatActivity { content.append("Tanggal: ").append(formatTransactionDate()).append("\n"); content.append("Metode: ").append(getPaymentMethodDisplay()).append("\n"); content.append("Jenis Kartu: ").append(getCardTypeDisplay()).append("\n\n"); + + if (emvMode && emvCardholderName != null) { + content.append("DETAIL EMV:\n"); + content.append("Cardholder: ").append(emvCardholderName).append("\n"); + content.append("AID: ").append(emvAid).append("\n\n"); + } + content.append("RINCIAN PEMBAYARAN:\n"); content.append("Total Transaksi: Rp ").append(formatter.format(subtotalAmount)).append("\n"); content.append("Pajak (11%): Rp ").append(formatter.format(taxAmount)).append("\n"); @@ -824,7 +652,6 @@ public class ResultTransactionActivity extends AppCompatActivity { private void navigateToNewTransaction() { if (isNavigating) return; - // Show confirmation dialog new AlertDialog.Builder(this) .setTitle("Transaksi Baru") .setMessage("Apakah Anda ingin melakukan transaksi baru?") @@ -839,7 +666,6 @@ public class ResultTransactionActivity extends AppCompatActivity { Log.d(TAG, "=== NAVIGATING TO NEW TRANSACTION ==="); isNavigating = true; - // Add small delay for better UX new Handler(Looper.getMainLooper()).postDelayed(() -> { try { Intent intent = new Intent(this, CreateTransactionActivity.class); @@ -874,6 +700,7 @@ public class ResultTransactionActivity extends AppCompatActivity { return; } navigateBack(); + super.onBackPressed(); } @Override diff --git a/app/src/main/res/layout/activity_receipt.xml b/app/src/main/res/layout/activity_receipt.xml index 36e9050..104e1a5 100644 --- a/app/src/main/res/layout/activity_receipt.xml +++ b/app/src/main/res/layout/activity_receipt.xml @@ -104,7 +104,7 @@ - - @@ -168,46 +159,36 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" + android:gravity="center" android:layout_marginBottom="8dp"> - - + + android:fontFamily="@font/inter" + android:paddingHorizontal="8dp"/> -