Untitled
unknown
plain_text
9 months ago
34 kB
7
Indexable
//
// QrisPayAmountViewModel.swift
// IDBank
//
// Created by Myoungkyu.Shin on 11/06/2019.
// Copyright (c) 2019 LINE Bank Indonesia Corporation. All rights reserved.
//
import RxSwift
import RxCocoa
import RxSwiftExt
import BuffettRemote
enum QrisPayAmountValidState {
case inputAmount
case sendMoney
case validate
var title: String {
switch self {
case .inputAmount:
return LocalizableString.commonOkButton
case .sendMoney, .validate:
return LocalizableString.qrpaymentPayButton
}
}
var isConfirmState: Bool { [.sendMoney, .validate].contains(self) }
}
enum QrisPayAmountResult {
enum ErrorType: Error {
case notEnough
case dataIsNil
case serverError(error: ResultError)
case serverErrorWithPay(error: ResultError)
}
enum Validation {
case ok
case ready
case overAccountBalance(message: String)
case overOnetimeLimit(message: String)
case underMinimumLimit(message: String)
case invalid(message: String)
}
}
enum QrisPayPendingStatus: Int {
case unknown
case first = 1
case second = 2
case last = 3
func next() -> QrisPayPendingStatus {
switch self {
case .unknown:
return .first
case .first:
return .second
case .second:
return .last
case .last:
return .unknown
}
}
}
extension QrisPayAmountResult.Validation: Equatable {
static func == (lhs: QrisPayAmountResult.Validation, rhs: QrisPayAmountResult.Validation) -> Bool {
switch (lhs, rhs) {
case (.ok, .ok):
return true
case (.ready, .ready):
return true
case (.overAccountBalance, .overAccountBalance):
return true
case (.overOnetimeLimit, .overOnetimeLimit):
return true
case (.underMinimumLimit, .underMinimumLimit):
return true
case (.invalid, .invalid):
return true
default:
return false
}
}
}
extension QrisPayAmountResult.Validation {
var valid: Bool { self == .ok }
var isError: Bool { errorMessage?.isEmpty != nil }
var errorMessage: String? {
switch self {
case let .overAccountBalance(message):
return message
case let .overOnetimeLimit(message):
return message
case let .underMinimumLimit(message):
return message
case let .invalid(message):
return message
default:
return nil
}
}
}
protocol QrisPayAmountViewModelType {
var inputs: QrisPayAmountViewModelInputs { get }
var outputs: QrisPayAmountViewModelOutputs { get }
}
protocol QrisPayAmountViewModelInputs {
func setup(params: QrisPayParams)
func fetch()
func refreshAccountData()
func fetchDisbursalLimit(payload: QrisPayAPIModel.QRISPaymentDisbursalLimitRequest)
func validation()
func amountChanged(to amount: Decimal)
// User input amount
func inputAmountChanged(to amount: Decimal)
func addAmount(to amount: Decimal)
func changeAccount(with account: AccountAPIModel.AccountSimpleInfo)
func createParams(payInfo: QrisPayAmountViewModel.PayInfo)
func pay(payload: QrisPayAmountViewModel.PayloadRequest)
func tipAmount(to amount: Decimal)
func changeInputState()
func changeSendMoneyState()
func updateState()
func qrisPendingResponse(_ pendingResponse: QrisPayAPIModel.PayResult, _ isDisbursal: Bool)
func allUsedQuickCreditPayment(_ isAll: Bool)
func showResult()
}
protocol QrisPayAmountViewModelOutputs {
var payParam: Observable<QrisPayParams?> { get }
var cellModelObservable: Observable<QrisPayAmountCellModel?> { get }
var accountList: Observable<AccountAPIModel.Accounts> { get }
var disbursalLimit: Observable<QrisPayAPIModel.QRISPaymentDisbursalLimitResponse> { get }
var validationResult: Driver<QrisPayAmountResult.Validation> { get }
var inputValidationResult: Driver<QrisPayAmountResult.Validation> { get }
var amountResult: Driver<String?> { get }
var tips: Observable<QrisPayParams.Tips> { get }
var fixedAmount: Observable<Bool> { get }
var tipAmount: Observable<Decimal> { get }
var createdParam: Observable<QrisPayAmountViewModel.PayloadRequest> { get }
var completeTransfer: Observable<QrisPayment.Transaction> { get }
var state: Driver<QrisPayAmountValidState> { get }
var isConfirmState: Bool { get }
var qrisPending: Observable<(QrisPayAPIModel.PayResult, Bool)> { get }
var qrisPendingStatus: Driver<QrisPayPendingStatus> { get }
var isAllUsedQuickCredit: Driver<Bool> { get }
var invalidRecentlyFavorite: Driver<()> { get }
var isLoading: Driver<Bool> { get }
var error: Driver<QrisPayAmountResult.ErrorType> { get }
}
private extension QrisPayAmountViewModel {
struct QrisPay: Codable {
// account Identification1
let acctIdentification1: String?
// acquiring institution Id
let acquInstId: String
// additional data
let addtnlData: String
// additional Data National
let addtnlDataNatnl: String
// approval code
let apprCode: String?
// card acceptorId
let cardAcceptId: String
// card acceptor name & location
let cardAcceptNmLoc: String
// card acceptor terminal identification
let cardAcceptTermId: String?
// currency
let ccy: String // length: 3
// amount convenience fee
let convFee: String?
// receiver account holder
let crAcctNm1: String? // length: 30
// receiver account holder2
let crAcctNm2: String? // length: 30
// receiver account no
let crAcctNo: String // length: 30
// receiver account type
let crAcctType: String? // length: 3
// receiver bank code
let crBankCd: String? // length: 6
// receiver card no
let crCardNo: String? // length: 50
// withdrawal account holder
let dbAcctNm: String
// withdrawal account no
let dbAcctNo: String // length: 30
// forwarding institution Id
let fwdInstId: String
// issuer Id
let issuerId: String
// merchantCity
let merchantCity: String?
// merchants name
let merchantName: String
// merchants type
let merchantType: String // length: 4
// reserved2
let reserved2: String?
// reserved3
let reserved3: String?
// point of service entry mode
let svcEntryMode: String
// tips
let tipsAmount: String?
// transaction amount
let trxAmt: String
// quick credit disbursal Y/N
let quickCreditDisbursalYn: String
// loan Account number
let loanAcctNo: String?
// using loan amount
let loanAmt: String?
}
}
final class QrisPayAmountViewModel: QrisPayAmountViewModelType {
typealias PayloadRequest = [String: Any]
typealias RecentlyFavoriteModel = TransferAPIModel.RecentBookMark.TransferTransaction
typealias LoanInfo = (quickCreditDisbursalYn: String, loanAcctNo: String?, loanAmt: String?)
// swiftlint:disable superfluous_disable_command large_tuple
typealias PayInfo = (withdrawMerchantPan: String, withdrawMerchantName: String, amount: Decimal, tip: Decimal, loanInfo: LoanInfo)
private enum Constant {
static let hotKeyAllAmountTag: Decimal = 4444_987_654_321
}
private let disposeBag = DisposeBag()
// output
private let cellModelRelay: BehaviorRelay<QrisPayAmountCellModel?> = .init(value: nil)
private let accountListSubject: PublishSubject<AccountAPIModel.Accounts?> = .init()
private let disbursalLimitRelay: BehaviorRelay<QrisPayAPIModel.QRISPaymentDisbursalLimitResponse?> = .init(value: nil)
private let validationResultSubject: BehaviorSubject<QrisPayAmountResult.Validation> = .init(value: .ready)
private let amountSubject: BehaviorSubject<String?> = .init(value: nil)
private let inputValidationResultSubject: BehaviorSubject<QrisPayAmountResult.Validation> = .init(value: .ready)
private let isLoadingRelay: BehaviorRelay<Bool> = .init(value: false)
private let tipsSubject: BehaviorSubject<QrisPayParams.Tips> = .init(value: (.none, nil))
private let enableAmountSubject: BehaviorSubject<Bool> = .init(value: true)
private let createdParamSubject: PublishSubject<PayloadRequest> = .init()
private let completeTransferSubject: PublishSubject<QrisPayment.Transaction> = .init()
private let stateSubject: BehaviorRelay<QrisPayAmountValidState> = .init(value: .inputAmount)
private let pendingResponseSubject: BehaviorSubject<(QrisPayAPIModel.PayResult, Bool)?> = .init(value: nil)
private let pendingStatusRelay: BehaviorRelay<QrisPayPendingStatus> = .init(value: .unknown)
private let errorSubject: PublishSubject<QrisPayAmountResult.ErrorType?> = .init()
init() {
setupRemote()
}
/// inputs
private let recentlyFavoriteModelProperty: BehaviorRelay<RecentlyFavoriteModel?> = .init(value: nil)
private let payParamProperty: BehaviorRelay<QrisPayParams?> = .init(value: nil)
private var transferOptionViewModel: TransferOptionViewModelType?
private let fetchProperty: PublishSubject<()> = .init()
private let refreshAccountDataProperty: PublishSubject<()> = .init()
private let fetchDisbursalLimitProperty: PublishSubject<QrisPayAPIModel.QRISPaymentDisbursalLimitRequest> = .init()
private let validationProperty: PublishSubject<()> = .init()
private let amountChangedProperty: PublishSubject<Decimal> = .init()
private let inputAmountChangedProperty: PublishSubject<Decimal> = .init()
private let addAmountProperty: PublishSubject<Decimal> = .init()
private let tipAmountProperty: BehaviorSubject<Decimal> = .init(value: 0)
private let currentAccountProperty: PublishSubject<AccountAPIModel.AccountSimpleInfo> = .init()
private let createParamProperty: PublishSubject<PayInfo> = .init()
private let payProperty: PublishSubject<PayloadRequest> = .init()
private let showResultProperty: PublishSubject<Void> = .init()
private let allUsedQuickCreditRelay: BehaviorRelay<Bool> = .init(value: false)
var inputs: QrisPayAmountViewModelInputs { self }
var outputs: QrisPayAmountViewModelOutputs { self }
}
extension QrisPayAmountViewModel: QrisPayAmountViewModelOutputs {
var payParam: Observable<QrisPayParams?> {
payParamProperty.asObservable()
}
var cellModelObservable: Observable<QrisPayAmountCellModel?> {
cellModelRelay.asObservable()
}
var accountList: Observable<AccountAPIModel.Accounts> {
accountListSubject.asObservable().filterNil()
}
var disbursalLimit: Observable<QrisPayAPIModel.QRISPaymentDisbursalLimitResponse> {
disbursalLimitRelay.asObservable().filterNil()
}
var validationResult: Driver<QrisPayAmountResult.Validation> {
validationResultSubject.asDriver(onErrorJustReturn: .ready)
}
var inputValidationResult: Driver<QrisPayAmountResult.Validation> {
inputValidationResultSubject.asDriver(onErrorJustReturn: .ready)
}
var amountResult: Driver<String?> {
amountSubject.asDriver(onErrorJustReturn: nil)
}
var tips: Observable<QrisPayParams.Tips> {
tipsSubject.asObservable()
}
var fixedAmount: Observable<Bool> {
enableAmountSubject.asObservable()
}
var tipAmount: Observable<Decimal> {
tipAmountProperty.asObservable()
}
var createdParam: Observable<QrisPayAmountViewModel.PayloadRequest> {
createdParamSubject.asObservable()
}
var completeTransfer: Observable<QrisPayment.Transaction> {
completeTransferSubject.asObservable()
}
var state: Driver<QrisPayAmountValidState> {
stateSubject.asDriver(onErrorJustReturn: .inputAmount)
}
var isConfirmState: Bool {
stateSubject.value.isConfirmState
}
var qrisPending: Observable<(QrisPayAPIModel.PayResult, Bool)> {
pendingResponseSubject.asObservable().filterNil()
}
var qrisPendingStatus: Driver<QrisPayPendingStatus> {
pendingStatusRelay.asDriver(onErrorJustReturn: .unknown)
}
var isAllUsedQuickCredit: Driver<Bool> {
allUsedQuickCreditRelay.asDriver(onErrorJustReturn: false)
}
var invalidRecentlyFavorite: Driver<()> {
error.filter { $0.serverError == .invalidRecentBookmarkAccountNo }.mapTo(())
}
var isLoading: Driver<Bool> {
isLoadingRelay.asDriver()
}
var error: Driver<QrisPayAmountResult.ErrorType> {
errorSubject.asDriver(onErrorJustReturn: nil).filterNil()
}
}
extension QrisPayAmountViewModel: QrisPayAmountViewModelInputs {
func setup(params: QrisPayParams) {
// recentlyFavoriteModelProperty.accept(recentlyFavorite)
tipsSubject.onNext(params.tips)
payParamProperty.accept(params)
}
func fetch() {
fetchProperty.onNext(())
}
func refreshAccountData() {
refreshAccountDataProperty.onNext(())
}
func fetchDisbursalLimit(payload: QrisPayAPIModel.QRISPaymentDisbursalLimitRequest) {
fetchDisbursalLimitProperty.onNext(payload)
}
func validation() {
validationResultSubject.onNext(.ready)
validationProperty.onNext(())
}
func amountChanged(to amount: Decimal) {
amountChangedProperty.onNext(amount)
}
func inputAmountChanged(to amount: Decimal) {
inputAmountChangedProperty.onNext(amount)
}
func addAmount(to amount: Decimal) {
addAmountProperty.onNext(amount)
}
func changeAccount(with account: AccountAPIModel.AccountSimpleInfo) {
currentAccountProperty.onNext(account)
}
func createParams(payInfo: PayInfo) {
createParamProperty.onNext(payInfo)
}
func pay(payload: QrisPayAmountViewModel.PayloadRequest) {
payProperty.onNext(payload)
}
func tipAmount(to amount: Decimal) {
tipAmountProperty.onNext(amount)
}
func changeInputState() {
stateSubject.accept(.inputAmount)
}
func changeSendMoneyState() {
stateSubject.accept(.sendMoney)
}
func updateState() {
stateSubject.accept(isConfirmState ? .validate : .sendMoney)
}
func qrisPendingResponse(_ pendingResponse: QrisPayAPIModel.PayResult, _ isDisbursal: Bool) {
pendingResponseSubject.onNext((pendingResponse, isDisbursal))
pendingStatusRelay.accept(pendingStatusRelay.value.next())
changeSendMoneyState()
}
func allUsedQuickCreditPayment(_ isAll: Bool) {
allUsedQuickCreditRelay.accept(isAll)
}
func showResult() {
showResultProperty.onNext(())
}
// func fetchDisbursalLimitQuickCredit(){
// isLoadingRelay.accept(true)
//
// disposable += allUsedQuickCreditRelay
// .flatMapLatest {
// Remote<QuickCreditAPIModel.Main>.QuickCredit
// .main(loanAccountNo: $0)
// .useErrorLog()
// .asObservable()
// }
// .subscribe(onNext: { [weak self] in
// guard let self = self else { return }
//
// self.isLoadingRelay.accept(false)
//
// switch $0 {
// case let .success(value):
// self.infoSubject.onNext(QuickCreditHeaderPresentModel(value))
// self.hasAnyPrevBillsSubject.onNext(value.monthlyBill != nil)
//
// case let .failure(error):
// self.errorSubject.onNext(error)
// }
// })
//
// }
}
// MARK: Factory
extension QrisPayAmountViewModel {
// func createOptionViewModel(transferType: QrisPaymentAPIModel.PayType) -> QrisPayOptionViewModelType? {
// switch transferType {
// case .lineToLine:
// return TransferLineBankViewModel()
// case .lineToNonBank:
// return TransferLineNonBankViewModel()
// case .phoneToLineBank:
// return TransferPhoneBankViewModel()
// case .phoneToNonBank:
// return TransferPhoneNonBankViewModel()
// case .lineBankAccount, .otherBankAccount:
// return TransferBankAccountViewModel()
// case .myAccount:
// return TransferMyAccountViewModel()
// case .unknown:
// return nil
// }
// }
}
// MARK: - Remote
private extension QrisPayAmountViewModel {
func setupRemote() {
fetchProperty
.do(onNext: { [weak self] _ in
self?.isLoadingRelay.accept(true)
})
.withLatestFrom(payParamProperty)
.filterNil()
.flatMapLatest { [weak self] param -> Observable<(QrisPayParams, QrisPayAPIModel.QRISPaymentDisbursalLimitResponse)> in
guard let self = self else { return .empty() }
let initData = QrisPayAPIModel.QRISPaymentDisbursalLimitRequest(loanAcctNo: 0, quickCreditDisbursalYn: "",amount: 0)
self.fetchDisbursalLimit(payload: initData)
return .create {
self.disbursalLimitRelay
.filterNil()
.map { (param, $0) }
.bind(to: $0)
}
}
.flatMapLatest { [weak self] param, limit -> Observable<(QrisPayParams, QrisPayAPIModel.QRISPaymentDisbursalLimitResponse, AccountAPIModel.Accounts)> in
guard let self = self else { return .empty() }
return .create { observer -> Disposable in
Remote<AccountAPIModel.Accounts>.Account
.list()
.useErrorLog()
.asObservable()
.subscribe(onNext: { [weak self] in
guard let self = self else { return }
switch $0 {
case let .success(value):
self.accountListSubject.onNext(value)
observer.onNext((param, limit, value))
case let .failure(error):
self.errorSubject.onNext(.serverError(error: error))
observer.onCompleted()
}
})
}
}
.bind(onNext: { [weak self] _, _, accountList in
guard let self = self else { return }
guard let currentAccount = accountList.list.filter({ $0.dpAcctYn }).first else {
self.errorSubject.onNext(.dataIsNil)
return
}
self.currentAccountProperty.onNext(currentAccount)
}).disposed(by: disposeBag)
refreshAccountDataProperty
.do(onNext: { [weak self] _ in self?.isLoadingRelay.accept(true) })
.flatMapLatest { _ -> Observable<Result<AccountAPIModel.Accounts>> in
Remote<AccountAPIModel.Accounts>.Account
.list()
.useErrorLog()
.asObservable()
}
.compactMap { [weak self] result -> AccountAPIModel.Accounts? in
guard let self = self else { return nil }
switch result {
case let .success(value):
return value
case let .failure(error):
self.errorSubject.onNext(.serverError(error: error))
return nil
}
}
.withLatestFrom(currentAccountProperty) { ($0, $1) }
.do(onNext: { [weak self] _ in self?.isLoadingRelay.accept(false) })
.bind(onNext: { [weak self] accountList, currentAccount in
guard let self = self else { return }
defer {
self.accountListSubject.onNext(accountList)
}
guard let currentAccount = accountList.list.first(where: { $0.acctNo == currentAccount.acctNo }) else {
self.errorSubject.onNext(.dataIsNil)
return
}
self.currentAccountProperty.onNext(currentAccount)
}).disposed(by: disposeBag)
fetchDisbursalLimitProperty
.flatMapLatest { data -> Observable<Result<QrisPayAPIModel.QRISPaymentDisbursalLimitResponse>> in
let payloadDisbursalLimit = QrisPayAPIModel.QRISPaymentDisbursalLimitRequest(loanAcctNo: data.loanAcctNo, quickCreditDisbursalYn: data.quickCreditDisbursalYn, amount: data.amount)
return Remote<QrisPayAPIModel.QRISPaymentDisbursalLimitResponse>.QrisPayment
.disbursalLimit(payloadDisbursalLimit)
.useErrorLog()
.asObservable()
}
.bind(onNext: { [weak self] in
guard let self = self else { return }
switch $0 {
case let .success(value):
self.disbursalLimitRelay.accept(value)
case let .failure(error):
self.errorSubject.onNext(.serverError(error: error))
}
}).disposed(by: disposeBag)
validationProperty
.withLatestFrom(cellModelRelay) { $1 }
.filterNil()
.map { self.validation(to: $0.amount) }
.bind(onNext: {
self.validationResultSubject.onNext($0)
}).disposed(by: disposeBag)
amountChangedProperty
.withLatestFrom(cellModelRelay) { ($0, $1) }
.map { amount, cellModel -> (QrisPayAmountResult.Validation, Decimal) in
let resultAmount = amount.compareLength(13) == .greaterThan ?
(cellModel?.amount ?? 0) : amount
let validationResult = self.validation(to: resultAmount)
cellModel?.amount = resultAmount
return (validationResult, resultAmount)
}.bind(onNext: {
self.validationResultSubject.onNext($0.0)
self.amountSubject.onNext($0.1 == 0 ? nil : $0.1.currencyFormatted(showSymbol: false))
}).disposed(by: disposeBag)
inputAmountChangedProperty
.withLatestFrom(cellModelRelay) { ($0, $1) }
.compactMap { [weak self] amount, cellModel -> (QrisPayAmountResult.Validation, Decimal)? in
guard let self = self else { return nil }
let resultAmount = amount.compareLength(13) == .greaterThan ?
(cellModel?.amount ?? 0) : amount
let validationResult = self.validation(to: resultAmount)
cellModel?.amount = resultAmount
return (validationResult, resultAmount)
}.bind(onNext: { [weak self] in
guard let self = self else { return }
self.inputValidationResultSubject.onNext($0.0)
switch $0.0 {
case .underMinimumLimit:
break
default:
self.validationResultSubject.onNext($0.0)
}
}).disposed(by: disposeBag)
tipAmountProperty
.withLatestFrom(tipsSubject)
.filter { $0.0 == .input }
.withLatestFrom(cellModelRelay)
.compactMap { [weak self] cellModel -> QrisPayAmountResult.Validation? in
guard let self = self else { return nil }
let validationResult = self.validation(to: cellModel?.amount ?? 0)
return validationResult
}.bind(onNext: { [weak self] in
guard let self = self else { return }
switch $0 {
case .underMinimumLimit:
break
default:
self.validationResultSubject.onNext($0)
}
}).disposed(by: disposeBag)
addAmountProperty
.withLatestFrom(cellModelRelay.filterNil()) { ($0, $1) }
.bind(onNext: { value, cellModel in
let result: Decimal = with(cellModel.amount + value) {
guard value != Constant.hotKeyAllAmountTag else {
return Decimal(cellModel.withdrawAccountBalance.intValue)
}
return $0
}
self.amountChangedProperty.onNext(result)
}).disposed(by: disposeBag)
currentAccountProperty
.asObservable()
.distinctUntilChanged()
.withLatestFrom(payParamProperty) { ($0, $1) }
.withLatestFrom(disbursalLimitRelay) { ($0, $1) }
.bind(onNext: { [weak self] info, limit in
guard let self = self, let param = info.1, let limit = limit else { return }
self.makeCellModel(limit, param, account: .init(model: info.0))
if let amount = param.transactionAmount {
self.amountChangedProperty.onNext(amount.decimalValue)
self.enableAmountSubject.onNext(false)
}
}).disposed(by: disposeBag)
createParamProperty
.withLatestFrom(payParam) { ($0, $1) }
.withLatestFrom(currentAccountProperty) { ($0.0, $0.1, $1) }
.map { [weak self] in
self?.makeRequestParam($0.0, $0.1, $0.2)?.asDictionary
}
.filterNil()
.bind(onNext: createdParamSubject.onNext)
.disposed(by: disposeBag)
payProperty
.do(onNext: { [weak self] _ in self?.isLoadingRelay.accept(true) })
.flatMapLatest { payload in
Remote<QrisPayAPIModel.PayResult>.QrisPayment
.pay(payloadRequest: payload)
.useErrorLog()
.asObservable()
}
.do(onNext: { [weak self] _ in self?.isLoadingRelay.accept(false) })
.bind(onNext: { [weak self] result in
guard let self = self else { return }
switch result {
case let .success(response):
self.completeTransferSubject.onNext(self.makeTransaction(receiver: response))
case let .failure(error):
self.errorSubject.onNext(.serverErrorWithPay(error: error))
}
}).disposed(by: disposeBag)
showResultProperty
.do(onNext: { [weak self] _ in self?.isLoadingRelay.accept(true) })
.withLatestFrom(qrisPending)
.flatMapLatest { payload in
Remote<QrisPayAPIModel.PayResult>.QrisPayment
.showResult(payload.0)
.useErrorLog()
.asObservable()
}
.do(onNext: { [weak self] _ in self?.isLoadingRelay.accept(false) })
.bind(onNext: { [weak self] result in
guard let self = self else { return }
switch result {
case let .success(response):
self.completeTransferSubject.onNext(self.makeTransaction(receiver: response))
case let .failure(error):
self.errorSubject.onNext(.serverError(error: error))
}
}).disposed(by: disposeBag)
}
}
// MARK: - Private
private extension QrisPayAmountViewModel {
func validation(to value: Decimal) -> QrisPayAmountResult.Validation {
guard let formModel = cellModelRelay.value else {
return .ready
}
if value < 0 {
return .invalid(message: "You should be valid amount.")
}
if let tips = try? tipsSubject.value() {
switch tips.0 {
case .percentage:
if let percentage = tips.1?.decimalValue, percentage > 0 {
tipAmountProperty.onNext(Decimal((value * percentage / 100).intValue))
}
case .amount:
tipAmountProperty.onNext(tips.1?.decimalValue ?? 0)
default:
break
}
}
if value == 0 {
return .ready
}
let tipAmount: Decimal = (try? tipAmountProperty.value()) ?? 0
// if !formModel.canWithdrawAmount(to: value + tipAmount) {
// return .overAccountBalance(message: LocalizableString.amountExceedamountErrorDesc)
// }
//
// if formModel.isUnderOneTimeAmountLimit(amount: value) {
// // formModel.representMinimumOneTimeAmount
// return .underMinimumLimit(message: LocalizableString.amountMinimumamountErrorDesc(formModel.minTransferOneTimeLimit.intValue))
// }
//
// if formModel.isOverOneTimeAmountLimit(amount: value) {
// // formModel.representMaximumOneTimeAmount
// return .overOnetimeLimit(message: LocalizableString.amountOvertxnlimitErrorDesc(formModel.maxTransferOneTimeLimit.intValue))
// }
return .ok
}
func makeCellModel(_ limit: QrisPayAPIModel.QRISPaymentDisbursalLimitResponse,
_ param: QrisPayParams,
account: AccountPresentModel) {
let cellModel = QrisPayAmountCellModel(limit, param, account: account)
if let cell = cellModelRelay.value {
cellModel.amount = cell.amount
cellModel.memo = cell.memo
}
cellModelRelay.accept(cellModel)
}
func makeRequestParam(_ payInfo: PayInfo, _ payParam: QrisPayParams?, _ currentAccount: AccountAPIModel.AccountSimpleInfo) -> QrisPay? {
guard let payParam = payParam else { return nil }
let amount = "\((payInfo.amount))"//.leftPadding(toLength: 14, withPad: "0")
let tip = payInfo.tip
let dbAcctNm = BankUserService.shared.currentUser?.userName ?? ""
return .init(acctIdentification1: nil,
acquInstId: payParam.acquInstId,
addtnlData: payParam.addtnlData,
addtnlDataNatnl: payParam.addtnlDataNatnl,
apprCode: nil,
cardAcceptId: payParam.cardAcceptId,
cardAcceptNmLoc: payParam.cardAcceptNmLoc,
cardAcceptTermId: payParam.cardAcceptTermId,
ccy: "360",
convFee: makeConvFee(tip, payParam),
crAcctNm1: nil,
crAcctNm2: payParam.crAcctNm2,
crAcctNo: payParam.merchantPan,
crAcctType: nil,
crBankCd: nil,
crCardNo: nil,
dbAcctNm: dbAcctNm, //currentAccount.acctTitle,
dbAcctNo: currentAccount.acctNo,
fwdInstId: "360002",
issuerId: "93600484 ",
merchantCity: payParam.merchantCity,
merchantName: payParam.merchantName ?? "",
merchantType: payParam.merchantType,
reserved2: nil,
reserved3: nil,
svcEntryMode: "012",
tipsAmount: "\(tip)",
trxAmt: amount,
quickCreditDisbursalYn: payInfo.loanInfo.quickCreditDisbursalYn,
loanAcctNo: payInfo.loanInfo.loanAcctNo,
loanAmt: payInfo.loanInfo.loanAmt
)
}
func makeTransaction(receiver: QrisPayAPIModel.PayResult) -> QrisPayment.Transaction {
.init(refNo: receiver.refNo ?? "",
hisNo: receiver.hisNo,
dbAcctNm: [receiver.dbAcctNm1, receiver.dbAcctNm2].compactMap { $0 }.joined(),
dbAcctNo: receiver.dbAcctNo ?? "",
crAcctNo: receiver.crAcctNo ?? "",
trxAmt: receiver.trxAmt ?? "",
tipAmount: receiver.tipsAmount ?? "",
crAcctNm1: receiver.crAcctNm1,
crAcctNm2: receiver.crAcctNm2,
merchantName: receiver.merchantName ?? "",
convFee: receiver.convFee ?? "",
date: receiver.localTrxTimeStamp.date ?? Date(),
disbursalAmount: receiver.loanAmt,
totalUsedCredit: receiver.loanBalance,
availableCredit: receiver.creditLimit,
disburseFee: receiver.disburseFee
)
}
private func makeConvFee(_ tip: Decimal, _ payParam: QrisPayParams?) -> String? {
guard let payParam = payParam else { return "" }
switch payParam.tipIndicator {
case .input:
let tipAmount: Decimal = (try? tipAmountProperty.value()) ?? 0
return "C" + "\(tipAmount)00".leftPadding(toLength: 8, withPad: "0")
case .amount:
guard let tipValue = payParam.tipValueOfFixed else { return "" }
return "D" + "\(tipValue)00".leftPadding(toLength: 8, withPad: "0")
case .percentage:
return "D" + "\(tip)00".leftPadding(toLength: 8, withPad: "0")
default:
return nil
}
}
private func makeAddtnlData(_ dbAcctNm: String, _ mc: String) -> String {
let count: Int
let title: String
if dbAcctNm.count > 30 {
count = 30
title = String(dbAcctNm[dbAcctNm.startIndex..<dbAcctNm.index(dbAcctNm.startIndex, offsetBy: 30)])
} else {
count = dbAcctNm.count
title = dbAcctNm
}
return "PI04Q001CD\(String(format: "%02d", count))\(title)MC\(String(format: "%02d", mc.count))\(mc)"
}
}
Editor is loading...
Leave a Comment