Implement bank transfer app flows
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import Foundation
|
||||
|
||||
// Mirrors Kotlin MibFinancingClient exactly.
|
||||
// Fetches the /financing?dashurl=1 HTML page and parses finance-card-holder data attributes.
|
||||
actor MibFinancingClient {
|
||||
|
||||
private let wvBase = "https://faisamobilex-wv.mib.com.mv"
|
||||
private let session: MibSession
|
||||
|
||||
private let urlSession: URLSession = {
|
||||
let cfg = URLSessionConfiguration.ephemeral
|
||||
cfg.timeoutIntervalForRequest = 30
|
||||
cfg.timeoutIntervalForResource = 60
|
||||
return URLSession(configuration: cfg)
|
||||
}()
|
||||
|
||||
init(session: MibSession) {
|
||||
self.session = session
|
||||
}
|
||||
|
||||
func fetchFinancing() async throws -> [MibFinanceDeal] {
|
||||
var req = URLRequest(url: URL(string: "\(wvBase)/financing?dashurl=1")!)
|
||||
req.setValue(cookieHeader, forHTTPHeaderField: "Cookie")
|
||||
req.setValue(
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148",
|
||||
forHTTPHeaderField: "User-Agent"
|
||||
)
|
||||
req.setValue("mv.com.mib.faisamobilex", forHTTPHeaderField: "X-Requested-With")
|
||||
|
||||
let (data, response) = try await urlSession.data(for: req)
|
||||
let code = (response as? HTTPURLResponse)?.statusCode ?? 0
|
||||
if code == 419 { throw MibError.sessionExpired }
|
||||
if code >= 500 { throw MibError.networkError("HTTP \(code)") }
|
||||
|
||||
guard let html = String(data: data, encoding: .utf8) else { return [] }
|
||||
return parseHtml(html)
|
||||
}
|
||||
|
||||
// MARK: - HTML parsing (mirrors Kotlin regex approach)
|
||||
|
||||
private func parseHtml(_ html: String) -> [MibFinanceDeal] {
|
||||
guard let cardRe = try? NSRegularExpression(pattern: #"finance-card-holder[^>]+>"#),
|
||||
let attrRe = try? NSRegularExpression(pattern: #"data-(\w+)\s*=\s*"([^"]*)""#) else {
|
||||
return []
|
||||
}
|
||||
|
||||
let ns = html as NSString
|
||||
return cardRe.matches(in: html, range: NSRange(location: 0, length: ns.length)).compactMap { m in
|
||||
let cardStr = ns.substring(with: m.range)
|
||||
let cardNS = cardStr as NSString
|
||||
var attrs: [String: String] = [:]
|
||||
attrRe.matches(in: cardStr, range: NSRange(location: 0, length: cardNS.length)).forEach { a in
|
||||
let key = cardNS.substring(with: a.range(at: 1))
|
||||
let val = cardNS.substring(with: a.range(at: 2))
|
||||
attrs[key] = val
|
||||
}
|
||||
guard let dealNo = attrs["dealNo"] else { return nil }
|
||||
return MibFinanceDeal(
|
||||
dealNo: dealNo,
|
||||
productDesc: attrs["productDesc"] ?? "",
|
||||
dealStatus: attrs["dealStatus"] ?? "",
|
||||
statusDesc: attrs["statusDesc"] ?? "",
|
||||
dealAmount: Double(attrs["dealAmount"] ?? "") ?? 0,
|
||||
paidAmount: Double(attrs["paidAmount"] ?? "") ?? 0,
|
||||
outstandingAmount: Double(attrs["outstandingAmount"] ?? "") ?? 0,
|
||||
dealDate: attrs["dealDate"] ?? "",
|
||||
overdueAmount: Double(attrs["overdueAmount"] ?? "") ?? 0,
|
||||
installmentAmount: Double(attrs["installmentAmount"] ?? "") ?? 0,
|
||||
noOfInstallments: Int(attrs["noOfInstallments"] ?? "") ?? 0,
|
||||
lastPaidDate: attrs["lastPaidDate"] ?? "",
|
||||
lastPayAmount: Double(attrs["lastPayAmount"] ?? "") ?? 0,
|
||||
currency: attrs["curCodeDesc"] ?? "MVR"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var cookieHeader: String {
|
||||
"mbmodel=IOS-1.0; xxid=\(session.xxid); IBSID=\(session.xxid); mbnonce=\(session.nonceGenerator); time-tracker=597"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user