qr-e-receipt

This commit is contained in:
MooN 2025-11-23 01:35:04 +06:30
parent 1811fefec1
commit 5d450e0a48
12 changed files with 320 additions and 8 deletions

View File

@ -14,8 +14,8 @@ android {
applicationId "com.utsmm.kbz" applicationId "com.utsmm.kbz"
minSdk 24 minSdk 24
targetSdk 33 targetSdk 33
versionCode 7 versionCode 8
versionName "1.07" versionName "1.08"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@ -1,12 +1,15 @@
package com.utsmm.kbz.ui.core_viewmodel; package com.utsmm.kbz.ui.core_viewmodel;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.util.Log;
import dagger.hilt.android.lifecycle.HiltViewModel; import dagger.hilt.android.lifecycle.HiltViewModel;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModel;
import com.google.gson.Gson;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest; import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse; import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.baselib.repo.Repository; import com.utsmyanmar.baselib.repo.Repository;
@ -26,9 +29,15 @@ import com.utsmyanmar.paylibs.utils.enums.HostType;
import java.util.List; import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import retrofit2.HttpException;
import com.utsmyanmar.paylibs.utils.LogUtil; import com.utsmyanmar.paylibs.utils.LogUtil;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject; import javax.inject.Inject;
@HiltViewModel @HiltViewModel
@ -415,4 +424,33 @@ public class SharedViewModel extends ViewModel {
} }
public void pushReceipt(Object body){
repository.sendReceipt(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> Log.d("E-Receipt", "Success =>"+ response.getMessage()),
error -> {
if (error instanceof HttpException) {
HttpException httpEx = (HttpException) error;
try {
String errorJson = httpEx.response().errorBody().string();
Log.e("E-Receipt", "Raw error JSON => " + errorJson);
// Parse JSON to model
EReceiptResponse res = new Gson().fromJson(errorJson, EReceiptResponse.class);
Log.e("E-Receipt", "Parsed Error => " + res.getMessage());
} catch (Exception e) {
e.printStackTrace();
}
} else {
Log.e("E-Receipt", "Unexpected error => " + error.getMessage());
}
}
);
}
} }

View File

@ -1,6 +1,7 @@
package com.utsmm.kbz.ui.kpay; package com.utsmm.kbz.ui.kpay;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -13,6 +14,7 @@ import androidx.annotation.Nullable;
import com.utsmyanmar.baselib.fragment.DataBindingFragment; import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.DemoQRStatusRequest; import com.utsmyanmar.baselib.network.model.DemoQRStatusRequest;
import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest; import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptQRRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig; import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TimeoutCallback; import com.utsmyanmar.baselib.util.TimeoutCallback;
import com.utsmyanmar.ecr.ECRHelper; import com.utsmyanmar.ecr.ECRHelper;
@ -27,6 +29,10 @@ import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.TransactionUtil; import com.utsmm.kbz.util.TransactionUtil;
import com.utsmm.kbz.util.ecr.CoreUtils; import com.utsmm.kbz.util.ecr.CoreUtils;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -308,6 +314,29 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
retrievedUpdatePayDetail(refLabel, payDetail,false); retrievedUpdatePayDetail(refLabel, payDetail,false);
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(payDetail.getAmount());
String currentTimeStamp = new java.text.SimpleDateFormat("MMddHHmmss", java.util.Locale.getDefault())
.format(new java.util.Date());
String serial = SystemParamsOperation.getInstance().getCurrentSerialNum();
String packageName = getActivity().getPackageName();
EReceiptQRRequest request = new EReceiptQRRequest();
request.setDE3("QR");
request.setDE4(amount);
request.setDE7(currentTimeStamp);
request.setDE37(payDetail.getReferNo());
request.setDE39("A");
request.setDE49("MMK");
request.setSerial(serial);
request.setAppId(packageName);
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setDE11(payDetail.getVoucherNo());
request.setDescription("qr pay success");
sharedViewModel.pushReceipt(request);
return; return;
} }

View File

@ -6,6 +6,9 @@
<domain includeSubdomains="true">128.199.170.203</domain> <domain includeSubdomains="true">128.199.170.203</domain>
<domain includeSubdomains="true">152.42.199.193</domain> <domain includeSubdomains="true">152.42.199.193</domain>
<domain includeSubdomains="true">api.kbzpay.com</domain> <domain includeSubdomains="true">api.kbzpay.com</domain>
<domain-config>
<domain includeSubdomains="true">receipt-nest.utsmyanmar.com</domain>
</domain-config>
<domain includeSubdomains="true">sirius-nest.utsmyanmar.com</domain> <domain includeSubdomains="true">sirius-nest.utsmyanmar.com</domain>
</domain-config> </domain-config>
</network-security-config> </network-security-config>

View File

@ -23,6 +23,7 @@ import com.utsmyanmar.baselib.db.dao.QuickPassDao;
import com.utsmyanmar.baselib.db.dao.RidDao; import com.utsmyanmar.baselib.db.dao.RidDao;
import com.utsmyanmar.baselib.db.model.EmvDetail; import com.utsmyanmar.baselib.db.model.EmvDetail;
import com.utsmyanmar.baselib.network.DemoQRApiService; import com.utsmyanmar.baselib.network.DemoQRApiService;
import com.utsmyanmar.baselib.network.EReceiptApiService;
import com.utsmyanmar.baselib.network.KPayApiService; import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService; import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService; import com.utsmyanmar.baselib.network.SiriusApiService;
@ -447,10 +448,12 @@ public class DatabaseModule {
com.utsmyanmar.baselib.repo.local.PayWaveRepository payWaveRepository, com.utsmyanmar.baselib.repo.local.PayWaveRepository payWaveRepository,
DemoQRApiService demoQRApiService, DemoQRApiService demoQRApiService,
KPayApiService kPayApiService, KPayApiService kPayApiService,
KPayRefundApiService kPayRefundApiService) { KPayRefundApiService kPayRefundApiService,
EReceiptApiService eReceiptApiService
) {
return new com.utsmyanmar.baselib.repo.Repository(payDetailDao, return new com.utsmyanmar.baselib.repo.Repository(payDetailDao,
siriusApiService, emvDetailDao, siriusApiService, emvDetailDao,
payWaveRepository, demoQRApiService, kPayApiService, kPayRefundApiService); payWaveRepository, demoQRApiService, kPayApiService, kPayRefundApiService, eReceiptApiService);
} }
@Provides @Provides

View File

@ -9,6 +9,7 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
//import com.utsmyanmar.baselib.BuildConfig; //import com.utsmyanmar.baselib.BuildConfig;
import com.utsmyanmar.baselib.network.DemoQRApiService; import com.utsmyanmar.baselib.network.DemoQRApiService;
import com.utsmyanmar.baselib.network.EReceiptApiService;
import com.utsmyanmar.baselib.network.KPayApiService; import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService; import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService; import com.utsmyanmar.baselib.network.SiriusApiService;
@ -386,5 +387,25 @@ public class NetworkModule {
return retrofit.create(KPayRefundApiService.class); return retrofit.create(KPayRefundApiService.class);
} }
@Provides
@Singleton
public EReceiptApiService provideEReceiptApiService(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.build();
return new Retrofit.Builder()
.baseUrl("http://receipt-nest.utsmyanmar.com/") // base URL
.addCallAdapterFactory(RxJava3CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
.create(EReceiptApiService.class);
}
} }

View File

@ -0,0 +1,20 @@
package com.utsmyanmar.baselib.network;
import com.google.gson.JsonElement;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import io.reactivex.rxjava3.core.Observable;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.POST;
public interface EReceiptApiService {
@POST("transaction")
Observable<EReceiptResponse> sendReceipt(
@Header("x-timestamp") String timestamp,
@Header("x-api-key") String apiKey,
@Body Object body
);
}

View File

@ -29,6 +29,7 @@ public class SiriusInterceptor implements Interceptor {
@Override @Override
public Response intercept(@NonNull Chain chain) throws IOException { public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request(); Request request = chain.request();
Request newRequest; Request newRequest;

View File

@ -0,0 +1,123 @@
package com.utsmyanmar.baselib.network.model.e_receipt;
public class EReceiptQRRequest {
private String DE3;
private String DE39;
private String DE41;
private String DE42;
private String serial;
private String DE49;
private String DE37;
private String DE7;
private String DE4;
private String appId;
private String invoiceNumber;
private String DE11;
private String description;
public EReceiptQRRequest() {}
// GETTERS
public String getDE3() {
return DE3;
}
public String getDE39() {
return DE39;
}
public String getDE41() {
return DE41;
}
public String getDE42() {
return DE42;
}
public String getSerial() {
return serial;
}
public String getDE49() {
return DE49;
}
public String getDE37() {
return DE37;
}
public String getDE7() {
return DE7;
}
public String getDE4() {
return DE4;
}
public String getAppId(){
return appId;
}
public String getInvoiceNumber(){
return invoiceNumber;
}
public String getDE11(){
return DE11;
}
public String getDescription(){
return description;
}
// SETTERS
public void setDE3(String DE3) {
this.DE3 = DE3;
}
public void setDE39(String DE39) {
this.DE39 = DE39;
}
public void setDE41(String DE41) {
this.DE41 = DE41;
}
public void setDE42(String DE42) {
this.DE42 = DE42;
}
public void setSerial(String serial) {
this.serial = serial;
}
public void setDE49(String DE49) {
this.DE49 = DE49;
}
public void setDE37(String DE37) {
this.DE37 = DE37;
}
public void setDE7(String DE7) {
this.DE7 = DE7;
}
public void setDE4(String DE4) {
this.DE4 = DE4;
}
public void setAppId(String appId){
this.appId = appId;
}
public void setInvoiceNumber(String invoiceNumber){
this.invoiceNumber = invoiceNumber;
}
public void setDE11(String DE11){
this.DE11 = DE11;
}
public void setDescription(String description){
this.description = description;
}
}

View File

@ -0,0 +1,14 @@
package com.utsmyanmar.baselib.network.model.e_receipt;
public class EReceiptResponse {
private String message;
private String code;
public String getMessage(){
return message;
}
public String getCode(){
return code;
}
}

View File

@ -4,11 +4,14 @@ package com.utsmyanmar.baselib.repo;
import androidx.lifecycle.LiveData; import androidx.lifecycle.LiveData;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.utsmyanmar.baselib.db.dao.EmvDetailDao; import com.utsmyanmar.baselib.db.dao.EmvDetailDao;
import com.utsmyanmar.baselib.db.dao.PayDetailDao; import com.utsmyanmar.baselib.db.dao.PayDetailDao;
import com.utsmyanmar.baselib.db.model.EmvDetail; import com.utsmyanmar.baselib.db.model.EmvDetail;
import com.utsmyanmar.baselib.db.model.PayWaveEntity; import com.utsmyanmar.baselib.db.model.PayWaveEntity;
import com.utsmyanmar.baselib.network.DemoQRApiService; import com.utsmyanmar.baselib.network.DemoQRApiService;
import com.utsmyanmar.baselib.network.EReceiptApiService;
import com.utsmyanmar.baselib.network.KPayApiService; import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService; import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService; import com.utsmyanmar.baselib.network.SiriusApiService;
@ -29,10 +32,12 @@ import com.utsmyanmar.baselib.network.model.WaveQRRequest;
import com.utsmyanmar.baselib.network.model.WaveQRResponse; import com.utsmyanmar.baselib.network.model.WaveQRResponse;
import com.utsmyanmar.baselib.network.model.WaveStatusRequest; import com.utsmyanmar.baselib.network.model.WaveStatusRequest;
import com.utsmyanmar.baselib.network.model.WaveTokenResponse; import com.utsmyanmar.baselib.network.model.WaveTokenResponse;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest; import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse; import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.baselib.network.model.KPayRefund; 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.paylibs.model.PayDetail; import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.List; import java.util.List;
@ -57,8 +62,9 @@ public class Repository {
private DemoQRApiService demoQRApiService; private DemoQRApiService demoQRApiService;
private KPayRefundApiService kPayRefundApiService; private KPayRefundApiService kPayRefundApiService;
private EReceiptApiService eReceiptApiService;
@Inject @Inject
public Repository(PayDetailDao payDetailDao, SiriusApiService siriusApiService, EmvDetailDao emvDetailDao, PayWaveRepository payWaveRepository, DemoQRApiService demoQRApiService, KPayApiService kPayApiService, KPayRefundApiService kPayRefundApiService) { public Repository(PayDetailDao payDetailDao, SiriusApiService siriusApiService, EmvDetailDao emvDetailDao, PayWaveRepository payWaveRepository, DemoQRApiService demoQRApiService, KPayApiService kPayApiService, KPayRefundApiService kPayRefundApiService, EReceiptApiService eReceiptApiService) {
this.payDetailDao = payDetailDao; this.payDetailDao = payDetailDao;
this.siriusApiService = siriusApiService; this.siriusApiService = siriusApiService;
this.emvDetailDao = emvDetailDao; this.emvDetailDao = emvDetailDao;
@ -66,6 +72,7 @@ public class Repository {
this.demoQRApiService = demoQRApiService; this.demoQRApiService = demoQRApiService;
this.kPayApiService = kPayApiService; this.kPayApiService = kPayApiService;
this.kPayRefundApiService = kPayRefundApiService; this.kPayRefundApiService = kPayRefundApiService;
this.eReceiptApiService = eReceiptApiService;
} }
public Observable<KPayQRResponse.QrResponse> qrGenerate(KPayQRRequest.QrRequest request) { public Observable<KPayQRResponse.QrResponse> qrGenerate(KPayQRRequest.QrRequest request) {
@ -98,9 +105,14 @@ public class Repository {
public Observable<SiriusResponse> getParams(SiriusRequest siriusRequest) { public Observable<SiriusResponse> getParams(SiriusRequest siriusRequest) {
return siriusApiService.getParams(siriusRequest); return siriusApiService.getParams(siriusRequest);
} }
public Observable<EReceiptResponse> sendReceipt(Object body){
String apiSecret = "8f4df38d1001bcc4620b5c736c66a03eef4653eb3ba31105faa2f2ee294c4a46";
String timestamp = String.valueOf(System.currentTimeMillis());
String bodyString = new Gson().toJson(body);
String dataToHash = bodyString + apiSecret + timestamp;
String signature = EReceiptHelper.sha256(dataToHash);
return eReceiptApiService.sendReceipt(timestamp, signature, body);
}
public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);} public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);}

View File

@ -0,0 +1,48 @@
package com.utsmyanmar.baselib.util;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class EReceiptHelper {
public static String hmacSha256(String key, String data) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
mac.init(secretKey);
byte[] hash = mac.doFinal(data.getBytes());
StringBuilder hex = new StringBuilder();
for (byte b : hash) {
hex.append(String.format("%02x", b));
}
return hex.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String sha256(String input) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}