Skip to content

Commit 4821ba5

Browse files
authored
Stripe Terminal SDK update 4.2.0 (#15377)
2 parents e60a418 + 0198905 commit 4821ba5

File tree

19 files changed

+115
-95
lines changed

19 files changed

+115
-95
lines changed

Hardware/Hardware.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
019A77CA2D88645100ABBB71 /* PaymentMethodType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 019A77C92D88645100ABBB71 /* PaymentMethodType.swift */; };
1011
028C39E028255CFE0007BA25 /* Models+Copiable.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 028C39DF28255CFE0007BA25 /* Models+Copiable.generated.swift */; };
1112
02B5147A28254ED300750B71 /* Codegen in Frameworks */ = {isa = PBXBuildFile; productRef = 02B5147928254ED300750B71 /* Codegen */; };
1213
02FDAB102CEEF11F00B6C8AA /* PaymentChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02FDAB0F2CEEF11D00B6C8AA /* PaymentChannel.swift */; };
@@ -159,6 +160,7 @@
159160
/* End PBXCopyFilesBuildPhase section */
160161

161162
/* Begin PBXFileReference section */
163+
019A77C92D88645100ABBB71 /* PaymentMethodType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentMethodType.swift; sourceTree = "<group>"; };
162164
02351FF56149ADCD11338B19 /* Pods-SampleReceiptPrinter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SampleReceiptPrinter.release.xcconfig"; path = "Target Support Files/Pods-SampleReceiptPrinter/Pods-SampleReceiptPrinter.release.xcconfig"; sourceTree = "<group>"; };
163165
028C39DF28255CFE0007BA25 /* Models+Copiable.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Models+Copiable.generated.swift"; sourceTree = "<group>"; };
164166
02FDAB0F2CEEF11D00B6C8AA /* PaymentChannel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentChannel.swift; sourceTree = "<group>"; };
@@ -476,6 +478,7 @@
476478
02FDAB0F2CEEF11D00B6C8AA /* PaymentChannel.swift */,
477479
D89B8F0125DDC7500001C726 /* PaymentIntentStatus.swift */,
478480
D88FDB2325DD21B000CB0DBD /* PaymentIntentParameters.swift */,
481+
019A77C92D88645100ABBB71 /* PaymentMethodType.swift */,
479482
03CF78D627DF9BE500523706 /* RefundParameters.swift */,
480483
D88FDB2025DD21AF00CB0DBD /* PaymentStatus.swift */,
481484
D845BDBB262D980C00A3E40F /* CardPresentTransactionDetails.swift */,
@@ -843,6 +846,7 @@
843846
isa = PBXSourcesBuildPhase;
844847
buildActionMask = 2147483647;
845848
files = (
849+
019A77CA2D88645100ABBB71 /* PaymentMethodType.swift in Sources */,
846850
B9C4AB2327FDE133007008B8 /* Email.swift in Sources */,
847851
D845BE59262ED84000A3E40F /* AirPrintReceiptPrinterService.swift in Sources */,
848852
317975C2274EBC1F004357B1 /* DeclineReason+Stripe.swift in Sources */,

Hardware/Hardware/CardReader/PaymentIntentParameters.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public struct PaymentIntentParameters {
4545
///
4646
/// Can be `card_present`, `interac_present`.
4747
///
48-
public let paymentMethodTypes: [String]
48+
public let paymentMethodTypes: [PaymentMethodType]
4949

5050
public init(amount: Decimal,
5151
currency: String,
@@ -54,7 +54,7 @@ public struct PaymentIntentParameters {
5454
receiptDescription: String? = nil,
5555
statementDescription: String? = nil,
5656
receiptEmail: String? = nil,
57-
paymentMethodTypes: [String] = [],
57+
paymentMethodTypes: [PaymentMethodType] = [],
5858
metadata: [String: String]? = nil) {
5959
self.amount = amount
6060
self.currency = currency
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/// PaymentMethodType defines supported hardware payment types.
2+
/// Mirrors supported types from StripeTerminal.PaymentMethodType https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPPaymentMethodType.html
3+
///
4+
public enum PaymentMethodType {
5+
case cardPresent
6+
case interacPresent
7+
}

Hardware/Hardware/CardReader/StripeCardReader/CardReaderDiscoveryMethod+Stripe.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ public extension CardReaderDiscoveryMethod {
55
func toStripe() -> DiscoveryMethod {
66
switch self {
77
case .localMobile:
8-
return .localMobile
8+
return .tapToPay
99
case .bluetoothScan:
1010
return .bluetoothScan
1111
}

Hardware/Hardware/CardReader/StripeCardReader/CardReaderType+Stripe.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extension CardReaderType {
1313
return .stripeM2
1414
case .wisePad3:
1515
return .wisepad3
16-
case .appleBuiltIn:
16+
case .tapToPay:
1717
return appleBuiltIn
1818
default:
1919
return .other
@@ -29,7 +29,7 @@ extension CardReaderType {
2929
case .wisepad3:
3030
return .wisePad3
3131
case .appleBuiltIn:
32-
return .appleBuiltIn
32+
return .tapToPay
3333
case .other:
3434
return nil
3535
}

Hardware/Hardware/CardReader/StripeCardReader/PaymentIntentParameters+Stripe.swift

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,32 +34,32 @@ private extension Hardware.PaymentIntentParameters {
3434

3535
func build(_ builder: PaymentIntentParametersBuilder,
3636
with cardReaderMetadata: CardReaderMetadata? = nil) throws -> StripeTerminal.PaymentIntentParameters? {
37-
builder.setPaymentMethodTypes(paymentMethodTypes)
38-
builder.setStripeDescription(receiptDescription)
37+
_ = builder.setPaymentMethodTypes(paymentMethodTypes.map(\.stripeTerminalPaymentMethodType))
38+
builder.setStripeDescription(receiptDescription)
3939

40-
/// Stripe allows the credit card statement descriptor to be nil, but not an empty string
41-
/// https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPPaymentIntentParameters.html#/c:objc(cs)SCPPaymentIntentParameters(py)statementDescriptor
42-
builder.setStatementDescriptor(nil)
40+
/// Stripe allows the credit card statement descriptor to be nil, but not an empty string
41+
/// https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPPaymentIntentParameters.html#/c:objc(cs)SCPPaymentIntentParameters(py)statementDescriptor
42+
builder.setStatementDescriptor(nil)
4343

44-
let descriptor = statementDescription ?? ""
45-
if !descriptor.isEmpty {
46-
builder.setStatementDescriptor(descriptor)
47-
}
44+
let descriptor = statementDescription ?? ""
45+
if !descriptor.isEmpty {
46+
builder.setStatementDescriptor(descriptor)
47+
}
4848

49-
if let applicationFee = applicationFee {
50-
/// Stripe requires that "The amount must be provided as a boxed UInt in the currency's smallest unit."
51-
/// Smallest-unit and UInt conversion is done in the same way as for the total amount, but that does not need to be boxed.
52-
let applicationFeeForStripe = NSNumber(value: prepareAmountForStripe(applicationFee))
53-
builder.setApplicationFeeAmount(applicationFeeForStripe)
54-
}
49+
if let applicationFee = applicationFee {
50+
/// Stripe requires that "The amount must be provided as a boxed UInt in the currency's smallest unit."
51+
/// Smallest-unit and UInt conversion is done in the same way as for the total amount, but that does not need to be boxed.
52+
let applicationFeeForStripe = NSNumber(value: prepareAmountForStripe(applicationFee))
53+
builder.setApplicationFeeAmount(applicationFeeForStripe)
54+
}
5555

56-
builder.setReceiptEmail(receiptEmail)
56+
builder.setReceiptEmail(receiptEmail)
5757

58-
let updatedMetadata = prepareMetadataForStripe(with: cardReaderMetadata)
59-
builder.setMetadata(updatedMetadata)
58+
let updatedMetadata = prepareMetadataForStripe(with: cardReaderMetadata)
59+
builder.setMetadata(updatedMetadata)
6060

61-
// Return payment intent built configuration:
62-
return try builder.build()
61+
// Return payment intent built configuration:
62+
return try builder.build()
6363
}
6464

6565
/// Updates the existing PaymentIntentParameters metadata with our CardReader metadata, if any.
@@ -80,4 +80,16 @@ private extension Hardware.PaymentIntentParameters {
8080
return updatedMetadata
8181
}
8282
}
83+
84+
private extension PaymentMethodType {
85+
var stripeTerminalPaymentMethodType: StripeTerminal.PaymentMethodType {
86+
switch self {
87+
case .cardPresent:
88+
return .cardPresent
89+
case .interacPresent:
90+
return .interacPresent
91+
}
92+
}
93+
}
94+
8395
#endif

Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ extension StripeCardReaderService: CardReaderService {
142142
throw error
143143
}
144144
case .localMobile:
145-
let localMobileConfig = LocalMobileDiscoveryConfigurationBuilder()
145+
let tapToPayConfig = TapToPayDiscoveryConfigurationBuilder()
146146
do {
147-
config = try localMobileConfig.setSimulated(shouldUseSimulatedCardReader).build()
147+
config = try tapToPayConfig.setSimulated(shouldUseSimulatedCardReader).build()
148148
} catch let error as UnderlyingError {
149-
DDLogError("Failed to start LocalMobileDiscovery. Error:\(String(describing: error.failureReason))")
149+
DDLogError("Failed to start TapToPayDiscovery. Error:\(String(describing: error.failureReason))")
150150
throw error
151151
} catch {
152152
DDLogError("\(error)")
@@ -159,8 +159,6 @@ extension StripeCardReaderService: CardReaderService {
159159
throw CardReaderServiceError.bluetoothDenied
160160
}
161161

162-
Terminal.shared.delegate = self
163-
164162
// We're now ready to start discovery, but first we'll check that we're not starting or canceling
165163
// another discovery process.
166164
// If we can't grab a lock quickly, let's fail rather than wait indefinitely
@@ -198,7 +196,7 @@ extension StripeCardReaderService: CardReaderService {
198196
// as the simulator won't have Bluetooth available.
199197
// If we're using Tap to Pay on iPhone, bluetooth is not required.
200198
private func shouldSkipBluetoothCheck(discoveryConfiguration: DiscoveryConfiguration) -> Bool {
201-
shouldUseSimulatedCardReader || discoveryConfiguration.discoveryMethod == .localMobile
199+
shouldUseSimulatedCardReader || discoveryConfiguration.discoveryMethod == .tapToPay
202200
}
203201

204202
public func cancelDiscovery() -> Future <Void, Error> {
@@ -273,7 +271,7 @@ extension StripeCardReaderService: CardReaderService {
273271
/// https://stripe.dev/stripe-terminal-ios/docs/Classes/SCPTerminal.html#/c:objc(cs)SCPTerminal(im)disconnectReader:
274272
/// The completion block for disconnect, apparently, is called when the SDK has not really transitioned to an idle state.
275273
/// Clients might need to dispatch operations that rely on this completion block to start a second operation on the card reader.
276-
/// (for example, starting a `localMobile` connection after a BlueTooth reader has been disconnected)
274+
/// (for example, starting a `tapToPay` connection after a BlueTooth reader has been disconnected)
277275
Terminal.shared.disconnectReader { error in
278276
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
279277
if let error = error {
@@ -453,7 +451,7 @@ extension StripeCardReaderService: CardReaderService {
453451

454452
connectionAttemptInvalidated = false
455453
switch stripeReader.deviceType {
456-
case .appleBuiltIn:
454+
case .tapToPay:
457455
return getLocalMobileConfiguration(stripeReader, options: options).flatMap { configuration in
458456
self.connect(stripeReader, configuration: configuration)
459457
}
@@ -480,7 +478,7 @@ extension StripeCardReaderService: CardReaderService {
480478
self.readerLocationProvider?.fetchDefaultLocationID { result in
481479
switch result {
482480
case .success(let locationId):
483-
let buildConfig = BluetoothConnectionConfigurationBuilder(locationId: locationId)
481+
let buildConfig = BluetoothConnectionConfigurationBuilder(delegate: self, locationId: locationId)
484482
do {
485483
let config = try buildConfig.build()
486484
return promise(.success(config))
@@ -497,7 +495,7 @@ extension StripeCardReaderService: CardReaderService {
497495
}
498496

499497
private func getLocalMobileConfiguration(_ reader: StripeTerminal.Reader,
500-
options: CardReaderConnectionOptions?) -> Future<LocalMobileConnectionConfiguration, Error> {
498+
options: CardReaderConnectionOptions?) -> Future<TapToPayConnectionConfiguration, Error> {
501499
return Future() { [weak self] promise in
502500
guard let self = self else {
503501
promise(.failure(CardReaderServiceError.connection()))
@@ -509,7 +507,7 @@ extension StripeCardReaderService: CardReaderService {
509507
self.readerLocationProvider?.fetchDefaultLocationID { result in
510508
switch result {
511509
case .success(let locationId):
512-
let localMobileConfig = LocalMobileConnectionConfigurationBuilder(locationId: locationId)
510+
let localMobileConfig = TapToPayConnectionConfigurationBuilder(delegate: self, locationId: locationId)
513511
localMobileConfig.setMerchantDisplayName(nil)
514512
localMobileConfig.setOnBehalfOf(nil)
515513
localMobileConfig.setTosAcceptancePermitted(options?.builtInOptions?.termsOfServiceAcceptancePermitted ?? true)
@@ -540,7 +538,7 @@ extension StripeCardReaderService: CardReaderService {
540538
return
541539
}
542540

543-
Terminal.shared.connectBluetoothReader(reader, delegate: self, connectionConfig: configuration) { [weak self] (reader, error) in
541+
Terminal.shared.connectReader(reader, connectionConfig: configuration) { [weak self] (reader, error) in
544542
guard let self = self else {
545543
promise(.failure(CardReaderServiceError.connection()))
546544
return
@@ -573,14 +571,14 @@ extension StripeCardReaderService: CardReaderService {
573571
}
574572
}
575573

576-
public func connect(_ reader: StripeTerminal.Reader, configuration: LocalMobileConnectionConfiguration) -> Future <CardReader, Error> {
574+
public func connect(_ reader: StripeTerminal.Reader, configuration: TapToPayConnectionConfiguration) -> Future <CardReader, Error> {
577575
return Future { [weak self] promise in
578576
guard let self = self else {
579577
promise(.failure(CardReaderServiceError.connection()))
580578
return
581579
}
582580

583-
Terminal.shared.connectLocalMobileReader(reader, delegate: self, connectionConfig: configuration) { [weak self] (reader, error) in
581+
Terminal.shared.connectReader(reader, connectionConfig: configuration) { [weak self] (reader, error) in
584582
guard let self = self else {
585583
promise(.failure(CardReaderServiceError.connection()))
586584
return
@@ -873,7 +871,7 @@ extension StripeCardReaderService: DiscoveryDelegate {
873871

874872

875873
// MARK: - ReaderDisplayDelegate.
876-
extension StripeCardReaderService: BluetoothReaderDelegate {
874+
extension StripeCardReaderService: MobileReaderDelegate {
877875
public func reader(_ reader: Reader, didReportAvailableUpdate update: ReaderSoftwareUpdate) {
878876
softwareUpdateSubject.send(.available)
879877
}
@@ -960,29 +958,33 @@ extension StripeCardReaderService: BluetoothReaderDelegate {
960958

961959
connectedReadersSubject.send([connectedReaderWithUpdatedBatteryLevel])
962960
}
961+
962+
public func reader(_ reader: Reader, didDisconnect reason: DisconnectReason) {
963+
connectedReadersSubject.send([])
964+
}
963965
}
964966

965-
extension StripeCardReaderService: LocalMobileReaderDelegate {
966-
public func localMobileReader(_ reader: Reader, didRequestReaderInput inputOptions: ReaderInputOptions = []) {
967+
extension StripeCardReaderService: TapToPayReaderDelegate {
968+
public func tapToPayReader(_ reader: Reader, didRequestReaderInput inputOptions: ReaderInputOptions = []) {
967969
sendReaderEvent(CardReaderEvent.make(stripeReaderInputOptions: inputOptions))
968970
}
969971

970-
public func localMobileReader(_ reader: Reader, didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage) {
972+
public func tapToPayReader(_ reader: Reader, didRequestReaderDisplayMessage displayMessage: ReaderDisplayMessage) {
971973
sendReaderEvent(CardReaderEvent.make(displayMessage: displayMessage))
972974
}
973975

974976

975977
// TODO: use a specific `deviceSetup` in these three functions instead of reusing the softwareUpdateSubject
976978
// https://github.com/woocommerce/woocommerce-ios/issues/8088
977-
public func localMobileReader(_ reader: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) {
979+
public func tapToPayReader(_ reader: Reader, didStartInstallingUpdate update: ReaderSoftwareUpdate, cancelable: Cancelable?) {
978980
softwareUpdateSubject.send(.started(cancelable: cancelable.map(StripeCancelable.init(cancelable:))))
979981
}
980982

981-
public func localMobileReader(_ reader: Reader, didReportReaderSoftwareUpdateProgress progress: Float) {
983+
public func tapToPayReader(_ reader: Reader, didReportReaderSoftwareUpdateProgress progress: Float) {
982984
softwareUpdateSubject.send(.installing(progress: progress))
983985
}
984986

985-
public func localMobileReader(_ reader: Reader, didFinishInstallingUpdate update: ReaderSoftwareUpdate?, error: Error?) {
987+
public func tapToPayReader(_ reader: Reader, didFinishInstallingUpdate update: ReaderSoftwareUpdate?, error: Error?) {
986988
if let error = error {
987989
let underlyingError = Self.logAndDecodeError(error)
988990
softwareUpdateSubject.send(.failed(
@@ -1002,18 +1004,11 @@ extension StripeCardReaderService: LocalMobileReaderDelegate {
10021004
}
10031005
}
10041006

1005-
public func localMobileReaderDidAcceptTermsOfService(_ reader: Reader) {
1007+
public func tapToPayReaderDidAcceptTermsOfService(_ reader: Reader) {
10061008
builtInCardReaderAcceptToSSubject.send(())
10071009
}
10081010
}
10091011

1010-
// MARK: - Terminal delegate
1011-
extension StripeCardReaderService: TerminalDelegate {
1012-
public func terminal(_ terminal: Terminal, didReportUnexpectedReaderDisconnect reader: Reader) {
1013-
connectedReadersSubject.send([])
1014-
}
1015-
}
1016-
10171012
// MARK: - Reader events
10181013
private extension StripeCardReaderService {
10191014
func sendReaderEvent(_ event: CardReaderEvent) {

Hardware/Hardware/CardReader/StripeCardReader/UnderlyingError+Stripe.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,23 +92,23 @@ extension UnderlyingError {
9292
self = .processorAPIError
9393
case .passcodeNotEnabled:
9494
self = .passcodeNotEnabled
95-
case .appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn:
95+
case .tapToPayReaderTOSAcceptanceRequiresiCloudSignIn:
9696
self = .appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn
9797
case .nfcDisabled:
9898
self = .nfcDisabled
99-
case .appleBuiltInReaderFailedToPrepare:
99+
case .tapToPayReaderFailedToPrepare:
100100
self = .appleBuiltInReaderFailedToPrepare
101-
case .appleBuiltInReaderTOSAcceptanceCanceled:
101+
case .tapToPayReaderTOSAcceptanceCanceled:
102102
self = .appleBuiltInReaderTOSAcceptanceCanceled
103-
case .appleBuiltInReaderTOSNotYetAccepted:
103+
case .tapToPayReaderTOSNotYetAccepted:
104104
self = .appleBuiltInReaderTOSNotYetAccepted
105-
case .appleBuiltInReaderTOSAcceptanceFailed:
105+
case .tapToPayReaderTOSAcceptanceFailed:
106106
self = .appleBuiltInReaderTOSAcceptanceFailed
107-
case .appleBuiltInReaderMerchantBlocked:
107+
case .tapToPayReaderMerchantBlocked:
108108
self = .appleBuiltInReaderMerchantBlocked
109-
case .appleBuiltInReaderInvalidMerchant:
109+
case .tapToPayReaderInvalidMerchant:
110110
self = .appleBuiltInReaderInvalidMerchant
111-
case .appleBuiltInReaderDeviceBanned:
111+
case .tapToPayReaderDeviceBanned:
112112
self = .appleBuiltInReaderDeviceBanned
113113
case .unsupportedMobileDeviceConfiguration:
114114
self = .unsupportedMobileDeviceConfiguration
@@ -170,7 +170,7 @@ extension UnderlyingError {
170170
self = .internetConnectTimeOut
171171
case .bluetoothReconnectStarted:
172172
self = .bluetoothReconnectStarted
173-
case .appleBuiltInReaderAccountDeactivated:
173+
case .tapToPayReaderAccountDeactivated:
174174
self = .appleBuiltInReaderAccountDeactivated
175175
case .readerMissingEncryptionKeys:
176176
self = .readerMissingEncryptionKeys

0 commit comments

Comments
 (0)