fixed signature

This commit is contained in:
moon 2026-08-18 13:55:21 +06:30
parent 62bae0df0c
commit af9ef8367c
16 changed files with 83 additions and 40 deletions

View File

@ -89,4 +89,9 @@
Refund - 1000 Refund - 1000
- 6. Reprint Last Settlement Report - 6. Reprint Last Settlement Report
Same process with settlement Same process with settlement
## [2.41]
- clear batch time fix
- removed mock qr button
- support Id fix for signature

View File

@ -39,8 +39,8 @@ android {
applicationId "com.utsmm.kbz.uat" applicationId "com.utsmm.kbz.uat"
versionNameSuffix "-uat" versionNameSuffix "-uat"
resValue "string", "app_name", "KBZ-UAT" resValue "string", "app_name", "KBZ-UAT"
versionCode 44 versionCode 46
versionName "2.39" versionName "2.41"
} }
prod { prod {

View File

@ -573,6 +573,7 @@ public class ProcessingFragment extends DataBindingFragment {
request.setDE39(isSuccess ? "A" : "E"); request.setDE39(isSuccess ? "A" : "E");
request.setDE41(terminalId); request.setDE41(terminalId);
request.setDE42(merchantId); request.setDE42(merchantId);
request.setHostId(SystemParamsOperation.getInstance().getHostId());
request.setDE49(mapCurrency(payDetail.getCurrencyCode())); request.setDE49(mapCurrency(payDetail.getCurrencyCode()));
request.setSerial(serial); request.setSerial(serial);
request.setInvoiceNumber(payDetail.getInvoiceNo()); request.setInvoiceNumber(payDetail.getInvoiceNo());

View File

@ -169,7 +169,15 @@ public class SignatureFragment extends DataBindingFragment {
} else { } else {
// Handle manual signature on paper // Handle manual signature on paper
sharedViewModel.signBitmap = null; // No digital signature sharedViewModel.signBitmap = null; // No digital signature
LogUtil.d(TAG, "Manual signature on paper selected - no digital signature stored"); PayDetail payDetail = sharedViewModel.payDetail.getValue();
EReceiptCardRequest request = EReceiptHelper.buildEReceiptCardReceipt(
requireContext(),
payDetail,
true,
BaseErrorCode.getCode(payDetail.getTradeAnswerCode()),
null);
sharedViewModel.pushReceipt(request);
LogUtil.d(TAG, "Manual signature on paper selected - receipt submitted without image");
callNextScreen(); callNextScreen();
} }
} }

View File

@ -63,7 +63,7 @@ public class QRPayFragment extends DataBindingFragment {
features.add(new QRPayItem("Refund", R.drawable.ic_refund, true)); features.add(new QRPayItem("Refund", R.drawable.ic_refund, true));
} }
features.add(new QRPayItem("History", R.drawable.ic_history, true)); features.add(new QRPayItem("History", R.drawable.ic_history, true));
features.add(new QRPayItem("Mock QR", R.drawable.ic_qr_pay, true)); // features.add(new QRPayItem("Mock QR", R.drawable.ic_qr_pay, true));
// List<QRPayItem> features = List.of( // List<QRPayItem> features = List.of(
// new QRPayItem("Sale", R.drawable.ic_qr_pay, true), // new QRPayItem("Sale", R.drawable.ic_qr_pay, true),

View File

@ -56,6 +56,7 @@ public class EReceiptHelper {
request.setDE39(isSuccess ? "A" : "E"); request.setDE39(isSuccess ? "A" : "E");
request.setDE41(terminalId); request.setDE41(terminalId);
request.setDE42(merchantId); request.setDE42(merchantId);
request.setHostId(SystemParamsOperation.getInstance().getHostId());
request.setDE49(mapCurrency(payDetail.getCurrencyCode())); request.setDE49(mapCurrency(payDetail.getCurrencyCode()));
request.setSerial(serial); request.setSerial(serial);
request.setInvoiceNumber(payDetail.getInvoiceNo()); request.setInvoiceNumber(payDetail.getInvoiceNo());

View File

@ -460,7 +460,7 @@ public class TMSSetupsImpl implements TMSSetups{
SystemParamsOperation.getInstance().setRandomPinPad(parseBoolean(data)); SystemParamsOperation.getInstance().setRandomPinPad(parseBoolean(data));
} else if (TextUtils.equals(name,"clear_batch_time")) { } else if (TextUtils.equals(name,"clear_batch_time")) {
SystemParamsOperation.getInstance().setClearBatchTime("22:00"); SystemParamsOperation.getInstance().setClearBatchTime(data);
} else if (TextUtils.equals(name,"alert_sound_enable")) { } else if (TextUtils.equals(name,"alert_sound_enable")) {
SystemParamsOperation.getInstance().setAlertSound(parseBoolean(data)); SystemParamsOperation.getInstance().setAlertSound(parseBoolean(data));

View File

@ -12,6 +12,19 @@ import java.util.TreeMap;
public class SignGenerateTest { public class SignGenerateTest {
@Test
public void tmsSignatureMatchesToolForFixedTimestamp() {
String secret = "y812J21lhha11OS";
String timestamp = "1786609802730";
String signature = EReceiptHelper.sha256("{}" + secret + timestamp);
assertEquals(
"3f1bfe50289d706074560fa22f424f3665c9c3022362aa89a6ea9c37469a0774",
signature
);
}
private static final String APP_KEY = "UTSMMuat@2025"; private static final String APP_KEY = "UTSMMuat@2025";
private static final String EXPECTED_SIGN = "D86AE09024C6581273C27B95A3BDBDBF3AC8EC20191365C8047ED6C42FC00DA5"; private static final String EXPECTED_SIGN = "D86AE09024C6581273C27B95A3BDBDBF3AC8EC20191365C8047ED6C42FC00DA5";
@ -425,4 +438,4 @@ public class SignGenerateTest {
// This test is for debugging - it doesn't need to pass // This test is for debugging - it doesn't need to pass
assertTrue("This test is for debugging purposes", true); assertTrue("This test is for debugging purposes", true);
} }
} }

View File

@ -11,8 +11,8 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"attributes": [], "attributes": [],
"versionCode": 44, "versionCode": 46,
"versionName": "2.39-uat", "versionName": "2.41-uat",
"outputFile": "app-uat-release.apk" "outputFile": "app-uat-release.apk"
} }
], ],

View File

@ -116,7 +116,7 @@ public class EmvParamOperation extends ParamOperation{
processPayWave(); processPayWave();
processPayPass(); processPayPass();
processQuickPass(); processQuickPass();
// processJSpeedy(); processJSpeedy();
processMPUCtls(); processMPUCtls();
processChips(); processChips();
processRids(); processRids();

View File

@ -14,6 +14,7 @@ public class EReceiptCardRequest {
private String DE7; private String DE7;
private String DE4; private String DE4;
private String appId; private String appId;
private String hostId;
private String invoiceNumber; private String invoiceNumber;
private String DE11; private String DE11;
private String description; private String description;
@ -72,6 +73,10 @@ public class EReceiptCardRequest {
public String getAppId(){ public String getAppId(){
return appId; return appId;
} }
public String getHostId(){
return hostId;
}
public String getDE55(){return DE55;} public String getDE55(){return DE55;}
@ -133,6 +138,10 @@ public class EReceiptCardRequest {
this.appId = appId; this.appId = appId;
} }
public void setHostId(String hostId){
this.hostId = hostId;
}
public void setInvoiceNumber(String invoiceNumber){ public void setInvoiceNumber(String invoiceNumber){
this.invoiceNumber = invoiceNumber; this.invoiceNumber = invoiceNumber;
} }

View File

@ -41,10 +41,7 @@ import com.utsmyanmar.baselib.network.model.KPayRefund;
import com.utsmyanmar.baselib.repo.local.PayWaveRepository; import com.utsmyanmar.baselib.repo.local.PayWaveRepository;
import com.utsmyanmar.baselib.util.EReceiptHelper; import com.utsmyanmar.baselib.util.EReceiptHelper;
import com.utsmyanmar.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@ -128,7 +125,6 @@ public class Repository {
} }
public Observable<EReceiptResponse> sendReceipt(Object body){ public Observable<EReceiptResponse> sendReceipt(Object body){
String apiSecret = BuildConfig.ERECEIPT_SECRET; String apiSecret = BuildConfig.ERECEIPT_SECRET;
// LogUtil.d("kmk", "receipt secret =>" + apiSecret);
String timestamp = String.valueOf(System.currentTimeMillis()); String timestamp = String.valueOf(System.currentTimeMillis());
String bodyString = new Gson().toJson(body); String bodyString = new Gson().toJson(body);
String dataToHash = bodyString + apiSecret + timestamp; String dataToHash = bodyString + apiSecret + timestamp;
@ -141,7 +137,6 @@ public class Repository {
String timestamp = String.valueOf(System.currentTimeMillis()); String timestamp = String.valueOf(System.currentTimeMillis());
String bodyString = buildMultipartSignaturePayload(request); String bodyString = buildMultipartSignaturePayload(request);
String dataToHash = bodyString + apiSecret + timestamp; String dataToHash = bodyString + apiSecret + timestamp;
// LogUtil.d("HTTP", dataToHash);
String apiKey = EReceiptHelper.sha256(dataToHash); String apiKey = EReceiptHelper.sha256(dataToHash);
Map<String, RequestBody> fields = buildFormFields(request); Map<String, RequestBody> fields = buildFormFields(request);
@ -155,31 +150,33 @@ public class Repository {
} }
private String buildMultipartSignaturePayload(EReceiptCardRequest request) { private String buildMultipartSignaturePayload(EReceiptCardRequest request) {
Map<String, String> flatBody = new LinkedHashMap<>(); Map<String, String> flatBody = new TreeMap<>();
putIfNotEmpty(flatBody, "DE2", request.getDE2()); putIfNotEmpty(flatBody, "DE2", request.getDE2());
putIfNotEmpty(flatBody, "DE3", request.getDE3());
putIfNotEmpty(flatBody, "DE4", request.getDE4());
putIfNotEmpty(flatBody, "DE7", request.getDE7());
putIfNotEmpty(flatBody, "DE11", request.getDE11());
putIfNotEmpty(flatBody, "DE14", request.getDE14());
putIfNotEmpty(flatBody, "DE22", request.getDE22()); putIfNotEmpty(flatBody, "DE22", request.getDE22());
putIfNotEmpty(flatBody, "DE37", request.getDE37()); putIfNotEmpty(flatBody, "DE3", request.getDE3());
putIfNotEmpty(flatBody, "DE38", request.getDE38());
putIfNotEmpty(flatBody, "DE39", request.getDE39()); putIfNotEmpty(flatBody, "DE39", request.getDE39());
putIfNotEmpty(flatBody, "DE41", request.getDE41()); putIfNotEmpty(flatBody, "DE41", request.getDE41());
putIfNotEmpty(flatBody, "DE42", request.getDE42()); putIfNotEmpty(flatBody, "DE42", request.getDE42());
putIfNotEmpty(flatBody, "DE43", request.getDE43());
putIfNotEmpty(flatBody, "DE48", request.getDE48());
putIfNotEmpty(flatBody, "DE49", request.getDE49());
putIfNotEmpty(flatBody, "DE55", request.getDE55());
putIfNotEmpty(flatBody, "appId", request.getAppId());
putIfNotEmpty(flatBody, "batchNumber", request.getBatchNumber());
putIfNotEmpty(flatBody, "cardLabel", request.getCardLabel());
putIfNotEmpty(flatBody, "description", request.getDescription());
putIfNotEmpty(flatBody, "invoiceNumber", request.getInvoiceNumber());
putIfNotEmpty(flatBody, "serial", request.getSerial()); putIfNotEmpty(flatBody, "serial", request.getSerial());
putIfNotEmpty(flatBody, "DE49", request.getDE49());
putIfNotEmpty(flatBody, "DE37", request.getDE37());
putIfNotEmpty(flatBody, "DE7", request.getDE7());
putIfNotEmpty(flatBody, "DE4", request.getDE4());
putIfNotEmpty(flatBody, "appId", request.getAppId());
putIfNotEmpty(flatBody, "hostId", request.getHostId());
putIfNotEmpty(flatBody, "invoiceNumber", request.getInvoiceNumber());
putIfNotEmpty(flatBody, "DE11", request.getDE11());
putIfNotEmpty(flatBody, "description", request.getDescription());
putIfNotEmpty(flatBody, "DE38", request.getDE38());
putIfNotEmpty(flatBody, "batchNumber", request.getBatchNumber());
putValueOrEmpty(flatBody, "DE55", request.getDE55());
putIfNotEmpty(flatBody, "DE14", request.getDE14());
putIfNotEmpty(flatBody, "cardLabel", request.getCardLabel());
putIfNotEmpty(flatBody, "DE43", request.getDE43());
putValueOrEmpty(flatBody, "DE48", request.getDE48());
return new Gson().toJson(new TreeMap<>(flatBody)); // DE115 is intentionally omitted because it is sent as the image part.
return new Gson().toJson(flatBody);
} }
private void putIfNotEmpty(Map<String, String> map, String key, String value) { private void putIfNotEmpty(Map<String, String> map, String key, String value) {
@ -188,8 +185,12 @@ public class Repository {
} }
} }
private void putValueOrEmpty(Map<String, String> map, String key, String value) {
map.put(key, value == null || value.trim().isEmpty() ? "" : value);
}
private Map<String, RequestBody> buildFormFields(EReceiptCardRequest request) { private Map<String, RequestBody> buildFormFields(EReceiptCardRequest request) {
Map<String, RequestBody> map = new HashMap<>(); Map<String, RequestBody> map = new TreeMap<>();
putField(map, "DE2", request.getDE2()); putField(map, "DE2", request.getDE2());
putField(map, "DE3", request.getDE3()); putField(map, "DE3", request.getDE3());
putField(map, "DE4", request.getDE4()); putField(map, "DE4", request.getDE4());
@ -203,10 +204,11 @@ public class Repository {
putField(map, "DE41", request.getDE41()); putField(map, "DE41", request.getDE41());
putField(map, "DE42", request.getDE42()); putField(map, "DE42", request.getDE42());
putField(map, "DE43", request.getDE43()); putField(map, "DE43", request.getDE43());
putField(map, "DE48", request.getDE48()); putFieldValueOrEmpty(map, "DE48", request.getDE48());
putField(map, "DE49", request.getDE49()); putField(map, "DE49", request.getDE49());
putField(map, "DE55", request.getDE55()); putFieldValueOrEmpty(map, "DE55", request.getDE55());
putField(map, "appId", request.getAppId()); putField(map, "appId", request.getAppId());
putField(map, "hostId", request.getHostId());
putField(map, "batchNumber", request.getBatchNumber()); putField(map, "batchNumber", request.getBatchNumber());
putField(map, "cardLabel", request.getCardLabel()); putField(map, "cardLabel", request.getCardLabel());
putField(map, "description", request.getDescription()); putField(map, "description", request.getDescription());
@ -221,6 +223,11 @@ public class Repository {
} }
} }
private void putFieldValueOrEmpty(Map<String, RequestBody> map, String key, String value) {
String normalizedValue = value == null || value.trim().isEmpty() ? "" : value;
map.put(key, RequestBody.create(MediaType.parse("text/plain"), normalizedValue));
}
public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);} public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);}
public Completable insertPay(PayDetail payDetail) { return payDetailDao.insertPay(payDetail); } public Completable insertPay(PayDetail payDetail) { return payDetailDao.insertPay(payDetail); }

View File

@ -530,7 +530,7 @@
"currencyCode": "0104" "currencyCode": "0104"
}, },
{ {
"aidEnable": false, "aidEnable": true,
"aid": "A00000006510", "aid": "A00000006510",
"cardScheme": "JCB", "cardScheme": "JCB",
"applicationVersion": "0200", "applicationVersion": "0200",
@ -553,7 +553,7 @@
"currencyCode": "0104" "currencyCode": "0104"
}, },
{ {
"aidEnable": false, "aidEnable": true,
"aid": "A0000000651010", "aid": "A0000000651010",
"cardScheme": "JCB", "cardScheme": "JCB",
"applicationVersion": "0200", "applicationVersion": "0200",
@ -576,7 +576,7 @@
"currencyCode": "0104" "currencyCode": "0104"
}, },
{ {
"aidEnable": false, "aidEnable": true,
"aid": "A0000001570040", "aid": "A0000001570040",
"cardScheme": "JCB", "cardScheme": "JCB",
"applicationVersion": "0200", "applicationVersion": "0200",

View File

@ -46,7 +46,6 @@ public class SystemParamsSettings implements Serializable {
// private String ereceiptAddress = "https://api-tms-uat.kbzbank.com:8443/receipt"; // private String ereceiptAddress = "https://api-tms-uat.kbzbank.com:8443/receipt";
private String tmsAddress = "https://sirius-nest.utsmyanmar.com/api/v1"; private String tmsAddress = "https://sirius-nest.utsmyanmar.com/api/v1";
private String ereceiptAddress = "https://receipt-nest.utsmyanmar.com"; private String ereceiptAddress = "https://receipt-nest.utsmyanmar.com";
// private String ereceiptAddress = "http://192.168.10.104:3003";
private String binValues = ""; private String binValues = "";
private String restrictedBinValues = ""; private String restrictedBinValues = "";