QRIS FLOW
This commit is contained in:
parent
074a4b1f53
commit
99fab68e71
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
package com.example.bdkipoc;
|
||||
|
||||
import android.util.Log; // ADD THIS IMPORT
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@ -11,10 +11,22 @@ import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
// ✅ TAMBAHKAN MISSING IMPORTS INI:
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
// ✅ TAMBAHKAN JSON IMPORTS:
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.TransactionViewHolder> {
|
||||
private List<TransactionActivity.Transaction> transactionList;
|
||||
private OnPrintClickListener printClickListener;
|
||||
@ -42,32 +54,58 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
|
||||
TransactionActivity.Transaction t = transactionList.get(position);
|
||||
|
||||
Log.d("TransactionAdapter", "📋 Binding transaction " + position + ":");
|
||||
Log.d("TransactionAdapter", " Reference: " + t.referenceId);
|
||||
Log.d("TransactionAdapter", " Status: " + t.status);
|
||||
Log.d("TransactionAdapter", " Amount: " + t.amount);
|
||||
|
||||
// Set reference ID
|
||||
holder.referenceId.setText(t.referenceId);
|
||||
|
||||
// Format the amount as Indonesian Rupiah
|
||||
try {
|
||||
// Clean and format amount
|
||||
String cleanAmount = cleanAmountString(t.amount);
|
||||
long amountValue = Long.parseLong(cleanAmount);
|
||||
|
||||
// Format using Indonesian formatting (dots as thousand separators)
|
||||
String formattedAmount = formatRupiah(amountValue);
|
||||
holder.amount.setText(formattedAmount);
|
||||
|
||||
Log.d("TransactionAdapter", "Original: '" + t.amount + "' -> Cleaned: '" +
|
||||
cleanAmount + "' -> Formatted: '" + formattedAmount + "'");
|
||||
Log.d("TransactionAdapter", "💰 Amount processed: '" + t.amount + "' -> '" + formattedAmount + "'");
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e("TransactionAdapter", "Error formatting amount: " + t.amount, e);
|
||||
// Fallback: show original amount with Rp prefix if not already present
|
||||
Log.e("TransactionAdapter", "❌ Amount format error: " + t.amount, e);
|
||||
String fallback = t.amount.startsWith("Rp") ? t.amount : "Rp " + t.amount;
|
||||
holder.amount.setText(fallback);
|
||||
}
|
||||
|
||||
// Set status with appropriate color
|
||||
holder.status.setText(t.status.toUpperCase());
|
||||
setStatusColor(holder.status, t.status);
|
||||
// ✅ ENHANCED STATUS HANDLING dengan comprehensive checking
|
||||
String displayStatus = t.status;
|
||||
|
||||
Log.d("TransactionAdapter", "🔍 Checking status for: " + t.referenceId + " (current: " + displayStatus + ")");
|
||||
|
||||
// Jika status adalah INIT atau PENDING, lakukan comprehensive check
|
||||
if ("INIT".equalsIgnoreCase(t.status) || "PENDING".equalsIgnoreCase(t.status)) {
|
||||
if (t.referenceId != null && !t.referenceId.isEmpty()) {
|
||||
// Show checking state
|
||||
holder.status.setText("CHECKING...");
|
||||
holder.status.setTextColor(ContextCompat.getColor(holder.itemView.getContext(),
|
||||
android.R.color.holo_orange_dark));
|
||||
|
||||
Log.d("TransactionAdapter", "🔄 Starting comprehensive check for: " + t.referenceId);
|
||||
|
||||
// Check real status dari semua kemungkinan sources
|
||||
checkMidtransStatus(t.referenceId, holder.status);
|
||||
} else {
|
||||
// No reference ID to check
|
||||
holder.status.setText(displayStatus.toUpperCase());
|
||||
setStatusColor(holder.status, displayStatus);
|
||||
Log.w("TransactionAdapter", "⚠️ No reference ID for status check");
|
||||
}
|
||||
} else {
|
||||
// Use existing status yang sudah confirmed
|
||||
holder.status.setText(displayStatus.toUpperCase());
|
||||
setStatusColor(holder.status, displayStatus);
|
||||
Log.d("TransactionAdapter", "✅ Using confirmed status: " + displayStatus);
|
||||
}
|
||||
|
||||
// Set payment method
|
||||
String paymentMethod = getPaymentMethodName(t.channelCode, t.channelCategory);
|
||||
@ -85,12 +123,10 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
printClickListener.onPrintClick(t);
|
||||
}
|
||||
});
|
||||
|
||||
Log.d("TransactionAdapter", "✅ Transaction binding complete for: " + t.referenceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXED: Clean amount string to extract raw number correctly
|
||||
* Handles various input formats properly
|
||||
*/
|
||||
private String cleanAmountString(String amount) {
|
||||
if (amount == null || amount.isEmpty()) {
|
||||
return "0";
|
||||
@ -157,17 +193,22 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
int color;
|
||||
|
||||
if (statusLower.equals("failed") || statusLower.equals("failure") ||
|
||||
statusLower.equals("error") || statusLower.equals("declined")) {
|
||||
// Red for failed statuses
|
||||
statusLower.equals("error") || statusLower.equals("declined") ||
|
||||
statusLower.equals("expire") || statusLower.equals("cancel")) {
|
||||
// Red for failed/error statuses
|
||||
color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_red_dark);
|
||||
} else if (statusLower.equals("success") || statusLower.equals("paid") ||
|
||||
statusLower.equals("settlement") || statusLower.equals("completed")) {
|
||||
statusLower.equals("settlement") || statusLower.equals("completed") ||
|
||||
statusLower.equals("capture")) {
|
||||
// Green for successful statuses
|
||||
color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_green_dark);
|
||||
} else if (statusLower.equals("pending") || statusLower.equals("processing") ||
|
||||
statusLower.equals("waiting") || statusLower.equals("init")) {
|
||||
// Orange/Yellow for pending statuses
|
||||
statusLower.equals("waiting") || statusLower.equals("checking...")) {
|
||||
// Orange for pending/processing statuses
|
||||
color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_orange_dark);
|
||||
} else if (statusLower.equals("init")) {
|
||||
// Yellow for init status
|
||||
color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.holo_orange_light);
|
||||
} else {
|
||||
// Default gray for unknown statuses
|
||||
color = ContextCompat.getColor(statusTextView.getContext(), android.R.color.darker_gray);
|
||||
@ -176,6 +217,159 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
statusTextView.setTextColor(color);
|
||||
}
|
||||
|
||||
private void checkMidtransStatus(String referenceId, TextView statusTextView) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Log.d("TransactionAdapter", "🔍 Comprehensive status check for reference: " + referenceId);
|
||||
|
||||
// STEP 1: Query ALL webhook logs (tidak filter specific order_id)
|
||||
// Karena kita perlu cari semua order_id yang terkait dengan reference_id ini
|
||||
String queryUrl = "https://be-edc.msvc.app/api-logs?limit=100&sortOrder=DESC&sortColumn=created_at";
|
||||
|
||||
URL url = new URL(queryUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setConnectTimeout(10000);
|
||||
conn.setReadTimeout(10000);
|
||||
|
||||
if (conn.getResponseCode() == 200) {
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
StringBuilder response = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
response.append(line);
|
||||
}
|
||||
|
||||
JSONObject json = new JSONObject(response.toString());
|
||||
JSONArray results = json.optJSONArray("results");
|
||||
|
||||
String realStatus = "INIT"; // Default
|
||||
String foundOrderId = null;
|
||||
String foundTransactionStatus = "";
|
||||
|
||||
if (results != null && results.length() > 0) {
|
||||
Log.d("TransactionAdapter", "📊 Processing " + results.length() + " log entries");
|
||||
|
||||
// STEP 2: Comprehensive search untuk semua kemungkinan relasi
|
||||
for (int i = 0; i < results.length(); i++) {
|
||||
JSONObject log = results.getJSONObject(i);
|
||||
JSONObject reqBody = log.optJSONObject("request_body");
|
||||
|
||||
if (reqBody != null) {
|
||||
String logOrderId = reqBody.optString("order_id", "");
|
||||
String logTransactionStatus = reqBody.optString("transaction_status", "");
|
||||
String logReferenceId = reqBody.optString("reference_id", "");
|
||||
|
||||
// ✅ METHOD 1: Direct reference_id match
|
||||
boolean isDirectMatch = referenceId.equals(logReferenceId);
|
||||
|
||||
// ✅ METHOD 2: Check custom_field1 untuk QR refresh tracking
|
||||
boolean isRefreshMatch = false;
|
||||
String customField1 = reqBody.optString("custom_field1", "");
|
||||
if (!customField1.isEmpty()) {
|
||||
try {
|
||||
JSONObject customData = new JSONObject(customField1);
|
||||
String originalReference = customData.optString("original_reference", "");
|
||||
if (referenceId.equals(originalReference)) {
|
||||
isRefreshMatch = true;
|
||||
Log.d("TransactionAdapter", "🔄 Found refresh match: " + logOrderId +
|
||||
" (original ref: " + originalReference + ")");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// Ignore custom field parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ METHOD 3: Check item details untuk backup tracking
|
||||
boolean isItemMatch = false;
|
||||
JSONArray itemDetails = reqBody.optJSONArray("item_details");
|
||||
if (itemDetails != null && itemDetails.length() > 0) {
|
||||
for (int j = 0; j < itemDetails.length(); j++) {
|
||||
JSONObject item = itemDetails.optJSONObject(j);
|
||||
if (item != null) {
|
||||
String itemName = item.optString("name", "");
|
||||
if (itemName.contains("(Ref: " + referenceId + ")")) {
|
||||
isItemMatch = true;
|
||||
Log.d("TransactionAdapter", "📦 Found item match: " + logOrderId +
|
||||
" (item: " + itemName + ")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ COMPREHENSIVE MATCH: Any of the three methods
|
||||
boolean isRelatedTransaction = isDirectMatch || isRefreshMatch || isItemMatch;
|
||||
|
||||
if (isRelatedTransaction) {
|
||||
Log.d("TransactionAdapter", "🎯 MATCH FOUND!");
|
||||
Log.d("TransactionAdapter", " Order ID: " + logOrderId);
|
||||
Log.d("TransactionAdapter", " Status: " + logTransactionStatus);
|
||||
Log.d("TransactionAdapter", " Reference: " + logReferenceId);
|
||||
Log.d("TransactionAdapter", " Match Type: " +
|
||||
(isDirectMatch ? "DIRECT" : "") +
|
||||
(isRefreshMatch ? "REFRESH" : "") +
|
||||
(isItemMatch ? "ITEM" : ""));
|
||||
|
||||
// Priority check: settlement > capture > success > pending > init
|
||||
if (logTransactionStatus.equals("settlement") ||
|
||||
logTransactionStatus.equals("capture") ||
|
||||
logTransactionStatus.equals("success")) {
|
||||
realStatus = "PAID";
|
||||
foundOrderId = logOrderId;
|
||||
foundTransactionStatus = logTransactionStatus;
|
||||
Log.d("TransactionAdapter", "✅ PAYMENT CONFIRMED: " + logOrderId + " -> " + logTransactionStatus);
|
||||
break; // Found paid status, stop searching
|
||||
} else if (logTransactionStatus.equals("pending") && realStatus.equals("INIT")) {
|
||||
realStatus = "PENDING";
|
||||
foundOrderId = logOrderId;
|
||||
foundTransactionStatus = logTransactionStatus;
|
||||
Log.d("TransactionAdapter", "⏳ PENDING found: " + logOrderId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("TransactionAdapter", "🔍 FINAL RESULT for " + referenceId + ":");
|
||||
Log.d("TransactionAdapter", " Status: " + realStatus);
|
||||
Log.d("TransactionAdapter", " Order ID: " + (foundOrderId != null ? foundOrderId : "N/A"));
|
||||
Log.d("TransactionAdapter", " Midtrans Status: " + (foundTransactionStatus != null ? foundTransactionStatus : "N/A"));
|
||||
}
|
||||
|
||||
// STEP 3: Update UI di main thread
|
||||
final String finalStatus = realStatus;
|
||||
final String finalOrderId = foundOrderId;
|
||||
final String finalTransactionStatus = foundTransactionStatus;
|
||||
|
||||
statusTextView.post(() -> {
|
||||
statusTextView.setText(finalStatus);
|
||||
setStatusColor(statusTextView, finalStatus);
|
||||
|
||||
Log.d("TransactionAdapter", "🎨 UI UPDATED:");
|
||||
Log.d("TransactionAdapter", " Reference: " + referenceId);
|
||||
Log.d("TransactionAdapter", " Display Status: " + finalStatus);
|
||||
Log.d("TransactionAdapter", " Found Order: " + (finalOrderId != null ? finalOrderId : "N/A"));
|
||||
});
|
||||
|
||||
} else {
|
||||
Log.w("TransactionAdapter", "⚠️ API call failed with code: " + conn.getResponseCode());
|
||||
statusTextView.post(() -> {
|
||||
statusTextView.setText("ERROR");
|
||||
setStatusColor(statusTextView, "ERROR");
|
||||
});
|
||||
}
|
||||
|
||||
} catch (IOException | JSONException e) {
|
||||
Log.e("TransactionAdapter", "❌ Comprehensive status check error: " + e.getMessage(), e);
|
||||
statusTextView.post(() -> {
|
||||
statusTextView.setText("INIT");
|
||||
setStatusColor(statusTextView, "INIT");
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private String getPaymentMethodName(String channelCode, String channelCategory) {
|
||||
// Convert channel code to readable payment method name
|
||||
if (channelCode == null) return "Unknown";
|
||||
|
Loading…
x
Reference in New Issue
Block a user