// // MandiriEmoneyApi.swift // Emoney Info // // Created by Wira Irawan on 27/07/24. // import Foundation import CoreNFC public class MandiriEmoneyApi : UnifiedNfcApi { var emoney : Emoney = Emoney() var riwayatList: [RiwayatCard] = [] var cardType : Int? var mapv1 : String = "" var start = 0 var finish = 256 var finish2 = 10 public override init() {} public func getCardNumber(){ apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.MANDIRI_APDU01, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.emoney.setCardLabel("Mandiri e-Money") self.emoney.setCardNumber(response.getData().hexEncodedString().subString(from: 0, to: 16)) self.cardType = response.getData().hexEncodedString().subString(from: 36, to: 38).hex2decimal() debugLog(self.cardType!) self.getBalance() } else { self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!)) } }) } private func getBalance(){ apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.MANDIRI_APDU02, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ debugLog(response.getData().hexEncodedString()) let balance = response.getData().hexEncodedString().subString(from: 0, to: 8) self.emoney.setBalance(self.getRealBalance(reverseHexa: balance)) // self.updateScreen() if (self.cardType! == 131){ self.getLogStep01(index: self.start) } else { self.getLogStep02(index: self.start) } } else { self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!)) } }) } private func getLogStep01(index : Int){ //00 d1 00 00 00 let hex = String(index, radix: 16) let MANDIRI_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xD1, p1Parameter : hex.hex2byte().bytes.first!, p2Parameter : 0x00, data : Data(), expectedResponseLength : CommonConstants.LE_GET_ALL_RESPONSE_DATA) apduRunner.exchangeApdu(apduCommand: MANDIRI_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.mapv1.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finish)){ self.getLogStep01(index: self.start) } else { self.parseNewLog() self.start = 0 self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending }) self.emoney.setRiwayatList(self.riwayatList) self.emoney.setTampilRiwayat(true) self.updateScreen() self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } } else { self.parseNewLog() self.start = 0 self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending }) self.emoney.setRiwayatList(self.riwayatList) self.emoney.setTampilRiwayat(true) self.updateScreen() self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } }) } func parseNewLog(){ let logs = self.mapv1.trimmingCharacters(in: .whitespacesAndNewlines) if (logs.count % 48 != 0){ return } let total = logs.count/48 for i in 0.. Date?{ let dateFormatter2 = DateFormatter() dateFormatter2.dateFormat = "HHmmss" let date12 = dateFormatter2.date(from: formatTime)! dateFormatter2.dateFormat = "hh:mm a" let date22 = dateFormatter2.string(from: date12) let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") // set locale to reliable US_POSIX dateFormatter.dateFormat = "ddMMyy hh:mm a" return dateFormatter.date(from:(formatDate + " " + date22)) } func getRealBalance(reverseHexa: String?) -> Int { guard let reverseHexa = reverseHexa?.trimmingCharacters(in: .whitespacesAndNewlines), !reverseHexa.isEmpty else { return 0 } if reverseHexa.count % 2 != 0 { return 0 } var sb = "" for l in stride(from: reverseHexa.count / 2, through: 1, by: -1) { let index1 = reverseHexa.index(reverseHexa.startIndex, offsetBy: l * 2 - 2) let index2 = reverseHexa.index(reverseHexa.startIndex, offsetBy: l * 2 - 1) sb.append(reverseHexa[index1]) sb.append(reverseHexa[index2]) } return sb.hex2decimal() } private func updateScreen(){ if (self.apduRunner.callback != nil){ self.apduRunner.callback?.complete(emoney: self.emoney) } } func riwayatCard(_ bArr: [UInt8]) -> RiwayatCard? { var str: String let riwayatCard = RiwayatCard() var wrap = Data(bArr) var bArr2 = [UInt8](repeating: 0, count: 6) var bArr3 = [UInt8](repeating: 0, count: 16) wrap.copyBytes(to: &bArr2, count: 6) wrap.removeFirst(10) let len = wrap.withUnsafeBytes { $0.load(as: Int32.self).bigEndian } wrap.removeFirst(4) let amount = wrap.withUnsafeBytes { $0.load(as: Int32.self).littleEndian } wrap.removeFirst(4) let desk = wrap.withUnsafeBytes { $0.load(as: Int32.self).littleEndian } wrap.removeFirst(4) do { wrap.copyBytes(to: &bArr3, count: 16) debugLog("bArr3: \(bArr3.map { String(format: "%02X", $0) }.joined())") } catch { debugLog("Error: \(error.localizedDescription)") } var type = 0 if len == 288 { str = "payment".localizeString(string: self.langCode!) type = 1 } else if len == 256 || len == 336 { type = 3 str = "topup".localizeString(string: self.langCode!) } else { type = -1 str = "unknown".localizeString(string: self.langCode!) } var str2 = "" for y in 0..<6 { str2 += String(format: "%02X", bArr2[y]) } let transactionTime = self.getTransactionTime(formatDate: str.subString(from: 0, to: 6), formatTime: str.subString(from: 6, to: 12)) riwayatCard.setTransactionTime(transactionTime!) riwayatCard.setAmount(Int(amount)) // if str.caseInsensitiveCompare("payment") == .orderedSame { // type = 1 // } else if str.caseInsensitiveCompare("topup") != .orderedSame { // type = 3 // } riwayatCard.setProsesTipe(type) riwayatCard.setTitle(str) if type == -1 { return nil } return riwayatCard } }