Skip to content

Commit 90382fa

Browse files
committed
Merge branch 'trunk' into issue/8146-introduce-quarter-ranges
2 parents a0db47a + cc9ae29 commit 90382fa

File tree

10 files changed

+500
-16
lines changed

10 files changed

+500
-16
lines changed

Hardware/Hardware/CardReader/StripeCardReader/StripeCardReaderService.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ extension StripeCardReaderService: CardReaderService {
174174

175175
self?.internalError(error)
176176
discoveryLock.unlock()
177-
promise(.failure(error))
177+
let underlyingError = UnderlyingError(with: error)
178+
promise(.failure(CardReaderServiceError.discovery(underlyingError: underlyingError)))
178179
}
179180
}
180181
}
@@ -838,7 +839,10 @@ private extension StripeCardReaderService {
838839

839840
func resetDiscoveredReadersSubject(error: Error? = nil) {
840841
if let error = error {
841-
discoveredReadersSubject.send(completion: .failure(error))
842+
let underlyingError = UnderlyingError(with: error)
843+
discoveredReadersSubject.send(completion:
844+
.failure(CardReaderServiceError.discovery(underlyingError: underlyingError))
845+
)
842846
}
843847
discoveredReadersSubject.send(completion: .finished)
844848
discoveredReadersSubject = CurrentValueSubject<[CardReader], Error>([])

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,36 @@ extension UnderlyingError {
8989
self = .readerSessionExpired
9090
case ErrorCode.Code.stripeAPIError.rawValue:
9191
self = .processorAPIError
92+
case ErrorCode.Code.passcodeNotEnabled.rawValue:
93+
self = .passcodeNotEnabled
94+
case ErrorCode.Code.appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn.rawValue:
95+
self = .appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn
96+
case ErrorCode.Code.nfcDisabled.rawValue:
97+
self = .nfcDisabled
98+
case ErrorCode.Code.appleBuiltInReaderFailedToPrepare.rawValue:
99+
self = .appleBuiltInReaderFailedToPrepare
100+
case ErrorCode.Code.appleBuiltInReaderTOSAcceptanceCanceled.rawValue:
101+
self = .appleBuiltInReaderTOSAcceptanceCanceled
102+
case ErrorCode.Code.appleBuiltInReaderTOSNotYetAccepted.rawValue:
103+
self = .appleBuiltInReaderTOSNotYetAccepted
104+
case ErrorCode.Code.appleBuiltInReaderTOSAcceptanceFailed.rawValue:
105+
self = .appleBuiltInReaderTOSAcceptanceFailed
106+
case ErrorCode.Code.appleBuiltInReaderMerchantBlocked.rawValue:
107+
self = .appleBuiltInReaderMerchantBlocked
108+
case ErrorCode.Code.appleBuiltInReaderInvalidMerchant.rawValue:
109+
self = .appleBuiltInReaderInvalidMerchant
110+
case ErrorCode.Code.appleBuiltInReaderDeviceBanned.rawValue:
111+
self = .appleBuiltInReaderDeviceBanned
112+
case ErrorCode.Code.unsupportedMobileDeviceConfiguration.rawValue:
113+
self = .unsupportedMobileDeviceConfiguration
114+
case ErrorCode.Code.readerNotAccessibleInBackground.rawValue:
115+
self = .readerNotAccessibleInBackground
116+
case ErrorCode.Code.commandNotAllowedDuringCall.rawValue:
117+
self = .commandNotAllowedDuringCall
118+
case ErrorCode.Code.invalidAmount.rawValue:
119+
self = .invalidAmount
120+
case ErrorCode.Code.invalidCurrency.rawValue:
121+
self = .invalidCurrency
92122
default:
93123
return nil
94124
}

Hardware/Hardware/CardReader/UnderlyingError.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,72 @@ public enum UnderlyingError: Error, Equatable {
140140

141141
/// There was no refund in progress to cancel
142142
case noRefundInProgress
143+
144+
// MARK: - Built-in reader related errors
145+
146+
/// The device must have a passcode in order to use the built-in reader
147+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorPasscodeNotEnabled
148+
case passcodeNotEnabled
149+
150+
/// The phone must have a signed-in iCloud account in order to accept the TOS for the built in reader.
151+
/// The signed-in account does not need to be the one used to connect the reader.
152+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn
153+
case appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn
154+
155+
/// NFC is disabled on the device. This could be a permissions issue, in particular due to a device management profile.
156+
/// It's unlikely that the user can directly correct this issue
157+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorNFCDisabled
158+
case nfcDisabled
159+
160+
/// Preparing the built-in reader failed. This is a retriable error
161+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderFailedToPrepare
162+
case appleBuiltInReaderFailedToPrepare
163+
164+
/// The user cancelled the built-in reader Terms of Service acceptance
165+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderTOSAcceptanceCanceled
166+
case appleBuiltInReaderTOSAcceptanceCanceled
167+
168+
/// The built-in reader Terms of Service have not been accepted. This error is retriable
169+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderTOSNotYetAccepted
170+
case appleBuiltInReaderTOSNotYetAccepted
171+
172+
/// The built-in reader Terms of Service could not be accepted. This may indicate an issue with the Apple ID used.
173+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderTOSAcceptanceFailed
174+
case appleBuiltInReaderTOSAcceptanceFailed
175+
176+
/// This (Stripe) merchant account cannot be used with the built-in reader as it has been blocked
177+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderMerchantBlocked
178+
case appleBuiltInReaderMerchantBlocked
179+
180+
/// The merchant account is invalid and cannot be used with the built-in reader
181+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderInvalidMerchant
182+
case appleBuiltInReaderInvalidMerchant
183+
184+
/// The built-in reader on this device cannot be used because it has been banned
185+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorAppleBuiltInReaderDeviceBanned
186+
case appleBuiltInReaderDeviceBanned
187+
188+
/// The device does not meet the minimum requirements for using the built-in reader
189+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorUnsupportedMobileDeviceConfiguration
190+
case unsupportedMobileDeviceConfiguration
191+
192+
/// The built-in reader cannot be used while the app is in the background
193+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorReaderNotAccessibleInBackground
194+
case readerNotAccessibleInBackground
195+
196+
/// The built-in reader cannot be used during a phone call
197+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorCommandNotAllowedDuringCall
198+
case commandNotAllowedDuringCall
199+
200+
/// The amount charged was not supported by the reader.
201+
/// (This may be a different amount than the minimum for a payment with Stripe. There is a maximum too.)
202+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorInvalidAmount
203+
case invalidAmount
204+
205+
/// The currency used was not supported by the reader.
206+
/// The reader may support a different set of currencies than WCPay or Stripe.
207+
/// https://stripe.dev/stripe-terminal-ios/docs/Enums/SCPError.html#/c:@E@SCPError@SCPErrorInvalidCurrency
208+
case invalidCurrency
143209
}
144210

145211
extension UnderlyingError {
@@ -328,6 +394,61 @@ extension UnderlyingError: LocalizedError {
328394
return NSLocalizedString("Sorry, this refund could not be canceled",
329395
comment: "Error message shown when a refund could not be canceled (likely because " +
330396
"it had already completed)")
397+
398+
// MARK: - Built-in reader errors
399+
case .passcodeNotEnabled:
400+
return NSLocalizedString("Your device needs a lock screen passcode set to use the built-in card reader",
401+
comment: "Error message shown when the built-in reader cannot be used because " +
402+
"the device does not have a passcode set.")
403+
case .appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn:
404+
return NSLocalizedString("Please sign in to iCloud on this device, so you can use the built-in card reader",
405+
comment: "Error message shown when the built-in reader cannot be used because " +
406+
"the device is not signed in to iCloud.")
407+
case .nfcDisabled:
408+
return NSLocalizedString("The app could not enable the card reader, because the NFC chip is disabled. " +
409+
"Please contact support for more details.",
410+
comment: "Error message shown when the built-in reader cannot be used because " +
411+
"the device's NFC chipset has been disabled by a device management policy.")
412+
case .appleBuiltInReaderFailedToPrepare, .readerNotAccessibleInBackground:
413+
return NSLocalizedString("There was an issue preparing the built in reader for payment – please try again.",
414+
comment: "Error message shown when the built-in reader cannot be used because " +
415+
"there was some issue with the connection. Retryable.")
416+
case .appleBuiltInReaderTOSAcceptanceCanceled, .appleBuiltInReaderTOSNotYetAccepted:
417+
return NSLocalizedString("Please try again, and accept Apple's Terms of Service, so you can use the " +
418+
"built-in card reader",
419+
comment: "Error message shown when the built-in reader cannot be used because " +
420+
"the merchant cancelled or did not complete the Terms of Service acceptance flow")
421+
case .appleBuiltInReaderTOSAcceptanceFailed:
422+
return NSLocalizedString("Please check your Apple ID is valid, and then try again. A valid Apple ID is " +
423+
"required to accept Apple's Terms of Service",
424+
comment: "Error message shown when the built-in reader cannot be used because " +
425+
"the Terms of Service acceptance flow failed, possibly due to issues with " +
426+
"the Apple ID")
427+
case .appleBuiltInReaderMerchantBlocked, .appleBuiltInReaderInvalidMerchant, .appleBuiltInReaderDeviceBanned:
428+
return NSLocalizedString("Please contact support – there was an issue connecting to the built-in reader",
429+
comment: "Error message shown when the built-in reader cannot be used because " +
430+
"there is an issue with the merchant account or device")
431+
case .unsupportedMobileDeviceConfiguration:
432+
return NSLocalizedString("Please check that your phone meets these requirements: " +
433+
"iPhone XS or newer running iOS 16.0 or above. Contact support if this error " +
434+
"shows on a supported device.",
435+
comment: "Error message shown when the built-in reader cannot be used because " +
436+
"the device does not meet minimum requirements.")
437+
case .commandNotAllowedDuringCall:
438+
return NSLocalizedString("The built-in reader cannot be used during a phone call. Please try again after " +
439+
"you finish your call",
440+
comment: "Error message shown when the built-in reader cannot be used because " +
441+
"there is a call in progress")
442+
case .invalidAmount:
443+
return NSLocalizedString("The amount is not supported by the built in reader – please try a hardware " +
444+
"reader or another payment method.",
445+
comment: "Error message shown when the built-in reader cannot be used because " +
446+
"the amount for payment is not supported by the built in reader.")
447+
case .invalidCurrency:
448+
return NSLocalizedString("The currency is not supported by the built in reader – please try a hardware " +
449+
"reader or another payment method.",
450+
comment: "Error message shown when the built-in reader cannot be used because " +
451+
"the currency for payment is not supported by the built in reader.")
331452
}
332453
}
333454
}

Hardware/HardwareTests/ErrorCodesTests.swift

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,66 @@ final class CardReaderServiceErrorTests: XCTestCase {
162162
XCTAssertEqual(.processorAPIError, domainError(stripeCode: 9020))
163163
}
164164

165+
func test_stripe_passcode_not_enabled_maps_to_expected_error() {
166+
XCTAssertEqual(.passcodeNotEnabled, domainError(stripeCode: 2920))
167+
}
168+
169+
func test_stripe_TOS_requires_iCloud_signin_maps_to_expected_error() {
170+
XCTAssertEqual(.appleBuiltInReaderTOSAcceptanceRequiresiCloudSignIn, domainError(stripeCode: 2960))
171+
}
172+
173+
func test_stripe_nfc_disabled_maps_to_expected_error() {
174+
XCTAssertEqual(.nfcDisabled, domainError(stripeCode: 3100))
175+
}
176+
177+
func test_stripe_built_in_reader_failed_to_prepare_maps_to_expected_error() {
178+
XCTAssertEqual(.appleBuiltInReaderFailedToPrepare, domainError(stripeCode: 3910))
179+
}
180+
181+
func test_stripe_TOS_acceptance_cancelled_maps_to_expected_error() {
182+
XCTAssertEqual(.appleBuiltInReaderTOSAcceptanceCanceled, domainError(stripeCode: 2970))
183+
}
184+
185+
func test_stripe_TOS_not_yet_accepted_maps_to_expected_error() {
186+
XCTAssertEqual(.appleBuiltInReaderTOSNotYetAccepted, domainError(stripeCode: 3930))
187+
}
188+
189+
func test_stripe_TOS_acceptance_failed_maps_to_expected_error() {
190+
XCTAssertEqual(.appleBuiltInReaderTOSAcceptanceFailed, domainError(stripeCode: 3940))
191+
}
192+
193+
func test_stripe_merchant_blocked_maps_to_expected_error() {
194+
XCTAssertEqual(.appleBuiltInReaderMerchantBlocked, domainError(stripeCode: 3950))
195+
}
196+
197+
func test_stripe_invalid_merchant_maps_to_expected_error() {
198+
XCTAssertEqual(.appleBuiltInReaderInvalidMerchant, domainError(stripeCode: 3960))
199+
}
200+
201+
func test_stripe_device_banned_maps_to_expected_error() {
202+
XCTAssertEqual(.appleBuiltInReaderDeviceBanned, domainError(stripeCode: 3920))
203+
}
204+
205+
func test_stripe_unsupported_mobile_device_maps_to_expected_error() {
206+
XCTAssertEqual(.unsupportedMobileDeviceConfiguration, domainError(stripeCode: 2910))
207+
}
208+
209+
func test_stripe_not_accessible_in_background_maps_to_expected_error() {
210+
XCTAssertEqual(.readerNotAccessibleInBackground, domainError(stripeCode: 3900))
211+
}
212+
213+
func test_stripe_command_not_allowed_during_call_maps_to_expected_error() {
214+
XCTAssertEqual(.commandNotAllowedDuringCall, domainError(stripeCode: 2930))
215+
}
216+
217+
func test_stripe_invalid_amount_maps_to_expected_error() {
218+
XCTAssertEqual(.invalidAmount, domainError(stripeCode: 2940))
219+
}
220+
221+
func test_stripe_invalid_currency_maps_to_expected_error() {
222+
XCTAssertEqual(.invalidCurrency, domainError(stripeCode: 2950))
223+
}
224+
165225
func test_stripe_catch_all_error() {
166226
// Any error code not mapped to an specific error will be
167227
// mapped to `internalServiceError`

0 commit comments

Comments
 (0)