display result transation

This commit is contained in:
riz081 2025-06-26 13:57:07 +07:00
parent 8a73206a76
commit 7a2ddc3f15
4 changed files with 1639 additions and 486 deletions

View File

@ -31,10 +31,11 @@ import java.util.Locale;
import com.example.bdkipoc.transaction.ResultTransactionActivity; import com.example.bdkipoc.transaction.ResultTransactionActivity;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONException;
/** /**
* CreateTransactionActivity - Updated with Midtrans Credit Card Integration * CreateTransactionActivity - Updated with Enhanced Midtrans Credit Card Integration
* Handles amount input, card scanning, and Midtrans payment processing * Handles amount input, card scanning, and Midtrans payment processing with improved bank detection
*/ */
public class CreateTransactionActivity extends AppCompatActivity implements public class CreateTransactionActivity extends AppCompatActivity implements
EMVManager.EMVManagerCallback, EMVManager.EMVManagerCallback,
@ -80,6 +81,9 @@ public class CreateTransactionActivity extends AppCompatActivity implements
private String emvTlvData; private String emvTlvData;
private String referenceId; private String referenceId;
// ENHANCED: Store last response for better debugging
private JSONObject lastMidtransResponse;
// deklarasi variabel success screen // deklarasi variabel success screen
private LinearLayout successScreen; private LinearLayout successScreen;
private ImageView successIcon; private ImageView successIcon;
@ -465,7 +469,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
emvManager.importPinInputStatus(3); // Error emvManager.importPinInputStatus(3); // Error
} }
// ====== NEW: MIDTRANS PAYMENT CALLBACK METHODS ====== // ====== ENHANCED: MIDTRANS PAYMENT CALLBACK METHODS ======
@Override @Override
public void onTokenizeSuccess(String cardToken) { public void onTokenizeSuccess(String cardToken) {
Log.d(TAG, "✅ Midtrans tokenization successful: " + cardToken); Log.d(TAG, "✅ Midtrans tokenization successful: " + cardToken);
@ -493,18 +497,31 @@ public class CreateTransactionActivity extends AppCompatActivity implements
public void onChargeSuccess(JSONObject chargeResponse) { public void onChargeSuccess(JSONObject chargeResponse) {
Log.d(TAG, "✅ Midtrans charge successful!"); Log.d(TAG, "✅ Midtrans charge successful!");
// ENHANCED: Store response for debugging
lastMidtransResponse = chargeResponse;
try { try {
String transactionId = chargeResponse.getString("transaction_id"); String transactionId = chargeResponse.getString("transaction_id");
String transactionStatus = chargeResponse.getString("transaction_status"); String transactionStatus = chargeResponse.getString("transaction_status");
String statusCode = chargeResponse.optString("status_code", ""); String statusCode = chargeResponse.optString("status_code", "");
// ENHANCED: Extract and log bank information
String bankInfo = extractBankFromResponse(chargeResponse);
Log.d(TAG, "✅ Payment Details:"); Log.d(TAG, "✅ Payment Details:");
Log.d(TAG, " - Transaction ID: " + transactionId); Log.d(TAG, " - Transaction ID: " + transactionId);
Log.d(TAG, " - Transaction Status: " + transactionStatus); Log.d(TAG, " - Transaction Status: " + transactionStatus);
Log.d(TAG, " - Status Code: " + statusCode); Log.d(TAG, " - Status Code: " + statusCode);
Log.d(TAG, " - Bank Info: " + bankInfo);
Log.d(TAG, " - Full Response: " + chargeResponse.toString());
// Navigate to success results with Midtrans data // Check transaction status and navigate accordingly
navigateToMidtransResults(chargeResponse); boolean isSuccess = "capture".equals(transactionStatus) ||
"settlement".equals(transactionStatus) ||
"pending".equals(transactionStatus);
// Navigate to results with Midtrans data
navigateToMidtransResults(chargeResponse, isSuccess);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage()); Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage());
@ -518,14 +535,31 @@ public class CreateTransactionActivity extends AppCompatActivity implements
@Override @Override
public void onChargeError(String errorMessage) { public void onChargeError(String errorMessage) {
Log.e(TAG, "❌ Midtrans charge failed: " + errorMessage); Log.e(TAG, "❌ Midtrans charge failed: " + errorMessage);
modalManager.hideModal();
// IMPROVED: Better error handling with user-friendly messages // ENHANCED: Try to get response even in error case
JSONObject errorResponse = midtransPaymentManager.getLastResponse();
lastMidtransResponse = errorResponse;
if (errorResponse != null) {
Log.d(TAG, "✅ Got Midtrans error response with data, using it for display");
// ENHANCED: Extract bank even from error response
String bankInfo = extractBankFromResponse(errorResponse);
Log.d(TAG, "Bank info from error response: " + bankInfo);
modalManager.hideModal();
String userMessage = getUserFriendlyErrorMessage(errorMessage); String userMessage = getUserFriendlyErrorMessage(errorMessage);
showToast(userMessage); showToast(userMessage);
// Show detailed error in logs but user-friendly message to user // Use Midtrans results even for failed transactions
Log.e(TAG, "Detailed error: " + errorMessage); navigateToMidtransResults(errorResponse, false);
} else {
Log.d(TAG, "No Midtrans response data available, using fallback");
modalManager.hideModal();
String userMessage = getUserFriendlyErrorMessage(errorMessage);
showToast(userMessage);
// Fallback to traditional results screen after delay // Fallback to traditional results screen after delay
new Handler(Looper.getMainLooper()).postDelayed(() -> { new Handler(Looper.getMainLooper()).postDelayed(() -> {
@ -534,6 +568,41 @@ public class CreateTransactionActivity extends AppCompatActivity implements
navigateToResults(cardType, null, emvManager.getCardNo()); navigateToResults(cardType, null, emvManager.getCardNo());
}, 3000); }, 3000);
} }
}
// NEW: Method to extract bank information from Midtrans response
private String extractBankFromResponse(JSONObject response) {
if (response == null) {
return "Unknown";
}
try {
// Try different possible bank field names
String[] possibleBankFields = {"bank", "issuer", "acquiring_bank", "card_type"};
for (String field : possibleBankFields) {
if (response.has(field)) {
String value = response.getString(field);
if (value != null && !value.trim().isEmpty()) {
Log.d(TAG, "Found bank info in field '" + field + "': " + value);
return value;
}
}
}
// If no bank field found, try to extract from card details
if (response.has("payment_method")) {
String paymentMethod = response.getString("payment_method");
Log.d(TAG, "Payment method: " + paymentMethod);
}
return "Unknown";
} catch (JSONException e) {
Log.e(TAG, "Error extracting bank from response: " + e.getMessage());
return "Unknown";
}
}
private String getUserFriendlyErrorMessage(String errorMessage) { private String getUserFriendlyErrorMessage(String errorMessage) {
if (errorMessage == null) { if (errorMessage == null) {
@ -750,8 +819,15 @@ public class CreateTransactionActivity extends AppCompatActivity implements
}); });
} }
// NEW: Navigate to results with Midtrans payment data // ENHANCED: Better navigation with comprehensive data
private void navigateToMidtransResults(JSONObject midtransResponse) { private void navigateToMidtransResults(JSONObject midtransResponse, boolean paymentSuccess) {
Log.d(TAG, "=== NAVIGATING TO MIDTRANS RESULTS ===");
Log.d(TAG, "Payment Success: " + paymentSuccess);
Log.d(TAG, "Response Length: " + (midtransResponse != null ? midtransResponse.length() : 0));
// ENHANCED: Validate and enhance response data
JSONObject enhancedResponse = enhanceMidtransResponse(midtransResponse);
showSuccessScreen(() -> { showSuccessScreen(() -> {
Intent intent = new Intent(this, ResultTransactionActivity.class); Intent intent = new Intent(this, ResultTransactionActivity.class);
intent.putExtra("TRANSACTION_AMOUNT", transactionAmount); intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
@ -759,19 +835,197 @@ public class CreateTransactionActivity extends AppCompatActivity implements
intent.putExtra("EMV_MODE", true); intent.putExtra("EMV_MODE", true);
intent.putExtra("REFERENCE_ID", referenceId); intent.putExtra("REFERENCE_ID", referenceId);
intent.putExtra("CARD_NO", emvCardNumber); intent.putExtra("CARD_NO", emvCardNumber);
intent.putExtra("MIDTRANS_RESPONSE", midtransResponse.toString()); intent.putExtra("PAYMENT_SUCCESS", paymentSuccess);
intent.putExtra("PAYMENT_SUCCESS", true);
// NEW: Add additional EMV data for receipt // ENHANCED: Send enhanced response
if (enhancedResponse != null) {
String responseString = enhancedResponse.toString();
intent.putExtra("MIDTRANS_RESPONSE", responseString);
Log.d(TAG, "✅ Sending Midtrans response: " + responseString);
} else {
Log.w(TAG, "⚠️ No Midtrans response to send");
}
// Add additional EMV data for receipt
intent.putExtra("EMV_CARDHOLDER_NAME", emvCardholderName); intent.putExtra("EMV_CARDHOLDER_NAME", emvCardholderName);
intent.putExtra("EMV_AID", emvAidIdentifier); intent.putExtra("EMV_AID", emvAidIdentifier);
intent.putExtra("EMV_EXPIRY", emvExpiryDate); intent.putExtra("EMV_EXPIRY", emvExpiryDate);
// ENHANCED: Add debugging extras
intent.putExtra("DEBUG_BANK_SOURCE", "midtrans_response");
startActivity(intent); startActivity(intent);
finish(); finish();
}); });
} }
// NEW: Method to enhance Midtrans response with additional data
private JSONObject enhanceMidtransResponse(JSONObject originalResponse) {
if (originalResponse == null) {
Log.w(TAG, "Original response is null, creating fallback response");
return createFallbackMidtransResponse();
}
try {
// Clone the original response
JSONObject enhanced = new JSONObject(originalResponse.toString());
// ENHANCED: Add bank information if missing
if (!enhanced.has("bank") || enhanced.getString("bank").trim().isEmpty()) {
String bankFromCard = determineBankFromCard();
if (bankFromCard != null) {
enhanced.put("bank", bankFromCard);
Log.d(TAG, "✅ Added bank to response: " + bankFromCard);
}
}
// ENHANCED: Add EMV data if available
if (emvCardNumber != null) {
enhanced.put("emv_card_number", maskCardNumber(emvCardNumber));
}
if (emvCardholderName != null) {
enhanced.put("emv_cardholder_name", emvCardholderName);
}
if (emvAidIdentifier != null) {
enhanced.put("emv_aid", emvAidIdentifier);
}
// Add transaction metadata
enhanced.put("processing_timestamp", System.currentTimeMillis());
enhanced.put("emv_mode", true);
Log.d(TAG, "✅ Enhanced response created with " + enhanced.length() + " fields");
return enhanced;
} catch (JSONException e) {
Log.e(TAG, "Error enhancing response: " + e.getMessage());
return originalResponse;
}
}
// NEW: Create fallback response when Midtrans response is unavailable
private JSONObject createFallbackMidtransResponse() {
try {
JSONObject fallback = new JSONObject();
// Basic transaction info
fallback.put("transaction_id", "FALLBACK_" + System.currentTimeMillis());
fallback.put("order_id", "ORDER_" + System.currentTimeMillis());
fallback.put("gross_amount", transactionAmount);
fallback.put("transaction_status", "processed");
fallback.put("status_code", "200");
fallback.put("status_message", "Success");
// ENHANCED: Determine bank from available data
String bankFromCard = determineBankFromCard();
if (bankFromCard != null) {
fallback.put("bank", bankFromCard);
} else {
fallback.put("bank", "BCA"); // Default
}
// EMV data
if (emvCardNumber != null) {
fallback.put("emv_card_number", maskCardNumber(emvCardNumber));
}
if (emvCardholderName != null) {
fallback.put("emv_cardholder_name", emvCardholderName);
}
fallback.put("payment_type", "credit_card");
fallback.put("transaction_time", new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date()));
fallback.put("is_fallback", true);
Log.d(TAG, "✅ Created fallback response: " + fallback.toString());
return fallback;
} catch (JSONException e) {
Log.e(TAG, "Error creating fallback response: " + e.getMessage());
return null;
}
}
// NEW: Determine bank from card number or EMV data
private String determineBankFromCard() {
// Priority 1: Use card BIN
if (emvCardNumber != null && emvCardNumber.length() >= 6) {
String bin = emvCardNumber.substring(0, 6);
return getBankFromBin(bin);
}
// Priority 2: Use EMV AID
if (emvAidIdentifier != null) {
return getBankFromAid(emvAidIdentifier);
}
return null;
}
// ENHANCED: Better BIN to bank mapping
private String getBankFromBin(String bin) {
if (bin == null || bin.length() < 4) {
return "BCA"; // Default
}
// Get first 4 digits for matching
String bin4 = bin.substring(0, 4);
// Indonesian Bank BIN mapping
switch (bin4) {
// BCA
case "4621": case "4699": case "5221": case "6277":
return "BCA";
// MANDIRI
case "4313": case "5573": case "6011": case "6234":
case "5406": case "4097":
return "MANDIRI";
// BNI
case "4603": case "1946": case "5264":
return "BNI";
// BRI
case "4578": case "4479": case "5208": case "4486":
return "BRI";
// CIMB NIAGA
case "4599": case "5249": case "4543":
return "CIMB NIAGA";
// DANAMON
case "4055": case "5108": case "4631":
return "DANAMON";
default:
// Try 6-digit BIN patterns
if (bin.length() >= 6) {
String bin6 = bin.substring(0, 6);
// Add more specific 6-digit patterns here if needed
Log.d(TAG, "Unknown BIN pattern: " + bin6);
}
return "BCA"; // Default fallback
}
}
private String getBankFromAid(String aid) {
if (aid == null) return "BCA";
// AID patterns for Indonesian banks
if (aid.contains("A0000000031010")) {
return "BCA"; // Common for VISA
} else if (aid.contains("A0000000041010")) {
return "MANDIRI"; // Common for Mastercard
}
return "BCA"; // Default
}
// NEW: Add getter for last response (for debugging)
public JSONObject getLastMidtransResponse() {
return lastMidtransResponse;
}
private void restartScanningAfterDelay() { private void restartScanningAfterDelay() {
Log.d(TAG, "Restarting scanning after delay..."); Log.d(TAG, "Restarting scanning after delay...");

View File

@ -16,13 +16,14 @@ import java.net.URI;
import java.net.URL; import java.net.URL;
/** /**
* MidtransCardPaymentManager - Fixed Version for EMV Card Processing * MidtransCardPaymentManager - Enhanced Version for EMV Card Processing
* *
* Key Features: * Key Features:
* - Uses static CVV "493" for all transactions (both EMV and regular cards) * - Uses static CVV "493" for all transactions (both EMV and regular cards)
* - EMV-first approach with tokenization fallback * - EMV-first approach with tokenization fallback
* - Handles Midtrans API requirements where CVV is mandatory even for EMV * - Handles Midtrans API requirements where CVV is mandatory even for EMV
* - Comprehensive error handling and retry logic * - Comprehensive error handling and retry logic
* - Enhanced response processing with bank detection
* *
* Note: Midtrans sandbox environment requires CVV even for EMV chip transactions * Note: Midtrans sandbox environment requires CVV even for EMV chip transactions
* during tokenization, so we use static CVV "493" as per curl example. * during tokenization, so we use static CVV "493" as per curl example.
@ -45,6 +46,10 @@ public class MidtransCardPaymentManager {
private int retryCount = 0; private int retryCount = 0;
private static final int MAX_RETRY = 2; private static final int MAX_RETRY = 2;
// ENHANCED: Store last response for debugging
private JSONObject lastResponse;
private String lastErrorMessage;
public interface MidtransCardPaymentCallback { public interface MidtransCardPaymentCallback {
void onTokenizeSuccess(String cardToken); void onTokenizeSuccess(String cardToken);
void onTokenizeError(String errorMessage); void onTokenizeError(String errorMessage);
@ -58,6 +63,16 @@ public class MidtransCardPaymentManager {
this.callback = callback; this.callback = callback;
} }
// ENHANCED: Getter for last response
public JSONObject getLastResponse() {
return lastResponse;
}
// ENHANCED: Getter for last error message
public String getLastErrorMessage() {
return lastErrorMessage;
}
/** /**
* Process EMV card payment - handles EMV-specific requirements * Process EMV card payment - handles EMV-specific requirements
*/ */
@ -241,14 +256,30 @@ public class MidtransCardPaymentManager {
response.append(responseLine.trim()); response.append(responseLine.trim());
} }
Log.d(TAG, "EMV Charge response: " + response.toString()); String responseString = response.toString();
chargeResponse = new JSONObject(response.toString()); Log.d(TAG, "EMV Charge response: " + responseString);
// ENHANCED: Store response for debugging
try {
chargeResponse = new JSONObject(responseString);
lastResponse = chargeResponse; // Store for later access
// ENHANCED: Add bank detection if missing
enhanceResponseWithBankInfo(chargeResponse);
} catch (JSONException e) {
Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
// Create fallback response object
chargeResponse = createFallbackResponse(responseString, responseCode);
lastResponse = chargeResponse;
}
return processChargeResponse(chargeResponse, responseCode); return processChargeResponse(chargeResponse, responseCode);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "EMV Charge request exception: " + e.getMessage(), e); Log.e(TAG, "EMV Charge request exception: " + e.getMessage(), e);
errorMessage = "Network error: " + e.getMessage(); errorMessage = "Network error: " + e.getMessage();
lastErrorMessage = errorMessage;
return false; return false;
} }
} }
@ -520,14 +551,30 @@ public class MidtransCardPaymentManager {
response.append(responseLine.trim()); response.append(responseLine.trim());
} }
Log.d(TAG, "Token Charge response: " + response.toString()); String responseString = response.toString();
chargeResponse = new JSONObject(response.toString()); Log.d(TAG, "Token Charge response: " + responseString);
// ENHANCED: Store response for debugging
try {
chargeResponse = new JSONObject(responseString);
lastResponse = chargeResponse; // Store for later access
// ENHANCED: Add bank detection if missing
enhanceResponseWithBankInfo(chargeResponse);
} catch (JSONException e) {
Log.e(TAG, "Error parsing response JSON: " + e.getMessage());
// Create fallback response object
chargeResponse = createFallbackResponse(responseString, responseCode);
lastResponse = chargeResponse;
}
return processChargeResponse(chargeResponse, responseCode); return processChargeResponse(chargeResponse, responseCode);
} catch (Exception e) { } catch (Exception e) {
Log.e(TAG, "Token charge request exception: " + e.getMessage(), e); Log.e(TAG, "Token charge request exception: " + e.getMessage(), e);
errorMessage = "Network error: " + e.getMessage(); errorMessage = "Network error: " + e.getMessage();
lastErrorMessage = errorMessage;
return false; return false;
} }
} }
@ -574,6 +621,114 @@ public class MidtransCardPaymentManager {
} }
} }
// ENHANCED: Method to enhance response with bank information
private void enhanceResponseWithBankInfo(JSONObject response) {
if (response == null) return;
try {
// If bank field is missing or empty, try to determine it
if (!response.has("bank") || response.getString("bank").trim().isEmpty()) {
String detectedBank = detectBankFromResponse(response);
if (detectedBank != null) {
response.put("bank", detectedBank);
Log.d(TAG, "✅ Enhanced response with detected bank: " + detectedBank);
}
} else {
String existingBank = response.getString("bank");
Log.d(TAG, "✅ Response already has bank field: " + existingBank);
}
// Log final bank value
if (response.has("bank")) {
Log.d(TAG, "Final bank value in response: '" + response.getString("bank") + "'");
}
} catch (JSONException e) {
Log.e(TAG, "Error enhancing response with bank info: " + e.getMessage());
}
}
// ENHANCED: Method to detect bank from various response fields
private String detectBankFromResponse(JSONObject response) {
try {
// Try various fields that might contain bank information
String[] possibleFields = {
"issuer", "acquiring_bank", "card_type", "payment_method",
"issuer_name", "bank_name", "acquirer"
};
for (String field : possibleFields) {
if (response.has(field)) {
String value = response.getString(field);
if (value != null && !value.trim().isEmpty()) {
String mappedBank = mapToBankName(value);
if (mappedBank != null) {
Log.d(TAG, "Detected bank '" + mappedBank + "' from field '" + field + "': " + value);
return mappedBank;
}
}
}
}
// If no bank detected from response fields, return default
Log.d(TAG, "No bank detected from response fields, using default");
return "BCA"; // Default
} catch (JSONException e) {
Log.e(TAG, "Error detecting bank from response: " + e.getMessage());
return "BCA"; // Default
}
}
// ENHANCED: Map various bank identifiers to standard bank names
private String mapToBankName(String identifier) {
if (identifier == null || identifier.trim().isEmpty()) {
return null;
}
String normalized = identifier.trim().toUpperCase();
// Map common bank identifiers
if (normalized.contains("BCA") || normalized.contains("CENTRAL ASIA")) {
return "BCA";
} else if (normalized.contains("MANDIRI")) {
return "MANDIRI";
} else if (normalized.contains("BNI") || normalized.contains("NEGARA INDONESIA")) {
return "BNI";
} else if (normalized.contains("BRI") || normalized.contains("RAKYAT INDONESIA")) {
return "BRI";
} else if (normalized.contains("CIMB") || normalized.contains("NIAGA")) {
return "CIMB NIAGA";
} else if (normalized.contains("DANAMON")) {
return "DANAMON";
} else if (normalized.contains("PERMATA")) {
return "PERMATA";
} else if (normalized.contains("MEGA")) {
return "MEGA";
}
return null; // No mapping found
}
// ENHANCED: Create fallback response when JSON parsing fails
private JSONObject createFallbackResponse(String rawResponse, int httpCode) {
try {
JSONObject fallback = new JSONObject();
fallback.put("status_code", String.valueOf(httpCode));
fallback.put("raw_response", rawResponse);
fallback.put("transaction_status", httpCode == 200 ? "capture" : "deny");
fallback.put("bank", "BCA"); // Default bank
fallback.put("is_fallback_response", true);
Log.d(TAG, "Created fallback response for HTTP " + httpCode);
return fallback;
} catch (JSONException e) {
Log.e(TAG, "Error creating fallback response: " + e.getMessage());
return new JSONObject();
}
}
// Helper Methods // Helper Methods
/** /**
@ -634,6 +789,26 @@ public class MidtransCardPaymentManager {
error.contains("timeout"); error.contains("timeout");
} }
// ENHANCED: Debug method to inspect response
public void debugResponse() {
if (lastResponse != null) {
Log.d(TAG, "=== DEBUGGING LAST RESPONSE ===");
try {
java.util.Iterator<String> keys = lastResponse.keys();
while (keys.hasNext()) {
String key = keys.next();
Object value = lastResponse.get(key);
Log.d(TAG, key + ": " + value);
}
} catch (Exception e) {
Log.e(TAG, "Error debugging response: " + e.getMessage());
}
Log.d(TAG, "===============================");
} else {
Log.d(TAG, "No last response available for debugging");
}
}
/** /**
* Enhanced Card data holder class with EMV detection * Enhanced Card data holder class with EMV detection
*/ */

View File

@ -1,55 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:background="@color/background_main"> android:background="#FFFFFF"
tools:context=".transaction.ResultTransactionActivity">
<!-- Toolbar --> <!-- AppBar Component -->
<androidx.appcompat.widget.Toolbar <include layout="@layout/component_appbar" />
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/toolbar_background"
android:theme="@style/CustomToolbarTheme"
app:titleTextAppearance="@style/ToolbarTitleStyle">
<LinearLayout <!-- Main Content -->
android:id="@+id/back_navigation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingStart="8dp"
android:paddingEnd="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@android:drawable/ic_menu_revert"
android:tint="@color/white"
android:layout_marginEnd="8dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Transaction Result"
style="@style/ToolbarTitleStyle" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
<!-- Content -->
<ScrollView <ScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" android:layout_weight="1"
android:fillViewport="true"> android:fillViewport="true"
android:overScrollMode="never"
android:scrollbars="none">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -57,14 +26,14 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<!-- Transaction Summary Card --> <!-- Receipt Card -->
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp" app:cardCornerRadius="12dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp" app:cardElevation="4dp"
app:cardBackgroundColor="@color/card_background"> app:cardBackgroundColor="#FFFFFF"
android:layout_marginBottom="16dp">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -72,12 +41,101 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="20dp"> android:padding="20dp">
<TextView <!-- Logo Section -->
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Transaction Summary" android:orientation="horizontal"
style="@style/CardTitleStyle" /> android:gravity="center_vertical"
android:layout_marginBottom="16dp">
<!-- Logo Icon -->
<TextView
android:layout_width="32dp"
android:layout_height="32dp"
android:text="P"
android:textColor="#FFFFFF"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="@font/inter"
android:gravity="center"
android:background="@drawable/ic_logo_icon"
android:layout_marginEnd="8dp" />
<!-- Logo Text -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Payvora"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="@font/inter" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PRO"
android:textColor="#DE0701"
android:textSize="12sp"
android:textStyle="bold"
android:fontFamily="@font/inter"
android:layout_marginTop="-2dp" />
</LinearLayout>
</LinearLayout>
<!-- Merchant Info -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_marginBottom="20dp">
<TextView
android:id="@+id/tv_merchant_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TOKO KLONTONG PAK EKO"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="@font/inter"
android:gravity="center"
android:layout_marginBottom="4dp" />
<TextView
android:id="@+id/tv_merchant_location"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ciputat Baru, Tangsel"
android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter"
android:gravity="center" />
</LinearLayout>
<!-- Horizontal Divider -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#E0E0E0"
android:layout_marginBottom="16dp" />
<!-- Transaction Details -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- MID and TID Row -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -88,162 +146,39 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Amount:" android:text="MID: 123456789901"
style="@style/SubHeaderTextStyle" /> android:textColor="#666666"
android:textSize="12sp"
android:fontFamily="@font/inter" />
<TextView <TextView
android:id="@+id/tv_amount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Rp 190.00" android:text="|"
style="@style/AmountDisplayStyle" android:textColor="#E0E0E0"
android:textSize="20sp" /> android:textSize="12sp"
android:layout_marginHorizontal="8dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<TextView <TextView
android:id="@+id/tv_tid"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Status:" android:text="TID: 123456789901"
style="@style/SubHeaderTextStyle" /> android:textColor="#666666"
android:textSize="12sp"
<TextView android:fontFamily="@font/inter"
android:id="@+id/tv_status" android:gravity="end" />
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FAILED"
style="@style/StatusTextStyle"
android:textColor="@color/status_error"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<LinearLayout <!-- Transaction Details Rows -->
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Reference:"
style="@style/SubHeaderTextStyle" />
<TextView
android:id="@+id/tv_reference"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ref-1234567890"
style="@style/BodyTextStyle"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Card:"
style="@style/SubHeaderTextStyle" />
<TextView
android:id="@+id/tv_card_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="4616****9849"
style="@style/BodyTextStyle"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Response Data Card -->
<androidx.cardview.widget.CardView
android:id="@+id/card_response_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp"
app:cardBackgroundColor="@color/card_background">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Payment Response Data"
style="@style/CardTitleStyle" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="350dp"
android:background="@color/light_gray"
android:padding="12dp">
<TextView
android:id="@+id/tv_response_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Loading response data..."
style="@style/MonospaceTextStyle"
android:lineSpacingExtra="4dp"
android:textIsSelectable="true" />
</ScrollView>
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Technical Details Card -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp"
app:cardBackgroundColor="@color/card_background">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="20dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Technical Details"
style="@style/CardTitleStyle" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<!-- Transaction Number -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -254,18 +189,24 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Payment Method:" android:text="Nomor transaksi"
style="@style/HintTextStyle" /> android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView <TextView
android:id="@+id/tv_payment_method" android:id="@+id/tv_transaction_number"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="EMV_MIDTRANS" android:text="3429483635"
style="@style/BodyTextStyle" /> android:textColor="#333333"
android:textSize="14sp"
android:fontFamily="@font/inter"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<!-- Transaction Date -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -276,19 +217,24 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Transaction ID:" android:text="Tanggal transaksi"
style="@style/HintTextStyle" /> android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView <TextView
android:id="@+id/tv_transaction_id" android:id="@+id/tv_transaction_date"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="N/A" android:text="13 Januari 2025 13:46"
style="@style/BodyTextStyle" android:textColor="#333333"
android:textIsSelectable="true" /> android:textSize="14sp"
android:fontFamily="@font/inter"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<!-- Payment Method -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -299,19 +245,67 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Order ID:" android:text="Metode pembayaran"
style="@style/HintTextStyle" /> android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView <TextView
android:id="@+id/tv_order_id" android:id="@+id/tv_payment_method_detail"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="N/A" android:text="Kartu Kredit"
style="@style/BodyTextStyle" android:textColor="#333333"
android:textIsSelectable="true" /> android:textSize="14sp"
android:fontFamily="@font/inter"
android:textStyle="bold" />
</LinearLayout> </LinearLayout>
<!-- Card Type -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Jenis Kartu"
android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView
android:id="@+id/tv_card_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="BCA"
android:textColor="#333333"
android:textSize="14sp"
android:fontFamily="@font/inter"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>
<!-- Amount Section Divider -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#E0E0E0"
android:layout_marginBottom="16dp" />
<!-- Amount Details -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Total Transaction -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -322,43 +316,176 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:text="Timestamp:" android:text="Total transaksi"
style="@style/HintTextStyle" /> android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView <TextView
android:id="@+id/tv_timestamp" android:id="@+id/tv_subtotal"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="N/A" android:text="3.500.000"
style="@style/BodyTextStyle" /> android:textColor="#333333"
android:textSize="14sp"
android:fontFamily="@font/inter" />
</LinearLayout> </LinearLayout>
<!-- Tax -->
<LinearLayout <LinearLayout
android:id="@+id/layout_error_details"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="horizontal"
android:visibility="gone"> android:layout_marginBottom="8dp">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="Error Details:" android:layout_weight="1"
style="@style/HintTextStyle" android:text="Pajak (%)"
android:layout_marginTop="8dp" android:textColor="#666666"
android:layout_marginBottom="4dp" /> android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView <TextView
android:id="@+id/tv_error_details" android:id="@+id/tv_tax"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="11%"
android:textColor="#333333"
android:textSize="14sp"
android:fontFamily="@font/inter" />
</LinearLayout>
<!-- Service Fee -->
<LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="" android:orientation="horizontal"
style="@style/BodyTextStyle" android:layout_marginBottom="16dp">
android:textColor="@color/status_error"
android:background="@color/light_gray" <TextView
android:padding="8dp" android:layout_width="0dp"
android:textIsSelectable="true" /> android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Biaya Layanan"
android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
<TextView
android:id="@+id/tv_service_fee"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="500"
android:textColor="#333333"
android:textSize="14sp"
android:fontFamily="@font/inter" />
</LinearLayout>
</LinearLayout>
<!-- Final Total Section -->
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#E0E0E0"
android:layout_marginBottom="16dp" />
<!-- Final Total -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="20dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="TOTAL"
android:textColor="#333333"
android:textSize="16sp"
android:textStyle="bold"
android:fontFamily="@font/inter" />
<TextView
android:id="@+id/tv_final_total"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="3.506.500"
android:textColor="#333333"
android:textSize="20sp"
android:textStyle="bold"
android:fontFamily="@font/inter" />
</LinearLayout>
<!-- Action Buttons Row -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="16dp">
<!-- Print Button -->
<LinearLayout
android:id="@+id/btn_print"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground"
android:padding="12dp"
android:layout_marginEnd="24dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_print"
app:tint="#666666"
android:layout_marginEnd="8dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cetak Ulang"
android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
</LinearLayout>
<!-- Email Button -->
<LinearLayout
android:id="@+id/btn_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground"
android:padding="12dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/ic_email"
app:tint="#666666"
android:layout_marginEnd="8dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Email"
android:textColor="#666666"
android:textSize="14sp"
android:fontFamily="@font/inter" />
</LinearLayout>
</LinearLayout> </LinearLayout>
@ -372,33 +499,51 @@
</ScrollView> </ScrollView>
<!-- Action Buttons --> <!-- Bottom Action Button -->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="vertical"
android:padding="16dp" android:padding="16dp"
android:background="@color/white" android:background="#FFFFFF">
android:elevation="4dp">
<Button <!-- Finish Button -->
android:id="@+id/btn_new_transaction" <com.google.android.material.button.MaterialButton
android:layout_width="0dp" android:id="@+id/btn_finish"
android:layout_width="match_parent"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_weight="1" android:text="Selesai"
android:layout_marginEnd="8dp" android:textColor="#FFFFFF"
android:text="New Transaction" android:textSize="14sp"
style="@style/PrimaryButton" /> android:textStyle="bold"
android:fontFamily="@font/inter"
android:background="@drawable/button_finish_background"
app:backgroundTint="@null"
app:cornerRadius="8dp"
app:rippleColor="#B3000000" />
<Button </LinearLayout>
android:id="@+id/btn_retry"
<!-- Hidden Components for Compatibility -->
<LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="0dp"
android:layout_weight="1" android:orientation="vertical"
android:layout_marginStart="8dp" android:visibility="gone">
android:text="Retry Payment"
style="@style/OutlineButton" <TextView android:id="@+id/tv_amount" android:layout_width="0dp" android:layout_height="0dp" />
android:visibility="gone" /> <TextView android:id="@+id/tv_status" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_reference" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_card_info" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_payment_method" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_transaction_id" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_order_id" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_timestamp" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_response_data" android:layout_width="0dp" android:layout_height="0dp" />
<TextView android:id="@+id/tv_error_details" android:layout_width="0dp" android:layout_height="0dp" />
<Button android:id="@+id/btn_new_transaction" android:layout_width="0dp" android:layout_height="0dp" />
<Button android:id="@+id/btn_retry" android:layout_width="0dp" android:layout_height="0dp" />
<LinearLayout android:id="@+id/layout_error_details" android:layout_width="0dp" android:layout_height="0dp" />
</LinearLayout> </LinearLayout>