transaction update
This commit is contained in:
parent
da8bcf17cc
commit
991f77dabe
@ -20,7 +20,9 @@ import java.util.Locale;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URI;
|
||||
|
||||
public class ReceiptActivity extends AppCompatActivity {
|
||||
|
||||
@ -286,10 +288,6 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
return fallbackPaymentMethod != null ? fallbackPaymentMethod : "QRIS";
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ ENHANCED: Dynamic acquirer detection from webhook data
|
||||
* This method tries to get the REAL acquirer instead of defaulting to GoPay
|
||||
*/
|
||||
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,7 +297,7 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
switch (acq) {
|
||||
// E-Wallet acquirers
|
||||
case "gopay": return "GoPay";
|
||||
case "shopeepay": return "ShopeePay";
|
||||
case "shopeepay": return "ShopeePay";
|
||||
case "ovo": return "OVO";
|
||||
case "dana": return "DANA";
|
||||
case "linkaja": return "LinkAja";
|
||||
@ -323,9 +321,6 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
case "bsi":
|
||||
case "bsi_va": return "BSI";
|
||||
case "maybank": return "Maybank";
|
||||
case "mega": return "Bank Mega";
|
||||
case "btn": return "BTN";
|
||||
case "bukopin": return "Bukopin";
|
||||
|
||||
// Credit card acquirers
|
||||
case "visa": return "Visa";
|
||||
@ -343,12 +338,6 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
case "kredivo": return "Kredivo";
|
||||
case "indodana": return "Indodana";
|
||||
|
||||
// Other acquirers
|
||||
case "emoney": return "E-Money";
|
||||
case "flazz": return "Flazz";
|
||||
case "tapcash": return "TapCash";
|
||||
case "brizzi": return "Brizzi";
|
||||
|
||||
default:
|
||||
// Return capitalized version of acquirer if not in mapping
|
||||
return capitalizeFirstLetter(acquirer);
|
||||
@ -361,11 +350,16 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
if (referenceId != null && !referenceId.isEmpty()) {
|
||||
Log.d("ReceiptActivity", "🔍 QRIS detected, fetching real acquirer for: " + referenceId);
|
||||
|
||||
// Try to get real acquirer from webhook data asynchronously
|
||||
fetchRealAcquirerFromWebhook(referenceId);
|
||||
// Try to get real acquirer from webhook data synchronously for initial load
|
||||
String realAcquirer = fetchRealAcquirerSync(referenceId);
|
||||
if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) {
|
||||
Log.d("ReceiptActivity", "✅ Found real acquirer synchronously: " + realAcquirer);
|
||||
return getCardTypeFromAcquirer(realAcquirer, null, null);
|
||||
}
|
||||
|
||||
// ✅ IMPROVED: Instead of defaulting to GoPay, show generic "QRIS" until real acquirer is found
|
||||
return "QRIS"; // Will be updated when real acquirer is found
|
||||
// If sync fetch failed, try async and show generic QRIS for now
|
||||
fetchRealAcquirerFromWebhook(referenceId);
|
||||
return "QRIS"; // ✅ FIXED: Show QRIS instead of defaulting to GoPay
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,7 +367,7 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
if (channelCode != null && !channelCode.isEmpty()) {
|
||||
String code = channelCode.toUpperCase();
|
||||
switch (code) {
|
||||
case "QRIS": return "QRIS"; // ✅ CHANGED: Generic QRIS instead of GoPay
|
||||
case "QRIS": return "QRIS"; // ✅ FIXED: Generic QRIS instead of GoPay
|
||||
case "DEBIT": return "Debit";
|
||||
case "CREDIT": return "Credit";
|
||||
case "BCA": return "BCA";
|
||||
@ -385,19 +379,97 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
}
|
||||
|
||||
// STEP 4: Final fallback
|
||||
return fallbackCardType != null ? fallbackCardType : "Unknown"; // ✅ CHANGED: Unknown instead of GoPay
|
||||
return fallbackCardType != null ? fallbackCardType : "Unknown"; // ✅ FIXED: Unknown instead of GoPay
|
||||
}
|
||||
|
||||
private String fetchRealAcquirerSync(String referenceId) {
|
||||
try {
|
||||
Log.d("ReceiptActivity", "🔍 Sync search for acquirer: " + referenceId);
|
||||
|
||||
String queryUrl = "https://be-edc.msvc.app/api-logs?limit=50&sortOrder=DESC&sortColumn=created_at";
|
||||
|
||||
URL url = new URL(queryUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setRequestProperty("User-Agent", "BDKIPOCApp/1.0");
|
||||
conn.setConnectTimeout(5000); // Short timeout for sync call
|
||||
conn.setReadTimeout(5000);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
org.json.JSONObject json = new org.json.JSONObject(response.toString());
|
||||
org.json.JSONArray results = json.optJSONArray("results");
|
||||
|
||||
if (results != null && results.length() > 0) {
|
||||
// Search for the most recent settlement/success transaction
|
||||
for (int i = 0; i < results.length(); i++) {
|
||||
org.json.JSONObject log = results.getJSONObject(i);
|
||||
org.json.JSONObject reqBody = log.optJSONObject("request_body");
|
||||
|
||||
if (reqBody != null) {
|
||||
String logReferenceId = reqBody.optString("reference_id", "");
|
||||
String logTransactionStatus = reqBody.optString("transaction_status", "");
|
||||
String logAcquirer = reqBody.optString("acquirer", "");
|
||||
String logIssuer = reqBody.optString("issuer", "");
|
||||
|
||||
// Check for direct reference match
|
||||
boolean isDirectMatch = referenceId.equals(logReferenceId);
|
||||
|
||||
// Check custom_field1 for refresh tracking
|
||||
boolean isRefreshMatch = false;
|
||||
String customField1 = reqBody.optString("custom_field1", "");
|
||||
if (!customField1.isEmpty()) {
|
||||
try {
|
||||
org.json.JSONObject customData = new org.json.JSONObject(customField1);
|
||||
String originalReference = customData.optString("original_reference", "");
|
||||
String appReferenceId = customData.optString("app_reference_id", "");
|
||||
if (referenceId.equals(originalReference) || referenceId.equals(appReferenceId)) {
|
||||
isRefreshMatch = true;
|
||||
}
|
||||
} catch (org.json.JSONException e) {
|
||||
// Ignore parsing errors
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this log matches our reference
|
||||
if (isDirectMatch || isRefreshMatch) {
|
||||
// Prioritize settlement/success status
|
||||
if (logTransactionStatus.equals("settlement") ||
|
||||
logTransactionStatus.equals("capture") ||
|
||||
logTransactionStatus.equals("success")) {
|
||||
|
||||
// Extract acquirer (prefer acquirer field over issuer)
|
||||
String foundAcquirer = !logAcquirer.isEmpty() ? logAcquirer : logIssuer;
|
||||
if (!foundAcquirer.isEmpty() && !foundAcquirer.equalsIgnoreCase("qris")) {
|
||||
Log.d("ReceiptActivity", "📋 Sync found acquirer: " + foundAcquirer + " (status: " + logTransactionStatus + ")");
|
||||
return foundAcquirer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("ReceiptActivity", "❌ Sync acquirer fetch error: " + e.getMessage());
|
||||
}
|
||||
|
||||
return null; // No acquirer found
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ ENHANCED: Fetch real acquirer from webhook data for QRIS transactions
|
||||
* This method searches webhook logs to find the actual acquirer used
|
||||
*/
|
||||
private void fetchRealAcquirerFromWebhook(String referenceId) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Log.d("ReceiptActivity", "🔍 Searching for real acquirer for reference: " + referenceId);
|
||||
Log.d("ReceiptActivity", "🔍 Async search for real acquirer: " + referenceId);
|
||||
|
||||
// Search webhook logs for this reference with broader search
|
||||
String queryUrl = "https://be-edc.msvc.app/api-logs?limit=100&sortOrder=DESC&sortColumn=created_at";
|
||||
|
||||
URL url = new URL(queryUrl);
|
||||
@ -423,7 +495,7 @@ public class ReceiptActivity extends AppCompatActivity {
|
||||
String realAcquirer = searchForRealAcquirer(results, referenceId);
|
||||
|
||||
if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) {
|
||||
Log.d("ReceiptActivity", "✅ Found real acquirer: " + realAcquirer + " for reference: " + referenceId);
|
||||
Log.d("ReceiptActivity", "✅ Async found real acquirer: " + realAcquirer + " for reference: " + referenceId);
|
||||
|
||||
// Update UI on main thread
|
||||
final String displayAcquirer = getCardTypeFromAcquirer(realAcquirer, null, null);
|
||||
|
@ -14,6 +14,7 @@ import androidx.core.content.ContextCompat;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream; // ✅ ADDED: Missing import
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
@ -211,14 +212,14 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
return "Rp. " + formatted;
|
||||
}
|
||||
|
||||
// ✅ FIXED: Enhanced status checking with comprehensive search
|
||||
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";
|
||||
// STEP 1: Query webhook logs untuk semua order_id yang terkait
|
||||
String queryUrl = "https://be-edc.msvc.app/api-logs?limit=200&sortOrder=DESC&sortColumn=created_at";
|
||||
|
||||
URL url = new URL(queryUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
@ -238,14 +239,14 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
JSONObject json = new JSONObject(response.toString());
|
||||
JSONArray results = json.optJSONArray("results");
|
||||
|
||||
String realStatus = "INIT"; // Default
|
||||
String finalStatus = "INIT"; // Default
|
||||
String foundOrderId = null;
|
||||
String foundTransactionStatus = "";
|
||||
String foundAcquirer = null;
|
||||
|
||||
if (results != null && results.length() > 0) {
|
||||
Log.d("TransactionAdapter", "📊 Processing " + results.length() + " log entries");
|
||||
|
||||
// STEP 2: Comprehensive search untuk semua kemungkinan relasi
|
||||
// STEP 2: Comprehensive search dengan multiple matching strategies
|
||||
for (int i = 0; i < results.length(); i++) {
|
||||
JSONObject log = results.getJSONObject(i);
|
||||
JSONObject reqBody = log.optJSONObject("request_body");
|
||||
@ -254,6 +255,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
String logOrderId = reqBody.optString("order_id", "");
|
||||
String logTransactionStatus = reqBody.optString("transaction_status", "");
|
||||
String logReferenceId = reqBody.optString("reference_id", "");
|
||||
String logAcquirer = reqBody.optString("acquirer", "");
|
||||
|
||||
// ✅ METHOD 1: Direct reference_id match
|
||||
boolean isDirectMatch = referenceId.equals(logReferenceId);
|
||||
@ -265,10 +267,10 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
try {
|
||||
JSONObject customData = new JSONObject(customField1);
|
||||
String originalReference = customData.optString("original_reference", "");
|
||||
if (referenceId.equals(originalReference)) {
|
||||
String appReferenceId = customData.optString("app_reference_id", "");
|
||||
if (referenceId.equals(originalReference) || referenceId.equals(appReferenceId)) {
|
||||
isRefreshMatch = true;
|
||||
Log.d("TransactionAdapter", "🔄 Found refresh match: " + logOrderId +
|
||||
" (original ref: " + originalReference + ")");
|
||||
Log.d("TransactionAdapter", "🔄 Found refresh match: " + logOrderId);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// Ignore custom field parsing errors
|
||||
@ -283,10 +285,10 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
JSONObject item = itemDetails.optJSONObject(j);
|
||||
if (item != null) {
|
||||
String itemName = item.optString("name", "");
|
||||
if (itemName.contains("(Ref: " + referenceId + ")")) {
|
||||
if (itemName.contains("(Ref: " + referenceId + ")") ||
|
||||
itemName.contains("- " + referenceId)) {
|
||||
isItemMatch = true;
|
||||
Log.d("TransactionAdapter", "📦 Found item match: " + logOrderId +
|
||||
" (item: " + itemName + ")");
|
||||
Log.d("TransactionAdapter", "📦 Found item match: " + logOrderId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -300,52 +302,63 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
Log.d("TransactionAdapter", "🎯 MATCH FOUND!");
|
||||
Log.d("TransactionAdapter", " Order ID: " + logOrderId);
|
||||
Log.d("TransactionAdapter", " Status: " + logTransactionStatus);
|
||||
Log.d("TransactionAdapter", " Reference: " + logReferenceId);
|
||||
Log.d("TransactionAdapter", " Acquirer: " + logAcquirer);
|
||||
Log.d("TransactionAdapter", " Match Type: " +
|
||||
(isDirectMatch ? "DIRECT" : "") +
|
||||
(isRefreshMatch ? "REFRESH" : "") +
|
||||
(isDirectMatch ? "DIRECT " : "") +
|
||||
(isRefreshMatch ? "REFRESH " : "") +
|
||||
(isItemMatch ? "ITEM" : ""));
|
||||
|
||||
// Priority check: settlement > capture > success > pending > init
|
||||
// ✅ PRIORITY SYSTEM: settlement > capture > success > pending > init
|
||||
if (logTransactionStatus.equals("settlement") ||
|
||||
logTransactionStatus.equals("capture") ||
|
||||
logTransactionStatus.equals("success")) {
|
||||
realStatus = "PAID";
|
||||
finalStatus = "PAID";
|
||||
foundOrderId = logOrderId;
|
||||
foundTransactionStatus = logTransactionStatus;
|
||||
foundAcquirer = logAcquirer;
|
||||
Log.d("TransactionAdapter", "✅ PAYMENT CONFIRMED: " + logOrderId + " -> " + logTransactionStatus);
|
||||
break; // Found paid status, stop searching
|
||||
} else if (logTransactionStatus.equals("pending") && realStatus.equals("INIT")) {
|
||||
realStatus = "PENDING";
|
||||
} else if (logTransactionStatus.equals("pending") && finalStatus.equals("INIT")) {
|
||||
finalStatus = "PENDING";
|
||||
foundOrderId = logOrderId;
|
||||
foundTransactionStatus = logTransactionStatus;
|
||||
foundAcquirer = logAcquirer;
|
||||
Log.d("TransactionAdapter", "⏳ PENDING found: " + logOrderId);
|
||||
} else if (logTransactionStatus.equals("expire") || logTransactionStatus.equals("cancel")) {
|
||||
if (finalStatus.equals("INIT")) { // Only update if no better status found
|
||||
finalStatus = "FAILED";
|
||||
foundOrderId = logOrderId;
|
||||
foundAcquirer = logAcquirer;
|
||||
Log.d("TransactionAdapter", "❌ FAILED status: " + logOrderId + " -> " + logTransactionStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d("TransactionAdapter", "🔍 FINAL RESULT for " + referenceId + ":");
|
||||
Log.d("TransactionAdapter", " Status: " + realStatus);
|
||||
Log.d("TransactionAdapter", " Status: " + finalStatus);
|
||||
Log.d("TransactionAdapter", " Order ID: " + (foundOrderId != null ? foundOrderId : "N/A"));
|
||||
Log.d("TransactionAdapter", " Midtrans Status: " + (foundTransactionStatus != null ? foundTransactionStatus : "N/A"));
|
||||
Log.d("TransactionAdapter", " Acquirer: " + (foundAcquirer != null ? foundAcquirer : "N/A"));
|
||||
}
|
||||
|
||||
// STEP 3: Update UI di main thread
|
||||
final String finalStatus = realStatus;
|
||||
final String finalOrderId = foundOrderId;
|
||||
final String finalTransactionStatus = foundTransactionStatus;
|
||||
final String displayStatus = finalStatus;
|
||||
final String detectedAcquirer = foundAcquirer;
|
||||
|
||||
statusTextView.post(() -> {
|
||||
statusTextView.setText(finalStatus);
|
||||
StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), finalStatus);
|
||||
statusTextView.setText(displayStatus);
|
||||
StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), displayStatus);
|
||||
|
||||
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"));
|
||||
Log.d("TransactionAdapter", " Display Status: " + displayStatus);
|
||||
Log.d("TransactionAdapter", " Detected Acquirer: " + (detectedAcquirer != null ? detectedAcquirer : "Unknown"));
|
||||
});
|
||||
|
||||
// ✅ BONUS: Update backend jika status berubah ke PAID
|
||||
if (finalStatus.equals("PAID")) {
|
||||
updateBackendTransactionStatus(referenceId, finalStatus, foundOrderId, detectedAcquirer);
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.w("TransactionAdapter", "⚠️ API call failed with code: " + conn.getResponseCode());
|
||||
statusTextView.post(() -> {
|
||||
@ -364,6 +377,57 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ NEW METHOD: Update backend transaction status when payment confirmed
|
||||
*/
|
||||
private void updateBackendTransactionStatus(String referenceId, String status, String orderId, String acquirer) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Log.d("TransactionAdapter", "🔄 Updating backend status for reference: " + referenceId);
|
||||
|
||||
JSONObject updatePayload = new JSONObject();
|
||||
updatePayload.put("status", status);
|
||||
updatePayload.put("payment_status", status);
|
||||
updatePayload.put("paid_order_id", orderId);
|
||||
updatePayload.put("detected_acquirer", acquirer);
|
||||
updatePayload.put("updated_at", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));
|
||||
updatePayload.put("settlement_time", new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new Date()));
|
||||
|
||||
String updateUrl = "https://be-edc.msvc.app/transactions/update-by-reference";
|
||||
URL url = new URL(updateUrl);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
conn.setRequestProperty("User-Agent", "BDKIPOCApp/1.0");
|
||||
conn.setDoOutput(true);
|
||||
conn.setConnectTimeout(15000);
|
||||
conn.setReadTimeout(15000);
|
||||
|
||||
JSONObject requestBody = new JSONObject();
|
||||
requestBody.put("reference_id", referenceId);
|
||||
requestBody.put("update_data", updatePayload);
|
||||
|
||||
try (OutputStream os = conn.getOutputStream()) {
|
||||
byte[] input = requestBody.toString().getBytes("utf-8");
|
||||
os.write(input, 0, input.length);
|
||||
}
|
||||
|
||||
int responseCode = conn.getResponseCode();
|
||||
Log.d("TransactionAdapter", "📥 Backend update response: " + responseCode);
|
||||
|
||||
if (responseCode == 200 || responseCode == 201) {
|
||||
Log.d("TransactionAdapter", "✅ Backend status updated successfully");
|
||||
} else {
|
||||
Log.e("TransactionAdapter", "❌ Backend update failed: " + responseCode);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("TransactionAdapter", "❌ Backend update error: " + e.getMessage(), e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Format created_at date to readable format
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user