diff --git a/app/build.gradle b/app/build.gradle index b46e78d..bd4afd4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { applicationId "com.utsmm.kbz" minSdk 24 targetSdk 33 - versionCode 1 - versionName "1.0" + versionCode 2 + versionName "1.01" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } diff --git a/app/sit/release/baselineProfiles/0/app-sit-release.dm b/app/sit/release/baselineProfiles/0/app-sit-release.dm new file mode 100644 index 0000000..3fd91cb Binary files /dev/null and b/app/sit/release/baselineProfiles/0/app-sit-release.dm differ diff --git a/app/sit/release/baselineProfiles/1/app-sit-release.dm b/app/sit/release/baselineProfiles/1/app-sit-release.dm new file mode 100644 index 0000000..5ef1955 Binary files /dev/null and b/app/sit/release/baselineProfiles/1/app-sit-release.dm differ diff --git a/app/sit/release/output-metadata.json b/app/sit/release/output-metadata.json new file mode 100644 index 0000000..4a6e51c --- /dev/null +++ b/app/sit/release/output-metadata.json @@ -0,0 +1,37 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.utsmm.kbz.sit", + "variantName": "sitRelease", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 2, + "versionName": "1.01-sit", + "outputFile": "app-sit-release.apk" + } + ], + "elementType": "File", + "baselineProfiles": [ + { + "minApi": 28, + "maxApi": 30, + "baselineProfiles": [ + "baselineProfiles/1/app-sit-release.dm" + ] + }, + { + "minApi": 31, + "maxApi": 2147483647, + "baselineProfiles": [ + "baselineProfiles/0/app-sit-release.dm" + ] + } + ], + "minSdkVersionForDexing": 24 +} \ No newline at end of file diff --git a/app/src/main/java/com/utsmm/kbz/ui/kpay/KPayViewModel.java b/app/src/main/java/com/utsmm/kbz/ui/kpay/KPayViewModel.java index b0a393a..f7ec8a0 100644 --- a/app/src/main/java/com/utsmm/kbz/ui/kpay/KPayViewModel.java +++ b/app/src/main/java/com/utsmm/kbz/ui/kpay/KPayViewModel.java @@ -42,8 +42,11 @@ import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.inject.Inject; @@ -190,10 +193,37 @@ public class KPayViewModel extends ViewModel { return createQR(amount, String.valueOf(System.currentTimeMillis()),mid); } + private int expireTime = 1; + + private void calculateTimeoutMinutes() { + String data = SystemParamsOperation.getInstance().getWaveIntervalTime(); + if (data == null || !data.contains("-")) { + data = "15-60"; // fallback + } + + try { + String[] parts = data.split("-"); + int waitingTimeSec = Integer.parseInt(parts[1]); // only take second part + + // convert seconds → minutes + expireTime = waitingTimeSec / 60; + + // enforce limits 1–120 + if (expireTime < 1) expireTime = 1; + if (expireTime > 120) expireTime = 120; + + } catch (Exception e) { + expireTime = 1; // safety fallback + LogUtil.d(TAG, "Invalid format for timeout config: " + data); + } + } + public KPayQRRequest.QrRequest createQR(String amount, String time,String mid) { String merchOrderId = "NEX"+SystemParamsOperation.getInstance().getCurrentSerialNum()+generateRandomTwoChars(); String serialNum = TerminalUtil.getInstance().getSerialNo(); String nonceStr = generateNonceStr(); // Generate random nonce_str + calculateTimeoutMinutes(); + String timeoutExpress = expireTime + "m"; Map bizContent = new HashMap<>(); bizContent.put("merch_order_id", merchOrderId); @@ -203,7 +233,7 @@ public class KPayViewModel extends ViewModel { bizContent.put("total_amount", amount); bizContent.put("title", "testing"); // bizContent.put("operator_id", serialNum); - bizContent.put("timeout_express", "100m"); + bizContent.put("timeout_express", timeoutExpress); bizContent.put("trans_currency", "MMK"); bizContent.put("callback_info", "callback"); @@ -226,7 +256,7 @@ public class KPayViewModel extends ViewModel { "testing", amount, "MMK", - "100m", + timeoutExpress, "callback" ); diff --git a/app/src/main/java/com/utsmm/kbz/ui/qr_pay/QRPayFragment.java b/app/src/main/java/com/utsmm/kbz/ui/qr_pay/QRPayFragment.java index 846dde5..6deb64c 100644 --- a/app/src/main/java/com/utsmm/kbz/ui/qr_pay/QRPayFragment.java +++ b/app/src/main/java/com/utsmm/kbz/ui/qr_pay/QRPayFragment.java @@ -58,7 +58,7 @@ public class QRPayFragment extends DataBindingFragment { List features = new ArrayList<>(); features.add(new QRPayItem("Sale", R.drawable.ic_qr_pay, true)); if(isQrRefundEnable){ - features.add(new QRPayItem("Refund", R.drawable.ic_refund, false)); + features.add(new QRPayItem("Refund", R.drawable.ic_refund, true)); } features.add(new QRPayItem("History", R.drawable.ic_history, true)); diff --git a/app/src/main/java/com/utsmm/kbz/util/DownloadUtil.java b/app/src/main/java/com/utsmm/kbz/util/DownloadUtil.java index afbe81d..299b33a 100644 --- a/app/src/main/java/com/utsmm/kbz/util/DownloadUtil.java +++ b/app/src/main/java/com/utsmm/kbz/util/DownloadUtil.java @@ -6,34 +6,89 @@ import com.utsmyanmar.paylibs.utils.LogUtil; import java.io.File; import java.io.FileOutputStream; -import okhttp3.OkHttp; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.schedulers.Schedulers; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class DownloadUtil { - final static String TAG = DownloadUtil.class.getSimpleName(); - private void downloadFile(String url){ - try{ - OkHttpClient okHttpClient = new OkHttpClient(); + private static final String TAG = DownloadUtil.class.getSimpleName(); + + // ============================== + // CALLBACK INTERFACE + // ============================== + public interface DownloadCallback { + void onDownloadSuccess(String path); + } + + // ============================== + // RX ASYNC DOWNLOAD METHOD + // ============================== + public static void downloadCertificateRx(String url, + String dynamicFilename, + DownloadCallback callback) { + + Observable.fromCallable(() -> downloadCert(url, dynamicFilename)) + .subscribeOn(Schedulers.io()) // download on background thread + .observeOn(AndroidSchedulers.mainThread()) // callback on main thread + .subscribe(path -> { + if (path != null) { + LogUtil.d(TAG, "Certificate saved at: " + path); + if (callback != null) callback.onDownloadSuccess(path); + } else { + LogUtil.e(TAG, "Certificate download failed."); + if (callback != null) callback.onDownloadSuccess(null); + } + }, error -> { + error.printStackTrace(); + if (callback != null) callback.onDownloadSuccess(null); + }); + } + + // ============================== + // ACTUAL DOWNLOAD LOGIC + // ============================== + public static String downloadCert(String url, String dynamicFilename) { + try { + OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); - Response response = okHttpClient.newCall(request).execute(); - if(!response.isSuccessful()){ - LogUtil.e(TAG, "file download failed"); - return; + Response response = client.newCall(request).execute(); + + if (!response.isSuccessful()) { + LogUtil.e(TAG, "Download failed: " + response.code()); + return null; } - byte[] bytes = response.body().bytes(); - saveFile("cert_file.ctr", bytes); - LogUtil.d(TAG, "file saved successfully."); - }catch (Exception e){ + // Detect extension (MIME or URL fallback) + String contentType = response.header("Content-Type", ""); + String ext = getExtensionFromContentType(contentType); + + if (ext.isEmpty()) ext = getExtensionFromUrl(url); + if (ext.isEmpty()) ext = ".bin"; // final fallback + + // build dynamic filename + String filename = dynamicFilename + ext; + + byte[] data = response.body().bytes(); + String savedPath = saveFile(filename, data); + + return savedPath; + + } catch (Exception e) { + e.printStackTrace(); + return null; } } - private void saveFile(String filename, byte[] data){ - try{ + // ============================== + // SAVE FILE TO INTERNAL STORAGE + // ============================== + private static String saveFile(String filename, byte[] data) { + try { File dir = MyApplication.getInstance().getFilesDir(); File file = new File(dir, filename); @@ -42,9 +97,42 @@ public class DownloadUtil { fos.flush(); fos.close(); - LogUtil.d(TAG, "Saved file at => " + file.getAbsolutePath()); + return file.getAbsolutePath(); + } catch (Exception e) { e.printStackTrace(); + return null; } } + + // ============================== + // MIME TYPE → EXTENSION + // ============================== + private static String getExtensionFromContentType(String contentType) { + if (contentType == null) return ""; + + switch (contentType) { + case "application/x-x509-ca-cert": + return ".crt"; + case "application/pkix-cert": + return ".cer"; + case "application/x-pem-file": + case "application/octet-stream": + return ".pem"; + case "application/x-pkcs12": + return ".p12"; + default: + return ""; + } + } + + // ============================== + // URL EXTENSION PARSER + // ============================== + private static String getExtensionFromUrl(String url) { + if (url == null) return ""; + int lastDot = url.lastIndexOf('.'); + if (lastDot == -1) return ""; + return url.substring(lastDot); + } } diff --git a/app/src/main/java/com/utsmm/kbz/util/tms/TMSSetupsImpl.java b/app/src/main/java/com/utsmm/kbz/util/tms/TMSSetupsImpl.java index aa03cc6..2b854bd 100644 --- a/app/src/main/java/com/utsmm/kbz/util/tms/TMSSetupsImpl.java +++ b/app/src/main/java/com/utsmm/kbz/util/tms/TMSSetupsImpl.java @@ -4,6 +4,7 @@ import android.content.pm.PackageInfo; import android.text.TextUtils; import com.google.gson.Gson; +import com.utsmm.kbz.util.DownloadUtil; import com.utsmyanmar.baselib.emv.EmvParamOperation; import com.utsmyanmar.baselib.network.model.sirius.SiriusHost; import com.utsmyanmar.baselib.network.model.sirius.SiriusMerchant; @@ -608,6 +609,17 @@ public class TMSSetupsImpl implements TMSSetups{ SystemParamsOperation.getInstance().setQrRefundEnable(parseBoolean(data)); } else if (TextUtils.equals(name, "tpdu_value")){ SystemParamsOperation.getInstance().setTpduValue(data); + } else if (TextUtils.equals(name, "certificate_file")){ + String tmsAddress = SystemParamsOperation.getInstance().getTmsAddress(); + String url = tmsAddress+"/file/download?filePath="+data; + DownloadUtil.downloadCertificateRx(url, "certificate_file", path -> { + if(path != null){ + SystemParamsOperation.getInstance().setCertFilePath(path); + LogUtil.d(TAG, "Cert file path saved in SystemParams => " + path); + }else{ + LogUtil.e(TAG, "Failed to download certificate file"); + } + }); } } diff --git a/baselib/src/main/java/com/utsmyanmar/baselib/di/NetworkModule.java b/baselib/src/main/java/com/utsmyanmar/baselib/di/NetworkModule.java index 28cfc86..756b454 100644 --- a/baselib/src/main/java/com/utsmyanmar/baselib/di/NetworkModule.java +++ b/baselib/src/main/java/com/utsmyanmar/baselib/di/NetworkModule.java @@ -285,8 +285,8 @@ public class NetworkModule { tmsAddress = getTMSUrlFromNative(); } - String baseUrl = tmsAddress.trim() + "/api/v1/"; -// String baseUrl = tmsAddress.trim() + "/"; +// String baseUrl = tmsAddress.trim() + "/api/v1/"; + String baseUrl = tmsAddress.trim() + "/"; final Gson gson = new GsonBuilder().create(); diff --git a/nexdlkey-lib/build/.transforms/0d4af20c009dcf5ca53c6eb0386ef6ba/results.bin b/nexdlkey-lib/build/.transforms/0d4af20c009dcf5ca53c6eb0386ef6ba/results.bin new file mode 100644 index 0000000..ceff831 --- /dev/null +++ b/nexdlkey-lib/build/.transforms/0d4af20c009dcf5ca53c6eb0386ef6ba/results.bin @@ -0,0 +1 @@ +i/nexgo-sdk-dlkey-1.0.3-runtime_dex diff --git a/nexdlkey-lib/build/.transforms/42372291e4065a0833ff472f7deb47e9/results.bin b/nexdlkey-lib/build/.transforms/42372291e4065a0833ff472f7deb47e9/results.bin new file mode 100644 index 0000000..c626d1b --- /dev/null +++ b/nexdlkey-lib/build/.transforms/42372291e4065a0833ff472f7deb47e9/results.bin @@ -0,0 +1 @@ +i/jars/classes.jar diff --git a/nexdlkey-lib/build/.transforms/64ffb5e990abcb564d467d9643c2d9b3/results.bin b/nexdlkey-lib/build/.transforms/64ffb5e990abcb564d467d9643c2d9b3/results.bin new file mode 100644 index 0000000..951bd91 --- /dev/null +++ b/nexdlkey-lib/build/.transforms/64ffb5e990abcb564d467d9643c2d9b3/results.bin @@ -0,0 +1 @@ +o/nexgo-sdk-dlkey-1.0.3-runtime diff --git a/nexdlkey-lib/build/.transforms/64ffb5e990abcb564d467d9643c2d9b3/transformed/nexgo-sdk-dlkey-1.0.3-runtime/nexgo-sdk-dlkey-1.0.3-runtime_dex/classes.dex b/nexdlkey-lib/build/.transforms/64ffb5e990abcb564d467d9643c2d9b3/transformed/nexgo-sdk-dlkey-1.0.3-runtime/nexgo-sdk-dlkey-1.0.3-runtime_dex/classes.dex new file mode 100644 index 0000000..70ca023 Binary files /dev/null and b/nexdlkey-lib/build/.transforms/64ffb5e990abcb564d467d9643c2d9b3/transformed/nexgo-sdk-dlkey-1.0.3-runtime/nexgo-sdk-dlkey-1.0.3-runtime_dex/classes.dex differ diff --git a/nexdlkey-lib/build/.transforms/8f2121b1ef7bc86489ca1abe0863f708/results.bin b/nexdlkey-lib/build/.transforms/8f2121b1ef7bc86489ca1abe0863f708/results.bin new file mode 100644 index 0000000..dc89b75 --- /dev/null +++ b/nexdlkey-lib/build/.transforms/8f2121b1ef7bc86489ca1abe0863f708/results.bin @@ -0,0 +1 @@ +i/nexgo-sdk-dlkey-1.0.3-runtime_global-synthetics diff --git a/nexdlkey-lib/build/.transforms/b885e125be3d23af311c7255a7c89a96/results.bin b/nexdlkey-lib/build/.transforms/b885e125be3d23af311c7255a7c89a96/results.bin new file mode 100644 index 0000000..e69de29 diff --git a/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsOperation.java b/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsOperation.java index 3cebc1e..95e239d 100644 --- a/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsOperation.java +++ b/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsOperation.java @@ -1605,4 +1605,15 @@ public class SystemParamsOperation { SystemParamsSettings params = getSystemParamsSettings(); return params.getQrRefundEnable(); } + + public void setCertFilePath(String path) { + SystemParamsSettings params = getSystemParamsSettings(); + params.setCertFilePath(path); + saveSystemParamsSettings(params); + } + + public String getCertFilePath(){ + SystemParamsSettings params = getSystemParamsSettings(); + return params.getCertFilePath(); + } } \ No newline at end of file diff --git a/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsSettings.java b/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsSettings.java index 2ad6ba7..79fd71d 100644 --- a/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsSettings.java +++ b/paylibs/src/main/java/com/utsmyanmar/paylibs/utils/core_utils/SystemParamsSettings.java @@ -42,8 +42,8 @@ public class SystemParamsSettings implements Serializable { // private String tmsAddress = "https://tms.smile-mm.com"; // private String tmsAddress = "http://128.199.170.203"; - private String tmsAddress = "http://sirius-nest.utsmyanmar.com"; -// private String tmsAddress = "https://api-tms-uat.kbzbank.com:8443/sirius"; +// private String tmsAddress = "http://sirius-nest.utsmyanmar.com"; + private String tmsAddress = "https://api-tms-uat.kbzbank.com:8443/sirius"; private String ereceiptAddress = "http://receipt-nest.utsmyanmar.com"; private String terminalCapability = "E0E8C8"; @@ -242,6 +242,7 @@ public class SystemParamsSettings implements Serializable { private boolean qrDecimalEnable = false; private boolean qrRefundEnable = false; + private String certFilePath = ""; public boolean isQrPartialRefundEnable(){ return qrPartialRefundEnable; @@ -961,6 +962,14 @@ public class SystemParamsSettings implements Serializable { return qrRefundEnable; } + public void setCertFilePath(String path) { + this.certFilePath = path; + } + + public String getCertFilePath() { + return certFilePath; + } + /* // 流水号起始 private String serialNum = Configs.getInstance().SERIAL_NUM(); // 批次号起始