diff --git a/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java b/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java index 23841f9..096aaae 100644 --- a/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java +++ b/app/src/main/java/com/example/bdkipoc/ReceiptActivity.java @@ -140,84 +140,85 @@ public class ReceiptActivity extends AppCompatActivity { String tid = intent.getStringExtra("tid"); // Log received data for debugging - Log.d("ReceiptActivity", "amount: " + amount); - Log.d("ReceiptActivity", "grossAmount: " + grossAmount); - Log.d("ReceiptActivity", "merchantName: " + merchantNameStr); - Log.d("ReceiptActivity", "referenceId: " + referenceId); - Log.d("ReceiptActivity", "createdAt: " + createdAt); - Log.d("ReceiptActivity", "channelCode: " + channelCode); - Log.d("ReceiptActivity", "acquirer: " + acquirer); - Log.d("ReceiptActivity", "mid: " + mid); - Log.d("ReceiptActivity", "tid: " + tid); + 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); // 1. Set merchant data with defaults merchantName.setText(merchantNameStr != null ? merchantNameStr : "Marcel Panjaitan"); merchantLocation.setText(merchantLocationStr != null ? merchantLocationStr : "Jakarta, Indonesia"); - // 2. Set MID and TID - prioritize from intent, then defaults + // 2. Set MID and TID midText.setText(mid != null ? mid : "71000026521"); tidText.setText(tid != null ? tid : "73001500"); - // 3. Set transaction number - prioritize reference_id + // 3. Set transaction number String displayTransactionNumber = null; if (referenceId != null && !referenceId.isEmpty()) { displayTransactionNumber = referenceId; - Log.d("ReceiptActivity", "Using reference_id as transaction number: " + referenceId); } else if (transactionId != null && !transactionId.isEmpty()) { displayTransactionNumber = transactionId; - Log.d("ReceiptActivity", "Using transaction_id as transaction number: " + transactionId); } else if (orderId != null && !orderId.isEmpty()) { displayTransactionNumber = orderId; - Log.d("ReceiptActivity", "Using order_id as transaction number: " + orderId); } transactionNumber.setText(displayTransactionNumber != null ? displayTransactionNumber : "N/A"); - // 4. Set transaction date - prioritize createdAt, format as dd/MM/yyyy HH:mm + // 4. Set transaction date String displayDate = null; if (createdAt != null && !createdAt.isEmpty()) { displayDate = formatDateFromCreatedAt(createdAt); - Log.d("ReceiptActivity", "Formatted createdAt: " + createdAt + " -> " + displayDate); } else if (transactionDateStr != null && !transactionDateStr.isEmpty()) { displayDate = transactionDateStr; - Log.d("ReceiptActivity", "Using provided transaction_date: " + transactionDateStr); } else { displayDate = getCurrentDateTime(); - Log.d("ReceiptActivity", "Using current datetime: " + displayDate); } transactionDate.setText(displayDate); - // 5. Set payment method - use channel_code mapping + // 5. Set payment method String displayPaymentMethod = getPaymentMethodFromChannelCode(channelCode, paymentMethodStr); paymentMethod.setText(displayPaymentMethod); - Log.d("ReceiptActivity", "Payment method: " + displayPaymentMethod + " (from channel: " + channelCode + ")"); - // 6. Set card type - use acquirer with comprehensive mapping - // For QRIS, we need to get the actual acquirer (GoPay, OVO, DANA, etc.) - String displayCardType = getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr); + // 6. ✅ IMPROVED: Enhanced card type detection for QRIS + String displayCardType = null; - // Special handling for QRIS - if we only have "QRIS" or "qris", try to get real acquirer - if (displayCardType.equalsIgnoreCase("QRIS") && channelCode != null && channelCode.equalsIgnoreCase("QRIS")) { - // For QRIS transactions, we should try to determine the actual acquirer - // This might require additional API call or webhook data parsing - Log.w("ReceiptActivity", "QRIS transaction detected but no specific acquirer found. Using default."); - // In production, this should fetch the real acquirer from transaction status API - displayCardType = acquirer != null && !acquirer.equalsIgnoreCase("qris") ? - getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr) : "GoPay"; // Default QRIS acquirer + 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); } cardType.setText(displayCardType); - Log.d("ReceiptActivity", "Card type: " + displayCardType + " (from acquirer: " + acquirer + ")"); + Log.d("ReceiptActivity", "💳 FINAL CARD TYPE: " + displayCardType); - // 7. Format and set amounts - prioritize amount over grossAmount + // 7. Format and set amounts setAmountData(amount, grossAmount); Log.d("ReceiptActivity", "=== TRANSACTION DATA LOADED ==="); } } - - /** - * Format date from createdAt field (yyyy-MM-dd HH:mm:ss) to Indonesian format (dd/MM/yyyy HH:mm) - */ + private String formatDateFromCreatedAt(String createdAt) { try { // Input format from database: "yyyy-MM-dd HH:mm:ss" @@ -291,83 +292,135 @@ public class ReceiptActivity extends AppCompatActivity { 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")) { - String acq = acquirer.toLowerCase(); + String acq = acquirer.toLowerCase().trim(); - // Comprehensive acquirer mapping based on Midtrans + Log.d("ReceiptActivity", "🔍 Mapping acquirer: '" + acquirer + "' -> '" + acq + "'"); + + // ✅ COMPREHENSIVE acquirer mapping (case-insensitive) switch (acq) { - // E-Wallet acquirers - case "gopay": return "GoPay"; - case "shopeepay": return "ShopeePay"; + // 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": return "LinkAja"; - case "jenius": return "Jenius"; - case "kaspro": return "KasPro"; + + 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": return "BCA"; + 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 "bsi_va": return "BSI"; - case "maybank": return "Maybank"; + 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": return "Mastercard"; + case "mastercard": + case "master_card": return "Mastercard"; case "jcb": return "JCB"; case "amex": case "american_express": return "American Express"; + case "discover": return "Discover"; + case "unionpay": + case "union_pay": return "UnionPay"; // Over-the-counter - case "alfamart": return "Alfamart"; - case "indomaret": return "Indomaret"; + case "alfamart": + case "alfa_mart": return "Alfamart"; + case "indomaret": + case "indo_maret": return "Indomaret"; + case "pos_indonesia": return "Pos Indonesia"; // Buy now pay later case "akulaku": return "Akulaku"; case "kredivo": return "Kredivo"; - case "indodana": return "Indodana"; + case "indodana": + case "indo_dana": return "Indodana"; + case "traveloka_paylater": + case "traveloka": return "Traveloka PayLater"; + case "atome": return "Atome"; default: // Return capitalized version of acquirer if not in mapping - return capitalizeFirstLetter(acquirer); + String capitalized = capitalizeFirstLetter(acquirer); + Log.d("ReceiptActivity", "🔤 Using capitalized acquirer: " + capitalized); + return capitalized; } } - // STEP 2: For QRIS transactions, try to fetch real acquirer from webhook data - if (channelCode != null && channelCode.equalsIgnoreCase("QRIS")) { - String referenceId = getIntent().getStringExtra("reference_id"); - if (referenceId != null && !referenceId.isEmpty()) { - Log.d("ReceiptActivity", "🔍 QRIS detected, fetching real acquirer for: " + referenceId); - - // Try to get real acquirer from webhook data synchronously for initial load - String realAcquirer = fetchRealAcquirerSync(referenceId); - if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) { - Log.d("ReceiptActivity", "✅ Found real acquirer synchronously: " + realAcquirer); - return getCardTypeFromAcquirer(realAcquirer, null, null); - } - - // If sync fetch failed, try async and show generic QRIS for now - fetchRealAcquirerFromWebhook(referenceId); - return "QRIS"; // ✅ FIXED: Show QRIS instead of defaulting to GoPay - } - } - - // STEP 3: Fallback based on channel code + // STEP 2: Fallback based on channel code if (channelCode != null && !channelCode.isEmpty()) { String code = channelCode.toUpperCase(); + Log.d("ReceiptActivity", "🔍 Using channel code fallback: " + code); + switch (code) { - case "QRIS": return "QRIS"; // ✅ FIXED: Generic QRIS instead of GoPay + case "QRIS": return "QRIS"; // Generic QRIS case "DEBIT": return "Debit"; case "CREDIT": return "Credit"; case "BCA": return "BCA"; @@ -378,23 +431,26 @@ public class ReceiptActivity extends AppCompatActivity { } } - // STEP 4: Final fallback - return fallbackCardType != null ? fallbackCardType : "Unknown"; // ✅ FIXED: Unknown instead of GoPay + // STEP 3: Final fallback + String result = fallbackCardType != null ? fallbackCardType : "Unknown"; + Log.d("ReceiptActivity", "🔍 Using final fallback: " + result); + return result; } private String fetchRealAcquirerSync(String referenceId) { try { Log.d("ReceiptActivity", "🔍 Sync search for acquirer: " + referenceId); - String queryUrl = "https://be-edc.msvc.app/api-logs?limit=50&sortOrder=DESC&sortColumn=created_at"; + // ✅ IMPROVED: Search with broader scope for better matches + String queryUrl = "https://be-edc.msvc.app/api-logs?limit=100&sortOrder=DESC&sortColumn=created_at"; URL url = new URL(queryUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); conn.setRequestProperty("User-Agent", "BDKIPOCApp/1.0"); - conn.setConnectTimeout(5000); // Short timeout for sync call - conn.setReadTimeout(5000); + conn.setConnectTimeout(8000); // Slightly longer timeout for better results + conn.setReadTimeout(8000); if (conn.getResponseCode() == 200) { BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); @@ -408,54 +464,27 @@ public class ReceiptActivity extends AppCompatActivity { org.json.JSONArray results = json.optJSONArray("results"); if (results != null && results.length() > 0) { - // Search for the most recent settlement/success transaction - for (int i = 0; i < results.length(); i++) { - org.json.JSONObject log = results.getJSONObject(i); - org.json.JSONObject reqBody = log.optJSONObject("request_body"); + Log.d("ReceiptActivity", "📊 Sync analyzing " + results.length() + " webhook logs"); + + // ✅ PRIORITY SEARCH: Look for settlement first, then pending, then any + String[] searchPriority = {"settlement", "capture", "success", "pending", ""}; + + for (String status : searchPriority) { + String[] targetStatuses = status.isEmpty() ? new String[]{} : new String[]{status}; + String foundAcquirer = searchLogsByStatus(results, referenceId, targetStatuses); - if (reqBody != null) { - String logReferenceId = reqBody.optString("reference_id", ""); - String logTransactionStatus = reqBody.optString("transaction_status", ""); - String logAcquirer = reqBody.optString("acquirer", ""); - String logIssuer = reqBody.optString("issuer", ""); - - // Check for direct reference match - boolean isDirectMatch = referenceId.equals(logReferenceId); - - // Check custom_field1 for refresh tracking - boolean isRefreshMatch = false; - String customField1 = reqBody.optString("custom_field1", ""); - if (!customField1.isEmpty()) { - try { - org.json.JSONObject customData = new org.json.JSONObject(customField1); - String originalReference = customData.optString("original_reference", ""); - String appReferenceId = customData.optString("app_reference_id", ""); - if (referenceId.equals(originalReference) || referenceId.equals(appReferenceId)) { - isRefreshMatch = true; - } - } catch (org.json.JSONException e) { - // Ignore parsing errors - } - } - - // Check if this log matches our reference - if (isDirectMatch || isRefreshMatch) { - // Prioritize settlement/success status - if (logTransactionStatus.equals("settlement") || - logTransactionStatus.equals("capture") || - logTransactionStatus.equals("success")) { - - // Extract acquirer (prefer acquirer field over issuer) - String foundAcquirer = !logAcquirer.isEmpty() ? logAcquirer : logIssuer; - if (!foundAcquirer.isEmpty() && !foundAcquirer.equalsIgnoreCase("qris")) { - Log.d("ReceiptActivity", "📋 Sync found acquirer: " + foundAcquirer + " (status: " + logTransactionStatus + ")"); - return foundAcquirer; - } - } - } + if (foundAcquirer != null && !foundAcquirer.isEmpty() && !foundAcquirer.equalsIgnoreCase("qris")) { + Log.d("ReceiptActivity", "🎯 Sync found acquirer: " + foundAcquirer + + " (priority: " + (status.isEmpty() ? "any" : status) + ")"); + return foundAcquirer; } } + + Log.w("ReceiptActivity", "⚠️ Sync search completed but no valid acquirer found"); } + + } else { + Log.w("ReceiptActivity", "⚠️ Sync API call failed with code: " + conn.getResponseCode()); } } catch (Exception e) { @@ -470,15 +499,15 @@ public class ReceiptActivity extends AppCompatActivity { try { Log.d("ReceiptActivity", "🔍 Async search for real acquirer: " + referenceId); - String queryUrl = "https://be-edc.msvc.app/api-logs?limit=100&sortOrder=DESC&sortColumn=created_at"; + String queryUrl = "https://be-edc.msvc.app/api-logs?limit=200&sortOrder=DESC&sortColumn=created_at"; URL url = new URL(queryUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "application/json"); conn.setRequestProperty("User-Agent", "BDKIPOCApp/1.0"); - conn.setConnectTimeout(10000); - conn.setReadTimeout(10000); + conn.setConnectTimeout(15000); + conn.setReadTimeout(15000); if (conn.getResponseCode() == 200) { BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); @@ -492,63 +521,100 @@ public class ReceiptActivity extends AppCompatActivity { org.json.JSONArray results = json.optJSONArray("results"); if (results != null && results.length() > 0) { + Log.d("ReceiptActivity", "📊 Async analyzing " + results.length() + " webhook logs"); + String realAcquirer = searchForRealAcquirer(results, referenceId); if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) { - Log.d("ReceiptActivity", "✅ Async found real acquirer: " + realAcquirer + " for reference: " + referenceId); + Log.d("ReceiptActivity", "✅ Async found real acquirer: " + realAcquirer); // Update UI on main thread final String displayAcquirer = getCardTypeFromAcquirer(realAcquirer, null, null); runOnUiThread(() -> { if (cardType != null) { cardType.setText(displayAcquirer); - Log.d("ReceiptActivity", "🎨 Updated card type from QRIS to: " + displayAcquirer); + Log.d("ReceiptActivity", "🎨 UI UPDATED: QRIS -> " + displayAcquirer); + + // Show a subtle indication that the data was updated + showToast("Card type updated: " + displayAcquirer); } }); } else { - Log.w("ReceiptActivity", "⚠️ Could not determine real acquirer for: " + referenceId); - // Keep showing "QRIS" instead of defaulting to GoPay + Log.w("ReceiptActivity", "⚠️ Async search found no valid acquirer for: " + referenceId); } + } else { + Log.w("ReceiptActivity", "⚠️ Async search returned no results"); } } else { - Log.w("ReceiptActivity", "⚠️ API call failed with code: " + conn.getResponseCode()); + Log.w("ReceiptActivity", "⚠️ Async API call failed with code: " + conn.getResponseCode()); } } catch (Exception e) { - Log.e("ReceiptActivity", "❌ Error fetching real acquirer: " + e.getMessage(), e); + Log.e("ReceiptActivity", "❌ Async acquirer fetch error: " + e.getMessage(), e); } }).start(); } - - /** - * ✅ NEW METHOD: Advanced search for real acquirer in webhook logs - */ + private String searchForRealAcquirer(org.json.JSONArray results, String referenceId) { try { Log.d("ReceiptActivity", "🔍 Analyzing " + results.length() + " webhook logs for acquirer"); + Log.d("ReceiptActivity", "🎯 Target reference ID: " + referenceId); + + // Get order_id from intent for additional matching + String orderId = getIntent().getStringExtra("order_id"); + Log.d("ReceiptActivity", "🎯 Target order ID: " + orderId); // Strategy 1: Look for settlement/success transactions first (highest priority) + Log.d("ReceiptActivity", "🔍 Strategy 1: Searching for settlement/success status"); String acquirerFromSettlement = searchLogsByStatus(results, referenceId, new String[]{"settlement", "capture", "success"}); if (acquirerFromSettlement != null) { - Log.d("ReceiptActivity", "🎯 Found acquirer from settlement: " + acquirerFromSettlement); + Log.d("ReceiptActivity", "🎯 SUCCESS: Found acquirer from settlement: " + acquirerFromSettlement); return acquirerFromSettlement; } // Strategy 2: Look for pending transactions + Log.d("ReceiptActivity", "🔍 Strategy 2: Searching for pending status"); String acquirerFromPending = searchLogsByStatus(results, referenceId, new String[]{"pending"}); if (acquirerFromPending != null) { - Log.d("ReceiptActivity", "🎯 Found acquirer from pending: " + acquirerFromPending); + Log.d("ReceiptActivity", "🎯 SUCCESS: Found acquirer from pending: " + acquirerFromPending); return acquirerFromPending; } - // Strategy 3: Look for any transaction with this reference + // Strategy 3: Look for any transaction with this reference (broadest search) + Log.d("ReceiptActivity", "🔍 Strategy 3: Searching for any status"); String acquirerFromAny = searchLogsByStatus(results, referenceId, new String[]{}); if (acquirerFromAny != null) { - Log.d("ReceiptActivity", "🎯 Found acquirer from any status: " + acquirerFromAny); + Log.d("ReceiptActivity", "🎯 SUCCESS: Found acquirer from any status: " + acquirerFromAny); return acquirerFromAny; } - Log.w("ReceiptActivity", "❌ No acquirer found in webhook logs for: " + referenceId); + Log.w("ReceiptActivity", "❌ FAILED: No acquirer found in webhook logs for reference: " + referenceId); + + // Strategy 4: Debug logging - show what we actually found + Log.d("ReceiptActivity", "🔍 DEBUG: Showing first 5 log entries for analysis:"); + for (int i = 0; i < Math.min(5, results.length()); i++) { + try { + org.json.JSONObject log = results.getJSONObject(i); + org.json.JSONObject reqBody = log.optJSONObject("request_body"); + if (reqBody != null) { + String logOrderId = reqBody.optString("order_id", ""); + String logRefId = reqBody.optString("reference_id", ""); + String logStatus = reqBody.optString("transaction_status", ""); + String logAcquirer = reqBody.optString("acquirer", ""); + String logIssuer = reqBody.optString("issuer", ""); + + Log.d("ReceiptActivity", " Log " + (i+1) + ": " + + "order_id=" + logOrderId + + ", ref_id=" + logRefId + + ", status=" + logStatus + + ", acquirer=" + logAcquirer + + ", issuer=" + logIssuer); + } + } catch (Exception e) { + Log.w("ReceiptActivity", "Error parsing debug log " + i + ": " + e.getMessage()); + } + } + return null; } catch (Exception e) { @@ -557,9 +623,6 @@ public class ReceiptActivity extends AppCompatActivity { } } - /** - * ✅ HELPER METHOD: Search logs by specific status - */ private String searchLogsByStatus(org.json.JSONArray results, String referenceId, String[] targetStatuses) { try { for (int i = 0; i < results.length(); i++) { @@ -571,10 +634,24 @@ public class ReceiptActivity extends AppCompatActivity { String logTransactionStatus = reqBody.optString("transaction_status", ""); String logAcquirer = reqBody.optString("acquirer", ""); String logIssuer = reqBody.optString("issuer", ""); + String logOrderId = reqBody.optString("order_id", ""); + + Log.d("ReceiptActivity", "🔍 Checking log: order_id=" + logOrderId + + ", status=" + logTransactionStatus + + ", acquirer=" + logAcquirer + + ", issuer=" + logIssuer); // Check for direct reference match boolean isDirectMatch = referenceId.equals(logReferenceId); + // ✅ IMPROVED: Also check order_id match from QrisResultActivity + boolean isOrderMatch = false; + String orderId = getIntent().getStringExtra("order_id"); + if (orderId != null && !orderId.isEmpty() && orderId.equals(logOrderId)) { + isOrderMatch = true; + Log.d("ReceiptActivity", "✅ Found order_id match: " + orderId); + } + // Check custom_field1 for refresh tracking boolean isRefreshMatch = false; String customField1 = reqBody.optString("custom_field1", ""); @@ -592,7 +669,13 @@ public class ReceiptActivity extends AppCompatActivity { } // Check if this log matches our reference - if (isDirectMatch || isRefreshMatch) { + if (isDirectMatch || isRefreshMatch || isOrderMatch) { + Log.d("ReceiptActivity", "🎯 TRANSACTION MATCH FOUND!"); + Log.d("ReceiptActivity", " Match type: " + + (isDirectMatch ? "DIRECT " : "") + + (isRefreshMatch ? "REFRESH " : "") + + (isOrderMatch ? "ORDER" : "")); + // If target statuses specified, check status if (targetStatuses.length > 0) { boolean statusMatches = false; @@ -602,14 +685,47 @@ public class ReceiptActivity extends AppCompatActivity { break; } } - if (!statusMatches) continue; // Skip if status doesn't match + if (!statusMatches) { + Log.d("ReceiptActivity", " Status doesn't match target: " + logTransactionStatus); + continue; // Skip if status doesn't match + } } - // Extract acquirer (prefer acquirer field over issuer) - String foundAcquirer = !logAcquirer.isEmpty() ? logAcquirer : logIssuer; - if (!foundAcquirer.isEmpty() && !foundAcquirer.equalsIgnoreCase("qris")) { - Log.d("ReceiptActivity", "📋 Found acquirer: " + foundAcquirer + " (status: " + logTransactionStatus + ")"); + // ✅ CRITICAL FIX: For QRIS transactions, prioritize issuer over acquirer + String foundAcquirer = null; + + // Check if this is a QRIS transaction by payment_type + String paymentType = reqBody.optString("payment_type", ""); + boolean isQrisTransaction = "qris".equalsIgnoreCase(paymentType); + + if (isQrisTransaction) { + // For QRIS: issuer contains the actual payment provider (LinkAja, DANA, etc.) + // acquirer is usually just "gopay" (the QRIS aggregator) + if (!logIssuer.isEmpty() && !logIssuer.equalsIgnoreCase("qris")) { + foundAcquirer = logIssuer; + Log.d("ReceiptActivity", "📱 QRIS: Using issuer as acquirer: " + foundAcquirer); + } else if (!logAcquirer.isEmpty() && !logAcquirer.equalsIgnoreCase("qris")) { + foundAcquirer = logAcquirer; + Log.d("ReceiptActivity", "📱 QRIS: Fallback to acquirer: " + foundAcquirer); + } + } else { + // For non-QRIS: prefer acquirer over issuer (traditional behavior) + if (!logAcquirer.isEmpty() && !logAcquirer.equalsIgnoreCase("qris")) { + foundAcquirer = logAcquirer; + Log.d("ReceiptActivity", "💳 Non-QRIS: Using acquirer: " + foundAcquirer); + } else if (!logIssuer.isEmpty() && !logIssuer.equalsIgnoreCase("qris")) { + foundAcquirer = logIssuer; + Log.d("ReceiptActivity", "💳 Non-QRIS: Fallback to issuer: " + foundAcquirer); + } + } + + if (foundAcquirer != null && !foundAcquirer.isEmpty()) { + Log.d("ReceiptActivity", "✅ FINAL ACQUIRER FOUND: " + foundAcquirer + + " (status: " + logTransactionStatus + + ", payment_type: " + paymentType + ")"); return foundAcquirer; + } else { + Log.w("ReceiptActivity", "⚠️ Transaction matched but no valid acquirer found"); } } } @@ -626,7 +742,18 @@ public class ReceiptActivity extends AppCompatActivity { if (input == null || input.isEmpty()) { return input; } - return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase(); + + // Handle special cases + String cleaned = input.trim(); + + // Special handling for common patterns + if (cleaned.toLowerCase().contains("pay")) { + // Keep "Pay" capitalized in payment methods + return cleaned.substring(0, 1).toUpperCase() + cleaned.substring(1).toLowerCase() + .replace("pay", "Pay"); + } + + return cleaned.substring(0, 1).toUpperCase() + cleaned.substring(1).toLowerCase(); } private void setAmountData(String amount, String grossAmount) {