// // BcaFlazzApi.swift // Emoney Info // // Created by Wira Irawan on 27/07/24. // import Foundation import CoreNFC public class BcaFlazzApi : UnifiedNfcApi { var emoney : Emoney = Emoney() var riwayatList: [RiwayatCard] = [] var start = 0 var finishV2 = 5 var finish2V202 = 256 var finishV1 = 16 var mapv1 : String = "" var mapv2 : String = "" public override init() {} public func checkFlazzCard(){ apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BCA_APDU01, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.emoney.setCardLabel("BCA Flazz") self.getCardNumber() } else { self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!)) } }) } private func getCardNumber(){ apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BCA_APDU02, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ let raw = String(data: response.getData(), encoding: .isoLatin1) let start = raw!.firstIndex(of: ";")?.utf16Offset(in: raw!) let end = raw!.firstIndex(of: "=")?.utf16Offset(in: raw!) self.emoney.setCardNumber(String((raw?.subString(from: (start! + 1), to: end!))!)) self.getBalance() } else { self.apduRunner.invalidateSession() } }) } private func getBalance(){ apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BCA_APDU03, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ let raw = response.getData().hexEncodedString() let balance = raw.subString(from: 2, to: 8) self.emoney.setBalance(balance.hex2decimal()) self.checkLog() } else { self.apduRunner.invalidateSession(msg: "readFailed".localizeString(string: self.langCode!)) } }) } private func checkLog(){ apduRunner.exchangeApdu(apduCommand: EmoneyApduCommands.BCA_APDU04, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ debugLog("log v2") // self.updateScreen() self.getLogV2step01(index: self.start) } else { debugLog("log v1") // self.updateScreen() self.getLogV1step01(index: self.start) } }) } private func getLogV2step01(index : Int){ debugLog("log getLogV2step01") //00 B0 85 00 78 let st = index*60 let hex = String(st, radix: 16) let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xB0, p1Parameter : 0x85, p2Parameter : hex.hex2byte().bytes.first!, data : Data(), expectedResponseLength : 120) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ debugLog("log data", response.getData().hexEncodedString()) self.mapv1.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finishV2)){ self.getLogV2step01(index: self.start) } else { //mapping first self.parseLogV201() self.start = 0 self.getLogV2step02(index: self.start) } } else { self.parseLogV201() self.start = 0 self.getLogV2step02(index: self.start) } }) } private func getLogV1step01(index : Int){ debugLog("log getLogV1step01") //00 b0 84 00 3c let st = index*15 let hex = String(st, radix: 16) let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xB0, p1Parameter : 0x84, p2Parameter : hex.hex2byte().bytes.first!, data : Data(), expectedResponseLength : 60) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.mapv1.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finishV1)){ self.getLogV1step01(index: self.start) } else { self.start = 0 self.getLogV1step02(index: self.start) } } else { self.start = 0 self.getLogV1step02(index: self.start) } }) } private func getLogV1step02(index : Int){ debugLog("log getLogV1step02") //00b085003c let st = index*15 let hex = String(st, radix: 16) let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xB0, p1Parameter : 0x85, p2Parameter : hex.hex2byte().bytes.first!, data : Data(), expectedResponseLength : 60) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.mapv1.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finishV1)){ self.getLogV1step02(index: self.start) } else { self.parseLogV101() self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending }) if (self.riwayatList.count > 0){ 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.parseLogV101() self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending }) if (self.riwayatList.count > 0){ self.emoney.setRiwayatList(self.riwayatList) self.emoney.setTampilRiwayat(true) } self.updateScreen() self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } }) } private func getLogV2step02(index : Int){ debugLog("log getLogV2step02") //00b08400f0 let st = index*60 let hex = String(st, radix: 16) let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xB0, p1Parameter : 0x84, p2Parameter : hex.hex2byte().bytes.first!, data : Data(), expectedResponseLength : 240) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.mapv1.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finishV2)){ self.getLogV2step02(index: self.start) } else { self.start = 0 self.getLogV2step03() } } else { self.start = 0 self.getLogV2step03() } }) } private func getLogV2step03(){ debugLog("log getLogV2step03") //00 84 00 00 08 let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0x84, p1Parameter : 0x00, p2Parameter : 0x00, data : Data(), expectedResponseLength : 8) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.getLogV2step04(data: response.getData()) } else { self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } }) } private func getLogV2step04(data: Data){ debugLog("log getLogV2step04") //90 32 03 00 0A 0801 0000000000000000 29 let send = "0801" + data.hexEncodedString() let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x90, instructionCode : 0x32, p1Parameter : 0x03, p2Parameter : 0x00, data : Data(_: send.hex2byte()), expectedResponseLength : 41) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.getLogV2step05(index: self.start) } else { self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } }) } private func getLogV2step05(index : Int){ debugLog("log getLogV2step05") //00 B0 89 00 40 let st = index let hex = String(st, radix: 16) let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xB0, p1Parameter : 0x89, p2Parameter : hex.hex2byte().bytes.first!, data : Data(), expectedResponseLength : 64) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.mapv2.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finish2V202)){ self.getLogV2step05(index: self.start) } else { self.start = 0 self.getLogV2step06(index: self.start) } } else { self.start = 0 self.getLogV2step06(index: self.start) } }) } private func getLogV2step06(index : Int){ debugLog("log getLogV2step06") //90 32 03 00 01 00 20 let st = index let hex = String(st, radix: 16) let BCAV2_LOG = NFCISO7816APDU(instructionClass : 0x90, instructionCode : 0x32, p1Parameter : 0x03, p2Parameter : 0x00, data : Data(_: hex.hex2byte()), expectedResponseLength : 32) apduRunner.exchangeApdu(apduCommand: BCAV2_LOG, completionHandler: {response in if (response.sw1 == 0x90 && response.sw2 == 0x00){ self.mapv2.append(response.getData().hexEncodedString()) self.start+=1 if (self.start < (self.finish2V202)){ self.getLogV2step06(index: self.start) } else { //mapping the data self.parseLogV202() self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending }) if (self.riwayatList.count > 0){ self.emoney.setRiwayatList(self.riwayatList) self.emoney.setTampilRiwayat(true) } self.updateScreen() self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } } else { //mapping the data self.parseLogV202() self.riwayatList = self.riwayatList.sorted(by: { $0.getTransationTime()?.compare($1.getTransationTime()!) == .orderedDescending }) if (self.riwayatList.count > 0){ self.emoney.setRiwayatList(self.riwayatList) self.emoney.setTampilRiwayat(true) } self.updateScreen() self.apduRunner.sessionEx?.alertMessage = "readFinish".localizeString(string: self.langCode!) self.apduRunner.invalidateSession() } }) } private func parseLogV101(){ debugLog("log parseLogV101") let logs = self.mapv1.trimmingCharacters(in: .whitespacesAndNewlines) if (logs.count % 120 != 0){ return } let total = logs.count/120 for i in 0.. 0){ riwayatList.append(riwayat) } } } private func parseLogV201(){ debugLog("log parseLogV201") let logs = self.mapv1.trimmingCharacters(in: .whitespacesAndNewlines) if (logs.count % 120 != 0){ return } let total = logs.count/120 for i in 0.. 0){ riwayatList.append(riwayat) } } } private func parseLogV202(){ debugLog("log parseLogV202") let logs = self.mapv2.trimmingCharacters(in: .whitespacesAndNewlines) if (logs.count % 64 != 0){ return } let total = logs.count/64 for i in 0.. 0){ riwayatList.append(riwayat) } } } private func formatDate(seconds : Int) -> Date{ // Specify date components var dateComponents = DateComponents() dateComponents.year = 1980 dateComponents.month = 1 dateComponents.day = 1 dateComponents.timeZone = TimeZone(identifier: "Asia/Jakarta")! dateComponents.hour = 0 dateComponents.minute = 0 dateComponents.second = seconds // Create date from components let userCalendar = Calendar.current // user calendar let someDateTime = userCalendar.date(from: dateComponents) return someDateTime! } private func updateScreen(){ if (self.apduRunner.callback != nil){ self.apduRunner.callback?.complete(emoney: self.emoney) } } }