Result Transaction dan QRIS

This commit is contained in:
riz081 2025-06-27 17:01:05 +07:00
parent b66ef4bb00
commit 53964211c2
4 changed files with 579 additions and 537 deletions

View File

@ -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<String, String> ISSUER_DISPLAY_MAP = new HashMap<String, String>() {{
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,89 +176,144 @@ 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;
// 6. ENHANCED: Set card type with EMV and QRIS priority
String displayCardType = getDisplayCardType(cardTypeStr, acquirer, channelCode, isEmvTransaction,
isQrisTransaction, midtransResponse, referenceId);
cardType.setText(displayCardType);
if (channelCode != null && channelCode.equalsIgnoreCase("QRIS")) {
Log.d("ReceiptActivity", "🔍 QRIS transaction detected - searching for real acquirer");
// 7. Format and set amounts with proper calculation
setAmountDataEnhanced(amount, grossAmount, isEmvTransaction, isQrisTransaction);
// 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";
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 ===");
}
}
// Start async search for better results
fetchRealAcquirerFromWebhook(referenceId);
}
} else {
displayCardType = "QRIS";
}
/**
* 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 {
// Non-QRIS transaction
displayCardType = getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr);
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
}
}
cardType.setText(displayCardType);
Log.d("ReceiptActivity", "💳 FINAL CARD TYPE: " + displayCardType);
// If all formats fail, return as-is
Log.w("ReceiptActivity", "Could not format EMV date: " + dateString);
return dateString;
// 7. Format and set amounts
setAmountData(amount, grossAmount);
Log.d("ReceiptActivity", "=== TRANSACTION DATA LOADED ===");
} catch (Exception e) {
Log.e("ReceiptActivity", "Error formatting EMV date: " + dateString, e);
return dateString;
}
}
@ -227,7 +323,7 @@ public class ReceiptActivity extends AppCompatActivity {
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

View File

@ -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<String, String> ISSUER_DISPLAY_MAP = new HashMap<String, String>() {{
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);

View File

@ -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();
// 4. Set transaction date
String displayDate = formatTransactionDate();
transactionDate.setText(displayDate);
// ENHANCED: Add debugging for final values
Log.d(TAG, "Payment Method: " + paymentMethod);
Log.d(TAG, "Final Card Type Display: " + cardTypeDisplay);
Log.d(TAG, "================================");
// 5. Set payment method
String displayPaymentMethod = getPaymentMethodDisplay();
paymentMethod.setText(displayPaymentMethod);
tvPaymentMethodDetail.setText(paymentMethod);
tvCardType.setText(cardTypeDisplay);
// 6. ENHANCED: Set card type with comprehensive detection
String displayCardType = getCardTypeDisplay();
cardType.setText(displayCardType);
// Amount details
// 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,9 +421,6 @@ 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");
@ -446,216 +428,111 @@ public class ResultTransactionActivity extends AppCompatActivity {
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<String> 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

View File

@ -104,7 +104,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="EDC "
android:text="Payvora "
android:textColor="#E31937"
android:textSize="24sp"
android:textStyle="bold"
@ -113,7 +113,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Merchant"
android:text="PRO"
android:textColor="#3F51B5"
android:textSize="24sp"
android:textStyle="bold"
@ -121,15 +121,6 @@
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BANK BRI"
android:textColor="#333333"
android:textSize="12sp"
android:fontFamily="@font/inter"
android:layout_marginTop="2dp"/>
</LinearLayout>
<!-- Merchant Info -->
@ -168,46 +159,36 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="MID: "
android:textColor="#666666"
android:textSize="12sp"
android:fontFamily="@font/inter"/>
<TextView
android:id="@+id/mid_text"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="1234567890"
android:text="MID:12345678901"
android:textColor="#333333"
android:textSize="12sp"
android:fontFamily="@font/inter"/>
<!-- Vertical Separator -->
<TextView
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TID: "
android:textColor="#666666"
android:text=" | "
android:textColor="#999999"
android:textSize="12sp"
android:fontFamily="@font/inter"/>
android:fontFamily="@font/inter"
android:paddingHorizontal="8dp"/>
<TextView
android:id="@+id/tid_text"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="1234567890"
android:text="TID:12345678901"
android:textColor="#333333"
android:textSize="12sp"
android:fontFamily="@font/inter"/>
</LinearLayout>
<!-- Separator Line -->