Skip to content
This repository was archived by the owner on Jan 4, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Revert "Merge pull request #687 from artsy/cardflight-update"
This reverts commit 24c721a, reversing
changes made to e98923f.
  • Loading branch information
ashfurrow committed Oct 29, 2018
commit d82521890a51b86c183cdf0111a270ab7402089d
3 changes: 0 additions & 3 deletions Kiosk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,6 @@
"${BUILT_PRODUCTS_DIR}/Artsy+UIFonts/Artsy_UIFonts.framework",
"${BUILT_PRODUCTS_DIR}/Artsy+UILabels/Artsy_UILabels.framework",
"${BUILT_PRODUCTS_DIR}/Artsy-UIButtons/Artsy_UIButtons.framework",
"${PODS_ROOT}/CardFlight-v4/CardFlight.framework",
"${BUILT_PRODUCTS_DIR}/DZNWebViewController/DZNWebViewController.framework",
"${BUILT_PRODUCTS_DIR}/ECPhoneNumberFormatter/ECPhoneNumberFormatter.framework",
"${BUILT_PRODUCTS_DIR}/FLKAutoLayout/FLKAutoLayout.framework",
Expand Down Expand Up @@ -1187,7 +1186,6 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Artsy_UIFonts.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Artsy_UILabels.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Artsy_UIButtons.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CardFlight.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DZNWebViewController.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ECPhoneNumberFormatter.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FLKAutoLayout.framework",
Expand Down Expand Up @@ -1553,7 +1551,6 @@
"$(SRCROOT)/Pods/CardFlight/**",
);
INFOPLIST_FILE = "Kiosk/Supporting Files/Info.plist";
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" -DDEBUG";
PRODUCT_BUNDLE_IDENTIFIER = net.artsy.kiosk.beta;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Kiosk/Supporting Files/BridgingHeader.h";
Expand Down
10 changes: 4 additions & 6 deletions Kiosk/Admin/AdminCardTestingViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,21 @@ class AdminCardTestingViewController: UIViewController {
return
}

let cardDetails = "Card: \(card.cardInfo.cardholderName ?? "") - \(card.cardInfo.lastFour ?? "") \n \(card.token)"
let cardDetails = "Card: \(card.name ?? "") - \(card.last4 ?? "") \n \(card.cardToken ?? "")"
self.log(cardDetails)
}
}
.disposed(by: rx.disposeBag)


cardHandler.startSearching()
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
cardHandler.end()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
cardHandler.startSearching()
}

func log(_ string: String) {
self.logTextView.text = "\(self.logTextView.text ?? "")\n\(string)"

Expand Down
198 changes: 62 additions & 136 deletions Kiosk/App/CardHandler.swift
Original file line number Diff line number Diff line change
@@ -1,181 +1,107 @@
import UIKit
import RxSwift
import CardFlight

class CardHandler: NSObject, CFTTransactionDelegate {
class CardHandler: NSObject, CFTReaderDelegate {

private let _cardStatus = PublishSubject<String>()
private let _userMessages = PublishSubject<String>()
private var cardReader: CFTCardReaderInfo?

var transaction: CFTTransaction?
fileprivate let _cardStatus = PublishSubject<String>()

var cardStatus: Observable<String> {
return _cardStatus.asObservable()
}

var userMessages: Observable<String> {
// User messages are things like "Swipe card", "processing", or "Swipe card again". Due to a problem with the
// CardFlight SDK, the user is prompted to accept processing for card tokenization, which is provides a
// unfriendly user experience (prompting to accept a transaction that we're not actually placing). So we
// auto-accept these requests and filter out confirmation messages, which don't apply to tokenization flows,
// until this issue is fixed: https://github.com/CardFlight/cardflight-v4-ios/issues/4
return _userMessages
.asObservable()
.filter { message -> Bool in
!message.hasSuffix("?")
}
}

var cardFlightCredentials: CFTCredentials {
let credentials = CFTCredentials()
credentials.setup(apiKey: self.APIKey, accountToken: self.APIToken, completion: nil)
return credentials
}

var card: (cardInfo: CFTCardInfo, token: String)?


var card: CFTCard?

let APIKey: String
let APIToken: String

var reader: CFTReader!
lazy var sessionManager = CFTSessionManager.sharedInstance()!

init(apiKey: String, accountToken: String){
APIKey = apiKey
APIToken = accountToken

super.init()

self.transaction = CFTTransaction(delegate: self)
}

deinit {
self.end()
sessionManager.setApiToken(APIKey, accountToken: APIToken, completed: nil)
}

func startSearching() {
_cardStatus.onNext("Starting search...")
let tokenizationParameters = CFTTokenizationParameters(customerId: nil, credentials: self.cardFlightCredentials)
self.transaction?.beginTokenizing(tokenizationParameters: tokenizationParameters)
}
sessionManager.setLogging(true)

func end() {
transaction?.select(processOption: CFTProcessOption.abort)
transaction = nil
reader = CFTReader(reader: CFTReaderType.UNKNOWN)
reader.delegate = self
reader.swipeHasTimeout(false)
_cardStatus.onNext("Started searching")
}

func transaction(_ transaction: CFTTransaction, didUpdate state: CFTTransactionState, error: Error?) {
switch state {
case .completed:
_cardStatus.onNext("Transaction completed")
case .processing:
_cardStatus.onNext("Transaction processing")
case .deferred:
_cardStatus.onNext("Transaction deferred")
case .pendingCardInput:
_cardStatus.onNext("Pending card input")
transaction.select(cardReaderInfo: cardReader, cardReaderModel: cardReader?.cardReaderModel ?? .unknown)
case .pendingTransactionParameters:
_cardStatus.onNext("Pending transaction parameters")
case .unknown:
_cardStatus.onNext("Unknown transactionstate")
case .pendingProcessOption:
break
}
func end() {
reader.cancelTransaction()
reader = nil
}

func transaction(_ transaction: CFTTransaction, didComplete historicalTransaction: CFTHistoricalTransaction) {
if let cardInfo = historicalTransaction.cardInfo, let token = historicalTransaction.cardToken {
self.card = (cardInfo: cardInfo, token: token)
func readerCardResponse(_ card: CFTCard?, withError error: Error?) {
if let card = card {
self.card = card;
_cardStatus.onNext("Got Card")
_cardStatus.onCompleted()
} else {
_cardStatus.onNext("Card Flight Error – could not retrieve card data.");
if let error = historicalTransaction.error {
_cardStatus.onNext("response Error \(error)");
logger.log("CardReader got a response it cannot handle")
}
startSearching()

card.tokenizeCard(success: { [weak self] in
self?._cardStatus.onCompleted()
logger.log("Card was tokenized")

}, failure: { [weak self] (error) in
self?._cardStatus.onNext("Card Flight Error: \(String(describing: error))");
logger.log("Card was not tokenizable")
})

} else if let error = error {
self._cardStatus.onNext("response Error \(error)");
logger.log("CardReader got a response it cannot handle")


reader.beginSwipe();
}
}

func transaction(_ transaction: CFTTransaction, didReceive cardReaderEvent: CFTCardReaderEvent, cardReaderInfo: CFTCardReaderInfo?) {
_cardStatus.onNext(cardReaderEvent.statusMessage)
func transactionResult(_ charge: CFTCharge!, withError error: Error!) {
logger.log("Unexcepted call to transactionResult callback: \(charge)\n\(error)")
}

func transaction(_ transaction: CFTTransaction, didUpdate cardReaderArray: [CFTCardReaderInfo]) {
self.cardReader = cardReaderArray.first
_cardStatus.onNext("Received new card reader availability, number of readers: \(cardReaderArray.count)")
// handle other delegate call backs with the status messages

func readerIsAttached() {
_cardStatus.onNext("Reader is attatched");
}

func transaction(_ transaction: CFTTransaction, didRequestProcessOption cardInfo: CFTCardInfo) {
logger.log("Received request for processing option, will process transaction.")
_cardStatus.onNext("Request for process option, automatically processing...")
// We auto-accept the process option on the user's behalf because the prompt doesn't make sense in a
// tokenization flow. See comments in `userMessages` property above.
transaction.select(processOption: .process)
func readerIsConnecting() {
_cardStatus.onNext("Reader is connecting");
}

func transaction(_ transaction: CFTTransaction, didRequestDisplay message: CFTMessage) {
let message = message.primary ?? message.secondary ?? ""
_userMessages.onNext(message)
logger.log("Received request to display message: \(message)")
_cardStatus.onNext("Received message for user: \(message)")
func readerIsDisconnected() {
_cardStatus.onNext("Reader is disconnected");
logger.log("Card Reader Disconnected")
}
}

typealias UnhandledDelegateCallbacks = CardHandler
/// We don't expect any of these functions to be called, but they are required for the delegate protocol.
extension UnhandledDelegateCallbacks {
func transaction(_ transaction: CFTTransaction, didDefer transactionData: Data) {
logger.log("Transaction has been deferred.")
_cardStatus.onNext("Transaction deferred")
func readerSwipeDidCancel() {
_cardStatus.onNext("Reader did cancel");
logger.log("Card Reader was Cancelled")
}

public func transaction(_ transaction: CFTTransaction, didRequest cvm: CFTCVM) {
if cvm == CFTCVM.signature {
logger.log("Transaction requested signature from user, which should not occur for tokenization.")
_cardStatus.onNext("Ignoring user signature request from CardFlight")
}
func readerGenericResponse(_ cardData: String!) {
_cardStatus.onNext("Reader received non-card data: \(cardData ?? "") ");
reader.beginSwipe()
}
}

extension CFTCardReaderEvent {
var statusMessage: String {
switch self {
case .unknown:
return "Unknown card event"
case .disconnected:
return "Reader is disconnected"
case .connected:
return "Reader is connected"
case .connectionErrored:
return "Connection error occurred"
case .cardSwiped:
return "Card swiped"
case .cardSwipeErrored:
return "Card swipe error"
case .cardInserted:
return "Card inserted"
case .cardInsertErrored:
return "Card insertion error"
case .cardRemoved:
return "Card removed"
case .cardTapped:
return "Card tapped"
case .cardTapErrored:
return "Card tap error"
case .updateStarted:
return "Update started"
case .updateCompleted:
return "Updated completed"
case .audioRecordingPermissionNotGranted:
return "iOS audio permissions no granted"
case .fatalError:
return "Fatal error"
case .connecting:
return "Connecting"
case .batteryStatusUpdated:
return "Battery status updated"
func readerIsConnected(_ isConnected: Bool, withError error: Error!) {
if isConnected {
_cardStatus.onNext("Reader is connected")
reader.beginSwipe()
} else {
if (error != nil) {
_cardStatus.onNext("Reader is disconnected: \(error.localizedDescription)");
} else {
_cardStatus.onNext("Reader is disconnected");
}
}
}
}
2 changes: 1 addition & 1 deletion Kiosk/App/GlobalFunctions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func responseIsOK(_ response: Response) -> Bool {

func detectDevelopmentEnvironment() -> Bool {
var developmentEnvironment = false
#if DEBUG || (arch(i386) || arch(x86_64))
#if DEBUG || (arch(i386) || arch(x86_64)) && os(iOS)
developmentEnvironment = true
#endif
return developmentEnvironment
Expand Down
30 changes: 11 additions & 19 deletions Kiosk/Bid Fulfillment/SwipeCreditCardViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ class SwipeCreditCardViewController: UIViewController, RegistrationSubController
@IBOutlet var cardStatusLabel: ARSerifLabel!
let finished = PublishSubject<Void>()

@IBOutlet weak var titleLabel: ARSerifLabel!
@IBOutlet weak var spinner: Spinner!
@IBOutlet weak var processingLabel: UILabel!
@IBOutlet weak var illustrationImageView: UIImageView!

@IBOutlet weak var titleLabel: ARSerifLabel!

class func instantiateFromStoryboard(_ storyboard: UIStoryboard) -> SwipeCreditCardViewController {
return storyboard.viewController(withID: .RegisterCreditCard) as! SwipeCreditCardViewController
Expand Down Expand Up @@ -43,27 +44,17 @@ class SwipeCreditCardViewController: UIViewController, RegistrationSubController
super.viewDidLoad()
self.setInProgress(false)

let pleaseWaitMessage = "Please wait..."

cardHandler.userMessages
.startWith(pleaseWaitMessage)
.map { message in
if message.isEmpty {
return pleaseWaitMessage
} else {
return message
}
}
.bind(to: titleLabel.rx.text)
.disposed(by: rx.disposeBag)

cardHandler.cardStatus
.takeUntil(self.viewWillDisappear)
.subscribe(onNext: { message in
self.cardStatusLabel.text = "Card Status: \(message)"
if message == "Got Card" {
self.setInProgress(true)
}

if message.hasPrefix("Card Flight Error") {
self.processingLabel.text = "ERROR PROCESSING CARD - SEE ADMIN"
}
},
onError: { error in
self.cardStatusLabel.text = "Card Status: Errored"
Expand All @@ -75,13 +66,13 @@ class SwipeCreditCardViewController: UIViewController, RegistrationSubController
self.cardStatusLabel.text = "Card Status: completed"

if let card = self.cardHandler.card {
self.cardName.value = card.cardInfo.cardholderName ?? ""
self.cardLastDigits.value = card.cardInfo.lastFour ?? ""
self.cardName.value = card.name
self.cardLastDigits.value = card.last4

self.cardToken.value = card.token
self.cardToken.value = card.cardToken

if let newUser = self.navigationController?.fulfillmentNav().bidDetails.newUser {
newUser.name.value = (newUser.name.value.isNilOrEmpty) ? card.cardInfo.cardholderName : newUser.name.value
newUser.name.value = (newUser.name.value.isNilOrEmpty) ? card.name : newUser.name.value
}
}

Expand Down Expand Up @@ -125,6 +116,7 @@ class SwipeCreditCardViewController: UIViewController, RegistrationSubController

func setInProgress(_ show: Bool) {
illustrationImageView.alpha = show ? 0.1 : 1
processingLabel.isHidden = !show
spinner.isHidden = !show
}

Expand Down
Loading