Compare commits

...

409 Commits

Author SHA1 Message Date
moon
ee3d6a74e1 duel settlement detail 2026-07-12 21:14:59 +06:30
moon
5ecaf4abcc duel settlement 2026-07-12 20:24:57 +06:30
moon
05eca7560b remove select settlement 2026-07-12 14:41:12 +06:30
moon
8c85699c5f manual settlement completed 2026-07-09 17:08:09 +06:30
moon
aad5652988 settlement receipt changed 2026-07-09 16:39:29 +06:30
moon
9e119e7c7e combined settlement receipt 2026-07-09 16:25:52 +06:30
moon
808e87b2f9 both settle 2026-07-09 15:04:49 +06:30
moon
b8bfd5d1a7 signature send to backend 2026-07-02 13:39:50 +06:30
moon
ed304217ec db update 2026-07-02 10:29:23 +06:30
moon
b87f987466 release 2026-06-29 11:41:56 +06:30
moon
22d6066933 Update QRRefundFragment.java 2026-06-26 01:47:18 +06:30
moon
affbe79d94 now query call check after failed and recall refund again 2026-06-26 01:32:04 +06:30
moon
240172214d fix qr history showing canceled records 2026-06-26 00:26:42 +06:30
moon
35973963e6 settlement fixed 2026-06-25 15:15:58 +06:30
moon
716030128a signature save 2026-06-25 12:46:16 +06:30
moon
afd3d925e1 void by invoice 2026-06-21 21:20:00 +06:30
moon
1e29fd9fab logic change for invoice number not to increace 2026-06-18 14:42:09 +06:30
moon
0289013a58 settlement fix 2026-06-18 11:12:12 +06:30
moon
2dd686d513 ereceipt push logic changed in signature receipt 2026-06-17 21:01:30 +06:30
moon
4ffdc1f679 signature in void 2026-06-15 23:32:27 +06:30
moon
9dbb722357 fix for signature 2026-06-15 23:06:31 +06:30
moon
bd90bc4a31 Merge branch 'duel_settlement' into ESignature 2026-06-15 22:30:34 +06:30
moon
1ff9aba77a new logic in CardWaitingFragment 2026-06-15 22:30:08 +06:30
moon
c729bce5a2 hide manual entry btn 2026-06-15 22:29:23 +06:30
moon
99adb4ffe3 removed previous manual disable logics 2026-06-15 22:28:59 +06:30
moon
e2f6b53fd2 enable online pin 2026-06-15 16:34:49 +06:30
moon
b5d3c601ce update for jcb response code 2026-06-15 16:32:22 +06:30
moon
fad587f632 auto settlement printing loop 2026-06-15 16:06:04 +06:30
moon
d371e0f4e6 online pin for jcb 2026-06-04 19:18:11 +06:30
moon
c8f0da379e saved signature 2026-06-03 21:36:16 +06:30
moon
7a317dede7 qr "cancel" history removed 2026-06-03 14:47:48 +06:30
moon
7145000175 fix or jcb 2026-06-02 13:58:44 +06:30
moon
2eac171a6c jcb fix 2026-06-01 20:20:05 +06:30
moon
14ad87e41f change on prime server 2026-05-31 00:03:55 +06:30
moon
4de793fd76 currently removed void 2026-05-26 09:28:13 +06:30
moon
daba685f40 Merge branch 'test' into duel_settlement 2026-05-25 15:14:58 +06:30
moon
31705e6a9f Merge branch 'production_2.2' into test 2026-05-25 14:20:39 +06:30
moon
c3246b907a Merge branch 'production_2.2' into test 2026-05-25 11:51:30 +06:30
moon
d9de201e88 void update 2026-05-22 12:42:58 +06:30
moon
951a520335 add control bar disable 2026-05-22 12:17:48 +06:30
b8f85b3693 If MPU contactless amount is above 1Lakh, now showing as MPU-UPI contactless Insert or type card 2026-05-19 14:04:05 +06:30
5e40b948de removed AID for JCB cards and Pre Auth Flow is fixed 2026-05-19 14:04:05 +06:30
eb400a2ee8 If MPU contactless amount is above 1Lakh, now showing as MPU-UPI contactless Insert or type card 2026-05-19 13:46:48 +07:00
d542388ce1 removed AID for JCB cards and Pre Auth Flow is fixed 2026-05-19 12:25:49 +07:00
moon
8f218b5c99 restricted_bin_list 2026-05-18 15:09:59 +06:30
moon
75fb969740 Merge branch 'duel_settlement' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into duel_settlement 2026-05-18 15:09:58 +06:30
bab8390ba4 MPU-UPI contactless is restricted, need to put it in db later. 2026-05-18 15:09:58 +06:30
moon
d32dd1ce5b jcb terminal capability added 2026-05-18 15:09:58 +06:30
77b8d1956a MPU contactless finished / if amount is exceeded 100000 MMK , will route to card capture to proceed chip 2026-05-18 15:09:58 +06:30
moon
7ec5424ac4 restricted_bin_list 2026-05-17 21:40:58 +06:30
moon
a5ce7dc8e5 Merge branch 'duel_settlement' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into duel_settlement 2026-05-17 14:46:50 +06:30
e929f63618 MPU-UPI contactless is restricted, need to put it in db later. 2026-05-17 15:01:40 +07:00
moon
0f58fcdd6e jcb terminal capability added 2026-05-17 09:22:00 +06:30
e004f925c9 MPU contactless finished / if amount is exceeded 100000 MMK , will route to card capture to proceed chip 2026-05-16 22:55:51 +07:00
a04e008c5b fix for settlement total amount alignment 2026-05-13 14:19:19 +06:30
781d7a0fce fixed for preauth comp cancel not allowed emv cards 2026-05-13 14:18:54 +06:30
01ece0d9c5 fix for settlement total amount alignment 2026-05-09 19:41:23 +07:00
moon
528bcbb004 prod ready 2026-05-08 14:10:14 +06:30
928879b9e7 fixed for preauth comp cancel not allowed emv cards 2026-05-07 12:49:59 +07:00
moon
6f711acaf1 Update CHANGELOG.md 2026-05-06 15:58:29 +06:30
moon
993892bcdb Update RefundCertificateManager.java 2026-05-06 15:55:09 +06:30
moon
2c8b95742e update on Prime link 2026-05-06 15:54:22 +06:30
moon
1d48c48c0e toggle beep 2026-05-06 15:51:18 +06:30
moon
309d105fe1 Merge branch 'duel_settlement' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into duel_settlement 2026-05-06 15:43:32 +06:30
moon
4eff1168c0 Create CHANGELOG.md 2026-05-06 15:43:29 +06:30
moon
79756a81aa add RRN search in ReprintAnyScreen 2026-05-06 15:38:06 +06:30
moon
9c8dc0be95 pinpad sound enable/disable toggle added 2026-05-06 15:27:19 +06:30
moon
6f3f6d553b Trace No to Invoice Num in ReprintAnyTran 2026-05-06 14:16:04 +06:30
fb21ca1a51 enable/disable beep, not yet bind with tms 2026-05-06 14:37:36 +07:00
moon
20be06c7db modification for upi certification cases 2026-05-05 19:05:22 +06:30
8c151e3136 Merge remote-tracking branch 'origin/duel_settlement' into duel_settlement 2026-04-27 12:23:57 +07:00
1f6031ae9e fixed for preAuth cancel, not allowing preauth cancel after partial pre comp was done. 2026-04-27 12:23:51 +07:00
moon
9fdb22f5f5 tmk key changed 2026-04-27 11:19:02 +06:30
moon
566d39a06c hide nav 2026-04-09 16:19:34 +06:30
moon
c9032de30d hide/show auto-reversal 2026-04-09 16:13:19 +06:30
moon
9b9d827d47 clear reversal moved into "See More" 2026-04-09 15:30:30 +06:30
moon
1287c45c0d fixTransactionType 2026-04-09 15:09:34 +06:30
moon
e45f61b4c2 change trace num to inv num 2026-04-09 15:00:53 +06:30
moon
ec7f04eb85 detail report with align column 3 2026-04-09 14:43:08 +06:30
moon
fb898b1a0f minify enabled 2026-04-09 14:30:29 +06:30
moon
a8d0e9d182 amount fix 2026-04-09 14:10:50 +06:30
moon
56187a6639 3 terminal capability 2026-04-08 15:04:17 +06:30
moon
ecaa5b8eda fix amount 2026-04-08 12:19:34 +06:30
moon
d0002fa928 amount hide if zero 2026-04-08 12:05:48 +06:30
cda1901d53 fix 1 for 8 digit bin visa 2026-04-06 16:53:44 +07:00
833d4b823e fixed refund for MPU 2026-04-06 12:14:56 +07:00
moon
71b4403adc Update BaseXPrint.java 2026-04-06 00:29:27 +06:30
moon
820dcf9aa6 fix of summary report bug 2026-04-06 00:13:41 +06:30
moon
2238988846 align col in summary report 2026-04-05 20:00:54 +06:30
moon
e6851cba3b decimal toggel in card Receipt 2026-04-05 19:09:52 +06:30
moon
f646c62362 Update BaseXPrint.java 2026-04-02 16:17:23 +06:30
moon
e2fc293e6b toast show when e-receipt success and failed 2026-04-01 20:52:09 +06:30
5c036eb954 Merge remote-tracking branch 'origin/duel_settlement' into duel_settlement 2026-04-01 16:40:31 +07:00
1f01c40a41 visa added aid 2026-04-01 16:40:24 +07:00
moon
ac7bff26b9 Update BaseXPrint.java 2026-04-01 14:38:37 +06:30
moon
8b49927b79 Merge branch 'duel_settlement' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into duel_settlement 2026-04-01 14:10:23 +06:30
4a2a7fca67 settlement fixed for e-receipt 2026-04-01 14:36:17 +07:00
moon
cb96ccb7ca Update TransactionSummaryFragment.java 2026-04-01 13:49:47 +06:30
moon
18eda34bff Update BaseXPrint.java 2026-04-01 13:44:51 +06:30
moon
6cd27b6482 new fields in e-receipt 2026-04-01 13:32:26 +06:30
moon
9a55fadcf0 Merge branch 'k' into duel_settlement 2026-04-01 00:57:08 +06:30
moon
d145cf321f summary report fixed for Duel currency 2026-04-01 00:56:59 +06:30
moon
88a5d064b5 Merge branch 'k' into duel_settlement 2026-03-31 22:36:41 +06:30
moon
1aac919c47 duel currency fix for detail report 2026-03-31 21:24:58 +06:30
ddd64fb48d fixed for settlement 2026-03-31 21:49:43 +07:00
175ebab148 settlement finished for dual currency 2026-03-31 18:55:22 +07:00
moon
aabab54048 settlement fix 2026-03-31 12:58:15 +06:30
moon
07227ca9bb duel currency in pre-auth 2026-03-31 12:44:11 +06:30
moon
7531e7edda duel currency in void 2026-03-31 00:15:11 +06:30
moon
f63f10e834 fix injection 2026-03-31 00:12:05 +06:30
moon
d143e49e4b sing on duel 2026-03-30 23:57:29 +06:30
moon
f7b200de73 Update SystemParamsOperation.java 2026-03-30 23:41:09 +06:30
moon
4b03e2feae Merge branch 'ecr' into duel_settlement 2026-03-30 23:36:18 +06:30
moon
f3e735b417 update BitmapConfig 2026-03-30 23:33:18 +06:30
ed5fadcdd6 preauth/preauth cancel/preauth comp added manual entry flag 2026-03-30 16:23:52 +07:00
moon
cdb7bb73c5 Merge branch 'ecr' into duel_settlement 2026-03-29 22:45:25 +06:30
moon
4f28f7a118 Update InjectKeyFragment.java 2026-03-29 22:43:15 +06:30
moon
3cb8fe218e duel key inject UI 2026-03-27 21:26:23 +06:30
890b1c55a8 fixed sending multiple transaction if there has reversal 2026-03-26 17:23:23 +07:00
01043dbbca added auto reversal 2026-03-26 16:04:14 +07:00
c503b95027 void fixing 2026-03-26 10:38:18 +07:00
e10e10a793 void fixed 2026-03-25 16:48:54 +07:00
2cdfc7c34f pre auth cancel fixed 2026-03-25 12:20:25 +07:00
moon
99b9f4835c UPI fix for certification 2026-03-24 19:19:22 +06:30
moon
a37b035493 host config break add 2026-03-20 12:20:48 +06:30
moon
49e787a56c printer disable in reprint settlement 2026-03-20 12:07:13 +06:30
dd265c1fa8 auto settlement completed 2026-03-16 18:00:02 +07:00
MooN
95d50ec3a7 location detection imp 2026-03-16 15:03:20 +06:30
MooN
c71eaef5b5 geo get set 2026-03-16 15:03:19 +06:30
moon
b56cb9f78b duel enable 2026-03-16 11:59:03 +06:30
moon
3c44f3d1ba add is_duel_currency flag 2026-03-16 11:38:30 +06:30
moon
b5f174ff54 fixed for upi 2026-03-13 16:04:00 +06:30
87a3b5da83 edited threshold value 2026-03-13 14:09:52 +07:00
moon
ef74f2cc82 toast for wrong password 2026-03-12 16:46:56 +06:30
moon
3e4b3e88a4 Merge branch 'ecr' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into ecr 2026-03-12 14:58:04 +06:30
e60985b2ec fixed for pinpad retry 2026-03-12 15:23:45 +07:00
moon
8173e0fcfe mag fix 2026-03-12 14:33:14 +06:30
ffc3908446 fixed for mag stripe 2026-03-09 14:31:15 +07:00
05a91782b5 Merge remote-tracking branch 'origin/ecr' into ecr 2026-03-09 13:03:22 +07:00
898c8edea4 fixed for de 61 SF 11 2026-03-09 13:03:15 +07:00
moon
2c87ee5828 fix receipt for duel host 2026-03-09 10:31:21 +06:30
MooN
df025bce34 duel currency select in sale 2026-03-07 15:55:06 +06:30
MooN
c251583f46 sec host 2026-03-07 15:55:05 +06:30
moon
a83e98e3c8 Merge branch 'ecr' into new_history 2026-03-06 23:43:09 +06:30
moon
5ee9745762 server address 2026-03-06 23:42:24 +06:30
moon
83bc15a7ab pinpad fix 2026-03-06 23:06:11 +06:30
80a1229d0f fixed for magstripe not allowed and 12 digit pin 2026-03-06 16:43:56 +07:00
e0906ab434 Merge remote-tracking branch 'origin/ecr' into ecr
# Conflicts:
#	baselib/src/main/java/com/utsmyanmar/baselib/viewModel/EmvBaseViewModel.java
2026-03-06 16:41:08 +07:00
620620e138 fixed for magstripe not allowed 2026-03-06 16:40:43 +07:00
moon
d8d1a5c1d5 amount fix 2026-03-06 15:25:14 +06:30
89252a6732 fixed com2 2026-03-06 11:21:54 +07:00
900ca456dd fixed for key issues 2026-03-05 16:40:25 +07:00
9e2dd24f3d fixed for DE 52 2026-03-05 14:16:54 +07:00
MooN
66680c0baf Merge branch 'new_history' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into new_history 2026-03-05 09:39:36 +06:30
MooN
b7822a2736 master tran error fix 2026-03-05 09:37:04 +06:30
b808a87f23 Merge remote-tracking branch 'origin/new_history' into new_history 2026-03-04 14:34:16 +07:00
8791e6de4c backup 2026-03-04 14:34:03 +07:00
MooN
84311f4dc8 emv receipt design changes 2026-03-04 11:51:34 +06:30
546893e806 rid fixed 2026-02-26 17:14:35 +07:00
66f4396e69 rid reorder 2026-02-26 16:56:37 +07:00
11d868210f retry fixed for visa 2026-02-25 21:32:23 +07:00
3d619d370f fixed for visa 2026-02-25 21:23:30 +07:00
e2b53af27c added master CAPK 2026-02-25 15:54:41 +07:00
MooN
34b88f81b3 duel currency select in sale 2026-02-23 23:33:40 +06:30
MooN
e4bb3df554 sec host 2026-02-23 23:16:06 +06:30
MooN
999b26de2b animation in host config 2026-02-23 22:21:03 +06:30
3811c04179 3rd fix for 1049 2026-02-23 14:31:53 +07:00
b685e026d7 2nd fix for 1049 2026-02-23 12:28:42 +07:00
423d6cd019 fix for 1049 2026-02-23 12:05:18 +07:00
2e26a62145 fixed for card retry 2026-02-23 11:31:59 +07:00
MooN
2b114dab5e settlement fix for amount always 0 2026-02-20 23:52:44 +06:30
538ae41938 5th fix for see phone 2026-02-20 10:53:40 +07:00
4aab58d0bf 4th fix for see phone 2026-02-19 16:39:30 +07:00
2e593d5dd9 3rd fix for see phone 2026-02-19 16:24:05 +07:00
de296b3b42 2nd fix for see phone 2026-02-19 16:21:55 +07:00
2c7bc3a3d5 fixed for see phone 2026-02-19 16:00:13 +07:00
8ff39cc1df added visa aid 2026-02-19 15:35:30 +07:00
8f6f9c54b9 added visa credit and debit aid 2026-02-19 15:23:33 +07:00
cd35f9ad06 put pin if isOnline 2026-02-19 14:43:36 +07:00
MooN
f2e9ad1e92 ecr tempo removed 2026-02-19 11:06:24 +06:30
bc3b2cbe4a changes in TTQ[index 1] and CAPK modulus and checksum to lowercase/ datetime as ascii to string 2026-02-19 10:56:20 +07:00
MooN
328f114ca0 fail receipt disable 2026-02-18 22:08:24 +06:30
MooN
74bed47d68 Update InputRRNFragment.java 2026-02-18 21:28:49 +06:30
MooN
b7830491d5 reversal open 2026-02-09 23:35:53 +06:30
MooN
1b68220c9d Update TransactionSummaryFragment.java 2026-02-09 22:48:09 +06:30
MooN
522af3578c Update BaseXPrint.java 2026-02-09 22:41:57 +06:30
MooN
7f9aa52e47 detail report for card design changed as qr 2026-02-09 22:29:37 +06:30
MooN
b87dc2a60a fix REprint Any Trax for showing SEttlement Data 2026-02-09 22:29:18 +06:30
MooN
28b4019bd7 Card detail report design change to meet as QR 2026-02-09 22:28:56 +06:30
MooN
e717a64c30 Hide settlement data in Dashboard history Fragment 2026-02-09 22:27:56 +06:30
MooN
7b8728da8f seperated QR and Card Latest Transaction Records 2026-02-09 21:53:41 +06:30
MooN
e873606e4c combine MPU and internation hosts 2026-02-08 21:28:53 +06:30
MooN
13cc4db6ab update ignore 2026-02-08 17:32:43 +06:30
MooN
f2443499a1 fix for print latest settlement 2026-02-06 11:38:30 +06:30
MooN
fe8c5efe8e Update DashboardHistoryFragment.java 2026-02-06 00:10:09 +06:30
MooN
b3406a12bb history added in Dashboard History Screen 2026-02-06 00:02:56 +06:30
MooN
8f9c61dbac printer disabled condition applied on QR Settlement 2026-02-05 23:11:01 +06:30
MooN
fccf781c3b print button hidden in Detail Report 2026-02-05 22:46:40 +06:30
MooN
fc4e3558f7 print button hide in Report Summary 2026-02-05 22:37:39 +06:30
MooN
dcaf00e542 hide print buttons in Re-prints 2026-02-05 21:33:17 +06:30
MooN
3b08569a48 print button hide in hostConfigScreen 2026-02-05 21:04:08 +06:30
MooN
74e9e21ee0 printer disable apply for transaction success 2026-02-05 21:03:42 +06:30
MooN
4867644d69 added new history screen 2026-02-05 15:23:16 +06:30
MooN
6669528fdb auto address fixex 2026-02-05 12:24:22 +06:30
MooN
eed5980cfa change printer_disabled to printer_enabled bez of user confusing 2026-02-04 11:21:49 +06:30
MooN
8482e4145d Re-imp for auto print after transaction 2026-02-04 10:59:05 +06:30
MooN
cb5177fa38 material lib for time picker 2026-02-03 22:32:03 +06:30
MooN
02629279cb button fix 2026-02-03 22:31:42 +06:30
MooN
caf656be60 timePicker added 2026-02-03 22:21:15 +06:30
MooN
7346bae086 mpu receipt fixed 2026-01-29 14:49:21 +06:30
MooN
77d1050021 Update RefundCertificateManager.java 2026-01-29 13:39:59 +06:30
MooN
f6067f8de6 host config address fix 2026-01-29 13:29:50 +06:30
MooN
3c77d4d9c7 changed to local server 2026-01-29 10:48:34 +06:30
MooN
37b1538e1c Merge branch 'refund_fix' into auto_print_disable 2026-01-28 16:22:23 +06:30
MooN
95ec5993b5 Update SettlementTransactionFragment.java 2026-01-28 11:08:44 +06:30
MooN
d89ce9771f fix 2026-01-26 15:11:29 +06:30
MooN
850bc21a9f disable / enable void in home screen 2026-01-26 12:19:19 +06:30
MooN
2c98727f2e Transactions buttons show / hide with config 2026-01-26 12:01:58 +06:30
MooN
fc8349ad2d re-unbind visible on hostConfig 2026-01-26 10:43:27 +06:30
MooN
0ddd5f269c Update BaseXPrint.java 2026-01-26 10:42:53 +06:30
MooN
d9b9c7cc80 Update network_security_config.xml 2026-01-26 10:42:52 +06:30
MooN
e6878a3d27 printer disable function 2026-01-26 10:42:49 +06:30
MooN
40c4ba2d25 Update NetworkModule.java 2026-01-26 10:42:48 +06:30
MooN
547c04e0ca re-unbind visible on hostConfig 2026-01-23 12:10:20 +06:30
MooN
4b10de477d uat url changed 2026-01-23 11:27:17 +06:30
MooN
f33f3463a8 Update BaseXPrint.java 2026-01-23 10:54:39 +06:30
MooN
133593a4d7 Update network_security_config.xml 2026-01-23 10:28:53 +06:30
MooN
2fedb6cee2 printer disable function 2026-01-23 10:28:10 +06:30
MooN
84f0620481 Update NetworkModule.java 2026-01-22 20:04:27 +06:30
MooN
b2a92f8cf8 changed to local server 2026-01-22 13:47:44 +06:30
67fc55800a Fixed Bin List issues 2026-01-21 19:33:57 +07:00
8e93e61eb5 fixed crashing on contactless 2026-01-20 15:31:25 +07:00
16b89ec19e fixed crashing on co-badge card. 2026-01-20 14:51:17 +07:00
4d30d0134c added bin list and its features 2026-01-19 23:46:55 +07:00
e4f706f24f Merge remote-tracking branch 'origin/merge_latest_SIT' into merge_latest_SIT 2026-01-18 23:05:46 +07:00
c2ec55e27f removed cvv at manual entry/ fixed settlement 0 transaction 2026-01-18 23:05:37 +07:00
MooN
bfba551b65 re-changed to local server 2026-01-18 11:21:03 +06:30
MooN
22ddf9284c Merge branch 'production_1.0' into merge_latest_SIT 2026-01-18 11:14:06 +06:30
MooN
36f5d521cd Update ReprintAnyTransactionFragment.java 2026-01-16 01:48:54 +06:30
MooN
3a14c574a8 Create release_key.jks 2026-01-16 01:28:59 +06:30
MooN
c495a29b06 temp card settlement fix 2026-01-16 01:28:47 +06:30
MooN
ce32ed2327 INV NO to TRACE NO 2026-01-15 23:57:31 +06:30
MooN
0ad0bc17b7 keyboard auto hide and search by trace no fix 2026-01-15 23:57:04 +06:30
MooN
fe97613f9b inv num to trc num in qr 2026-01-15 23:56:26 +06:30
MooN
e4ea98f6e6 Update .gitignore 2026-01-15 20:11:06 +06:30
MooN
042fe6307e log clean 2026-01-15 20:11:00 +06:30
MooN
98c94ec2d3 refund url 2026-01-14 11:45:03 +06:30
MooN
58118a2fb5 production ready 2026-01-13 23:34:12 +06:30
c9dd1b93f7 optimized refund 2026-01-13 14:57:32 +06:30
MooN
5b749073d0 update rules for printer crash 2026-01-13 11:09:55 +06:30
398a0d7986 optimized proguard rules 2026-01-12 23:14:22 +07:00
MooN
89466f8209 latest 2026-01-12 22:10:15 +06:30
720b3ddccb optimized refund 2026-01-12 14:47:20 +07:00
MooN
b4ecd40b46 uat url changed 2026-01-11 19:25:11 +06:30
MooN
0f7b57e516 Update .gitignore 2026-01-11 19:23:35 +06:30
13617eead1 fixed DE55 length 2026-01-10 16:16:43 +07:00
25ba7022da changed 9F41 len 03 to 04 2026-01-10 13:05:53 +07:00
280d8f154d removed extra 1 byte for DE55 2026-01-10 12:21:57 +07:00
ca9a4a0485 removed 9F53 tag 2026-01-10 11:28:05 +07:00
452f0beb97 fixed ISO packet format for DE55 2026-01-09 17:35:37 +07:00
MooN
6bb3ab45c1 grand total in qr settlement 2026-01-09 16:01:14 +06:30
MooN
f071f03dd2 receipt design change 2026-01-09 15:58:43 +06:30
MooN
654e7ddc7e receipt design change 2026-01-09 15:56:13 +06:30
MooN
3e342a71ca Update build.gradle 2026-01-09 15:38:52 +06:30
MooN
c5fb59473f Update QRRefundDetailFragment.java 2026-01-09 15:22:56 +06:30
17ff039cec need to check conflict at print functions 2026-01-09 15:44:19 +07:00
37a3fb27bc Merge remote-tracking branch 'origin/merge_latest_SIT' into merge_latest_SIT
# Conflicts:
#	paylibs/src/main/java/com/utsmyanmar/paylibs/print/printx/BaseXPrint.java
2026-01-09 15:05:14 +07:00
bf3159361a card settlement/ manual entry optimized 2026-01-09 15:02:31 +07:00
MooN
0faaf36fde alert fix for refund 2026-01-08 16:18:39 +06:30
MooN
ccebba6ba7 added Grand total in Settlement 2026-01-08 12:47:07 +06:30
MooN
00bd4c417e refund fail amount wrong fixed 2026-01-06 17:45:54 +06:30
MooN
adbd0724af SIM INTERNET FIX 2026-01-05 20:46:37 +06:30
MooN
48581e1291 receipt design change 2026-01-05 20:46:18 +06:30
MooN
8cb901d522 font change 2025-12-31 15:30:19 +06:30
MooN
8cac361ce0 some fix for detail report 2025-12-30 13:08:03 +06:30
MooN
bd13d50848 Merge branch 'merge_latest_SIT' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into merge_latest_SIT 2025-12-30 10:46:50 +06:30
MooN
1f1eaca712 date time in detail report 2025-12-30 10:46:47 +06:30
14fe578bce Added QR Last Settlement - final 2025-12-30 03:09:33 +07:00
MooN
4c77b376f0 address fix 2025-12-29 11:10:06 +06:30
MooN
6a4ee61066 Update EReceiptUtil.java 2025-12-29 02:31:40 +06:30
MooN
7276f8c572 settlement QR setup 2025-12-29 02:19:58 +06:30
MooN
c6c5dd39e4 Update RefundCertificateManager.java 2025-12-29 01:25:40 +06:30
MooN
cd7cfa2a7d refund cached certificate fixed 2025-12-29 01:03:47 +06:30
MooN
062533662f update refund certificate download logic 2025-12-28 13:42:24 +06:30
e1dacaadcb QR auto settlement completed (pls adjust for E-Receipt both in manual & auto) AutoSettleService/QRSettlementTransactionFragment 2025-12-27 02:30:29 +07:00
1e1f749148 QR auto settlement completed (pls adjust for E-Receipt both in manual & auto) AutoSettleService/QRSettlementTransactionFragment 2025-12-27 02:29:29 +07:00
9a7f4a5245 Merge remote-tracking branch 'origin/merge_latest_SIT' into merge_latest_SIT 2025-12-25 17:37:28 +07:00
bcbe16e201 QR Auto Settlement draft version,still need to optimize it 2025-12-25 17:37:20 +07:00
MooN
1eb0a550fe update for QRSettlement slip design and hide navi 2025-12-24 20:30:31 +06:30
MooN
6c596cb061 disabled navigation 2025-12-24 15:46:35 +06:30
MooN
5163ce2840 Merge branch 'merge_latest_SIT' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into merge_latest_SIT 2025-12-24 15:17:20 +06:30
MooN
29073c0ef8 address 3 added 2025-12-24 15:17:15 +06:30
122c526cc9 disable home button and removed exit button from navigation menu 2025-12-24 15:10:33 +07:00
MooN
9e8315050a Update fragment_host_config.xml 2025-12-23 23:24:57 +06:30
MooN
83af914911 fix for refund decimal in summary and detail reports 2025-12-23 23:03:15 +06:30
MooN
bbabe9b824 physical receipt fix decimal for print summery 2025-12-23 21:31:54 +06:30
MooN
063805e77a fix detail report decimal 2025-12-23 20:06:27 +06:30
MooN
34b2fe8189 e-receipt fix for qr settlement 2025-12-23 12:05:46 +06:30
c96d68ff2f QR Settlement is optimized and History was also resolved 2025-12-23 01:39:47 +07:00
MooN
ee037a1101 hide time picker 2025-12-22 22:54:28 +06:30
MooN
aa34f8e65e Update TMSSetupsImpl.java 2025-12-22 16:24:59 +06:30
MooN
28b17a28be qr_decimal_fix 2025-12-22 16:07:35 +06:30
MooN
aa1cf12d93 print terminal config print method refactor 2025-12-21 20:40:10 +06:30
MooN
e864630aaf modified the physical receipt 2025-12-21 11:10:37 +06:30
MooN
05eed5776d added payment id 2025-12-18 12:09:13 +06:30
MooN
d843415366 update for operator_id 2025-12-18 11:51:35 +06:30
MooN
691ccff49f file re-download fix 2025-12-17 23:14:43 +06:30
MooN
666325ed82 e-receipt new fields 2025-12-17 22:55:44 +06:30
MooN
5799072d9f Update HostConfigFragment.java 2025-12-17 15:03:39 +06:30
MooN
015fe3524d fix for receipt 2025-12-17 13:59:05 +06:30
MooN
4984a726e9 hide history in see more 2025-12-17 02:27:46 +06:30
MooN
9a4d8e279a close order 2025-12-17 00:58:47 +06:30
MooN
5ce14a31ca fix incorrect value in QR refund 2025-12-17 00:54:46 +06:30
MooN
cca1b6ded6 SiriusTerminal added
added new reponse for SiriusTerminal
2025-12-16 23:10:16 +06:30
7d8f9f5af2 QR settlement added crash handling 2025-12-16 00:31:22 +07:00
c46b8793a1 QR settlement fixed 2025-12-16 00:05:05 +07:00
3eb67d92d9 modified commented code block 2025-12-15 23:15:37 +07:00
83d7f44561 Merge remote-tracking branch 'origin/merge_latest_SIT' into merge_latest_SIT
# Conflicts:
#	paylibs/src/main/java/com/utsmyanmar/paylibs/print/printx/BaseXPrint.java
2025-12-15 23:13:14 +07:00
4e26b2ec4d fixed QR Settlement & History 2025-12-15 23:12:51 +07:00
MooN
4ce7a9355c shrinking physical receipt 2025-12-15 22:35:22 +06:30
MooN
c697d6c3f9 auto print disabled
currently auto print disabled for QR
2025-12-15 15:49:12 +06:30
MooN
53a9d8ba1b added change log 2025-12-12 14:14:48 +06:30
MooN
e6e6f68360 Update TMSSetupsImpl.java
update Null safety for shortCode
2025-12-12 14:14:27 +06:30
MooN
d5bef92cff slip font change 2025-12-12 02:07:31 +06:30
MooN
a47137f624 qr close order api
qr close order api  called on back press of generated qr
2025-12-12 01:01:42 +06:30
MooN
299c8fc892 dynamic refund certi 2025-12-11 23:43:27 +06:30
f2e797a0dc fixed for shortCode 2025-12-11 16:59:17 +07:00
52ee779e18 decimal hide is fixed 2025-12-11 15:11:14 +07:00
a071c53823 no 7 and 8 fixed 2025-12-11 13:21:46 +07:00
d76b48d4ca fixed amount screen issues 2025-12-11 12:22:24 +07:00
MooN
ff9450a50c refund crash fixed 2025-12-11 04:38:57 +06:30
MooN
7452443a45 add expire time on QR 2025-12-11 03:02:40 +06:30
7d894116dd Merge remote-tracking branch 'origin/merge_latest_SIT' into merge_latest_SIT 2025-12-10 15:20:04 +07:00
0804f7b762 dynamic tpdu 2025-12-10 15:19:50 +07:00
MooN
a1c0f55472 Merge branch 'merge_latest_SIT' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into merge_latest_SIT 2025-12-10 14:48:44 +06:30
MooN
71e0e24b3c disable demo mode 2025-12-10 14:47:25 +06:30
014bdc18e3 implemented ubuntu_mono font for all receipts 2025-12-10 05:13:26 +07:00
MooN
97014377a8 Merge branch 'merge_latest_SIT' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into merge_latest_SIT 2025-12-09 16:07:37 +06:30
MooN
424678e0ca update button hide/show on qr refund 2025-12-09 16:07:21 +06:30
b92f9bfe4e QR settlement completed 2025-12-09 05:58:09 +07:00
MooN
d42ee5d2f7 Merge branch 'merge_latest_SIT' of https://hub.utsmyanmar.com/Kyaw_Min_Khant/KBZ-POS into merge_latest_SIT 2025-12-08 22:04:42 +06:30
MooN
3fe711cc2f multi variant 2025-12-08 11:44:23 +06:30
f91ae09be7 QR settlement need to adjust slip design. 2025-12-07 05:49:17 +07:00
MooN
eebe9ac2c6 released 2025-12-05 13:56:22 +06:30
MooN
d3a8bf6ab5 release mode 2025-12-04 12:10:30 +06:30
MooN
311f7a7e1e nexdkey-lib fix 2025-12-04 10:53:25 +06:30
MooN
2204e99463 Merge branch 'merge-latest' into fix_ 2025-12-04 10:44:38 +06:30
8200cefb96 fixed xml not included 2025-12-04 11:13:42 +07:00
MooN
c18704f031 Merge branch 'merge-latest' into fix_ 2025-12-04 10:37:09 +06:30
MooN
eed7dd4680 host config view 2025-12-04 10:32:31 +06:30
1a7e86077c fixed nexgo-sdklkey lib not included 2025-12-04 08:10:41 +07:00
MooN
20417eb811 settings buttons disable 2025-12-03 21:45:07 +06:30
MooN
0d796ab20f qr_partial_enable/disable fixed 2025-12-03 20:59:39 +06:30
8b211ca530 changed static key index to config index at sign on 2025-12-03 05:06:03 +07:00
6bf49b3f19 e-receipt settlement/popup retry dialog when paper roll is empty/key inject implemented 2025-12-03 04:56:57 +07:00
MooN
4d5124fc64 qr e-receipt push fix 2025-12-03 00:01:11 +06:30
MooN
6b1c6ef4b5 dashboard fixed 2025-12-02 23:47:30 +06:30
MooN
a6db8970af Merge branch 'merge-latest' into fix_ 2025-12-02 14:13:45 +06:30
2f4db87441 e signature is now printing on paper 2025-12-02 05:10:22 +07:00
MooN
315d37ca31 fix bugs from excel 1 2025-12-02 01:01:39 +06:30
MooN
ff68ee6a1f qr fixes 2025-12-01 15:58:06 +06:30
aa37e14ea4 integrated E-Receipt for card transactions and
fixed some other issues
2025-12-01 05:46:01 +07:00
MooN
a0ffff71c4 qr decimal fix 2025-11-28 09:43:25 +06:30
MooN
2441ba5141 e-receipt for qr fixed 2025-11-27 19:18:49 +06:30
MooN
90c9a48197 Merge branch 'merge-latest' into geo_fench 2025-11-26 10:13:16 +06:30
1ac8c14b09 updated some codes 2025-11-25 17:12:42 +07:00
MooN
af45ed4b43 + 1 version 2025-11-25 13:40:47 +06:30
MooN
799f03dbcd update for Mid length 2025-11-25 13:40:35 +06:30
44f4e1ea94 updated process code 2025-11-24 16:00:12 +07:00
MooN
c619166ea8 fix - sec ip empty 2025-11-24 08:46:49 +06:30
49b929ab27 switched BPC host 2025-11-23 20:19:00 +07:00
MooN
ab4801d687 version fix 2025-11-23 18:45:49 +06:30
MooN
d8befb7f2a Update fragment_qr_refund_detail.xml 2025-11-23 17:16:52 +06:30
MooN
3d2b316e99 Merge remote-tracking branch 'origin/mpu_latest' into latest 2025-11-23 15:56:10 +06:30
MooN
8bf4d6da22 decimal fix
decimal fix
2025-11-23 15:55:48 +06:30
MooN
adfa73070f Update fragment_qr_refund_detail.xml
disable partial refund
2025-11-23 15:45:34 +06:30
MooN
a449a366e0 Update QRTransactionFragment.java 2025-11-23 15:24:26 +06:30
MooN
5d450e0a48 qr-e-receipt 2025-11-23 01:35:04 +06:30
ced683e302 mpu certi final 2025-11-21 14:34:06 +07:00
MooN
1811fefec1 disable printing on qr_pay_failed 2025-11-20 15:50:29 +06:30
MooN
b484f69cb3 mmqr_interval
mmqr interval connection with TMS
2025-11-20 15:49:55 +06:30
MooN
8cd56777cc color testing 2025-11-19 15:58:02 +06:30
MooN
53f6414f70 Merge branch 'new_dashboard' into merge_with_mpu 2025-11-19 15:48:01 +06:30
MooN
bb60828767 Merge remote-tracking branch 'origin/mpu' into merge_with_mpu 2025-11-19 15:42:49 +06:30
MooN
749abb1455 square btn changed 2025-11-19 12:13:17 +06:30
a8624195b0 mpu certi ready 2025-11-19 02:07:54 +07:00
MooN
65eb17e3b9 new dashboard design 2025-11-18 22:05:34 +06:30
2f9f06ce87 mpu certi almost ready 2025-11-18 06:17:40 +07:00
41b6fb7782 mpu certi almost ready 2025-11-18 06:13:31 +07:00
MooN
7e2a589636 ui fix 2025-11-17 01:41:45 +06:30
MooN
7c266677e7 merge fix 2025-11-16 22:34:22 +06:30
MooN
063dfae87f Merge remote-tracking branch 'origin/master' into merge 2025-11-14 16:37:56 +06:30
MooN
9502ee7a04 13-Nov-25 2025-11-13 14:45:18 +06:30
MooN
5509bf4349 up 2025-11-13 11:27:36 +06:30
476e1d9eaa corrected refund flow 2025-11-13 02:49:21 +07:00
28d4672878 fixed password for refund 2025-11-13 01:01:01 +07:00
MooN
644df8211d qr_refundable_from_list 2025-11-12 21:16:22 +06:30
MooN
2358782f3d safe to route fix 2025-11-11 23:12:22 +06:30
MooN
f142df0144 refudn list 2025-11-11 22:58:55 +06:30
8da4b2399e fixed some formats 2025-11-11 17:26:54 +07:00
MooN
efb0c76f69 qr only history 2025-11-10 16:27:06 +06:30
MooN
d861c278a2 qr_generate 2025-11-09 16:32:15 +06:30
5efcc7ef24 fixed can't call sign on on demo mode 2025-11-07 15:34:00 +07:00
c7ac1e90bf fixed second generated QR 2025-11-07 14:04:59 +07:00
1b4a56c29f sec commit 2025-11-07 01:46:25 +07:00
a885d05b62 sec commit 2025-11-07 01:40:46 +07:00
1cedbc987c sec commit 2025-11-07 01:38:39 +07:00
0a2cc295a1 first commit 2025-11-06 13:38:40 +07:00
b5a82a5fd1 first commit 2025-11-06 01:08:32 +07:00
521 changed files with 32207 additions and 8190 deletions

10
.gitignore vendored
View File

@ -13,3 +13,13 @@
.externalNativeBuild
.cxx
local.properties
/.idea
/app/release/*
/app/sit/release/*
/app/uat/release/*
/app/uat
/.idea
/app/prod/release
nexdlkey-lib/build/
/nexsdk-lib
/nexsdk-lib

View File

@ -1 +1 @@
KBZ-Master
KBZ-Master-latest

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AskMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Ask2AgentMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EditMigrationStateService">
<option name="migrationStatus" value="COMPLETED" />
</component>
</project>

View File

@ -4,7 +4,40 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-10-28T19:40:02.148831Z">
<DropdownSelection timestamp="2026-01-20T10:19:44.274945Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=0123456789ABCDEF" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="unitTest">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-01-20T10:19:44.274945Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=0123456789ABCDEF" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="androidTest">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-01-20T10:19:44.274945Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=0123456789ABCDEF" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
<SelectionState runConfigName="main">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-01-20T10:19:44.274945Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=0123456789ABCDEF" />

View File

@ -14,10 +14,11 @@
<option value="$PROJECT_DIR$/baselib" />
<option value="$PROJECT_DIR$/ecr" />
<option value="$PROJECT_DIR$/ecr-service-lib" />
<option value="$PROJECT_DIR$/link-service-lib" />
<option value="$PROJECT_DIR$/mpulib" />
<option value="$PROJECT_DIR$/nexdlkey-lib" />
<option value="$PROJECT_DIR$/nexsdk-lib" />
<option value="$PROJECT_DIR$/paylibs" />
<option value="$PROJECT_DIR$/paysdk-lib" />
<option value="$PROJECT_DIR$/qrgen-lib" />
<option value="$PROJECT_DIR$/xpay" />
</set>

View File

@ -0,0 +1,4 @@
kotlin version: 2.0.21
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
1. Kotlin compile daemon is ready

32
AGENT.MD Normal file
View File

@ -0,0 +1,32 @@
# Ponytail, lazy senior dev mode
You are a lazy senior developer. Lazy means efficient, not careless. The best code is the code never written.
Before writing any code, stop at the first rung that holds:
1. Does this need to be built at all? (YAGNI)
2. Does it already exist in this codebase? Reuse the helper, util, or pattern that's already here, don't re-write it.
3. Does the standard library already do this? Use it.
4. Does a native platform feature cover it? Use it.
5. Does an already-installed dependency solve it? Use it.
6. Can this be one line? Make it one line.
7. Only then: write the minimum code that works.
The ladder runs after you understand the problem, not instead of it: read the task and the code it touches, trace the real flow end to end, then climb.
Bug fix = root cause, not symptom: a report names a symptom. Grep every caller of the function you touch and fix the shared function once — one guard there is a smaller diff than one per caller, and patching only the path the ticket names leaves a sibling caller still broken.
Rules:
- No abstractions that weren't explicitly requested.
- No new dependency if it can be avoided.
- No boilerplate nobody asked for.
- Deletion over addition. Boring over clever. Fewest files possible.
- Shortest working diff wins, but only once you understand the problem. The smallest change in the wrong place isn't lazy, it's a second bug.
- Question complex requests: "Do you actually need X, or does Y cover it?"
- Pick the edge-case-correct option when two stdlib approaches are the same size, lazy means less code, not the flimsier algorithm.
- Mark intentional simplifications with a `ponytail:` comment. If the shortcut has a known ceiling (global lock, O(n²) scan, naive heuristic), the comment names the ceiling and the upgrade path.
Not lazy about: understanding the problem (read it fully and trace the real flow before picking a rung, a small diff you don't understand is just laziness dressed up as efficiency), input validation at trust boundaries, error handling that prevents data loss, security, accessibility, the calibration real hardware needs (the platform is never the spec ideal, a clock drifts, a sensor reads off), anything explicitly requested. Lazy code without its check is unfinished: non-trivial logic leaves ONE runnable check behind, the smallest thing that fails if the logic breaks (an assert-based demo/self-check or one small test file; no frameworks, no fixtures). Trivial one-liners need no test.
(Yes, this file also applies to agents working on the ponytail repo itself. Especially to them.)

36
CHANGELOG.md Normal file
View File

@ -0,0 +1,36 @@
# Change log UAT
## [2.12] - 06-05-2026
- **Pinpad Sound Toggle**
- Beep is ON by default
- Can Toggle PinPad sound on Side Navigation ( Under SystemManagement Tab)
- **Transaction Search**
- Search now matches by both Trace No and RRN instead of Trace No only
- Trace No uses partial match (`contains`), RRN uses exact match (`equals`)
## [2.31] - 22-05-2026
- **Control Bar Disabled**
- Control Bar disabled in normal status and could not be down to go settings
- But can auto enable when password is correct in function tab
- **Void Fixed**
- Void transaction fixed
## [2.34] - 29-05-2026
- **General**
- Settlement fix for qr records missing after Card settlement run
- fixed receipt printing loop for auto settlement
-
- **MMQR Refund**
- new auto-retry when MMQR status is "REFUNDING" in response
- no more fail records on refund list if "DUPLICATED" response
- removed "CANCEL" records in MMQR History
-
- **CARD Payments**
- signature saved in pos device
- signature re-show on re-printing receipt
- signature re-show on void
- signature re-show on refund
- pre-auth, pre-auth can, pre-auth com s' manual entry flow are changed
- can show/hide manual entry in all transactions

View File

@ -14,32 +14,48 @@ android {
applicationId "com.utsmm.kbz"
minSdk 24
targetSdk 33
versionCode 5
versionName "1.05"
versionCode 47
versionName "2.38"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// // Add CMake configuration
// externalNativeBuild {
// cmake {
// cppFlags "-std=c++14"
// }
// }
}
// Configure CMake
// externalNativeBuild {
// cmake {
// path "src/main/cpp/CMakeLists.txt"
// version "3.22.1"
// }
// }
// -----------------------------
// 🔥 MULTI-VARIANT FLAVORS
// -----------------------------
flavorDimensions "env"
productFlavors {
sit {
dimension "env"
applicationId "com.utsmm.kbz.sit"
versionNameSuffix "-sit"
resValue "string", "app_name", "KBZ-SIT"
}
uat {
dimension "env"
applicationId "com.utsmm.kbz.uat"
versionNameSuffix "-uat"
resValue "string", "app_name", "KBZ-UAT"
}
prod {
dimension "env"
applicationId "com.utsmm.kbz"
versionNameSuffix ""
resValue "string", "app_name", "KBZ-POS"
}
}
buildTypes {
release {
minifyEnabled false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
@ -57,6 +73,11 @@ android {
buildConfig = true
}
lint {
abortOnError false
checkReleaseBuilds false
}
packagingOptions {
pickFirst '**/libnative-lib.so'
pickFirst '**/libxgd_ddi_jni.so'
@ -84,13 +105,17 @@ kapt {
arg("room.incremental", "true")
arg("room.expandProjection", "true")
}
// Exclude ECR integration classes from kapt processing
javacOptions {
option("-Xmaxerrs", 500)
}
}
dependencies {
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
def nav_version = "2.3.2"
def lottieVersion = "3.5.0"
def fragment_version = "1.2.0"
@ -161,8 +186,8 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation "com.github.akarnokd:rxjava3-retrofit-adapter:3.0.0"
// ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel:2.2.0'
// ViewModel with Kotlin extensions
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
// LiveData
implementation 'androidx.lifecycle:lifecycle-livedata:2.2.0'
@ -177,18 +202,29 @@ dependencies {
// implementation 'com.sunmi:sunmiui:1.1.27' //1.1.27
implementation project(path: ':ecr-service-lib')
// ECR Dependencies - Modern ECR Client Library
// implementation project(path: ':ecr-client-lib')
// implementation project(path: ':ecr-service-lib')
// Core Libraries
implementation project(path: ':paylibs')
implementation project(path: ':mpulib')
implementation project(path: ':baselib')
implementation project(path: ':paysdk-lib')
// implementation project(path: ':paysdk-lib')
implementation project(path: ':nexsdk-lib')
implementation project(path: ':qrgen-lib')
implementation project(path: ':xpay')
// implementation project(path: ':sunmiui-lib')
implementation project(path: ':qrgen-lib')
//// implementation project(path: ':samlSirius')
// Legacy ECR (kept for backward compatibility during migration)
implementation project(path: ':ecr')
implementation project(path: ':nexdlkey-lib')
// Coroutines for modern ECR client
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' // 1.1.2
@ -196,15 +232,16 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'
androidTestImplementation 'androidx.test:core:1.5.0'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
// Add Mockito dependency for mocking
testImplementation 'org.mockito:mockito-core:3.12.4'
androidTestImplementation 'org.mockito:mockito-android:3.12.4'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'
testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
//time picker
implementation "com.google.android.material:material:1.11.0"
}

245
app/proguard-rules.pro vendored
View File

@ -20,7 +20,250 @@
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-keepattributes Signature, *Annotation*, SourceFile, LineNumberTable, InnerClasses, EnclosingMethod
-dontwarn sun.misc.**
# Keep Bouncy Castle classes for security providers
# Hilt ProGuard Rules - CRITICAL FOR DEPENDENCY INJECTION
-dontwarn dagger.hilt.**
-keep class dagger.hilt.** { *; }
-keep class * extends dagger.hilt.** { *; }
# Keep all Hilt generated classes
-keep class **_HiltComponents { *; }
-keep class **_HiltComponents$* { *; }
-keep class **_HiltModules { *; }
-keep class **_HiltModules$* { *; }
-keep class * extends dagger.hilt.android.internal.managers.ApplicationComponentManager { *; }
-keep class * extends dagger.hilt.android.internal.managers.ActivityComponentManager { *; }
-keep class * extends dagger.hilt.android.internal.managers.FragmentComponentManager { *; }
-keep class * extends dagger.hilt.android.internal.managers.ViewComponentManager { *; }
-keep class * extends dagger.hilt.android.internal.managers.ServiceComponentManager { *; }
# Keep Hilt entry points and components
-keep @dagger.hilt.InstallIn class * { *; }
-keep @dagger.hilt.android.AndroidEntryPoint class * { *; }
-keep @dagger.hilt.android.HiltAndroidApp class * { *; }
# Keep classes annotated with @Module, @Component, @Subcomponent
-keep @dagger.Module class * { *; }
-keep @dagger.Component class * { *; }
-keep @dagger.Component.Builder class * { *; }
-keep @dagger.Subcomponent class * { *; }
-keep @dagger.Subcomponent.Builder class * { *; }
# Keep all classes that have @Inject constructors, fields, or methods
-keepclasseswithmembers class * {
@javax.inject.Inject <init>(...);
}
-keepclasseswithmembers class * {
@javax.inject.Inject <fields>;
}
-keepclasseswithmembers class * {
@javax.inject.Inject <methods>;
}
# Keep Application class and its generated Hilt class
-keep class com.utsmm.kbz.MyApplication { *; }
-keep class com.utsmm.kbz.Hilt_MyApplication { *; }
-keep class com.utsmyanmar.baselib.BaseApplication { *; }
# Keep main activity and its generated classes
-keep class com.utsmm.kbz.MainActivity { *; }
# ViewModels and Repository classes (important for injection)
-keep class * extends androidx.lifecycle.ViewModel { *; }
-keep class com.utsmyanmar.baselib.repo.Repository { *; }
-keep class com.utsmyanmar.baselib.emv.EmvParamOperation { *; }
-keep class com.utsmyanmar.baselib.emv.TerminalParamOperation { *; }
# Keep all Dagger generated classes - these are critical!
-keep class **_Factory { *; }
-keep class **_MembersInjector { *; }
-keep class **_Provide* { *; }
-keep class com.utsmyanmar.baselib.network.model.* { <fields>; }
-keep class com.utsmyanmar.baselib.network.model.sirius.* { <fields>; }
-keep class com.utsmyanmar.baselib.db.model.* { <fields>; }
-keep class com.utsmyanmar.ecr.data.* { <fields>; }
-keep class com.utsmyanmar.ecr.data.model.* { <fields>; }
-keep class com.utsmyanmar.mpulib.data.model.* { <fields>; }
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# this is start
-keep class org.bouncycastle.** { *; }
-keep interface org.bouncycastle.** { *; }
-keep class okhttp3.internal.platform.BouncyCastlePlatform { *; }
# Keep classes from the JNDI package
-keep class javax.naming.** { *; }
-keep class javax.naming.directory.** { *; }
# Keep Bouncy Castle classes
-keep class org.bouncycastle.** { *; }
# Keep Conscrypt classes
-keep class org.conscrypt.** { *; }
# Keep OpenJSSE classes
-keep class org.openjsse.** { *; }
# Keep classes from okhttp3.internal.platform.BouncyCastlePlatform
-keep class okhttp3.internal.platform.BouncyCastlePlatform { *; }
# Keep classes from okhttp3.internal.platform.ConscryptPlatform
-keep class okhttp3.internal.platform.ConscryptPlatform$Companion { *; }
#-keep class okhttp3.internal.platform.ConscryptPlatform$platformTrustManager$2 { *; }
# Keep classes from okhttp3.internal.platform.OpenJSSEPlatform
-keep class okhttp3.internal.platform.OpenJSSEPlatform { *; }
#this is end
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
-keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
# Jun9 2022
#-keep public class * implements com.bumptech.glide.module.GlideModule
#-keep class * extends com.bumptech.glide.module.AppGlideModule {
# <init>(...);
#}
#-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
# **[] $VALUES;
# public *;
#}
#-assumenosideeffects class android.util.Log {
#public static int d(...);
#public static int v(...);
#public static int i(...);
#public static int w(...);
#public static int wtf(...);
# }
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**
# RxJava 3 Rules - Critical for Observable chains
-keep class io.reactivex.rxjava3.** { *; }
-dontwarn io.reactivex.rxjava3.**
-keep class io.reactivex.rxjava3.core.** { *; }
-keep class io.reactivex.rxjava3.android.** { *; }
-keep class io.reactivex.rxjava3.schedulers.** { *; }
# Retrofit and OkHttp rules
-keepattributes RuntimeVisibleAnnotations
-keep class retrofit2.** { *; }
-keepclassmembernames interface * {
@retrofit2.http.* <methods>;
}
# AndroidX and Lifecycle components
-keep class androidx.lifecycle.** { *; }
-keep class * extends androidx.lifecycle.ViewModel { *; }
-keep class androidx.activity.** { *; }
-keep class androidx.fragment.** { *; }
# Navigation component rules
-keep class androidx.navigation.** { *; }
-keep class * implements androidx.navigation.NavDirections { *; }
# DataBinding rules
-keep class androidx.databinding.** { *; }
-keep class * extends androidx.databinding.ViewDataBinding { *; }
# Room database rules (if using Room)
-keep class androidx.room.** { *; }
-keep class * extends androidx.room.RoomDatabase { *; }
# PayLibs and device-specific rules
-keep class com.utsmyanmar.paylibs.** { *; }
-keep class com.nexgo.** { *; }
-keep class com.sunmi.** { *; }
-keep class com.utsmyanmar.checkxread.** { *; }
-keep class com.utsmyanmar.ecr.** { *; }
-keep class com.utsmyanmar.mpulib.** { *; }
-keep class com.utsmyanmar.baselib.** { *; }
# Enum preservation for critical enums
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Keep model classes from obfuscation completely
-keep class com.utsmyanmar.paylibs.model.** { *; }
-keep class com.utsmyanmar.baselib.db.model.** { *; }
# Critical: Keep all native methods and JNI classes
-keepclasseswithmembernames class * {
native <methods>;
}
# Keep critical system params and utils
-keep class com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation { *; }
-keep class com.utsmyanmar.paylibs.utils.** { *; }
# Prevent obfuscation of classes used in reflection
-keep class * extends java.lang.Exception
-keep class * extends java.lang.Error
# CRITICAL: Keep all generated application components from obfuscation
-keep class com.utsmm.kbz.MyApplication_GeneratedInjector { *; }
#-keep class com.utsmm.kbz.MyApplication_HiltComponents$SingletonC { *; }
#-keep class com.utsmm.kbz.MyApplication_HiltComponents { *; }
# Keep all classes with "Generated" in their name (Hilt generates these)
-keep class **_Generated { *; }
-keep class **GeneratedInjector { *; }
# Additional protection for the specific classes causing crashes
-keep class **.D0.h { *; }
-keep class **.m2.f { *; }
-keep class **.J3.b { *; }
# Keep everything in the di package to prevent injection failures
-keep class com.utsmyanmar.baselib.di.** { *; }
-keep class com.utsmm.kbz.di.** { *; }
# Hilt Extension points
#-keep class * extends dagger.hilt.android.internal.managers.ApplicationComponentManager$ApplicationComponentBuilder { *; }
# Don't obfuscate any annotation processing related classes
-keep class dagger.internal.** { *; }
-keepnames class dagger.internal.** { *; }
# Keep annotation-based bindings
-keep @javax.inject.Inject class * { *; }
-keep @dagger.Provides class * { *; }
-keep @dagger.Module class * { *; }
# Keep Nexgo SDK
-keep class com.nexgo.** { *; }
# Keep DDI interface (VERY IMPORTANT)
-keep class com.xinguodu.ddiinterface.** { *; }
# Keep printer implementations
-keep class com.nexgo.oaf.apiv3.device.printer.** { *; }
# Prevent method shrinking/optimization
-dontoptimize
-dontshrink
-dontwarn com.xgd.smartpos.manager.app.UsageInfo
-dontwarn com.xgd.smartpos.manager.app.UsageStats
-dontwarn javax.lang.model.element.Modifier

BIN
app/release/app-release.apk Normal file

Binary file not shown.

View File

@ -4,15 +4,15 @@
"type": "APK",
"kind": "Directory"
},
"applicationId": "com.utsmm.kbz",
"applicationId": "com.utsmm.kbz.sit",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 5,
"versionName": "1.05",
"versionCode": 1,
"versionName": "1.0",
"outputFile": "app-release.apk"
}
],

Binary file not shown.

Binary file not shown.

View File

@ -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
}

View File

@ -10,6 +10,32 @@ import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import com.utsmm.kbz.util.MockData;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmyanmar.checkxread.model.CardDataX;
import com.utsmyanmar.checkxread.util.CardTypeX;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.isobuilder.ISOMode;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion;
import com.utsmyanmar.paylibs.model.MsgField;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.model.enums.TransCVM;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.MessageType;
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.BaseCardType;
import com.utsmyanmar.paylibs.utils.enums.HostName;
import com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.params.Params;
import java.util.Locale;
import java.util.Map;
/**
* Instrumented test, which will execute on an Android device.
*
@ -23,4 +49,185 @@ public class ExampleInstrumentedTest {
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.utsmm.kbz", appContext.getPackageName());
}
@Test
public void testVoidPacket() {
CardDataX cardDataX = MockData.getInstance().generateMPUCard();
TradeData tradeData = TransactionUtil.getInstance().initMPUTransaction(cardDataX, CardTypeX.IC);
String bitmap = BitmapConfig.MPU_NEW_VOID;
MessageType messageType = MessageType.FINANCIAL;
ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1987, ISOMode.ONLY_HEADER, HostName.FINEXUS)
.build();
TradeData transTradeData = setUpRebuildTransactions(tradeData, TransactionsType.VOID, HostName.FINEXUS);
transTradeData.getPayDetail().setAmount(50000);
PayDetail transPayDetail = transTradeData.getPayDetail();
byte[] sendBytes;
try {
sendBytes = isoMsgX.buildISOPackets(transTradeData, bitmap, messageType);
System.out.println("Hex Str : "+ ByteUtil.bytes2HexStr(sendBytes));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testSalePacket() {
CardDataX cardDataX = MockData.getInstance().generateMPUCard();
TradeData tradeData = TransactionUtil.getInstance().initEMVTransaction(cardDataX, CardTypeX.IC);
PayDetail payDetail = tradeData.getPayDetail();
payDetail.setProcessCode("000000");
payDetail.setTransactionType(TransactionType.SALE);
String bitmap = BitmapConfig.BPC_SALE;
MessageType messageType = MessageType.FINANCIAL;
ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.BOTH_HEADER_TPDU, HostName.BPC)
.build();
byte[] sendBytes;
try {
sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, messageType);
System.out.println("Hex Str : "+ ByteUtil.bytes2HexStr(sendBytes));
} catch (Exception e) {
e.printStackTrace();
}
}
@Test
public void testResponseSalePacket() {
String resp = "0071600000051730323130723000000A80820031363437363137333130303030303030323730303030303030303030303030353530303030313039313634353532303030303033323630313039313731353532303031303030303735343431393130303030343733343531303400048A023936";
resp = "00DB600000051730323130723000000A80820031363335373130353030333938393032373030303030303030303030303030353030303030313230313633323039303030303133323630313230313633323033303031303030303832333632393130383132333037313431303401105F2A02010482021C008407A00000006510109505808800E0009A032601209C01009F02060000000500009F03060000000000009F100807010104A09000B09F1A0201049F26080611D1D33562865F9F2701809F3303E0E8C89F34031E03009F360200049F37045A647F128A023936";
byte[] response = ByteUtil.hexStr2Bytes(resp);
String bitmap = BitmapConfig.BPC_SALE;
MessageType messageType = MessageType.FINANCIAL;
ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(ISOVersion.VERSION_1993, ISOMode.BOTH_HEADER_TPDU, HostName.BPC)
.build();
Map<String, MsgField> responseMap = isoMsgX.parseISOPackets(response,response.length);
System.out.println("Resp Str : "+ responseMap);
// try {
// responseMap = isoMsgX.parseISOPackets(response,response.length);
//
// System.out.println("Resp Str : "+ responseMap);
// } catch (Exception e) {
// e.printStackTrace();
// }
}
private TradeData setUpRebuildTransactions(TradeData tradeData, TransactionsType transactionsType, HostName hostName) {
LogUtil.d(Constant.TAG, "Starting Online Transaction--" + hostName + "--" + transactionsType);
TradeData newTrade = Params.newTrade(true);
PayDetail newPay = newTrade.getPayDetail();
PayDetail oldPay = tradeData.getPayDetail();
newPay.setCardType(100);
newPay.setPINCipher("");
newPay.setHostName(hostName.name);
newPay.setTransType(transactionsType.name);
newPay.setProcessCode(transactionsType.processCode);
newPay.setCardNo(oldPay.getCardNo());
newPay.setCardHolderName(oldPay.getCardHolderName());
newPay.setEXPDate(oldPay.getEXPDate());
newPay.setTradeDate(oldPay.getTradeDate());
newPay.setTradeTime(oldPay.getTradeTime());
newPay.setAmount(oldPay.getAmount());
newPay.setTransactionType(transactionsType.value);
newPay.setAccountType(oldPay.getAccountType());
newPay.setCardInfo(oldPay.getCardInfo());
newPay.setSettlementEnabled(SystemParamsOperation.getInstance().getSettlementStatus());
// added on Nov, 13 2024
newPay.setICC55(oldPay.getICC55());
if(newPay.getICC55() != null && !newPay.getICC55().isEmpty()) {
newPay.setAppLabel(oldPay.getAppLabel());
newPay.setAppName(oldPay.getAppName());
newPay.setTSI(oldPay.getTSI());
newPay.setAID(oldPay.getAID());
newPay.setArqC(oldPay.getArqC());
newPay.setTVR(oldPay.getTVR());
}
if(transactionsType != TransactionsType.PRE_AUTH_COMPLETE) {
newPay.setTradeDateTime(oldPay.getTradeDateTime());
}
if (transactionsType == TransactionsType.VOID || transactionsType == TransactionsType.REFUND) {
String field60;
if(hostName == HostName.BPC) {
field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount());
} else {
field60 = String.format(Locale.getDefault(), "%012d", oldPay.getAmount());
}
// String field60 = String.format(Locale.getDefault(), "%010d00", oldPay.getAmount());
newPay.setReferNo(oldPay.getReferNo());
newPay.setTransCVM(TransCVM.SIGNATURE);
newTrade.setField60(field60);
} else if (transactionsType == TransactionsType.PRE_AUTH_VOID) {
newPay.setCardType(oldPay.getCardType());
newPay.setCustomOrderNo(oldPay.getVoucherNo());
newPay.setReferNo(oldPay.getReferNo());
newPay.setCardInfo(oldPay.getCardInfo());
newPay.setPINCipher(oldPay.getPINCipher());
newPay.setTempKSN(oldPay.getTempKSN());
newPay.setTransCVM(TransCVM.SIGNATURE);
// for manual entry reversal which need de 35
} else if (transactionsType == TransactionsType.TIP_ADJUSTMENT ) {
newPay.setReferNo(oldPay.getReferNo());
newPay.setApprovalCode(oldPay.getApprovalCode());
} else if (transactionsType == TransactionsType.PRE_AUTH_COMPLETE) {
newPay.setCardType(oldPay.getCardType());
newPay.setCardInfo(oldPay.getCardInfo());
newPay.setPINCipher(oldPay.getPINCipher());
newPay.setReferNo(oldPay.getReferNo());
newPay.setTempKSN(oldPay.getTempKSN());
} else if (transactionsType == TransactionsType.PRE_AUTH_COMPLETE_VOID) {
newPay.setVoucherNo(oldPay.getVoucherNo());
newPay.setCustomOrderNo(oldPay.getVoucherNo());
newPay.setReferNo(oldPay.getReferNo());
newPay.setCardInfo(oldPay.getCardInfo());
/*
* new requirements */
newPay.setCardType(BaseCardType.IC.getValue());
newPay.setPINCipher("55");
newPay.setTransCVM(TransCVM.SIGNATURE);
}
if(oldPay.getAccountType().equals("MPU")){
// newPay.setIsFreeSign(true);
newPay.setTransCVM(TransCVM.SIGNATURE);
}
newTrade.setPayDetail(newPay);
return newTrade;
}
}

View File

@ -9,7 +9,8 @@
<uses-feature
android:name="android.hardware.telephony"
android:required="false" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -27,6 +28,18 @@
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- Permission for exact alarms on Android 12+ -->
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<!-- Permissions for background auto settlement -->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<!-- android:noHistory="true"-->
<!-- android:excludeFromRecents="true"-->
@ -46,30 +59,32 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar"
>
android:theme="@style/AppTheme.NoActionBar">
<!-- android:manageSpaceActivity="com.utsmyanmar.upos.config.UTSManageSpaceActivity"-->
<!-- <activity-->
<!-- android:screenOrientation="portrait"-->
<!-- android:name=".config.UTSManageSpaceActivity"/>-->
<activity android:name="com.utsmm.kbz.MainActivity"
android:exported="true">
android:exported="true"
android:showWhenLocked="true"
android:turnScreenOn="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.journeyapps.barcodescanner.CaptureActivity"
android:screenOrientation="fullSensor"
tools:replace="screenOrientation" />
<receiver android:name="com.utsmm.kbz.service.SmileAlarmReceiver" />
<receiver android:name="com.utsmm.kbz.service.AutoAlarmReceiver" />
<service
android:name="com.utsmm.kbz.service.SmileSettleService"
android:name="com.utsmm.kbz.service.AutoSettleService"
android:enabled="true"
android:exported="false">
<meta-data

Binary file not shown.

View File

@ -4,6 +4,7 @@ import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
@ -16,23 +17,40 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.Toast;
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.navigation.NavigationView;
import com.nexgo.downloadkey.downloadflow.DownloadFEntity;
import com.nexgo.downloadkey.downloadflow.DownloadFlow;
import com.nexgo.downloadkey.downloadflow.DownloadFlowProcessListener;
import com.nexgo.downloadkey.downloadflow.DownloadFlowResultEntity;
import com.nexgo.downloadkey.downloadflow.DownloadResult;
import com.nexgo.oaf.apiv3.device.pinpad.PinPad;
import com.nexgo.oaf.apiv3.device.pinpad.WorkKeyTypeEnum;
import com.utsmm.kbz.util.LocationUtil;
import com.utsmyanmar.baselib.BaseApplication;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.baselib.ui.AnimationDialog;
import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.ecr.ECRHelper;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.SettleData;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsSettings;
import com.utsmyanmar.paylibs.utils.enums.TransMenu;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.databinding.ActivityMainNewBinding;
import com.utsmm.kbz.service.SmileAlarmReceiver;
import com.utsmm.kbz.service.AutoAlarmReceiver;
import com.utsmm.kbz.ui.core_ui.InputPasswordFragment;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.navigation.NaviAdminFragment;
@ -40,14 +58,12 @@ import com.utsmm.kbz.ui.navigation.NaviMainFragment;
import com.utsmm.kbz.ui.navigation.NaviSetupFragment;
import com.utsmm.kbz.util.ecr.CoreUtils;
import com.utsmm.kbz.util.tms.TMSUtil;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
@ -56,6 +72,7 @@ import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.params.Params;
/**
* Main Activity handling application navigation and fragment interactions
*/
@ -95,6 +112,9 @@ public class MainActivity extends AppCompatActivity implements
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
// Keep screen on
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
@ -124,10 +144,41 @@ public class MainActivity extends AppCompatActivity implements
// Initialize special back handling fragments
initSpecialBackHandlingFragments();
final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
@Override
public void run() {
initKey();
}
}, 500);
}
private void initKey(){
// SystemParamsOperation.getInstance().setTmsAddress("https://api-tms.kbzbank.com/sirius");
// SystemParamsOperation.getInstance().setEReceiptAddress("https://api-tms.kbzbank.com/receipt");
// SystemParamsOperation.getInstance().setTmsAddress("https://api-tms-uat.kbzbank.com:8443/sirius");
// SystemParamsOperation.getInstance().setEReceiptAddress("https://api-tms-uat.kbzbank.com:8443/receipt");
SystemParamsOperation.getInstance().setTmsAddress("https://sirius-nest.utsmyanmar.com/api/v1");
SystemParamsOperation.getInstance().setEReceiptAddress("https://receipt-nest.utsmyanmar.com");
// SystemParamsOperation.getInstance().setEReceiptAddress("https://receipt-nest.utsmyanmar.com");
PinPad pinPad = MyApplication.getInstance().deviceEngine.getPinPad();
byte[] encryptedPIK = SystemParamsOperation.getInstance().getPIK();
if (encryptedPIK != null && encryptedPIK.length != 0) {
int result = pinPad.writeWKey(9, WorkKeyTypeEnum.PINKEY, encryptedPIK, encryptedPIK.length);
LogUtil.d(TAG, "save PIK key result: "+result);
} else {
LogUtil.d(TAG, "There's no PIK key!");
}
}
private void initViewModels() {
sharedViewModel = new ViewModelProvider(this).get(SharedViewModel.class);
sharedViewModel.getToastMsg().observe(this, this::showToast);
}
private void setupUI() {
@ -240,7 +291,7 @@ public class MainActivity extends AppCompatActivity implements
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this,
0,
new Intent(this, SmileAlarmReceiver.class),
new Intent(this, AutoAlarmReceiver.class),
PendingIntent.FLAG_UPDATE_CURRENT
);
@ -379,23 +430,148 @@ public class MainActivity extends AppCompatActivity implements
@Override
public void onResume() {
super.onResume();
getLocation();
SystemParamsOperation.getInstance().setSetupEcr(false);
SystemParamsOperation.getInstance().setDownloadedParams(false);
handleAutoSettlementIntent(getIntent());
// BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
}
@Override
public void onStop(){
super.onStop();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleAutoSettlementIntent(intent);
}
public void getLocation() {
LocationUtil.getCurrentLocation(MainActivity.this, new LocationUtil.LocationCallback() {
@Override
public void onSuccess(double lat, double lng) {
LogUtil.d("Location", "Current Location: " + lat + ", " + lng);
LocationUtil.setLat(lat);
LocationUtil.setLng(lng);
}
@Override
public void onError(String reason) {
LogUtil.e("Location", "get Location error: " + reason);
LocationUtil.setLat(0.00);
LocationUtil.setLng(0.00);
}
});
}
private void handleAutoSettlementIntent(Intent intent) {
if (intent == null) return;
boolean hasPOS = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
boolean hasQR = intent.getBooleanExtra("AUTO_QR_SETTLEMENT", false);
if (!hasPOS && !hasQR) return;
// Clear immediately so onResume won't re-trigger on next wake
intent.removeExtra("AUTO_SETTLEMENT");
intent.removeExtra("AUTO_QR_SETTLEMENT");
if (hasPOS) {
PayDetail payDetail = (PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL");
if (payDetail != null) {
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(TransactionsType.SETTLEMENT);
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountSeparatorFormat(payDetail.getAmount()));
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Navigation error: " + e.getMessage());
}
// If QR also exists, trigger it AFTER POS result is done
if (hasQR) {
navigateToQRSettlementAfterPOS(intent);
}
} else {
// POS had no data, go straight to QR if available
if (hasQR) {
handleQRSettlement(intent);
}
}
} else if (hasQR) {
// Only QR settlement, no POS
handleQRSettlement(intent);
}
}
private void navigateToQRSettlementAfterPOS(Intent intent) {
// Observe transactionResultFragment's completion via ViewModel
// When POS result screen signals "done" (print complete / user dismissed),
// then navigate to QR result
sharedViewModel.onSettlementResultDone.observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean done) {
if (done != null && done) {
// Remove observer so it only fires once
sharedViewModel.onSettlementResultDone.removeObserver(this);
// Reset flag
sharedViewModel.onSettlementResultDone.postValue(false);
// Now navigate to QR
handleQRSettlement(intent);
}
}
});
}
private void handleQRSettlement(Intent intent) {
int qrSaleCount = intent.getIntExtra("QR_SALE_COUNT", 0);
long qrSaleAmount = intent.getLongExtra("QR_SALE_AMOUNT", 0L);
int qrRefundCount = intent.getIntExtra("QR_REFUND_COUNT", 0);
long qrRefundAmount = intent.getLongExtra("QR_REFUND_AMOUNT", 0L);
long totalAmount = intent.getLongExtra("QR_TOTAL_AMOUNT", 0L);
List<PayDetail> qrTransList = (List<PayDetail>) intent.getSerializableExtra("QR_TRANS_LIST");
SettleData settleData = new SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
TradeData tradeData = Params.newTrade(false);
PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountSeparatorFormat(totalAmount));
sharedViewModel.payDetails.setValue(qrTransList);
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(TransactionsType.MMQR_SETTLEMENT);
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "QR Auto Settlement navigation error: " + e.getMessage());
}
}
@Override
public void onDestroy() {
super.onDestroy();
// Clean up RxJava disposables
compositeDisposable.clear();
// Reset screen mode
// Disconnect ECR
ECRHelper.INSTANCE.disconnect();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
}
// Navigation drawer interaction listeners
@ -406,6 +582,12 @@ public class MainActivity extends AppCompatActivity implements
navController.navigate(R.id.inputPasswordFragment);
}
@Override
public void onPause(){
super.onPause();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
}
@Override
public void onClickFunction() {
closeDrawer();

File diff suppressed because it is too large Load Diff

View File

@ -11,6 +11,7 @@ import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModel;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.utsmm.kbz.service.AutoSettleService;
import com.utsmyanmar.baselib.db.model.EmvDetail;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
@ -22,7 +23,6 @@ import com.utsmyanmar.paylibs.reversal.ReversalAction;
import com.utsmyanmar.paylibs.reversal.ReversalListener;
import com.utsmyanmar.paylibs.system.SingleLiveEvent;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmm.kbz.service.SmileSettleService;
import com.utsmm.kbz.util.enums.ReversalStatus;
import com.utsmm.kbz.util.ecr.ECRConnectionStatus;
@ -58,6 +58,9 @@ public class MainViewModel extends ViewModel {
public MutableLiveData<Boolean> kPayStatus = new MutableLiveData<>();
public MutableLiveData<Boolean> voidStatus = new MutableLiveData<>();
public SingleLiveEvent<String> disabledMsg = new SingleLiveEvent<>();
public SingleLiveEvent<List<PayDetail>> settlementPOS = new SingleLiveEvent<>();
@ -75,8 +78,8 @@ public class MainViewModel extends ViewModel {
private BroadcastReceiver dataReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(SmileSettleService.ACTION_DATA_RECEIVED)) {
String data = intent.getStringExtra(SmileSettleService.EXTRA_DATA);
if (intent.getAction().equals(AutoSettleService.ACTION_DATA_RECEIVED)) {
String data = intent.getStringExtra(AutoSettleService.EXTRA_DATA);
}
}
@ -90,13 +93,15 @@ public class MainViewModel extends ViewModel {
playAtOnce.setValue(false);
LocalBroadcastManager.getInstance(context).registerReceiver(dataReceiver,
new IntentFilter(SmileSettleService.ACTION_DATA_RECEIVED));
new IntentFilter(AutoSettleService.ACTION_DATA_RECEIVED));
settlementStatus.setValue(SystemParamsOperation.getInstance().getSettlementStatus());
kPayStatus.setValue(SystemParamsOperation.getInstance().getWavePayStatus());
kPayStatus.setValue(SystemParamsOperation.getInstance().getQRPayStatus());
disabledMsg.setValue(SystemParamsOperation.getInstance().getDisabledMsg());
voidStatus.setValue(SystemParamsOperation.getInstance().getVoidStatus());
}
@Override
@ -123,8 +128,8 @@ public class MainViewModel extends ViewModel {
public LiveData<List<PayDetail>> getDeleteTrans(String batchNo) {
return repository.getDeleteTrans(batchNo);
public LiveData<List<PayDetail>> getDeleteTrans() {
return repository.getDeleteTrans();
}
private LiveData<List<PayDetail>> getPreAuthTrans() {
@ -166,7 +171,7 @@ public class MainViewModel extends ViewModel {
settlementPOS.postValue(payDetails);
}
});
getDeleteTrans(SystemParamsOperation.getInstance().getCurrentBatchNum()).observeForever(new Observer<List<PayDetail>>() {
getDeleteTrans().observeForever(new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
deleteTrans.postValue(payDetails);

View File

@ -5,5 +5,6 @@ public enum CardTransactionType {
MPU,
EMV,
MAG,
FALLBACK
FALLBACK,
MOCK
}

View File

@ -0,0 +1,156 @@
package com.utsmm.kbz.service;
import android.app.AlarmManager;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import androidx.core.app.NotificationCompat;
import com.utsmm.kbz.R;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import java.util.Calendar;
public class AutoAlarmReceiver extends BroadcastReceiver {
private static final String TAG = AutoAlarmReceiver.class.getSimpleName();
private static final String WAKE_LOCK_TAG = "KBZAutoSettle::AlarmWakeLock";
private static final String NOTIFICATION_CHANNEL_ID = "AUTO_SETTLEMENT_ALARM";
@Override
public void onReceive(Context context, Intent intent) {
LogUtil.d(TAG, "AutoAlarmReceiver triggered at: " + new java.util.Date().toString());
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
wakeLock.acquire(60000);
try {
showAlarmTriggeredNotification(context);
scheduleNextDayAlarm(context);
try {
Intent serviceIntent = new Intent(context, AutoSettleService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
context.startForegroundService(serviceIntent);
LogUtil.d(TAG, "AutoSettleService started as foreground service");
} else {
context.startService(serviceIntent);
LogUtil.d(TAG, "AutoSettleService started successfully");
}
} catch (Exception e) {
LogUtil.e(TAG, "Failed to start AutoSettleService: " + e.getMessage());
e.printStackTrace();
}
} finally {
// Release wake lock
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
}
private void scheduleNextDayAlarm(Context context) {
try {
String timeStr = SystemParamsOperation.getInstance().getClearBatchTime();
LogUtil.d(TAG, " Rescheduling next day's alarm for timeStr: " + timeStr);
if (timeStr == null || timeStr.trim().isEmpty()) {
LogUtil.d(TAG, "Clear batch time is empty, cannot reschedule");
return;
}
String[] parts = timeStr.trim().split(":");
if (parts.length != 2) {
LogUtil.d(TAG, "Invalid time format: " + timeStr);
return;
}
int hour = Integer.parseInt(parts[0]);
int minute = Integer.parseInt(parts[1]);
// Validate time range
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
LogUtil.d(TAG, "Invalid time values - hour: " + hour + ", minute: " + minute);
return;
}
// Set up next day's alarm
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 1); // Next day
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long nextTriggerTime = calendar.getTimeInMillis();
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager == null) {
LogUtil.e(TAG, "AlarmManager is null, cannot reschedule");
return;
}
Intent intent = new Intent(context, AutoAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
1001,
intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} else {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void showAlarmTriggeredNotification(Context context) {
try {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"Auto Settlement Alarms",
NotificationManager.IMPORTANCE_HIGH
);
channel.setDescription("Notifications for auto settlement alarm triggers");
notificationManager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Auto Settlement Triggered")
.setContentText("Auto settlement alarm triggered at " + new java.util.Date().toString())
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true);
notificationManager.notify(1001, builder.build());
} catch (Exception e) {
LogUtil.e(TAG, "Failed to show alarm notification: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,694 @@
package com.utsmm.kbz.service;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.google.gson.Gson;
import com.utsmm.kbz.MainActivity;
import com.utsmm.kbz.util.EReceiptUtil;
import com.utsmm.kbz.util.enums.TransactionStatus;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmm.kbz.BuildConfig;
import com.utsmm.kbz.R;
import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.schedulers.Schedulers;
import retrofit2.HttpException;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.batch_upload.BatchListener;
import com.utsmyanmar.paylibs.batch_upload.BatchUploadProcess;
import com.utsmyanmar.paylibs.isobuilder.ISOMode;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion;
import com.utsmyanmar.paylibs.model.CardSettleData;
import com.utsmyanmar.paylibs.model.MsgField;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.QRSettleData;
import com.utsmyanmar.paylibs.model.SettleData;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.network.ISOCallback;
import com.utsmyanmar.paylibs.network.ISOSocket;
import com.utsmyanmar.paylibs.sign_on.EchoTestProcess;
import com.utsmyanmar.paylibs.sign_on.SignOnListener;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.MessageType;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.HostName;
import com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.params.Params;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@AndroidEntryPoint
public class AutoSettleService extends Service {
private static final String TAG = AutoSettleService.class.getSimpleName();
public static final String ACTION_DATA_RECEIVED = BuildConfig.APPLICATION_ID + ".ACTION_DATA_RECEIVED";
public static final String EXTRA_DATA = BuildConfig.APPLICATION_ID + ".EXTRA_DATA";
private Handler handler;
private SettlementViewModel settlementViewModel;
// Flags to prevent infinite loops
private boolean regularSettlementCompleted = false;
private boolean qrSettlementCompleted = false;
private boolean posHasData = false; // true = POS actually ran and has result
private boolean qrHasData = false; // true = QR actually ran and has result
// Observers to keep reference for cleanup
private Observer<List<PayDetail>> regularSettlementObserver;
private Observer<List<PayDetail>> qrSettlementObserver;
private int i = 0;
private boolean flag = false;
@Inject
Repository repository;
private PayDetail pendingPayDetail = null;
private Bundle pendingQrData = null;
private boolean posSettlementReady = false;
private boolean qrSettlementReady = false;
ArrayList<PayDetail> payDetails = new ArrayList<>();
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
private void startForegroundService() {
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Create notification channel for Android 8+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"Auto Settlement Service",
NotificationManager.IMPORTANCE_LOW // Use LOW importance for foreground service
);
channel.setDescription("Auto settlement background service");
notificationManager.createNotificationChannel(channel);
}
// Create foreground notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Auto Settlement Active")
.setContentText("Processing scheduled settlement...")
.setPriority(NotificationCompat.PRIORITY_LOW)
.setOngoing(true) // Make it non-dismissible
.setAutoCancel(false);
// Start foreground service
startForeground(1002, builder.build());
}
private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager)getSystemService( NOTIFICATION_SERVICE ) ;
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext() , default_notification_channel_id ) ;
mBuilder.setContentTitle( "Auto Settlement Running" ) ;
mBuilder.setContentText( "Processing transactions..." ) ;
mBuilder.setTicker( "Auto Settlement Service" ) ;
mBuilder.setSmallIcon(R.drawable. ic_launcher_foreground ) ;
mBuilder.setAutoCancel( true ) ;
if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) {
int importance = NotificationManager. IMPORTANCE_HIGH ;
NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "AUTO_SETTLEMENT_CHANNEL" , importance) ;
mBuilder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel) ;
}
assert mNotificationManager != null;
mNotificationManager.notify(( int ) System. currentTimeMillis () , mBuilder.build()) ;
}
private void sendDataToViewModel(String data) {
Intent intent = new Intent(ACTION_DATA_RECEIVED);
intent.putExtra(EXTRA_DATA, data);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@SuppressLint("CheckResult")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.d(TAG, "<<<<<<<<<<<<<<< Auto Settlement Service Started >>>>>>>>>>>>>>>>.");
try {
// Start as foreground service for Android 8+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
startForegroundService();
}
sendDataToViewModel("Auto settlement initiated");
// Also create regular notification
createNotification();
if (SystemParamsOperation.getInstance().getSettlementStatus()) {
performSettlement();
// regularSettlementCompleted = true;
performQRSettlement();
} else {
LogUtil.d(TAG, "Settlement is disabled in system parameters");
stopSelf();
}
} catch (Exception e) {
LogUtil.e(TAG, "Error in onStartCommand: " + e.getMessage());
e.printStackTrace();
stopSelf();
}
return START_NOT_STICKY;
}
private void performSettlement() {
if (regularSettlementCompleted) {
return;
}
final ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(
ISOVersion.VERSION_1993,
ISOMode.BOTH_HEADER_TPDU,
HostName.BPC
).build();
regularSettlementObserver = new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> list) {
if (regularSettlementCompleted) {
return;
}
payDetails.clear();
payDetails.addAll(list);
regularSettlementCompleted = true;
repository.getSettlementPOS().removeObserver(this);
int saleCount = 0;
long saleAmount = 0L;
int preCount = 0;
long preAmount = 0L;
int refundCount = 0;
long refundAmount = 0L;
int caCount = 0;
long caAmount = 0L;
if (list != null && !list.isEmpty()) {
for (PayDetail pay : list) {
if (pay.getTransactionType() == TransactionType.SALE && !pay.isCanceled) {
saleCount++;
saleAmount += pay.getAmount();
} else if (pay.getTransactionType() == TransactionType.PRE_SALE_COMPLETE && !pay.isCanceled) {
preCount++;
preAmount += pay.getAmount();
} else if (pay.getTransactionType() == TransactionType.REFUND) {
refundCount++;
refundAmount += pay.getAmount();
} else if (pay.getTransactionType() == TransactionType.CASH_ADVANCE) {
caCount++;
caAmount += pay.getAmount();
}
}
}
TradeData tradeData = Params.newTrade(true);
PayDetail payDetail = tradeData.getPayDetail();
String bitmap = BitmapConfig.BPC_SETTLEMENT;
payDetail.setTransType(TransactionsType.SETTLEMENT.name);
payDetail.setTransactionType(TransactionType.SETTLEMENT);
if(!flag) {
payDetail.setProcessCode(TransactionsType.SETTLEMENT.processCode);
} else {
bitmap = BitmapConfig.BPC_SETTLEMENT_TRAILER;
payDetail.setProcessCode("910000");
}
payDetail.setBatchNo(SystemParamsOperation.getInstance().getCurrentBatchNum());
SettleData settleData = new SettleData(
saleCount, saleAmount, preCount, preAmount, refundCount, refundAmount, caCount, caAmount);
payDetail.setSettleDataObj(settleData);
payDetail.setCardSettleData(CardSettleData.convertFromPayDetail(list));
long totalAmount = saleAmount + preAmount + refundAmount + caAmount;
String settlementData;
if (refundAmount != 0L) {
long creditTotal = saleAmount + preAmount + caAmount;
long subTotal = creditTotal - refundAmount;
if (subTotal < 0L) {
settlementData = "D" + String.format(Locale.getDefault(), "%012d", Math.abs(subTotal));
} else {
settlementData = "C" + String.format(Locale.getDefault(), "%012d", subTotal);
}
} else {
settlementData = "C" + String.format(Locale.getDefault(), "%012d", totalAmount);
}
if(totalAmount == 0 && (saleCount == 0 || preCount == 0 || refundCount == 0 || caCount == 0)) {
posSettlementReady = true;
posHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
return;
}
payDetail.setSettleData(settlementData);
payDetail.setAmount(totalAmount);
tradeData.setPayDetail(payDetail);
tradeData.setField60(SystemParamsOperation.getInstance().getCurrentBatchNum());
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, MessageType.SETTLEMENT);
ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, new ISOCallback() {
@Override
public void onReceive(byte[] bytes, int length) {
Map<String, MsgField> responseMap = isoMsgX.parseISOPackets(bytes, length);
if (responseMap != null) {
String resultStr = "";
try {
resultStr = responseMap.get("F039").getDataStr();
} catch (NullPointerException e) {
e.printStackTrace();
payDetail.setIsNeedReversal(true);
return;
}
payDetail.setTradeAnswerCode(resultStr);
if (TextUtils.equals(resultStr, Constant.ANSWER_CODE_ACCEPT) || TextUtils.equals(resultStr, Constant.ANSWER_CODE_APPROVED)) {
payDetail.setIsNeedReversal(false);
} else if (TextUtils.equals(resultStr, "95") || TextUtils.equals(resultStr, "095")) {
payDetail.setIsNeedReversal(!flag);
}
}
}
@Override
public void onError(String msg) {
ISOSocket.getInstance().switchIp();
ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, this);
}
@Override
public void onComplete() {
LogUtil.d(TAG, "Auto settlement transaction completed successfully");
if(payDetail.getIsNeedReversal()) {
flag = true;
batchUploadProcess();
} else {
flag = false;
if (!list.isEmpty()) {
for (PayDetail p : list) {
repository.deletePayDetail(p);
}
}
repository.insertPayDetail(payDetail);
networkCutOver(payDetail);
}
}
});
}
};
repository.getSettlementPOS().observeForever(regularSettlementObserver);
}
private void batchUploadProcess() {
if(payDetails == null || payDetails.isEmpty()){
regularSettlementCompleted = false;
performSettlement();
return;
}
PayDetail payDetail = payDetails.get(i);
TradeData tradeData = new TradeData();
tradeData.setPayDetail(payDetail);
BatchUploadProcess.getInstance().enqueue(tradeData).startBatchUpload(new BatchListener() {
@Override
public void onSuccessBatch() {
if (i < payDetails.size() - 1) {
LogUtil.d(TAG, "Pay detail Size:" + payDetails.size());
LogUtil.d(TAG, "Count value:" + i);
i++;
batchUploadProcess();
} else {
regularSettlementCompleted = false;
performSettlement();
}
LogUtil.e(TAG, "Batch Upload Success");
}
@Override
public void onFailBatch() {
LogUtil.e(TAG, "Batch Upload Fail");
}
});
}
private void performQRSettlement() {
if (qrSettlementCompleted) {
return;
}
qrSettlementObserver = new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetailList) {
if (qrSettlementCompleted) {
return;
}
qrSettlementCompleted = true;
repository.getTransactionHistory().removeObserver(this);
if (payDetailList != null) {
int qrSaleCount = 0;
long qrSaleAmount = 0;
int qrRefundCount = 0;
long qrRefundAmount = 0;
ArrayList<PayDetail> qrTransactionsList = new ArrayList<>();
ArrayList<PayDetail> qrTransListAll = new ArrayList<>();
try {
for (PayDetail payDetail : payDetailList) {
if ((payDetail.getTransactionType() == TransactionsType.MMQR.value
|| payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value)
&& payDetail.getQrTransStatus() == 1) {
qrTransactionsList.add(payDetail);
if (payDetail.getTransactionType() == TransactionsType.MMQR.value) {
qrSaleCount++;
qrSaleAmount += payDetail.getAmount();
} else if (payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
qrRefundCount++;
qrRefundAmount += payDetail.getAmount();
}
} else if (payDetail.getTransactionType() == TransactionsType.MMQR.value
|| payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
qrTransListAll.add(payDetail);
}
}
} catch (IllegalStateException e) {
LogUtil.e(TAG, "QR Auto Settlement: Database cursor error - likely due to large data size: " + e);
qrSettlementReady = true;
qrHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
return;
}
if (!qrTransactionsList.isEmpty()) {
LogUtil.d(TAG, "Processing QR Auto Settlement with " + qrTransactionsList.size() + " transactions");
long totalAmount = qrSaleAmount - qrRefundAmount;
// Increment batch number for QR settlement
SystemParamsOperation.getInstance().getIncrementBatchNo();
// Create settlement data for QR transactions
SettleData settleData = new SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
TradeData tradeData = Params.newTrade(false);
PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
payDetail.setBatchNo(SystemParamsOperation.getInstance().getCurrentBatchNum());
payDetail.setQrSettleData(QRSettleData.convertFromPayDetail(qrTransactionsList));
// Insert QR settlement record
repository.insertPayDetail(payDetail);
// Delete settled QR transactions
for (PayDetail pay : qrTransactionsList) {
repository.deletePayDetail(pay);
}
// Delete all other QR transactions (failed ones)
for (PayDetail pay : qrTransListAll) {
repository.deletePayDetail(pay);
}
// Push e-receipt data using repository directly
try {
EReceiptRequest request = EReceiptUtil.getInstance().generateQRSettlement(payDetail, QRSettleData.convertFromPayDetail(qrTransactionsList));
sendEReceipt(request);
LogUtil.d(TAG, "QR Settlement e-receipt data prepared and sent");
} catch (Exception e) {
LogUtil.e(TAG, "Error preparing QR settlement e-receipt: " + e.getMessage());
}
try {
// Create QR settlement data bundle
Bundle qrData = new Bundle();
qrData.putInt("QR_SALE_COUNT", qrSaleCount);
qrData.putLong("QR_SALE_AMOUNT", qrSaleAmount);
qrData.putInt("QR_REFUND_COUNT", qrRefundCount);
qrData.putLong("QR_REFUND_AMOUNT", qrRefundAmount);
qrData.putLong("QR_TOTAL_AMOUNT", totalAmount);
qrData.putSerializable("QR_TRANS_LIST", qrTransactionsList);
// Use notification-based approach to reliably launch the app
pendingQrData = qrData;
qrHasData = true;
// launchAppWithSettlementResult("AUTO_QR_SETTLEMENT", null, qrData,false);
} catch (Exception e) {
LogUtil.e(TAG, "Error launching app for QR settlement: " + e.getMessage());
qrHasData = false;
e.printStackTrace();
}
LogUtil.d(TAG, "QR Auto Settlement completed successfully");
} else {
LogUtil.d(TAG, "No QR transactions found for auto settlement");
qrHasData = false;
}
}
qrSettlementReady = true;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
};
repository.getTransactionHistory().observeForever(qrSettlementObserver);
}
private void checkIfBothSettlementsComplete() {
if (regularSettlementCompleted && qrSettlementCompleted) {
LogUtil.d(TAG, "Both settlements completed, stopping service");
stopSelf();
}
}
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
// Clean up observers to prevent memory leaks
if (regularSettlementObserver != null && repository != null) {
repository.getSettlementPOS().removeObserver(regularSettlementObserver);
}
if (qrSettlementObserver != null && repository != null) {
repository.getTransactionHistory().removeObserver(qrSettlementObserver);
}
}
private void networkCutOver(PayDetail payDetail) {
EchoTestProcess.getInstance().enqueueCutOver().startSignOn(new SignOnListener() {
@Override
public void onSuccessSignOn() {
pendingPayDetail = payDetail;
posHasData = true;
posSettlementReady = true;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
@Override
public void onFailureSignOn(Integer resultCode) {
LogUtil.d(TAG,"Failure Cut Over message: "+resultCode);
// POS done but cutover failed — still mark ready
posSettlementReady = true;
posHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
@Override
public void onNetworkFailSignOn(String message) {
LogUtil.d(TAG,"Network Fail Cut Over message: "+message);
posSettlementReady = true;
posHasData = false;
checkAndLaunch();
checkIfBothSettlementsComplete();
}
});
}
private void checkAndLaunch() {
// !!!! need to add flag, if don't want both at once
if (!posSettlementReady || !qrSettlementReady) {
LogUtil.d(TAG, "Waiting for both settlements... POS=" + posSettlementReady + " QR=" + qrSettlementReady);
return;
}
LogUtil.d(TAG, "Both settlements ready. POS has data=" + posHasData + ", QR has data=" + qrHasData);
if (!posHasData && !qrHasData) {
LogUtil.d(TAG, "No settlement data to display");
return;
}
launchMainActivity();
}
private void launchMainActivity() {
try {
Intent launchIntent = new Intent(this, MainActivity.class);
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (posHasData && pendingPayDetail != null) {
launchIntent.putExtra("AUTO_SETTLEMENT", true);
launchIntent.putExtra("EXTRA_TRANSACTION_TYPE", TransactionsType.SETTLEMENT.value);
launchIntent.putExtra("EXTRA_PAY_DETAIL", pendingPayDetail);
}
if (qrHasData && pendingQrData != null) {
launchIntent.putExtra("AUTO_QR_SETTLEMENT", true);
launchIntent.putExtra("EXTRA_QR_TRANSACTION_TYPE", TransactionsType.MMQR_SETTLEMENT.value);
launchIntent.putExtras(pendingQrData);
}
PendingIntent pendingIntent = PendingIntent.getActivity(
this,
(int) System.currentTimeMillis(),
launchIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"SETTLEMENT_COMPLETE", "Settlement Complete", NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("Auto settlement completion notifications");
notificationManager.createNotificationChannel(channel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "SETTLEMENT_COMPLETE")
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle("Auto Settlement Complete")
.setContentText("Tap to view settlement results and print receipt")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setFullScreenIntent(pendingIntent, true);
notificationManager.notify(2001, builder.build());
// Reset state
pendingPayDetail = null;
pendingQrData = null;
posSettlementReady = false;
qrSettlementReady = false;
posHasData = false;
qrHasData = false;
startActivity(launchIntent);
} catch (Exception e) {
LogUtil.e(TAG, "Error launching MainActivity: " + e.getMessage());
e.printStackTrace();
}
}
private void sendEReceipt(EReceiptRequest request) {
try {
LogUtil.d(TAG, "Sending e-receipt: " + new Gson().toJson(request));
repository.sendReceipt(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> LogUtil.d(TAG, "E-Receipt sent successfully: " + response.getMessage()),
error -> {
if (error instanceof HttpException) {
HttpException httpEx = (HttpException) error;
try {
String errorJson = httpEx.response().errorBody().string();
LogUtil.e(TAG, "E-Receipt error: " + errorJson);
} catch (Exception e) {
LogUtil.e(TAG, "E-Receipt send failed: " + error.getMessage());
}
} else {
LogUtil.e(TAG, "E-Receipt send failed: " + error.getMessage());
}
}
);
} catch (Exception e) {
LogUtil.e(TAG, "Error sending e-receipt: " + e.getMessage());
}
}
}

View File

@ -1,14 +0,0 @@
package com.utsmm.kbz.service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class SmileAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Start the service to send the API request
Intent serviceIntent = new Intent(context, SmileSettleService.class);
context.startService(serviceIntent);
}
}

View File

@ -1,183 +0,0 @@
package com.utsmm.kbz.service;
import android.annotation.SuppressLint;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmm.kbz.BuildConfig;
import com.utsmm.kbz.R;
import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import com.utsmyanmar.paylibs.utils.LogUtil;
// Temporarily disabled Hilt
// @AndroidEntryPoint
public class SmileSettleService extends Service {
private static final String TAG = SmileSettleService.class.getSimpleName();
public static final String ACTION_DATA_RECEIVED = BuildConfig.APPLICATION_ID + ".ACTION_DATA_RECEIVED";
public static final String EXTRA_DATA = BuildConfig.APPLICATION_ID + ".EXTRA_DATA";
private Handler handler;
private SettlementViewModel settlementViewModel;
@Inject
Repository repository;
public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default";
private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager)getSystemService( NOTIFICATION_SERVICE ) ;
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext() , default_notification_channel_id ) ;
mBuilder.setContentTitle( "Alarm manager is running!" ) ;
mBuilder.setContentText( "Wiping out the transactions!" ) ;
mBuilder.setTicker( "Notification Listener Service Example" ) ;
mBuilder.setSmallIcon(R.drawable. ic_launcher_foreground ) ;
mBuilder.setAutoCancel( true ) ;
if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) {
int importance = NotificationManager. IMPORTANCE_HIGH ;
NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "NOTIFICATION_CHANNEL_NAME" , importance) ;
mBuilder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel) ;
}
assert mNotificationManager != null;
mNotificationManager.notify(( int ) System. currentTimeMillis () , mBuilder.build()) ;
}
private void sendDataToViewModel(String data) {
Intent intent = new Intent(ACTION_DATA_RECEIVED);
intent.putExtra(EXTRA_DATA, data);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
@SuppressLint("CheckResult")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Perform the Settlement request here
// ...
LogUtil.d(TAG,"<<<<<<<<<<<<<<< This action was called >>>>>>>>>>>>>>>>.");
sendDataToViewModel("Hello there");
createNotification();
// Observer<List<PayDetail>> settleObserver = new Observer<List<PayDetail>>() {
// @Override
// public void onChanged(List<PayDetail> list) {
//
//
// if(list == null || list.size() == 0) {
// LogUtil.d(TAG, "Settle is null");
// return;
// }
// LogUtil.d(TAG,"Size Settle : "+list.size());
//
// for (PayDetail pay:list) {
// repository.deletePayDetail(pay);
// }
//
//
// }
// };
//
//
// repository.getSettlementPOS().observeForever(settleObserver);
//
// repository.getSettlementPOS().removeObserver(settleObserver);
//
// Observer<List<PayDetail>> deleteTransObserver = new Observer<List<PayDetail>>() {
// @Override
// public void onChanged(List<PayDetail> lists) {
// if(lists == null || lists.size() == 0) {
// return;
// }
//
// LogUtil.d(TAG,"Size Delete Trans : "+lists.size());
// for (PayDetail pay:lists) {
// repository.deletePayDetail(pay);
// }
// }
// };
//
// repository.getDeleteTrans(SystemParamsOperation.getInstance().getCurrentBatchNum()).observeForever(deleteTransObserver);
//
// repository.getDeleteTrans(SystemParamsOperation.getInstance().getCurrentBatchNum()).removeObserver(deleteTransObserver);
//
//
// try{
// handler.post(new Runnable() {
// @Override
// public void run() {
// // Create and show the dialog
// AlertDialog.Builder builder = new AlertDialog.Builder(SmileSettleService.this);
// builder.setTitle("Auto Settle!")
// .setMessage("Settlement process is going to start!")
// .setPositiveButton("OK", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// // Handle OK button click
// dialog.dismiss(); // Dismiss the dialog
// }
// })
// .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// // Handle Cancel button click
// dialog.dismiss(); // Dismiss the dialog
// }
// });
//
//
// AlertDialog dialog = builder.create();
//// dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
//// dialog.show();
// }
// });
// }catch (Exception e){
// e.printStackTrace();
// }
return START_NOT_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
handler = new Handler();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}

View File

@ -1,17 +1,26 @@
package com.utsmm.kbz.ui;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.Navigation;
import com.google.gson.Gson;
import com.google.gson.stream.MalformedJsonException;
import com.utsmm.kbz.MainActivity;
import com.utsmm.kbz.config.Constants;
import com.nexgo.downloadkey.downloadflow.DownloadFEntity;
import com.nexgo.downloadkey.downloadflow.DownloadFlow;
import com.nexgo.downloadkey.downloadflow.DownloadFlowProcessListener;
import com.nexgo.downloadkey.downloadflow.DownloadFlowResultEntity;
import com.nexgo.downloadkey.downloadflow.DownloadResult;
import com.utsmyanmar.baselib.BaseApplication;
import com.utsmyanmar.baselib.emv.EmvParamOperation;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.sirius.SiriusError;
@ -23,11 +32,14 @@ import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.baselib.util.URLValidator;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.TransMenu;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.MainViewModel;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.data.model.TMSUpdate;
import com.utsmm.kbz.databinding.FragmentSettingsModernBinding;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import com.utsmm.kbz.util.tms.TMSUtil;
@ -48,9 +60,10 @@ public class SettingsFragment extends DataBindingFragment {
private SettlementViewModel settlementViewModel;
private MainViewModel mainViewModel;
private SharedViewModel sharedViewModel;
private int count = 0;
// Data binding will handle view access automatically
private FragmentSettingsModernBinding binding;
@Inject
@ -58,14 +71,13 @@ public class SettingsFragment extends DataBindingFragment {
@Override
protected void initViewModel() {
// Initialize ViewModels in initViewModel as per pattern
settlementViewModel = getFragmentScopeViewModel(SettlementViewModel.class);
mainViewModel = getFragmentScopeViewModel(MainViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
// This is the key method that links the XML and fragment properly
return new DataBindingConfig(R.layout.fragment_settings_modern, 0, null)
.addBindingParam(BR.click, new ClickEvent());
}
@ -89,30 +101,24 @@ public class SettingsFragment extends DataBindingFragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
try {
LogUtil.d(TAG, "onViewCreated called");
SystemParamsOperation.getInstance().setDemoStatus(false);
// Get the binding from the base class - this is automatically created
binding = (FragmentSettingsModernBinding) mBinding;
updateDemoSettings();
LogUtil.d(TAG, "Updating settings...");
updateSettings();
LogUtil.d(TAG, "Settings fragment initialized successfully");
} catch (Exception e) {
LogUtil.e(TAG, "Error initializing fragment: " + e.getMessage());
e.printStackTrace();
}
}
@Override
public void onResume() {
super.onResume();
try {
setToolBarTitleWithBackIcon("Settings");
LogUtil.d(TAG, "Settings screen resumed");
} catch (Exception e) {
LogUtil.e(TAG, "Error in onResume: " + e.getMessage());
}
setToolBarTitleWithBackIcon("Settings");
BaseApplication.getInstance().deviceEngine.getPlatform().enableControlBar();
}
@ -144,22 +150,25 @@ public class SettingsFragment extends DataBindingFragment {
private void updateDemoSettings() {
boolean isDemo = SystemParamsOperation.getInstance().getDemoStatus();
binding.demoSwitch.setChecked(isDemo);
binding.demoSummary.setText(isDemo ?
"Demo mode active - Test transactions without real processing" :
"Simulator mode active - Real transaction processing");
// binding.demoSwitch.setChecked(isDemo);
// binding.demoSummary.setText(isDemo ?
// "Demo mode active - Test transactions without real processing" :
// "Simulator mode active - Real transaction processing");
}
private void updateAlertSoundSettings() {
boolean isEnabled = SystemParamsOperation.getInstance().isAlertSound();
binding.alertSoundSwitch.setChecked(isEnabled);
binding.alertSoundSummary.setText(isEnabled ?
"Sound alerts enabled for notifications" :
"Sound alerts disabled - Silent mode");
// binding.alertSoundSwitch.setChecked(isEnabled);
// binding.alertSoundSummary.setText(isEnabled ?
// "Sound alerts enabled for notifications" :
// "Sound alerts disabled - Silent mode");
}
private void updateReversalSettings() {
boolean isVisible = SystemParamsOperation.getInstance().getAutoReversalEnable();
LogUtil.d("SettingsFragment", "isVisible " + isVisible);
binding.reversalCard.setVisibility(isVisible ? View.VISIBLE : View.GONE);
boolean isEnabled = SystemParamsOperation.getInstance().isReversalOn();
binding.reversalSwitch.setChecked(isEnabled);
binding.reversalSummary.setText(isEnabled ?
@ -169,10 +178,10 @@ public class SettingsFragment extends DataBindingFragment {
private void updateMultiHostSettings() {
boolean isEnabled = SystemParamsOperation.getInstance().isMultiHost();
binding.multiHostSwitch.setChecked(isEnabled);
binding.multiHostSummary.setText(isEnabled ?
"Multiple host support enabled" :
"Single host mode - Primary host only");
// binding.multiHostSwitch.setChecked(isEnabled);
// binding.multiHostSummary.setText(isEnabled ?
// "Multiple host support enabled" :
// "Single host mode - Primary host only");
}
private void updateTmsAddressSettings() {
@ -183,6 +192,12 @@ public class SettingsFragment extends DataBindingFragment {
// ClickEvent class for data binding - this is the proper pattern
public class ClickEvent {
public void onHostConfigClick(){
int routeId = R.id.action_nav_settings_to_hostConfigFragment;
int currentId = R.id.nav_settings;
int hostId = Constants.NAV_HOST_ID;
safeRouteTo(currentId, routeId, hostId);
}
public void onVersionClick() {
try {
@ -197,6 +212,16 @@ public class SettingsFragment extends DataBindingFragment {
}
}
public void onInjectKeyClick() {
try {
LogUtil.d(TAG, "Inject Key clicked");
Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
.navigate(R.id.action_nav_settings_to_injectKeyFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Error navigating to inject key: " + e.getMessage());
}
}
public void onDemoClick() {
try {
boolean newValue = !SystemParamsOperation.getInstance().getDemoStatus();
@ -278,7 +303,6 @@ public class SettingsFragment extends DataBindingFragment {
public void onUpdateConfigClick() {
try {
LogUtil.d(TAG, "Update config clicked");
settlementViewModel.getSettlementPOS().observe(getViewLifecycleOwner(), payDetails -> {
try {
if (payDetails != null && payDetails.size() > 0) {
@ -299,6 +323,28 @@ public class SettingsFragment extends DataBindingFragment {
}
}
public void onClearBatchClick() {
try {
sharedViewModel.transactionsType.postValue(TransactionsType.CLEAR_BATCH);
sharedViewModel.setTransMenu(TransMenu.CLEAR_BATCH);
Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
.navigate(R.id.inputPasswordFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Error in clear batch click: " + e.getMessage());
}
}
public void onClearReversalClick() {
try {
sharedViewModel.transactionsType.postValue(TransactionsType.CLEAR_REVERSAL);
sharedViewModel.setTransMenu(TransMenu.CLEAR_REVERSAL);
Navigation.findNavController(requireActivity(), R.id.nav_host_fragment)
.navigate(R.id.inputPasswordFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Error in clear reversal click: " + e.getMessage());
}
}
public void onTmsConfigClick() {
try {
LogUtil.d(TAG, "TMS Config clicked");
@ -365,6 +411,9 @@ public class SettingsFragment extends DataBindingFragment {
private void downloadParams(String lastTransName, String lastTransTime) {
try {
LogUtil.d(TAG, "Downloading params for transaction: " + lastTransName);
((MainActivity) requireActivity()).getLocation();
SiriusRequest siriusReq = TMSUtil.getInstance().generateRequestParams(getContext(), lastTransName, lastTransTime);
mainViewModel.getParams(siriusReq)

View File

@ -2,7 +2,10 @@ package com.utsmm.kbz.ui.adapters;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PorterDuff;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
@ -262,6 +265,19 @@ public class MyBindingAdapter {
imageView.setImageBitmap(bitmap);
}
@BindingAdapter("signatureBlob")
public static void setSignatureFromBlob(ImageView imageView, byte[] blob) {
if (blob != null && blob.length > 0) {
Bitmap bitmap = BitmapFactory.decodeByteArray(blob, 0, blob.length);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
imageView.setVisibility(View.VISIBLE);
return;
}
}
imageView.setVisibility(View.GONE);
}
@BindingAdapter({"mmpayData"})
public static void setMMPayQRCode(AppCompatImageView imageView,String data) {
//250
@ -294,9 +310,12 @@ public class MyBindingAdapter {
@BindingAdapter({"isTrace"})
public static void checkIsTraceOrInvoice(TextView textView, PayDetail payDetail) {
if(payDetail.getTransactionType() == TransactionsType.MMQR.value || payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value) {
textView.setText("INV:"+payDetail.getInvoiceNo());
// textView.setText("INV:"+payDetail.getInvoiceNo());
//tempo fix for qr trace num
textView.setText("TRC:"+payDetail.getVoucherNo());
} else {
textView.setText("TRC:"+payDetail.getVoucherNo());
}
}
}

View File

@ -0,0 +1,78 @@
package com.utsmm.kbz.ui.adapters;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemQrPayButtonBinding;
import com.utsmm.kbz.ui.qr_pay.QRPayItem;
import java.util.List;
public class QRPayAdapter extends RecyclerView.Adapter<QRPayAdapter.ViewHolder> {
public interface OnItemClickListener {
void onItemClick(int position);
}
private final List<QRPayItem> items;
private final OnItemClickListener listener;
public QRPayAdapter(List<QRPayItem> item, OnItemClickListener listener) {
this.items = item;
this.listener = listener;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemQrPayButtonBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_qr_pay_button,
parent,
false
);
return new ViewHolder(binding);
}
private final int[] colors = new int[]{
R.color.colorPrimary,
R.color.amber,
R.color.forestGreen
};
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
QRPayItem item = items.get(position);
holder.binding.setItem(item);
holder.binding.executePendingBindings();
holder.binding.btnItem.setOnClickListener(v -> {
if(item.isActive && listener != null){
listener.onItemClick(position);
}
});
}
@Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
final ItemQrPayButtonBinding binding;
public ViewHolder(@NonNull ItemQrPayButtonBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}

View File

@ -8,11 +8,19 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.nexgo.oaf.apiv3.device.reader.CardSlotTypeEnum;
import com.utsmm.kbz.ui.pinpad.PinPadViewModel;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TerminalUtil;
import com.utsmyanmar.baselib.util.TimeoutCallback;
import com.utsmyanmar.checkxread.checkcard.CheckCardResultX;
import com.utsmyanmar.checkxread.model.CardDataX;
import com.utsmyanmar.checkxread.readcard.MPUXReadCard;
import com.utsmyanmar.checkxread.readcard.ReadCardResultX;
import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.checkxread.util.CardTypeX;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.BR;
@ -37,6 +45,8 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
private TransProcessViewModel transProcessViewModel;
private PinPadViewModel pinPadViewModel;
private static final String TAG = CardWaitingFragment.class.getSimpleName();
private static final int cardWaitingTimeOut = 60;
@ -59,6 +69,8 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
transProcessViewModel = getFragmentScopeViewModel(TransProcessViewModel.class);
pinPadViewModel = getFragmentScopeViewModel(PinPadViewModel.class);
}
@Override
@ -106,11 +118,50 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
public void onClickManualEntry() {
// showSingleInfoDialogAutoHide("Coming Soon!");
routeId = R.id.action_cardWaitingFragment_to_manualEntryFragment;
safeNavigateToRouteId();
}
public void onClickTap() {
cardReadViewModel.setCardTransactionType(CardTransactionType.MOCK);
routeId = R.id.action_cardWaitingFragment_to_processingCardFragment;
safeNavigateToRouteId();
}
}
private void updateManualEntryVisibility() {
TransactionsType type = sharedViewModel.transactionsType.getValue();
boolean manualEntryStatus = SystemParamsOperation.getInstance().getManualEntryStatus();
boolean preAuthManualEntryStatus = SystemParamsOperation.getInstance().getPreAuthManualEnabled();
boolean preAuthCompManualEntryStatus = SystemParamsOperation.getInstance().getPreAuthCompleteManualEnabled();
boolean preAuthCancelManualEntryStatus = SystemParamsOperation.getInstance().getPreAuthVoidManualEnabled();
boolean show;
if (type == null) {
show = manualEntryStatus;
} else {
switch (type) {
case SALE:
case CASH_OUT:
case CASH_DEPOSIT:
case REFUND:
case VOID:
show = manualEntryStatus;
break;
case PRE_AUTH_SALE:
show = preAuthManualEntryStatus;
break;
case PRE_AUTH_VOID:
show = preAuthCancelManualEntryStatus;
break;
case PRE_AUTH_COMPLETE:
show = preAuthCompManualEntryStatus;
break;
default:
show = false;
break;
}
}
sharedViewModel.setManualEntryStatus(show);
}
@Override
@ -119,6 +170,8 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
resetUI();
updateManualEntryVisibility();
setToolBarTitleWithBackIcon(getResourceString(R.string.txt_title_card_capture));
delayFunctionCall(()-> {
@ -132,6 +185,15 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
if(sharedViewModel.getIsFallback().getValue() != null && sharedViewModel.getIsFallback().getValue()) {
cardReadViewModel.setCheckCardAlertMsg("Fallback!\nPlease stripe!",false);
setupCardReadProcess(true);
}else if(sharedViewModel.getIsSeePhone().getValue() != null && sharedViewModel.getIsSeePhone().getValue()) {
cardReadViewModel.setCheckCardAlertMsg("Please Tap!",false);
setupCardReadProcess(true);
} else if(sharedViewModel.getIsCardAppBlock().getValue() != null && sharedViewModel.getIsCardAppBlock().getValue()) {
cardReadViewModel.setCheckCardAlertMsg("Magstripe not allowed!",false);
setupCardReadProcess(true);
} else if(sharedViewModel.getIsContactlessRestricted().getValue() != null && sharedViewModel.getIsContactlessRestricted().getValue()) {
cardReadViewModel.setCheckCardAlertMsg("Tap not allowed!",false);
setupCardReadProcess(true);
} else {
setupCardReadProcess(false);
}
@ -143,6 +205,8 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
super.onPause();
sharedViewModel.setIsFallback(false);
sharedViewModel.setIsSeePhone(false);
sharedViewModel.setIsContactlessRestricted(false);
// cardReadViewModel.cancelCheckCard();
@ -214,6 +278,8 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
private void initCheckCard(boolean isFallback) {
int waitTimeout = cardWaitingTimeOut + 5;
// int allType = AidlConstants.CardType.NFC.getValue() | AidlConstants.CardType.IC.getValue() | AidlConstants.CardType.MAGNETIC.getValue();
//
//
@ -227,12 +293,33 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
// }
// }
HashSet<CardSlotTypeEnum> allType = new HashSet<>();
allType.add(CardSlotTypeEnum.ICC1);
allType.add(CardSlotTypeEnum.RF);
allType.clear();
if(sharedViewModel.getIsSeePhone().getValue() != null && sharedViewModel.getIsSeePhone().getValue()) {
allType.add(CardSlotTypeEnum.RF);
waitTimeout = 20;
} else if(!SystemParamsOperation.getInstance().isNfcEnabled()) {
allType.add(CardSlotTypeEnum.ICC1);
allType.add(CardSlotTypeEnum.SWIPE);
} else if(sharedViewModel.getIsContactlessRestricted().getValue() != null && sharedViewModel.getIsContactlessRestricted().getValue()) {
allType.add(CardSlotTypeEnum.ICC1);
// allType.add(CardSlotTypeEnum.SWIPE);
} else {
allType.add(CardSlotTypeEnum.ICC1);
allType.add(CardSlotTypeEnum.RF);
if(SystemParamsOperation.getInstance().isMagStripeEnabled()) {
allType.add(CardSlotTypeEnum.SWIPE);
}
}
// int allType = AidlConstants.CardType.IC.getValue() | AidlConstants.CardType.MAGNETIC.getValue();
cardReadViewModel.startCheckXProcessNex(allType, cardWaitingTimeOut + 5, new CheckCardResultX() {
cardReadViewModel.startCheckXProcessNex(allType, waitTimeout, new CheckCardResultX() {
@Override
public void onSuccess(CardTypeX cardType, boolean isMPU) {
LogUtil.d(TAG,"**** on success *****");
@ -254,17 +341,20 @@ public class CardWaitingFragment extends DataBindingFragment implements DataBind
} else if (isFallback && cardType == CardTypeX.MAG) {
/* do fallback transactions */
sharedViewModel.isEmv.setValue(false);
sharedViewModel.isEmv.postValue(false);
cardReadViewModel.setCardTransactionType(CardTransactionType.FALLBACK);
} else if (cardType == CardTypeX.IC || cardType == CardTypeX.NFC) {
cardReadViewModel.cardTypeData.postValue(cardType.value);
if(isMPU) {
sharedViewModel.isEmv.setValue(false);
cardReadViewModel.setCardTransactionType(CardTransactionType.MPU);
} else {
cardReadViewModel.cardTypeData.postValue(cardType.value);
// cardReadViewModel.cardTypeData.postValue(cardType.value);
cardReadViewModel.setCardTransactionType(CardTransactionType.EMV);
}
// cardReadViewModel.cardTypeData.postValue(cardType.value);
// cardReadViewModel.setCardTransactionType(CardTransactionType.EMV);
}
delayFunctionCall(()->{
routeId = R.id.action_cardWaitingFragment_to_processingCardFragment;

View File

@ -106,6 +106,10 @@ public class ConfirmTransactionFragment extends DataBindingFragment implements D
setToolBarTitleWithBackIcon(getResourceString(R.string.txt_title_trans_info));
isMerchantCopy = true;
sharedViewModel.printerDisabled.setValue(!SystemParamsOperation.getInstance().getPrinterEnabled());
if (sharedViewModel.getTransMenu().getValue() != TransMenu.REPRINT) {
sharedViewModel.suppressReprintButtons.setValue(false);
}
if(sharedViewModel.getTransMenu().getValue() == TransMenu.REVIEW) {
@ -122,9 +126,17 @@ public class ConfirmTransactionFragment extends DataBindingFragment implements D
} else if(sharedViewModel.getTransMenu().getValue() == TransMenu.REPRINT) {
sharedViewModel.reprintTransTypeMsg.setValue(getResourceString(R.string.txt_print_merchant_copy));
sharedViewModel.oneBtnLayout.setValue(8);
sharedViewModel.twoBtnLayout.setValue(8);
sharedViewModel.reprintBtnLayout.setValue(0);
boolean suppress = sharedViewModel.suppressReprintButtons.getValue() != null
&& sharedViewModel.suppressReprintButtons.getValue();
if (suppress) {
sharedViewModel.oneBtnLayout.setValue(8);
sharedViewModel.twoBtnLayout.setValue(8);
sharedViewModel.reprintBtnLayout.setValue(8);
} else {
sharedViewModel.oneBtnLayout.setValue(8);
sharedViewModel.twoBtnLayout.setValue(8);
sharedViewModel.reprintBtnLayout.setValue(sharedViewModel.printerDisabled.getValue() != null && sharedViewModel.printerDisabled.getValue() ? 8 : 0);
}
// printReceipt(true);
@ -234,7 +246,6 @@ public class ConfirmTransactionFragment extends DataBindingFragment implements D
public void onConfirm(){
if (sharedViewModel.transactionsType.getValue() == TransactionsType.TIP_ADJUSTMENT) {
routeId = R.id.action_confirmTransactionFragment_to_inputTipAmountFragment;
safeNavigateToRouteId();
@ -258,6 +269,10 @@ public class ConfirmTransactionFragment extends DataBindingFragment implements D
}
public void onPrint() {
if (!SystemParamsOperation.getInstance().getPrinterEnabled()) {
showDeclineDialog("Printer is disabled!");
return;
}
printReceipt(isMerchantCopy);

View File

@ -1,295 +0,0 @@
package com.utsmm.kbz.ui.core_ui;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Observer;
import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.ui.ListDialog;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.params.Params;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.EmvReadCardViewModel;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.core_viewmodel.TransProcessViewModel;
import com.utsmm.kbz.ui.pre_auth.PreAuthCompViewModel;
import com.utsmm.kbz.util.enums.EmvReadStatus;
import com.utsmm.kbz.util.ecr.CoreUtils;
import java.util.ArrayList;
import java.util.List;
import com.utsmyanmar.paylibs.utils.LogUtil;
/**
* Currently this class is not used
*/
public class EmvReadCardFragment extends DataBindingFragment {
private EmvReadCardViewModel emvReadCardViewModel;
private SharedViewModel sharedViewModel;
private PreAuthCompViewModel preAuthCompViewModel;
private TransProcessViewModel transProcessViewModel;
private ListDialog mListDialog;
private static final String TAG = EmvReadCardFragment.class.getSimpleName();
private int routeID;
private int popBackID;
private List<PayDetail> lists = new ArrayList<>();
private String currentCardNo = "";
@Override
protected void initViewModel() {
emvReadCardViewModel = getFragmentScopeViewModel(EmvReadCardViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
preAuthCompViewModel = getFragmentScopeViewModel(PreAuthCompViewModel.class);
transProcessViewModel = getFragmentScopeViewModel(TransProcessViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_emv_card_read, BR.emv,emvReadCardViewModel)
.addBindingParam(BR.click,new ClickEvent());
}
@Override
protected int currentId() {
return 0;
}
@Override
protected int hostId() {
return 0;
}
@Override
protected int routeId() {
return 0;
}
@Override
public void onResume() {
super.onResume();
observeData();
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onDestroy() {
super.onDestroy();
emvReadCardViewModel.terminateTrans();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
emvReadCardViewModel.startProcess();
emvReadCardViewModel.resultStatus.observe(getViewLifecycleOwner(), new Observer<EmvReadStatus>() {
@Override
public void onChanged(EmvReadStatus emvReadStatus) {
switch (emvReadStatus){
case SUCCESS: emvReadCardViewModel.isButtonVisible.postValue(true);
currentCardNo = emvReadCardViewModel.cardNum.getValue();
break;
case FAIL: cardReadFailAction(emvReadCardViewModel.errorCode.getValue()); break;
case SELECT_APP: observeList();break;
}
}
});
}
private void cardReadFailAction(String message){
try{
if(message == null){
message = getResourceString(R.string.txt_card_read_fail);
}else if(message.contains(":")){
message = message.split(":")[1];
}
getNavController(Constants.NAV_HOST_ID).navigate(popBackID);
showSingleInfoDialog(message);
if(sharedViewModel.isEcr.getValue() != null){
if(sharedViewModel.isEcr.getValue()){
sharedViewModel.isEcr.postValue(false);
sharedViewModel.isEcrFinished.postValue(true);
CoreUtils.getInstance(sharedViewModel).responseRejectMsg(message);
// ECRHelper.INSTANCE.send(message.getBytes());
}
}
}catch (IllegalArgumentException e){
e.printStackTrace();
getNavController(Constants.NAV_HOST_ID).navigate(popBackID);
}
}
private void observeData(){
switch (sharedViewModel.transactionsType.getValue()){
case PRE_AUTH_COMPLETE:
observePreAuthComp();
routeID = R.id.action_emvReadCardFragment_to_inputRRNFragment;
popBackID = R.id.action_emvReadCardFragment_to_nav_main;
break;
case PRE_AUTH_VOID:
routeID = R.id.action_emvReadCardFragment_to_inputRRNFragment;
popBackID = R.id.action_emvReadCardFragment_to_nav_main;
break;
}
}
private void observePreAuthComp() {
preAuthCompViewModel.getPreAuthComData().observe(getViewLifecycleOwner(), new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
if(payDetails.size() > 0) {
LogUtil.d(TAG,"Pre Auth Comp size :"+payDetails.size());
lists.addAll(payDetails);
sharedViewModel.payDetailList.postValue(payDetails);
} else {
LogUtil.d(TAG,"Pre Auth Comp data not found!");
}
}
});
}
private boolean checkPreAuthCardNo() {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID) {
return sharedViewModel.payDetail.getValue().getCardNo().equals(currentCardNo);
} else if (sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_COMPLETE) {
for(PayDetail payDetail: lists) {
if(payDetail.getCardNo().equals(currentCardNo)){
sharedViewModel.cardNo.postValue(currentCardNo);
return true;
}else{
LogUtil.d(TAG,"not found 1!");
}
}
return false;
}
else {
return true;
}
}
private void observeList(){
emvReadCardViewModel.emvList.observe(getViewLifecycleOwner(), new Observer<List<EMVCandidateV2>>() {
@Override
public void onChanged(List<EMVCandidateV2> emvCandidateV2s) {
showListDialog(emvCandidateV2s);
}
});
}
private void showListDialog(List<EMVCandidateV2> list) {
if (mListDialog == null) {
mListDialog = new ListDialog(getContext());
mListDialog.setTitleText(R.string.index_select_app);
mListDialog.setCancelable(true);
mListDialog.setCanceledOnTouchOutside(true);
}
List<String> tempList = new ArrayList<>();
for (EMVCandidateV2 emvCandidateV2 : list) {
tempList.add(emvCandidateV2.appLabel);
}
mListDialog.setData(tempList);
mListDialog.setOnItemClickListener(
position -> {
emvReadCardViewModel.setEmvAppSelect(position);
}
);
try {
mListDialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
private void buildCardData(){
TradeData tradeData = Params.newTrade(false);
PayDetail payDetails = tradeData.getPayDetail();
payDetails.setCardNo(emvReadCardViewModel.cardNum.getValue());
payDetails.setEXPDate(emvReadCardViewModel.expDate.getValue());
payDetails.setCardType(1);
payDetails.setAccountType("VISA");
payDetails.setCardHolderName("card holder");
// payDetails.setCardInfo();
transProcessViewModel.setTradeData(tradeData);
transProcessViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
}
private void checkRoute(){
buildCardData();
if(checkPreAuthCardNo()){
getNavController(Constants.NAV_HOST_ID).navigate(routeID);
}else{
showDeclineDialog("Mismatch Card No!");
}
}
public class ClickEvent {
public void onConfirm() {
checkRoute();
}
public void onCancel() {
if(sharedViewModel.isEcr.getValue() != null){
if(sharedViewModel.isEcr.getValue()){
sharedViewModel.isEcr.postValue(false);
sharedViewModel.isEcrFinished.postValue(true);
CoreUtils.getInstance(sharedViewModel).responseRejectMsg("Canceled Transactions!");
// ECRHelper.INSTANCE.send("Canceled Transactions!".getBytes());
}
}
getNavController(Constants.NAV_HOST_ID).navigate(popBackID);
}
}
}

View File

@ -3,6 +3,7 @@ package com.utsmm.kbz.ui.core_ui;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -10,12 +11,14 @@ import androidx.appcompat.app.AlertDialog;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import com.utsmm.kbz.ui.core_viewmodel.CardReadViewModel;
import com.utsmm.kbz.ui.pinpad.PinPadViewModel;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.ui.CustomPinPadKeyboard;
import com.utsmyanmar.baselib.ui.ListDialog;
import com.utsmyanmar.baselib.util.CardDetectCallback;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.checkxread.util.CardTypeX;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
@ -41,6 +44,8 @@ public class EmvTransactionFragment extends DataBindingFragment {
private EmvTransactionProcessViewModel emvTransactionViewModel;
private CardReadViewModel cardReadViewModel;
private PinPadViewModel pinPadViewModel;
private SharedViewModel sharedViewModel;
@ -75,6 +80,7 @@ public class EmvTransactionFragment extends DataBindingFragment {
emvTransactionViewModel = getFragmentScopeViewModel(EmvTransactionProcessViewModel.class);
pinPadViewModel = getFragmentScopeViewModel(PinPadViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
cardReadViewModel = getFragmentScopeViewModel(CardReadViewModel.class);
}
@Override
@ -134,6 +140,8 @@ public class EmvTransactionFragment extends DataBindingFragment {
emvTransactionViewModel.customPinPadKeyboard = binding.customPinPad;
initSharedData();
initData();
@ -151,6 +159,7 @@ public class EmvTransactionFragment extends DataBindingFragment {
}
observeEmvResultStatus();
observePinWarning();
}
private boolean isNotManualTransaction() {
@ -203,22 +212,86 @@ public class EmvTransactionFragment extends DataBindingFragment {
});
break;
case SEE_PHONE:
isCardTaped("Please check your phone!",new CardDetectCallback() {
@Override
public void onComplete() {
sharedViewModel.setIsSeePhone(true);
navigateToCheckCard();
}
});
break;
case OTHER_INTERFACE:
sharedViewModel.set_errorFragmentMsg("Please use other interface!");
navigateToError();
break;
case PIN_PAD_CANCEL:
navigateToMain();
break;
case ON_NEXT_SCREEN:
navigateToRRNScreen();
break;
case INSERT_OR_MANUAL:
if(cardReadViewModel.cardTypeData.getValue() != null && cardReadViewModel.cardTypeData.getValue() == CardTypeX.NFC.value) {
isCardTaped("Please insert or type card",new CardDetectCallback() {
@Override
public void onComplete() {
sharedViewModel.setIsContactlessRestricted(true);
navigateToCheckCard();
}
});
}
break;
case READ_CARD_RETRY:
sharedViewModel.set_errorFragmentMsg("Interface not found!");
navigateToError();
LogUtil.d(TAG,"Card type :"+cardReadViewModel.cardTypeData.getValue());
if(cardReadViewModel.cardTypeData.getValue()!= null && cardReadViewModel.cardTypeData.getValue() == CardTypeX.IC.value) {
isCardInserted(new CardDetectCallback() {
@Override
public void onComplete() {
sharedViewModel.setIsFallback(true);
navigateToCheckCard();
}
});
} else if(cardReadViewModel.cardTypeData.getValue() != null && cardReadViewModel.cardTypeData.getValue() == CardTypeX.NFC.value) {
isCardTaped("Please try again",new CardDetectCallback() {
@Override
public void onComplete() {
sharedViewModel.setIsSeePhone(false);
navigateToCheckCard();
}
});
}
break;
case CARD_APP_BLOCKED:
if(cardReadViewModel.cardTypeData.getValue()!= null && cardReadViewModel.cardTypeData.getValue() == CardTypeX.IC.value) {
isCardInserted(new CardDetectCallback() {
@Override
public void onComplete() {
sharedViewModel.setIsCardAppBlock(true);
navigateToCheckCard();
}
});
} else if(cardReadViewModel.cardTypeData.getValue() != null && cardReadViewModel.cardTypeData.getValue() == CardTypeX.NFC.value) {
isCardTaped("Please try again",new CardDetectCallback() {
@Override
public void onComplete() {
sharedViewModel.setIsCardAppBlock(true);
navigateToCheckCard();
}
});
}
// sharedViewModel.set_errorFragmentMsg("Interface not found!");
// navigateToError();
break;
case READ_CARD_NO_APP:
isCardTaped("Terminal has no APP!");
break;
case TRY_AGAIN:
sharedViewModel.set_errorFragmentMsg("Please replace the card");
navigateToError();
// sharedViewModel.set_errorFragmentMsg("Please replace the card");
// navigateToError();
showSingleInfoDialog("Please check your phone!");
navigateToCheckCard();
break;
case OFFLINE_DECLINE:
@ -289,6 +362,15 @@ public class EmvTransactionFragment extends DataBindingFragment {
});
}
private void observePinWarning() {
emvTransactionViewModel.pinRemainingCount.observe(getViewLifecycleOwner(), msg -> {
if (!TextUtils.isEmpty(msg)) {
Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show();
emvTransactionViewModel.pinRemainingCount.setValue("");
}
});
}
private void observeEmvErrorCode() {
dismissLoadingDialog();
emvTransactionViewModel.errorCodeMsg.observe(getViewLifecycleOwner(), pair -> {

View File

@ -7,6 +7,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.ui.NumberKeyboard;
import com.utsmyanmar.baselib.util.CardDetectCallback;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TimeoutCallback;
@ -71,7 +72,7 @@ public class InputAmountFragment extends DataBindingFragment implements DataBind
super.onViewCreated(view, savedInstanceState);
setUpTimer();
// setUpTimer();
setupBackButtonPressDetection(this);
}
@ -98,6 +99,22 @@ public class InputAmountFragment extends DataBindingFragment implements DataBind
setToolBarTitleWithBackIcon(getResourceString(R.string.txt_subtitle_amount));
boolean decimalEnabled = SystemParamsOperation.getInstance().getDecimalEnable();
boolean qrDecimalEnabled = SystemParamsOperation.getInstance().isQrDecimalEnable();
boolean isDecimalEnabled;
if(sharedViewModel.transactionsType.getValue() == TransactionsType.MMQR){
isDecimalEnabled = qrDecimalEnabled;
}else {
isDecimalEnabled = decimalEnabled;
}
View keyboardView = getView().findViewById(R.id.numberKeyboard);
if (keyboardView instanceof NumberKeyboard) {
((NumberKeyboard) keyboardView).setDecimalEnable(isDecimalEnabled);
}
if(sharedViewModel.isEcr.getValue() != null){
if(sharedViewModel.isEcr.getValue()){
@ -204,7 +221,17 @@ public class InputAmountFragment extends DataBindingFragment implements DataBind
if(isInputValid(Objects.requireNonNull(inputAmountViewModel.inputAmountView.getValue()))){
inputAmountViewModel.invalidAmountMsg.setValue("");
boolean decimalEnabled = SystemParamsOperation.getInstance().getDecimalEnable();
// boolean decimalEnabled = SystemParamsOperation.getInstance().getDecimalEnable();
boolean cardDecimalEnabled = SystemParamsOperation.getInstance().getDecimalEnable();
boolean qrDecimalEnabled = SystemParamsOperation.getInstance().isQrDecimalEnable();
boolean decimalEnabled;
if(sharedViewModel.transactionsType.getValue() == TransactionsType.MMQR){
decimalEnabled = qrDecimalEnabled;
}else {
decimalEnabled = cardDecimalEnabled;
}
String inputValue = inputAmountViewModel.inputAmountView.getValue();
if (decimalEnabled) {

View File

@ -142,6 +142,10 @@ public class InputPasswordFragment extends DataBindingFragment implements DataBi
private void checkRoute() {
switch (Objects.requireNonNull(sharedViewModel.transactionsType.getValue())) {
case MMQR_REFUND:
inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM);
routeId = R.id.action_inputPasswordFragment_to_QRRefundProcessFragment;
break;
case PRE_AUTH_COMPLETE_VOID:
inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM);
sharedViewModel.transactionName.postValue(getResourceString(R.string.title_pre_auth_complete));
@ -172,7 +176,7 @@ public class InputPasswordFragment extends DataBindingFragment implements DataBi
break;
case REFUND:
inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM);
routeId = R.id.action_inputPasswordFragment_to_selectRefundFragment;
routeId = R.id.action_inputPasswordFragment_to_inputAmountFragment;
break;
case PRE_AUTH_COMPLETE:
inputPasswordViewModel.passwordType.setValue(InputPasswordType.SYSTEM);

View File

@ -201,7 +201,7 @@ public class InputRRNFragment extends DataBindingFragment {
private void checkRRNPreAuthVoid(boolean isECR) {
rrnViewModel.searchPayDetail(cardNo,rrnNo).observe(getViewLifecycleOwner(), new Observer<PayDetail>() {
rrnViewModel.searchPayDetailPreCancel(cardNo,rrnNo).observe(getViewLifecycleOwner(), new Observer<PayDetail>() {
@Override
public void onChanged(PayDetail payDetail) {
if(payDetail != null) {
@ -220,11 +220,13 @@ public class InputRRNFragment extends DataBindingFragment {
newPay = transProcessViewModel.getPayDetail();
}
newPay.setReferNo(rrnNo);
if(sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_FULL_VOID) {
newPay.setAmount(payDetail.getAmount());
}
}
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountFormat(payDetail.getAmount()));
newPay.setTradeDateTime(payDetail.getTradeDateTime());
if(isEmvTrans()) {
@ -243,6 +245,8 @@ public class InputRRNFragment extends DataBindingFragment {
} else {
LogUtil.d(TAG,"PreAuth Cancel RRN not found!");
terminateProcess(isECR);
}
@ -311,7 +315,9 @@ public class InputRRNFragment extends DataBindingFragment {
}
newPay.setReferNo(rrnNo);
newPay.setApprovalCode(payDetail.getApprovalCode());
newPay.setTradeTime(payDetail.getTradeTime());
newPay.setTradeDate(payDetail.getTradeDate());
if(newPay.getAmount() > payDetail.getAmount()) {
newPay.setTradeAnswerCode("C1"); // ---> refer to BaseErrorCode class// ,
newPay.setTransType(sharedViewModel.transactionsType.getValue().name);
@ -475,15 +481,19 @@ public class InputRRNFragment extends DataBindingFragment {
}
private void refundProcessEmv(boolean isECR) {
PayDetail payDetail = emvTransactionProcessViewModel.getPayDetail();
payDetail.setReferNo(rrnNo);
rrnViewModel.searchPayDetail(cardNo, rrnNo).observe(getViewLifecycleOwner(), payDetail -> {
if (payDetail != null && isCurrentBatchUnsettled(payDetail)) {
terminatedTransRefund(isECR);
return;
}
PayDetail newPay = emvTransactionProcessViewModel.getPayDetail();
newPay.setReferNo(rrnNo);
emvTransactionProcessViewModel.setPayDetail(newPay);
emvTransactionProcessViewModel.setPayDetail(payDetail);
sharedViewModel.rrNNo.setValue(rrnNo);
callNextScreen();
sharedViewModel.rrNNo.setValue(rrnNo);
callNextScreen();
});
}
private void checkRRNRefund(boolean isECR) {
@ -514,6 +524,11 @@ public class InputRRNFragment extends DataBindingFragment {
LogUtil.d(TAG,"Trans Type:"+payDetail.getTransactionType());
if (isCurrentBatchUnsettled(payDetail)) {
terminatedTransRefund(isECR);
return;
}
if(payDetail.getTransactionType() != TransactionsType.SALE.value) {
terminatedTransRefund(isECR);
return;
@ -522,7 +537,7 @@ public class InputRRNFragment extends DataBindingFragment {
String date = payDetail.getTransDate();
String time = payDetail.getTransTime();
if(checkRefund(configTime,date,time)) {
// if(checkRefund(configTime,date,time)) {
PayDetail newPay = transProcessViewModel.getPayDetail();
newPay.setReferNo(rrnNo);
@ -533,11 +548,12 @@ public class InputRRNFragment extends DataBindingFragment {
callNextScreen();
} else {
terminatedTransRefund(isECR);
}
// } else {
// terminatedTransRefund(isECR);
// }
} else {
// terminatedTransRefund(isECR);
PayDetail newPay = transProcessViewModel.getPayDetail();
newPay.setReferNo(rrnNo);
@ -554,6 +570,17 @@ public class InputRRNFragment extends DataBindingFragment {
}
private boolean isCurrentBatchUnsettled(PayDetail payDetail) {
if (payDetail == null) {
return false;
}
String currentBatch = SystemParamsOperation.getInstance().getCurrentBatchNum();
String batchNo = payDetail.getBatchNo();
return batchNo != null
&& batchNo.equals(currentBatch)
&& !payDetail.isSettle;
}
private void terminatedTransRefund(boolean isECR) {
showDeclineDialog(getResourceString(R.string.txt_refund_not_allow));
if (isECR) {

View File

@ -116,7 +116,14 @@ public class InputTraceNoFragment extends DataBindingFragment {
clearInputView();
setToolBarTitleWithBackIcon(getResourceString(R.string.title_trace_number));
TransactionsType currentTransType = sharedViewModel.transactionsType.getValue();
if (currentTransType == TransactionsType.VOID
|| currentTransType == TransactionsType.TIP_ADJUSTMENT
|| currentTransType == TransactionsType.PRE_AUTH_COMPLETE_VOID) {
setToolBarTitleWithBackIcon(getResourceString(R.string.title_invoice_number));
} else {
setToolBarTitleWithBackIcon(getResourceString(R.string.title_trace_number));
}
if (sharedViewModel.isEcr.getValue() != null) {
if (sharedViewModel.isEcr.getValue()) {
@ -151,10 +158,9 @@ public class InputTraceNoFragment extends DataBindingFragment {
configTime = "23:00";
}
LogUtil.d(TAG,"Input trace :"+traceNo);
LogUtil.d(TAG,"Input invoice :"+traceNo);
// voidViewModel.getGenericVoidTransactions(transType,POSUtil.getInstance().formatTrace(traceNo),isEmv).observe(getViewLifecycleOwner(), new Observer<PayDetail>() {
voidViewModel.getVoidTrans(transType,POSUtil.getInstance().formatTrace(traceNo)).observe(getViewLifecycleOwner(), new Observer<PayDetail>() {
voidViewModel.getVoidTransByInvoice(transType, POSUtil.getInstance().formatTrace(traceNo)).observe(getViewLifecycleOwner(), new Observer<PayDetail>() {
@Override
public void onChanged(PayDetail payDetail) {
@ -164,7 +170,7 @@ public class InputTraceNoFragment extends DataBindingFragment {
LogUtil.d(TAG,"transactions trace :"+payDetail.getVoucherNo());
if(POSUtil.getInstance().checkVoid(configTime,date,time)) {
// if(POSUtil.getInstance().checkVoid(configTime,date,time)) {
if(payDetail.getIsCanceled()) {
clearInputView();
@ -173,19 +179,27 @@ public class InputTraceNoFragment extends DataBindingFragment {
}
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountSeparatorFormat(payDetail.getAmount()));
if(sharedViewModel.hostType.getValue() == HostType.MPU) {
sharedViewModel.isEmv.setValue(false);
transProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
transProcessViewModel.setOldTransPayDetail(payDetail);
transProcessViewModel.setPayDetail(payDetail);
boolean isMpu = "MPU".equals(payDetail.getAccountType());
if (isMpu) {
sharedViewModel.isEmv.setValue(false);
transProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
transProcessViewModel.setOldTransPayDetail(payDetail);
transProcessViewModel.setPayDetail(payDetail);
} else {
sharedViewModel.isEmv.setValue(true);
emvTransactionProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
emvTransactionProcessViewModel.setPayDetail(payDetail);
}
} else {
sharedViewModel.isEmv.setValue(true);
emvTransactionProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
emvTransactionProcessViewModel.setPayDetail(payDetail);
}
safeNavigateToRouteId();
}
// }
} else {
if (isEcr)
@ -299,7 +313,7 @@ public class InputTraceNoFragment extends DataBindingFragment {
if(sharedViewModel.hostType.getValue() == HostType.MPU) {
sharedViewModel.isEmv.setValue(false);
transProcessViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
transProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
transProcessViewModel.setOldTransPayDetail(payDetail);
transProcessViewModel.setPayDetail(payDetail);
} else {

View File

@ -17,7 +17,6 @@ import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.print.PaperRollStatusCallback;
import com.utsmyanmar.paylibs.print.PrintHelper;
import com.utsmyanmar.paylibs.print.PrintReceipt;
import com.utsmyanmar.paylibs.utils.PrintStatus;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
@ -28,7 +27,8 @@ import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
public class PrintReceiptFragment extends DataBindingFragment implements DataBindingFragment.BackPressCallback {
private SharedViewModel sharedViewModel;
private static final String TAG = PrintReceipt.class.getSimpleName();
private static final String TAG = PrintReceiptFragment.class.getSimpleName();
private boolean printerEnabled = true;
@Override
protected int currentId() {
@ -49,7 +49,6 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
@Override
protected void initViewModel() {
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
}
@Override
@ -61,6 +60,7 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
@Override
public void onResume() {
super.onResume();
printerEnabled = SystemParamsOperation.getInstance().getPrinterEnabled();
/*
* check TMS parameter here
@ -76,7 +76,8 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
setToolBarTitleWithoutBackIcon(getResourceString(R.string.title_print_receipt));
sharedViewModel.printReceiptButtons.setValue(0);
sharedViewModel.printReceiptButtons.setValue(printerEnabled ? 0 : 8);
sharedViewModel.printerDisabled.setValue(!printerEnabled);
PayDetail payDetail = sharedViewModel.payDetail.getValue();
@ -121,10 +122,16 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
printerEnabled = SystemParamsOperation.getInstance().getPrinterEnabled();
sharedViewModel.setPrintStatus(PrintStatus.FIRST_PRINT);
observePrintProcess();
if (!printerEnabled) {
sharedViewModel.setPrintStatus(PrintStatus.NOT_PRINT);
sharedViewModel.setVisibilityPrintReceiptButtons(8);
sharedViewModel.postPrintReceiptMsg("Receipt is saved as E-receipt!");
} else {
sharedViewModel.setPrintStatus(PrintStatus.FIRST_PRINT);
observePrintProcess();
}
setBackPressCallback(this);
}
@ -139,8 +146,11 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
case FIRST_PRINT:
sharedViewModel.postPrintReceiptMsg("Printing Receipt for Merchant");
startPrintProcess(true);
// sharedViewModel.postPrintReceiptMsg("Print Receipt for Customer?");
// sharedViewModel.setVisibilityPrintReceiptButtons(0);
break;
case FIRST_PRINT_DONE:
// sharedViewModel.postPrintReceiptMsg("Print Receipt for Customer?");
sharedViewModel.postPrintReceiptMsg("Confirm Print Receipt for Customer");
if(sharedViewModel.isEcr.getValue() != null &&
sharedViewModel.isEcr.getValue() &&
@ -172,6 +182,9 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
private void startPrintProcess(boolean isFirstPrint) {
if (!SystemParamsOperation.getInstance().getPrinterEnabled()) {
return;
}
PrintHelper.getInstance().checkPaperRollStatus(new PaperRollStatusCallback() {
@ -215,7 +228,6 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
@Override
public void onClickCancel() {
dismissPrinterAlertDialog();
}
@Override
public void onClickRetry() {
@ -248,16 +260,39 @@ public class PrintReceiptFragment extends DataBindingFragment implements DataBin
public class ClickEvent {
public void onConfirm() {
// PrintStatus printStatus = sharedViewModel.getPrintStatus().getValue();
//
// if(printStatus == PrintStatus.NOT_PRINT){
// sharedViewModel.setVisibilityPrintReceiptButtons(8);
// sharedViewModel.setPrintStatus(PrintStatus.FIRST_PRINT);
// }else if(printStatus == PrintStatus.FIRST_PRINT || printStatus == PrintStatus.FIRST_PRINT_DONE){
// sharedViewModel.setVisibilityPrintReceiptButtons(8);
// sharedViewModel.setPrintStatus(PrintStatus.SECOND_PRINT);
// }
sharedViewModel.setVisibilityPrintReceiptButtons(8);
//
sharedViewModel.setPrintStatus(PrintStatus.SECOND_PRINT);
}
public void onCancel() {
sharedViewModel.setVisibilityPrintReceiptButtons(8);
isCardInside();
// PrintStatus printStatus = sharedViewModel.getPrintStatus().getValue();
//
// if(printStatus == PrintStatus.NOT_PRINT){
// sharedViewModel.setPrintStatus(PrintStatus.FIRST_PRINT_DONE);
// }else{
// sharedViewModel.setVisibilityPrintReceiptButtons(8);
// isCardInside();
// }
}
public void onContinue(){
isCardInside();
}
}

View File

@ -1,18 +1,27 @@
package com.utsmm.kbz.ui.core_ui;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.nexgo.oaf.apiv3.device.reader.CardSlotTypeEnum;
import com.utsmm.kbz.config.data.model.CardTransactionType;
import com.utsmm.kbz.util.MockData;
import com.utsmm.kbz.util.ecr.CoreUtils;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TerminalUtil;
import com.utsmyanmar.checkxread.checkcard.CheckCardResultX;
import com.utsmyanmar.checkxread.model.CardDataX;
import com.utsmyanmar.checkxread.readcard.MAGXReadCard;
import com.utsmyanmar.checkxread.readcard.MPUXReadCard;
import com.utsmyanmar.checkxread.readcard.ReadCardResultX;
import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.checkxread.util.CardTypeX;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
@ -34,6 +43,8 @@ import com.utsmm.kbz.util.TransactionUtil;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.util.HashSet;
public class ProcessingCardFragment extends DataBindingFragment {
private static final String TAG = ProcessingCardFragment.class.getSimpleName();
private CardReadViewModel cardReadViewModel;
@ -41,6 +52,8 @@ public class ProcessingCardFragment extends DataBindingFragment {
private TransProcessViewModel transProcessViewModel;
private PinPadViewModel pinPadViewModel;
private EmvTransactionProcessViewModel emvTransactionViewModel;
private int routeId;
@Override
@ -94,7 +107,8 @@ public class ProcessingCardFragment extends DataBindingFragment {
private void checkCardTransactionType() {
switch (cardReadViewModel.getCardTransactionType()) {
case MPU: readMPUCard();
case MPU:
readMPUCard();
break;
case EMV: handlePreEmvProcess();
break;
@ -102,38 +116,65 @@ public class ProcessingCardFragment extends DataBindingFragment {
break;
case MAG: readMAGStripe(false,true);
break;
case MOCK: mockMPUCard();
break;
}
}
private void processMPU(){
if(sharedViewModel.getAmountExist().getValue() != null && !sharedViewModel.getAmountExist().getValue()) {
routeId = R.id.action_processingCardFragment_to_inputAmountFragment;
}
routeId = R.id.action_processingCardFragment_to_pinPadFragment;
safeNavigateToRouteId();
}
private void mockMPUCard() {
LogUtil.d(TAG,"initialize mock card data...");
sharedViewModel.isEmv.setValue(false);
routeId = R.id.action_processingCardFragment_to_pinPadFragment;
transProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
pinPadViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
CardDataX cardDataX = MockData.getInstance().generateMPUCard();
TradeData tradeData = TransactionUtil.getInstance().initMPUTransaction(cardDataX, CardTypeX.IC);
transProcessViewModel.setTradeData(tradeData);
pinPadViewModel.setTradeData(tradeData);
sharedViewModel.setCardDataExist(true);
safeNavigateToRouteId();
}
private void readMPUCard() {
cardReadViewModel.startReadXProcess(MPUXReadCard.getInstance(), new ReadCardResultX() {
@Override
public void onSuccess(CardDataX cardDataX) {
if(!TerminalUtil.getInstance().getBinList().isEmpty()) {
if(TerminalUtil.getInstance().getBinList().contains(cardDataX.getBin())) {
LogUtil.d(TAG," <= BIN found in list, switching to EMV process");
cardReadViewModel.cancelCheckCard();
initCheckCard();
return;
}
}
sharedViewModel.isEmv.setValue(false);
routeId = R.id.action_processingCardFragment_to_pinPadFragment;
transProcessViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
pinPadViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
transProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
pinPadViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
TradeData tradeData = TransactionUtil.getInstance().initMPUTransaction(cardDataX, CardTypeX.IC);
transProcessViewModel.setTradeData(tradeData);
pinPadViewModel.setTradeData(tradeData);
if(sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_PARTIAL_VOID ) {
sharedViewModel.set_errorFragmentMsg(getResourceString(R.string.txt_function_not_supported));
routeId = R.id.action_processingCardFragment_to_errorFragment;
}
// else if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND) {
// sharedViewModel.set_errorFragmentMsg(getResourceString(R.string.txt_card_not_supported));
// routeId = R.id.action_processingCardFragment_to_errorFragment;
//
// }
sharedViewModel.setCardDataExist(true);
if(sharedViewModel.getAmountExist().getValue() != null && !sharedViewModel.getAmountExist().getValue()) {
if(sharedViewModel.getTransMenu().getValue() != TransMenu.PRE_AUTH_FULL_VOID
&& sharedViewModel.getAmountExist().getValue() != null
&& !sharedViewModel.getAmountExist().getValue()) {
routeId = R.id.action_processingCardFragment_to_inputAmountFragment;
}
@ -164,7 +205,7 @@ public class ProcessingCardFragment extends DataBindingFragment {
// pin required scenario
routeId = R.id.action_processingCardFragment_to_pinPadFragment;
transProcessViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
transProcessViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
pinPadViewModel.transType.postValue(sharedViewModel.transactionsType.getValue());
/* need to check card scheme according to card number --!^--- check below function*/
@ -188,16 +229,6 @@ public class ProcessingCardFragment extends DataBindingFragment {
transProcessViewModel.setTradeData(tradeData);
}
if(sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_PARTIAL_VOID ) {
// sharedViewModel.transMenu.postValue(null);
sharedViewModel.setTransMenu(null);
routeId = R.id.action_processingCardFragment_to_errorFragment;
}
// else if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND) {
// routeId = R.id.action_processingCardFragment_to_errorFragment;
// }
sharedViewModel.setCardDataExist(true);
if(sharedViewModel.getTransMenu().getValue() != TransMenu.PRE_AUTH_FULL_VOID && sharedViewModel.getAmountExist().getValue() != null && !sharedViewModel.getAmountExist().getValue()) {
@ -223,8 +254,11 @@ public class ProcessingCardFragment extends DataBindingFragment {
emvTransactionViewModel.transType.setValue(sharedViewModel.transactionsType.getValue());
if (SystemParamsOperation.getInstance().isEmvEnabled()) {
prepareEmvTransaction();
routeId = R.id.action_processingCardFragment_to_emvTransactionFragment;
} else {
routeId = R.id.action_processingCardFragment_to_errorFragment;
sharedViewModel.set_errorFragmentMsg(getResourceString(R.string.txt_please_enable_emv));
@ -245,6 +279,43 @@ public class ProcessingCardFragment extends DataBindingFragment {
}
private void initCheckCard() {
HashSet<CardSlotTypeEnum> allType = new HashSet<>();
allType.add(CardSlotTypeEnum.ICC1);
cardReadViewModel.startCheckXProcessNex(allType, 60, new CheckCardResultX() {
@Override
public void onSuccess(CardTypeX cardType, boolean isMPU) {
LogUtil.d(TAG,"**** on success *****");
LogUtil.d(TAG,"card type:"+cardType);
LogUtil.d(TAG,"is MPU:"+isMPU);
handlePreEmvProcess();
}
@Override
public void onError(int code, String message) {
LogUtil.d(TAG,"on error:"+code+" - message :"+message);
showDeclineDialog(message);
routeId = R.id.action_processingCardFragment_to_errorFragment;
safeNavigateToRouteId();
}
@Override
public void onCommError() {
LogUtil.d(TAG,"on Comm Error...");
ecrActionCancel(getResourceString(R.string.txt_cancel_trans));
showDeclineDialog("Chip not detected!");
routeId = R.id.action_processingCardFragment_to_errorFragment;
safeNavigateToRouteId();
}
});
}
private void prepareEmvTransaction() {
sharedViewModel.isEmv.postValue(true);
cardReadViewModel.cardTypeData.observe(getViewLifecycleOwner(), cardType -> {
@ -254,5 +325,22 @@ public class ProcessingCardFragment extends DataBindingFragment {
payDetail.setCardType(cardType);
emvTransactionViewModel.setTradeData(tradeData);
});
// TradeData tradeData = Params.newTrade(false);
// PayDetail payDetail = tradeData.getPayDetail();
//
// payDetail.setCardType(cardReadViewModel.cardTypeData.getValue());
// emvTransactionViewModel.setTradeData(tradeData);
}
private void ecrActionCancel(String msg) {
if (sharedViewModel.isEcr.getValue() != null) {
if (sharedViewModel.isEcr.getValue()) {
sharedViewModel.isEcr.postValue(false);
CoreUtils.getInstance(sharedViewModel).responseRejectMsg(msg);
sharedViewModel.isEcrFinished.postValue(true);
}
}
}
}

View File

@ -6,12 +6,24 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.navigation.NavController;
import androidx.navigation.NavDestination;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptCardRequest;
import com.utsmm.kbz.util.EReceiptUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TimeoutCallback;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.print.PrintUtils;
import com.utsmyanmar.paylibs.reversal.ReversalAction;
import com.utsmyanmar.paylibs.reversal.ReversalListener;
import com.utsmyanmar.paylibs.system.BaseErrorCode;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
@ -25,6 +37,8 @@ import com.utsmm.kbz.util.ecr.CoreUtils;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.text.DecimalFormat;
public class ProcessingFragment extends DataBindingFragment {
private static final String TAG = ProcessingFragment.class.getSimpleName();
@ -97,6 +111,17 @@ public class ProcessingFragment extends DataBindingFragment {
setToolBarTitleWithoutBackIcon(getResourceString(R.string.title_processing));
checkReversal();
// disableNavigationBar();
getNavController(Constants.NAV_HOST_ID).addOnDestinationChangedListener(listener);
}
private void startTransactionProcess() {
if(isEmvTrans()) {
startTransactionProcess(emvTransactionProcessViewModel);
} else {
@ -104,13 +129,55 @@ public class ProcessingFragment extends DataBindingFragment {
}
sharedViewModel.loadingMsg(getResourceString(R.string.txt_sending));
// disableNavigationBar();
getNavController(Constants.NAV_HOST_ID).addOnDestinationChangedListener(listener);
}
private void checkReversal() {
observeOnce(sharedViewModel.getLastReversalTransaction(), payDetail -> {
if (payDetail != null) {
TradeData tradeData = new TradeData();
tradeData.setPayDetail(payDetail);
LogUtil.d(TAG, "Sending Reversal!");
sharedViewModel.loadingMsg(getResourceString(R.string.txt_sending_reversal));
ReversalAction.getInstance().setData(tradeData).enqueue().startReversal(new ReversalListener() {
@Override
public void onSuccessReversal() {
LogUtil.d(TAG, "Reversal Success!");
payDetail.setIsNeedReversal(false);
sharedViewModel.updatePayDetail(payDetail);
delayFunctionCall(()->startTransactionProcess());
}
@Override
public void onNetworkFail(String msg) {
LogUtil.d(TAG, "Reversal Network Fail!" + msg);
startTransactionProcess();
}
@Override
public void onFailReversal(String msg) {
LogUtil.d(TAG, "Reversal Fail!" + msg);
startTransactionProcess();
}
});
} else {
LogUtil.d(TAG,"No reversal found!");
startTransactionProcess();
}
});
}
private <T> void observeOnce(LiveData<T> liveData, Observer<T> observer) {
liveData.observe(getViewLifecycleOwner(), new Observer<T>() {
@Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}
private boolean isEmvTrans() {
return sharedViewModel.isEmv.getValue() != null && sharedViewModel.isEmv.getValue();
}
@ -168,14 +235,19 @@ public class ProcessingFragment extends DataBindingFragment {
} else {
LogUtil.d(TAG,"Updated sharedViewmodel payDetail for non emv");
sharedViewModel.payDetail.setValue(transProcessViewModel.payDetailResult.getValue());
}
// if(sharedViewModel.payDetail.getValue() != null) {
// EReceiptRequest request = EReceiptUtil.getInstance().generateMPUReceipt(sharedViewModel.payDetail.getValue());
// sharedViewModel.pushReceipt(request);
// }
// transProcessViewModel.payDetailResult.observe(getViewLifecycleOwner(), payDetail -> sharedViewModel.payDetail.postValue(payDetail));
}
private void callNextScreen(){
updateData();
// updateData();
if (requiresSignature()) {
routeId = R.id.action_processingFragment_to_signatureFragment;
} else {
@ -203,7 +275,7 @@ public class ProcessingFragment extends DataBindingFragment {
LogUtil.d(TAG,"tradeAnswerCode: "+payDetail.getTradeAnswerCode());
// For now, let's check if it's a card transaction and successful
boolean isSuccessful = "00".equals(payDetail.getTradeAnswerCode());
boolean isSuccessful = "00".equals(payDetail.getTradeAnswerCode()) || "000".equals(payDetail.getTradeAnswerCode());
// You can customize this logic based on your business requirements
return isSuccessful && !isEmvTrans(); // Require signature for successful non-EMV transactions
@ -246,15 +318,43 @@ public class ProcessingFragment extends DataBindingFragment {
safeNavigateToRouteId();
}
private void handleAfterTransaction() {
if (SystemParamsOperation.getInstance().getDemoStatus()) {
delayFunctionCall(() -> {
sharedViewModel.dismissLoadingMsg();
callNextScreen();
}, 1500);
} else {
sharedViewModel.dismissLoadingMsg();
callNextScreen();
}
}
private void observeData(ProcessingTransaction processingTransaction) {
processingTransaction.getTransStatus().observe(getViewLifecycleOwner(), transResultStatus -> {
LogUtil.d(TAG,"Transaction RESULT :"+transResultStatus);
PayDetail payDetail = isEmvTrans()
? emvTransactionProcessViewModel.payDetailResult.getValue()
: transProcessViewModel.payDetailResult.getValue();
LogUtil.d(TAG, "PayDetail from VM: " + payDetail);
switch (transResultStatus) {
case FAIL:
sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, false, BaseErrorCode.getCode(payDetail.getTradeAnswerCode())));
handleAfterTransaction();
break;
case SUCCESS:
// sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, true, BaseErrorCode.getCode(payDetail.getTradeAnswerCode())));
// handleAfterTransaction();
if(requiresSignature()){
handleAfterTransaction();
}else{
sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, true, BaseErrorCode.getCode(payDetail.getTradeAnswerCode())));
handleAfterTransaction();
}
break;
case OFFLINE_SUCCESS:
LogUtil.d(TAG,"This was called!");
if(SystemParamsOperation.getInstance().getDemoStatus()) {
delayFunctionCall(()->{
// updateData();
@ -263,12 +363,14 @@ public class ProcessingFragment extends DataBindingFragment {
},1500);
} else {
// updateData();
sharedViewModel.dismissLoadingMsg();
callNextScreen();
}
break;
case OFFLINE_FAILURE:
sharedViewModel.pushReceipt(buildEReceiptCardReceipt(payDetail, false, BaseErrorCode.getCode(payDetail.getTradeAnswerCode())));
sharedViewModel.dismissLoadingMsg();
sharedViewModel.set_errorFragmentMsg(getResourceString(R.string.txt_offline_failure));
navigateToError();
@ -366,4 +468,125 @@ public class ProcessingFragment extends DataBindingFragment {
});
}
public static String mapTransactionType(int type) {
switch (type) {
case 1: // SALE
return "S";
case 2: // VOID SALE
return "V";
case 4: // REFUND
return "R";
case 5: // PRE-AUTH
return "P";
case 6: // PRE-AUTH VOID
return "PV";
case 7: // PRE-AUTH COMPLETE
return "PC";
case 8: // PRE-AUTH COMPLETE VOID
return "PCV";
case 9: // CASH OUT
return "CAV";
case 18: // TIP ADJUST
return "TA";
case 20: // QR PAYMENT
return "QR";
case 34: // QR REFUND
return "QRV";
default:
return "UNK";
}
}
private String mapCurrency(String currency) {
if (currency == null) return "MMK";
switch (currency) {
case "104": return "MMK";
case "840": return "USD";
case "764": return "THB";
case "702": return "SGD";
case "978": return "EUR";
default: return currency; // Already alphabetic? return as-is
}
}
private String formatDE43(String value) {
if (value == null) value = "";
if (value.length() > 40) {
return value.substring(0, 40);
}
return String.format("%-40s", value); // right pad with space
}
private EReceiptCardRequest buildEReceiptCardReceipt(PayDetail payDetail, boolean isSuccess, String reason){
//current timestamp
String currentTimestamp = new java.text.SimpleDateFormat(
"MMddHHmmss", java.util.Locale.getDefault()
).format(new java.util.Date());
//DeviceInfo
String serial = TMSUtil.getInstance().getSerialNumber();
String appId = requireActivity().getPackageName();
//TerminalInfo
String terminalId = SystemParamsOperation.getInstance().getTerminalId();
String merchantId = SystemParamsOperation.getInstance().getMerchantId();
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
String rawName = PrintUtils.getInstance()
.getCardHolderName(payDetail.getCardHolderName());
String secondLine = PrintUtils.getInstance()
.processNameSpaceSecondLine(rawName);
String firstLine = PrintUtils.getInstance()
.processNameSpaceFirstLine(rawName, secondLine);
String cardHolderName = firstLine +
(secondLine.isEmpty() ? "" : " " + secondLine);
EReceiptCardRequest request = new EReceiptCardRequest();
request.setDE2(PrintUtils.getInstance().maskCardNumberPciDss(payDetail.getCardNo()));
request.setDE3(mapTransactionType(payDetail.getTransactionType()));
request.setDE4(amount);
request.setDE7(currentTimestamp);
request.setDE11(payDetail.getVoucherNo());
// request.setDE22(mapTransactionType(payDetail.getTransactionType()));
request.setDE22(PrintUtils.getInstance().getEntryType(payDetail.getCardType()));
request.setDE37(payDetail.getReferNo());
request.setDE38(payDetail.getApprovalCode());
request.setDE39(isSuccess ? "A" : "E");
request.setDE41(terminalId);
request.setDE42(merchantId);
request.setDE49(mapCurrency(payDetail.getCurrencyCode()));
request.setSerial(serial);
request.setInvoiceNumber(payDetail.getInvoiceNo());
request.setAppId(appId);
request.setDescription(reason);
request.setBatchNumber(payDetail.batchNo);
request.setDE55(payDetail.getAppLabel());
request.setCardLabel(payDetail.getAccountType());
request.setDE14(PrintUtils.getInstance().getFormatExpDate(payDetail.getEXPDate()));
request.setDE43(formatDE43(cardHolderName));
request.setDE48(payDetail.getAID());
request.setDE115("");
return request;
}
}

View File

@ -9,14 +9,21 @@ import androidx.annotation.NonNull;
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;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.print.printx.PrintX;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.system.BaseErrorCode;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.print_utils.BitmapUtils;
public class SignatureFragment extends DataBindingFragment {
@ -40,8 +47,8 @@ public class SignatureFragment extends DataBindingFragment {
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_signature_view, BR.vm,sharedViewModel)
.addBindingParam(BR.click,new ClickEvent());
return new DataBindingConfig(R.layout.fragment_signature_view, BR.vm, sharedViewModel)
.addBindingParam(BR.click, new ClickEvent());
}
@Override
@ -80,14 +87,12 @@ public class SignatureFragment extends DataBindingFragment {
@Override
public void onSigned() {
LogUtil.d(TAG,"ON Signed !");
isSigned = true;
}
@Override
public void onClear() {
isSigned = false;
LogUtil.d(TAG,"ON Clear !");
}
});
}
@ -125,11 +130,27 @@ public class SignatureFragment extends DataBindingFragment {
setToolBarTitleWithoutBackIcon(getResourceString(R.string.signature_title));
}
private void callNextScreen(){
private void callNextScreen() {
routeId = R.id.action_signatureFragment_to_transactionResultFragment;
safeNavigateToRouteId();
}
private void updateData() {
PayDetail payDetail = sharedViewModel.payDetail.getValue();
payDetail.setESignHexData(BitmapUtils.bitmapToHexString(mSignaturePad.getSignatureBitmap()));
byte[] blob = BitmapUtils.bitmapToBytes(mSignaturePad.getSignatureBitmap());
payDetail.setESignBlob(blob);
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.updateESignBlob(payDetail.voucherNo, blob);
EReceiptCardRequest request = EReceiptHelper.buildEReceiptCardReceipt(
requireContext(),
payDetail,
true,
BaseErrorCode.getCode(payDetail.getTradeAnswerCode()),
null);
sharedViewModel.pushReceiptWithImage(request, blob);
}
public class ClickEvent {
@ -138,6 +159,8 @@ public class SignatureFragment extends DataBindingFragment {
// Handle digital signature
if (isSigned) {
sharedViewModel.signBitmap = mSignaturePad.getSignatureBitmap();
updateData();
LogUtil.d(TAG, "Digital signature confirmed and stored");
callNextScreen();
} else {
@ -147,7 +170,6 @@ public class SignatureFragment extends DataBindingFragment {
// Handle manual signature on paper
sharedViewModel.signBitmap = null; // No digital signature
LogUtil.d(TAG, "Manual signature on paper selected - no digital signature stored");
showSingleInfoDialog("Please sign on the printed receipt manually and proceed.");
callNextScreen();
}
}

View File

@ -21,7 +21,7 @@ public class TimeoutFragment extends DataBindingFragment {
private SharedViewModel sharedViewModel;
private int resultTimeOut = 3;
private int resultTimeOut = 5;
private CountDownTimer countDownTimer;
private int routeId;

View File

@ -8,7 +8,10 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.MalformedJsonException;
import com.utsmm.kbz.MainActivity;
import com.utsmm.kbz.util.LocationUtil;
import com.utsmyanmar.baselib.emv.EmvParamOperation;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.sirius.SiriusError;
@ -26,6 +29,7 @@ import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.print.PaperRollStatusCallback;
import com.utsmyanmar.paylibs.print.PrintHelper;
import com.utsmyanmar.paylibs.print.printx.PrintXStatus;
import com.utsmyanmar.paylibs.utils.PrintStatus;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
@ -42,18 +46,19 @@ import java.io.IOException;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import retrofit2.HttpException;
import com.utsmyanmar.paylibs.utils.LogUtil;
// Temporarily disabled Hilt
// @AndroidEntryPoint
@AndroidEntryPoint
public class TransactionResultFragment extends DataBindingFragment implements DataBindingFragment.BackPressCallback {
private static final String TAG = TransactionResultFragment.class.getSimpleName();
private TransProcessViewModel transProcessViewModel;
private SharedViewModel sharedViewModel;
private static final int resultTimeOut = 1;
private static final int resultTimeOut = 3;
private int routeId;
private final RxUtil<SiriusResponse> siriusRxUtil = new RxUtil<>();
@ -91,7 +96,6 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
@Override
public void onStart() {
super.onStart();
}
@Override
@ -111,7 +115,8 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
}
if (resultStr.equals(Constant.ANSWER_CODE_ACCEPT) || resultStr.equals(Constant.ANSWER_CODE_APPROVED)) {
/* ISO 8583 RESPONSE */
if(sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.SETTLEMENT.value) {
if(sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.SETTLEMENT.value
&& sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.MMQR_SETTLEMENT.value) {
if(SystemParamsOperation.getInstance().isAlertSound()) {
startSound(getResourceString(R.string.txt_audio_thank_you));
@ -119,7 +124,7 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
}
setToolBarTitleWithoutBackIcon(getResourceString(R.string.txt_title_trans_result));
} else if(qrStatus == 1 || qrStatus == -1 || qrStatus == 2 || qrStatus == 3 ) {
/* WAVE QR STATUS */
/* QR STATUS */
setToolBarTitleWithoutBackIcon(getResourceString(R.string.txt_title_qr_pay));
} else {
setToolBarTitleWithoutBackIcon(getResourceString(R.string.txt_title_error));
@ -146,11 +151,15 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
PayDetail payDetail = sharedViewModel.payDetail.getValue();
if (payDetail == null) {
navigateToMainScreen();
return;
}
((MainActivity) requireActivity()).getLocation();
SiriusRequest siriusReq = TMSUtil.getInstance().generateRequestParams(
getContext(),
payDetail.getTransType(),
@ -159,16 +168,23 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
TransactionsType transactionType = sharedViewModel.transactionsType.getValue();
if (isNonApprovedTrade(payDetail) && isNonWavepayTransaction(transactionType)) {
startPrintProcess(false);
if (isNonApprovedTrade(payDetail) && isNonQRPayTransaction(transactionType)) {
if (SystemParamsOperation.getInstance().getPrinterEnabled()) {
startPrintProcess(false);
}
isCardInside();
} else if (transactionType == TransactionsType.SETTLEMENT) {
} else if (transactionType == TransactionsType.SETTLEMENT || transactionType == TransactionsType.MMQR_SETTLEMENT) {
handleSettlementTransaction(siriusReq);
} else if (isWavePayNonSuccessTransaction(transactionType, payDetail)) {
startPrintProcess(false);
} else if (isQRPayNonSuccessTransaction(transactionType, payDetail)) {
// startPrintProcess(false);
if(transactionType == TransactionsType.MMQR_REFUND){
showDeclineDialog("QR Refund Failed! \n" + payDetail.getTradeResultDes());
}else{
showDeclineDialog("QR Payment Failed! \n" + payDetail.getTradeResultDes());
}
navigateToMainScreen();
} else if (isWavePaySuccessTransaction(transactionType, payDetail)) {
handleWavePaySuccessTransaction(payDetail, siriusReq);
} else if (isQRPaySuccessTransaction(transactionType, payDetail)) {
handleQRPaySuccessTransaction(payDetail, siriusReq);
} else {
handleDefaultTransaction(payDetail, siriusReq);
}
@ -182,32 +198,37 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
!payDetail.getTradeAnswerCode().equals(Constant.ANSWER_CODE_ACCEPT);
}
private boolean isNonWavepayTransaction(TransactionsType transactionType) {
private boolean isNonQRPayTransaction(TransactionsType transactionType) {
return transactionType != TransactionsType.MMQR &&
transactionType != TransactionsType.MMQR_INQUIRY_STATUS &&
transactionType != TransactionsType.MMQR_REFUND;
}
private boolean isWavePayNonSuccessTransaction(TransactionsType transactionType, PayDetail payDetail) {
private boolean isQRPayNonSuccessTransaction(TransactionsType transactionType, PayDetail payDetail) {
return (transactionType == TransactionsType.MMQR && payDetail.getQrTransStatus() != 1) ||
(transactionType == TransactionsType.MMQR_INQUIRY_STATUS && payDetail.getQrTransStatus() != 1) ||
(transactionType == TransactionsType.MMQR_REFUND && payDetail.getQrTransStatus() != 1);
}
private boolean isWavePaySuccessTransaction(TransactionsType transactionType, PayDetail payDetail) {
private boolean isQRPaySuccessTransaction(TransactionsType transactionType, PayDetail payDetail) {
return (transactionType == TransactionsType.MMQR || transactionType == TransactionsType.MMQR_REFUND) &&
payDetail.getQrTransStatus() == 1;
}
private void handleSettlementTransaction(SiriusRequest siriusReq) {
if (!SystemParamsOperation.getInstance().getPrinterEnabled()) {
downloadParameters(siriusReq, TMSUpdate.UPDATE);
isCardInside();
return;
}
startPrintProcess(true);
downloadParameters(siriusReq, TMSUpdate.UPDATE);
showSuccessDialog(getResourceString(R.string.txt_configs_are_updated));
navigateToMainScreen();
// showSuccessDialog(getResourceString(R.string.txt_configs_are_updated));
// navigateToMainScreen();
}
private void handleWavePaySuccessTransaction(PayDetail payDetail, SiriusRequest siriusReq) {
private void handleQRPaySuccessTransaction(PayDetail payDetail, SiriusRequest siriusReq) {
SystemParamsOperation.getInstance().setLastSuccessTrnx(payDetail.getVoucherNo());
downloadParameters(siriusReq, TMSUpdate.CHECK);
navigateToPrintScreen();
@ -238,15 +259,12 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
try {
SiriusError siriusError = new Gson().fromJson(error.response().errorBody().string(), SiriusError.class);
LogUtil.d(TAG,siriusError.getMessage());
} catch (IOException ex) {
} catch (IOException | JsonSyntaxException ex) {
ex.printStackTrace();
}
} else {
LogUtil.d(TAG,getResourceString(R.string.txt_failure)+throwable.getMessage());
}
throwable.printStackTrace();
}
@ -314,7 +332,18 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
@Override
public void printerReady() {
if(isSettlement) {
sharedViewModel.startPrintProcessSettlement();
sharedViewModel.startPrintSettlement(new PrintXStatus() {
@Override
public void onSuccess() {
sharedViewModel.onSettlementResultDone.postValue(true);
isCardInside();
}
@Override
public void onFailure() {
handleForEmptyPaperRoll(isSettlement);
}
});
return;
}
sharedViewModel.startPrintProcess();
@ -322,26 +351,7 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
@Override
public void paperRollIsEmpty() {
cancelTimeout();
showPrinterAlertDialog(getResourceString(R.string.txt_paper_roll_not_ready), new DialogCallback() {
@Override
public void onClickCancel() {
dismissPrinterAlertDialog();
isCardInside();
}
@Override
public void onClickRetry() {
dismissPrinterAlertDialog();
startPrintProcess(isSettlement);
}
});
if(SystemParamsOperation.getInstance().isAlertSound()) {
startSound(getResourceString(R.string.txt_audio_printer_alert));
}
handleForEmptyPaperRoll(isSettlement);
}
@Override
@ -368,6 +378,30 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
});
}
private void handleForEmptyPaperRoll(boolean isSettle) {
cancelTimeout();
showPrinterAlertDialog(getResourceString(R.string.txt_paper_roll_not_ready), new DialogCallback() {
@Override
public void onClickCancel() {
dismissPrinterAlertDialog();
isCardInside();
}
@Override
public void onClickRetry() {
dismissPrinterAlertDialog();
startPrintProcess(isSettle);
}
});
if(SystemParamsOperation.getInstance().isAlertSound()) {
startSound(getResourceString(R.string.txt_audio_printer_alert));
}
}
private void alertPaperRoll(String title, String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext());
builder.setTitle(title)

View File

@ -25,6 +25,8 @@ import java.util.HashSet;
import javax.inject.Inject;
import io.reactivex.rxjava3.core.Single;
public class CardReadViewModel extends ViewModel {
@ -46,7 +48,6 @@ public class CardReadViewModel extends ViewModel {
public SingleLiveEvent<Integer> cardTypeData = new SingleLiveEvent<>();
public SingleLiveEvent<PayDetail> payDetail = new SingleLiveEvent<>();
public SingleLiveEvent<CardReadStatus> readStatus = new SingleLiveEvent<>();
public SingleLiveEvent<String> checkCardAlertMsg = new SingleLiveEvent<>();
@ -95,96 +96,6 @@ public class CardReadViewModel extends ViewModel {
checkCardAlertMsg.setValue(null);
}
// public void initCardReadProcess(int allType, int timeOutInSec, ReadCardResult readCardResult) {
// ReadCardProcess.getInstance().startReadCard(allType,"",timeOutInSec,false).onStartReadCardProcess(new CheckCardListener() {
// @Override
// public void onCheckCardSuccess(int cardType, MPUCardData mpuCardData, CardScheme cardScheme) {
//
// cardData.postValue(mpuCardData);
// cardSchemeData.postValue(cardScheme);
// cardTypeData.postValue(cardType);
// PayDetail payDetailData = new PayDetail();
// payDetailData.setCardNo(mpuCardData.getPan());
// payDetailData.setEXPDate(mpuCardData.getExp());
// payDetailData.setAccountType(cardScheme.name());
// payDetailData.setCardType(cardType);
// payDetailData.setCardHolderName(mpuCardData.getCardHolderName());
// CardInfo cardInfo = new CardInfo();
// MAGCardInfo magCardInfo = new MAGCardInfo();
// LogUtil.d(TAG,"track 2 : "+mpuCardData.getTrack2());
// magCardInfo.setTrack2Cipher(mpuCardData.getTrack2());
// cardInfo.setMAGCardInfo(magCardInfo);
// payDetailData.setCardInfo(cardInfo);
//
// payDetail.postValue(payDetailData);
//
// mainThreadHandler.post(readCardResult::onSuccess);
//// readCardResult.onSuccess();
//
// }
//
// @Override
// public void onCheckCardFail(int code, String message) {
//
// if(code == -2801) {
// mainThreadHandler.post(readCardResult::onCommunicationError);
//// readCardResult.onCommunicationError();
// } else {
// mainThreadHandler.post(() -> readCardResult.onError(code,message));
//// readCardResult.onError(code,message);
// }
//
// }
// });
// }
//
// public SingleLiveEvent<CardReadStatus> startReadProcess(int allType,int timeOutInSec){
//
// ReadCardProcess.getInstance().startReadCard(allType,"",timeOutInSec,false).onStartReadCardProcess(new CheckCardListener() {
// @Override
// public void onCheckCardSuccess(int cardType, MPUCardData mpuCardData, CardScheme cardScheme) {
// flag.postValue(true);
// cardData.postValue(mpuCardData);
// cardSchemeData.postValue(cardScheme);
// cardTypeData.postValue(cardType);
// PayDetail payDetailData = new PayDetail();
// payDetailData.setCardNo(mpuCardData.getPan());
// payDetailData.setEXPDate(mpuCardData.getExp());
// payDetailData.setAccountType(cardScheme.name());
// payDetailData.setCardType(cardType);
// payDetailData.setCardHolderName(mpuCardData.getCardHolderName());
// CardInfo cardInfo = new CardInfo();
// MAGCardInfo magCardInfo = new MAGCardInfo();
// LogUtil.d(TAG,"track 2 : "+mpuCardData.getTrack2());
// magCardInfo.setTrack2Cipher(mpuCardData.getTrack2());
// cardInfo.setMAGCardInfo(magCardInfo);
// payDetailData.setCardInfo(cardInfo);
//
// payDetail.postValue(payDetailData);
//
// readStatus.postValue(CardReadStatus.SUCCESS);
//
//
// }
//
// @Override
// public void onCheckCardFail(int code, String message) {
//
// readStatus.postValue(CardReadStatus.FAIL);
// // this action just need to do once because there's too many reason to get fail
// if(!oneTimeFlag){
// errorCode.postValue(code+":"+message);
// flag.postValue(false);
// cardSchemeData.postValue(CardScheme.UNK);
// oneTimeFlag = true;
// }
//
// }
// });
//
// return readStatus;
// }
public void resetOneTimeFlag(){
oneTimeFlag = false;
}
@ -193,19 +104,43 @@ public class CardReadViewModel extends ViewModel {
NexGoSDK.getInstance().cancelCheckCard();
}
// public void checkCard(){
// int allType = AidlConstants.CardType.NFC.getValue() | AidlConstants.CardType.IC.getValue() | AidlConstants.CardType.MAGNETIC.getValue();
// ReadCardProcess.getInstance().startReadCard(allType,"",60,false).onStartReadCardProcess(new CheckCardListener() {
// @Override
// public void onCheckCardSuccess(int cardType, MPUCardData mpuCardData, CardScheme cardScheme) {
// System.out.println("Success");
// }
//
// @Override
// public void onCheckCardFail(int code, String message) {
//
// System.out.println("Failed");
// }
// });
// }
/**
* Properly cleanup card reading operations with sequential steps
*/
public void performCompleteCleanup(Runnable onCleanupComplete) {
// Step 1: Cancel check card operations
NexGoSDK.getInstance().cancelCheckCard();
// Step 2: Cancel X process operations
cancelCheckXProcess();
// Step 3: Reset internal flags and state
resetOneTimeFlag();
resetUI();
// Step 4: Wait for SDK to be ready before proceeding
if (onCleanupComplete != null) {
waitForSDKReady(onCleanupComplete, 0);
}
}
/**
* Wait for SDK to be ready with timeout
*/
private void waitForSDKReady(Runnable callback, int retryCount) {
final int MAX_RETRIES = 10; // Max 2 seconds (10 * 200ms)
if (NexGoSDK.getInstance().isSDKReady() || retryCount >= MAX_RETRIES) {
// SDK is ready or we've reached max retries
if (retryCount >= MAX_RETRIES) {
// Log warning if we reached max retries
android.util.Log.w(TAG, "SDK readiness check timed out after " + (MAX_RETRIES * 200) + "ms");
}
callback.run();
} else {
// Wait a bit more and check again
mainThreadHandler.postDelayed(() -> waitForSDKReady(callback, retryCount + 1), 200);
}
}
}

View File

@ -1,460 +0,0 @@
package com.utsmm.kbz.ui.core_viewmodel;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import androidx.lifecycle.ViewModel;
import com.sunmi.pay.hardware.aidl.bean.CardInfo;
import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2;
import com.sunmi.pay.hardware.aidlv2.bean.EMVTransDataV2;
import com.sunmi.pay.hardware.aidlv2.emv.EMVListenerV2;
import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.system.SingleLiveEvent;
import com.utsmyanmar.paylibs.utils.core_utils.KernelDataProcessUtil;
import com.utsmm.kbz.MyApplication;
import com.utsmm.kbz.util.enums.EmvReadStatus;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
/*
* Currently this class is not used
* */
@HiltViewModel
public class EmvReadCardViewModel extends ViewModel {
private static final String TAG = EmvReadCardViewModel.class.getSimpleName();
private final Repository repository;
public SingleLiveEvent<String> cardNum = new SingleLiveEvent<>();
public SingleLiveEvent<String> expDate = new SingleLiveEvent<>();
public SingleLiveEvent<EmvReadStatus> resultStatus = new SingleLiveEvent<>();
public SingleLiveEvent<List<EMVCandidateV2>> emvList = new SingleLiveEvent<>();
public SingleLiveEvent<Boolean> isButtonVisible = new SingleLiveEvent<>();
public SingleLiveEvent<String> emvCardType = new SingleLiveEvent<>();
public SingleLiveEvent<String> errorCode = new SingleLiveEvent<>();
private String mTag9F06Value;
public TradeData mTradeData;
public PayDetail mPayDetail;
private EMVOptV2 mEMVOptV2;
private PinPadOptV2 mPinPadOptV2;
private static final int CHECK_CARD_FAIL = 1;
private static final int CHECK_CARD_SUCCESS = 2;
private static final int CHECK_CARD_MAG = 3;
private static final int CHECK_CARD_NFC = 4;
private static final int CHECK_CARD_IC = 5;
private static final int EMV_APP_SELECT = 6;
private static final int EMV_CONFIRM_CARD_NO = 7;
private Looper mLooper = Looper.getMainLooper();
private final Handler mHandler = new Handler(mLooper) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case CHECK_CARD_FAIL:
String hint = (String) msg.obj;
resultStatus.postValue(EmvReadStatus.FAIL);
// checkCardFail(msg.arg1, hint);
break;
case EMV_APP_SELECT:
List<EMVCandidateV2> list = ( List<EMVCandidateV2> ) msg.obj;
emvList.postValue(list);
resultStatus.postValue(EmvReadStatus.SELECT_APP);
case CHECK_CARD_SUCCESS:
getCardInfo();
getF055Data();
terminateTrans();
resultStatus.postValue(EmvReadStatus.SUCCESS);
break;
case EMV_CONFIRM_CARD_NO:
getCardInfo();
getF055Data();
terminateTrans();
resultStatus.postValue(EmvReadStatus.SUCCESS);
try {
mEMVOptV2.importCardNoStatus(0);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
case CHECK_CARD_MAG:
Bundle bundle = (Bundle) msg.obj;
// handleMagCard(bundle);
break;
case CHECK_CARD_NFC:
// handleNFCCard();
break;
case CHECK_CARD_IC:
// handleICCard();
break;
}
}
};
public void terminateTrans(){
try {
mEMVOptV2.abortTransactProcess();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void setEmvAppSelect(int position){
try {
mEMVOptV2.importAppSelect(position);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Inject
public EmvReadCardViewModel(Repository repository){
this.repository = repository;
}
public void setTradeData(TradeData tradeData){
this.mTradeData = tradeData;
mPayDetail = mTradeData.getPayDetail();
}
public TradeData getTradeData(){
return mTradeData;
}
public void startProcess(){
initView();
initEmvTlvData();
transactProcess();
}
private void initView(){
mEMVOptV2 = MyApplication.getInstance().mEMVOptV2;
mPinPadOptV2 = MyApplication.getInstance().mPinPadOptV2;
}
private void initEmvTlvData() {
try {
// set PayWave(Visa) tlv data
String[] tagsPayWave = {"DF8124", "DF8125", "DF8126"};
String[] valuesPayWave = {"999999999999", "999999999999", "000000000000"};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYWAVE, tagsPayWave, valuesPayWave);
// set JCB Wave tlv data
String[] tagsJCB = {"9F53", "DF8161"};
String[] valuesJCB = {"708000", "7F00"};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_JCB, tagsJCB, valuesJCB);
// set PayPass(MasterCard) tlv data
String[] tagsPayPass = {"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C",
"DF8123", "DF8124", "DF8125", "DF8126",
"DF811B", "DF811D", "DF8122", "DF8120", "DF8121"};
String[] valuesPayPass = {"E0", "F8", "F8", "E8", "00", "00",
"000000000000", "000000100000", "999999999999", "000000100000",
"30", "02", "0000000000", "000000000000", "000000000000"};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass);
// set AMEX(AmericanExpress) tlv data
String[] tagsAE = {"9F6D", "9F6E", "9F33", "9F35", "DF8168", "DF8167", "DF8169", "DF8170"};
String[] valuesAE = {"C0", "D8E00000", "E0E888", "22", "00", "00", "00", "60"};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_AE, tagsAE, valuesAE);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void transactProcess() {
// LogUtil.e(TAG, "***************************************************************");
// LogUtil.e(TAG, "****************************Start Process**********************");
// LogUtil.e(TAG, "***************************************************************");
//
// LogUtil.e(TAG, "transactProcess");
try {
mEMVOptV2.initEmvProcess();
EMVTransDataV2 emvTransData = new EMVTransDataV2();
emvTransData.amount = "1000";
// if(mPayDetail.cardType == AidlConstantsV2.CardType.NFC.getValue()){
// emvTransData.flowType = AidlConstantsV2.EMV.FlowType.TYPE_NFC_SPEEDUP;
// }else{
// emvTransData.flowType = AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD;
// }
emvTransData.flowType = 2;
emvTransData.cardType = mPayDetail.cardType;
mEMVOptV2.transactProcess(emvTransData, mEMVListener);
} catch (Exception e) {
resultStatus.postValue(EmvReadStatus.FAIL);
e.printStackTrace();
}
}
private void initEmvProcess() {
try {
// Set normal TLV data
String[] tags = {"5F2A", "5F36"};
String[] values = {"0104", "00"};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tags, values);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() {
@Override
public void onWaitAppSelect(List<EMVCandidateV2> appNameList, boolean isFirstSelect) throws RemoteException {
// LogUtil.e(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect);
mHandler.obtainMessage(EMV_APP_SELECT, appNameList).sendToTarget();
}
@Override
public void onAppFinalSelect(String tag9F06Value) throws RemoteException {
// LogUtil.d(TAG, "tag9F06Value:" + tag9F06Value);
mTag9F06Value = tag9F06Value;
initEmvProcess();
/* if (tag9F06Value != null && tag9F06Value.length() > 0) {
boolean visa = tag9F06Value.startsWith("A000000003");
boolean master = tag9F06Value.startsWith("A000000004");
boolean unionPay = tag9F06Value.startsWith("A000000333");
boolean jcb = tag9F06Value.startsWith("A000000065");
if (visa) {
emvCardType.postValue("VISA");
// VISA - PayWave
mPayDetail.setAccountType("Visa");
LogUtil.d(TAG, "detect VISA card");
String[] tagsPayWave = {
// "DF8124", "DF8125", "DF8126"
"9F33"
};
String[] valuesPayWave = {
// "999999999999", "999999999999", "000000000000"
"002888"
};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tagsPayWave, valuesPayWave);
} else if (master) {
emvCardType.postValue("MASTER");
// MasterCard - PayPass
mPayDetail.setAccountType("Master");
LogUtil.d(TAG, "detect MasterCard card");
// String[] tagsPayPass = {
// "DF8118", "DF8119", "DF811F", "DF811E",
// "DF8123", "DF8124", "DF8125", "DF8126",
// "DF811B", "DF811D", "DF8122", "DF8120", "DF8121"
// };
// String[] valuesPayPass = {
// "28", "08", "08", "10",
// "000000010000", "000000030000", "000000050000", "000000001000",
// "00", "00", "0000000000", "0000000000", "0000000000"
// };
String[] tagsPayPass = {
"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C",
"DF8123", "DF8124", "DF8125", "DF8126",
"DF811B", "DF811D", "DF8122", "DF8120", "DF8121"
};
String[] valuesPayPass = {
"E0", "F8", "F8", "E8", "00", "00",
"999999999999", "000000001000", "000000001000", "000000001000",
"30", "02", "0000000000", "000000000000", "000000000000"
};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass);
} else if (unionPay) {
emvCardType.postValue("UnionPay");
mPayDetail.setAccountType("UPI");
// UnionPay
LogUtil.d(TAG, "detect UnionPay card");
String[] tagsPayPass = {
"DF8120", "DF8121", "DF8122"
};
String[] valuesPayPass = {
"D84000A800", "DC4004F800", "0100000000"
};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tagsPayPass, valuesPayPass);
} else if(jcb) {
emvCardType.postValue("JCB");
mPayDetail.setAccountType("JCB");
// UnionPay
LogUtil.d(TAG, "detect JCB card");
}
}
if (AidlConstants.CardType.IC.getValue() == mPayDetail.cardType) {
String[] tags = {
"9F33", "9F09", "DF81FF"
};
String[] values = {
"6060C8", "0111", "01"
// "0008C8", "0111", "01"
};
mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tags, values);
}*/
mEMVOptV2.importAppFinalSelectStatus(0);
}
@Override
public void onConfirmCardNo(String cardNo) {
// LogUtil.e(TAG, "onConfirmCardNo cardNo:" + cardNo);
mHandler.sendEmptyMessage(EMV_CONFIRM_CARD_NO);
}
@Override
public void onRequestShowPinPad(int type, int remainTime) throws RemoteException {
// LogUtil.e(TAG, "requestShowPinPad type:" + type + " remainTime:" + remainTime);
mHandler.sendEmptyMessage(CHECK_CARD_SUCCESS);
}
@Override
public void onRequestSignature() throws RemoteException {
// LogUtil.e(TAG, "signatureStatus");
mEMVOptV2.importSignatureStatus(0);
}
@Override
public void onCertVerify(int certType, String certInfo) throws RemoteException {
// LogUtil.e(TAG, "onCertVerify certType:" + certType + " certInfo:" + certInfo);
mEMVOptV2.importCertStatus(0);
}
@Override
public void onOnlineProc() throws RemoteException {
// LogUtil.e(TAG, "onProcessEnd");
mHandler.sendEmptyMessage(CHECK_CARD_SUCCESS);
}
@Override
public void onCardDataExchangeComplete() throws RemoteException {
// LogUtil.e(TAG, "onCardDataExchangeComplete");
}
@Override
public void onConfirmationCodeVerified() throws RemoteException {
// LogUtil.e(TAG, "onConfirmationCodeVerified");
}
@Override
public void onRequestDataExchange(String s) throws RemoteException {
}
@Override
public void onTermRiskManagement() throws RemoteException {
}
@Override
public void onPreFirstGenAC() throws RemoteException {
}
@Override
public void onTransResult(int code, String desc) throws RemoteException {
/*
* Temporary Error Message
* */
String errorMsg = "Card read failed!";
errorCode.postValue(code+":"+errorMsg);
if (code != 0) {
if( code == -50011) {
mHandler.sendEmptyMessage(CHECK_CARD_SUCCESS);
} else {
mHandler.obtainMessage(CHECK_CARD_FAIL, code, code, desc).sendToTarget();
}
} else {
mHandler.sendEmptyMessage(CHECK_CARD_SUCCESS);
}
// LogUtil.e(TAG, "onTransResult code:" + code + " desc:" + desc);
// LogUtil.e(TAG, "***************************************************************");
// LogUtil.e(TAG, "****************************End Process************************");
// LogUtil.e(TAG, "***************************************************************");
}
};
private void getCardInfo() {
String[] tagList = {"5A", "5F24", "57", "5F34", "9F6B"};
byte[] outData = new byte[1024];
try {
int len = mEMVOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tagList, outData);
if (len > 0) {
byte[] data = new byte[len];
System.arraycopy(outData, 0, data, 0, len);
CardInfo cardInfo = KernelDataProcessUtil.getCardInfo(mPayDetail.cardType, data);
mTradeData = KernelDataProcessUtil.fillTradeDataInfo(cardInfo, mTradeData);
//display card data in layout
cardNum.postValue(mPayDetail.getCardNo());
expDate.postValue(mPayDetail.getEXPDate());
} else {
// LogUtil.e(Constant.TAG, "The data length of the card information is negative = " + len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void getF055Data() {
/* For Yoma */
/*Jun 6 Added 9F2A*/
String[] tagList={
"9F2A","9F26","9F27","9F10","9F37","9F36","95","9A","9C","9F02","5F2A","5F34","82","9F1A", //field 55 tag
"9F03","9F33","4F","9F08","9F34","9F35","9F1E","9F53","84","9F09","9F41", //field55 tag
"9F5B","8A","9B","9F6E","9F7C","9F4C","50","9F12","DF01","9F63","5F20","9F0B","9F42"
};
byte[] outData = new byte[2048];
try {
int len = mEMVOptV2.getTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL, tagList, outData);
if (len > 0) {
byte[] data = new byte[len];
System.arraycopy(outData, 0, data, 0, len);
mTradeData = KernelDataProcessUtil.readKernelData(data, mTradeData);
} else {
// LogUtil.e(Constant.TAG, "Get the data length of the 55 field as a negative number = " + len);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -16,6 +16,7 @@ import com.utsmyanmar.baselib.emv.EmvParamOperation;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.baselib.util.enums.EmvResultStatus;
import com.utsmyanmar.baselib.viewModel.EmvBaseViewModel;
import com.utsmyanmar.checkxread.util.CardTypeX;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.model.enums.TransCVM;
@ -77,13 +78,21 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
break;
}
try {
if (mPayDetail.PINCipher != null && !mPayDetail.PINCipher.isEmpty()) {
LogUtil.d("EmvTransactionProcessViewModel", "mPayDetail" + mPayDetail.PINCipher);
LogUtil.d("EmvTransactionProcessViewModel", "is offline pin entered " + isOfflinePinEntered);
if ((mPayDetail.PINCipher != null && !mPayDetail.PINCipher.isEmpty())) {
LogUtil.d("EmvTransactionProcessViewModel", "condition 1");
emvHandler.onSetPinInputResponse(true, false);
} else if (isOfflinePinEntered) {
} else if(isOfflinePinEntered){
LogUtil.d("EmvTransactionProcessViewModel", "condition 2");
emvHandler.onSetPinInputResponse(true, false);
} else {
emvHandler.onSetPinInputResponse(false, true);
}else {
LogUtil.d("EmvTransactionProcessViewModel", "condition 3");
emvHandler.onSetPinInputResponse(true, true);
}
if (transType.getValue() == TransactionsType.PRE_AUTH_COMPLETE || transType.getValue() == TransactionsType.PRE_AUTH_VOID || transType.getValue() == TransactionsType.REFUND) {
emvResultStatus.postValue(EmvResultStatus.ON_NEXT_SCREEN);
}
@ -161,7 +170,13 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
handleTransactionFail(msg.arg1, msg.obj.toString());
break;
case EMV_TRY_AGAIN:
emvResultStatus.postValue(EmvResultStatus.TRY_AGAIN);
emvResultStatus.postValue(EmvResultStatus.READ_CARD_RETRY);
break;
case EMV_INSERT_OR_MANUAL:
emvResultStatus.postValue(EmvResultStatus.INSERT_OR_MANUAL);
break;
case EMV_CARD_APP_BLOCK:
emvResultStatus.postValue(EmvResultStatus.CARD_APP_BLOCKED);
break;
case EMV_CONFIRM_CODE_VERIFY:
emvResultStatus.postValue(EmvResultStatus.CONFIRM_CODE_VERIFY);
@ -175,9 +190,16 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
case EMV_SUCCESS_OFFLINE:
transResult.postValue(TransResultStatus.OFFLINE_SUCCESS);
break;
case EMV_FAILURE_OFFLINE:
transResult.postValue(TransResultStatus.OFFLINE_FAILURE);
break;
case EMV_SEE_PHONE:
emvResultStatus.postValue(EmvResultStatus.SEE_PHONE);
break;
case EMV_OTHER_INTERFACE:
emvResultStatus.postValue(EmvResultStatus.OTHER_INTERFACE);
break;
}
}
@ -203,12 +225,13 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
} else if (mProcessStep == EMV_CONFIRM_CARD_NO) {
emvHandler.onSetConfirmCardNoResponse(false);
} else if (mProcessStep == EMV_CERT_VERIFY) {
// importCertStatus(1);
// importCertStatus(1);
} else if (mProcessStep == PIN_ERROR) {
emvHandler.onSetPinInputResponse(false, false);
} else if (mProcessStep == EMV_ONLINE_PROCESS) {
importOnlineProcess(SdkResult.Success, new EmvOnlineResultEntity());
} else if (mProcessStep == EMV_SIGNATURE) {
// emvHandler.
// importSignatureStatus(1);
} else if (mProcessStep == EMV_SHOW_PIN_PAD) {
emvHandler.onSetPinInputResponse(false, false);
@ -264,7 +287,11 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
/*Jun 15 2022*/
PayDetail payDetailRes = tradeData.getPayDetail();
payDetailRes.setInvoiceNo(SystemParamsOperation.getInstance().getIncrementInvoiceNum());
if (transType.getValue() == TransactionsType.VOID) {
payDetailRes.setInvoiceNo(mPayDetail.getInvoiceNo());
} else {
payDetailRes.setInvoiceNo(SystemParamsOperation.getInstance().getIncrementInvoiceNum());
}
LogUtil.d(TAG,"Transaction Operation Success: "+payDetailRes.getTradeAnswerCode());
LogUtil.d(TAG,"Transaction Type: "+payDetailRes.getTransactionType());
@ -288,6 +315,8 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
if(isCardLessTrans()) {
transResult.setValue(TransResultStatus.SUCCESS);
} else if(payDetailRes.getCardType() == CardTypeX.MANUAL.value) {
transResult.setValue(TransResultStatus.SUCCESS);
}
} else {
@ -295,6 +324,8 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
if(isCardLessTrans()) {
transResult.setValue(TransResultStatus.FAIL);
} else if(payDetailRes.getCardType() == CardTypeX.MANUAL.value) {
transResult.setValue(TransResultStatus.FAIL);
}
/*
* @Note
@ -361,13 +392,16 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
@Override
public void startDemoProcess() {
SystemParamsOperation.getInstance().getIncrementInvoiceNum();
if (transType.getValue() != TransactionsType.VOID) {
SystemParamsOperation.getInstance().getIncrementInvoiceNum();
}
PayDetail payDetailRes;
if (transType.getValue() == TransactionsType.VOID) {
payDetailRes = DemoUtil.getInstance().builtDemoVoid(mPayDetail);
payDetailRes.setInvoiceNo(mPayDetail.getInvoiceNo());
processVoidDB(payDetailRes);
} else if (transType.getValue() == TransactionsType.REFUND) {
@ -425,13 +459,14 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
public void processVoidDB(PayDetail payResult) {
mPayDetail.setIsCanceled(true);
updatePayDetail(mPayDetail);
payResult.setESignBlob(mPayDetail.getESignBlob());
repository.insertPayDetail(updateCurrentDateAndTime(payResult));
}
@Override
public void processPreVoidDb(PayDetail payResult) {
mPayDetail.setIsCanceled(true);
updatePayDetail(mPayDetail);
oldTransPayDetail.setIsCanceled(true);
updatePayDetail(oldTransPayDetail);
repository.insertPayDetail(updateCurrentDateAndTime(payResult));
}
@ -445,9 +480,11 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
if(oldTransPayDetail.getAmount() == payResult.getAmount()) {
oldTransPayDetail.setIsCanceled(true);
oldTransPayDetail.setAdjust(true);
updatePayDetail(oldTransPayDetail);
} else {
oldTransPayDetail.setIsCanceled(false);
oldTransPayDetail.setAdjust(true);
updatePayDetail(oldTransPayDetail);
}
repository.insertPayDetail(updateCurrentDateAndTime(payResult));
@ -492,9 +529,9 @@ public class EmvTransactionProcessViewModel extends EmvBaseViewModel implements
private void handleTransactionProcess() {
String cardNo = mPayDetail.getCardNo();
if (cardNo == null || TextUtils.equals(cardNo, "")) {
// if (cardNo == null || TextUtils.equals(cardNo, "")) {
getCardInfo();
}
// }
getPayWaveData();
getF055Data();
if(transType.getValue() == TransactionsType.PRE_AUTH_COMPLETE || transType.getValue() == TransactionsType.PRE_AUTH_VOID || transType.getValue() == TransactionsType.REFUND ) {

View File

@ -13,126 +13,109 @@ import java.util.Objects;
public class InputAmountViewModel extends ViewModel {
private static final String TAG = InputAmountViewModel.class.getSimpleName();
public MutableLiveData<String> inputAmountView = new MutableLiveData<>();
public SingleLiveEvent<String> invalidAmountMsg = new SingleLiveEvent<>();
public SingleLiveEvent<String> availableAmount = new SingleLiveEvent<>();
private static final int MAX_INT_DIGITS = 10;
private static final int MAX_DECIMAL_DIGITS = 2;
public InputAmountViewModel() {
inputAmountView.setValue("0");
}
private int countDecimalDigits(double number) {
String numberStr = Double.toString(number);
int decimalIndex = numberStr.indexOf('.');
return decimalIndex >= 0 ? numberStr.length() - decimalIndex - 1 : 0;
private String formatInteger(String value) {
if (TextUtils.isEmpty(value)) return "0";
double amt = Double.parseDouble(value);
DecimalFormat formatter = new DecimalFormat("#,###");
return formatter.format(amt);
}
public NumberKeyboard.KeyClickCallback onKeyClick() {
return new NumberKeyboard.KeyClickCallback() {
@Override
public void onNumClick(int keyNum) {
if (inputAmountView.getValue().length() == 15) {
return;
}
String current = inputAmountView.getValue();
if (current == null) return;
if (TextUtils.equals(inputAmountView.getValue(), "0")) {
inputAmountView.setValue(keyNum + "");
} else if (inputAmountView.getValue().contains(".")) {
StringBuilder inputAmount = new StringBuilder();
inputAmount.append(inputAmountView.getValue());
String raw = current.replace(",", "");
int dotIndex = inputAmount.indexOf(".");
if (raw.contains(".")) {
if(inputAmount.substring(dotIndex).length() > 0 && inputAmount.substring(dotIndex+1).length() == 2) {
String[] parts = raw.split("\\.");
String intPart = parts[0];
String decimalPart = parts.length > 1 ? parts[1] : "";
if (decimalPart.length() >= MAX_DECIMAL_DIGITS) {
return;
}
if (inputAmountView.getValue().contains(",")) {
inputAmountView.setValue(inputAmountView.getValue().replace(",", ""));
}
double amt = Double.parseDouble(inputAmountView.getValue());
if (countDecimalDigits(amt) < 2) {
inputAmountView.setValue(inputAmountView.getValue() + keyNum);
}
raw = raw + keyNum;
} else {
if (inputAmountView.getValue().contains(",")) {
inputAmountView.setValue(inputAmountView.getValue().replace(",", ""));
if (raw.length() >= MAX_INT_DIGITS) {
return;
}
inputAmountView.setValue(inputAmountView.getValue() + keyNum);
double amt = Double.parseDouble(inputAmountView.getValue());
DecimalFormat formatter = new DecimalFormat("#,###");
inputAmountView.setValue(formatter.format(amt));
if (TextUtils.equals(raw, "0")) {
raw = String.valueOf(keyNum);
} else {
raw = raw + keyNum;
}
}
if (raw.contains(".")) {
String[] parts = raw.split("\\.");
String formattedInt = formatInteger(parts[0]);
String decimal = parts.length > 1 ? parts[1] : "";
inputAmountView.setValue(formattedInt + "." + decimal);
} else {
inputAmountView.setValue(formatInteger(raw));
}
}
@Override
public void onDelClick() {
if (Objects.requireNonNull(inputAmountView.getValue()).contains(".")) {
return;
}
if (inputAmountView.getValue().length() == 15) {
return;
}
if (TextUtils.equals(inputAmountView.getValue(), "0")) {
String current = inputAmountView.getValue();
if (current == null) return;
if (current.contains(".")) return;
if (TextUtils.equals(current, "0")) {
inputAmountView.setValue("0.");
} else {
inputAmountView.setValue(inputAmountView.getValue() + ".");
inputAmountView.setValue(current + ".");
}
}
@Override
public void onCleanClick() {
if (inputAmountView.getValue() != null) {
if (inputAmountView.getValue().contains(".")) {
StringBuilder inputAmount = new StringBuilder();
inputAmount.append(inputAmountView.getValue());
inputAmount.deleteCharAt(inputAmount.length() - 1);
inputAmountView.setValue(inputAmount.toString());
} else if (!inputAmountView.getValue().equals("0")) {
StringBuilder inputAmount = new StringBuilder();
inputAmount.append(inputAmountView.getValue());
if (inputAmount.length() > 4) {
if(inputAmount.toString().contains(",")) {
int commaIndex = inputAmount.indexOf(",");
inputAmount.deleteCharAt(commaIndex);
} else {
inputAmount.deleteCharAt(inputAmount.length() - 1);
}
inputAmountView.setValue(inputAmount.toString());
String current = inputAmountView.getValue();
if (current == null) return;
String raw = current.replace(",", "");
// inputAmount.deleteCharAt(inputAmount.toString().length() - 4);
// if (inputAmount.charAt(inputAmount.toString().length() - 4) == ',') {
// inputAmount.deleteCharAt(inputAmount.toString().length() - 4);
// }
// inputAmountView.setValue(inputAmount.toString());
} else if(inputAmount.length() == 1) {
inputAmountView.setValue("0");
} else {
StringBuilder currentAmount = new StringBuilder();
currentAmount.append(inputAmountView.getValue());
currentAmount.deleteCharAt(currentAmount.length() - 1);
inputAmountView.setValue(currentAmount.toString());
}
if (raw.length() <= 1) {
inputAmountView.setValue("0");
return;
}
}
raw = raw.substring(0, raw.length() - 1);
if (raw.contains(".")) {
String[] parts = raw.split("\\.");
String formattedInt = formatInteger(parts[0]);
String decimal = parts.length > 1 ? parts[1] : "";
inputAmountView.setValue(formattedInt + "." + decimal);
} else {
inputAmountView.setValue(formatInteger(raw));
}
}
};
}
}

View File

@ -41,6 +41,12 @@ public class InputRRNViewModel extends ViewModel {
return repository.searchPayDetail(cardNo,rrn);
}
public LiveData<PayDetail> searchPayDetailPreCancel(String cardNo,String rrn) {
return repository.searchPayDetailPreCancel(cardNo,rrn);
}
public NumberKeyboard.KeyClickCallback onKeyClick(){
return new NumberKeyboard.KeyClickCallback() {

View File

@ -1,18 +1,28 @@
package com.utsmm.kbz.ui.core_viewmodel;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.os.Looper;
import android.util.Log;
import android.widget.ListView;
import dagger.hilt.android.lifecycle.HiltViewModel;
import androidx.lifecycle.LiveData;
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;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.baselib.util.TerminalUtil;
import com.utsmyanmar.ecr.data.TransType;
import com.utsmyanmar.ecr.data.model.Transactions;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.QRSettleData;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.print.printx.PrintXStatus;
import com.utsmyanmar.paylibs.system.SingleLiveEvent;
@ -26,9 +36,15 @@ import com.utsmyanmar.paylibs.utils.enums.HostType;
import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import retrofit2.HttpException;
import com.utsmyanmar.paylibs.utils.LogUtil;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.inject.Inject;
@HiltViewModel
@ -54,15 +70,16 @@ public class SharedViewModel extends ViewModel {
public SingleLiveEvent<String> processCode = new SingleLiveEvent<>();
public SingleLiveEvent<PayDetail> payDetail = new SingleLiveEvent<>();
public SingleLiveEvent<List<PayDetail>> payDetails = new SingleLiveEvent<>();
public SingleLiveEvent<PrintStatus> printStatus = new SingleLiveEvent<>();
public SingleLiveEvent<String> merchantName = new SingleLiveEvent<>();
public SingleLiveEvent<String> transactionName = new SingleLiveEvent<>();
/*Apr 28 2022*/
public SingleLiveEvent<List<PayDetail>> payDetailList = new SingleLiveEvent<>();
// In SharedViewModel
public MutableLiveData<Boolean> onSettlementResultDone = new MutableLiveData<>(false);
/*May 16 2022*/
public SingleLiveEvent<Boolean> isEcr = new SingleLiveEvent<>();
@ -104,6 +121,8 @@ public class SharedViewModel extends ViewModel {
public SingleLiveEvent<HostType> hostType = new SingleLiveEvent<>();
public MutableLiveData<Integer> printReceiptButtons = new MutableLiveData<>(0);
public SingleLiveEvent<Boolean> printerDisabled = new SingleLiveEvent<>();
public SingleLiveEvent<Boolean> suppressReprintButtons = new SingleLiveEvent<>();
public SingleLiveEvent<String> printReceiptMsg = new SingleLiveEvent<>();
@ -148,8 +167,13 @@ public class SharedViewModel extends ViewModel {
public MutableLiveData<Integer> loadingView = new MutableLiveData<>(8);
public SingleLiveEvent<String> loadingMsg = new SingleLiveEvent<>();
private final SingleLiveEvent<String> toastMsg = new SingleLiveEvent<>();
private SingleLiveEvent<Boolean> isFallback = new SingleLiveEvent<>();
private SingleLiveEvent<Boolean> isSeePhone = new SingleLiveEvent<>();
private SingleLiveEvent<Boolean> isContactlessRestricted = new SingleLiveEvent<>();
private SingleLiveEvent<Boolean> isCardAppBlock = new SingleLiveEvent<>();
private SingleLiveEvent<Boolean> _isCardDataExist = new SingleLiveEvent<>();
@ -157,13 +181,19 @@ public class SharedViewModel extends ViewModel {
private PayDetail mPayDetail = new PayDetail();
public MutableLiveData<String> serialNumber = new MutableLiveData<>();
// Updated constructor for Hilt ViewModel
@Inject
public SharedViewModel(Repository repository) {
this.repository = repository;
setPrintStatus(PrintStatus.FIRST_PRINT);
isReprint.setValue(false);
suppressReprintButtons.setValue(false);
cardNo.setValue("");
String sn = TerminalUtil.getInstance().getSerialNo();
serialNumber.setValue(sn);
}
@ -171,7 +201,9 @@ public class SharedViewModel extends ViewModel {
isEmv.setValue(status);
}
public SingleLiveEvent<Boolean> isEmvTrans() { return isEmv;}
public SingleLiveEvent<Boolean> isEmvTrans() {
return isEmv;
}
private PayDetail getPayDetail() {
return mPayDetail;
@ -185,26 +217,100 @@ public class SharedViewModel extends ViewModel {
printReceiptButtons.postValue(value);
}
public void postPrintReceiptMsg(String msg) { this.printReceiptMsg.postValue(msg);}
public void setPrintStatus(PrintStatus printStatus) { this.printStatus.setValue(printStatus); }
public void postPrintStatus(PrintStatus printStatus) { this.printStatus.postValue(printStatus); }
public void setTransactionsType(TransactionsType transactionsType) {
this.transactionsType.setValue(transactionsType);
}
public SingleLiveEvent<PrintStatus> getPrintStatus() { return this.printStatus; }
public SingleLiveEvent<TransactionsType> getTransactionsType() {
return transactionsType;
}
public void setIsFallback(boolean status) { isFallback.setValue(status);}
public void setAmount(String amount) {
this.amount.setValue(amount);
}
public SingleLiveEvent<Boolean> getIsFallback() { return isFallback;}
public SingleLiveEvent<String> getAmount() {
return amount;
}
public void setCardDataExist(boolean exist) { _isCardDataExist.setValue(exist);}
public void setPrintReceiptMsg(String msg) {
this.printReceiptMsg.setValue(msg);
}
public SingleLiveEvent<Boolean> getCardDataExist() { return _isCardDataExist; }
public void postPrintReceiptMsg(String msg) {
this.printReceiptMsg.postValue(msg);
}
public void setAmountExist(boolean exist) { _isAmountExist.setValue(exist);}
public void setPrintStatus(PrintStatus printStatus) {
// this.printStatus.postValue(printStatus);
if (Looper.myLooper() == Looper.getMainLooper()) {
this.printStatus.setValue(printStatus);
} else {
this.printStatus.postValue(printStatus);
}
public SingleLiveEvent<Boolean> getAmountExist() { return _isAmountExist; }
}
public void postPrintStatus(PrintStatus printStatus) {
this.printStatus.postValue(printStatus);
}
public SingleLiveEvent<PrintStatus> getPrintStatus() {
return this.printStatus;
}
public void setIsFallback(boolean status) {
isFallback.setValue(status);
}
public SingleLiveEvent<Boolean> getIsFallback() {
return isFallback;
}
public void setIsCardAppBlock(boolean status) {
isCardAppBlock.setValue(status);
}
public SingleLiveEvent<Boolean> getIsCardAppBlock() {
return isCardAppBlock;
}
public void setIsSeePhone(boolean status) {
isSeePhone.setValue(status);
}
public SingleLiveEvent<Boolean> getIsSeePhone() {
return isSeePhone;
}
public void setIsContactlessRestricted(boolean status) {
isContactlessRestricted.setValue(status);
}
public SingleLiveEvent<Boolean> getIsContactlessRestricted() {
return isContactlessRestricted;
}
public void setCardDataExist(boolean exist) {
_isCardDataExist.setValue(exist);
}
public SingleLiveEvent<Boolean> getCardDataExist() {
return _isCardDataExist;
}
public void setAmountExist(boolean exist) {
_isAmountExist.setValue(exist);
}
public SingleLiveEvent<Boolean> getAmountExist() {
return _isAmountExist;
}
public MutableLiveData<Boolean> getManualEntryStatus() { return _manualEntryStatus;}
public MutableLiveData<Boolean> getManualEntryStatus() {
return _manualEntryStatus;
}
public void setManualEntryStatus(boolean status) {
_manualEntryStatus.setValue(status);
@ -222,11 +328,18 @@ public class SharedViewModel extends ViewModel {
return _successFragmentMsg;
}
public void set_currencyText(String msg) { this._currencyText.setValue(msg);}
public void set_currencyText(String msg) {
this._currencyText.setValue(msg);
}
public SingleLiveEvent<String> get_currencyText() { return _currencyText; }
public SingleLiveEvent<String> get_currencyText() {
return _currencyText;
}
public void set_successFragmentMsg(String msg) {
this._successFragmentMsg.setValue(msg);
}
public void set_successFragmentMsg(String msg) { this._successFragmentMsg.setValue(msg);}
public SingleLiveEvent<TransMenu> getTransMenu() {
return _transMenu;
}
@ -246,19 +359,28 @@ public class SharedViewModel extends ViewModel {
loadingMsg.setValue("");
}
public SingleLiveEvent<String> getToastMsg() {
return toastMsg;
}
public void postToastMsg(String msg) {
toastMsg.postValue(msg);
}
public Observable<SiriusResponse> getParams(SiriusRequest siriusRequest) {
return repository.getParams(siriusRequest);
}
private void printReceipt(boolean isMerchantCopy) {
PrintXReceipt.getInstance().printSmileReceipt(payDetail.getValue(), isMerchantCopy, new PrintXStatus() {
@Override
public void onSuccess() {
if(isMerchantCopy) {
if (isMerchantCopy) {
if(!SystemParamsOperation.getInstance().getDemoStatus()) {
if(SystemParamsOperation.getInstance().getPrintISOStatus()) {
if (!SystemParamsOperation.getInstance().getDemoStatus()) {
if (SystemParamsOperation.getInstance().getPrintISOStatus()) {
PrintXReceipt.getInstance().printSmileISoReceipt(payDetail.getValue());
}
}
@ -272,7 +394,7 @@ public class SharedViewModel extends ViewModel {
@Override
public void onFailure() {
LogUtil.d(TAG,"Print Status Result Failure!");
LogUtil.d(TAG, "Print Status Result Failure!");
printXStatus.postValue(PrintStatus.EMPTY_PAPER_ROLL);
printReceiptButtons.postValue(0);
}
@ -280,35 +402,34 @@ public class SharedViewModel extends ViewModel {
}
public void startPrintReceipt(boolean isFirstPrint) {
/*
* First Print is Merchant Copy..
* */
if(payDetail.getValue() != null) {
if (payDetail.getValue() != null) {
setPayDetail(payDetail.getValue());
}
LogUtil.d(TAG,"PayDetail trade Answer Code :"+getPayDetail().getTradeAnswerCode());
LogUtil.d(TAG, "PayDetail trade Answer Code :" + getPayDetail().getTradeAnswerCode());
LogUtil.d(TAG,"PayDetail trade Answer Code :"+payDetail.getValue().getTradeAnswerCode());
LogUtil.d(TAG, "PayDetail trade Answer Code :" + payDetail.getValue().getTradeAnswerCode());
PrintXReceipt.getInstance().printSmileReceipt(getPayDetail(), isFirstPrint, new PrintXStatus() {
@Override
public void onSuccess() {
if(isFirstPrint) {
if (isFirstPrint) {
if(!SystemParamsOperation.getInstance().getDemoStatus()) {
if(SystemParamsOperation.getInstance().getPrintISOStatus()) {
if (!SystemParamsOperation.getInstance().getDemoStatus()) {
if (SystemParamsOperation.getInstance().getPrintISOStatus()) {
PrintXReceipt.getInstance().printSmileISoReceipt(getPayDetail());
}
}
postPrintStatus(PrintStatus.FIRST_PRINT_DONE);
if(isEcr.getValue() != null &&
if (isEcr.getValue() != null &&
isEcr.getValue() &&
SystemParamsOperation.getInstance().isAutoPrintCustomerCopy()) {
printReceiptButtons.postValue(8);
@ -323,7 +444,7 @@ public class SharedViewModel extends ViewModel {
@Override
public void onFailure() {
LogUtil.d(TAG,"Print Status Result Failure!");
LogUtil.d(TAG, "Print Status Result Failure!");
if (isFirstPrint) {
setPrintStatus(PrintStatus.EMPTY_PAPER_ROLL_FIRST);
} else {
@ -335,10 +456,9 @@ public class SharedViewModel extends ViewModel {
}
@SuppressWarnings("ConstantConditions")
public void startPrintProcess() {
LogUtil.d(TAG,"Print status :"+getPrintStatus().getValue());
LogUtil.d(TAG, "Print status :" + getPrintStatus().getValue());
switch (getPrintStatus().getValue()) {
case FIRST_PRINT:
@ -361,23 +481,32 @@ public class SharedViewModel extends ViewModel {
}
public void startPrintProcessSettlement() {
public void startPrintSettlement(PrintXStatus printXStatus) {
if (payDetail.getValue() == null) return;
if (payDetail.getValue().getTransactionType() == TransactionsType.SETTLEMENT.value) {
startPrintProcessSettlement(printXStatus);
} else {
startPrintProcessQRSettlement(printXStatus);
}
}
if(payDetail.getValue() == null) return;
public void startPrintProcessSettlement(PrintXStatus printXStatus) {
PrintXReceipt.getInstance().printSmileSettlementReport(payDetail.getValue(), new PrintXStatus() {
@Override
public void onSuccess() {
if (payDetail.getValue() == null) return;
}
if (payDetails.getValue() != null && !payDetails.getValue().isEmpty()) {
PrintXReceipt.getInstance().printSmileSettlementReport(payDetail.getValue(), QRSettleData.convertFromPayDetail(payDetails.getValue()), printXStatus);
} else {
PrintXReceipt.getInstance().printSmileSettlementReport(payDetail.getValue(), printXStatus);
}
}
@Override
public void onFailure() {
private void startPrintProcessQRSettlement(PrintXStatus printXStatus) {
}
});
/*POS */
// PrintReceipt.getInstance().printSettlementReceiptPOS(settleData.getSaleCount(), settleData.getSaleAmount(), settleData.getRefundCount(), settleData.getRefundAmount(), settleData.getPreAuthCount(), settleData.getPreAuthAmount(), true);
if (payDetail.getValue() == null && payDetails.getValue() == null) return;
PrintXReceipt.getInstance().printQRSettlementReport(payDetail.getValue(), QRSettleData.convertFromPayDetail(payDetails.getValue()), printXStatus);
}
@ -386,19 +515,32 @@ public class SharedViewModel extends ViewModel {
}
public LiveData<PayDetail> getReversalTransaction(String voucherNo) {
return repository.getReversalTransaction(voucherNo);
}
public void updatePayDetail(PayDetail payDetail){
public LiveData<PayDetail> getLastReversalTransaction() {
return repository.getLastReversalTransaction();
}
public LiveData<List<PayDetail>> getAllReversalTransaction() {
return repository.getAllReversalTransactions();
}
public void updatePayDetail(PayDetail payDetail) {
repository.updatePayDetail(payDetail);
}
public void insertPayDetail(PayDetail payDetail) { repository.insertPayDetail(payDetail); }
public void updateESignBlob(String voucherNo, byte[] blob) {
repository.updateESignBlob(voucherNo, blob);
}
public void insertPayDetail(PayDetail payDetail) {
repository.insertPayDetail(payDetail);
}
public void enableCardStatusIcon(boolean tapCard,boolean tapDevice,boolean insertCard, boolean swipeCard) {
public void enableCardStatusIcon(boolean tapCard, boolean tapDevice, boolean insertCard, boolean swipeCard) {
tapCardStatus.setValue(tapCard ? 1 : 0);
tapDeviceStatus.setValue(tapDevice ? 1 : 0);
insertCardStatus.setValue(insertCard ? 1 : 0);
@ -406,4 +548,86 @@ public class SharedViewModel extends ViewModel {
}
@SuppressLint("CheckResult")
public void pushReceipt(Object body) {
Log.d("push receipt", new Gson().toJson(body));
repository.sendReceipt(body)
.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);
// Parse JSON to model
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!");
}
}
);
}
@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() {
@Override
public void onSuccess() {
Log.d("PrintConfig", "Print Terminal Config Success.");
}
@Override
public void onFailure() {
Log.e("PrintConfig", "Print Terminal Config Success.");
}
});
}
}

View File

@ -71,7 +71,7 @@ public class TMSProcessViewModel extends ViewModel {
}
public void loadEmvParameters() {
emvParamOperation.loadAidRids();
// emvParamOperation.loadAidRids();
emvParamOperation.loadEmvTerminalParam();
}

View File

@ -139,7 +139,11 @@ public class TransProcessViewModel extends ViewModel implements ProcessingTransa
LogUtil.d(TAG,"Transaction Operation Success: "+payDetailRes.getTradeAnswerCode());
if (TextUtils.equals(payDetailRes.getTradeAnswerCode(),RC_APPROVED_V1) || TextUtils.equals(payDetailRes.getTradeAnswerCode(),RC_APPROVED_V2)) {
payDetailRes.setInvoiceNo(SystemParamsOperation.getInstance().getIncrementInvoiceNum());
if (transType.getValue() == TransactionsType.VOID) {
payDetailRes.setInvoiceNo(payDetail.getInvoiceNo());
} else {
payDetailRes.setInvoiceNo(SystemParamsOperation.getInstance().getIncrementInvoiceNum());
}
LogUtil.d(TAG,"MPU Transaction type :"+transType.getValue());
@ -229,13 +233,16 @@ public class TransProcessViewModel extends ViewModel implements ProcessingTransa
@Override
public void startDemoProcess() {
SystemParamsOperation.getInstance().getIncrementInvoiceNum();
if (transType.getValue() != TransactionsType.VOID) {
SystemParamsOperation.getInstance().getIncrementInvoiceNum();
}
PayDetail payDetailRes;
if (transType.getValue() == TransactionsType.VOID) {
payDetailRes = DemoUtil.getInstance().builtDemoVoid(payDetail);
payDetailRes.setInvoiceNo(payDetail.getInvoiceNo());
processVoidDB(payDetailRes);
} else if (transType.getValue() == TransactionsType.REFUND) {
@ -292,6 +299,7 @@ public class TransProcessViewModel extends ViewModel implements ProcessingTransa
public void processVoidDB(PayDetail payResult) {
payDetail.setIsCanceled(true);
updatePayDetail(payDetail);
payResult.setESignBlob(payDetail.getESignBlob());
repository.insertPayDetail(updateCurrentDateAndTime(payResult));
}
@ -304,13 +312,16 @@ public class TransProcessViewModel extends ViewModel implements ProcessingTransa
}
@Override
public void processPreCompDb(PayDetail payResult) {
if(oldTransPayDetail.getAmount() == payResult.getAmount()) {
oldTransPayDetail.setIsCanceled(true);
oldTransPayDetail.setAdjust(true);
updatePayDetail(oldTransPayDetail);
} else {
oldTransPayDetail.setIsCanceled(false);
oldTransPayDetail.setAdjust(true);
updatePayDetail(oldTransPayDetail);
}
repository.insertPayDetail(updateCurrentDateAndTime(payResult));

View File

@ -0,0 +1,118 @@
package com.utsmm.kbz.ui.dashboard;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.enums.HostType;
import com.utsmyanmar.paylibs.utils.enums.TransMenu;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.databinding.FragmentDashboardHistoryScreenBinding;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.management.CardViewAdapter;
import com.utsmm.kbz.ui.management.ManagementViewModel;
import java.util.ArrayList;
public class DashboardHistoryFragment extends DataBindingFragment {
private ManagementViewModel managementViewModel;
private SharedViewModel sharedViewModel;
private CardViewAdapter cardViewAdapter;
private int routeId;
private final ArrayList<PayDetail> lists = new ArrayList<>();
@Override
protected void initViewModel() {
managementViewModel = getFragmentScopeViewModel(ManagementViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_dashboard_history_screen, BR.manageViewModel, managementViewModel)
.addBindingParam(BR.sharedViewModel, sharedViewModel);
}
@Override
protected int currentId() {
return R.id.dashboardHistoryFragment;
}
@Override
protected int hostId() {
return Constants.NAV_HOST_ID;
}
@Override
protected int routeId() {
return routeId;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
observeHistory();
}
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithBackIcon(getString(R.string.title_history));
}
private void observeHistory() {
managementViewModel.getTransactionHistory().observe(getViewLifecycleOwner(), payDetailList -> {
if (payDetailList == null) {
return;
}
lists.clear();
if (payDetailList.isEmpty()) {
managementViewModel.detailReportLayoutVisibility.setValue(0);
managementViewModel.detailReportBottomLayoutVisibility.setValue(8);
} else {
managementViewModel.detailReportLayoutVisibility.setValue(8);
managementViewModel.detailReportBottomLayoutVisibility.setValue(0);
}
for (PayDetail payDetail : payDetailList) {
if (payDetail.getTransactionType() == TransactionsType.MMQR.value
|| payDetail.getTransactionType() == TransactionsType.MMQR_REFUND.value
|| payDetail.getTransactionType() == TransactionsType.SETTLEMENT.value
|| payDetail.getTransactionType() == TransactionsType.MMQR_SETTLEMENT.value
|| payDetail.getTransactionType() == TransactionsType.REVERSAL.value) {
continue;
}
lists.add(payDetail);
}
if (lists.isEmpty()) {
managementViewModel.detailReportBottomLayoutVisibility.setValue(8);
managementViewModel.detailReportLayoutVisibility.setValue(0);
}
cardViewAdapter = new CardViewAdapter(lists, payDetail -> {
sharedViewModel.setTransMenu(TransMenu.REPRINT);
sharedViewModel.suppressReprintButtons.setValue(true);
sharedViewModel.payDetail.postValue(payDetail);
routeId = R.id.action_dashboardHistoryFragment_to_confirmTransactionFragment;
safeRouteTo(currentId(), routeId, hostId());
});
((FragmentDashboardHistoryScreenBinding) mBinding).setMyAdapter(cardViewAdapter);
});
}
}

View File

@ -9,12 +9,14 @@ import com.utsmm.kbz.MainViewModel;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.CurrencySelectionHelper;
public class DashboardPreAuthBottomSheet extends DataBindingBottomSheetFragment {
private MainViewModel mainViewModel;
private SharedViewModel sharedViewModel;
private CurrencySelectionHelper currencySelectionHelper;
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
@ -24,6 +26,7 @@ public class DashboardPreAuthBottomSheet extends DataBindingBottomSheetFragment
protected void initViewModel() {
mainViewModel = getFragmentScopeViewModel(MainViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
currencySelectionHelper = new CurrencySelectionHelper(this, sharedViewModel);
}
@Override
@ -52,10 +55,11 @@ public class DashboardPreAuthBottomSheet extends DataBindingBottomSheetFragment
} else if (checkTid()) {
showDeclineDialog("Please Download Config!");
} else {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_SALE);
routeId = R.id.action_dashboardPreAuthBottomSheet_to_inputAmountFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_SALE);
routeId = R.id.action_dashboardPreAuthBottomSheet_to_inputAmountFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
}
@ -65,10 +69,11 @@ public class DashboardPreAuthBottomSheet extends DataBindingBottomSheetFragment
} else if (checkTid()) {
showDeclineDialog("Please Download Config!");
} else {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_COMPLETE);
routeId = R.id.action_dashboardPreAuthBottomSheet_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_COMPLETE);
routeId = R.id.action_dashboardPreAuthBottomSheet_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
}
@ -86,4 +91,8 @@ public class DashboardPreAuthBottomSheet extends DataBindingBottomSheetFragment
}
}
}
private void showCurrencyChooserForCardFlow(Runnable onSelected) {
currencySelectionHelper.showCardCurrencyChooser(onSelected);
}
}

View File

@ -10,6 +10,7 @@ import com.utsmm.kbz.MainViewModel;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.CurrencySelectionHelper;
public class DashboardPreAuthCancelBottomSheet extends DataBindingBottomSheetFragment {
@ -17,6 +18,7 @@ public class DashboardPreAuthCancelBottomSheet extends DataBindingBottomSheetFra
private MainViewModel mainViewModel;
private SharedViewModel sharedViewModel;
private CurrencySelectionHelper currencySelectionHelper;
private int currentId = R.id.dashboardPreAuthCancelBottomSheet;
private int routeId;
@ -26,6 +28,7 @@ public class DashboardPreAuthCancelBottomSheet extends DataBindingBottomSheetFra
protected void initViewModel() {
mainViewModel = getFragmentScopeViewModel(MainViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
currencySelectionHelper = new CurrencySelectionHelper(this, sharedViewModel);
}
@Override
@ -61,12 +64,13 @@ public class DashboardPreAuthCancelBottomSheet extends DataBindingBottomSheetFra
} else if (checkTid()) {
showDeclineDialog("Please Download Config!");
} else {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_FULL_VOID);
sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardPreAuthCancelBottomSheet_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_FULL_VOID);
sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardPreAuthCancelBottomSheet_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
}
@ -82,14 +86,19 @@ public class DashboardPreAuthCancelBottomSheet extends DataBindingBottomSheetFra
} else if (checkTid()) {
showDeclineDialog("Please Download Config!");
} else {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_PARTIAL_VOID);
sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardPreAuthCancelBottomSheet_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_PARTIAL_VOID);
sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardPreAuthCancelBottomSheet_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
}
}
private void showCurrencyChooserForCardFlow(Runnable onSelected) {
currencySelectionHelper.showCardCurrencyChooser(onSelected);
}
}

View File

@ -2,14 +2,25 @@ package com.utsmm.kbz.ui.dashboard;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import android.view.Gravity;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatButton;
import androidx.core.content.ContextCompat;
import com.utsmm.kbz.util.enums.SettlementType;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
import com.utsmyanmar.paylibs.utils.enums.TransMenu;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.BR;
@ -19,19 +30,30 @@ import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.config.data.model.Features;
import com.utsmm.kbz.ui.adapters.MainAdapter;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.CurrencySelectionHelper;
import com.utsmm.kbz.util.tms.TMSUtil;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
public class DashboardTransFragment extends DataBindingFragment {
private MainViewModel mainViewModel;
private SharedViewModel sharedViewModel;
private CurrencySelectionHelper currencySelectionHelper;
private ArrayList<Features> featuresList = new ArrayList<>();
MainAdapter mainAdapter;
private boolean cardPrimaryHostCached = false;
private String cardPrimaryHostName = "";
private String cardPrimaryTid = "";
private String cardPrimaryMid = "";
private String cardPrimaryIp = "";
private String cardPrimarySecIp = "";
private CurrencyType cardPrimaryCurrency = CurrencyType.MMK;
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
@ -56,6 +78,7 @@ public class DashboardTransFragment extends DataBindingFragment {
protected void initViewModel() {
mainViewModel = getFragmentScopeViewModel(MainViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
currencySelectionHelper = new CurrencySelectionHelper(this, sharedViewModel);
}
@Override
@ -66,8 +89,60 @@ public class DashboardTransFragment extends DataBindingFragment {
initData();
}
// private void initData() {
// TMSUtil.getInstance().checkFeaturesList(requireActivity(),featuresList);
// mainAdapter.notifyDataSetChanged();
// }
Boolean isRefundEnabled = SystemParamsOperation.getInstance().getRefundStatus();
Boolean isVoidEnabled = SystemParamsOperation.getInstance().getVoidStatus();
Boolean isPreAuthEnabled = SystemParamsOperation.getInstance().getPreAuthStatus();
Boolean isCashAdvanceEnabled = SystemParamsOperation.getInstance().getCashAdvanceStatus();
private void initData() {
TMSUtil.getInstance().checkFeaturesList(requireActivity(),featuresList);
featuresList.clear();
ArrayList<Features> allFeatures = new ArrayList<>();
TMSUtil.getInstance().checkFeaturesList(requireActivity(), allFeatures);
for (Features feature : allFeatures) {
switch (feature.getFeaturesType()) {
case REFUND:
if (isRefundEnabled) {
featuresList.add(feature);
}
break;
case VOID:
if (isVoidEnabled) {
featuresList.add(feature);
}
break;
case PRE_AUTH_SALE:
case PRE_AUTH_COMPLETE_VOID:
case PRE_AUTH_VOID:
case PRE_AUTH_COMPLETE:
if (isPreAuthEnabled) {
featuresList.add(feature);
}
break;
case CASH_ADVANCE:
if(isCashAdvanceEnabled){
featuresList.add(feature);
}
break;
default:
featuresList.add(feature);
break;
}
}
mainAdapter.notifyDataSetChanged();
}
@ -103,8 +178,11 @@ public class DashboardTransFragment extends DataBindingFragment {
case CASH_ADVANCE:
new DashboardTransFragment.ClickEvent().onClickCashAdvance();
break;
case HISTORY:
new DashboardTransFragment.ClickEvent().onClickHistory();
case CLEAR_REVERSAL:
new DashboardTransFragment.ClickEvent().onClickClearReversal();
break;
case DEVICE_CONFIG:
new DashboardTransFragment.ClickEvent().onClickDeviceConfig();
break;
}
@ -125,7 +203,7 @@ public class DashboardTransFragment extends DataBindingFragment {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Boolean demoStatus = SystemParamsOperation.getInstance().getDemoStatus();
}
public class ClickEvent {
@ -135,7 +213,7 @@ public class DashboardTransFragment extends DataBindingFragment {
} else if (checkTid()) {
showDeclineDialog("Please Download Config!");
} else {
sharedViewModel.transactionsType.setValue(TransactionsType.VOID);
sharedViewModel.setTransactionsType(TransactionsType.VOID);
// sharedViewModel.transMenu.postValue(TransMenu.TRANSACTIONS);
sharedViewModel.setTransMenu(TransMenu.TRANSACTIONS);
@ -147,18 +225,19 @@ public class DashboardTransFragment extends DataBindingFragment {
public void onClickSettlement() {
sharedViewModel.settlementType.setValue(SettlementType.NORMAL);
sharedViewModel.transactionsType.setValue(TransactionsType.SETTLEMENT);
sharedViewModel.setTransMenu(TransMenu.SETTLEMENT);
sharedViewModel.setTransactionsType(TransactionsType.SETTLEMENT);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
}
public void onClickRefund() {
sharedViewModel.transactionsType.setValue(TransactionsType.REFUND);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.setTransactionsType(TransactionsType.REFUND);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
public void onClickPreAuth () {
@ -167,32 +246,38 @@ public class DashboardTransFragment extends DataBindingFragment {
} else if (checkTid()) {
showDeclineDialog("Please Download Config!");
} else {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_SALE);
routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_SALE);
routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
}
public void onClickPreAuthCancel () {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_FULL_VOID);
sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_VOID);
sharedViewModel.setTransMenu(TransMenu.PRE_AUTH_FULL_VOID);
sharedViewModel.amount.postValue("0"); // null to 0
// sharedViewModel.amount.postValue(null);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
public void onClickPreAuthComp () {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_COMPLETE);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_COMPLETE);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
public void onClickPreAuthCompCancel () {
sharedViewModel.transactionsType.setValue(TransactionsType.PRE_AUTH_COMPLETE_VOID);
sharedViewModel.setTransactionsType(TransactionsType.PRE_AUTH_COMPLETE_VOID);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId,routeId,hostId);
@ -200,18 +285,213 @@ public class DashboardTransFragment extends DataBindingFragment {
public void onClickCashAdvance() {
sharedViewModel.transactionsType.setValue(TransactionsType.CASH_OUT);
routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment;
safeRouteTo(currentId,routeId,hostId);
showCurrencyChooserForCardFlow(() -> {
sharedViewModel.setTransactionsType(TransactionsType.CASH_OUT);
sharedViewModel.setTransMenu(TransMenu.CASH_OUT);
routeId = R.id.action_dashboardTransFragment_to_inputAmountFragment;
safeRouteTo(currentId, routeId, hostId);
});
}
public void onClickHistory() {
routeId = R.id.action_dashboardTransFragment_to_manageFunctionFragment;
safeRouteTo(currentId,routeId,hostId);
public void onClickClearReversal() {
sharedViewModel.transactionsType.postValue(TransactionsType.CLEAR_REVERSAL);
sharedViewModel.setTransMenu(TransMenu.CLEAR_REVERSAL);
routeId = R.id.action_dashboardTransFragment_to_inputPasswordFragment;
safeRouteTo(currentId, routeId, hostId);
}
public void onClickDeviceConfig(){
routeId = R.id.action_dashboardTransFragment_to_deviceConfig;
safeRouteTo(currentId, routeId, hostId);
}
}
private void showCurrencyChooserForCardFlow(Runnable onSelected) {
List<String> currencies = getAvailableCardSaleCurrencies();
if (currencies.isEmpty()) {
applyCardCurrencyAndHost("MMK");
onSelected.run();
return;
}
if (currencies.size() == 1) {
applyCardCurrencyAndHost(currencies.get(0));
onSelected.run();
return;
}
showCurrencyPickerDialog("Choose Currency", currencies, currency -> {
applyCardCurrencyAndHost(currency);
onSelected.run();
});
}
private List<String> getAvailableCardSaleCurrencies() {
cachePrimaryCardHostIfNeeded();
LinkedHashSet<String> values = new LinkedHashSet<>();
if (cardPrimaryCurrency != null && !TextUtils.isEmpty(cardPrimaryCurrency.name)) {
values.add(normalizeCurrency(cardPrimaryCurrency.name));
}
String secondaryCurrency = normalizeCurrency(SystemParamsOperation.getInstance().getThirdHostCurrency());
if (!TextUtils.isEmpty(SystemParamsOperation.getInstance().getThirdHostName()) && !TextUtils.isEmpty(secondaryCurrency)) {
values.add(secondaryCurrency);
}
return new ArrayList<>(values);
}
private void applyCardCurrencyAndHost(String selectedCurrency) {
cachePrimaryCardHostIfNeeded();
String selected = normalizeCurrency(selectedCurrency);
String secondaryCurrency = normalizeCurrency(SystemParamsOperation.getInstance().getThirdHostCurrency());
boolean useSecondaryCardHost = !TextUtils.isEmpty(SystemParamsOperation.getInstance().getThirdHostName())
&& !TextUtils.isEmpty(secondaryCurrency)
&& secondaryCurrency.equals(selected);
if (useSecondaryCardHost) {
SystemParamsOperation.getInstance().setHostName(SystemParamsOperation.getInstance().getThirdHostName());
SystemParamsOperation.getInstance().setTerminalId(SystemParamsOperation.getInstance().getThirdHostTerminalId());
SystemParamsOperation.getInstance().setMerchantId(SystemParamsOperation.getInstance().getThirdHostMerchantId());
SystemParamsOperation.getInstance().setIpAddress(SystemParamsOperation.getInstance().getThirdHostIpAddress());
SystemParamsOperation.getInstance().setSecIpAddress(SystemParamsOperation.getInstance().getThirdHostSecIpAddress());
} else {
SystemParamsOperation.getInstance().setHostName(cardPrimaryHostName);
SystemParamsOperation.getInstance().setTerminalId(cardPrimaryTid);
SystemParamsOperation.getInstance().setMerchantId(cardPrimaryMid);
SystemParamsOperation.getInstance().setIpAddress(cardPrimaryIp);
SystemParamsOperation.getInstance().setSecIpAddress(cardPrimarySecIp);
}
CurrencyType currencyType = useSecondaryCardHost ? toCurrencyType(selected) : cardPrimaryCurrency;
SystemParamsOperation.getInstance().setCurrencyType(currencyType);
sharedViewModel.set_currencyText(currencyType.name);
}
private void cachePrimaryCardHostIfNeeded() {
if (cardPrimaryHostCached) {
return;
}
cardPrimaryHostName = SystemParamsOperation.getInstance().getHostName();
cardPrimaryTid = SystemParamsOperation.getInstance().getTerminalId();
cardPrimaryMid = SystemParamsOperation.getInstance().getMerchantId();
cardPrimaryIp = SystemParamsOperation.getInstance().getIpAddress();
cardPrimarySecIp = SystemParamsOperation.getInstance().getSecIpAddress();
CurrencyType activeCurrency = SystemParamsOperation.getInstance().getCurrencyType();
cardPrimaryCurrency = activeCurrency == null ? CurrencyType.MMK : activeCurrency;
cardPrimaryHostCached = true;
}
private CurrencyType toCurrencyType(String value) {
String normalized = normalizeCurrency(value);
try {
return CurrencyType.valueOf(normalized);
} catch (Exception ignored) {
return CurrencyType.MMK;
}
}
private String normalizeCurrency(String raw) {
if (TextUtils.isEmpty(raw)) {
return "";
}
String value = raw.trim().toUpperCase();
switch (value) {
case "104":
case "MMK":
return "MMK";
case "840":
case "USD":
return "USD";
case "156":
case "CNY":
case "RMB":
return "CNY";
case "764":
case "THB":
return "THB";
case "643":
case "RUB":
return "RUB";
default:
return value;
}
}
private void showCurrencyPickerDialog(String title, List<String> currencies, CurrencySelectCallback callback) {
LinearLayout root = new LinearLayout(requireContext());
root.setOrientation(LinearLayout.VERTICAL);
root.setPadding(dp(20), dp(20), dp(20), dp(12));
TextView tvTitle = new TextView(requireContext());
tvTitle.setText(title);
tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
tvTitle.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorTextTitle));
tvTitle.setPadding(0, 0, 0, dp(16));
root.addView(tvTitle);
final AlertDialog[] dialogHolder = new AlertDialog[1];
for (String currency : currencies) {
AppCompatButton button = new AppCompatButton(requireContext());
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dp(56)
);
lp.bottomMargin = dp(10);
button.setLayoutParams(lp);
button.setAllCaps(false);
button.setText(currency);
button.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18);
button.setGravity(Gravity.CENTER);
button.setBackgroundResource(R.drawable.bg_rounded_btn_cv);
button.setTextColor(ContextCompat.getColor(requireContext(), R.color.white));
button.setOnClickListener(v -> {
if (dialogHolder[0] != null) {
dialogHolder[0].dismiss();
}
callback.onSelected(currency);
});
root.addView(button);
}
AppCompatButton cancel = new AppCompatButton(requireContext());
LinearLayout.LayoutParams cancelLp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
dp(52)
);
cancel.setLayoutParams(cancelLp);
cancel.setAllCaps(false);
cancel.setText("Cancel");
cancel.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
cancel.setBackgroundResource(R.drawable.bg_rounded_btn_cancel_cv);
cancel.setTextColor(ContextCompat.getColor(requireContext(), R.color.colorPrimary));
cancel.setOnClickListener(v -> {
if (dialogHolder[0] != null) {
dialogHolder[0].dismiss();
}
});
root.addView(cancel);
AlertDialog dialog = new AlertDialog.Builder(requireContext())
.setView(root)
.create();
dialogHolder[0] = dialog;
dialog.show();
}
private int dp(int value) {
return Math.round(value * requireContext().getResources().getDisplayMetrics().density);
}
private interface CurrencySelectCallback {
void onSelected(String currency);
// currencySelectionHelper.showCardCurrencyChooser(onSelected);
}
}

View File

@ -4,6 +4,7 @@ import androidx.databinding.library.baseAdapters.BR;
import androidx.lifecycle.Observer;
import com.google.gson.stream.MalformedJsonException;
import com.utsmm.kbz.MainActivity;
import com.utsmyanmar.baselib.emv.EmvParamOperation;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.sirius.SiriusRequest;
@ -104,6 +105,7 @@ public class DisabledTerminalFragment extends DataBindingFragment {
private void downloadParams(String lastTransName,String lastTransTime) {
showLoadingDialog("Downloading...");
((MainActivity) requireActivity()).getLocation();
SiriusRequest siriusReq = TMSUtil.getInstance().generateRequestParams(getContext(),lastTransName,lastTransTime);
mainViewModel.getParams(siriusReq).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View File

@ -13,6 +13,7 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.kizzy.xpay.util.Sign;
import com.utsmm.kbz.ui.qr_pay.QRRefund;
import com.utsmyanmar.baselib.network.model.DemoQRRequest;
import com.utsmyanmar.baselib.network.model.DemoQRResponse;
import com.utsmyanmar.baselib.network.model.DemoQRReturnRequest;
@ -36,12 +37,16 @@ import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.system.SingleLiveEvent;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
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;
@ -52,7 +57,11 @@ public class KPayViewModel extends ViewModel {
private final Repository repository;
private final String appKey = "UTSMMuat@2025";
private final String appKey = SystemParamsOperation.getInstance().getAppKey();
// private final String appKey = "UTSMMuat@2025";
private final String appId = SystemParamsOperation.getInstance().getAppId();
// private final String appId = "kp9b0794b349ae85b00c51e0677484c7";
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final Handler mainHandler = new Handler(Looper.getMainLooper());
@ -61,10 +70,7 @@ public class KPayViewModel extends ViewModel {
private static final String NONCE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
private static final int NONCE_LENGTH = 32;
private final SecureRandom secureRandom = new SecureRandom();
public MutableLiveData<Boolean> partialRefundEnabled = new MutableLiveData<>();
private String generateRandomTwoChars() {
// You can reuse the existing character set and SecureRandom instance
@ -114,7 +120,7 @@ public class KPayViewModel extends ViewModel {
bizContent.put("refund_request_no", refundOrderId);
bizContent.put("merch_order_id", origOrderId);
bizContent.put("merch_code", mid);
bizContent.put("appid", "kp9b0794b349ae85b00c51e0677484c7");
bizContent.put("appid", appId);
if(!refundAmount.equals("0")) {
bizContent.put("refund_amount", refundAmount);
}
@ -138,7 +144,7 @@ public class KPayViewModel extends ViewModel {
KPayRefund.RefundRequest.RequestBody.BizContent biz =
new KPayRefund.RefundRequest.RequestBody.BizContent(
"kp9b0794b349ae85b00c51e0677484c7",
appId,
mid,
origOrderId,
refundAmount, // This can be null for original refunds
@ -160,7 +166,7 @@ public class KPayViewModel extends ViewModel {
} else {
KPayRefund.RefundRequest.RequestBody.BizContent biz =
new KPayRefund.RefundRequest.RequestBody.BizContent(
"kp9b0794b349ae85b00c51e0677484c7",
appId,
mid,
origOrderId,
refundOrderId,
@ -187,20 +193,47 @@ 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<String, Object> bizContent = new HashMap<>();
bizContent.put("merch_order_id", merchOrderId);
bizContent.put("merch_code", mid);
bizContent.put("appid", "kp9b0794b349ae85b00c51e0677484c7");
bizContent.put("appid", appId);
bizContent.put("trade_type", "PAY_BY_QRCODE");
bizContent.put("total_amount", amount);
bizContent.put("title", "testing");
// bizContent.put("operator_id", serialNum);
bizContent.put("timeout_express", "100m");
bizContent.put("operator_id", serialNum);
bizContent.put("timeout_express", timeoutExpress);
bizContent.put("trans_currency", "MMK");
bizContent.put("callback_info", "callback");
@ -216,15 +249,16 @@ public class KPayViewModel extends ViewModel {
String sign = Sign.INSTANCE.generateSign(requestMap, appKey);
KPayQRRequest.QrRequest.RequestBody.BizContent biz = new KPayQRRequest.QrRequest.RequestBody.BizContent(
"kp9b0794b349ae85b00c51e0677484c7",
appId,
mid,
merchOrderId,
"PAY_BY_QRCODE",
"testing",
amount,
"MMK",
"100m",
"callback"
timeoutExpress,
"callback",
serialNum
);
KPayQRRequest.QrRequest.RequestBody body = new KPayQRRequest.QrRequest.RequestBody(
@ -243,6 +277,44 @@ public class KPayViewModel extends ViewModel {
return qrRequest;
}
public KPayQRRequest.CloseOrderRequest closeQrOrder(String merchOrderId, String mid){
String currentTime = String.valueOf(System.currentTimeMillis());
String nonceStr = generateNonceStr(); // Generate random nonce_str
Map<String, Object> bizContentMap = new HashMap<>();
bizContentMap.put("merch_order_id", merchOrderId);
bizContentMap.put("merch_code", mid);
bizContentMap.put("appid", appId);
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("timestamp", currentTime);
requestMap.put("nonce_str", nonceStr);
requestMap.put("method", "kbz.payment.closeorder");
requestMap.put("version", "3.0");
requestMap.put("biz_content", bizContentMap);
String sign = Sign.INSTANCE.generateSign(requestMap, appKey);
KPayQRRequest.CloseOrderRequest.Request.BizContent bizContent = new KPayQRRequest.CloseOrderRequest.Request.BizContent(
merchOrderId,
mid,
appId
);
KPayQRRequest.CloseOrderRequest.Request requestBody = new KPayQRRequest.CloseOrderRequest.Request(
currentTime,
"kbz.payment.closeorder",
nonceStr,
"SHA256",
sign,
"3.0",
bizContent
);
KPayQRRequest.CloseOrderRequest request = new KPayQRRequest.CloseOrderRequest(requestBody);
return request;
}
public KPayQRQueryRequest.QRQueryRequest getQrStatus(String merchOrderId, String mid) {
@ -252,7 +324,7 @@ public class KPayViewModel extends ViewModel {
Map<String, Object> bizContentMap = new HashMap<>();
bizContentMap.put("merch_order_id", merchOrderId);
bizContentMap.put("merch_code", mid);
bizContentMap.put("appid", "kp9b0794b349ae85b00c51e0677484c7");
bizContentMap.put("appid", appId);
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("timestamp", currentTime);
@ -264,7 +336,7 @@ public class KPayViewModel extends ViewModel {
String sign = Sign.INSTANCE.generateSign(requestMap, appKey);
KPayQRQueryRequest.QRQueryRequest.Request.BizContent bizContent = new KPayQRQueryRequest.QRQueryRequest.Request.BizContent(
"kp9b0794b349ae85b00c51e0677484c7",
appId,
mid,
merchOrderId
);
@ -284,13 +356,19 @@ public class KPayViewModel extends ViewModel {
return request;
}
@Inject
public KPayViewModel(Repository repository) {
this.repository = repository;
boolean enabled = SystemParamsOperation.getInstance().isQrPartialRefundEnable();
partialRefundEnabled.setValue(enabled);
}
private TradeData tradeData;
private PayDetail payDetail;
private QRRefund qrRefund;
public void setTradeData(TradeData tradeData){
this.tradeData = tradeData;
@ -304,6 +382,12 @@ public class KPayViewModel extends ViewModel {
public void setPayDetail(PayDetail payDetail) {
this.payDetail = payDetail;
}
public void setQrRefund(QRRefund qrRefund) {
this.qrRefund = qrRefund;
}
public QRRefund getQrRefund() {
return qrRefund;
}
public PayDetail getPayDetail() { return payDetail; }
@ -355,4 +439,53 @@ public class KPayViewModel extends ViewModel {
public Observable<KPayRefund.RefundResponse> kPayRefund(KPayRefund.RefundRequest refundRequest) {
return repository.kPayRefund(refundRequest);
}
public Observable<KPayQRRequest.CloseOrderResponse> closeQrOrderApi(
KPayQRRequest.CloseOrderRequest request
) {
return repository.qrCloseOrder(request);
}
public KPayQRRequest.QRQueryRefundRequest getQueryRefundRequest(String merchOrderId, String mid) {
String currentTime = String.valueOf(System.currentTimeMillis());
String nonceStr = generateNonceStr();
Map<String, Object> bizContentMap = new HashMap<>();
bizContentMap.put("appid", appId);
bizContentMap.put("merch_code", mid);
bizContentMap.put("merch_order_id", merchOrderId);
Map<String, Object> requestMap = new HashMap<>();
requestMap.put("timestamp", currentTime);
requestMap.put("nonce_str", nonceStr);
requestMap.put("method", "kbz.payment.queryrefund");
requestMap.put("sign_type", "SHA256");
requestMap.put("version", "1.0");
requestMap.put("biz_content", bizContentMap);
String sign = Sign.INSTANCE.generateSign(requestMap, appKey);
KPayQRRequest.QRQueryRefundRequest.Request.BizContent bizContent =
new KPayQRRequest.QRQueryRefundRequest.Request.BizContent(appId, mid, merchOrderId);
KPayQRRequest.QRQueryRefundRequest.Request requestBody =
new KPayQRRequest.QRQueryRefundRequest.Request(
currentTime,
nonceStr,
"kbz.payment.queryrefund",
"SHA256",
sign,
"1.0",
bizContent
);
return new KPayQRRequest.QRQueryRefundRequest(requestBody);
}
public Observable<KPayRefund.RefundResponse> checkRefundStatus(KPayQRRequest.QRQueryRefundRequest request) {
return repository.checkRefundStatus(request);
}
}

View File

@ -25,6 +25,7 @@ import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmm.kbz.util.ecr.CoreUtils;
import java.text.DecimalFormat;
import java.util.Locale;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
@ -135,10 +136,14 @@ public class QRConnectingFragment extends DataBindingFragment implements DataBin
}
private void generateQR() {
long originalAmount = payDetail.getAmount();
// String amount = String.format(Locale.getDefault(), "%.2f", payDetail.getAmount()/100.0);
String amount = String.format(Locale.getDefault(), "%d", (int)(payDetail.getAmount()/100.0));
// String amount = String.format(Locale.getDefault(), "%d", (int)(payDetail.getAmount()/100.0)); //this method have problem for 10055 to 100.00 auto convert (should convert to 100.55)
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
String amount = df.format(realAmount);
KPayQRRequest.QrRequest kPayQRRequest = KPayViewModel.createQR(amount, merchantId);
@ -163,7 +168,6 @@ public class QRConnectingFragment extends DataBindingFragment implements DataBin
LogUtil.d(TAG,"Ref Label :"+refLabel);
payDetail.setPayQRCode(response.getResponse().getQrCode());
payDetail.setQrReferNo(refLabel);
payDetail.setQrTransStatus(2);
payDetail.setReferNo(refLabel);

View File

@ -11,8 +11,11 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.KPayQRRequest;
import com.utsmyanmar.baselib.network.model.KPayRefund;
//import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptQRRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
@ -34,6 +37,8 @@ import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import com.utsmyanmar.paylibs.utils.LogUtil;
import java.text.DecimalFormat;
public class QRRefundFragment extends DataBindingFragment {
private SharedViewModel sharedViewModel;
@ -52,6 +57,8 @@ public class QRRefundFragment extends DataBindingFragment {
private boolean isPartialRefund = false;
CompositeDisposable refundDisposable = new CompositeDisposable();
CompositeDisposable refundStatusDisposable = new CompositeDisposable();
CompositeDisposable retrieveUpdateDisposable = new CompositeDisposable();
private static final String TAG = QRRefundFragment.class.getSimpleName();
@ -95,6 +102,7 @@ public class QRRefundFragment extends DataBindingFragment {
public void onDestroyView() {
super.onDestroyView();
refundDisposable.dispose();
refundStatusDisposable.dispose();
retrieveUpdateDisposable.dispose();
}
@ -167,7 +175,7 @@ public class QRRefundFragment extends DataBindingFragment {
.subscribe(
response -> {
dismissLoadingDialog();
handleRefundResponse(response, referenceNo);
handleRefundResponse(response, referenceNo, originalAmount);
},
throwable -> {
dismissLoadingDialog();
@ -182,7 +190,39 @@ public class QRRefundFragment extends DataBindingFragment {
refundDisposable.add(refundDi);
}
private void handleRefundResponse(KPayRefund.RefundResponse response, String referenceNo) {
private void callQueryRefund(String referenceNo, String orgAmount) {
String mid = payDetail.getMerchantNo();
KPayQRRequest.QRQueryRefundRequest request = KPayViewModel.getQueryRefundRequest(referenceNo, mid);
showLoadingDialog("Checking refund...");
refundStatusDisposable.add(KPayViewModel.checkRefundStatus(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
dismissLoadingDialog();
if (response == null || response.getResponse() == null) {
LogUtil.d(TAG, "Query refund: null response");
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
return;
}
LogUtil.d(TAG, "Query refund result: " + response.getResponse().getResult());
LogUtil.d(TAG, "Query refund status: " + response.getResponse().getRefundStatus());
handleRefundResponse(response, referenceNo, orgAmount);
}, throwable -> {
throwable.printStackTrace();
dismissLoadingDialog();
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
}));
}
private void handleRefundResponse(KPayRefund.RefundResponse response, String referenceNo, String orgAmount) {
if (response != null && response.getResponse() != null && "REFUND_SUCCESS".equalsIgnoreCase(response.getResponse().getRefundStatus())) {
LogUtil.d(TAG, "Refund successful!");
@ -192,16 +232,25 @@ public class QRRefundFragment extends DataBindingFragment {
payDetail.setAmount(refundAmount == null ? 0 : POSUtil.getInstance().convertAmount(refundAmount));
payDetail.setOriginalTransDate(dateTime);
payDetail.setQrTransStatus(1);
payDetail.setQrReferNo(referenceNo);
payDetail.setReferNo(referenceNo);
payDetail.setIsCanceled(true);
// sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, true, "Refund success"));
retrievedUpdatePayDetail(referenceNo);
} else {
} else if (response != null && response.getResponse() != null
&& "DUPLICATED_REFUND_REQ_NO".equalsIgnoreCase(response.getResponse().getCode())){
LogUtil.d(TAG, "Duplicated refund request, querying actual result...");
callQueryRefund(referenceNo , orgAmount);
} else if (response != null && response.getResponse() != null
&& "REFUNDING".equalsIgnoreCase(response.getResponse().getRefundStatus())){
LogUtil.d(TAG, "Refunding refund request, querying actual result...");
callQueryRefund(referenceNo , orgAmount);
}
else {
LogUtil.d(TAG, "Refund failed!");
payDetail.setQrTransStatus(-1);
payDetail.setQrReferNo(referenceNo);
payDetail.setReferNo(referenceNo);
String errorMsg = "Refund failed";
@ -210,6 +259,8 @@ public class QRRefundFragment extends DataBindingFragment {
}
payDetail.setTradeResultDes(errorMsg);
// sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, true, "RESP CODE [68]\n Refund Failed"));
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
}
@ -328,4 +379,6 @@ public class QRRefundFragment extends DataBindingFragment {
}
}
}
}

View File

@ -2,7 +2,6 @@ package com.utsmm.kbz.ui.kpay;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@ -10,9 +9,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.util.EReceiptUtil;
import com.utsmm.kbz.util.enums.TransResultStatus;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.DemoQRStatusRequest;
import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest;
import com.utsmyanmar.baselib.network.model.KPayQRRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.TimeoutCallback;
import com.utsmyanmar.ecr.ECRHelper;
@ -123,6 +125,7 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
terminalId = TransactionUtil.getInstance().getQRTerminalId();
merchantId = TransactionUtil.getInstance().getQRMerchantId();
refLabel = sharedViewModel.qrRefNum.getValue();
// tradeData = waveViewModel.getTradeData();
@ -132,6 +135,7 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
sharedViewModel.insertPayDetail(payDetail);
}
@ -180,14 +184,25 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
initSetup();
calculateIntervalAndCount();
LogUtil.d(TAG,"intervalInSec: "+intervalInSec+" totalCount: "+totalCount);
setUpTimeout();
setUpCountDown();
requireActivity().getOnBackPressedDispatcher().addCallback(
getViewLifecycleOwner(),
new androidx.activity.OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
showExitConfirmation();
}
}
);
}
private void setUpCountDown() {
setupTimeout(10, new TimeoutCallback() {
int totalTime = intervalInSec * totalCount;
setupTimeout(totalTime, new TimeoutCallback() {
@Override
public void onTrick(long trickTime) {
sharedViewModel.countDownTxt.setValue((trickTime / 1000)+"");
@ -272,9 +287,6 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
Disposable setUpDi = Observable.interval(intervalInSec, TimeUnit.SECONDS) // Emit an item every 10 seconds
.take(totalCount) // Take only 3 emissions
.flatMap(tick -> {
// Make the API call here using Retrofit
// WaveStatusRequest request = new WaveStatusRequest(refLabel, merchantId);
// DemoQRStatusRequest request = new DemoQRStatusRequest(refLabel, merchantId);
KPayQRQueryRequest.QRQueryRequest request = kPayViewModel.getQrStatus(refLabel,merchantId);
return kPayViewModel.checkStatus(request)
@ -297,17 +309,29 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
* trans is success !*/
// String transStatus = response.getTransactionStatus();
// String transTime = response.getResponse().;
if(response.getResponse().getWalletIdentifier() != null) {
payDetail.setCustomerMobile(response.getResponse().getWalletIdentifier());
} else {
payDetail.setCustomerMobile("KBZPay");
}
payDetail.setCustomerMobile("KBZ PAY");
payDetail.setQrReferNo(refLabel);
if(response.getResponse().getMmqrRef() != null) {
payDetail.setQrReferNo(response.getResponse().getMmqrRef());
}
payDetail.setTC(response.getResponse().getTradeStatus());
payDetail.setQrTransId(response.getResponse().getMmOrderId());
payDetail.setReferNo(refLabel);
payDetail.setApprovalCode(response.getResponse().getMmOrderId());
payDetail.setQrTransStatus(1);
payDetail.setOriginalTransDate(response.getResponse().getPaySuccessTime());
payDetail.setTradeDateAndTime(response.getResponse().getPaySuccessTime());
retrievedUpdatePayDetail(refLabel, payDetail,false);
EReceiptRequest request = EReceiptUtil.getInstance().generateQRReceipt(payDetail, TransResultStatus.SUCCESS);
sharedViewModel.pushReceipt(request);
return;
}
@ -317,6 +341,11 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
payDetail.setQrTransStatus(2);
}
EReceiptRequest request = EReceiptUtil.getInstance().generateQRReceipt(payDetail, TransResultStatus.FAIL);
sharedViewModel.pushReceipt(request);
sharedViewModel.insertPayDetail(payDetail);
sharedViewModel.payDetail.postValue(payDetail);
safeNavigateToRouteId();
@ -324,16 +353,24 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
} catch (Exception e) {
LogUtil.d(TAG, "On Exception::");
// sharedViewModel.pushReceipt(buildEReceiptQRReceipt(payDetail, false, "QR Failed! :" + e.getMessage()));
e.printStackTrace();
if (count == totalCount) {
if(payDetail.getQrTransStatus() != -1) {
payDetail.setQrTransStatus(2);
}
EReceiptRequest request = EReceiptUtil.getInstance().generateQRReceipt(payDetail, TransResultStatus.TIME_OUT);
sharedViewModel.pushReceipt(request);
sharedViewModel.insertPayDetail(payDetail);
sharedViewModel.payDetail.postValue(payDetail);
safeNavigateToRouteId();
}
}
@ -397,6 +434,48 @@ public class QRTransactionFragment extends DataBindingFragment implements DataBi
popBackStack();
}
private void showExitConfirmation() {
new androidx.appcompat.app.AlertDialog.Builder(requireContext())
.setTitle("Exit QR Transaction?")
.setMessage("Are you sure you want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", (dialog, which) -> {
dialog.dismiss();
if (sharedViewModel.isEcr.getValue() != null) {
if (sharedViewModel.isEcr.getValue()) {
finishECR();
}
}
closeOrder();
popBackStack();
})
.setNegativeButton("No", (dialog, which) -> dialog.dismiss())
.show();
}
private void closeOrder(){
String refNo = sharedViewModel.qrRefNum.getValue();
String mid = TransactionUtil.getInstance().getQRMerchantId();
if (refNo == null || mid == null) {
LogUtil.e("QR", "Missing ref or merchant ID. Cannot close order.");
return;
}
KPayQRRequest.CloseOrderRequest request = kPayViewModel.closeQrOrder(refNo, mid);
kPayViewModel.closeQrOrderApi(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
resp -> {
LogUtil.d("CloseOrder", "Result = " + resp.getResponse().getResult());
LogUtil.d("CloseOrder", "Msg = " + resp.getResponse().getMsg());
},
err -> {
LogUtil.e("CloseOrder", "Error = " + err.getMessage());
}
);
}
private String getECRResponseMessage() {
sharedViewModel.payDetail.setValue(payDetail);

View File

@ -7,7 +7,6 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.ui.ListDialog;
import com.utsmyanmar.baselib.util.DataBindingConfig;
@ -68,20 +67,6 @@ public class TestFragment extends DataBindingFragment {
return 0;
}
private String[] getCandidateNames(List<EMVCandidateV2> candiList) {
if (candiList == null || candiList.size() == 0) return new String[0];
String[] result = new String[candiList.size()];
for (int i = 0; i < candiList.size(); i++) {
EMVCandidateV2 candi = candiList.get(i);
String name = candi.appPreName;
name = TextUtils.isEmpty(name) ? candi.appLabel : name;
name = TextUtils.isEmpty(name) ? candi.appName : name;
name = TextUtils.isEmpty(name) ? "" : name;
result[i] = name;
LogUtil.e(Constant.TAG, "EMVCandidateV2: " + name);
}
return result;
}
private void showCandiListDialog(String[] list) {
if (mListDialog == null) {

View File

@ -72,6 +72,7 @@ public class DetailReportFragment extends DataBindingFragment {
super.onResume();
setToolBarTitleWithBackIcon(getResourceString(R.string.txt_subtitle_print_detail_report));
sharedViewModel.printerDisabled.setValue(!SystemParamsOperation.getInstance().getPrinterEnabled());
showLoadingView();
}
@ -93,12 +94,8 @@ public class DetailReportFragment extends DataBindingFragment {
managementViewModel.hostTypeDR.setValue("PAYMENT");
managementViewModel.cardTypeDR.setValue("QR PAY");
} else if(sharedViewModel.hostType.getValue() == HostType.MPU) {
managementViewModel.hostTypeDR.setValue("MPU");
managementViewModel.cardTypeDR.setValue("MPU");
} else if(sharedViewModel.hostType.getValue() == HostType.VISA_MASTER) {
managementViewModel.hostTypeDR.setValue("VISA/MASTER");
managementViewModel.cardTypeDR.setValue("VISA/MASTER");
managementViewModel.hostTypeDR.setValue("CARD");
managementViewModel.cardTypeDR.setValue("CARD");
}
}
@ -152,7 +149,12 @@ public class DetailReportFragment extends DataBindingFragment {
if(sharedViewModel.hostType.getValue() == HostType.MPU) {
if(payDetail.getAccountType().equals("MPU") && payDetail.getTransactionType() != TransactionsType.MMQR_REFUND.value && payDetail.getTransactionType() != TransactionsType.MMQR.value && payDetail.getTransactionType() != TransactionsType.REVERSAL.value && payDetail.getTransactionType() != TransactionsType.SETTLEMENT.value && POSUtil.getInstance().getYesterdayDate().compareTo(POSUtil.getInstance().getDateByString(payDetail.transDate)) < 1) {
if(payDetail.getTransactionType() != TransactionsType.MMQR_REFUND.value
&& payDetail.getTransactionType() != TransactionsType.MMQR.value
&& payDetail.getTransactionType() != TransactionsType.MMQR_SETTLEMENT.value
&& payDetail.getTransactionType() != TransactionsType.REVERSAL.value
&& payDetail.getTransactionType() != TransactionsType.SETTLEMENT.value
&& POSUtil.getInstance().getYesterdayDate().compareTo(POSUtil.getInstance().getDateByString(payDetail.transDate)) < 1) {
boolean isNeedMinusSign = payDetail.getTransactionType() == TransactionsType.VOID.value || payDetail.getTransactionType() == TransactionsType.REFUND.value;
lists.add(payDetail);
count++;
@ -174,19 +176,6 @@ public class DetailReportFragment extends DataBindingFragment {
totalAmount += payDetail.getAmount();
}
}
} if(sharedViewModel.hostType.getValue() == HostType.VISA_MASTER) {
if(!payDetail.getAccountType().equals("MPU") && payDetail.getTransactionType() != TransactionsType.MMQR_REFUND.value && payDetail.getTransactionType() != TransactionsType.MMQR.value && payDetail.getTransactionType() != TransactionsType.REVERSAL.value && payDetail.getTransactionType() != TransactionsType.SETTLEMENT.value && POSUtil.getInstance().getYesterdayDate().compareTo(POSUtil.getInstance().getDateByString(payDetail.transDate)) < 1) {
boolean isNeedMinusSign = payDetail.getTransactionType() == TransactionsType.VOID.value || payDetail.getTransactionType() == TransactionsType.REFUND.value;
lists.add(payDetail);
count++;
if(isNeedMinusSign) {
totalAmount -= payDetail.getAmount();
} else {
totalAmount += payDetail.getAmount();
}
}
}
}
@ -222,6 +211,10 @@ public class DetailReportFragment extends DataBindingFragment {
}
public void onPrint() {
if (!SystemParamsOperation.getInstance().getPrinterEnabled()) {
showDeclineDialog("Printer is disabled!");
return;
}
TradeData tradeData = Params.newTrade(false);
PayDetail payDetail = tradeData.getPayDetail();
payDetail.setTransType(TransactionsType.DETAIL_REPORT.name);

View File

@ -10,7 +10,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemViewDetailReportBinding;
import com.utsmm.kbz.databinding.ItemViewHistoryBinding;
import java.util.List;
@ -35,7 +35,7 @@ public class HistoryAdapter extends RecyclerView.Adapter<HistoryAdapter.ViewHold
@NonNull
@Override
public HistoryAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemViewDetailReportBinding binding = DataBindingUtil.inflate(
ItemViewHistoryBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_view_history, parent, false);
@ -58,9 +58,9 @@ public class HistoryAdapter extends RecyclerView.Adapter<HistoryAdapter.ViewHold
public class ViewHolder extends RecyclerView.ViewHolder{
public ItemViewDetailReportBinding itemBinding;
public ItemViewHistoryBinding itemBinding;
public ViewHolder(ItemViewDetailReportBinding itemRowBinding){
public ViewHolder(ItemViewHistoryBinding itemRowBinding){
super(itemRowBinding.getRoot());
this.itemBinding = itemRowBinding;
}

View File

@ -2,9 +2,11 @@ package com.utsmm.kbz.ui.management;
import androidx.lifecycle.Observer;
import com.utsmyanmar.baselib.BaseApplication;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.TransMenu;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
@ -53,6 +55,7 @@ public class ManagementFunctionFragment extends DataBindingFragment {
public void onResume() {
super.onResume();
sharedViewModel.printerDisabled.setValue(!SystemParamsOperation.getInstance().getPrinterEnabled());
setToolBarTitleWithBackIcon(getString(R.string.title_select_function));
}
@ -122,6 +125,7 @@ public class ManagementFunctionFragment extends DataBindingFragment {
public void onClickReprintLastTrans() {
sharedViewModel.setTransMenu(TransMenu.REPRINT);
sharedViewModel.suppressReprintButtons.setValue(false);
observeLastTransaction();
}

View File

@ -27,6 +27,7 @@ public class ManagementViewModel extends ViewModel {
public SingleLiveEvent<String> processCodeText = new SingleLiveEvent<>();
public SingleLiveEvent<PayDetail> payDetail = new SingleLiveEvent<>();
public SingleLiveEvent<List<PayDetail>> payDetails = new SingleLiveEvent<>();
public SingleLiveEvent<String> panErrorMsg = new SingleLiveEvent<>();
@ -75,6 +76,10 @@ public class ManagementViewModel extends ViewModel {
return repository.getLastSettlement(voucherNo);
}
public LiveData<List<PayDetail>> getLastSettlementQR(String voucherNo){
return repository.getLastSettlementQR(voucherNo);
}
public LiveData<List<PayDetail>> getLastTransaction(String voucherNo){
return repository.getLastTransaction(voucherNo);
}

View File

@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmm.kbz.util.helper.KeyboardHelper;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.KPayQRQueryRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
@ -128,7 +129,7 @@ public class ReprintAnyTransactionFragment extends DataBindingFragment {
private void searchByTrace(String trace) {
for (PayDetail s : lists) {
if(s.getVoucherNo().equals(trace)) {
if(s.getVoucherNo().contains(trace)) {
searchedLists.add(s);
}
}
@ -144,6 +145,18 @@ public class ReprintAnyTransactionFragment extends DataBindingFragment {
cardViewAdapter.updateList(searchedLists);
}
private void searchByTraceAndRRN(String input) {
for (PayDetail s : lists) {
boolean matchTrace = s.getVoucherNo() != null && s.getVoucherNo().contains(input);
boolean matchRRN = s.getReferNo() != null && s.getReferNo().contains(input);
if (matchTrace || matchRRN) {
searchedLists.add(s);
}
}
cardViewAdapter.updateList(searchedLists);
}
public class ClickEvent {
public void onClickSearch() {
@ -151,19 +164,13 @@ public class ReprintAnyTransactionFragment extends DataBindingFragment {
String input = managementViewModel.txtRRNTrace.getValue();
if(input != null && !input.isEmpty()) {
if(input.length() == 6) {
searchByTrace(input);
} else if(input.length() == 12) {
searchByRRN(input);
}
searchByTraceAndRRN(input);
} else {
searchedLists.addAll(lists);
}
cardViewAdapter.updateList(searchedLists);
KeyboardHelper.hide(requireActivity());
}
}
@ -296,24 +303,33 @@ public class ReprintAnyTransactionFragment extends DataBindingFragment {
// LogUtil.d(TAG,"Transaction settlement config : "+pay.isSettlementEnabled());
// LogUtil.d(TAG,"Transaction batch : "+pay.getBatchNo());
// LogUtil.d(TAG,"Current batch : "+SystemParamsOperation.getInstance().getCurrentBatchNum());
//
// LogUtil.d(TAG,"----------------------------------------------------------");
if(sharedViewModel.transactionsType.getValue() != null && sharedViewModel.transactionsType.getValue() == TransactionsType.MMQR_INQUIRY_STATUS) {
LogUtil.d(TAG,"----------------------------------------------------------");
if(sharedViewModel.transactionsType.getValue() != null && sharedViewModel.transactionsType.getValue() == TransactionsType.MMQR_HISTORY) {
// Wave Inquiry Status::
// April 2, 2024 QR Inquiry status check is for unsuccessful transactions.
if(yesterday.compareTo(transDate) <= 0 && pay.getTransactionType() == TransactionsType.MMQR.value && pay.getQrTransStatus() != 1) {
// if(yesterday.compareTo(transDate) <= 0 && (pay.getTransactionType() == TransactionsType.MMQR.value || pay.getTransactionType() == TransactionsType.MMQR_REFUND.value) ) {
//
// filteredLists.add(pay);
// }
if(yesterday.compareTo(transDate) <= 0
&& (pay.getTransactionType() == TransactionsType.MMQR.value
|| pay.getTransactionType() == TransactionsType.MMQR_REFUND.value)
&& pay.getQrTransStatus() == 1) {
filteredLists.add(pay);
}
} else {
// Reprint Any Transaction::
if(yesterday.compareTo(transDate) <= 0 && pay.getTransactionType() != TransactionsType.SETTLEMENT.value && pay.getTransactionType() != TransactionsType.REVERSAL.value) {
if(yesterday.compareTo(transDate) <= 0
&& pay.getTransactionType() != TransactionsType.SETTLEMENT.value
&& pay.getTransactionType() != TransactionsType.MMQR_SETTLEMENT.value
&& pay.getTransactionType() != TransactionsType.REVERSAL.value) {
// April 2, 2024 client requested to add QR host at host selection type
if(sharedViewModel.hostType.getValue() == HostType.MPU) {
if(pay.getAccountType().equals("MPU") && pay.getTransactionType() != TransactionsType.MMQR.value && pay.getTransactionType() != TransactionsType.MMQR_REFUND.value) {
if(pay.getTransactionType() != TransactionsType.MMQR.value && pay.getTransactionType() != TransactionsType.MMQR_REFUND.value) {
filteredLists.add(pay);
}
@ -323,10 +339,6 @@ public class ReprintAnyTransactionFragment extends DataBindingFragment {
filteredLists.add(pay);
}
}
} else if(sharedViewModel.hostType.getValue() == HostType.VISA_MASTER) {
if(!pay.getAccountType().equals("MPU") && pay.getTransactionType() != TransactionsType.MMQR.value && pay.getTransactionType() != TransactionsType.MMQR_REFUND.value) {
filteredLists.add(pay);
}
}
}
@ -367,7 +379,12 @@ public class ReprintAnyTransactionFragment extends DataBindingFragment {
* */
// sharedViewModel.transMenu.postValue(TransMenu.REPRINT);
sharedViewModel.setTransMenu(TransMenu.REPRINT);
payDetail.setTransType(payDetail.getTransType()+"(REPRINT)");
sharedViewModel.suppressReprintButtons.setValue(false);
// payDetail.setTransType(payDetail.getTransType()+"(REPRINT)");
//tempo guarding the Reprint duplicate by kmk
if (!payDetail.getTransType().contains("REPRINT")) {
payDetail.setTransType(payDetail.getTransType() + "(REPRINT)");
}
sharedViewModel.payDetail.postValue(payDetail);
routeId = R.id.action_reprintAnyTransactionFragment_to_confirmTransactionFragment;

View File

@ -1,5 +1,7 @@
package com.utsmm.kbz.ui.management;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.view.View;
@ -13,8 +15,8 @@ import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.DialogCallback;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.QRSettleData;
import com.utsmyanmar.paylibs.print.PrintHelper;
import com.utsmyanmar.paylibs.print.PrintReceipt;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.print.printx.PrintXStatus;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
@ -26,6 +28,9 @@ import com.utsmm.kbz.ui.core_viewmodel.TransProcessViewModel;
import java.util.List;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.enums.HostType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmyanmar.paylibs.utils.POSUtil;
public class ReprintReceiptFragment extends DataBindingFragment {
@ -34,9 +39,11 @@ public class ReprintReceiptFragment extends DataBindingFragment {
private ManagementViewModel managementViewModel;
private SharedViewModel sharedViewModel;
private static final String TAG = PrintReceipt.class.getSimpleName();
private static final String TAG = ReprintReceiptFragment.class.getSimpleName();
private PayDetail payDetail;
private ObjectAnimator slideUpAnimator;
private View printingCard;
@Override
protected void initViewModel() {
@ -73,6 +80,7 @@ public class ReprintReceiptFragment extends DataBindingFragment {
@Override
public void onPause() {
super.onPause();
stopPrintingAnimation();
}
@ -102,13 +110,13 @@ public class ReprintReceiptFragment extends DataBindingFragment {
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
printingCard = mBinding.getRoot().findViewById(R.id.printingCard);
}
private void updateUIPrintReceiptFailure(PayDetail payDetail) {
sharedViewModel.printReceiptMsg.postValue(getString(R.string.txt_printing_settlement_report_failure));
sharedViewModel.reprintBtnLayout.postValue(0);
ReprintReceiptFragment.this.payDetail = payDetail;
// ReprintReceiptFragment.this.payDetail = payDetail;
}
private void checkPaperExists(PayDetail payDetail) {
@ -137,45 +145,98 @@ public class ReprintReceiptFragment extends DataBindingFragment {
}
} else if (PrintHelper.getInstance().paperRollStatus() == SdkResult.Success) {
startPrintingAnimation();
printReceipt(payDetail);
}
}
private void observeLastSettlement() {
managementViewModel.getLastSettlement(SystemParamsOperation.getInstance().getCurrentSerialNum()).observe(getViewLifecycleOwner(), new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
if (payDetails != null) {
if (payDetails.size() != 0) {
PayDetail payDetail = payDetails.get(payDetails.size() - 1);
payDetail.setTransType( payDetail.getTransType()+"(REPRINT)");
checkPaperExists(payDetail);
} else {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
navigateMainScreen();
}
}
}
});
payDetail = sharedViewModel.payDetail.getValue();
if (payDetail == null) {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
navigateMainScreen();
return;
}
sharedViewModel.totalAmount.postValue(payDetail.getAmount());
sharedViewModel.setAmount(POSUtil.getInstance().getDecimalAmountSeparatorFormat(payDetail.getAmount()));
String transType = payDetail.getTransType();
if (transType == null) {
transType = "";
}
payDetail.setTransType(transType + "(REPRINT)");
checkPaperExists(payDetail);
// managementViewModel.payDetail.observe(getViewLifecycleOwner(), new Observer<PayDetail>() {
// @Override
// public void onChanged(PayDetail pay) {
// if (pay != null) {
//
// payDetail = pay;
//
//
//
//
//
// } else {
// showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
// navigateMainScreen();
// }
// }
// });
}
private void printReceipt(PayDetail payDetail) {
PrintXReceipt.getInstance().printSmileSettlementReport(payDetail, new PrintXStatus() {
@Override
public void onSuccess() {
delayFunctionCall(ReprintReceiptFragment.this::navigateMainScreen);
}
@Override
public void onFailure() {
updateUIPrintReceiptFailure(payDetail);
if (payDetail.getTransactionType() == TransactionsType.MMQR_SETTLEMENT.value) {
checkPaperExists(payDetail);
}
});
PrintXReceipt.getInstance().printQRSettlementReport(payDetail, payDetail.getQrSettleData(), printXStatus);
} else {
PrintXReceipt.getInstance().printSmileSettlementReport(payDetail, printXStatus);
}
}
private PrintXStatus printXStatus = new PrintXStatus() {
@Override
public void onSuccess() {
stopPrintingAnimation();
delayFunctionCall(ReprintReceiptFragment.this::navigateMainScreen);
}
@Override
public void onFailure() {
stopPrintingAnimation();
updateUIPrintReceiptFailure(payDetail);
// checkPaperExists(payDetail);
}
};
private void startPrintingAnimation() {
if (printingCard == null) {
return;
}
if (slideUpAnimator != null && slideUpAnimator.isRunning()) {
return;
}
slideUpAnimator = ObjectAnimator.ofFloat(printingCard, View.TRANSLATION_Y, 0f, -20f);
slideUpAnimator.setDuration(420);
slideUpAnimator.setRepeatMode(ValueAnimator.REVERSE);
slideUpAnimator.setRepeatCount(ValueAnimator.INFINITE);
slideUpAnimator.start();
}
private void stopPrintingAnimation() {
if (slideUpAnimator != null) {
slideUpAnimator.cancel();
slideUpAnimator = null;
}
if (printingCard != null) {
printingCard.setTranslationY(0f);
}
}

View File

@ -72,6 +72,7 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
}
}
checkQRButtonVisibility();
// try {
// int result = MyApplication.getInstance().basicOptBinder.setScreenMode(1);
// } catch (RemoteException e) {
@ -168,14 +169,16 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
observerData();
isReturnTransactions();
}
private void isReturnTransactions() {
if(sharedViewModel.getTransMenu().getValue() == TransMenu.VOID || sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_COMPLETE_VOID || sharedViewModel.getTransMenu().getValue() == TransMenu.REVERSAL || sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_FULL_VOID) {
private void checkQRButtonVisibility() {
if(sharedViewModel.getTransMenu().getValue() == TransMenu.VOID || sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_COMPLETE_VOID || sharedViewModel.getTransMenu().getValue() == TransMenu.REVERSAL || sharedViewModel.getTransMenu().getValue() == TransMenu.PRE_AUTH_FULL_VOID ) {
sharedViewModel.qrPayVisibility.setValue(8);
} else {
sharedViewModel.qrPayVisibility.setValue(0);
}
}
@ -207,21 +210,43 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
private void clearReversal() {
checkPointReversal = false;
sharedViewModel.getReversalTransaction(SystemParamsOperation.getInstance().getCurrentSerialNum()).observe(getViewLifecycleOwner(), payDetail -> {
if (payDetail != null) {
if (!checkPointReversal) {
payDetail.setIsNeedReversal(false);
sharedViewModel.updatePayDetail(payDetail);
checkPointReversal = true;
// sharedViewModel.getReversalTransaction(SystemParamsOperation.getInstance().getCurrentSerialNum()).observe(getViewLifecycleOwner(), payDetail -> {
// if (payDetail != null) {
// if (!checkPointReversal) {
// payDetail.setIsNeedReversal(false);
// sharedViewModel.updatePayDetail(payDetail);
// checkPointReversal = true;
//
// showSuccessDialog("Clear reversal success!");
// }
// } else {
// if (!checkPointReversal) {
// showInfoDialog("Clear reversal", "No reversal found!");
// checkPointReversal = true;
// }
//
// }
// });
showSuccessDialog("Clear reversal success!");
}
} else {
if (!checkPointReversal) {
showInfoDialog("Clear reversal", "No reversal found!");
checkPointReversal = true;
}
sharedViewModel.getAllReversalTransaction().observe(getViewLifecycleOwner(), new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
if(payDetails != null && !payDetails.isEmpty()) {
if(!checkPointReversal) {
for (PayDetail payDetail : payDetails) {
payDetail.setIsNeedReversal(false);
sharedViewModel.updatePayDetail(payDetail);
}
checkPointReversal = true;
showSuccessDialog("Clear reversal success!");
}
} else {
if (!checkPointReversal) {
showInfoDialog("Clear reversal", "No reversal found!");
checkPointReversal = true;
}
}
}
});
}
@ -262,27 +287,62 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
});
}
private void observeLastSettlement() {
managementViewModel.getLastSettlement(SystemParamsOperation.getInstance().getCurrentSerialNum()).observe(getViewLifecycleOwner(), new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
if (payDetails != null) {
if (!payDetails.isEmpty()) {
routeId = R.id.action_selectHostFragment_to_settlementTransactionFragment;
} else {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
routeId = R.id.action_selectHostFragment_to_nav_main;
}
safeNavigateToRouteId();
} else {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
routeId = R.id.action_selectHostFragment_to_nav_main;
safeNavigateToRouteId();
}
private PayDetail findLastByType(List<PayDetail> payDetails, int transactionType) {
if (payDetails == null) {
return null;
}
for (int i = payDetails.size() - 1; i >= 0; i--) {
PayDetail payDetail = payDetails.get(i);
if (payDetail != null && payDetail.getTransactionType() == transactionType) {
return payDetail;
}
});
}
return null;
}
private void observeLastSettlement() {
HostType hostType = sharedViewModel.hostType.getValue();
if (hostType == HostType.QR) {
managementViewModel.getLastSettlementQR(SystemParamsOperation.getInstance().getCurrentSerialNum())
.observe(getViewLifecycleOwner(), payDetails -> handleLastSettlement(payDetails, HostType.QR));
} else {
managementViewModel.getLastSettlement(SystemParamsOperation.getInstance().getCurrentSerialNum())
.observe(getViewLifecycleOwner(), payDetails -> handleLastSettlement(payDetails, hostType));
}
}
private void handleLastSettlement(List<PayDetail> payDetails, HostType hostType) {
if (payDetails == null || payDetails.isEmpty()) {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
routeId = R.id.action_selectHostFragment_to_nav_main;
safeNavigateToRouteId();
return;
}
PayDetail payDetail;
if (hostType == HostType.QR) {
payDetail = findLastByType(payDetails, TransactionsType.MMQR_SETTLEMENT.value);
if (payDetail != null) {
managementViewModel.payDetails.setValue(java.util.Collections.singletonList(payDetail));
routeId = R.id.action_selectHostFragment_to_QRSettlementTransactionFragment;
} else {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
routeId = R.id.action_selectHostFragment_to_nav_main;
}
} else {
payDetail = findLastByType(payDetails, TransactionsType.SETTLEMENT.value);
if (payDetail != null) {
routeId = R.id.action_selectHostFragment_to_settlementTransactionFragment;
} else {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
routeId = R.id.action_selectHostFragment_to_nav_main;
}
}
safeNavigateToRouteId();
}
private void observeRoute() {
switch (Objects.requireNonNull(sharedViewModel.getTransMenu().getValue())) {
case REVERSAL:
@ -296,13 +356,14 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
clearBatch();
break;
case LAST_SETTLEMENT:
if(sharedViewModel.hostType.getValue() == HostType.MPU)
observeLastSettlement();
else {
showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
routeId = R.id.action_selectHostFragment_to_nav_main;
safeNavigateToRouteId();
}
observeLastSettlement();
// if(sharedViewModel.hostType.getValue() != HostType.QR) {
// observeLastSettlement();
// } else {
// showSingleInfoDialog(getString(R.string.txt_no_trans_to_print));
// routeId = R.id.action_selectHostFragment_to_nav_main;
// safeNavigateToRouteId();
// }
break;
case CLEAR_REVERSAL:
clearReversal();
@ -336,13 +397,11 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
}
break;
case TIP_ADJUSTMENT:
if(sharedViewModel.hostType.getValue() == HostType.VISA_MASTER) {
if(sharedViewModel.hostType.getValue() != HostType.QR) {
routeId = R.id.action_selectHostFragment_to_inputTraceFragment;
} else {
routeId = R.id.action_selectHostFragment_to_errorFragment;
sharedViewModel.set_errorFragmentMsg(getResourceString(R.string.txt_tip_adjust_not_allowed));
}
safeNavigateToRouteId();
break;
@ -393,23 +452,9 @@ public class SelectHostFragment extends DataBindingFragment implements DataBindi
public class ClickEvent {
public void onClickVisaMaster() {
sharedViewModel.hostType.setValue(HostType.VISA_MASTER);
if(SystemParamsOperation.getInstance().isEmvEnabled()) {
observeRoute();
} else {
showSingleInfoDialog("Currently this menu is unavailable!");
}
}
public void onClickMPU() {
public void onClickCards() {
sharedViewModel.hostType.setValue(HostType.MPU);
observeRoute();
}
public void onClickQR() {

View File

@ -5,6 +5,8 @@ import android.app.TimePickerDialog;
import androidx.lifecycle.Observer;
import com.google.android.material.timepicker.MaterialTimePicker;
import com.google.android.material.timepicker.TimeFormat;
import com.nexgo.oaf.apiv3.SdkResult;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
@ -49,6 +51,8 @@ public class TransactionSummaryFragment extends DataBindingFragment {
final Calendar myCalendar = Calendar.getInstance();
final Calendar myCalendar2 = Calendar.getInstance();
ArrayList<PayDetail> filteredLists = new ArrayList<>();
@Override
protected void initViewModel() {
managementViewModel = getFragmentScopeViewModel(ManagementViewModel.class);
@ -60,6 +64,7 @@ public class TransactionSummaryFragment extends DataBindingFragment {
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_transaction_summary_screen, BR.manageViewModel,managementViewModel)
.addBindingParam(BR.summaryViewModel,summaryViewModel)
.addBindingParam(BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.click,new ClickEvent());
}
@ -84,6 +89,8 @@ public class TransactionSummaryFragment extends DataBindingFragment {
setToolBarTitleWithBackIcon(getString(R.string.title_trans_summary));
sharedViewModel.printerDisabled.setValue(!SystemParamsOperation.getInstance().getPrinterEnabled());
summaryViewModel.resetField();
}
@ -153,11 +160,19 @@ public class TransactionSummaryFragment extends DataBindingFragment {
if (isStart) {
String selectedDate = dateFormat.format(myCalendar.getTime());
summaryViewModel.startDate.postValue(selectedDate);
LogUtil.d(TAG, "Start date updated: " + selectedDate);
// LogUtil.d(TAG, "Start date updated: " + selectedDate);
// STATIC START TIME
// summaryViewModel.startHr.setValue("00");
// summaryViewModel.startMin.setValue("00");
summaryViewModel.startDayOrNight.setValue(getString(R.string.txt_am));
} else {
String selectedDate = dateFormat.format(myCalendar2.getTime());
summaryViewModel.endDate.postValue(selectedDate);
LogUtil.d(TAG, "End date updated: " + selectedDate);
// STATIC END TIME
// summaryViewModel.endHr.setValue("23");
// summaryViewModel.endMin.setValue("59");
summaryViewModel.endDayOrNight.setValue(getString(R.string.txt_pm));
// LogUtil.d(TAG, "End date updated: " + selectedDate);
}
} catch (Exception e) {
LogUtil.e(TAG, "Error updating date label: " + e.getMessage());
@ -211,10 +226,15 @@ public class TransactionSummaryFragment extends DataBindingFragment {
}
}
private SettleData processTransactionData(List<PayDetail> payDetailList, SimpleDateFormat formatter,
Date yesterday, Date start, Date end) {
private SettleData processTransactionData(List<PayDetail> payDetailList,
SimpleDateFormat formatter,
Date yesterday,
Date start,
Date end) {
try {
// Create a local helper class for readable mutability
// ✅ IMPORTANT: clear old data
filteredLists.clear();
class Counter {
int count = 0;
long amount = 0L;
@ -237,84 +257,59 @@ public class TransactionSummaryFragment extends DataBindingFragment {
Counter preAuthCompVoid = new Counter();
Counter refund = new Counter();
// ✅ Use ONE consistent format
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy HH:mm", Locale.getDefault());
ArrayList<PayDetail> filteredLists = new ArrayList<>();
// Filter transactions by date
for (PayDetail pay : payDetailList) {
try {
Date transDate = formatter.parse(pay.getTransDate());
if (yesterday.compareTo(transDate) <= 0) {
filteredLists.add(pay);
}
} catch (ParseException e) {
LogUtil.e(TAG, "Error parsing transaction date: " + e.getMessage());
}
}
LogUtil.d(TAG, "Filtered transactions count: " + filteredLists.size());
// Process filtered transactions
for (PayDetail pay : filteredLists) {
try {
// ✅ Combine date + time correctly
Date currentDate = sdf.parse(pay.transDate + " " + pay.transTime);
if ((currentDate.after(start) || currentDate.equals(start)) &&
(currentDate.before(end) || currentDate.equals(end))) {
if (currentDate == null) continue;
// ✅ Correct inclusive range check
if (!currentDate.before(start) && !currentDate.after(end)) {
// ✅ Keep filtered list for printing
filteredLists.add(pay);
if (sharedViewModel.hostType.getValue() == HostType.QR) {
if (pay.getTransactionType() == TransactionsType.MMQR.value && pay.getQrTransStatus() == 1) {
wave.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.MMQR_REFUND.value && pay.getQrTransStatus() == 1) {
waveRefund.inc(pay.amount);
}
} else if (sharedViewModel.hostType.getValue() == HostType.MPU) {
if ("MPU".equals(pay.getAccountType())) {
if (pay.getTransactionType() == TransactionsType.VOID.value) {
voidTran.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.SALE.value) {
sale.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.CASH_OUT.value) {
ca.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_SALE.value) {
preAuth.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_VOID.value) {
preAuthVoid.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE.value) {
preAuthComp.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE_VOID.value) {
preAuthCompVoid.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.REFUND.value) {
refund.inc(pay.amount);
}
}
} else if (sharedViewModel.hostType.getValue() == HostType.VISA_MASTER) {
if (!"MPU".equals(pay.getAccountType())) {
if (pay.getTransactionType() == TransactionsType.VOID.value) {
voidTran.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.SALE.value) {
sale.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.CASH_OUT.value) {
ca.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_SALE.value) {
preAuth.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_VOID.value) {
preAuthVoid.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE.value) {
preAuthComp.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE_VOID.value) {
preAuthCompVoid.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.REFUND.value) {
refund.inc(pay.amount);
}
if (pay.getTransactionType() == TransactionsType.VOID.value) {
voidTran.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.SALE.value) {
sale.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.CASH_OUT.value) {
ca.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_SALE.value) {
preAuth.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_VOID.value) {
preAuthVoid.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE.value) {
preAuthComp.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.PRE_AUTH_COMPLETE_VOID.value) {
preAuthCompVoid.inc(pay.amount);
} else if (pay.getTransactionType() == TransactionsType.REFUND.value) {
refund.inc(pay.amount);
}
}
}
} catch (ParseException e) {
LogUtil.e(TAG, "Error parsing transaction datetime: " + e.getMessage());
}
}
LogUtil.d(TAG, "Filtered transactions count: " + filteredLists.size());
return new SettleData(
sale.count, sale.amount,
wave.count, wave.amount,
@ -327,6 +322,7 @@ public class TransactionSummaryFragment extends DataBindingFragment {
refund.count, refund.amount,
waveRefund.count, waveRefund.amount
);
} catch (Exception e) {
LogUtil.e(TAG, "Error processing transaction data: " + e.getMessage());
return null;
@ -337,9 +333,7 @@ public class TransactionSummaryFragment extends DataBindingFragment {
if (sharedViewModel.hostType.getValue() == HostType.QR) {
return "WALLET";
} else if (sharedViewModel.hostType.getValue() == HostType.MPU) {
return "MPU";
} else if (sharedViewModel.hostType.getValue() == HostType.VISA_MASTER) {
return "Visa/Master";
return "CARD";
}
return "";
}
@ -377,7 +371,7 @@ public class TransactionSummaryFragment extends DataBindingFragment {
private void printReceipt(PayDetail payDetail) {
try {
LogUtil.d(TAG, "Starting receipt print");
PrintXReceipt.getInstance().printSmileSummaryReport(payDetail, new PrintXStatus() {
PrintXReceipt.getInstance().printSmileSummaryReport(payDetail, filteredLists, sharedViewModel.hostType.getValue(), new PrintXStatus() {
@Override
public void onSuccess() {
LogUtil.d(TAG, "Receipt printed successfully");
@ -463,47 +457,104 @@ public class TransactionSummaryFragment extends DataBindingFragment {
}
public void onClickStartHr() {
showTimePicker(startTime, myCalendar);
// showTimePicker(startTime, myCalendar);
showMaterialTimePicker(myCalendar, true);
}
public void onClickEndHr() {
showTimePicker(endTime, myCalendar2);
// showTimePicker(endTime, myCalendar2);
showMaterialTimePicker(myCalendar2, false);
}
public void onClickStartMin() {
showTimePicker(startTime, myCalendar);
// showTimePicker(startTime, myCalendar);
showMaterialTimePicker(myCalendar, true);
}
public void onClickEndMin() {
showTimePicker(endTime, myCalendar2);
// showTimePicker(endTime, myCalendar2);
showMaterialTimePicker(myCalendar2, false);
}
public void onClickStartDayOrNight() {
showTimePicker(startTime, myCalendar);
// showTimePicker(startTime, myCalendar);
showMaterialTimePicker(myCalendar, true);
}
public void onClickEndDayOrNight() {
showTimePicker(endTime, myCalendar2);
// showTimePicker(endTime, myCalendar2);
showMaterialTimePicker(myCalendar2, false);
}
private void showTimePicker(TimePickerDialog.OnTimeSetListener listener, Calendar calendar) {
try {
TimePickerDialog timePickerDialog = new TimePickerDialog(requireContext(),
listener, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), false);
TimePickerDialog timePickerDialog =
new TimePickerDialog(
requireContext(),
listener,
calendar.get(Calendar.HOUR_OF_DAY),
calendar.get(Calendar.MINUTE),
false);
timePickerDialog.show();
} catch (Exception e) {
LogUtil.e(TAG, "Error showing time picker: " + e.getMessage());
}
}
private void showMaterialTimePicker(
Calendar calendar,
boolean isStart
) {
MaterialTimePicker timePicker = new MaterialTimePicker.Builder()
.setTimeFormat(TimeFormat.CLOCK_24H)
.setHour(calendar.get(Calendar.HOUR_OF_DAY))
.setMinute(calendar.get(Calendar.MINUTE))
.setInputMode(MaterialTimePicker.INPUT_MODE_KEYBOARD)
.setTitleText("Select Time")
.build();
timePicker.addOnPositiveButtonClickListener(v -> {
int hour = timePicker.getHour();
int minute = timePicker.getMinute();
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
String amPm = hour >= 12
? getString(R.string.txt_pm)
: getString(R.string.txt_am);
if (isStart) {
summaryViewModel.startHr.setValue(String.format(Locale.getDefault(), "%02d", hour));
summaryViewModel.startMin.setValue(String.format(Locale.getDefault(), "%02d", minute));
summaryViewModel.startDayOrNight.setValue(amPm);
} else {
summaryViewModel.endHr.setValue(String.format(Locale.getDefault(), "%02d", hour));
summaryViewModel.endMin.setValue(String.format(Locale.getDefault(), "%02d", minute));
summaryViewModel.endDayOrNight.setValue(amPm);
}
LogUtil.d(TAG, (isStart ? "Start" : "End") +
" time selected: " + hour + ":" + minute);
});
timePicker.show(getParentFragmentManager(), "material_time_picker");
}
public void onPrint() {
try {
if (!SystemParamsOperation.getInstance().getPrinterEnabled()) {
showDeclineDialog("Printer is disabled!");
return;
}
if (summaryViewModel.validateField()) {
LogUtil.d(TAG, "Print button clicked - validation passed");
startSearchProcess();
} else {
LogUtil.w(TAG, "Print button clicked - validation failed");
showSingleInfoDialog("Please select date!");
showSingleInfoDialog("Please select date and time!");
}
} catch (Exception e) {
LogUtil.e(TAG, "Error in print action: " + e.getMessage());

View File

@ -77,7 +77,7 @@ public class ManualEntryFragment extends DataBindingFragment {
}
private void processEmvCard() {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID) {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_COMPLETE) {
routeId = R.id.action_manualEntryFragment_to_inputRRNFragment;
} else {
routeId = R.id.action_manualEntryFragment_to_emvTransactionFragment;
@ -104,9 +104,9 @@ public class ManualEntryFragment extends DataBindingFragment {
payDetail.setAccountType(manualEntryViewModel.get_cardScheme().getValue().name);
}
payDetail.setICC55("");
payDetail.setAuthNo(manualEntryViewModel.cvv.getValue());
// payDetail.setAuthNo(manualEntryViewModel.cvv.getValue());
emvTransactionProcessViewModel.cardNum.setValue(cardNo);
emvTransactionProcessViewModel.setTradeData(tradeData);
safeNavigateToRouteId();
@ -166,7 +166,7 @@ public class ManualEntryFragment extends DataBindingFragment {
public void onConfirm() {
if(manualEntryViewModel.validateData()) {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID) {
if(sharedViewModel.transactionsType.getValue() == TransactionsType.REFUND || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.SALE || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_VOID || sharedViewModel.transactionsType.getValue() == TransactionsType.PRE_AUTH_COMPLETE) {
if(manualEntryViewModel.get_cardScheme().getValue() != null && manualEntryViewModel.get_cardScheme().getValue() == CardScheme.MPU) {
processMPUCard();

View File

@ -90,6 +90,9 @@ public class ManualEntryViewModel extends ViewModel {
return expDate;
}
private boolean validateCard() {
return (cardFieldState.getValue() == EntryState.UNSELECTED && expFieldState.getValue() == EntryState.UNSELECTED && expDate.getValue() != null && !expDate.getValue().isEmpty()) ;
}
private boolean validateDataMPU() {
return (cardFieldState.getValue() == EntryState.UNSELECTED && expFieldState.getValue() == EntryState.UNSELECTED && expDate.getValue() != null && !expDate.getValue().isEmpty()) ;
}
@ -106,8 +109,9 @@ public class ManualEntryViewModel extends ViewModel {
}
public boolean validateData() {
if(_cardScheme.getValue() == CardScheme.MPU) return validateDataMPU();
else return validateDataEmv();
return validateCard();
// if(_cardScheme.getValue() == CardScheme.MPU) return validateDataMPU();
// else return validateDataEmv();
}
public void resetUI() {
@ -306,23 +310,23 @@ public class ManualEntryViewModel extends ViewModel {
if (pan.startsWith("4"))
{
_cardScheme.setValue(CardScheme.VISA);
cvvVisibility.setValue(0);
// cvvVisibility.setValue(0);
}
else if (pan.startsWith("51") || pan.startsWith("52") || pan.startsWith("53") || pan.startsWith("54") || pan.startsWith("55")) {
_cardScheme.setValue(CardScheme.MASTERCARD);
cvvVisibility.setValue(0);
// cvvVisibility.setValue(0);
}
else if (pan.startsWith("3528")) {
if(pan.length() > 4 && (Integer.parseInt(pan.substring(0, 4)) >= 3528 && Integer.parseInt(pan.substring(0, 4)) <= 3589)) {
_cardScheme.setValue(CardScheme.JCB);
cvvVisibility.setValue(0);
// cvvVisibility.setValue(0);
}
}
else if (pan.startsWith("60") || pan.startsWith("62") || pan.startsWith("65") || pan.startsWith("81"))
{
_cardScheme.setValue(CardScheme.UPI);
cvvVisibility.setValue(0);
// cvvVisibility.setValue(0);
}
else
{

View File

@ -1,18 +1,39 @@
package com.utsmm.kbz.ui.navigation;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.databinding.ObservableBoolean;
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.LineOptionEntity;
import com.nexgo.oaf.apiv3.device.printer.Printer;
import com.utsmyanmar.baselib.BaseApplication;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.sirius.SiriusHost;
import com.utsmyanmar.baselib.network.model.sirius.SiriusMerchant;
import com.utsmyanmar.baselib.network.model.sirius.SiriusResponse;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.sign_on.EchoTestProcess;
import com.utsmyanmar.paylibs.sign_on.SignOnListener;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import java.util.List;
import java.util.Observable;
public class NaviMainFragment extends DataBindingFragment {
private OnFragmentInteractionListener mListener;
@ -101,6 +122,36 @@ public class NaviMainFragment extends DataBindingFragment {
public class ClickEvent {
private static final String PREF_NAME = "pinpad_prefs";
private static final String KEY_PINPAD_SOUND = "pinpad_sound_enabled";
public final ObservableBoolean pinpadSoundEnabled;
public ClickEvent() {
boolean savedValue = loadPinpadSoundState();
this.pinpadSoundEnabled = new ObservableBoolean(savedValue);
BaseApplication.getInstance().deviceEngine.getPlatform().enablePinpadBeep(savedValue);
}
public boolean loadPinpadSoundState(){
return BaseApplication.getInstance().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).getBoolean(KEY_PINPAD_SOUND, true);
}
public void savePinpadSoundState(boolean enabled){
BaseApplication.getInstance().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit().putBoolean(KEY_PINPAD_SOUND, enabled).apply();
}
public void onClickPinpadSound(){
boolean newState = !pinpadSoundEnabled.get();
int result = BaseApplication.getInstance().deviceEngine.getPlatform().enablePinpadBeep(newState);
if(result == SdkResult.Success){
pinpadSoundEnabled.set(newState);
savePinpadSoundState(newState);
// LogUtil.d("NaviMainFragment", "pinpad enabled");
}
}
public void onClickEcho() {
showLoadingDialog("Sending Echo!");
processEchoTest(false);
@ -137,6 +188,15 @@ public class NaviMainFragment extends DataBindingFragment {
});
}
public void onClickWifi(){
try {
Intent intent = new Intent(Settings.ACTION_WIFI_SETTINGS);
startActivity(intent);
} catch (Exception e) {
LogUtil.e("NaviMainFragment", "error on going wifi");
}
}
public void onClickLogOff() {
showLoadingDialog("Sending LogOff!");
@ -176,6 +236,34 @@ public class NaviMainFragment extends DataBindingFragment {
mListener.onClickFunction();
}
private GrayLevelEnum getGrayLevel() {
int gray = 2;
GrayLevelEnum grayLevelEnum = GrayLevelEnum.LEVEL_1;
switch (gray) {
case 0:
grayLevelEnum = GrayLevelEnum.LEVEL_0;
break;
case 1:
grayLevelEnum = GrayLevelEnum.LEVEL_1;
break;
case 2:
grayLevelEnum = GrayLevelEnum.LEVEL_2;
break;
case 3:
grayLevelEnum = GrayLevelEnum.LEVEL_3;
break;
case 4:
grayLevelEnum = GrayLevelEnum.LEVEL_4;
break;
default:
grayLevelEnum = GrayLevelEnum.LEVEL_1;
break;
}
return grayLevelEnum;
}
public void onClickVersion(){
mListener.onClickVersion();
}
@ -184,7 +272,6 @@ public class NaviMainFragment extends DataBindingFragment {
mListener.onClickExit();
}
}
public interface OnFragmentInteractionListener {
@ -195,5 +282,6 @@ public class NaviMainFragment extends DataBindingFragment {
void onClickVersion();
void onClickExit();
}
}

View File

@ -6,6 +6,8 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.DeviceInfo;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmm.kbz.BR;
@ -32,6 +34,7 @@ public class VersionFragment extends DataBindingFragment {
setToolBarTitleWithBackIcon(getString(R.string.title_version));
fetchVersions();
versionViewModel.loadDeviceInfo(requireContext());
}
@Override
@ -75,5 +78,4 @@ public class VersionFragment extends DataBindingFragment {
}

View File

@ -1,13 +1,13 @@
package com.utsmm.kbz.ui.navigation;
import android.os.RemoteException;
import android.content.Context;
import androidx.lifecycle.ViewModel;
import com.sunmi.pay.hardware.aidl.AidlConstants;
import com.nexgo.oaf.apiv3.APIProxy;
import com.nexgo.oaf.apiv3.DeviceEngine;
import com.nexgo.oaf.apiv3.DeviceInfo;
import com.utsmyanmar.paylibs.system.SingleLiveEvent;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmm.kbz.MyApplication;
public class VersionViewModel extends ViewModel {
@ -28,31 +28,25 @@ public class VersionViewModel extends ViewModel {
public VersionViewModel() {
initData();
// initData();
// loadDeviceInfo();
}
private void initData() {
String txtHardwareVersion = getParams(AidlConstants.SysParam.HARDWARE_VERSION);
hardwareVersion.setValue(txtHardwareVersion);
String txtFirmwareVersion = getParams(AidlConstants.SysParam.FIRMWARE_VERSION);
firmwareVersion.setValue(txtFirmwareVersion);
String txtSerialNo = getParams(AidlConstants.SysParam.SN);
serialNumber.setValue(txtSerialNo);
String txtDeviceModel = getParams(AidlConstants.SysParam.DEVICE_MODEL);
deviceModel.setValue(txtDeviceModel);
swFinalVersion.setValue(SystemParamsOperation.getInstance().getFinalVersion());
}
private String getParams(String name) {
String value = "";
try {
value = MyApplication.getInstance().basicOptBinder.getSysParam(name);
} catch (RemoteException e) {
e.printStackTrace();
public void loadDeviceInfo(Context context){
try{
DeviceEngine deviceEngine = APIProxy.getDeviceEngine(context);
DeviceInfo deviceInfo = deviceEngine.getDeviceInfo();
serialNumber.setValue(deviceInfo.getSn());
deviceModel.setValue(deviceInfo.getModel());
hardwareVersion.setValue(deviceInfo.getKernelVer());
firmwareVersion.setValue(deviceInfo.getFirmWareVer());
swFinalVersion.setValue(deviceInfo.getFirmWareFullVersion());
payHardwareVersion.setValue(deviceInfo.getSpCoreVersion());
romVersion.setValue(deviceInfo.getOsVer());
} catch (Exception e) {
throw new RuntimeException(e);
}
return value;
}

View File

@ -13,11 +13,11 @@ import com.nexgo.oaf.apiv3.SdkResult;
import com.nexgo.oaf.apiv3.device.pinpad.AlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.OnPinPadInputListener;
import com.nexgo.oaf.apiv3.device.pinpad.PinAlgorithmModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinKeyboardModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinKeyboardViewModeEnum;
import com.nexgo.oaf.apiv3.device.pinpad.PinPad;
import com.nexgo.oaf.apiv3.device.pinpad.PinPadKeyCode;
import com.nexgo.oaf.apiv3.device.pinpad.PinpadLayoutEntity;
import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
import com.utsmyanmar.baselib.BaseApplication;
import com.utsmyanmar.baselib.ui.CustomPinPadKeyboard;
@ -37,7 +37,6 @@ public class PinPadViewModel extends ViewModel {
private static final String TAG = PinPadViewModel.class.getSimpleName();
private PinPadOptV2 mPinPadOptV2;
public SingleLiveEvent<String> pinText = new SingleLiveEvent<>();
public SingleLiveEvent<String> alertMsg = new SingleLiveEvent<>();
@ -119,7 +118,12 @@ public class PinPadViewModel extends ViewModel {
case ON_CONFIRM_CLICK:
LogUtil.d(TAG, "ON CLICK CONFIRM");
// increasedKSN();
pinStatus.postValue(PinPadStatus.ON_CONFIRM);
if(transType.getValue() == TransactionsType.PRE_AUTH_COMPLETE || transType.getValue() == TransactionsType.PRE_AUTH_VOID || transType.getValue() == TransactionsType.REFUND ) {
pinStatus.postValue(PinPadStatus.ON_NEXT_SCREEN);
} else {
pinStatus.postValue(PinPadStatus.ON_CONFIRM);
}
break;
case ON_CANCEL_CLICK:
LogUtil.d(TAG, "ON CLICK CANCEL");
@ -168,13 +172,13 @@ public class PinPadViewModel extends ViewModel {
}
public void cancelPinPad() {
try {
mPinPadOptV2.cancelInputPin();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
// public void cancelPinPad() {
// try {
// mPinPadOptV2.cancelInputPin();
// } catch (RemoteException e) {
// throw new RuntimeException(e);
// }
// }
private void increasedKSN() {
pinPad.dukptKsnIncrease(pinIndex); //3
@ -317,6 +321,8 @@ public class PinPadViewModel extends ViewModel {
r.bottom = customPinPadKeyboard.getKey_ok().getHeight() + r.top;
pinpadLayout.setKeyConfirm(r);
pinPad.setPinKeyboardMode(PinKeyboardModeEnum.FIXED);
byte[] number = pinPad.setPinpadLayout(pinpadLayout);
if(number != null) {
@ -331,6 +337,8 @@ public class PinPadViewModel extends ViewModel {
byte[] panBytes = pan.substring(length - 13).getBytes(StandardCharsets.US_ASCII);
// byte[] panBytes = pan.substring(length - 13, length - 1).getBytes(StandardCharsets.US_ASCII);
pinPad.setPinKeyboardViewMode(PinKeyboardViewModeEnum.DEFAULT);
pinPad.setPinKeyboardMode(PinKeyboardModeEnum.FIXED);
// pinPad.setAlgorithmMode(AlgorithmModeEnum.DES);
@ -351,7 +359,10 @@ public class PinPadViewModel extends ViewModel {
handler.obtainMessage(ON_ERROR_PIN_PAD,retCode).sendToTarget();
}
LogUtil.d(TAG,"RetCode:"+retCode);
payDetail.setPINCipher(ByteUtil.bytes2HexStr(data));
if(retCode != SdkResult.PinPad_No_Pin_Input) {
payDetail.setPINCipher(ByteUtil.bytes2HexStr(data));
}
LogUtil.d(TAG,"data bytes:"+ByteUtil.bytes2HexStr(data));
}

View File

@ -8,6 +8,9 @@ import com.utsmyanmar.paylibs.model.PayDetail;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class PreAuthVoidViewModel extends ViewModel {
private Repository repository;

View File

@ -0,0 +1,61 @@
package com.utsmm.kbz.ui.qr_pay;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemQrHistoryBinding;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.ArrayList;
import java.util.List;
public class QRHistoryAdapter extends RecyclerView.Adapter<QRHistoryAdapter.ViewHolder> {
private List<PayDetail> items = new ArrayList<>();
public void setItems(List<PayDetail> data){
items = data;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemQrHistoryBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_qr_history,
parent,
false
);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.bind(items.get(position));
}
@Override
public int getItemCount() {
return items.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final ItemQrHistoryBinding binding;
public ViewHolder(@NonNull ItemQrHistoryBinding binding){
super(binding.getRoot());
this.binding = binding;
}
public void bind(PayDetail item){
binding.setPayDetail(item);
binding.executePendingBindings();
}
}
}

View File

@ -0,0 +1,78 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class QRHistoryFragment extends DataBindingFragment {
private QRHistoryAdapter adapter;
private QRHistoryViewModel viewModel;
@Override
protected void initViewModel() {
viewModel = new ViewModelProvider(this).get(QRHistoryViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
DataBindingConfig bindingConfig = new DataBindingConfig(R.layout.fragment_qr_history, BR.viewModel, viewModel);
bindingConfig.addBindingParam(BR.adapter, adapter);
return bindingConfig;
}
@Override
protected int currentId() {
return 0;
}
@Override
protected int hostId() {
return 0;
}
@Override
protected int routeId() {
return 0;
}
@Override
public void onResume(){
super.onResume();
setToolBarTitleWithBackIcon("History");
observeData();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstance){
super.onViewCreated(view, savedInstance);
RecyclerView rvHistory = view.findViewById(R.id.qrRvHistory);
rvHistory.setLayoutManager(new LinearLayoutManager(getContext()));
adapter = new QRHistoryAdapter();
rvHistory.setAdapter(adapter);
observeData();
}
private void observeData(){
viewModel.getAllQrHistory().observe(getViewLifecycleOwner(), list -> {
adapter.setItems(list);
});
}
}

View File

@ -0,0 +1,35 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.ViewModel;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class QRHistoryViewModel extends ViewModel {
private final Repository repository;
private final LiveData<List<PayDetail>> history;
private final LiveData<List<PayDetail>> refundList;
@Inject
public QRHistoryViewModel(Repository repository){
this.repository = repository;
this.history = repository.getAllQRHistory();
this.refundList = repository.getRefundableQRHistory();
}
public LiveData<List<PayDetail>> getAllQrHistory(){
return history;
}
public LiveData<List<PayDetail>> getRefundableQrHistory(){
return refundList;
}
}

View File

@ -0,0 +1,430 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.MainActivity;
import com.utsmm.kbz.MainViewModel;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.adapters.QRPayAdapter;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.management.ManagementViewModel;
import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import com.utsmm.kbz.util.Connectivity;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.enums.CurrencyType;
import com.utsmyanmar.paylibs.utils.enums.HostType;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.util.MockData;
import com.utsmyanmar.paylibs.model.TradeData;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class QRPayFragment extends DataBindingFragment {
private MainViewModel mainViewModel;
private SharedViewModel sharedViewModel;
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
private static final int currentId = R.id.qrFragment;
private Observer<PayDetail> observeLastTrans;
private PayDetail lastPay;
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithBackIcon("MMQR PAY");
}
@Override
protected void initViewModel() {
mainViewModel = new ViewModelProvider(requireActivity()).get(MainViewModel.class);
// sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
boolean isQrRefundEnable = SystemParamsOperation.getInstance().getQrRefundEnable();
List<QRPayItem> 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, true));
}
features.add(new QRPayItem("History", R.drawable.ic_history, true));
features.add(new QRPayItem("Mock QR", R.drawable.ic_qr_pay, true));
// List<QRPayItem> features = List.of(
// new QRPayItem("Sale", R.drawable.ic_qr_pay, true),
// new QRPayItem("Refund", R.drawable.ic_refund, false),
// new QRPayItem("History", R.drawable.ic_history, true)
// );
QRPayAdapter adapter = new QRPayAdapter(features, position -> {
QRPayItem selectedBtn = features.get(position);
switch (selectedBtn.title){
case "Sale":
onClickQRPay();
break;
case "Refund":
onClickRefund();
break;
case "History":
onClickHistory();
break;
case "Mock QR":
onClickMockQR();
break;
}
});
DataBindingConfig config = new DataBindingConfig(R.layout.fragment_qr_pay, BR.mainViewModel, mainViewModel);
config.addBindingParam(BR.sharedViewModel,sharedViewModel);
config.addBindingParam(BR.adapter, adapter);
return config;
}
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {
return hostId;
}
@Override
protected int routeId() {
return routeId;
}
public void onClickQRPay() {
String mmqrIp = SystemParamsOperation.getInstance().getSecHostIpAddress();
if (mainViewModel.payDetailSingle.getValue() != null) {
mainViewModel.startReversal(mainViewModel.payDetailSingle.getValue());
}
// else if (TMSUtil.getInstance().checkSecHostParams().isStatus() == ValidityStatus.FAILURE) {
// showDeclineDialog(getResourceString(R.string.txt_please_download_config)+"\n"+TMSUtil.getInstance().checkSecHostParams().getMessage());
// }
else if (!Connectivity.isConnectedWifi(getContext()) && !Connectivity.isConnectedMobile(getContext())) {
showSingleInfoDialog(getResourceString(R.string.txt_please_enable_internet));
}
// else if (!isValidDomain(mmqrIp)) {
// showDeclineDialog(getResourceString(R.string.txt_please_check_mmqr_ip));
// }
else {
CurrencyType currencyType = TMSUtil.getInstance().currencyTextToCurrencyType(SystemParamsOperation.getInstance().getSecHostCurrency());
sharedViewModel.set_currencyText(currencyType.name);
processBatch();
sharedViewModel.transactionsType.setValue(TransactionsType.MMQR);
navigateToAmount();
}
}
private void onClickHistory(){
sharedViewModel.hostType.setValue(HostType.QR);
sharedViewModel.setTransactionsType(TransactionsType.MMQR_HISTORY);
routeId = R.id.action_qrFragment_to_qrHistory;
safeNavigateToRouteId();
}
private void onClickMockQR(){
TradeData sale1 = MockData.getInstance().generateMockQRWithAmount(TransactionsType.MMQR, 5000L, "QR001");
sharedViewModel.insertPayDetail(sale1.getPayDetail());
TradeData sale2 = MockData.getInstance().generateMockQRWithAmount(TransactionsType.MMQR, 10000L, "QR002");
sharedViewModel.insertPayDetail(sale2.getPayDetail());
TradeData sale3 = MockData.getInstance().generateMockQRWithAmount(TransactionsType.MMQR, 3500L, "QR003");
sharedViewModel.insertPayDetail(sale3.getPayDetail());
TradeData refund1 = MockData.getInstance().generateMockQRWithAmount(TransactionsType.MMQR_REFUND, 2000L, "QR004");
sharedViewModel.insertPayDetail(refund1.getPayDetail());
showSingleInfoDialog("Mock QR inserted successfully");
}
private void onClickRefund(){
// sharedViewModel.setTransactionsType(TransactionsType.MMQR_REFUND);
routeId = R.id.action_qrFragment_to_qr_refund_list;
// routeId = R.id.action_qrFragment_to_inputPasswordFragment;
safeNavigateToRouteId();
}
private void navigateToAmount() {
routeId = R.id.action_qrFragment_to_inputAmountFragment;
safeNavigateToRouteId();
}
private void processBatch() {
calculateLastTransaction();
mainViewModel.observeSettlementPOS();
observeLastTrans = payDetail -> {
if(payDetail == null) {
return;
}
// SystemParamsOperation.getInstance().saveSerialNumber(payDetail.getVoucherNo());
// SystemParamsOperation.getInstance().saveInvoiceNumber(payDetail.getInvoiceNo());
if(lastPay != null) {
Date lastPayDate = POSUtil.getInstance().getDateByString(lastPay.getTransDate());
Date observePayDate = POSUtil.getInstance().getDateByString(payDetail.getTransDate());
if(lastPayDate.compareTo(observePayDate) < 0) {
payDetail = lastPay;
}
}
// LogUtil.d(TAG,"TransDetail :"+payDetail.getTransType() + "- Trans Time :"+payDetail.getTransDate());
if(payDetail.getTransactionType() == TransactionsType.SETTLEMENT.value){
return;
}
/*
* saving for api post request, to send in next time.
* */
SystemParamsOperation.getInstance().setLastTransTime(String.valueOf(payDetail.getTransNum()));
SystemParamsOperation.getInstance().setLastTransName(payDetail.getTransType());
String lastTransDate = payDetail.getTransDate();
String lastTransTime = payDetail.getTransTime();
Date today = POSUtil.getInstance().getCurrentDate();
Date transDate;
Date current = new Date(System.currentTimeMillis());
String configTimer = SystemParamsOperation.getInstance().getClearBatchTime();
// LogUtil.d(TAG,"Config Time :" + configTimer);
String configTime = "23:00";
int configDay = 10;
String configRawDay = SystemParamsOperation.getInstance().getClearBatchDay();
if(configRawDay != null && !configRawDay.trim().isEmpty()) {
try {
configDay = Integer.parseInt(configRawDay);
} catch (Exception e) {
e.printStackTrace();
}
}
if(configTimer != null && !configTimer.trim().isEmpty() && checkValidTime(configTimer.trim())) {
configTime = configTimer.trim();
}
Date configDateTime = POSUtil.getInstance().getDateTime(configTime);
Date yesterdayConfigDateTime = POSUtil.getInstance().getYesterdayConfigDateTime(configTime);
transDate = POSUtil.getInstance().getDateByString(lastTransDate);
Date yesterdayDate = POSUtil.getInstance().getYesterdayDate();
if(today.compareTo(transDate) == 0) {
// LogUtil.d(TAG,"Last Trans is Today!");
// LogUtil.d(TAG,"date current :!"+current);
// LogUtil.d(TAG,"date config :!"+configDateTime);
// Today
if(current.compareTo(configDateTime) > 0) {
// clear batch and force update param
// LogUtil.d(TAG,"Last Trans is Today! Config time is passed!");
// if (!SystemParamsOperation.getInstance().isClearBatch()) {
if (checkSyncTrans() || !SystemParamsOperation.getInstance().isClearBatch() ) {
// LogUtil.d(TAG,"Clearing....");
if(!SystemParamsOperation.getInstance().getSettlementStatus()) {
clearBatchAndDownload(configDay,payDetail);
}
}
} else {
// LogUtil.d(TAG,"Last Trans is Today! Config time is not passed yet.....");
SystemParamsOperation.getInstance().setClearBatch(false);
}
} else {
// LogUtil.d(TAG,"Last Trans is Before Today!");
Date yest;
if(configTime.equals("00:00")) {
yest = configDateTime;
} else {
yest = yesterdayConfigDateTime;
}
Date lastTransDateTime = POSUtil.getInstance().getDateByTransDateTime(lastTransDate,lastTransTime);
// LogUtil.d(TAG,"Compare Result : "+lastTransDateTime.compareTo(yest));
if(yest.compareTo(lastTransDateTime) < 0) {
// LogUtil.d(TAG,"Trans is yesterday, but after config time, no action!");
} else {
// LogUtil.d(TAG,"Trans is yesterday, before config time,");
// if(!checkSyncTrans()) {
// LogUtil.d(TAG,"Clearing....");
if(!SystemParamsOperation.getInstance().getSettlementStatus()) {
clearBatchAndDownload(configDay,payDetail);
}
// }
}
}
};
if(mainViewModel.lastTrans.hasActiveObservers()) {
mainViewModel.lastTrans.removeObserver(observeLastTrans);
}
mainViewModel.lastTrans.observe(getViewLifecycleOwner(),observeLastTrans);
}
private void calculateLastTransaction() {
mainViewModel.allTrans.observe(getViewLifecycleOwner(), new Observer<List<PayDetail>>() {
@Override
public void onChanged(List<PayDetail> payDetails) {
if(payDetails == null || payDetails.isEmpty()) {
return;
}
PayDetail tempPay = payDetails.get(0);
for (int i = 0; i < payDetails.size() ; i++) {
Date tempDate = POSUtil.getInstance().getDateByString(tempPay.getTransDate());
Date indexDate = POSUtil.getInstance().getDateByString(payDetails.get(i).getTransDate());
if(tempDate.compareTo(indexDate) > 0) {
// if before
tempPay = payDetails.get(i);
}
}
lastPay = tempPay;
// LogUtil.d(TAG,"Transaction Name : "+tempPay.getTransType());
// LogUtil.d(TAG,"Transaction Type : "+tempPay.getTransactionType());
// LogUtil.d(TAG,"Transaction Amount : "+tempPay.getAmount());
// LogUtil.d(TAG,"Transaction Date/Time : "+tempPay.getTransDate()+"/"+tempPay.getTransTime());
// LogUtil.d(TAG,"RRN/Trace : "+tempPay.getReferNo()+"/"+tempPay.getVoucherNo());
// LogUtil.d(TAG,"Transaction is canceled : "+tempPay.isCanceled());
// LogUtil.d(TAG,"Transaction settlement config : "+tempPay.isSettlementEnabled());
// LogUtil.d(TAG,"----------------------------------------------------------");
}
});
}
private boolean checkSyncTrans() {
String transDateString = SystemParamsOperation.getInstance().getSyncTransDate();
Date today = POSUtil.getInstance().getCurrentDate();
if(transDateString == null || transDateString.isEmpty()) {
return true;
}
Date transDate = POSUtil.getInstance().getDateByString(transDateString);
return today.compareTo(transDate) == 0;
}
private void clearBatchAndDownload(int configDay,PayDetail payDetail) {
Date yesterday = POSUtil.getInstance().getYesterdayDate();
Date lastInputDay = POSUtil.getInstance().getLastInputDay(configDay);
mainViewModel.settlementPOS.observe(getViewLifecycleOwner(), list -> {
Date transDate;
if(list == null || list.size() == 0) {
return;
}
for (PayDetail pay:list) {
/*
* To Hold One day transaction!
* */
transDate = POSUtil.getInstance().getDateByString(pay.getTransDate());
if (pay.getTransactionType() == TransactionsType.SALE.value) {
if(lastInputDay.compareTo(transDate) > 0) {
mainViewModel.deletePayDetail(pay);
}
} else {
if(yesterday.compareTo(transDate) > 0 ) {
mainViewModel.deletePayDetail(pay);
}
}
}
});
mainViewModel.deleteTrans.observe(getViewLifecycleOwner(), lists -> {
Date transDate;
if(lists == null || lists.size() == 0) {
return;
}
for (PayDetail pay:lists) {
/*
* To Hold One day transaction!
* */
transDate = POSUtil.getInstance().getDateByString(pay.getTransDate());
// LogUtil.d(TAG,"Trans Type : "+pay.getTransType()+"<=====> Trans Date :"+pay.getTransDate());
// LogUtil.d(TAG,"Yesterday Date: "+yesterday.toString());
// LogUtil.d(TAG,"Compare result :"+yesterday.compareTo(transDate));
if(yesterday.compareTo(transDate) > 0) {
mainViewModel.deletePayDetail(pay);
}
}
});
mainViewModel.preAuthTrans.observe(getViewLifecycleOwner(), lists -> {
Date transDate;
if(lists == null || lists.size() == 0) {
return;
}
for (PayDetail pay:lists) {
transDate = POSUtil.getInstance().getDateByString(pay.getTransDate());
if (lastInputDay.compareTo(transDate) > 0) {
mainViewModel.deletePayDetail(pay);
}
}
});
/*
*
* Need to think about his problem .... Nov 7, 2024
* */
if(!SystemParamsOperation.getInstance().isClearBatch()) {
// downloadParams(payDetail.getTransType(),String.valueOf(payDetail.getTransNum()),TMSUpdate.UPDATE,false);
}
}
}

View File

@ -0,0 +1,14 @@
package com.utsmm.kbz.ui.qr_pay;
public class QRPayItem {
public String title;
public int icon;
public boolean isActive;
public QRPayItem(String title, int icon, boolean isActive) {
this.title = title;
this.icon = icon;
this.isActive = isActive;
}
}

View File

@ -0,0 +1,51 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.utsmyanmar.baselib.repo.Repository;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.List;
import javax.inject.Inject;
import dagger.hilt.android.lifecycle.HiltViewModel;
@HiltViewModel
public class QRPayViewModel extends ViewModel {
private final Repository repository;
private final LiveData<List<PayDetail>> refundableHistory;
private final RefundCertificateManager refundCertificateManager;
private final MutableLiveData<PayDetail> _payDetail = new MutableLiveData<>();
public LiveData<PayDetail> payDetail = _payDetail;
@Inject
public QRPayViewModel(
Repository repository,
RefundCertificateManager refundCertificateManager
){
this.repository = repository;
this.refundableHistory = repository.getRefundableQRHistory();
this.refundCertificateManager = refundCertificateManager;
}
public LiveData<List<PayDetail>> getRefundableQrHistory(){
return refundableHistory;
}
public void setPayDetail(PayDetail payDetail){
_payDetail.setValue(payDetail);
}
public void checkCertificateFiles(){
refundCertificateManager.updateCertificateFiles();
}
public Runnable onCancel;
public Runnable onConfirm;
}

View File

@ -0,0 +1,49 @@
package com.utsmm.kbz.ui.qr_pay;
public class QRRefund {
private String referenceNo;
private String refundAmount;
private String originalAmount;
private String reason;
public QRRefund(String referenceNo, String refundAmount, String originalAmount, String reason) {
this.referenceNo = referenceNo;
this.refundAmount = refundAmount;
this.originalAmount = originalAmount;
this.reason = reason;
}
public String getReferenceNo() {
return referenceNo;
}
public void setReferenceNo(String referenceNo) {
this.referenceNo = referenceNo;
}
public String getRefundAmount() {
return refundAmount;
}
public void setRefundAmount(String refundAmount) {
this.refundAmount = refundAmount;
}
public String getOriginalAmount() {
return originalAmount;
}
public void setOriginalAmount(String originalAmount) {
this.originalAmount = originalAmount;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
}

View File

@ -0,0 +1,235 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.text.InputFilter;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmm.kbz.util.helper.KeyboardHelper;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.model.TradeData;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.util.DecimalDigitsInputFilter;
import com.utsmm.kbz.util.TransactionUtil;
import java.text.DecimalFormat;
public class QRRefundDetailFragment extends DataBindingFragment {
private SharedViewModel sharedViewModel;
private KPayViewModel kPayViewModel;
private int routeId;
private TradeData tradeData;
private PayDetail payDetail;
// UI Elements
private RadioGroup radioGroupRefundType;
private RadioButton radioOriginal, radioPartial;
private EditText etReferenceNo, etOriginalAmount, etRefundAmount, etRefundReason;
private LinearLayout originalAmountLayout, refundAmountLayout;
private String originalRefundAmount;
private boolean isPartialRefund = false;
private static final String TAG = com.utsmm.kbz.ui.kpay.QRRefundFragment.class.getSimpleName();
private static final int hostId = Constants.NAV_HOST_ID;
private static final int currentId = R.id.qrRefundDetail;
@Override
protected void initViewModel() {
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
kPayViewModel = getFragmentScopeViewModel(KPayViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_qr_refund_detail, BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.kPayViewModel, kPayViewModel)
.addBindingParam(BR.click, new ClickEvent());
}
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {
return hostId;
}
@Override
protected int routeId() {
return routeId;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(getArguments() != null){
payDetail = (PayDetail) getArguments().getSerializable("payDetail");
}
}
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithBackIcon("QR Refund");
kPayViewModel.invalidAmountMsg.setValue("");
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initViews();
initData();
setupRadioGroupListener();
}
private void initViews() {
radioGroupRefundType = mBinding.getRoot().findViewById(R.id.radio_group_refund_type);
radioOriginal = mBinding.getRoot().findViewById(R.id.radio_original);
radioPartial = mBinding.getRoot().findViewById(R.id.radio_partial);
etReferenceNo = mBinding.getRoot().findViewById(R.id.et_reference_no);
etOriginalAmount = mBinding.getRoot().findViewById(R.id.et_original_amount);
etRefundAmount = mBinding.getRoot().findViewById(R.id.et_refund_amount);
etRefundReason = mBinding.getRoot().findViewById(R.id.et_refund_reason);
originalAmountLayout = mBinding.getRoot().findViewById(R.id.original_amount_layout);
refundAmountLayout = mBinding.getRoot().findViewById(R.id.refund_amount_layout);
// Set input filters for amount fields
etOriginalAmount.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(11, 2)});
etRefundAmount.setFilters(new InputFilter[]{new DecimalDigitsInputFilter(11, 2)});
}
private void initData() {
double realAmount = payDetail.getAmount() / 100.0;
DecimalFormat df = new DecimalFormat("0.00");
originalRefundAmount = df.format(realAmount);
payDetail = TransactionUtil.getInstance().initWalletTransaction(TransactionsType.MMQR_REFUND);
payDetail.setInvoiceNo(SystemParamsOperation.getInstance().getIncrementInvoiceNum());
if(getArguments() != null){
PayDetail passData = (PayDetail) getArguments().getSerializable("payDetail");
if(passData != null && passData.getReferNo() != null){
etReferenceNo.setText(passData.getReferNo());
etReferenceNo.setEnabled(false);
}
}
}
private void setupRadioGroupListener() {
radioGroupRefundType.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.radio_partial) {
// Show both amount fields for partial refund
isPartialRefund = true;
originalAmountLayout.setVisibility(View.VISIBLE);
refundAmountLayout.setVisibility(View.VISIBLE);
} else {
// Hide amount fields for original refund
isPartialRefund = false;
originalAmountLayout.setVisibility(View.GONE);
refundAmountLayout.setVisibility(View.GONE);
}
});
}
public class ClickEvent {
public void onCancel() {
safePopBackStack();
}
public void onConfirm() {
kPayViewModel.invalidAmountMsg.setValue("");
String referenceNo = etReferenceNo.getText().toString().trim();
String refundReason = etRefundReason.getText().toString().trim();
// Validate reference number
if (referenceNo.isEmpty()) {
kPayViewModel.invalidAmountMsg.setValue("Enter reference number");
return;
}
if (isPartialRefund) {
// Partial refund validation
String originalAmountStr = etOriginalAmount.getText().toString().trim();
String refundAmountStr = etRefundAmount.getText().toString().trim();
if (originalAmountStr.isEmpty()) {
kPayViewModel.invalidAmountMsg.setValue("Enter original amount");
return;
}
if (refundAmountStr.isEmpty()) {
kPayViewModel.invalidAmountMsg.setValue("Enter refund amount");
return;
}
double originalAmount = Double.parseDouble(originalAmountStr);
double refundAmount = Double.parseDouble(refundAmountStr);
if (originalAmount <= 0) {
kPayViewModel.invalidAmountMsg.setValue("Enter valid original amount");
return;
}
if (refundAmount <= 0) {
kPayViewModel.invalidAmountMsg.setValue("Enter valid refund amount");
return;
}
if (refundAmount > originalAmount) {
kPayViewModel.invalidAmountMsg.setValue("Refund amount cannot exceed original amount");
return;
}
QRRefund qrRefund = new QRRefund(referenceNo, refundAmountStr, originalAmountStr, refundReason);
kPayViewModel.setQrRefund(qrRefund);
} else {
// String originalAmountStr = etOriginalAmount.getText().toString().trim();
// String refundAmountStr = etRefundAmount.getText().toString().trim();
QRRefund qrRefund = new QRRefund(referenceNo, "0", originalRefundAmount, refundReason);
kPayViewModel.setQrRefund(qrRefund);
}
sharedViewModel.transactionsType.setValue(TransactionsType.MMQR_REFUND);
kPayViewModel.setPayDetail(payDetail);
// sharedViewModel.amount.setValue(refundAmountStr);
KeyboardHelper.hide(requireActivity());
routeId = R.id.action_qrRefundDetail_inputPasswordFragment;
safeNavigateToRouteId();
}
}
}

View File

@ -0,0 +1,147 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.fragment.NavHostFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmm.kbz.util.helper.KeyboardHelper;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.ArrayList;
import java.util.List;
public class QRRefundFragment extends DataBindingFragment {
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
private static final int currentId = R.id.qrRefundList;
private QRPayViewModel qrPayViewModel;
private SharedViewModel sharedViewModel;
private QRRefundViewAdapter adapter;
private LinearLayout emptyStateView;
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {return hostId;}
@Override
protected int routeId() {return routeId;}
@Override
protected void initViewModel() {
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
qrPayViewModel = new ViewModelProvider(requireActivity()).get(QRPayViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
adapter = new QRRefundViewAdapter(this::onRefundItemClicked);
return new DataBindingConfig(R.layout.fragment_qr_refund, BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.adapter, adapter)
.addBindingParam(BR.click, new ClickEvent());
}
@Override
public void onResume(){
super.onResume();
setToolBarTitleWithBackIcon("QR Refund");
}
@Override
public void onCreate(@Nullable Bundle savedInstance){
super.onCreate(savedInstance);
adapter = new QRRefundViewAdapter(this::onRefundItemClicked);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
RecyclerView recyclerView = view.findViewById(R.id.qrRefundHistory);
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
recyclerView.setAdapter(adapter);
emptyStateView = view.findViewById(R.id.emptyStateSectionRefund);
qrPayViewModel.checkCertificateFiles();
observeData();
}
private void observeData(){
qrPayViewModel.getRefundableQrHistory().observe(getViewLifecycleOwner(), this::updateList);
}
private void updateList(List<PayDetail> list){
if (adapter == null) {
return;
}
if(list == null || list.isEmpty()){
emptyStateView.setVisibility(View.VISIBLE);
getView().findViewById(R.id.qrRefundHistory).setVisibility(View.GONE);
}else{
emptyStateView.setVisibility(View.GONE);
getView().findViewById(R.id.qrRefundHistory).setVisibility(View.VISIBLE);
adapter.setData(list);
}
}
public class ClickEvent {
public void onCancel(){
safeNavigateToRouteId();
}
public void onSearch(){
searchByTrace();
KeyboardHelper.hide(requireActivity());
}
}
private void onRefundItemClicked(PayDetail payDetail) {
Bundle bundle = new Bundle();
bundle.putSerializable("payDetail", payDetail);
routeId = R.id.action_qrRefundList_to_qrRefundDetail;
NavHostFragment.findNavController(this).navigate(routeId, bundle);
}
private void searchByTrace(){
EditText editText = getView().findViewById(R.id.rnn_trace_id);
String keyword = editText.getText().toString().trim();
if(keyword.isEmpty()){
updateList(qrPayViewModel.getRefundableQrHistory().getValue());
return;
}
List<PayDetail> originalList = qrPayViewModel.getRefundableQrHistory().getValue();
if(originalList == null) return;
List<PayDetail> filteredList = new ArrayList<>();
for(PayDetail item: originalList){
boolean match = item.getVoucherNo() != null && item.getVoucherNo().contains(keyword);
if(match){
filteredList.add(item);
}
}
updateList(filteredList);
}
}

View File

@ -0,0 +1,260 @@
package com.utsmm.kbz.ui.qr_pay;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
import com.utsmm.kbz.ui.core_viewmodel.SharedViewModel;
import com.utsmm.kbz.ui.kpay.KPayViewModel;
import com.utsmm.kbz.ui.kpay.QRRefundFragment;
import com.utsmm.kbz.util.EReceiptUtil;
import com.utsmm.kbz.util.TransactionUtil;
import com.utsmm.kbz.util.ecr.CoreUtils;
import com.utsmm.kbz.util.enums.TransResultStatus;
import com.utsmm.kbz.util.tms.TMSUtil;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.network.model.KPayRefund;
//import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptQRRequest;
import com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.system.SystemDateTime;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.POSUtil;
import java.text.DecimalFormat;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class QRRefundProcessFragment extends DataBindingFragment {
private static final String TAG = QRRefundProcessFragment.class.getSimpleName();
private SharedViewModel sharedViewModel;
private KPayViewModel kPayViewModel;
private PayDetail payDetail;
private QRRefund qrRefund;
private static final int hostId = Constants.NAV_HOST_ID;
private int routeId;
private static final int currentId = R.id.QRRefundProcessFragment;
CompositeDisposable retrieveUpdateDisposable = new CompositeDisposable();
CompositeDisposable refundDisposable = new CompositeDisposable();
@Override
protected void initViewModel() {
sharedViewModel = getFragmentScopeViewModel(SharedViewModel.class);
kPayViewModel = getFragmentScopeViewModel(KPayViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_qr_refund_process, BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.kPayViewModel, kPayViewModel);
}
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithoutBackIcon("QR Refund Process");
}
@Override
public void onDestroyView() {
super.onDestroyView();
retrieveUpdateDisposable.dispose();
refundDisposable.dispose();
}
@Override
protected int currentId() {
return currentId;
}
@Override
protected int hostId() {
return hostId;
}
@Override
protected int routeId() {
return routeId;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initData();
processKPayRefund(qrRefund.getReferenceNo(), qrRefund.getRefundAmount(), qrRefund.getOriginalAmount(), qrRefund.getReason());
}
private void initData() {
payDetail = kPayViewModel.getPayDetail();
qrRefund = kPayViewModel.getQrRefund();
}
private void processKPayRefund(String referenceNo, String refundAmount, String originalAmount, String reason) {
String merchantId = TransactionUtil.getInstance().getQRMerchantId();
// Generate unique refund request ID
String refundRequestId = referenceNo + "R";
// Create KPay refund request
KPayRefund.RefundRequest refundRequest = kPayViewModel.createRefundRequest(
refundRequestId,
referenceNo,
merchantId,
refundAmount,
reason != null ? reason : "Refund request"
);
Disposable refundDi = kPayViewModel.kPayRefund(refundRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
handleRefundResponse(response, referenceNo, originalAmount);
},
throwable -> {
LogUtil.e(TAG, "Refund error: " + throwable.getMessage());
showDeclineDialog("Refund failed!\nCommunication Error!");
ecrActionCancel("Refund failed");
navigateToMain();
},
() -> LogUtil.d(TAG, "Refund request completed")
);
refundDisposable.add(refundDi);
}
private void handleRefundResponse(KPayRefund.RefundResponse response, String referenceNo, String orgAmount) {
if (response != null && response.getResponse() != null && "REFUND_SUCCESS".equalsIgnoreCase(response.getResponse().getRefundStatus())) {
LogUtil.d(TAG, "Refund successful!");
String refundAmount = response.getResponse().getRefundAmount();
long text = POSUtil.getInstance().convertAmount(refundAmount);
String dateTime = SystemDateTime.getTodayDateFormat() + " " + SystemDateTime.getTodayTimeFormat();
payDetail.setTC(response.getResponse().getRefundStatus());
payDetail.setAmount(refundAmount == null ? 0 : POSUtil.getInstance().convertAmount(refundAmount));
payDetail.setOriginalTransDate(dateTime);
payDetail.setQrTransStatus(1);
if(response.getResponse().getMmqrRef() != null) {
payDetail.setQrReferNo(response.getResponse().getMmqrRef());
}
payDetail.setReferNo(referenceNo);
payDetail.setIsCanceled(true);
payDetail.setCustomerMobile(response.getResponse().getWalletIdentifier());
payDetail.setTradeDateAndTime(response.getResponse().getRefundTime());
EReceiptRequest request = EReceiptUtil.getInstance().generateQRRefundReceipt(
refundAmount,
referenceNo,
payDetail != null ? payDetail.getInvoiceNo() : "",
payDetail != null ? payDetail.getVoucherNo() : "",
payDetail != null ? payDetail.getQrReferNo() != null ? payDetail.getQrReferNo() : "" : "" ,
payDetail != null ? payDetail.getCustomerMobile() : "",
TransResultStatus.SUCCESS
);
sharedViewModel.pushReceipt(request);
sharedViewModel.amount.setValue(refundAmount);
retrievedUpdatePayDetail(referenceNo);
} else if (response != null && response.getResponse() != null && "DUPLICATED_REFUND_REQ_NO".equalsIgnoreCase(response.getResponse().getRefundStatus())) {
}
else {
LogUtil.d(TAG, "Refund failed!");
payDetail.setQrTransStatus(-1);
payDetail.setReferNo(referenceNo);
String errorMsg = "Refund failed";
if (response != null && response.getResponse() != null && response.getResponse().getMsg() != null) {
errorMsg = response.getResponse().getMsg();
}
payDetail.setTradeResultDes(errorMsg);
EReceiptRequest request = EReceiptUtil.getInstance().generateQRRefundReceipt(
"0",
referenceNo,
payDetail != null ? payDetail.getInvoiceNo() : "",
payDetail != null ? payDetail.getVoucherNo() : "",
payDetail != null ? payDetail.getQrReferNo() != null ? payDetail.getQrReferNo() : "" : "" ,
payDetail != null ? payDetail.getCustomerMobile() : "",
TransResultStatus.FAIL
);
sharedViewModel.pushReceipt(request);
sharedViewModel.amount.setValue(orgAmount);
sharedViewModel.payDetail.setValue(payDetail);
navigateToNext();
}
}
private void retrievedUpdatePayDetail(String refNum) {
LogUtil.d(TAG, "Trying to update Database!");
retrieveUpdateDisposable.add(kPayViewModel.searchPayByRefNum(refNum)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(oldPay -> {
LogUtil.d(TAG, "Inside the subscribe!");
if (oldPay != null) {
oldPay.setIsCanceled(true);
payDetail.setQrTransId(oldPay.getQrTransId());
payDetail.setCustomerMobile(oldPay.getCustomerMobile());
sharedViewModel.updatePayDetail(oldPay);
}
updateData();
navigateToNext();
},
onError -> {
LogUtil.d(TAG, "On error Unable to retrieve PayDetail");
updateData();
navigateToNext();
},
() -> {
LogUtil.d(TAG, "No data found! navigating to Result Page!");
updateData();
navigateToNext();
}
));
}
private void updateData() {
kPayViewModel.insertPayDetail(payDetail);
sharedViewModel.payDetail.postValue(payDetail);
}
private void navigateToNext() {
routeId = R.id.action_QRRefundProcessFragment_to_transactionResultFragment;
safeNavigateToRouteId();
}
private void navigateToMain() {
routeId = R.id.action_QRRefundProcessFragment_to_nav_main;
safeNavigateToRouteId();
}
private void ecrActionCancel(String msg) {
if (sharedViewModel.isEcr.getValue() != null) {
if (sharedViewModel.isEcr.getValue()) {
sharedViewModel.isEcr.postValue(false);
CoreUtils.getInstance(sharedViewModel).responseRejectMsg(msg);
sharedViewModel.isEcrFinished.postValue(true);
}
}
}
}

View File

@ -0,0 +1,84 @@
package com.utsmm.kbz.ui.qr_pay;
import androidx.annotation.NonNull;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.ItemQrRefundBinding;
import com.utsmm.kbz.ui.adapters.QRPayAdapter;
import com.utsmyanmar.paylibs.model.PayDetail;
import java.util.ArrayList;
import java.util.List;
public class QRRefundViewAdapter extends RecyclerView.Adapter<QRRefundViewAdapter.ViewHolder> {
private List<PayDetail> payDetailList = new ArrayList<>();
private final OnRefundItemClick listener;
public interface OnRefundItemClick {
void onClick(PayDetail payDetail);
}
public QRRefundViewAdapter(OnRefundItemClick listener){
this.listener = listener;
}
// public QRRefundViewAdapter(List<PayDetail> payDetailList, OnRefundItemClick listener){
// this.payDetailList = payDetailList;
// this.listener = listener;
// }
public void setData(List<PayDetail> data){
this.payDetailList = data != null ? data : new ArrayList<>();
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemQrRefundBinding binding = DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.item_qr_refund,
parent,
false
);
return new ViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
PayDetail payDetail = payDetailList.get(position);
holder.bind(payDetail, listener);
}
@Override
public int getItemCount() {
return payDetailList == null ? 0 : payDetailList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final ItemQrRefundBinding binding;
public ViewHolder(ItemQrRefundBinding binding){
super(binding.getRoot());
this.binding = binding;
}
public void bind(PayDetail payDetail, OnRefundItemClick listener){
binding.setVariable(BR.payDetail, payDetail);
binding.executePendingBindings();
binding.getRoot().setOnClickListener( v -> {
if(listener != null) listener.onClick(payDetail);
});
}
}
}

View File

@ -0,0 +1,106 @@
package com.utsmm.kbz.ui.qr_pay;
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;
@Singleton
public class RefundCertificateManager {
private static final String TAG = RefundCertificateManager.class.getSimpleName();
private static final String E_RECEIPT_SECRET =
com.utsmyanmar.baselib.BuildConfig.ERECEIPT_SECRET;
// Prevent duplicate parallel downloads
private final AtomicBoolean isUpdating = new AtomicBoolean(false);
@Inject
public RefundCertificateManager() {
}
/**
* Download & refresh refund certificates.
* Safe to call multiple times.
*/
public void updateCertificateFiles() {
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() + "/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_" + "file", // 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");
}
}
);
// ---------- CLIENT CERT ----------
DownloadUtil.downloadCertificateRx(
downloadBase + clientCertUrl,
"refund_client_" + "file", // 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) {
String bodyString = "{}";
String dataToHash = bodyString + E_RECEIPT_SECRET + timestamp;
return EReceiptHelper.sha256(dataToHash);
}
}

View File

@ -39,6 +39,10 @@ public class VoidViewModel extends ViewModel {
return repository.getVoidTransaction(transType,voucherNo);
}
public LiveData<PayDetail> getVoidTransByInvoice(int transType, String invoiceNo) {
return repository.getVoidTransactionByInvoice(transType, invoiceNo);
}
public void observeVoidTrans(int transType) {
getVoidTransactions(transType).observeForever(new Observer<List<PayDetail>>() {

View File

@ -2,8 +2,6 @@ package com.utsmm.kbz.ui.settings;
import android.os.RemoteException;
import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
import com.sunmi.pay.hardware.aidlv2.security.SecurityOptV2;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmm.kbz.BR;
@ -19,7 +17,7 @@ public class DeleteKeyFragment extends DataBindingFragment {
private InputKeyViewModel inputKeyViewModel;
private final SecurityOptV2 securityOptV2 = MyApplication.getInstance().mSecurityOptV2;
// private final SecurityOptV2 securityOptV2 = MyApplication.getInstance().mSecurityOptV2;
@Override
protected void initViewModel() {
@ -65,9 +63,9 @@ public class DeleteKeyFragment extends DataBindingFragment {
int keyIndex = Integer.parseInt(inputKeyViewModel.keyIndex.getValue());
try {
int result = securityOptV2.deleteKey(AidlConstantsV2.Security.SEC_MKSK,keyIndex);
// int result = securityOptV2.deleteKey(AidlConstantsV2.Security.SEC_MKSK,keyIndex);
int result = -1;
LogUtil.d(TAG,"Delete Key result :"+ result);
if( result == 0) {
showSuccessDialog(keyIndex + " was deleted successfully!");
@ -77,9 +75,6 @@ public class DeleteKeyFragment extends DataBindingFragment {
showDeclineDialog(keyIndex + " delete process failure \nError Code : "+result);
inputKeyViewModel.keyIndex.setValue("");
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
public void onCancel() {

View File

@ -0,0 +1,169 @@
package com.utsmm.kbz.ui.settings;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.os.Bundle;
import android.util.Log;
import android.view.animation.LinearInterpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import android.view.View;
import com.nexgo.oaf.apiv3.device.printer.Printer;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.config.Constants;
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.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.print.printx.PrintXStatus;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
public class HostConfigFragment extends DataBindingFragment {
private HostConfigViewModel viewModel;
private SharedViewModel sharedViewModel;
protected Printer printer;
int FONT_NORMAL = 20;
int FONT_HEADER = 24;
private ObjectAnimator rollUpAnimator;
private View hostCardView;
private volatile boolean isPrintingHostConfig = false;
@Override
protected void initViewModel() {
viewModel = new ViewModelProvider(this).get(HostConfigViewModel.class);
sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
}
@Override
protected DataBindingConfig getDataBindingConfig() {
return new DataBindingConfig(R.layout.fragment_host_config, BR.viewModel, viewModel)
.addBindingParam(BR.sharedViewModel, sharedViewModel)
.addBindingParam(BR.click, new ClickHandler());
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
FragmentHostConfigBinding binding = (FragmentHostConfigBinding) this.binding;
hostCardView = binding.getRoot().findViewById(R.id.hostCard);
viewModel.loadConfig();
}
@Override
public void onResume(){
super.onResume();
setToolBarTitleWithBackIcon("Host Configs");
sharedViewModel.printerDisabled.setValue(!SystemParamsOperation.getInstance().getPrinterEnabled());
}
@Override
public void onPause() {
super.onPause();
stopRollUpAnimation();
isPrintingHostConfig = false;
}
@Override
protected int currentId() {
return R.id.hostConfigFragment;
}
@Override
protected int hostId() {
return Constants.NAV_HOST_ID;
}
@Override
protected int routeId() {
return 0;
}
public class ClickHandler {
public void onCancel(){
safePopBack(R.id.hostConfigFragment, Constants.NAV_HOST_ID);
}
public void onPrint(){
if (!SystemParamsOperation.getInstance().getPrinterEnabled()) {
showDeclineDialog("Printer is disabled!");
return;
}
if (isPrintingHostConfig) {
return;
}
startHostConfigPrint();
}
}
private void startHostConfigPrint() {
isPrintingHostConfig = true;
startRollUpAnimation();
PrintXReceipt.getInstance().printTerminalHostConfig(new PrintXStatus() {
@Override
public void onSuccess() {
requireActivity().runOnUiThread(() -> {
isPrintingHostConfig = false;
stopRollUpAnimation();
Log.d("PrintConfig", "Print Terminal Config Success.");
});
}
@Override
public void onFailure() {
requireActivity().runOnUiThread(() -> {
isPrintingHostConfig = false;
stopRollUpAnimation();
Log.e("PrintConfig", "Print Terminal Config Failure.");
showDeclineDialog("Failed to print host config.");
});
}
});
}
private void startRollUpAnimation() {
if (hostCardView == null) {
return;
}
if (rollUpAnimator != null && rollUpAnimator.isRunning()) {
return;
}
hostCardView.post(() -> {
int cardTop = hostCardView.getTop();
int cardHeight = hostCardView.getHeight();
if (cardHeight <= 0) {
return;
}
// Move past the top edge so the card fully leaves the screen.
float offScreenDistance = cardTop + cardHeight + 80f;
rollUpAnimator = ObjectAnimator.ofFloat(hostCardView, View.TRANSLATION_Y, 0f, -offScreenDistance);
rollUpAnimator.setDuration(3000);
rollUpAnimator.setInterpolator(new LinearInterpolator());
rollUpAnimator.setRepeatMode(ValueAnimator.RESTART);
rollUpAnimator.setRepeatCount(ValueAnimator.INFINITE);
rollUpAnimator.start();
});
}
private void stopRollUpAnimation() {
if (rollUpAnimator != null) {
rollUpAnimator.cancel();
rollUpAnimator = null;
}
if (hostCardView != null) {
hostCardView.setTranslationY(0f);
}
}
}

View File

@ -0,0 +1,33 @@
package com.utsmm.kbz.ui.settings;
public class HostConfigItem {
public String name;
public String ip;
public String mid;
public String tid;
public String secHostName;
public String secHostIp;
public String secHostMid;
public String secHostTid;
public HostConfigItem(
String name,
String ip,
String mid,
String tid,
String secHostName,
String secHostIp,
String secHostMid,
String secHostTid
){
this.name = name;
this.ip = ip;
this.mid = mid;
this.tid = tid;
this.secHostName = secHostName;
this.secHostIp = secHostIp;
this.secHostMid = secHostMid;
this.secHostTid = secHostTid;
}
}

View File

@ -0,0 +1,218 @@
package com.utsmm.kbz.ui.settings;
import static com.utsmyanmar.paylibs.print.printx.BaseXPrint.wrapAddressText;
import android.text.TextUtils;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import com.utsmm.kbz.BuildConfig;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import java.util.ArrayList;
import java.util.List;
public class HostConfigViewModel extends ViewModel {
public String appVersion = BuildConfig.VERSION_NAME;
public MutableLiveData<String> merchantName = new MutableLiveData<>();
public MutableLiveData<String> merchantPhone = new MutableLiveData<>();
public MutableLiveData<String> merchantAddress1 = new MutableLiveData<>();
public MutableLiveData<String> merchantAddress2 = new MutableLiveData<>();
// Primary Host
public MutableLiveData<String> hostName = new MutableLiveData<>();
public MutableLiveData<String> tid = new MutableLiveData<>();
public MutableLiveData<String> mid = new MutableLiveData<>();
public MutableLiveData<String> primaryIp = new MutableLiveData<>();
public MutableLiveData<String> primaryPort = new MutableLiveData<>();
public MutableLiveData<String> secondaryIp = new MutableLiveData<>();
public MutableLiveData<String> secondaryPort = new MutableLiveData<>();
public MutableLiveData<String> currencyCode = new MutableLiveData<>();
// Secondary Host
public MutableLiveData<String> secHostName = new MutableLiveData<>();
// public MutableLiveData<String> secHostTid = new MutableLiveData<>();
// public MutableLiveData<String> secHostMid = new MutableLiveData<>();
public MutableLiveData<String> shortCode = new MutableLiveData<>();
public MutableLiveData<String> secHostPrimaryIp = new MutableLiveData<>();
public MutableLiveData<String> secHostPrimaryPort = new MutableLiveData<>();
public MutableLiveData<String> secHostSecondaryIp = new MutableLiveData<>();
public MutableLiveData<String> secHostSecondaryPort = new MutableLiveData<>();
public MutableLiveData<String> secHostCurrency = new MutableLiveData<>();
// Third Host
public MutableLiveData<String> thirdHostName = new MutableLiveData<>();
public MutableLiveData<String> thirdHostTid = new MutableLiveData<>();
public MutableLiveData<String> thirdHostMid = new MutableLiveData<>();
public MutableLiveData<String> thirdHostShortCode = new MutableLiveData<>();
public MutableLiveData<String> thirdHostPrimaryIp = new MutableLiveData<>();
public MutableLiveData<String> thirdHostPrimaryPort = new MutableLiveData<>();
public MutableLiveData<String> thirdHostSecondaryIp = new MutableLiveData<>();
public MutableLiveData<String> thirdHostSecondaryPort = new MutableLiveData<>();
public MutableLiveData<String> thirdHostCurrency = new MutableLiveData<>();
public MutableLiveData<String> terminalName = new MutableLiveData<>();
public void loadConfig() {
SystemParamsOperation sp = SystemParamsOperation.getInstance();
// Merchant Info
merchantName.setValue(sp.getMerchantName());
merchantPhone.setValue(sp.getMerchantPhoneNo());
merchantAddress1.setValue(wrapMerchantAddress(sp.getMerchantAddress()));
terminalName.setValue(wrapTerminalName(sp.getTerminalName()));
// PRIMARY HOST
hostName.setValue(sp.getHostName());
tid.setValue(sp.getTerminalId());
mid.setValue(sp.getMerchantId());
String[] pri = split(sp.getIpAddress());
primaryIp.setValue(pri[0]);
primaryPort.setValue(pri[1]);
String[] sec = split(sp.getSecIpAddress());
secondaryIp.setValue(sec[0]);
secondaryPort.setValue(sec[1]);
currencyCode.setValue(normalizeCurrency(sp.getCurrencyType() != null ? sp.getCurrencyType().toString() : ""));
if (sp.getSecHostName() != null && !sp.getSecHostName().isEmpty()) {
secHostName.setValue(sp.getSecHostName());
// secHostTid.setValue(sp.getSecHostTerminalId());
// secHostMid.setValue(sp.getSecHostMerchantId());
shortCode.setValue(sp.getShortCode());
String[] shp = split(sp.getSecHostIpAddress());
secHostPrimaryIp.setValue(shp[0]);
secHostPrimaryPort.setValue(shp[1]);
String[] shs = split(sp.getSecHostSecIpAddress());
secHostSecondaryIp.setValue(shs[0]);
secHostSecondaryPort.setValue(shs[1]);
secHostCurrency.setValue(normalizeCurrency(sp.getSecHostCurrency()));
}
if (sp.getThirdHostName() != null && !sp.getThirdHostName().isEmpty()) {
thirdHostName.setValue(sp.getThirdHostName());
thirdHostTid.setValue(sp.getThirdHostTerminalId());
thirdHostMid.setValue(sp.getThirdHostMerchantId());
thirdHostShortCode.setValue(sp.getThirdHostShortCode());
String[] thp = split(sp.getThirdHostIpAddress());
thirdHostPrimaryIp.setValue(thp[0]);
thirdHostPrimaryPort.setValue(thp[1]);
String[] ths = split(sp.getThirdHostSecIpAddress());
thirdHostSecondaryIp.setValue(ths[0]);
thirdHostSecondaryPort.setValue(ths[1]);
thirdHostCurrency.setValue(normalizeCurrency(sp.getThirdHostCurrency()));
}
}
private String normalizeCurrency(String raw) {
if (TextUtils.isEmpty(raw)) return "";
String value = raw.trim().toUpperCase();
switch (value) {
case "104":
case "MMK":
return "MMK";
case "840":
case "USD":
return "USD";
case "156":
case "CNY":
case "RMB":
return "CNY";
case "764":
case "THB":
return "THB";
case "643":
case "RUB":
return "RUB";
default:
return value;
}
}
private String wrapMerchantAddress(String address) {
if (TextUtils.isEmpty(address)) return "";
String addr = address
.replace("\\r\\n", "\n")
.replace("\\n", "\n")
.replace("\r\n", "\n")
.trim();
//
List<String> lines = wrapAddressText(addr, 29);
//
return TextUtils.join("\n", lines);
}
private String wrapTerminalName(String name) {
if (TextUtils.isEmpty(name)) return "";
String formattedName = name
.replace("\\r\\n", "\n")
.replace("\\n", "\n")
.replace("\r\n", "\n")
.trim();
List<String> lines = wrapAddressText(formattedName, 29);
return TextUtils.join("\n", lines);
}
private String[] split(String raw) {
if (raw == null || raw.trim().isEmpty()) {
return new String[]{"", ""};
}
raw = raw.trim();
// If starts with http/https
if (raw.startsWith("http://") || raw.startsWith("https://")) {
// Attempt to extract host + port via URL
try {
java.net.URL url = new java.net.URL(raw);
String host = url.getHost(); // hostname or IP
int port = url.getPort(); // -1 if no port
String path = url.getPath(); // /api or /
// If port exists → split normally
if (port != -1) {
return new String[]{
host, // e.g. 10.10.10.10
String.valueOf(port) // e.g. 5000
};
}
// No port → do NOT split
return new String[]{ raw, "" };
} catch (Exception e) {
// fail-safe: do not split
return new String[]{ raw, "" };
}
}
// Normal "IP:Port" split
int idx = raw.lastIndexOf(":");
if (idx == -1) {
return new String[]{ raw, "" };
}
return new String[]{
raw.substring(0, idx),
raw.substring(idx + 1)
};
}
}

View File

@ -0,0 +1,178 @@
package com.utsmm.kbz.ui.settings;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.nexgo.downloadkey.downloadflow.DownloadFEntity;
import com.nexgo.downloadkey.downloadflow.DownloadFlow;
import com.nexgo.downloadkey.downloadflow.DownloadFlowProcessListener;
import com.nexgo.downloadkey.downloadflow.DownloadFlowResultEntity;
import com.nexgo.downloadkey.downloadflow.DownloadResult;
import com.utsmyanmar.baselib.fragment.DataBindingFragment;
import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmm.kbz.BR;
import com.utsmm.kbz.R;
import com.utsmm.kbz.databinding.FragmentInjectKeyBinding;
import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmm.kbz.util.tms.TMSUtil;
public class InjectKeyFragment extends DataBindingFragment {
private static final String TAG = InjectKeyFragment.class.getSimpleName();
// Data binding will handle view access automatically
private FragmentInjectKeyBinding binding;
// Key injection variables
private DownloadFlow mDownloadFlow;
private int keyIndexTmp = 8; // Default key index
@Override
protected void initViewModel() {
// No specific viewmodels needed for this fragment
}
@Override
protected DataBindingConfig getDataBindingConfig() {
// This is the key method that links the XML and fragment properly
return new DataBindingConfig(R.layout.fragment_inject_key, 0, null)
.addBindingParam(BR.click, new ClickEvent());
}
@Override
protected int currentId() {
return R.id.injectKeyFragment;
}
@Override
protected int hostId() {
return R.id.nav_host_fragment;
}
@Override
protected int routeId() {
return 0;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
try {
// Get the binding from the base class - this is automatically created
binding = (FragmentInjectKeyBinding) mBinding;
updateConfigurationInfo();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onResume() {
super.onResume();
setToolBarTitleWithBackIcon("Inject Key");
}
private void updateConfigurationInfo() {
binding.serialNumberValue.setText(getDisplayValue(TMSUtil.getInstance().getSerialNumber()));
}
private String getDisplayValue(String value) {
return !TextUtils.isEmpty(value) ? value : "Not configured";
}
private void loadKeyFromKeyPOS() {
try {
if (TextUtils.isEmpty(binding.etKeyIndex.getText())) {
Toast.makeText(getContext(), "Please input key index", Toast.LENGTH_SHORT).show();
return;
}
String keyIndexText = binding.etKeyIndex.getText().toString().trim();
try {
keyIndexTmp = Integer.parseInt(keyIndexText);
} catch (NumberFormatException e) {
Toast.makeText(getContext(), "Invalid key index format", Toast.LENGTH_SHORT).show();
return;
}
showLoadingDialog("Loading key...");
mDownloadFlow = DownloadFlow.getInstance();
String serialNo = TMSUtil.getInstance().getSerialNumber();
if (TextUtils.isEmpty(serialNo)) {
dismissLoadingDialog();
showDeclineDialog("Serial number is not configured");
return;
}
final DownloadFEntity downloadFEntity = new DownloadFEntity();
downloadFEntity.setSn(serialNo.getBytes());
downloadFEntity.setMid(null);
downloadFEntity.setTid(null);
downloadFEntity.setTmkIndex(keyIndexTmp);
downloadFEntity.setPort(0);
downloadFEntity.setTimeOut(10);
int result = mDownloadFlow.startLoadKey(getActivity(), downloadFEntity, onDownloadFlowProcessListener);
if (result != DownloadResult.Success) {
dismissLoadingDialog();
showDeclineDialog("Failed to start key injection process");
LogUtil.e(TAG, "Failed to start key injection, result: " + result);
}
} catch (Exception e) {
dismissLoadingDialog();
LogUtil.e(TAG, "Error in key injection: " + e.getMessage());
showDeclineDialog("Error occurred during key injection: " + e.getMessage());
}
}
private DownloadFlowProcessListener onDownloadFlowProcessListener = new DownloadFlowProcessListener() {
@Override
public void onFinish(int ret, DownloadFlowResultEntity downloadFlowResultEntity) {
try {
dismissLoadingDialog();
if (ret == DownloadResult.Success) {
// Show success dialog with key index
showSuccessDialog("Key injection successful!\nKey Index: " + keyIndexTmp);
LogUtil.d(TAG, "Key injection successful for index: " + keyIndexTmp);
} else {
// Show decline dialog
showDeclineDialog("Key injection failed!\nError code: " + ret);
LogUtil.e(TAG, "Key injection failed with error code: " + ret);
}
} catch (Exception e) {
LogUtil.e(TAG, "Error handling injection result: " + e.getMessage());
showDeclineDialog("Error processing injection result");
}
}
};
// ClickEvent class for data binding - this is the proper pattern
public class ClickEvent {
public void onInjectKeyClick() {
try {
LogUtil.d(TAG, "Inject key button clicked");
loadKeyFromKeyPOS();
} catch (Exception e) {
LogUtil.e(TAG, "Error in inject key click: " + e.getMessage());
showDeclineDialog("Error occurred: " + e.getMessage());
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More