Initial commit
This commit is contained in:
263
Emoney Info/Classes/api/MandiriEmoneyApi.swift
Executable file
263
Emoney Info/Classes/api/MandiriEmoneyApi.swift
Executable file
@ -0,0 +1,263 @@
|
||||
//
|
||||
// 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..<total {
|
||||
let start = i*48
|
||||
let end = start + 48
|
||||
let data = logs.subString(from: start, to: end)
|
||||
let riwayat = RiwayatCard()
|
||||
let time = self.getTransactionTime(formatDate: data.subString(from: 0, to: 6), formatTime: data.subString(from: 6, to: 12))
|
||||
riwayat.setTransactionTime(time!)
|
||||
let processType = Int(data.subString(from: 28, to: 32))
|
||||
if (processType == 100){
|
||||
riwayat.setProsesTipe(0)
|
||||
riwayat.setTitle("Top Up")
|
||||
} else {
|
||||
riwayat.setProsesTipe(1)
|
||||
riwayat.setTitle("Payment")
|
||||
}
|
||||
riwayat.setLocationId(data.subString(from: 12, to: 20))
|
||||
let amount = data.subString(from: 32, to: 40)
|
||||
riwayat.setAmount(getRealBalance(reverseHexa: amount))
|
||||
riwayatList.append(riwayat)
|
||||
}
|
||||
}
|
||||
|
||||
private func getLogStep02(index : Int){
|
||||
//00 b2 00 00 1e
|
||||
let hex = String(index, radix: 16)
|
||||
let MANDIRI_LOG = NFCISO7816APDU(instructionClass : 0x00, instructionCode : 0xB2, p1Parameter : hex.hex2byte().bytes.first!, p2Parameter : 0x00, data : Data(), expectedResponseLength : 30)
|
||||
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.finish2)){
|
||||
self.getLogStep02(index: self.start)
|
||||
} else {
|
||||
self.parseOldLog()
|
||||
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.parseOldLog()
|
||||
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 parseOldLog(){
|
||||
let logs = self.mapv1.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
if (logs.count % 60 != 0){
|
||||
return
|
||||
}
|
||||
let total = logs.count/60
|
||||
for i in 0..<total {
|
||||
let start = i*60
|
||||
let end = start + 60
|
||||
let data = logs.subString(from: start, to: end)
|
||||
let riwayat = riwayatCard(data.hex2byte().bytes)
|
||||
riwayatList.append(riwayat!)
|
||||
}
|
||||
}
|
||||
|
||||
func getTransactionTime(formatDate : String, formatTime : String) -> 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user