signature save

This commit is contained in:
moon 2026-06-25 12:46:16 +06:30
parent afd3d925e1
commit 716030128a
9 changed files with 121 additions and 12 deletions

View File

@ -11,6 +11,7 @@ import androidx.annotation.Nullable;
import com.github.gcacace.signaturepad.views.SignaturePad;
import com.utsmm.kbz.util.helper.EReceiptHelper;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptCardRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
@ -141,14 +142,13 @@ public class SignatureFragment extends DataBindingFragment {
payDetail.setESignBlob(blob);
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.updateESignBlob(payDetail.voucherNo, blob);
sharedViewModel.pushReceipt(EReceiptHelper.buildEReceiptCardReceipt(
EReceiptCardRequest request = EReceiptHelper.buildEReceiptCardReceipt(
requireContext(),
payDetail,
true,
BaseErrorCode.getCode(payDetail.getTradeAnswerCode()),
blob)
);
null);
sharedViewModel.pushReceiptWithImage(request, blob);
}
public class ClickEvent {

View File

@ -12,6 +12,7 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.google.gson.Gson;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptCardRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
@ -535,6 +536,38 @@ public class SharedViewModel extends ViewModel {
);
}
@SuppressLint("CheckResult")
public void pushReceiptWithImage(EReceiptCardRequest request, byte[] imageBytes) {
Log.d("push receipt", new Gson().toJson(request));
repository.sendReceiptWithImage(request, imageBytes)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
Log.d("E-Receipt", "Success =>" + response.getMessage());
postToastMsg("E-Receipt saved successfully");
},
error -> {
if (error instanceof HttpException) {
HttpException httpEx = (HttpException) error;
try {
String errorJson = httpEx.response().errorBody().string();
Log.e("E-Receipt", "Raw error JSON => " + errorJson);
EReceiptResponse res = new Gson().fromJson(errorJson, EReceiptResponse.class);
Log.e("E-Receipt", "Parsed Error => " + res.getMessage());
postToastMsg("E-Receipt could not save!");
} catch (Exception e) {
e.printStackTrace();
postToastMsg("E-Receipt could not save!");
}
} else {
Log.e("E-Receipt", "Unexpected error => " + error.getMessage());
postToastMsg("E-Receipt could not save!");
}
}
);
}
public void printTerminalHostConfigs(){
PrintXReceipt.getInstance().printTerminalHostConfig(new PrintXStatus() {

View File

@ -67,7 +67,9 @@ public class EReceiptHelper {
request.setDE14(PrintUtils.getInstance().getFormatExpDate(payDetail.getEXPDate()));
request.setDE43(formatDE43(cardHolderName));
request.setDE48(payDetail.getAID());
request.setDE115(signature != null ? Base64.encodeToString(signature, Base64.NO_WRAP) : "");
if (signature != null) {
request.setDE115(Base64.encodeToString(signature, Base64.NO_WRAP));
}
return request;
}
@ -132,6 +134,6 @@ public class EReceiptHelper {
return value.substring(0, 40);
}
return String.format("%-40s", value); // right pad with space
return String.format("%-40s", value).trim();
}
}

View File

@ -8,6 +8,7 @@
<domain includeSubdomains="true">api.kbzpay.com</domain>
<domain includeSubdomains="true">api-uat.kbzpay.com</domain>
<domain includeSubdomains="true">sirius-nest.utsmyanmar.com</domain>
<domain includeSubdomains="true">192.168.10.105</domain>
</domain-config>
<domain-config cleartextTrafficPermitted="true">

View File

@ -80,7 +80,7 @@ public interface PayDetailDao {
// 6 = PRE AUTH CANCEL, 7= PRE_AUTH_COMPLETE , 5 = PRE AUTH
// @Query(("SELECT * FROM paydetail WHERE transactionType = 1 AND isCanceled = 1 OR transactionType = 2 AND isCanceled = 0 OR transactionType = 18 AND isCanceled = 0 AND batchNo IN(:batchNo)"))
// @Query(("SELECT * FROM paydetail WHERE transactionType = 1 AND isCanceled = 1 OR transactionType = 5 AND isCanceled = 1 OR transactionType = 7 AND isCanceled = 1 OR transactionType = 8 OR transactionType = 6 OR transactionType = 10 OR transactionType = 2 AND isCanceled = 0 OR transactionType = 18 AND isCanceled = 0 OR transactionType = 20 AND isCanceled = 0 OR transactionType = 34 AND isCanceled = 1 AND batchNo IN(:batchNo)"))
@Query(("SELECT * FROM paydetail WHERE transactionType = 1 AND isCanceled = 1 OR transactionType = 5 AND isCanceled = 1 OR transactionType = 7 AND isCanceled = 1 OR transactionType = 8 OR transactionType = 6 OR transactionType = 10 OR transactionType = 2 AND isCanceled = 0 OR transactionType = 18 AND isCanceled = 0 OR transactionType = 20 OR transactionType = 34 AND isCanceled = 1 AND batchNo IN(:batchNo)"))
@Query(("SELECT * FROM paydetail WHERE transactionType = 1 AND isCanceled = 1 OR transactionType = 5 AND isCanceled = 1 OR transactionType = 7 AND isCanceled = 1 OR transactionType = 8 OR transactionType = 6 OR transactionType = 10 OR transactionType = 2 AND isCanceled = 0 OR transactionType = 18 AND isCanceled = 0 OR transactionType = 34 AND isCanceled = 1 AND batchNo IN(:batchNo)"))
LiveData<List<PayDetail>> getDeleteTrans(String batchNo);
@Query("SELECT * FROM paydetail WHERE transactionType = 5 AND isCanceled = 0")

View File

@ -415,7 +415,9 @@ public Retrofit provideKPayRefundRetrofit(@ApplicationContext Context context) {
@Singleton
public EReceiptApiService provideEReceiptApiService(){
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(
message -> LogUtil.d("HTTP", message)
);
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()

View File

@ -1,13 +1,16 @@
package com.utsmyanmar.baselib.network;
import com.google.gson.JsonElement;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptResponse;
import java.util.Map;
import io.reactivex.rxjava3.core.Observable;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
public interface EReceiptApiService {
@ -17,4 +20,13 @@ public interface EReceiptApiService {
@Header("x-api-key") String apiKey,
@Body Object body
);
@Multipart
@POST("transaction")
Observable<EReceiptResponse> sendReceiptMultipart(
@Header("x-timestamp") String timestamp,
@Header("x-api-key") String apiKey,
@PartMap Map<String, RequestBody> fields,
@Part MultipartBody.Part de115Image
);
}

View File

@ -165,6 +165,7 @@ public class EReceiptCardRequest {
public void setDE43(String DE43){
this.DE43 = DE43;
}
public String getDE48(){ return DE48; }
public void setDE48(String DE48){
this.DE48 = DE48;
}

View File

@ -17,6 +17,7 @@ import com.utsmyanmar.baselib.network.KPayApiService;
import com.utsmyanmar.baselib.network.KPayRefundApiService;
import com.utsmyanmar.baselib.network.SiriusApiService;
import com.utsmyanmar.baselib.network.model.DemoQRRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptCardRequest;
import com.utsmyanmar.baselib.network.model.DemoQRResponse;
import com.utsmyanmar.baselib.network.model.DemoQRReturnRequest;
import com.utsmyanmar.baselib.network.model.DemoQRReturnResponse;
@ -42,12 +43,17 @@ import com.utsmyanmar.baselib.util.EReceiptHelper;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Provider;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Flowable;
import io.reactivex.rxjava3.core.Maybe;
@ -128,6 +134,58 @@ public class Repository {
return eReceiptApiService.sendReceipt(timestamp, signature, body);
}
public Observable<EReceiptResponse> sendReceiptWithImage(EReceiptCardRequest request, byte[] imageBytes) {
String apiSecret = BuildConfig.ERECEIPT_SECRET;
String timestamp = String.valueOf(System.currentTimeMillis());
request.setDE115(null);
String bodyString = new Gson().toJson(request);
String dataToHash = bodyString + apiSecret + timestamp;
LogUtil.d("HTTP", dataToHash);
String apiKey = EReceiptHelper.sha256(dataToHash);
Map<String, RequestBody> fields = buildFormFields(request);
MultipartBody.Part imagePart = null;
if (imageBytes != null) {
RequestBody imageBody = RequestBody.create(MediaType.parse("image/png"), imageBytes);
imagePart = MultipartBody.Part.createFormData("DE115", "signature.png", imageBody);
}
return eReceiptApiService.sendReceiptMultipart(timestamp, apiKey, fields, imagePart);
}
private Map<String, RequestBody> buildFormFields(EReceiptCardRequest request) {
Map<String, RequestBody> map = new HashMap<>();
putField(map, "DE2", request.getDE2());
putField(map, "DE3", request.getDE3());
putField(map, "DE4", request.getDE4());
putField(map, "DE7", request.getDE7());
putField(map, "DE11", request.getDE11());
putField(map, "DE14", request.getDE14());
putField(map, "DE22", request.getDE22());
putField(map, "DE37", request.getDE37());
putField(map, "DE38", request.getDE38());
putField(map, "DE39", request.getDE39());
putField(map, "DE41", request.getDE41());
putField(map, "DE42", request.getDE42());
putField(map, "DE43", request.getDE43());
putField(map, "DE48", request.getDE48());
putField(map, "DE49", request.getDE49());
putField(map, "DE55", request.getDE55());
putField(map, "appId", request.getAppId());
putField(map, "batchNumber", request.getBatchNumber());
putField(map, "cardLabel", request.getCardLabel());
putField(map, "description", request.getDescription());
putField(map, "invoiceNumber", request.getInvoiceNumber());
putField(map, "serial", request.getSerial());
return map;
}
private void putField(Map<String, RequestBody> map, String key, String value) {
if (value != null && !value.isEmpty()) {
map.put(key, RequestBody.create(MediaType.parse("text/plain"), value));
}
}
public void insertPayDetail(PayDetail payDetail){ payDetailDao.insert(payDetail);}
public Completable insertPay(PayDetail payDetail) { return payDetailDao.insertPay(payDetail); }