diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9027cba..2e7dcfd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -71,9 +71,17 @@ + + + + + diff --git a/app/src/main/java/com/example/bdkipoc/MainActivity.java b/app/src/main/java/com/example/bdkipoc/MainActivity.java index b5f704e..4c04cae 100644 --- a/app/src/main/java/com/example/bdkipoc/MainActivity.java +++ b/app/src/main/java/com/example/bdkipoc/MainActivity.java @@ -19,6 +19,8 @@ import androidx.core.view.WindowInsetsCompat; import com.google.android.material.button.MaterialButton; +import com.example.bdkipoc.kredit.CreateTransactionActivity; +import com.example.bdkipoc.kredit.EmvTransactionActivity; import com.example.bdkipoc.kredit.CreditCardActivity; public class MainActivity extends AppCompatActivity { @@ -160,7 +162,7 @@ public class MainActivity extends AppCompatActivity { if (cardView != null) { cardView.setOnClickListener(v -> { if (cardId == R.id.card_kartu_kredit) { - startActivity(new Intent(MainActivity.this, CreditCardActivity.class)); + startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class)); } else if (cardId == R.id.card_kartu_debit) { startActivity(new Intent(MainActivity.this, PaymentActivity.class)); } else if (cardId == R.id.card_qris) { diff --git a/app/src/main/java/com/example/bdkipoc/kredit/CreateTransactionActivity.java b/app/src/main/java/com/example/bdkipoc/kredit/CreateTransactionActivity.java new file mode 100644 index 0000000..cc8f56b --- /dev/null +++ b/app/src/main/java/com/example/bdkipoc/kredit/CreateTransactionActivity.java @@ -0,0 +1,179 @@ +package com.example.bdkipoc.kredit; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import com.example.bdkipoc.R; + +import java.text.NumberFormat; +import java.util.Locale; + +/** + * CreateTransactionActivity - Handle amount input and transaction creation + */ +public class CreateTransactionActivity extends AppCompatActivity { + private static final String TAG = "CreateTransaction"; + + // UI Components + private TextView tvAmountDisplay; + private TextView tvModeIndicator; + private Button btnConfirm; + private Button btnToggleMode; + + // Amount Input Keypad + private Button btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btn00, btnClear; + + // State Management + private String transactionAmount = "0"; + private boolean isEMVMode = true; // Default to EMV mode + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_create_transaction); + + initViews(); + setupListeners(); + updateAmountDisplay(); + updateModeDisplay(); + } + + private void initViews() { + // Setup Toolbar + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle("Input Nominal Transaksi"); + } + + // Initialize UI components + tvAmountDisplay = findViewById(R.id.tv_amount_display); + tvModeIndicator = findViewById(R.id.tv_mode_indicator); + btnConfirm = findViewById(R.id.btn_confirm); + btnToggleMode = findViewById(R.id.btn_toggle_mode); + + // Initialize keypad buttons + btn1 = findViewById(R.id.btn_1); + btn2 = findViewById(R.id.btn_2); + btn3 = findViewById(R.id.btn_3); + btn4 = findViewById(R.id.btn_4); + btn5 = findViewById(R.id.btn_5); + btn6 = findViewById(R.id.btn_6); + btn7 = findViewById(R.id.btn_7); + btn8 = findViewById(R.id.btn_8); + btn9 = findViewById(R.id.btn_9); + btn0 = findViewById(R.id.btn_0); + btn00 = findViewById(R.id.btn_00); + btnClear = findViewById(R.id.btn_clear); + } + + private void setupListeners() { + // Keypad number listeners + View.OnClickListener numberClickListener = v -> { + Button btn = (Button) v; + String number = btn.getText().toString(); + appendToAmount(number); + }; + + btn1.setOnClickListener(numberClickListener); + btn2.setOnClickListener(numberClickListener); + btn3.setOnClickListener(numberClickListener); + btn4.setOnClickListener(numberClickListener); + btn5.setOnClickListener(numberClickListener); + btn6.setOnClickListener(numberClickListener); + btn7.setOnClickListener(numberClickListener); + btn8.setOnClickListener(numberClickListener); + btn9.setOnClickListener(numberClickListener); + btn0.setOnClickListener(numberClickListener); + btn00.setOnClickListener(numberClickListener); + + // Clear button + btnClear.setOnClickListener(v -> clearAmount()); + + // Confirm button + btnConfirm.setOnClickListener(v -> handleConfirmAmount()); + + // Toggle mode button + btnToggleMode.setOnClickListener(v -> toggleEMVMode()); + } + + private void appendToAmount(String number) { + // Remove leading zeros and handle maximum amount + String currentAmount = transactionAmount.equals("0") ? "" : transactionAmount; + String newAmount = currentAmount + number; + + // Limit to reasonable amount (max 999999999 = 9,999,999.99) + if (newAmount.length() <= 9) { + transactionAmount = newAmount; + updateAmountDisplay(); + } + } + + private void clearAmount() { + if (transactionAmount.length() > 1) { + transactionAmount = transactionAmount.substring(0, transactionAmount.length() - 1); + } else { + transactionAmount = "0"; + } + updateAmountDisplay(); + } + + private void updateAmountDisplay() { + if (tvAmountDisplay != null) { + long amountCents = Long.parseLong(transactionAmount); + double amountRupiah = amountCents / 100.0; + NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID")); + String formattedAmount = formatter.format(amountRupiah); + tvAmountDisplay.setText(formattedAmount); + } + } + + private void handleConfirmAmount() { + long amountCents = Long.parseLong(transactionAmount); + if (amountCents < 100) { // Minimum Rp 1.00 + showToast("Minimum amount is Rp 1.00"); + return; + } + + // Start EMV Transaction Activity + Intent intent = new Intent(this, EmvTransactionActivity.class); + intent.putExtra("TRANSACTION_AMOUNT", transactionAmount); + intent.putExtra("EMV_MODE", isEMVMode); + startActivity(intent); + finish(); // Remove this activity from stack + } + + private void toggleEMVMode() { + isEMVMode = !isEMVMode; + updateModeDisplay(); + showToast("Mode switched to: " + (isEMVMode ? "EMV Mode" : "Simple Mode")); + } + + private void updateModeDisplay() { + String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)"; + if (tvModeIndicator != null) { + tvModeIndicator.setText("Current Mode: " + mode); + } + if (btnToggleMode != null) { + btnToggleMode.setText(isEMVMode ? "Switch to Simple" : "Switch to EMV"); + } + } + + private void showToast(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java b/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java index ba3c26c..2318b79 100644 --- a/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java +++ b/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java @@ -3,20 +3,16 @@ package com.example.bdkipoc.kredit; import android.content.ClipboardManager; import android.content.ClipData; import android.content.Context; +import android.content.Intent; import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.os.Message; import android.os.RemoteException; import android.text.TextUtils; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; -import android.widget.EditText; -import android.view.View; -import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import com.example.bdkipoc.MyApplication; import com.example.bdkipoc.R; @@ -24,1010 +20,102 @@ import com.example.bdkipoc.utils.ByteUtil; import com.example.bdkipoc.utils.Utility; import com.sunmi.emv.l2.utils.iso8583.TLV; import com.sunmi.emv.l2.utils.iso8583.TLVUtils; -import com.sunmi.pay.hardware.aidl.AidlConstants.CardType; -import com.sunmi.pay.hardware.aidl.bean.CardInfo; import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2; -import com.sunmi.pay.hardware.aidlv2.AidlErrorCodeV2; -import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2; -import com.sunmi.pay.hardware.aidlv2.bean.PinPadConfigV2; -import com.sunmi.pay.hardware.aidlv2.emv.EMVListenerV2; import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2; -import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadListenerV2; -import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2; -import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2; +import java.text.NumberFormat; import java.util.Arrays; -import java.util.List; +import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.text.NumberFormat; -import java.util.Locale; /** - * Enhanced CreditCardActivity with EMV capabilities and Amount Input + * CreditCardActivity - Display detailed transaction results and TLV data */ public class CreditCardActivity extends AppCompatActivity { private static final String TAG = "CreditCard"; - // UI State Constants - private static final int STATE_INPUT_AMOUNT = 0; - private static final int STATE_SCANNING = 1; - private static final int STATE_EMV_PROCESS = 2; - // UI Components - private TextView tvResult; - private TextView tvModeIndicator; - private TextView tvAmountDisplay; - private EditText etAmountInput; - private Button btnCheckCard; - private Button btnToggleMode; + private TextView tvTransactionSummary; + private TextView tvCardData; private Button btnCopyData; - private Button btnClearData; + private Button btnNewTransaction; + private Button btnPrintReceipt; - // Amount Input Keypad - private Button btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btn00, btnClear; - private View layoutAmountInput; - private View layoutCardResult; - - // State Management - private int currentState = STATE_INPUT_AMOUNT; - private boolean checkingCard = false; - private String transactionAmount = "0"; + // Transaction Data + private String transactionAmount; + private String cardType; + private boolean isEMVMode; + private String cardNo; + private Bundle cardData; // EMV Components private EMVOptV2 mEMVOptV2; - private PinPadOptV2 mPinPadOptV2; - - // EMV Process Variables - private int mCardType; - private String mCardNo; - private int mPinType; - private String mCertInfo; - private int mSelectIndex; - private int mProcessStep; - private AlertDialog mAppSelectDialog; - private boolean isEMVMode = false; - - // EMV Process Constants - private static final int EMV_APP_SELECT = 1; - private static final int EMV_FINAL_APP_SELECT = 2; - private static final int EMV_CONFIRM_CARD_NO = 3; - private static final int EMV_CERT_VERIFY = 4; - private static final int EMV_SHOW_PIN_PAD = 5; - private static final int EMV_ONLINE_PROCESS = 6; - private static final int EMV_SIGNATURE = 7; - private static final int EMV_TRANS_SUCCESS = 888; - private static final int EMV_TRANS_FAIL = 999; - - private static final int PIN_CLICK_NUMBER = 50; - private static final int PIN_CLICK_PIN = 51; - private static final int PIN_CLICK_CONFIRM = 52; - private static final int PIN_CLICK_CANCEL = 53; - private static final int PIN_ERROR = 54; - - private final Handler mHandler = new Handler(Looper.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - super.handleMessage(msg); - switch (msg.what) { - case EMV_FINAL_APP_SELECT: - importFinalAppSelectStatus(0); - break; - - case EMV_APP_SELECT: - String[] candiNames = (String[]) msg.obj; - showAppSelectDialog(candiNames); - break; - - case EMV_CONFIRM_CARD_NO: - showCardNumberConfirmation(); - break; - - case EMV_CERT_VERIFY: - showCertificateVerification(); - break; - - case EMV_SHOW_PIN_PAD: - dismissLoadingDialog(); - android.util.Log.d(TAG, "Initializing PIN pad..."); - initPinPad(); - break; - - case EMV_ONLINE_PROCESS: - mockOnlineProcess(); - break; - - case EMV_SIGNATURE: - importSignatureStatus(0); - break; - - case PIN_CLICK_PIN: - android.util.Log.d(TAG, "PIN input confirmed"); - importPinInputStatus(0); - break; - - case PIN_CLICK_CONFIRM: - android.util.Log.d(TAG, "PIN confirm without input (bypass)"); - importPinInputStatus(2); - break; - - case PIN_CLICK_CANCEL: - showToast("PIN input cancelled by user"); - importPinInputStatus(1); - break; - - case PIN_ERROR: - android.util.Log.e(TAG, "PIN Error: " + msg.obj + " -- " + msg.arg1); - showToast("PIN Error: " + msg.obj); - importPinInputStatus(3); - break; - - case EMV_TRANS_FAIL: - resetToAmountInput(); - showToast("Transaction failed: " + msg.obj + " -- " + msg.arg1); - break; - - case EMV_TRANS_SUCCESS: - getTlvData(); // Get complete card data with amount - showToast("Transaction successful!"); - break; - } - } - }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - android.util.Log.d(TAG, "onCreate called"); setContentView(R.layout.activity_credit_card); + getIntentData(); + initViews(); initEMVComponents(); - initView(); - initEMVData(); - checkPaySDKStatus(); - updateUIForCurrentState(); + loadCardData(); + } + + private void getIntentData() { + Intent intent = getIntent(); + transactionAmount = intent.getStringExtra("TRANSACTION_AMOUNT"); + cardType = intent.getStringExtra("CARD_TYPE"); + isEMVMode = intent.getBooleanExtra("EMV_MODE", true); + cardNo = intent.getStringExtra("CARD_NO"); + cardData = intent.getBundleExtra("CARD_DATA"); + + if (transactionAmount == null) { + transactionAmount = "0"; + } + } + + private void initViews() { + // Setup Toolbar + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle("Detail Transaksi"); + } + + tvTransactionSummary = findViewById(R.id.tv_transaction_summary); + tvCardData = findViewById(R.id.tv_card_data); + btnCopyData = findViewById(R.id.btn_copy_data); + btnNewTransaction = findViewById(R.id.btn_new_transaction); + btnPrintReceipt = findViewById(R.id.btn_print_receipt); + + btnCopyData.setOnClickListener(v -> copyCardDataToClipboard()); + btnNewTransaction.setOnClickListener(v -> startNewTransaction()); + btnPrintReceipt.setOnClickListener(v -> printReceipt()); } private void initEMVComponents() { if (MyApplication.app != null) { mEMVOptV2 = MyApplication.app.emvOptV2; - mPinPadOptV2 = MyApplication.app.pinPadOptV2; - android.util.Log.d(TAG, "EMV components initialized"); + android.util.Log.d(TAG, "EMV components initialized for TLV data retrieval"); + } + } + + private void loadCardData() { + if (isEMVMode && mEMVOptV2 != null) { + loadEMVTlvData(); } else { - android.util.Log.e(TAG, "MyApplication.app is null"); + loadSimpleCardData(); } } - private void initEMVData() { - try { - if (mEMVOptV2 != null) { - mEMVOptV2.initEmvProcess(); - initEmvTlvData(); - android.util.Log.d(TAG, "EMV data initialized successfully"); - } - } catch (Exception e) { - android.util.Log.e(TAG, "Error initializing EMV data: " + e.getMessage()); - e.printStackTrace(); - } - } - - private void initEmvTlvData() { - try { - // Set PayPass (MasterCard) TLV data - String[] tagsPayPass = {"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C", - "DF8123", "DF8124", "DF8125", "DF8126", "DF811B", "DF811D", "DF8122", "DF8120", "DF8121"}; - String[] valuesPayPass = {"E0", "F8", "F8", "E8", "00", "00", - "000000000000", "000000100000", "999999999999", "000000100000", - "30", "02", "0000000000", "000000000000", "000000000000"}; - mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass); - - // Set AMEX (American Express) TLV data - String[] tagsAE = {"9F6D", "9F6E", "9F33", "9F35", "DF8168", "DF8167", "DF8169", "DF8170"}; - String[] valuesAE = {"C0", "D8E00000", "E0E888", "22", "00", "00", "00", "60"}; - mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_AE, tagsAE, valuesAE); - - // Set JCB TLV data - String[] tagsJCB = {"9F53", "DF8161"}; - String[] valuesJCB = {"708000", "7F00"}; - mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_JCB, tagsJCB, valuesJCB); - - // Set DPAS TLV data - String[] tagsDPAS = {"9F66"}; - String[] valuesDPAS = {"B600C000"}; - mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_DPAS, tagsDPAS, valuesDPAS); - - // Set Flash TLV data - String[] tagsFLASH = {"9F58", "9F59", "9F5A", "9F5D", "9F5E"}; - String[] valuesFLASH = {"03", "D88700", "00", "000000000000", "E000"}; - mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_FLASH, tagsFLASH, valuesFLASH); - - // Set Pure TLV data - String[] tagsPURE = {"DF7F", "DF8134", "DF8133"}; - String[] valuesPURE = {"A0000007271010", "DF", "36006043F9"}; - mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PURE, tagsPURE, valuesPURE); - - Bundle bundle = new Bundle(); - bundle.putBoolean("optOnlineRes", true); - mEMVOptV2.setTermParamEx(bundle); - - } catch (RemoteException e) { - android.util.Log.e(TAG, "Error setting EMV TLV data: " + e.getMessage()); - e.printStackTrace(); - } - } - - private void checkPaySDKStatus() { - if (MyApplication.app != null) { - android.util.Log.d(TAG, "MyApplication.app exists"); - android.util.Log.d(TAG, "PaySDK connected: " + MyApplication.app.isConnectPaySDK()); - android.util.Log.d(TAG, "readCardOptV2 null: " + (MyApplication.app.readCardOptV2 == null)); - android.util.Log.d(TAG, "emvOptV2 null: " + (MyApplication.app.emvOptV2 == null)); - } else { - android.util.Log.e(TAG, "MyApplication.app is null"); - } - } - - private void initView() { - android.util.Log.d(TAG, "initView called"); - - // Setup Toolbar as ActionBar - androidx.appcompat.widget.Toolbar toolbar = findViewById(R.id.toolbar); - if (toolbar != null) { - setSupportActionBar(toolbar); - if (getSupportActionBar() != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setTitle("Enhanced Card Reader with EMV"); - } - } - - // Initialize UI components - tvResult = findViewById(R.id.tv_result); - tvModeIndicator = findViewById(R.id.tv_mode_indicator); - tvAmountDisplay = findViewById(R.id.tv_amount_display); - etAmountInput = findViewById(R.id.et_amount_input); - btnCheckCard = findViewById(R.id.btn_check_card); - btnToggleMode = findViewById(R.id.btn_toggle_mode); - btnCopyData = findViewById(R.id.btn_copy_data); - btnClearData = findViewById(R.id.btn_clear_data); - - // Initialize layouts - layoutAmountInput = findViewById(R.id.layout_amount_input); - layoutCardResult = findViewById(R.id.layout_card_result); - - // Initialize keypad buttons - btn1 = findViewById(R.id.btn_1); - btn2 = findViewById(R.id.btn_2); - btn3 = findViewById(R.id.btn_3); - btn4 = findViewById(R.id.btn_4); - btn5 = findViewById(R.id.btn_5); - btn6 = findViewById(R.id.btn_6); - btn7 = findViewById(R.id.btn_7); - btn8 = findViewById(R.id.btn_8); - btn9 = findViewById(R.id.btn_9); - btn0 = findViewById(R.id.btn_0); - btn00 = findViewById(R.id.btn_00); - btnClear = findViewById(R.id.btn_clear); - - setupKeypadListeners(); - setupButtonListeners(); - - // Initialize amount display - updateAmountDisplay(); - } - - private void setupKeypadListeners() { - View.OnClickListener numberClickListener = v -> { - Button btn = (Button) v; - String number = btn.getText().toString(); - appendToAmount(number); - }; - - btn1.setOnClickListener(numberClickListener); - btn2.setOnClickListener(numberClickListener); - btn3.setOnClickListener(numberClickListener); - btn4.setOnClickListener(numberClickListener); - btn5.setOnClickListener(numberClickListener); - btn6.setOnClickListener(numberClickListener); - btn7.setOnClickListener(numberClickListener); - btn8.setOnClickListener(numberClickListener); - btn9.setOnClickListener(numberClickListener); - btn0.setOnClickListener(numberClickListener); - btn00.setOnClickListener(numberClickListener); - - btnClear.setOnClickListener(v -> clearAmount()); - } - - private void setupButtonListeners() { - if (btnToggleMode != null) { - btnToggleMode.setOnClickListener(v -> toggleEMVMode()); - } - - if (btnCheckCard != null) { - btnCheckCard.setOnClickListener(v -> handleMainButtonClick()); - } - - if (btnCopyData != null) { - btnCopyData.setOnClickListener(v -> copyCardDataToClipboard()); - } - - if (btnClearData != null) { - btnClearData.setOnClickListener(v -> clearCardData()); - } - } - - private void appendToAmount(String number) { - if (currentState != STATE_INPUT_AMOUNT) return; - - // Remove leading zeros and handle maximum amount - String currentAmount = transactionAmount.equals("0") ? "" : transactionAmount; - String newAmount = currentAmount + number; - - // Limit to reasonable amount (max 999999999 = 9,999,999.99) - if (newAmount.length() <= 9) { - transactionAmount = newAmount; - updateAmountDisplay(); - } - } - - private void clearAmount() { - if (currentState != STATE_INPUT_AMOUNT) return; - - if (transactionAmount.length() > 1) { - transactionAmount = transactionAmount.substring(0, transactionAmount.length() - 1); - } else { - transactionAmount = "0"; - } - updateAmountDisplay(); - } - - private void updateAmountDisplay() { - if (tvAmountDisplay != null) { - long amountCents = Long.parseLong(transactionAmount); - double amountRupiah = amountCents / 100.0; - NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID")); - String formattedAmount = formatter.format(amountRupiah); - tvAmountDisplay.setText(formattedAmount); - } - - if (etAmountInput != null) { - etAmountInput.setText(transactionAmount); - } - } - - private void handleMainButtonClick() { - switch (currentState) { - case STATE_INPUT_AMOUNT: - handleConfirmAmount(); - break; - case STATE_SCANNING: - handleScanAction(); - break; - case STATE_EMV_PROCESS: - handleEMVAction(); - break; - } - } - - private void handleConfirmAmount() { - long amountCents = Long.parseLong(transactionAmount); - if (amountCents < 100) { // Minimum Rp 1.00 - showToast("Minimum amount is Rp 1.00"); - return; - } - - // Transition to scanning state - currentState = STATE_SCANNING; - updateUIForCurrentState(); - - android.util.Log.d(TAG, "Amount confirmed: " + transactionAmount + " cents"); - showToast("Amount confirmed: " + formatAmount(amountCents)); - } - - private void handleScanAction() { - if (mProcessStep == 0) { - if (checkingCard) { - stopCardCheck(); - } else { - startCardCheck(); - } - } else if (mProcessStep == EMV_CONFIRM_CARD_NO) { - android.util.Log.d(TAG, "User confirmed card number"); - showLoadingDialog("Processing card confirmation..."); - btnCheckCard.setText("Processing..."); - importCardNoStatus(0); - } else if (mProcessStep == EMV_CERT_VERIFY) { - android.util.Log.d(TAG, "User confirmed certificate"); - showLoadingDialog("Processing certificate..."); - btnCheckCard.setText("Processing..."); - importCertStatus(0); - } - } - - private void handleEMVAction() { - // Handle EMV process actions (same as before) - android.util.Log.d(TAG, "EMV action in process step: " + mProcessStep); - } - - private void updateUIForCurrentState() { - runOnUiThread(() -> { - switch (currentState) { - case STATE_INPUT_AMOUNT: - showAmountInput(); - break; - case STATE_SCANNING: - showScanningUI(); - break; - case STATE_EMV_PROCESS: - // UI handled by EMV process - break; - } - }); - } - - private void showAmountInput() { - layoutAmountInput.setVisibility(View.VISIBLE); - layoutCardResult.setVisibility(View.GONE); - - btnCheckCard.setText("Konfirmasi"); - btnCheckCard.setEnabled(true); - - if (tvResult != null) { - tvResult.setText("Masukkan nominal pembayaran\nGunakan keypad di bawah untuk memasukkan nominal"); - } - - updateModeDisplay(); - updateAmountDisplay(); - } - - private void showScanningUI() { - layoutAmountInput.setVisibility(View.GONE); - layoutCardResult.setVisibility(View.VISIBLE); - - btnCheckCard.setText("Start Scanning"); - btnCheckCard.setEnabled(true); - - if (tvResult != null) { - String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)"; - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nReady to scan card...\nCurrent Mode: " + mode + - "\nPlace your card on the reader"); - } - - updateModeDisplay(); - } - - private void toggleEMVMode() { - isEMVMode = !isEMVMode; - updateModeDisplay(); - android.util.Log.d(TAG, "EMV Mode toggled to: " + isEMVMode); - - if (btnToggleMode == null) { - showToast("Mode switched to: " + (isEMVMode ? "EMV Mode" : "Simple Mode")); - } - } - - private void updateModeDisplay() { - String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)"; - - if (tvModeIndicator != null) { - tvModeIndicator.setText("Current Mode: " + mode); - } - } - - private String formatAmount(long amountCents) { - double amountRupiah = amountCents / 100.0; - NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID")); - return formatter.format(amountRupiah); - } - - // ====== CARD SCANNING METHODS (Same as before) ====== - private void startCardCheck() { - try { - if (isEMVMode) { - startEMVCardCheck(); - } else { - startSimpleCardCheck(); - } - checkingCard = true; - btnCheckCard.setText("Stop Scanning"); - currentState = STATE_EMV_PROCESS; - } catch (Exception e) { - android.util.Log.e(TAG, "Error starting card check: " + e.getMessage()); - updateUI("Error starting card check: " + e.getMessage(), false); - } - } - - private void startSimpleCardCheck() { - try { - if (!validateApplicationState()) { - return; - } - - if (!MyApplication.app.isConnectPaySDK()) { - tvResult.setText("Connecting to PaySDK..."); - android.util.Log.d(TAG, "PaySDK not connected, binding service..."); - MyApplication.app.bindPaySDKService(); - - btnCheckCard.postDelayed(() -> { - if (MyApplication.app.isConnectPaySDK() && MyApplication.app.readCardOptV2 != null) { - startSimpleCardScan(); - } else { - updateUI("Error: Failed to connect to PaySDK", false); - } - }, 2000); - return; - } - - if (MyApplication.app.readCardOptV2 == null) { - updateUI("Error: Card reader not initialized", false); - android.util.Log.e(TAG, "readCardOptV2 is null"); - return; - } - - startSimpleCardScan(); - - } catch (Exception e) { - e.printStackTrace(); - android.util.Log.e(TAG, "Error in startSimpleCardCheck: " + e.getMessage()); - updateUI("Error starting card scan: " + e.getMessage(), false); - } - } - - private void startEMVCardCheck() { - try { - if (!validateApplicationState()) { - return; - } - - if (mEMVOptV2 == null) { - updateUI("Error: EMV not initialized", false); - android.util.Log.e(TAG, "EMV not initialized"); - return; - } - - mEMVOptV2.initEmvProcess(); - initEmvTlvData(); - - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nEMV Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); - - int cardType = AidlConstantsV2.CardType.NFC.getValue() | AidlConstantsV2.CardType.IC.getValue(); - android.util.Log.d(TAG, "Starting EMV checkCard with cardType: " + cardType); - - MyApplication.app.readCardOptV2.checkCard(cardType, mEMVCheckCardCallback, 60); - - } catch (Exception e) { - e.printStackTrace(); - android.util.Log.e(TAG, "Error in startEMVCardCheck: " + e.getMessage()); - updateUI("Error starting EMV card scan: " + e.getMessage(), false); - } - } - - private void startSimpleCardScan() { - try { - int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue(); - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nSimple Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); - - android.util.Log.d(TAG, "Starting simple checkCard with cardType: " + cardType); - - MyApplication.app.readCardOptV2.checkCard(cardType, mSimpleCheckCardCallback, 60); - - } catch (Exception e) { - e.printStackTrace(); - android.util.Log.e(TAG, "Error in startSimpleCardScan: " + e.getMessage()); - updateUI("Error starting card scan: " + e.getMessage(), false); - } - } - - private void stopCardCheck() { - try { - if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) { - MyApplication.app.readCardOptV2.cancelCheckCard(); - } - checkingCard = false; - btnCheckCard.setText("Start Scanning"); - currentState = STATE_SCANNING; - updateUIForCurrentState(); - } catch (Exception e) { - android.util.Log.e(TAG, "Error stopping card check: " + e.getMessage()); - checkingCard = false; - btnCheckCard.setText("Start Scanning"); - } - } - - private boolean validateApplicationState() { - if (MyApplication.app == null) { - updateUI("Error: Application not initialized", false); - android.util.Log.e(TAG, "MyApplication.app is null"); - return false; - } - return true; - } - - // ====== SIMPLE CARD DETECTION CALLBACK (Same as before) ====== - private final CheckCardCallbackV2 mSimpleCheckCardCallback = new CheckCardCallbackV2.Stub() { - @Override - public void findMagCard(Bundle info) throws RemoteException { - android.util.Log.d(TAG, "Simple Mode: findMagCard callback triggered"); - runOnUiThread(() -> handleSimpleMagCardResult(info)); - } - - @Override - public void findICCard(String atr) throws RemoteException { - android.util.Log.d(TAG, "Simple Mode: findICCard callback triggered with ATR: " + atr); - Bundle info = new Bundle(); - info.putString("atr", atr); - runOnUiThread(() -> handleSimpleICCardResult(info)); - } - - @Override - public void findRFCard(String uuid) throws RemoteException { - android.util.Log.d(TAG, "Simple Mode: findRFCard callback triggered with UUID: " + uuid); - Bundle info = new Bundle(); - info.putString("uuid", uuid); - runOnUiThread(() -> handleSimpleRFCardResult(info)); - } - - @Override - public void onError(int code, String message) throws RemoteException { - android.util.Log.e(TAG, "Simple Mode: onError callback triggered - Code: " + code + ", Message: " + message); - Bundle info = new Bundle(); - info.putInt("code", code); - info.putString("message", message); - runOnUiThread(() -> handleSimpleErrorResult(info)); - } - - @Override - public void findICCardEx(Bundle info) throws RemoteException { - android.util.Log.d(TAG, "Simple Mode: findICCardEx callback triggered"); - runOnUiThread(() -> handleSimpleICCardResult(info)); - } - - @Override - public void findRFCardEx(Bundle info) throws RemoteException { - android.util.Log.d(TAG, "Simple Mode: findRFCardEx callback triggered"); - runOnUiThread(() -> handleSimpleRFCardResult(info)); - } - - @Override - public void onErrorEx(Bundle info) throws RemoteException { - android.util.Log.e(TAG, "Simple Mode: onErrorEx callback triggered"); - runOnUiThread(() -> handleSimpleErrorResult(info)); - } - }; - - // ====== EMV CARD DETECTION CALLBACK (Same as before) ====== - private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() { - @Override - public void findMagCard(Bundle info) throws RemoteException { - android.util.Log.d(TAG, "EMV Mode: findMagCard callback triggered"); - runOnUiThread(() -> handleEMVMagCardResult(info)); - } - - @Override - public void findICCard(String atr) throws RemoteException { - android.util.Log.d(TAG, "EMV Mode: findICCard callback triggered with ATR: " + atr); - MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0); - mCardType = AidlConstantsV2.CardType.IC.getValue(); - runOnUiThread(() -> startEMVTransactionProcess()); - } - - @Override - public void findRFCard(String uuid) throws RemoteException { - android.util.Log.d(TAG, "EMV Mode: findRFCard callback triggered with UUID: " + uuid); - mCardType = AidlConstantsV2.CardType.NFC.getValue(); - runOnUiThread(() -> startEMVTransactionProcess()); - } - - @Override - public void onError(int code, String message) throws RemoteException { - android.util.Log.e(TAG, "EMV Mode: onError callback triggered - Code: " + code + ", Message: " + message); - Bundle info = new Bundle(); - info.putInt("code", code); - info.putString("message", message); - runOnUiThread(() -> handleEMVErrorResult(info)); - } - - @Override - public void findICCardEx(Bundle info) throws RemoteException { - android.util.Log.d(TAG, "EMV Mode: findICCardEx callback triggered"); - mCardType = AidlConstantsV2.CardType.IC.getValue(); - runOnUiThread(() -> startEMVTransactionProcess()); - } - - @Override - public void findRFCardEx(Bundle info) throws RemoteException { - android.util.Log.d(TAG, "EMV Mode: findRFCardEx callback triggered"); - mCardType = AidlConstantsV2.CardType.NFC.getValue(); - runOnUiThread(() -> startEMVTransactionProcess()); - } - - @Override - public void onErrorEx(Bundle info) throws RemoteException { - android.util.Log.e(TAG, "EMV Mode: onErrorEx callback triggered"); - runOnUiThread(() -> handleEMVErrorResult(info)); - } - }; - - // ====== SIMPLE CARD RESULT HANDLERS (Updated with amount) ====== - private void handleSimpleMagCardResult(Bundle info) { - logMagneticCardData(info); - - String track1 = Utility.null2String(info.getString("TRACK1")); - String track2 = Utility.null2String(info.getString("TRACK2")); - String track3 = Utility.null2String(info.getString("TRACK3")); - - StringBuilder sb = new StringBuilder(); - sb.append("==== TRANSACTION SUMMARY ====\n"); - sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); - sb.append("Payment Method: Magnetic Card\n\n"); - sb.append("MAGNETIC CARD DETECTED (Simple Mode)\n"); - sb.append("Track1: ").append(track1.isEmpty() ? "N/A" : track1).append("\n"); - sb.append("Track2: ").append(track2.isEmpty() ? "N/A" : track2).append("\n"); - sb.append("Track3: ").append(track3.isEmpty() ? "N/A" : track3); - - updateUI(sb.toString(), true); - } - - private void handleSimpleICCardResult(Bundle info) { - logICCardData(info); - - String atr = info.getString("atr", ""); - int cardType = info.getInt("cardType", -1); - - StringBuilder sb = new StringBuilder(); - sb.append("==== TRANSACTION SUMMARY ====\n"); - sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); - sb.append("Payment Method: IC Card\n\n"); - sb.append("IC CARD DETECTED (Simple Mode)\n"); - sb.append("ATR: ").append(atr.isEmpty() ? "N/A" : atr).append("\n"); - if (cardType != -1) { - sb.append("Card Type: ").append(cardType).append("\n"); - } - - updateUI(sb.toString(), true); - } - - private void handleSimpleRFCardResult(Bundle info) { - logRFCardData(info); - - String uuid = info.getString("uuid", ""); - String ats = info.getString("ats", ""); - int sak = info.getInt("sak", -1); - - StringBuilder sb = new StringBuilder(); - sb.append("==== TRANSACTION SUMMARY ====\n"); - sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); - sb.append("Payment Method: NFC/RF Card\n\n"); - sb.append("RF/NFC CARD DETECTED (Simple Mode)\n"); - sb.append("UUID: ").append(uuid.isEmpty() ? "N/A" : uuid).append("\n"); - if (!ats.isEmpty()) { - sb.append("ATS: ").append(ats).append("\n"); - } - if (sak != -1) { - sb.append("SAK: ").append(String.format("0x%02X", sak)).append("\n"); - sb.append("Type: ").append(analyzeCardTypeBySAK(sak)).append("\n"); - } - - updateUI(sb.toString(), true); - } - - private void handleSimpleErrorResult(Bundle info) { - logCardError(info); - - int code = info.getInt("code", -1); - String msg = info.getString("message", "Unknown error"); - String error = "Error: " + msg + " (Code: " + code + ")"; - updateUI(error, true); - } - - // ====== EMV CARD RESULT HANDLERS (Same as before) ====== - private void handleEMVMagCardResult(Bundle info) { - handleSimpleMagCardResult(info); - String currentText = tvResult.getText().toString(); - tvResult.setText(currentText.replace("(Simple Mode)", "(EMV Mode - Magnetic)")); - } - - private void handleEMVErrorResult(Bundle info) { - logCardError(info); - - int code = info.getInt("code", -1); - String msg = info.getString("message", "Unknown error"); - String error = "EMV Error: " + msg + " (Code: " + code + ")"; - updateUI(error, true); - } - - // ====== EMV TRANSACTION PROCESS (Updated with amount) ====== - private void startEMVTransactionProcess() { - android.util.Log.d(TAG, "Starting EMV transaction process"); - try { - Bundle bundle = new Bundle(); - bundle.putString("amount", transactionAmount); // Use actual transaction amount - bundle.putString("transType", "00"); - - if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { - bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD); - } else { - bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD); - } - bundle.putInt("cardType", mCardType); - - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nEMV processing started...\nReading card data..."); - - mEMVOptV2.transactProcessEx(bundle, mEMVListener); - } catch (Exception e) { - android.util.Log.e(TAG, "Error starting EMV transaction: " + e.getMessage()); - e.printStackTrace(); - updateUI("Error starting EMV transaction: " + e.getMessage(), true); - } - } - - // ====== EMV LISTENER (Same as before) ====== - private final EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() { - - @Override - public void onWaitAppSelect(List appNameList, boolean isFirstSelect) throws RemoteException { - android.util.Log.d(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect); - mProcessStep = EMV_APP_SELECT; - String[] candidateNames = getCandidateNames(appNameList); - mHandler.obtainMessage(EMV_APP_SELECT, candidateNames).sendToTarget(); - } - - @Override - public void onAppFinalSelect(String tag9F06Value) throws RemoteException { - android.util.Log.d(TAG, "onAppFinalSelect tag9F06Value:" + tag9F06Value); - mProcessStep = EMV_FINAL_APP_SELECT; - mHandler.obtainMessage(EMV_FINAL_APP_SELECT, tag9F06Value).sendToTarget(); - } - - @Override - public void onConfirmCardNo(String cardNo) throws RemoteException { - android.util.Log.d(TAG, "onConfirmCardNo cardNo:" + maskCardNumber(cardNo)); - mCardNo = cardNo; - mProcessStep = EMV_CONFIRM_CARD_NO; - mHandler.obtainMessage(EMV_CONFIRM_CARD_NO).sendToTarget(); - } - - @Override - public void onRequestShowPinPad(int pinType, int remainTime) throws RemoteException { - android.util.Log.d(TAG, "onRequestShowPinPad pinType:" + pinType + " remainTime:" + remainTime); - mPinType = pinType; - if (mCardNo == null) { - mCardNo = getCardNo(); - } - mProcessStep = EMV_SHOW_PIN_PAD; - mHandler.obtainMessage(EMV_SHOW_PIN_PAD).sendToTarget(); - } - - @Override - public void onRequestSignature() throws RemoteException { - android.util.Log.d(TAG, "onRequestSignature"); - mProcessStep = EMV_SIGNATURE; - mHandler.obtainMessage(EMV_SIGNATURE).sendToTarget(); - } - - @Override - public void onCertVerify(int certType, String certInfo) throws RemoteException { - android.util.Log.d(TAG, "onCertVerify certType:" + certType + " certInfo:" + certInfo); - mCertInfo = certInfo; - mProcessStep = EMV_CERT_VERIFY; - mHandler.obtainMessage(EMV_CERT_VERIFY).sendToTarget(); - } - - @Override - public void onOnlineProc() throws RemoteException { - android.util.Log.d(TAG, "onOnlineProcess"); - mProcessStep = EMV_ONLINE_PROCESS; - mHandler.obtainMessage(EMV_ONLINE_PROCESS).sendToTarget(); - } - - @Override - public void onCardDataExchangeComplete() throws RemoteException { - android.util.Log.d(TAG, "onCardDataExchangeComplete"); - if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { - MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0); - } - } - - @Override - public void onTransResult(int code, String desc) throws RemoteException { - if (mCardNo == null) { - mCardNo = getCardNo(); - } - android.util.Log.d(TAG, "onTransResult code:" + code + " desc:" + desc); - - if (code == 1 || code == 2 || code == 5 || code == 6) { - mHandler.obtainMessage(EMV_TRANS_SUCCESS, code, code, desc).sendToTarget(); - } else { - mHandler.obtainMessage(EMV_TRANS_FAIL, code, code, desc).sendToTarget(); - } - } - - @Override - public void onConfirmationCodeVerified() throws RemoteException { - android.util.Log.d(TAG, "onConfirmationCodeVerified"); - } - - @Override - public void onRequestDataExchange(String cardNo) throws RemoteException { - android.util.Log.d(TAG, "onRequestDataExchange,cardNo:" + cardNo); - mEMVOptV2.importDataExchangeStatus(0); - } - - @Override - public void onTermRiskManagement() throws RemoteException { - android.util.Log.d(TAG, "onTermRiskManagement"); - mEMVOptV2.importTermRiskManagementStatus(0); - } - - @Override - public void onPreFirstGenAC() throws RemoteException { - android.util.Log.d(TAG, "onPreFirstGenAC"); - mEMVOptV2.importPreFirstGenACStatus(0); - } - - @Override - public void onDataStorageProc(String[] containerID, String[] containerContent) throws RemoteException { - android.util.Log.d(TAG, "onDataStorageProc"); - String[] tags = new String[0]; - String[] values = new String[0]; - mEMVOptV2.importDataStorage(tags, values); - } - }; - - // ====== EMV DIALOG HANDLERS (Same as before) ====== - private void showAppSelectDialog(String[] candidateNames) { - mAppSelectDialog = new AlertDialog.Builder(this) - .setTitle("Please select application") - .setNegativeButton("Cancel", (dialog, which) -> importAppSelect(-1)) - .setPositiveButton("OK", (dialog, which) -> importAppSelect(mSelectIndex)) - .setSingleChoiceItems(candidateNames, 0, (dialog, which) -> mSelectIndex = which) - .create(); - mSelectIndex = 0; - mAppSelectDialog.show(); - } - - private void showCardNumberConfirmation() { - runOnUiThread(() -> { - dismissLoadingDialog(); - - String displayText = "Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nCard Number: " + maskCardNumber(mCardNo) + - "\n\nPlease confirm this card number to continue"; - tvResult.setText(displayText); - - btnCheckCard.setText("Confirm Card Number"); - mProcessStep = EMV_CONFIRM_CARD_NO; - - android.util.Log.d(TAG, "Card number confirmation UI updated, waiting for user input"); - }); - } - - private void showCertificateVerification() { - runOnUiThread(() -> { - dismissLoadingDialog(); - - String displayText = "Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nCertificate Information:\n" + mCertInfo + - "\n\nPlease confirm certificate to continue"; - tvResult.setText(displayText); - - btnCheckCard.setText("Confirm Certificate"); - mProcessStep = EMV_CERT_VERIFY; - - android.util.Log.d(TAG, "Certificate verification UI updated, waiting for user input"); - }); - } - - // ====== TLV DATA RETRIEVAL (Updated with transaction summary) ====== - private void getTlvData() { - android.util.Log.d(TAG, "======== RETRIEVING COMPLETE CARD DATA ========"); + private void loadEMVTlvData() { + android.util.Log.d(TAG, "======== RETRIEVING COMPLETE EMV CARD DATA ========"); try { String[] standardTagList = { @@ -1056,7 +144,7 @@ public class CreditCardActivity extends AppCompatActivity { Map allTlvMap = new TreeMap<>(); int tlvOpCode = AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL; - if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { + if ("NFC".equals(cardType)) { tlvOpCode = AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS; } @@ -1074,7 +162,7 @@ public class CreditCardActivity extends AppCompatActivity { android.util.Log.d(TAG, "Parsed " + tlvMap.size() + " standard TLV tags"); } - if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { + if ("NFC".equals(cardType)) { android.util.Log.d(TAG, "Requesting " + payPassTagList.length + " PayPass specific tags"); len = mEMVOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, payPassTagList, outData); @@ -1089,149 +177,207 @@ public class CreditCardActivity extends AppCompatActivity { } } - StringBuilder sb = new StringBuilder(); + displayEMVData(allTlvMap); - // Transaction Summary - sb.append("==== TRANSACTION COMPLETED ====\n"); - sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); - sb.append("Payment Method: EMV ").append(mCardType == AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC").append("\n"); - sb.append("Status: SUCCESS\n\n"); - - // Card Summary - sb.append("==== CARD SUMMARY ====\n"); - - TLV panTlv = allTlvMap.get("5A"); - if (panTlv != null && !TextUtils.isEmpty(panTlv.getValue())) { - sb.append("Card Number: ").append(panTlv.getValue()).append("\n"); - } - - TLV labelTlv = allTlvMap.get("50"); - if (labelTlv != null && !TextUtils.isEmpty(labelTlv.getValue())) { - sb.append("App Label: ").append(hexToString(labelTlv.getValue())).append("\n"); - } - - TLV nameTlv = allTlvMap.get("5F20"); - if (nameTlv != null && !TextUtils.isEmpty(nameTlv.getValue())) { - sb.append("Cardholder: ").append(hexToString(nameTlv.getValue()).trim()).append("\n"); - } - - TLV expiryTlv = allTlvMap.get("5F24"); - if (expiryTlv != null && !TextUtils.isEmpty(expiryTlv.getValue())) { - String expiry = expiryTlv.getValue(); - if (expiry.length() == 6) { - sb.append("Expiry: ").append(expiry.substring(2, 4)).append("/").append(expiry.substring(0, 2)).append("\n"); - } - } - - TLV aidTlv = allTlvMap.get("9F06"); - if (aidTlv != null && !TextUtils.isEmpty(aidTlv.getValue())) { - sb.append("Scheme: ").append(identifyPaymentScheme(aidTlv.getValue())).append("\n"); - } - - sb.append("\n==== ALL TLV DATA ====\n"); - - // All TLV Data - Set keySet = allTlvMap.keySet(); - for (String key : keySet) { - TLV tlv = allTlvMap.get(key); - String value = tlv != null ? tlv.getValue() : ""; - String description = getTlvDescription(key); - - sb.append(key); - if (!description.equals("Unknown")) { - sb.append(" (").append(description).append(")"); - } - sb.append(": "); - - if (key.equals("5A") && !value.isEmpty()) { - sb.append(value); - } else if (key.equals("50") && !value.isEmpty()) { - String decodedLabel = hexToString(value); - sb.append(value).append(" (").append(decodedLabel).append(")"); - } else if (key.equals("5F20") && !value.isEmpty()) { - String decodedName = hexToString(value); - sb.append(value).append(" (").append(decodedName.trim()).append(")"); - } else if (key.equals("9F06") && !value.isEmpty()) { - String scheme = identifyPaymentScheme(value); - sb.append(value).append(" (").append(scheme).append(")"); - } else if (key.equals("5F24") && !value.isEmpty()) { - sb.append(value); - if (value.length() == 6) { - sb.append(" (").append(value.substring(2, 4)).append("/").append(value.substring(0, 2)).append(")"); - } - } else if (key.equals("9F02") && !value.isEmpty()) { - sb.append(value); - try { - long amount = Long.parseLong(value, 16); - sb.append(" (").append(String.format("%.2f", amount / 100.0)).append(")"); - } catch (Exception e) { - // Keep original value - } - } else { - sb.append(value); - } - - sb.append("\n"); - - android.util.Log.d(TAG, "Tag " + key + " (" + description + "): " + value); - } - - sb.append("\nTotal TLV tags retrieved: ").append(keySet.size()); - - android.util.Log.d(TAG, "Total TLV tags retrieved: " + keySet.size()); + android.util.Log.d(TAG, "Total TLV tags retrieved: " + allTlvMap.size()); android.util.Log.d(TAG, "=================================="); - android.util.Log.d(TAG, "Complete card data retrieved successfully"); - updateUI(sb.toString(), true); - } catch (Exception e) { android.util.Log.e(TAG, "Error retrieving TLV data: " + e.getMessage()); e.printStackTrace(); - updateUI("Error retrieving card data: " + e.getMessage(), true); + showSimpleError("Error retrieving EMV data: " + e.getMessage()); } } - // ====== RESET AND CLEANUP METHODS ====== - private void resetToAmountInput() { - runOnUiThread(() -> { - currentState = STATE_INPUT_AMOUNT; - mProcessStep = 0; - checkingCard = false; + private void loadSimpleCardData() { + StringBuilder summary = new StringBuilder(); + StringBuilder cardInfo = new StringBuilder(); + + // Transaction Summary + summary.append("==== TRANSACTION COMPLETED ====\n"); + summary.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + summary.append("Payment Method: ").append(getCardTypeDisplay()).append("\n"); + summary.append("Status: SUCCESS\n"); + + // Card Information + if (cardData != null) { + cardInfo.append("==== CARD INFORMATION ====\n"); - dismissAppSelectDialog(); - updateUIForCurrentState(); - - if (btnCopyData != null) { - btnCopyData.setVisibility(View.GONE); + if ("MAGNETIC".equals(cardType)) { + String track1 = Utility.null2String(cardData.getString("TRACK1")); + String track2 = Utility.null2String(cardData.getString("TRACK2")); + String track3 = Utility.null2String(cardData.getString("TRACK3")); + + cardInfo.append("Track1: ").append(track1.isEmpty() ? "N/A" : track1).append("\n"); + cardInfo.append("Track2: ").append(track2.isEmpty() ? "N/A" : track2).append("\n"); + cardInfo.append("Track3: ").append(track3.isEmpty() ? "N/A" : track3).append("\n"); + + } else if ("IC".equals(cardType)) { + String atr = cardData.getString("atr", ""); + cardInfo.append("ATR: ").append(atr.isEmpty() ? "N/A" : atr).append("\n"); + + } else if ("NFC".equals(cardType)) { + String uuid = cardData.getString("uuid", ""); + String ats = cardData.getString("ats", ""); + int sak = cardData.getInt("sak", -1); + + cardInfo.append("UUID: ").append(uuid.isEmpty() ? "N/A" : uuid).append("\n"); + if (!ats.isEmpty()) { + cardInfo.append("ATS: ").append(ats).append("\n"); + } + if (sak != -1) { + cardInfo.append("SAK: ").append(String.format("0x%02X", sak)).append("\n"); + cardInfo.append("Type: ").append(analyzeCardTypeBySAK(sak)).append("\n"); + } } - if (btnClearData != null) { - btnClearData.setVisibility(View.GONE); + } + + tvTransactionSummary.setText(summary.toString()); + tvCardData.setText(cardInfo.toString()); + } + + private void displayEMVData(Map allTlvMap) { + StringBuilder summary = new StringBuilder(); + StringBuilder cardInfo = new StringBuilder(); + + // Transaction Summary + summary.append("==== TRANSACTION COMPLETED ====\n"); + summary.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + summary.append("Payment Method: EMV ").append(cardType).append("\n"); + summary.append("Status: SUCCESS\n"); + + // Card Summary + summary.append("\n==== CARD SUMMARY ====\n"); + + TLV panTlv = allTlvMap.get("5A"); + if (panTlv != null && !TextUtils.isEmpty(panTlv.getValue())) { + summary.append("Card Number: ").append(panTlv.getValue()).append("\n"); + } + + TLV labelTlv = allTlvMap.get("50"); + if (labelTlv != null && !TextUtils.isEmpty(labelTlv.getValue())) { + summary.append("App Label: ").append(hexToString(labelTlv.getValue())).append("\n"); + } + + TLV nameTlv = allTlvMap.get("5F20"); + if (nameTlv != null && !TextUtils.isEmpty(nameTlv.getValue())) { + summary.append("Cardholder: ").append(hexToString(nameTlv.getValue()).trim()).append("\n"); + } + + TLV expiryTlv = allTlvMap.get("5F24"); + if (expiryTlv != null && !TextUtils.isEmpty(expiryTlv.getValue())) { + String expiry = expiryTlv.getValue(); + if (expiry.length() == 6) { + summary.append("Expiry: ").append(expiry.substring(2, 4)).append("/").append(expiry.substring(0, 2)).append("\n"); + } + } + + TLV aidTlv = allTlvMap.get("9F06"); + if (aidTlv != null && !TextUtils.isEmpty(aidTlv.getValue())) { + summary.append("Scheme: ").append(identifyPaymentScheme(aidTlv.getValue())).append("\n"); + } + + // Detailed TLV Data + cardInfo.append("==== DETAILED TLV DATA ====\n"); + + Set keySet = allTlvMap.keySet(); + for (String key : keySet) { + TLV tlv = allTlvMap.get(key); + String value = tlv != null ? tlv.getValue() : ""; + String description = getTlvDescription(key); + + cardInfo.append(key); + if (!description.equals("Unknown")) { + cardInfo.append(" (").append(description).append(")"); + } + cardInfo.append(": "); + + if (key.equals("5A") && !value.isEmpty()) { + cardInfo.append(value); + } else if (key.equals("50") && !value.isEmpty()) { + String decodedLabel = hexToString(value); + cardInfo.append(value).append(" (").append(decodedLabel).append(")"); + } else if (key.equals("5F20") && !value.isEmpty()) { + String decodedName = hexToString(value); + cardInfo.append(value).append(" (").append(decodedName.trim()).append(")"); + } else if (key.equals("9F06") && !value.isEmpty()) { + String scheme = identifyPaymentScheme(value); + cardInfo.append(value).append(" (").append(scheme).append(")"); + } else if (key.equals("5F24") && !value.isEmpty()) { + cardInfo.append(value); + if (value.length() == 6) { + cardInfo.append(" (").append(value.substring(2, 4)).append("/").append(value.substring(0, 2)).append(")"); + } + } else if (key.equals("9F02") && !value.isEmpty()) { + cardInfo.append(value); + try { + long amount = Long.parseLong(value, 16); + cardInfo.append(" (").append(String.format("%.2f", amount / 100.0)).append(")"); + } catch (Exception e) { + // Keep original value + } + } else { + cardInfo.append(value); } - android.util.Log.d(TAG, "UI reset to amount input state"); - }); + cardInfo.append("\n"); + } + + cardInfo.append("\nTotal TLV tags retrieved: ").append(keySet.size()); + + tvTransactionSummary.setText(summary.toString()); + tvCardData.setText(cardInfo.toString()); + } + + private void showSimpleError(String error) { + StringBuilder summary = new StringBuilder(); + summary.append("==== TRANSACTION ERROR ====\n"); + summary.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + summary.append("Status: FAILED\n"); + + tvTransactionSummary.setText(summary.toString()); + tvCardData.setText(error); + } + + private String getCardTypeDisplay() { + switch (cardType) { + case "MAGNETIC": return "Magnetic Card"; + case "IC": return "IC Card"; + case "NFC": return "NFC/RF Card"; + default: return cardType; + } + } + + private String formatAmount(long amountCents) { + double amountRupiah = amountCents / 100.0; + NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID")); + return formatter.format(amountRupiah); } private void copyCardDataToClipboard() { - if (tvResult != null) { - String cardData = tvResult.getText().toString(); - if (!cardData.isEmpty() && !cardData.contains("Ready to scan")) { - ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Card Data", cardData); - clipboard.setPrimaryClip(clip); - showToast("Card data copied to clipboard"); - } else { - showToast("No card data to copy"); - } - } + String summary = tvTransactionSummary.getText().toString(); + String cardData = tvCardData.getText().toString(); + String fullData = summary + "\n\n" + cardData; + + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Transaction Data", fullData); + clipboard.setPrimaryClip(clip); + showToast("Transaction data copied to clipboard"); } - private void clearCardData() { - resetToAmountInput(); - showToast("Transaction cleared"); + private void startNewTransaction() { + Intent intent = new Intent(this, CreateTransactionActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + startActivity(intent); + finish(); } - // ====== HELPER METHODS (All the same as before) ====== + private void printReceipt() { + // TODO: Implement print functionality + showToast("Print functionality to be implemented"); + } + + // ====== HELPER METHODS ====== private String getTlvDescription(String tag) { switch (tag.toUpperCase()) { case "4F": return "Application Identifier"; @@ -1422,79 +568,6 @@ public class CreditCardActivity extends AppCompatActivity { } } - private String getCardNo() { - try { - String[] tagList = {"57", "5A"}; - byte[] outData = new byte[256]; - int len = mEMVOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tagList, outData); - if (len <= 0) { - return ""; - } - byte[] bytes = Arrays.copyOf(outData, len); - Map tlvMap = TLVUtils.buildTLVMap(bytes); - - if (tlvMap.get("57") != null && !TextUtils.isEmpty(Objects.requireNonNull(tlvMap.get("57")).getValue())) { - TLV tlv57 = tlvMap.get("57"); - CardInfo cardInfo = parseTrack2(tlv57.getValue()); - return cardInfo.cardNo; - } - if (tlvMap.get("5A") != null && !TextUtils.isEmpty(Objects.requireNonNull(tlvMap.get("5A")).getValue())) { - return Objects.requireNonNull(tlvMap.get("5A")).getValue(); - } - } catch (RemoteException e) { - e.printStackTrace(); - } - return ""; - } - - public static CardInfo parseTrack2(String track2) { - String track_2 = stringFilter(track2); - int index = track_2.indexOf("="); - if (index == -1) { - index = track_2.indexOf("D"); - } - CardInfo cardInfo = new CardInfo(); - if (index == -1) { - return cardInfo; - } - String cardNumber = ""; - if (track_2.length() > index) { - cardNumber = track_2.substring(0, index); - } - String expiryDate = ""; - if (track_2.length() > index + 5) { - expiryDate = track_2.substring(index + 1, index + 5); - } - String serviceCode = ""; - if (track_2.length() > index + 8) { - serviceCode = track_2.substring(index + 5, index + 8); - } - cardInfo.cardNo = cardNumber; - cardInfo.expireDate = expiryDate; - cardInfo.serviceCode = serviceCode; - return cardInfo; - } - - static String stringFilter(String str) { - String regEx = "[^0-9=D]"; - Pattern p = Pattern.compile(regEx); - Matcher matcher = p.matcher(str); - return matcher.replaceAll("").trim(); - } - - private String maskCardNumber(String cardNo) { - if (cardNo == null || cardNo.length() < 8) { - return cardNo; - } - String first4 = cardNo.substring(0, 4); - String last4 = cardNo.substring(cardNo.length() - 4); - StringBuilder middle = new StringBuilder(); - for (int i = 0; i < cardNo.length() - 8; i++) { - middle.append("*"); - } - return first4 + middle.toString() + last4; - } - private String identifyPaymentScheme(String aid) { if (aid == null) return "Unknown"; @@ -1536,362 +609,13 @@ public class CreditCardActivity extends AppCompatActivity { } } - private String[] getCandidateNames(List candiList) { - if (candiList == null || candiList.size() == 0) return new String[0]; - String[] result = new String[candiList.size()]; - for (int i = 0; i < candiList.size(); i++) { - EMVCandidateV2 candi = candiList.get(i); - String name = candi.appPreName; - name = TextUtils.isEmpty(name) ? candi.appLabel : name; - name = TextUtils.isEmpty(name) ? candi.appName : name; - name = TextUtils.isEmpty(name) ? "" : name; - result[i] = name; - } - return result; - } - - // ====== EMV IMPORT METHODS (Same as before) ====== - private void importAppSelect(int selectIndex) { - try { - mEMVOptV2.importAppSelect(selectIndex); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void importFinalAppSelectStatus(int status) { - try { - mEMVOptV2.importAppFinalSelectStatus(status); - } catch (RemoteException e) { - e.printStackTrace(); - } - } - - private void importCardNoStatus(int status) { - try { - mEMVOptV2.importCardNoStatus(status); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void importCertStatus(int status) { - try { - mEMVOptV2.importCertStatus(status); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void importPinInputStatus(int inputResult) { - try { - mEMVOptV2.importPinInputStatus(mPinType, inputResult); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void importSignatureStatus(int status) { - try { - mEMVOptV2.importSignatureStatus(status); - } catch (Exception e) { - e.printStackTrace(); - } - } - - // ====== PIN PAD METHODS (Same as before) ====== - private void initPinPad() { - android.util.Log.e(TAG, "========== PIN PAD INITIALIZATION =========="); - try { - if (mPinPadOptV2 == null) { - throw new IllegalStateException("PIN Pad service not available"); - } - - if (mCardNo == null || mCardNo.length() < 13) { - throw new IllegalArgumentException("Invalid card number for PIN"); - } - - PinPadConfigV2 pinPadConfig = new PinPadConfigV2(); - pinPadConfig.setPinPadType(0); - pinPadConfig.setPinType(mPinType); - pinPadConfig.setOrderNumKey(false); - - String panForPin = mCardNo.substring(mCardNo.length() - 13, mCardNo.length() - 1); - byte[] panBytes = panForPin.getBytes("US-ASCII"); - pinPadConfig.setPan(panBytes); - - pinPadConfig.setTimeout(60 * 1000); - pinPadConfig.setPinKeyIndex(12); - pinPadConfig.setMaxInput(12); - pinPadConfig.setMinInput(0); - pinPadConfig.setKeySystem(0); - pinPadConfig.setAlgorithmType(0); - - runOnUiThread(() -> { - updateUI("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nPIN Input Required\n\nPIN pad is ready\nPlease enter your PIN on the device", false); - btnCheckCard.setText("PIN Pad Active"); - btnCheckCard.setEnabled(false); - }); - - mPinPadOptV2.initPinPad(pinPadConfig, mPinPadListener); - - } catch (Exception e) { - android.util.Log.e(TAG, "PIN pad initialization failed: " + e.getMessage()); - mHandler.obtainMessage(PIN_ERROR, -1, -1, - "PIN Error: " + e.getMessage()).sendToTarget(); - } - } - - private final PinPadListenerV2 mPinPadListener = new PinPadListenerV2.Stub() { - @Override - public void onPinLength(int len) throws RemoteException { - android.util.Log.d(TAG, "PIN input length: " + len); - runOnUiThread(() -> { - String dots = ""; - for (int i = 0; i < len; i++) { - dots += "• "; - } - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nPIN Input: " + dots + "\n\nEntering PIN... (" + len + " digits)"); - }); - } - - @Override - public void onConfirm(int i, byte[] pinBlock) throws RemoteException { - android.util.Log.d(TAG, "PIN input confirmed"); - - runOnUiThread(() -> { - btnCheckCard.setEnabled(true); - btnCheckCard.setText("Processing..."); - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nPIN confirmed, processing..."); - }); - - if (pinBlock != null) { - String hexStr = ByteUtil.bytes2HexStr(pinBlock); - android.util.Log.d(TAG, "PIN block received: " + hexStr); - mHandler.obtainMessage(PIN_CLICK_PIN, pinBlock).sendToTarget(); - } else { - android.util.Log.d(TAG, "PIN bypass confirmed"); - mHandler.obtainMessage(PIN_CLICK_CONFIRM).sendToTarget(); - } - } - - @Override - public void onCancel() throws RemoteException { - android.util.Log.d(TAG, "PIN input cancelled by user"); - - runOnUiThread(() -> { - btnCheckCard.setEnabled(true); - btnCheckCard.setText("Start Scanning"); - tvResult.setText("PIN input cancelled"); - }); - - mHandler.obtainMessage(PIN_CLICK_CANCEL).sendToTarget(); - } - - @Override - public void onError(int code) throws RemoteException { - android.util.Log.e(TAG, "PIN pad error: " + code); - String msg = AidlErrorCodeV2.valueOf(code).getMsg(); - - runOnUiThread(() -> { - btnCheckCard.setEnabled(true); - btnCheckCard.setText("Start Scanning"); - tvResult.setText("PIN error: " + msg); - }); - - mHandler.obtainMessage(PIN_ERROR, code, code, msg).sendToTarget(); - } - - @Override - public void onHover(int event, byte[] data) throws RemoteException { - android.util.Log.d(TAG, "PIN pad hover event: " + event); - } - }; - - // ====== MOCK ONLINE PROCESS (Same as before) ====== - private void mockOnlineProcess() { - new Thread(() -> { - try { - runOnUiThread(() -> tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\nProcessing online authorization...")); - Thread.sleep(2000); - - try { - String[] tags = {"71", "72", "91", "8A", "89"}; - String[] values = {"", "", "", "", ""}; - byte[] out = new byte[1024]; - int len = mEMVOptV2.importOnlineProcStatus(0, tags, values, out); - if (len < 0) { - android.util.Log.e(TAG, "importOnlineProcessStatus error,code:" + len); - } - } catch (Exception e) { - e.printStackTrace(); - } - } catch (Exception e) { - e.printStackTrace(); - } - }).start(); - } - - // ====== UI UPDATE METHODS ====== - private void updateUI(String message, boolean isTransactionComplete) { - runOnUiThread(() -> { - if (tvResult != null) { - tvResult.setText(message); - } - if (isTransactionComplete) { - checkingCard = false; - btnCheckCard.setText("New Transaction"); - btnCheckCard.setOnClickListener(v -> resetToAmountInput()); - currentState = STATE_INPUT_AMOUNT; // Will be updated when button clicked - - if (btnCopyData != null && message.contains("====")) { - btnCopyData.setVisibility(View.VISIBLE); - } - if (btnClearData != null && message.contains("====")) { - btnClearData.setVisibility(View.VISIBLE); - } - } - }); - } - - private void showLoadingDialog(String message) { - runOnUiThread(() -> { - if (tvResult != null) { - tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + - "\n\n" + message + "..."); - } - android.util.Log.d(TAG, "Loading: " + message); - }); - } - - private void dismissLoadingDialog() { - android.util.Log.d(TAG, "Loading dismissed"); - } - - private void dismissAppSelectDialog() { - if (mAppSelectDialog != null) { - try { - mAppSelectDialog.dismiss(); - } catch (Exception e) { - e.printStackTrace(); - } - mAppSelectDialog = null; - } - } - private void showToast(String message) { - runOnUiThread(() -> Toast.makeText(this, message, Toast.LENGTH_SHORT).show()); + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } - // ====== LOGGING METHODS (Same as before) ====== - private void logMagneticCardData(Bundle info) { - android.util.Log.d(TAG, "======================================="); - android.util.Log.d(TAG, " MAGNETIC CARD DETECTED "); - android.util.Log.d(TAG, "======================================="); - - String track1 = Utility.null2String(info.getString("TRACK1")); - String track2 = Utility.null2String(info.getString("TRACK2")); - String track3 = Utility.null2String(info.getString("TRACK3")); - - android.util.Log.d(TAG, "TRACK DATA:"); - android.util.Log.d(TAG, " Track1: " + (track1.isEmpty() ? "N/A" : track1)); - android.util.Log.d(TAG, " Track2: " + (track2.isEmpty() ? "N/A" : track2)); - android.util.Log.d(TAG, " Track3: " + (track3.isEmpty() ? "N/A" : track3)); - android.util.Log.d(TAG, "======================================="); - } - - private void logICCardData(Bundle info) { - android.util.Log.d(TAG, "======================================="); - android.util.Log.d(TAG, " IC CARD DETECTED "); - android.util.Log.d(TAG, "======================================="); - - String atr = info.getString("atr", ""); - int cardType = info.getInt("cardType", -1); - - android.util.Log.d(TAG, "BASIC INFO:"); - android.util.Log.d(TAG, " ATR: " + (atr.isEmpty() ? "N/A" : atr)); - android.util.Log.d(TAG, " Card Type: " + cardType); - android.util.Log.d(TAG, "======================================="); - } - - private void logRFCardData(Bundle info) { - android.util.Log.d(TAG, "======================================="); - android.util.Log.d(TAG, " RF/NFC CARD DETECTED "); - android.util.Log.d(TAG, "======================================="); - - String uuid = info.getString("uuid", ""); - String ats = info.getString("ats", ""); - int sak = info.getInt("sak", -1); - - android.util.Log.d(TAG, "BASIC INFO:"); - android.util.Log.d(TAG, " UUID: " + (uuid.isEmpty() ? "N/A" : uuid)); - android.util.Log.d(TAG, " ATS: " + (ats.isEmpty() ? "N/A" : ats)); - android.util.Log.d(TAG, " SAK: " + (sak == -1 ? "N/A" : String.format("0x%02X (%d)", sak, sak))); - android.util.Log.d(TAG, "======================================="); - } - - private void logCardError(Bundle info) { - android.util.Log.e(TAG, "======================================="); - android.util.Log.e(TAG, " CARD READ ERROR "); - android.util.Log.e(TAG, "======================================="); - - int code = info.getInt("code", -1); - String message = info.getString("message", ""); - - android.util.Log.e(TAG, "ERROR INFO:"); - android.util.Log.e(TAG, " Error Code: " + code); - android.util.Log.e(TAG, " Error Message: " + (message.isEmpty() ? "N/A" : message)); - android.util.Log.e(TAG, "======================================="); - } - - // ====== LIFECYCLE METHODS (Same as before) ====== @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } - - // ====== LIFECYCLE METHODS ====== - @Override - protected void onDestroy() { - android.util.Log.d(TAG, "onDestroy called"); - cancelCheckCard(); - super.onDestroy(); - } - - @Override - protected void onPause() { - super.onPause(); - android.util.Log.d(TAG, "onPause called"); - if (checkingCard) { - android.util.Log.d(TAG, "App paused, stopping card check"); - stopCardCheck(); - } - } - - @Override - protected void onResume() { - super.onResume(); - android.util.Log.d(TAG, "onResume called"); - checkPaySDKStatus(); - } - - private void cancelCheckCard() { - try { - if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) { - android.util.Log.d(TAG, "Cancelling card operations..."); - MyApplication.app.readCardOptV2.cardOff(CardType.NFC.getValue()); - MyApplication.app.readCardOptV2.cardOff(CardType.IC.getValue()); - MyApplication.app.readCardOptV2.cancelCheckCard(); - android.util.Log.d(TAG, "Card operations cancelled successfully"); - } - } catch (Exception e) { - android.util.Log.e(TAG, "Error cancelling card operations: " + e.getMessage()); - e.printStackTrace(); - } - } } \ No newline at end of file diff --git a/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java b/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java new file mode 100644 index 0000000..bfd86f0 --- /dev/null +++ b/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java @@ -0,0 +1,1082 @@ +package com.example.bdkipoc.kredit; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; +import android.widget.Button; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + +import com.example.bdkipoc.MyApplication; +import com.example.bdkipoc.R; +import com.example.bdkipoc.utils.ByteUtil; +import com.example.bdkipoc.utils.Utility; +import com.sunmi.pay.hardware.aidl.AidlConstants.CardType; +import com.sunmi.pay.hardware.aidl.bean.CardInfo; +import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2; +import com.sunmi.pay.hardware.aidlv2.AidlErrorCodeV2; +import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2; +import com.sunmi.pay.hardware.aidlv2.bean.PinPadConfigV2; +import com.sunmi.pay.hardware.aidlv2.emv.EMVListenerV2; +import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2; +import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadListenerV2; +import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2; +import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2; + +import java.text.NumberFormat; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * EmvTransactionActivity - Handle card reading and EMV processing + */ +public class EmvTransactionActivity extends AppCompatActivity { + private static final String TAG = "EmvTransaction"; + + // UI Components + private TextView tvStatus; + private TextView tvAmountDisplay; + private Button btnAction; + private Button btnCancel; + + // Transaction Data + private String transactionAmount; + private boolean isEMVMode; + private boolean isProcessing = false; + private boolean isButtonProcessing = false; + + // EMV Components + private EMVOptV2 mEMVOptV2; + private PinPadOptV2 mPinPadOptV2; + + // EMV Process Variables + private int mCardType; + private String mCardNo; + private int mPinType; + private String mCertInfo; + private int mSelectIndex; + private int mProcessStep; + private AlertDialog mAppSelectDialog; + + // EMV Process Constants + private static final int EMV_APP_SELECT = 1; + private static final int EMV_FINAL_APP_SELECT = 2; + private static final int EMV_CONFIRM_CARD_NO = 3; + private static final int EMV_CERT_VERIFY = 4; + private static final int EMV_SHOW_PIN_PAD = 5; + private static final int EMV_ONLINE_PROCESS = 6; + private static final int EMV_SIGNATURE = 7; + private static final int EMV_TRANS_SUCCESS = 888; + private static final int EMV_TRANS_FAIL = 999; + + private static final int PIN_CLICK_PIN = 51; + private static final int PIN_CLICK_CONFIRM = 52; + private static final int PIN_CLICK_CANCEL = 53; + private static final int PIN_ERROR = 54; + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + switch (msg.what) { + case EMV_FINAL_APP_SELECT: + importFinalAppSelectStatus(0); + break; + + case EMV_APP_SELECT: + String[] candiNames = (String[]) msg.obj; + showAppSelectDialog(candiNames); + break; + + case EMV_CONFIRM_CARD_NO: + showCardNumberConfirmation(); + break; + + case EMV_CERT_VERIFY: + showCertificateVerification(); + break; + + case EMV_SHOW_PIN_PAD: + android.util.Log.d(TAG, "Initializing PIN pad..."); + initPinPad(); + break; + + case EMV_ONLINE_PROCESS: + mockOnlineProcess(); + break; + + case EMV_SIGNATURE: + importSignatureStatus(0); + break; + + case PIN_CLICK_PIN: + android.util.Log.d(TAG, "PIN input confirmed"); + importPinInputStatus(0); + break; + + case PIN_CLICK_CONFIRM: + android.util.Log.d(TAG, "PIN confirm without input (bypass)"); + importPinInputStatus(2); + break; + + case PIN_CLICK_CANCEL: + showToast("PIN input cancelled by user"); + importPinInputStatus(1); + break; + + case PIN_ERROR: + android.util.Log.e(TAG, "PIN Error: " + msg.obj + " -- " + msg.arg1); + showToast("PIN Error: " + msg.obj); + importPinInputStatus(3); + break; + + case EMV_TRANS_FAIL: + // Handle EMV transaction failure with error code check + int errorCode = msg.arg1; + String errorDesc = (String) msg.obj; + + Log.e(TAG, "EMV Transaction failed: " + errorDesc + " (Code: " + errorCode + ")"); + + if (errorCode == -50009) { + // EMV process conflict - reset and retry + Log.d(TAG, "EMV process conflict detected - resetting..."); + showToast("EMV busy, resetting..."); + resetEMVAndRetry(); + } else { + // Other errors - show message but don't finish + showToast("Transaction failed: " + errorDesc); + resetScanningState(); + } + break; + + case EMV_TRANS_SUCCESS: + // Navigate to results screen + navigateToResults(); + break; + } + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_emv_transaction); + + getIntentData(); + initViews(); + initEMVComponents(); + initEMVData(); + updateUI(); + } + + private void getIntentData() { + Intent intent = getIntent(); + transactionAmount = intent.getStringExtra("TRANSACTION_AMOUNT"); + isEMVMode = intent.getBooleanExtra("EMV_MODE", true); + + if (transactionAmount == null) { + transactionAmount = "0"; + finish(); + return; + } + } + + private void initViews() { + // Setup Toolbar + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + if (getSupportActionBar() != null) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setTitle("Scan Kartu"); + } + + tvStatus = findViewById(R.id.tv_status); + tvAmountDisplay = findViewById(R.id.tv_amount_display); + btnAction = findViewById(R.id.btn_action); + btnCancel = findViewById(R.id.btn_cancel); + + btnAction.setOnClickListener(v -> handleActionClick()); + btnCancel.setOnClickListener(v -> finish()); + } + + private void initEMVComponents() { + if (MyApplication.app != null) { + mEMVOptV2 = MyApplication.app.emvOptV2; + mPinPadOptV2 = MyApplication.app.pinPadOptV2; + android.util.Log.d(TAG, "EMV components initialized"); + } else { + android.util.Log.e(TAG, "MyApplication.app is null"); + showToast("Application error"); + finish(); + } + } + + private void initEMVData() { + try { + if (mEMVOptV2 != null) { + // Reset any existing EMV process first + mEMVOptV2.initEmvProcess(); + + // Small delay to ensure reset is complete + new Handler(Looper.getMainLooper()).postDelayed(() -> { + try { + initEmvTlvData(); + android.util.Log.d(TAG, "EMV data initialized successfully"); + } catch (Exception e) { + android.util.Log.e(TAG, "Error in delayed EMV init: " + e.getMessage()); + } + }, 500); + } + } catch (Exception e) { + android.util.Log.e(TAG, "Error initializing EMV data: " + e.getMessage()); + e.printStackTrace(); + } + } + + private void initEmvTlvData() { + try { + // Set PayPass (MasterCard) TLV data + String[] tagsPayPass = {"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C", + "DF8123", "DF8124", "DF8125", "DF8126", "DF811B", "DF811D", "DF8122", "DF8120", "DF8121"}; + String[] valuesPayPass = {"E0", "F8", "F8", "E8", "00", "00", + "000000000000", "000000100000", "999999999999", "000000100000", + "30", "02", "0000000000", "000000000000", "000000000000"}; + mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass); + + // Set AMEX TLV data + String[] tagsAE = {"9F6D", "9F6E", "9F33", "9F35", "DF8168", "DF8167", "DF8169", "DF8170"}; + String[] valuesAE = {"C0", "D8E00000", "E0E888", "22", "00", "00", "00", "60"}; + mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_AE, tagsAE, valuesAE); + + // Set JCB TLV data + String[] tagsJCB = {"9F53", "DF8161"}; + String[] valuesJCB = {"708000", "7F00"}; + mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_JCB, tagsJCB, valuesJCB); + + // Set DPAS TLV data + String[] tagsDPAS = {"9F66"}; + String[] valuesDPAS = {"B600C000"}; + mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_DPAS, tagsDPAS, valuesDPAS); + + // Set Flash TLV data + String[] tagsFLASH = {"9F58", "9F59", "9F5A", "9F5D", "9F5E"}; + String[] valuesFLASH = {"03", "D88700", "00", "000000000000", "E000"}; + mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_FLASH, tagsFLASH, valuesFLASH); + + // Set Pure TLV data + String[] tagsPURE = {"DF7F", "DF8134", "DF8133"}; + String[] valuesPURE = {"A0000007271010", "DF", "36006043F9"}; + mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PURE, tagsPURE, valuesPURE); + + Bundle bundle = new Bundle(); + bundle.putBoolean("optOnlineRes", true); + mEMVOptV2.setTermParamEx(bundle); + + } catch (RemoteException e) { + android.util.Log.e(TAG, "Error setting EMV TLV data: " + e.getMessage()); + e.printStackTrace(); + } + } + + private void updateUI() { + long amountCents = Long.parseLong(transactionAmount); + double amountRupiah = amountCents / 100.0; + NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID")); + String formattedAmount = formatter.format(amountRupiah); + + tvAmountDisplay.setText("Nominal: " + formattedAmount); + + String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)"; + String status = "Ready to scan card...\n" + + "Current Mode: " + mode + "\n\n" + + "Please insert, swipe, or tap your card"; + + tvStatus.setText(status); + btnAction.setText("Start Scanning"); + } + + private void handleActionClick() { + // Prevent multiple rapid clicks + if (isButtonProcessing) { + Log.d(TAG, "Button click ignored - already processing"); + return; + } + + isButtonProcessing = true; + Log.d(TAG, "handleActionClick - mProcessStep: " + mProcessStep + ", isProcessing: " + isProcessing); + + // Reset button processing flag after delay + btnAction.postDelayed(() -> isButtonProcessing = false, 1000); + + if (mProcessStep == 0) { + if (isProcessing) { + stopCardCheck(); + } else { + startCardCheck(); + } + } else if (mProcessStep == EMV_CONFIRM_CARD_NO) { + android.util.Log.d(TAG, "User confirmed card number"); + btnAction.setText("Processing..."); + importCardNoStatus(0); + } else if (mProcessStep == EMV_CERT_VERIFY) { + android.util.Log.d(TAG, "User confirmed certificate"); + btnAction.setText("Processing..."); + importCertStatus(0); + } + } + + private void startCardCheck() { + // Prevent multiple calls + if (isProcessing) { + Log.d(TAG, "Card check already in progress - ignoring call"); + return; + } + + Log.d(TAG, "Starting card check - setting isProcessing = true"); + isProcessing = true; + btnAction.setText("Initializing..."); + btnAction.setEnabled(false); + + try { + // Force EMV reset before any operation + if (mEMVOptV2 != null) { + Log.d(TAG, "Forcing EMV reset before card check"); + mEMVOptV2.initEmvProcess(); + + // Wait a bit before continuing + btnAction.postDelayed(() -> { + if (isProcessing) { // Double check we're still processing + continueCardCheck(); + } + }, 500); + + } else { + continueCardCheck(); + } + + } catch (Exception e) { + Log.e(TAG, "Error in startCardCheck: " + e.getMessage(), e); + resetScanningState(); + showToast("Error: " + e.getMessage()); + } + } + + private void continueCardCheck() { + try { + Log.d(TAG, "continueCardCheck - isProcessing: " + isProcessing); + + if (!isProcessing) { + Log.d(TAG, "continueCardCheck called but not processing - returning"); + return; + } + + btnAction.setText("Stop Scanning"); + btnAction.setEnabled(true); + + if (isEMVMode) { + startEMVCardCheck(); + } else { + startSimpleCardCheck(); + } + + } catch (Exception e) { + Log.e(TAG, "Error in continueCardCheck: " + e.getMessage(), e); + resetScanningState(); + showToast("Error: " + e.getMessage()); + } + } + + private void startEMVCardCheck() { + try { + if (mEMVOptV2 == null) { + showToast("EMV not initialized"); + return; + } + + mEMVOptV2.initEmvProcess(); + initEmvTlvData(); + + tvStatus.setText("EMV Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); + + int cardType = AidlConstantsV2.CardType.NFC.getValue() | AidlConstantsV2.CardType.IC.getValue(); + android.util.Log.d(TAG, "Starting EMV checkCard with cardType: " + cardType); + + MyApplication.app.readCardOptV2.checkCard(cardType, mEMVCheckCardCallback, 60); + + } catch (Exception e) { + e.printStackTrace(); + android.util.Log.e(TAG, "Error in startEMVCardCheck: " + e.getMessage()); + showToast("Error starting EMV card scan: " + e.getMessage()); + } + } + + private void startSimpleCardCheck() { + try { + if (!MyApplication.app.isConnectPaySDK()) { + tvStatus.setText("Connecting to PaySDK..."); + MyApplication.app.bindPaySDKService(); + return; + } + + int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue(); + tvStatus.setText("Simple Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); + + MyApplication.app.readCardOptV2.checkCard(cardType, mSimpleCheckCardCallback, 60); + + } catch (Exception e) { + e.printStackTrace(); + showToast("Error starting card scan: " + e.getMessage()); + } + } + + private void stopCardCheck() { + try { + if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) { + MyApplication.app.readCardOptV2.cancelCheckCard(); + } + isProcessing = false; + btnAction.setText("Start Scanning"); + updateUI(); + } catch (Exception e) { + android.util.Log.e(TAG, "Error stopping card check: " + e.getMessage()); + } + } + + // ====== SIMPLE CARD DETECTION CALLBACK ====== + private final CheckCardCallbackV2 mSimpleCheckCardCallback = new CheckCardCallbackV2.Stub() { + @Override + public void findMagCard(Bundle info) throws RemoteException { + android.util.Log.d(TAG, "Simple Mode: findMagCard callback triggered"); + runOnUiThread(() -> handleSimpleCardResult(info, "MAGNETIC")); + } + + @Override + public void findICCard(String atr) throws RemoteException { + Bundle info = new Bundle(); + info.putString("atr", atr); + runOnUiThread(() -> handleSimpleCardResult(info, "IC")); + } + + @Override + public void findRFCard(String uuid) throws RemoteException { + Bundle info = new Bundle(); + info.putString("uuid", uuid); + runOnUiThread(() -> handleSimpleCardResult(info, "NFC")); + } + + @Override + public void onError(int code, String message) throws RemoteException { + runOnUiThread(() -> { + showToast("Card error: " + message); + stopCardCheck(); + }); + } + + @Override + public void findICCardEx(Bundle info) throws RemoteException { + runOnUiThread(() -> handleSimpleCardResult(info, "IC")); + } + + @Override + public void findRFCardEx(Bundle info) throws RemoteException { + runOnUiThread(() -> handleSimpleCardResult(info, "NFC")); + } + + @Override + public void onErrorEx(Bundle info) throws RemoteException { + runOnUiThread(() -> { + String msg = info.getString("message", "Unknown error"); + showToast("Card error: " + msg); + stopCardCheck(); + }); + } + }; + + // ====== EMV CARD DETECTION CALLBACK ====== + private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() { + @Override + public void findMagCard(Bundle info) throws RemoteException { + runOnUiThread(() -> handleSimpleCardResult(info, "MAGNETIC")); + } + + @Override + public void findICCard(String atr) throws RemoteException { + MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0); + mCardType = AidlConstantsV2.CardType.IC.getValue(); + runOnUiThread(() -> startEMVTransactionProcess()); + } + + @Override + public void findRFCard(String uuid) throws RemoteException { + mCardType = AidlConstantsV2.CardType.NFC.getValue(); + runOnUiThread(() -> startEMVTransactionProcess()); + } + + @Override + public void onError(int code, String message) throws RemoteException { + runOnUiThread(() -> { + showToast("EMV Error: " + message); + stopCardCheck(); + }); + } + + @Override + public void findICCardEx(Bundle info) throws RemoteException { + mCardType = AidlConstantsV2.CardType.IC.getValue(); + runOnUiThread(() -> startEMVTransactionProcess()); + } + + @Override + public void findRFCardEx(Bundle info) throws RemoteException { + mCardType = AidlConstantsV2.CardType.NFC.getValue(); + runOnUiThread(() -> startEMVTransactionProcess()); + } + + @Override + public void onErrorEx(Bundle info) throws RemoteException { + runOnUiThread(() -> { + String msg = info.getString("message", "Unknown error"); + showToast("EMV Error: " + msg); + stopCardCheck(); + }); + } + }; + + private void handleSimpleCardResult(Bundle info, String cardType) { + // For simple mode, navigate directly to results + Intent intent = new Intent(this, CreditCardActivity.class); + intent.putExtra("TRANSACTION_AMOUNT", transactionAmount); + intent.putExtra("CARD_TYPE", cardType); + intent.putExtra("CARD_DATA", info); + intent.putExtra("EMV_MODE", isEMVMode); + startActivity(intent); + finish(); + } + + private void startEMVTransactionProcess() { + // Prevent multiple calls + if (mProcessStep != 0) { + Log.d(TAG, "EMV transaction already in progress (step: " + mProcessStep + ") - ignoring call"); + return; + } + + android.util.Log.d(TAG, "Starting EMV transaction process"); + mProcessStep = 1; // Set step to prevent multiple calls + + try { + // Ensure EMV is properly reset before starting + mEMVOptV2.initEmvProcess(); + + // Small delay to ensure reset is complete + new Handler(Looper.getMainLooper()).postDelayed(() -> { + try { + // Double check we're still in the right state + if (mProcessStep <= 0) { + Log.d(TAG, "EMV process was cancelled - not starting"); + return; + } + + Bundle bundle = new Bundle(); + bundle.putString("amount", transactionAmount); + bundle.putString("transType", "00"); + + if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { + bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD); + } else { + bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD); + } + bundle.putInt("cardType", mCardType); + + tvStatus.setText("EMV processing started...\nReading card data..."); + + Log.d(TAG, "Starting transactProcessEx with reset EMV"); + mEMVOptV2.transactProcessEx(bundle, mEMVListener); + + } catch (Exception e) { + Log.e(TAG, "Error in delayed EMV start: " + e.getMessage(), e); + resetScanningState(); + showToast("Error starting EMV: " + e.getMessage()); + } + }, 300); // 300ms delay + + } catch (Exception e) { + android.util.Log.e(TAG, "Error starting EMV transaction: " + e.getMessage()); + e.printStackTrace(); + resetScanningState(); + showToast("Error starting EMV transaction: " + e.getMessage()); + } + } + + // ====== EMV LISTENER ====== + private final EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() { + + @Override + public void onWaitAppSelect(List appNameList, boolean isFirstSelect) throws RemoteException { + android.util.Log.d(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect); + mProcessStep = EMV_APP_SELECT; + String[] candidateNames = getCandidateNames(appNameList); + mHandler.obtainMessage(EMV_APP_SELECT, candidateNames).sendToTarget(); + } + + @Override + public void onAppFinalSelect(String tag9F06Value) throws RemoteException { + android.util.Log.d(TAG, "onAppFinalSelect tag9F06Value:" + tag9F06Value); + mProcessStep = EMV_FINAL_APP_SELECT; + mHandler.obtainMessage(EMV_FINAL_APP_SELECT, tag9F06Value).sendToTarget(); + } + + @Override + public void onConfirmCardNo(String cardNo) throws RemoteException { + android.util.Log.d(TAG, "onConfirmCardNo cardNo:" + maskCardNumber(cardNo)); + mCardNo = cardNo; + mProcessStep = EMV_CONFIRM_CARD_NO; + mHandler.obtainMessage(EMV_CONFIRM_CARD_NO).sendToTarget(); + } + + @Override + public void onRequestShowPinPad(int pinType, int remainTime) throws RemoteException { + android.util.Log.d(TAG, "onRequestShowPinPad pinType:" + pinType + " remainTime:" + remainTime); + mPinType = pinType; + if (mCardNo == null) { + mCardNo = getCardNo(); + } + mProcessStep = EMV_SHOW_PIN_PAD; + mHandler.obtainMessage(EMV_SHOW_PIN_PAD).sendToTarget(); + } + + @Override + public void onRequestSignature() throws RemoteException { + android.util.Log.d(TAG, "onRequestSignature"); + mProcessStep = EMV_SIGNATURE; + mHandler.obtainMessage(EMV_SIGNATURE).sendToTarget(); + } + + @Override + public void onCertVerify(int certType, String certInfo) throws RemoteException { + android.util.Log.d(TAG, "onCertVerify certType:" + certType + " certInfo:" + certInfo); + mCertInfo = certInfo; + mProcessStep = EMV_CERT_VERIFY; + mHandler.obtainMessage(EMV_CERT_VERIFY).sendToTarget(); + } + + @Override + public void onOnlineProc() throws RemoteException { + android.util.Log.d(TAG, "onOnlineProcess"); + mProcessStep = EMV_ONLINE_PROCESS; + mHandler.obtainMessage(EMV_ONLINE_PROCESS).sendToTarget(); + } + + @Override + public void onCardDataExchangeComplete() throws RemoteException { + android.util.Log.d(TAG, "onCardDataExchangeComplete"); + if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { + MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0); + } + } + + @Override + public void onTransResult(int code, String desc) throws RemoteException { + if (mCardNo == null) { + mCardNo = getCardNo(); + } + android.util.Log.d(TAG, "onTransResult code:" + code + " desc:" + desc); + + if (code == 1 || code == 2 || code == 5 || code == 6) { + mHandler.obtainMessage(EMV_TRANS_SUCCESS, code, code, desc).sendToTarget(); + } else { + mHandler.obtainMessage(EMV_TRANS_FAIL, code, code, desc).sendToTarget(); + } + } + + @Override + public void onConfirmationCodeVerified() throws RemoteException { + android.util.Log.d(TAG, "onConfirmationCodeVerified"); + } + + @Override + public void onRequestDataExchange(String cardNo) throws RemoteException { + android.util.Log.d(TAG, "onRequestDataExchange,cardNo:" + cardNo); + mEMVOptV2.importDataExchangeStatus(0); + } + + @Override + public void onTermRiskManagement() throws RemoteException { + android.util.Log.d(TAG, "onTermRiskManagement"); + mEMVOptV2.importTermRiskManagementStatus(0); + } + + @Override + public void onPreFirstGenAC() throws RemoteException { + android.util.Log.d(TAG, "onPreFirstGenAC"); + mEMVOptV2.importPreFirstGenACStatus(0); + } + + @Override + public void onDataStorageProc(String[] containerID, String[] containerContent) throws RemoteException { + android.util.Log.d(TAG, "onDataStorageProc"); + String[] tags = new String[0]; + String[] values = new String[0]; + mEMVOptV2.importDataStorage(tags, values); + } + }; + + // ====== EMV DIALOG HANDLERS ====== + private void showAppSelectDialog(String[] candidateNames) { + mAppSelectDialog = new AlertDialog.Builder(this) + .setTitle("Please select application") + .setNegativeButton("Cancel", (dialog, which) -> importAppSelect(-1)) + .setPositiveButton("OK", (dialog, which) -> importAppSelect(mSelectIndex)) + .setSingleChoiceItems(candidateNames, 0, (dialog, which) -> mSelectIndex = which) + .create(); + mSelectIndex = 0; + mAppSelectDialog.show(); + } + + private void showCardNumberConfirmation() { + runOnUiThread(() -> { + String displayText = "Card Number: " + maskCardNumber(mCardNo) + + "\n\nConfirming card number automatically..."; + tvStatus.setText(displayText); + + // Auto-confirm after short delay to avoid manual interaction + btnAction.postDelayed(() -> { + Log.d(TAG, "Auto-confirming card number"); + btnAction.setText("Processing..."); + importCardNoStatus(0); + }, 1500); + + mProcessStep = EMV_CONFIRM_CARD_NO; + }); + } + + private void showCertificateVerification() { + runOnUiThread(() -> { + String displayText = "Certificate Information:\n" + mCertInfo + + "\n\nConfirming certificate automatically..."; + tvStatus.setText(displayText); + + // Auto-confirm after short delay + btnAction.postDelayed(() -> { + Log.d(TAG, "Auto-confirming certificate"); + btnAction.setText("Processing..."); + importCertStatus(0); + }, 1500); + + mProcessStep = EMV_CERT_VERIFY; + }); + } + + // ====== PIN PAD METHODS ====== + private void initPinPad() { + android.util.Log.e(TAG, "========== PIN PAD INITIALIZATION =========="); + try { + if (mPinPadOptV2 == null) { + throw new IllegalStateException("PIN Pad service not available"); + } + + if (mCardNo == null || mCardNo.length() < 13) { + throw new IllegalArgumentException("Invalid card number for PIN"); + } + + PinPadConfigV2 pinPadConfig = new PinPadConfigV2(); + pinPadConfig.setPinPadType(0); + pinPadConfig.setPinType(mPinType); + pinPadConfig.setOrderNumKey(false); + + String panForPin = mCardNo.substring(mCardNo.length() - 13, mCardNo.length() - 1); + byte[] panBytes = panForPin.getBytes("US-ASCII"); + pinPadConfig.setPan(panBytes); + + pinPadConfig.setTimeout(60 * 1000); + pinPadConfig.setPinKeyIndex(12); + pinPadConfig.setMaxInput(12); + pinPadConfig.setMinInput(0); + pinPadConfig.setKeySystem(0); + pinPadConfig.setAlgorithmType(0); + + runOnUiThread(() -> { + tvStatus.setText("PIN Input Required\n\nPIN pad is ready\nPlease enter your PIN on the device"); + btnAction.setText("PIN Pad Active"); + btnAction.setEnabled(false); + }); + + mPinPadOptV2.initPinPad(pinPadConfig, mPinPadListener); + + } catch (Exception e) { + android.util.Log.e(TAG, "PIN pad initialization failed: " + e.getMessage()); + mHandler.obtainMessage(PIN_ERROR, -1, -1, + "PIN Error: " + e.getMessage()).sendToTarget(); + } + } + + private final PinPadListenerV2 mPinPadListener = new PinPadListenerV2.Stub() { + @Override + public void onPinLength(int len) throws RemoteException { + android.util.Log.d(TAG, "PIN input length: " + len); + runOnUiThread(() -> { + String dots = ""; + for (int i = 0; i < len; i++) { + dots += "• "; + } + tvStatus.setText("PIN Input: " + dots + "\n\nEntering PIN... (" + len + " digits)"); + }); + } + + @Override + public void onConfirm(int i, byte[] pinBlock) throws RemoteException { + android.util.Log.d(TAG, "PIN input confirmed"); + + runOnUiThread(() -> { + btnAction.setEnabled(true); + btnAction.setText("Processing..."); + tvStatus.setText("PIN confirmed, processing..."); + }); + + if (pinBlock != null) { + String hexStr = ByteUtil.bytes2HexStr(pinBlock); + android.util.Log.d(TAG, "PIN block received: " + hexStr); + mHandler.obtainMessage(PIN_CLICK_PIN, pinBlock).sendToTarget(); + } else { + android.util.Log.d(TAG, "PIN bypass confirmed"); + mHandler.obtainMessage(PIN_CLICK_CONFIRM).sendToTarget(); + } + } + + @Override + public void onCancel() throws RemoteException { + android.util.Log.d(TAG, "PIN input cancelled by user"); + + runOnUiThread(() -> { + btnAction.setEnabled(true); + btnAction.setText("Start Scanning"); + tvStatus.setText("PIN input cancelled"); + }); + + mHandler.obtainMessage(PIN_CLICK_CANCEL).sendToTarget(); + } + + @Override + public void onError(int code) throws RemoteException { + android.util.Log.e(TAG, "PIN pad error: " + code); + String msg = AidlErrorCodeV2.valueOf(code).getMsg(); + + runOnUiThread(() -> { + btnAction.setEnabled(true); + btnAction.setText("Start Scanning"); + tvStatus.setText("PIN error: " + msg); + }); + + mHandler.obtainMessage(PIN_ERROR, code, code, msg).sendToTarget(); + } + + @Override + public void onHover(int event, byte[] data) throws RemoteException { + android.util.Log.d(TAG, "PIN pad hover event: " + event); + } + }; + + // ====== EMV RESET METHODS ====== + private void resetEMVAndRetry() { + new Thread(() -> { + try { + Log.d(TAG, "Resetting EMV process..."); + + // Cancel any existing card operations first + if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) { + MyApplication.app.readCardOptV2.cancelCheckCard(); + } + + Thread.sleep(500); // Wait for cancel to complete + + // Reset EMV process + if (mEMVOptV2 != null) { + mEMVOptV2.initEmvProcess(); // Reset EMV state + } + + Thread.sleep(1500); // Wait longer for complete reset + + runOnUiThread(() -> { + Log.d(TAG, "EMV reset complete - ready for retry"); + resetScanningState(); + showToast("Ready to scan - please try again"); + }); + + } catch (Exception e) { + Log.e(TAG, "Error resetting EMV: " + e.getMessage(), e); + runOnUiThread(() -> { + resetScanningState(); + showToast("Reset failed - please try again"); + }); + } + }).start(); + } + + private void resetScanningState() { + Log.d(TAG, "Resetting scanning state"); + isProcessing = false; + isButtonProcessing = false; + mProcessStep = 0; + btnAction.setText("Start Scanning"); + btnAction.setEnabled(true); + updateUI(); // Reset UI to initial state + } + + // ====== HELPER METHODS ====== + private void navigateToResults() { + Intent intent = new Intent(this, CreditCardActivity.class); + intent.putExtra("TRANSACTION_AMOUNT", transactionAmount); + intent.putExtra("CARD_TYPE", mCardType == AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC"); + intent.putExtra("EMV_MODE", isEMVMode); + intent.putExtra("CARD_NO", mCardNo); + startActivity(intent); + finish(); + } + + private void mockOnlineProcess() { + new Thread(() -> { + try { + runOnUiThread(() -> tvStatus.setText("Processing online authorization...")); + Thread.sleep(2000); + + try { + String[] tags = {"71", "72", "91", "8A", "89"}; + String[] values = {"", "", "", "", ""}; + byte[] out = new byte[1024]; + int len = mEMVOptV2.importOnlineProcStatus(0, tags, values, out); + if (len < 0) { + android.util.Log.e(TAG, "importOnlineProcessStatus error,code:" + len); + } + } catch (Exception e) { + e.printStackTrace(); + } + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + } + + private String getCardNo() { + // Implementation from original code + return ""; + } + + private String maskCardNumber(String cardNo) { + if (cardNo == null || cardNo.length() < 8) { + return cardNo; + } + String first4 = cardNo.substring(0, 4); + String last4 = cardNo.substring(cardNo.length() - 4); + StringBuilder middle = new StringBuilder(); + for (int i = 0; i < cardNo.length() - 8; i++) { + middle.append("*"); + } + return first4 + middle.toString() + last4; + } + + private String[] getCandidateNames(List candiList) { + if (candiList == null || candiList.size() == 0) return new String[0]; + String[] result = new String[candiList.size()]; + for (int i = 0; i < candiList.size(); i++) { + EMVCandidateV2 candi = candiList.get(i); + String name = candi.appPreName; + name = TextUtils.isEmpty(name) ? candi.appLabel : name; + name = TextUtils.isEmpty(name) ? candi.appName : name; + name = TextUtils.isEmpty(name) ? "" : name; + result[i] = name; + } + return result; + } + + // ====== EMV IMPORT METHODS ====== + private void importAppSelect(int selectIndex) { + try { + mEMVOptV2.importAppSelect(selectIndex); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void importFinalAppSelectStatus(int status) { + try { + mEMVOptV2.importAppFinalSelectStatus(status); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + + private void importCardNoStatus(int status) { + try { + mEMVOptV2.importCardNoStatus(status); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void importCertStatus(int status) { + try { + mEMVOptV2.importCertStatus(status); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void importPinInputStatus(int inputResult) { + try { + mEMVOptV2.importPinInputStatus(mPinType, inputResult); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void importSignatureStatus(int status) { + try { + mEMVOptV2.importSignatureStatus(status); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void showToast(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + + @Override + protected void onDestroy() { + Log.d(TAG, "onDestroy - cleaning up EMV resources"); + + try { + // Cancel card operations + if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) { + MyApplication.app.readCardOptV2.cancelCheckCard(); + } + + // Reset EMV process + if (mEMVOptV2 != null) { + mEMVOptV2.initEmvProcess(); + } + + } catch (Exception e) { + Log.e(TAG, "Error cleaning up EMV: " + e.getMessage()); + } + + super.onDestroy(); + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_create_transaction.xml b/app/src/main/res/layout/activity_create_transaction.xml new file mode 100644 index 0000000..ec0b3b2 --- /dev/null +++ b/app/src/main/res/layout/activity_create_transaction.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +