refund cached certificate fixed

This commit is contained in:
MooN 2025-12-29 01:03:47 +06:30
parent 062533662f
commit cd7cfa2a7d
13 changed files with 136 additions and 92 deletions

View File

@ -72,7 +72,7 @@ public class InputAmountFragment extends DataBindingFragment implements DataBind
super.onViewCreated(view, savedInstanceState);
setUpTimer();
// setUpTimer();
setupBackButtonPressDetection(this);
}

View File

@ -132,7 +132,13 @@ public class QRRefundFragment extends DataBindingFragment {
List<PayDetail> filteredList = new ArrayList<>();
for(PayDetail item: originalList){
if(item.getReferNo() != null && item.getReferNo().contains(keyword)){
boolean match =
(item.getReferNo() != null && item.getReferNo().contains(keyword)) ||
(item.getInvoiceNo() != null && item.getInvoiceNo().contains(keyword)) ||
(item.getVoucherNo() != null && item.getVoucherNo().contains(keyword));
if(match){
filteredList.add(item);
}
}
@ -140,4 +146,5 @@ public class QRRefundFragment extends DataBindingFragment {
updateList(filteredList);
}
}

View File

@ -1,13 +1,12 @@
package com.utsmm.kbz.ui.qr_pay;
import android.util.Log;
import com.utsmm.kbz.util.DownloadUtil;
import com.utsmyanmar.baselib.util.EReceiptHelper;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.inject.Inject;
import javax.inject.Singleton;
@ -15,55 +14,94 @@ import javax.inject.Singleton;
public class RefundCertificateManager {
private static final String TAG = RefundCertificateManager.class.getSimpleName();
private static final String CERTIFICATE_FILENAME = "refund_client_certificate";
private static final String CERTIFICATE_CLIENT = "refund_client_certificate";
// String tmsAddress = SystemParamsOperation.getInstance().getTmsAddress(); //for uat
String tmsAddress = SystemParamsOperation.getInstance().getTmsAddress() + "/api/v1";
String downloadAddress = tmsAddress + "/file/download?filePath=";
String certificate_url = SystemParamsOperation.getInstance().getCertificateUrl();
private static final String E_RECEIPT_SECRET =
com.utsmyanmar.baselib.BuildConfig.ERECEIPT_SECRET;
String certificate_client = SystemParamsOperation.getInstance().getCertificateClientUrl();
private static final String E_RECEIPT_SECRET = com.utsmyanmar.baselib.BuildConfig.ERECEIPT_SECRET;
String timestamp = String.valueOf(System.currentTimeMillis());
// Prevent duplicate parallel downloads
private final AtomicBoolean isUpdating = new AtomicBoolean(false);
@Inject
public RefundCertificateManager(){}
public void updateCertificateFiles(){
public RefundCertificateManager() {
}
String signature = generateSignature(timestamp);
/**
* Download & refresh refund certificates.
* Safe to call multiple times.
*/
public void updateCertificateFiles() {
DownloadUtil.downloadCertificateRx(
downloadAddress + certificate_url,
CERTIFICATE_FILENAME,
timestamp,
signature,
path -> {
if(path != null){
SystemParamsOperation.getInstance().setCertClientFilePath(path);
LogUtil.d(TAG, "Cert file path saved in SystemParams => " + path);
}else{
LogUtil.e(TAG, "Failed to download certificate file");
if (!isUpdating.compareAndSet(false, true)) {
LogUtil.d(TAG, "Certificate update already in progress");
return;
}
try {
String tmsAddress = SystemParamsOperation.getInstance().getTmsAddress();
if (tmsAddress == null || tmsAddress.trim().isEmpty()) {
LogUtil.e(TAG, "TMS address is empty");
return;
}
String downloadBase =
tmsAddress.trim() + "/api/v1/file/download?filePath=";
String certUrl =
SystemParamsOperation.getInstance().getCertificateUrl();
String clientCertUrl =
SystemParamsOperation.getInstance().getCertificateClientUrl();
if (certUrl == null || clientCertUrl == null) {
LogUtil.e(TAG, "Certificate URLs are missing");
return;
}
String timestamp = String.valueOf(System.currentTimeMillis());
String signature = generateSignature(timestamp);
LogUtil.d(TAG, "Download base => " + downloadBase);
LogUtil.d(TAG, "Cert URL => " + certUrl);
LogUtil.d(TAG, "Client Cert URL => " + clientCertUrl);
// ---------- CA CERT ----------
DownloadUtil.downloadCertificateRx(
downloadBase + certUrl,
"refund_ca_" + timestamp, // IMPORTANT: unique filename
timestamp,
signature,
path -> {
if (path != null) {
SystemParamsOperation.getInstance()
.setCertFilePath(path);
LogUtil.d(TAG, "CA cert saved => " + path);
} else {
LogUtil.e(TAG, "Failed to download CA cert");
}
}
}
);
DownloadUtil.downloadCertificateRx(
downloadAddress + certificate_client,
CERTIFICATE_CLIENT,
timestamp,
signature,
path -> {
if(path != null){
SystemParamsOperation.getInstance().setCertClientFilePath(path);
LogUtil.d(TAG, "Cert client file path saved in SystemParams => " + path);
}else{
LogUtil.e(TAG, "Failed to download certificate client file");
);
// ---------- CLIENT CERT ----------
DownloadUtil.downloadCertificateRx(
downloadBase + clientCertUrl,
"refund_client_" + timestamp, // IMPORTANT: unique filename
timestamp,
signature,
path -> {
if (path != null) {
SystemParamsOperation.getInstance()
.setCertClientFilePath(path);
LogUtil.d(TAG, "Client cert saved => " + path);
} else {
LogUtil.e(TAG, "Failed to download client cert");
}
}
}
);
);
} finally {
isUpdating.set(false);
}
}
private static String generateSignature(String timestamp) {
LogUtil.d(TAG, "timestamp " + timestamp);
String bodyString = "{}";
String dataToHash = bodyString + E_RECEIPT_SECRET + timestamp;
return EReceiptHelper.sha256(dataToHash);

View File

@ -1,34 +1,12 @@
package com.utsmm.kbz.ui.settings;
import static androidx.databinding.DataBindingUtil.getBinding;
import static com.utsmyanmar.baselib.BaseApplication.sunmiPrinterService;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.res.ResourcesCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.nexgo.oaf.apiv3.APIProxy;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.DeviceInfo;
import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.printer.AlignEnum;
import com.nexgo.oaf.apiv3.device.printer.GrayLevelEnum;
import com.nexgo.oaf.apiv3.device.printer.Printer;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
@ -37,15 +15,7 @@ import com.utsmm.kbz.databinding.FragmentHostConfigBinding;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.DialogCallback;
import com.utsmyanmar.paylibs.PayLibsUtils;
import com.utsmyanmar.paylibs.print.PaperRollStatusCallback;
import com.utsmyanmar.paylibs.print.PrintHelper;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.print_utils.BitmapUtils;
import java.util.Objects;
public class HostConfigFragment extends DataBindingFragment {

View File

@ -2,9 +2,12 @@ package com.utsmm.kbz.ui.settings;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.utsmm.kbz.BuildConfig;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
public class HostConfigViewModel extends ViewModel {
public String appVersion = BuildConfig.VERSION_NAME;
public MutableLiveData<String> merchantName = new MutableLiveData<>();
public MutableLiveData<String> merchantPhone = new MutableLiveData<>();

View File

@ -52,8 +52,8 @@ public class DownloadUtil {
String dynamicFilename,
String timestamp,
String signature) {
LogUtil.d(TAG, "cert timestamp => " + timestamp);
LogUtil.d(TAG, "cert signature => " + signature);
// LogUtil.d(TAG, "cert timestamp => " + timestamp);
// LogUtil.d(TAG, "cert signature => " + signature);
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
@ -126,7 +126,7 @@ public class DownloadUtil {
case "application/octet-stream":
return ".pem";
case "application/x-pkcs12":
return ".p12";
return ".p12";
default:
return "";
}

View File

@ -639,6 +639,7 @@ public class TMSSetupsImpl implements TMSSetups{
continue;
}
SystemParamsOperation.getInstance().setCertificateClientUrl(data);
}
else if (TextUtils.equals(name, "certificate_password")) {
SystemParamsOperation.getInstance().setCertificatePassword(data);
@ -649,7 +650,7 @@ public class TMSSetupsImpl implements TMSSetups{
}
private static String generateSignature(String apiSecret, String timestamp) {
String bodyString = "";
String bodyString = "{}";
String dataToHash = bodyString + apiSecret + timestamp;
return EReceiptHelper.sha256(dataToHash);
}

View File

@ -81,6 +81,7 @@
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bannerCard"
android:paddingBottom="32dp"
app:layout_constraintVertical_bias="1.0">
<FrameLayout

View File

@ -310,12 +310,26 @@
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="--------------------------------------------------------------------------"
android:text="*************************************************"
android:paddingTop="8dp"
android:paddingBottom="8dp"
tools:text="--------------------------------------------------------------------------"/>
tools:text="********************************************************"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
tools:visibility="visible">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text='@{"App Version : " + viewModel.appVersion}'
android:fontFamily="@font/rubik_regular"
android:textSize="14sp"
android:textStyle="bold"
tools:text="App Version : 1.0"/>
</LinearLayout>
</LinearLayout>
</ScrollView>
</FrameLayout>

View File

@ -449,11 +449,14 @@ public class DatabaseModule {
DemoQRApiService demoQRApiService,
KPayApiService kPayApiService,
KPayRefundApiService kPayRefundApiService,
EReceiptApiService eReceiptApiService
EReceiptApiService eReceiptApiService,
Provider<KPayRefundApiService> kPayRefundApiProvider
) {
return new com.utsmyanmar.baselib.repo.Repository(payDetailDao,
siriusApiService, emvDetailDao,
payWaveRepository, demoQRApiService, kPayApiService, kPayRefundApiService, eReceiptApiService);
payWaveRepository, demoQRApiService, kPayApiService, kPayRefundApiService, eReceiptApiService,
kPayRefundApiProvider
);
}
@Provides

View File

@ -54,6 +54,7 @@ import dagger.hilt.InstallIn;
import dagger.hilt.android.qualifiers.ApplicationContext;
import dagger.hilt.components.SingletonComponent;
import hu.akarnokd.rxjava3.retrofit.RxJava3CallAdapterFactory;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
@ -389,7 +390,6 @@ public class NetworkModule {
//@Reusable
//@KPayRefundRetrofit
@Provides
@Singleton
@KPayRefundRetrofit
public Retrofit provideKPayRefundRetrofit(@ApplicationContext Context context) {
@ -408,7 +408,9 @@ public Retrofit provideKPayRefundRetrofit(@ApplicationContext Context context) {
Security.addProvider(new BouncyCastleProvider());
String clientPath = SystemParamsOperation.getInstance().getCertClientFilePath();
LogUtil.d("cert", "ca client path => " + clientPath);
String caPath = SystemParamsOperation.getInstance().getCertFilePath();
LogUtil.d("cert", "capath => " + caPath);
if (TextUtils.isEmpty(clientPath) || TextUtils.isEmpty(caPath)) {
LogUtil.e("RefundCert", "Certificate files missing.");
@ -450,6 +452,7 @@ public Retrofit provideKPayRefundRetrofit(@ApplicationContext Context context) {
.sslSocketFactory(sslContext.getSocketFactory(), x509TrustManager)
.addInterceptor(interceptor)
.hostnameVerifier((h, s) -> true)
.connectionPool(new ConnectionPool(0, 1, TimeUnit.SECONDS))
.build();
return new Retrofit.Builder()
@ -475,7 +478,6 @@ public Retrofit provideKPayRefundRetrofit(@ApplicationContext Context context) {
@Provides
@Singleton
public KPayRefundApiService provideKPayRefundApiService(@KPayRefundRetrofit Retrofit retrofit) {
return retrofit.create(KPayRefundApiService.class);
}

View File

@ -46,6 +46,7 @@ import java.util.List;
import javax.inject.Inject;
import javax.inject.Provider;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
@ -64,9 +65,11 @@ public class Repository {
private DemoQRApiService demoQRApiService;
private KPayRefundApiService kPayRefundApiService;
private Provider<KPayRefundApiService> kPayRefundApiProvider;
private EReceiptApiService eReceiptApiService;
@Inject
public Repository(PayDetailDao payDetailDao, SiriusApiService siriusApiService, EmvDetailDao emvDetailDao, PayWaveRepository payWaveRepository, DemoQRApiService demoQRApiService, KPayApiService kPayApiService, KPayRefundApiService kPayRefundApiService, EReceiptApiService eReceiptApiService) {
public Repository(PayDetailDao payDetailDao, SiriusApiService siriusApiService, EmvDetailDao emvDetailDao, PayWaveRepository payWaveRepository, DemoQRApiService demoQRApiService, KPayApiService kPayApiService, KPayRefundApiService kPayRefundApiService, EReceiptApiService eReceiptApiService, Provider<KPayRefundApiService> kPayRefundApiProvider) {
this.payDetailDao = payDetailDao;
this.siriusApiService = siriusApiService;
this.emvDetailDao = emvDetailDao;
@ -74,6 +77,7 @@ public class Repository {
this.demoQRApiService = demoQRApiService;
this.kPayApiService = kPayApiService;
this.kPayRefundApiService = kPayRefundApiService;
this.kPayRefundApiProvider = kPayRefundApiProvider;
this.eReceiptApiService = eReceiptApiService;
}
@ -92,7 +96,8 @@ public class Repository {
}
public Observable<KPayRefund.RefundResponse> kPayRefund(KPayRefund.RefundRequest request) {
return kPayRefundApiService.refundRequest(request);
// return kPayRefundApiService.refundRequest(request);
return kPayRefundApiProvider.get().refundRequest(request);
}
public Observable<DemoQRResponse> demoQRGenerate(DemoQRRequest demoQRRequest) {
return demoQRApiService.qrRequest(demoQRRequest);

View File

@ -740,13 +740,13 @@ public abstract class BaseXPrint {
for (PayDetail pay : lists) {
printString("Trans Id: " + pay.getQrTransId());
printString("Status : " + pay.getTC());
printString("Trans Id:" + pay.getQrTransId());
printString("Status :" + pay.getTC());
// printString("Date : " + PrintUtils.getInstance().formatTimestamp(Long.parseLong(pay.getTradeDateAndTime())));
printString("Date : " + pay.getTransDate());
printString("Time : " + pay.getTransTime());
printString("Date :" + pay.getTransDate());
printString("Time :" + pay.getTransTime());
printString("Amount : " + "MMK " + PrintUtils.getInstance().getSeparatorOnlyNumberFormat(pay.getAmount()));
printString("Amount :" + "MMK " + PrintUtils.getInstance().getSeparatorOnlyNumberFormat(pay.getAmount()));
if (pay.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
refundTotal += pay.getAmount();
@ -1109,7 +1109,7 @@ public abstract class BaseXPrint {
printer.appendPrnStr("Secondary Port: " + secHostSecIp[1], fontNormal, AlignEnum.LEFT, false);
printer.appendPrnStr("Currency Code : " + sp.getCurrencyType(), fontNormal, AlignEnum.LEFT, false);
lineBreak();
printer.appendPrnStr("App Version : " + getAppVersion(), fontNormal, AlignEnum.LEFT, false);
printer.appendPrnStr("App Version : " + getAppVersion(), fontNormal, AlignEnum.LEFT, false);
}
}