[FIX] 取貨碼/通行碼:封堵付款備援漏出、API 防護、掃碼即時回饋

通行碼漏洞封堵
- BuyDialog 單品模式 NFC 不支援時會強制顯示通行碼(buttonAccessCode)當備援,
  未判斷 isPassCode;改為從屬取貨模組(isPickupModule && isPassCode)才顯示

API 防護(defense-in-depth,UI 漏出入口也擋下)
- submitPassCode/submitPickupCode 開頭加守門:功能關閉即不呼叫 B670/B660,狀態列提示「功能未開放」

掃碼 UX 回饋
- 新增 SET_SCAN_INPUT 事件:掃碼後即時把掃到的碼顯示在輸入框
- 送出驗證前顯示「驗證中…」狀態,取代原本易忽略的遮罩 Toast,避免使用者誤以為沒掃到
- 版號 verPatch 75→76

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sky121113 2026-06-11 14:11:06 +08:00
parent 018da036b1
commit e8b3185b8a
4 changed files with 44 additions and 11 deletions

View File

@ -10,7 +10,7 @@ plugins {
//
// =========================================================================
def verMinor = 1
def verPatch = 75
def verPatch = 76

View File

@ -295,9 +295,13 @@ public class BuyDialog extends DialogAbstract {
binding.buttonNfcCard.setVisibility(View.GONE);
binding.buttonNfcPhone.setVisibility(View.GONE);
binding.buttonNfcRecharge.setVisibility(View.GONE);
binding.buttonAccessCode.setVisibility(View.VISIBLE);
// 通行碼備援入口仍須從屬取貨模組且通行碼開啟才顯示
// 否則 NFC 不支援時會把已關閉的通行碼漏出來
boolean passCodeOn = MyApp.getInstance().getDevSet().isPickupModule()
&& MyApp.getInstance().getDevSet().isPassCode();
binding.buttonAccessCode.setVisibility(passCodeOn ? View.VISIBLE : View.GONE);
binding.buttonVerificationPickup.setVisibility(View.GONE);
binding.layoutVerificationPickup.setVisibility(View.VISIBLE);
binding.layoutVerificationPickup.setVisibility(passCodeOn ? View.VISIBLE : View.GONE);
// binding.layoutNfcPay.setVisibility(View.GONE);
}

View File

@ -46,6 +46,7 @@ public class CheckoutAccessCodeDialog extends DialogAbstract {
CHANGE_DISPATCH(6),
CHANGE_BUY(7),
SET_STATUS_TEXT(8),
SET_SCAN_INPUT(9),
;
private int option;
@ -113,6 +114,11 @@ public class CheckoutAccessCodeDialog extends DialogAbstract {
case SET_STATUS_TEXT:
setStatusText(message);
break;
case SET_SCAN_INPUT:
// 掃碼即時回饋把掃到的內容顯示在輸入框讓使用者確認有掃到
binding.editTextNumber.setText(message);
binding.editTextNumber.setSelection(binding.editTextNumber.getText().length());
break;
}
}
}
@ -269,10 +275,10 @@ public class CheckoutAccessCodeDialog extends DialogAbstract {
if (isRun() && openBarcode) {
int size = usbDev.read(buffer, 5000);
if (size > 5) {
String barCodeString = new String(buffer, 0, size);
getHandlerMain().start(getLogs().getClassName(), getContext().getString(R.string.receive_barcode)+""+ Tools.randomReplace(barCodeString, 0.3)+""+getContext().getString(R.string.send_transaction));//收到條碼送出交易
submitPassCode(barCodeString.trim());
String barCodeString = new String(buffer, 0, size).trim();
// 掃碼即時回饋先把掃到的內容顯示在輸入框再送出驗證
getHandlerMain().start(getClass().getSimpleName(), Option.SET_SCAN_INPUT.getOption(), barCodeString);
submitPassCode(barCodeString);
openBarcode = false;
}
@ -335,6 +341,14 @@ public class CheckoutAccessCodeDialog extends DialogAbstract {
* 成功後保存 code_id MQTT finalizepayment_type=5 通行碼再進入出貨
*/
private void submitPassCode(String code) {
// 防護通行碼從屬取貨模組任一關閉即不開放呼叫 B670即使 UI 漏出入口也擋下
if (!(MyApp.getInstance().getDevSet().isPickupModule() && MyApp.getInstance().getDevSet().isPassCode())) {
openBarcode = true;
getHandlerMain().start(getClass().getSimpleName(), Option.SET_STATUS_TEXT.getOption(), "通行碼功能未開放");
return;
}
// 送出前顯示驗證中避免使用者以為沒反應
getHandlerMain().start(getClass().getSimpleName(), Option.SET_STATUS_TEXT.getOption(), "驗證中…");
new com.unibuy.smartdevice.external.StarCloudAPI(getHandlerMain(), getHandlerMain())
.verifyPassCode(code, new com.unibuy.smartdevice.external.StarCloudAPI.VerificationCallback() {
@Override

View File

@ -46,6 +46,7 @@ public class FontendPickupCodeDialog extends DialogAbstract {
CHANGE_DISPATCH(6),
CHANGE_BUY(7),
SET_STATUS_TEXT(8),
SET_SCAN_INPUT(9),
;
private int option;
@ -151,6 +152,11 @@ public class FontendPickupCodeDialog extends DialogAbstract {
case SET_STATUS_TEXT:
setStatusText(message);
break;
case SET_SCAN_INPUT:
// 掃碼即時回饋把掃到的內容顯示在輸入框讓使用者確認有掃到
binding.editTextNumber.setText(message);
binding.editTextNumber.setSelection(binding.editTextNumber.getText().length());
break;
}
}
};
@ -313,13 +319,14 @@ public class FontendPickupCodeDialog extends DialogAbstract {
if (isRun() && openBarcode) {
int size = usbDev.read(buffer, 5000);
if (size > 5) {
String barCodeString = new String(buffer, 0, size);
getHandlerMain().start(getLogs().getClassName(), getCtx().getString(R.string.receive_barcode)+""+ Tools.randomReplace(barCodeString, 0.3)+""+getCtx().getString(R.string.send_transaction));//收到條碼,送出交易
String barCodeString = new String(buffer, 0, size).trim();
// 掃碼即時回饋先把掃到的內容顯示在輸入框再送出驗證
getHandlerMain().start(getClass().getSimpleName(), Option.SET_SCAN_INPUT.getOption(), barCodeString);
if ("pickup_sheet".equals(MyApp.getInstance().getShoppingMode())) {
submitViaMemberVerification(barCodeString.trim());
submitViaMemberVerification(barCodeString);
} else {
submitPickupCode(barCodeString.trim());
submitPickupCode(barCodeString);
}
openBarcode = false;
@ -389,6 +396,14 @@ public class FontendPickupCodeDialog extends DialogAbstract {
* finalizepayment_type=6 取貨碼再進入取貨確認 出貨
*/
private void submitPickupCode(String code) {
// 防護取貨碼從屬取貨模組任一關閉即不開放呼叫 B660即使 UI 漏出入口也擋下
if (!(MyApp.getInstance().getDevSet().isPickupModule() && MyApp.getInstance().getDevSet().isPickupCode())) {
openBarcode = true;
getHandlerMain().start(getClass().getSimpleName(), Option.SET_STATUS_TEXT.getOption(), "取貨碼功能未開放");
return;
}
// 送出前顯示驗證中避免使用者以為沒反應
getHandlerMain().start(getClass().getSimpleName(), Option.SET_STATUS_TEXT.getOption(), "驗證中…");
new com.unibuy.smartdevice.external.StarCloudAPI(getHandlerMain(), getHandlerMain())
.verifyPickupCode(code, new com.unibuy.smartdevice.external.StarCloudAPI.PickupVerificationCallback() {
@Override