Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

World's worst HACK week code – Remote Tap to Pay on iPhone #14065

Open
wants to merge 6 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import Foundation

public enum CardReaderDiscoveryMethod {
case localMobile
case remoteMobile
case bluetoothScan
}
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/CardReaderEvent.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// The possible events from a connected reader.
public enum CardReaderEvent: Equatable {
public enum CardReaderEvent: Equatable, Codable {
/// The reader begins waiting for input.
/// The app should prompt the customer to present a payment method
case waitingForInput(CardReaderInput)
Expand Down
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/CardReaderInputOptions.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

public struct CardReaderInput: OptionSet {
public struct CardReaderInput: OptionSet, Codable {
public let rawValue: Int

public init(rawValue: Int) {
Expand Down
4 changes: 4 additions & 0 deletions Hardware/Hardware/CardReader/CardReaderType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public enum CardReaderType: String, CaseIterable {
case wisepad3
/// Tap on Mobile: Apple built in reader
case appleBuiltIn
/// Tap on Mobile: Apple built in reader over a peer to peer network
case remoteTapToPay
/// Other
case other
}
Expand All @@ -30,6 +32,8 @@ extension CardReaderType {
return "WISEPAD_3"
case .appleBuiltIn:
return "COTS_DEVICE"
case .remoteTapToPay:
return "REMOTE_COTS_DEVICE"
default:
return "UNKNOWN"
}
Expand Down
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/Charge.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Codegen

/// A struct representing a charge.
public struct Charge: Identifiable, GeneratedCopiable, GeneratedFakeable {
public struct Charge: Identifiable, GeneratedCopiable, GeneratedFakeable, Codable {
/// The unique identifier for the charge.
public let id: String

Expand Down
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/ChargeStatus.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Codegen

/// The possible statuses for a charge
public enum ChargeStatus: Equatable, GeneratedFakeable {
public enum ChargeStatus: Equatable, GeneratedFakeable, Codable {
/// The charge succeeded.
case succeeded

Expand Down
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/PaymentIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Codegen

/// A PaymentIntent tracks the process of collecting a payment from your customer.
/// We would create exactly one PaymentIntent for each order
public struct PaymentIntent: Identifiable, GeneratedCopiable, GeneratedFakeable {
public struct PaymentIntent: Identifiable, GeneratedCopiable, GeneratedFakeable, Codable {
/// Unique identifier for the PaymentIntent
public let id: String

Expand Down
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/PaymentIntentStatus.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Codegen

/// The possible statuses for a PaymentIntent.
public enum PaymentIntentStatus: Equatable, GeneratedFakeable {
public enum PaymentIntentStatus: Equatable, GeneratedFakeable, Codable {
case requiresPaymentMethod
case requiresConfirmation
case requiresCapture
Expand Down
2 changes: 1 addition & 1 deletion Hardware/Hardware/CardReader/PaymentMethod.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Codegen

/// The type of the PaymentMethod.
public enum PaymentMethod: Equatable, GeneratedFakeable {
public enum PaymentMethod: Equatable, GeneratedFakeable, Codable {
/// A card payment method.
case card

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public extension CardReader {
return .localMobile
case .chipper, .stripeM2, .wisepad3:
return .bluetoothScan
case .remoteTapToPay:
return .remoteMobile
case .other:
return nil
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import StripeTerminal
public extension CardReaderDiscoveryMethod {
func toStripe() -> DiscoveryMethod {
switch self {
case .localMobile:
case .localMobile, .remoteMobile:
return .localMobile
case .bluetoothScan:
return .bluetoothScan
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ extension CardReaderType {
return .wisePad3
case .appleBuiltIn:
return .appleBuiltIn
case .other:
case .remoteTapToPay,
.other:
return nil
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ extension StripeCardReaderService: CardReaderService {
DDLogError("\(error)")
throw error
}
case .localMobile:
case .localMobile, .remoteMobile:
let localMobileConfig = LocalMobileDiscoveryConfigurationBuilder()
do {
config = try localMobileConfig.setSimulated(shouldUseSimulatedCardReader).build()
Expand Down
1 change: 1 addition & 0 deletions Storage/Storage/Model/CardReaderType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum CardReaderType: String, Codable {
case wisepad3
/// Tap on Mobile: Apple built in reader
case appleBuiltIn
case remoteTapToPay
/// Other
case other
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Foundation
import SwiftUI
import UIKit
import WordPressAuthenticator
import Experiments
Expand Down Expand Up @@ -33,6 +34,7 @@ final class LoginPrologueViewController: UIViewController {
return .portrait
}

private var tapToPayServer: RemoteTapToPayReaderServer?

// MARK: - Overridden Methods

Expand All @@ -57,6 +59,17 @@ final class LoginPrologueViewController: UIViewController {
setupCarousel()
}

@IBAction func remoteTapToPayTapped(_ sender: Any) {
guard tapToPayServer == nil else {
return
}
let tapToPayServer = RemoteTapToPayReaderServer()
tapToPayServer.start()
self.tapToPayServer = tapToPayServer
let hostingController = UIHostingController<RemoteTapToPayServerView>(rootView: RemoteTapToPayServerView(server: tapToPayServer))
present(hostingController, animated: true)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
Expand Down Expand Up @@ -41,6 +41,15 @@
<constraint firstAttribute="height" constant="36" id="gWf-lm-a4c"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="xF9-Re-9ZA">
<rect key="frame" x="208" y="47" width="166" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Remote Tap to Pay"/>
<connections>
<action selector="remoteTapToPayTapped:" destination="-1" eventType="touchUpInside" id="k5g-eK-0td"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="3oc-0l-uZX"/>
<constraints>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ final class CardPresentPaymentInvalidatablePaymentOrchestrator: PaymentCaptureOr
}

func collectPayment(for order: Order,
using cardReader: CardReader,
orderTotal: NSDecimalNumber,
paymentGatewayAccount: PaymentGatewayAccount,
paymentMethodTypes: [String],
Expand All @@ -24,6 +25,7 @@ final class CardPresentPaymentInvalidatablePaymentOrchestrator: PaymentCaptureOr
return onCompletion(.failure(CardPresentPaymentInvalidatablePaymentOrchestratorError.paymentInvalidated))
}
paymentOrchestrator.collectPayment(for: order,
using: cardReader,
orderTotal: orderTotal,
paymentGatewayAccount: paymentGatewayAccount,
paymentMethodTypes: paymentMethodTypes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ extension CardReaderConnectionMethod {
return .bluetoothScan
case .tapToPay:
return .localMobile
case .remoteTapToPay:
return .remoteMobile
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ final class CardPresentPaymentService: CardPresentPaymentFacade {
paymentEventSubject.send(.show(eventDetails: .connectionSuccess(done: { [weak self] in
self?.paymentEventSubject.send(.idle)
})))
if connectionMethod == .remoteTapToPay {
readerConnectionStatusSubject.send(.connected(connectedReader))
}
return .connected(connectedReader)
case .canceled:
readerConnectionStatusSubject.send(.disconnected)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import struct Yosemite.CardPresentPaymentsConfiguration
final class CardPresentPaymentsConnectionControllerManager {
let externalReaderConnectionController: CardReaderConnectionController<CardPresentPaymentBluetoothReaderConnectionAlertsProvider,
CardPresentPaymentsAlertPresenterAdaptor>
let tapToPayConnectionController: BuiltInCardReaderConnectionController<CardPresentPaymentBuiltInReaderConnectionAlertsProvider,
let tapToPayConnectionController: RemoteBuiltInCardReaderConnectionController<CardPresentPaymentBuiltInReaderConnectionAlertsProvider,
CardPresentPaymentsAlertPresenterAdaptor>
let analyticsTracker: CardReaderConnectionAnalyticsTracker

Expand All @@ -27,11 +27,10 @@ final class CardPresentPaymentsConnectionControllerManager {
alertsProvider: CardPresentPaymentBluetoothReaderConnectionAlertsProvider(),
configuration: configuration,
analyticsTracker: analyticsTracker)
self.tapToPayConnectionController = BuiltInCardReaderConnectionController(
self.tapToPayConnectionController = RemoteBuiltInCardReaderConnectionController(
forSiteID: siteID,
alertsPresenter: alertsPresenter,
alertsProvider: CardPresentPaymentBuiltInReaderConnectionAlertsProvider(),
configuration: configuration,
analyticsTracker: analyticsTracker)
configuration: configuration)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ import Foundation
enum CardReaderConnectionMethod {
case bluetooth
case tapToPay
case remoteTapToPay
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,17 @@ struct CardReaderConnectionStatusView: View {
case .cancellingConnection:
progressIndicatingCardReaderStatus(title: Localization.pleaseWait)
case .disconnected:
Button {
connectionViewModel.connectReader()
Menu {
Button {
connectionViewModel.connectReader()
} label: {
Text("Bluetooth reader")
}
Button {
connectionViewModel.connectRemoteReader()
} label: {
Text("Tap to Pay on iPhone (remote)")
}
} label: {
HStack(spacing: Constants.buttonImageAndTextSpacing) {
circleIcon(with: Color(.wooCommerceAmber(.shade60)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ final class CardReaderConnectionViewModel: ObservableObject {
}
}

func connectRemoteReader() {
guard connectionStatus == .disconnected else {
return
}
Task { @MainActor in
do {
let _ = try await cardPresentPayment.connectReader(using: .remoteTapToPay)
} catch {
DDLogError("🔴 POS tap to pay connection error: \(error)")
}
}
}

func disconnectReader() {
guard case .connected = connectionStatus else {
return
Expand Down
4 changes: 2 additions & 2 deletions WooCommerce/Classes/POS/ViewModels/TotalsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ final class TotalsViewModel: ObservableObject, TotalsViewModelProtocol {
func connectReaderTapped() {
Task { @MainActor in
do {
let _ = try await cardPresentPaymentService.connectReader(using: .bluetooth)
let _ = try await cardPresentPaymentService.connectReader(using: .remoteTapToPay)
} catch {
DDLogError("🔴 POS reader connection error: \(error)")
}
Expand Down Expand Up @@ -236,7 +236,7 @@ private extension TotalsViewModel {

@MainActor
func collectPayment(for order: Order) async throws {
_ = try await cardPresentPaymentService.collectPayment(for: order, using: .bluetooth)
_ = try await cardPresentPaymentService.collectPayment(for: order, using: .remoteTapToPay)
}
}

Expand Down
6 changes: 6 additions & 0 deletions WooCommerce/Classes/ServiceLocator/ServiceLocator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ final class ServiceLocator {

private static var _cardReaderConfigProvider: CommonReaderConfigProviding = CommonReaderConfigProvider()

private static var _remoteCardReaderClient: RemoteTapToPayReaderClient = RemoteTapToPayReaderClient()

/// Support for printing receipts
///
private static var _receiptPrinter: PrinterService = AirPrintReceiptPrinterService()
Expand Down Expand Up @@ -228,6 +230,10 @@ final class ServiceLocator {
_cardReaderConfigProvider
}

static var remoteCardReaderClient: RemoteTapToPayReaderClient {
_remoteCardReaderClient
}

/// Provides the access point to the ReceiptPrinterService.
/// - Returns: An implementation of the ReceiptPrinterService protocol.
static var receiptPrinterService: PrinterService {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private extension CardReaderDiscoveryMethod {
return NSLocalizedString(
"Bluetooth Reader",
comment: "The button title on the reader type alert, for the user to choose a bluetooth reader.")
case .localMobile:
case .localMobile, .remoteMobile:
return NSLocalizedString(
"Tap to Pay on iPhone",
comment: "The button title on the reader type alert, for the user to choose Tap to Pay on iPhone.")
Expand Down
Loading