Skip to content

Commit 2445b9c

Browse files
Merge pull request #34 from checkout/release/3.1.0
Release/3.1.0
2 parents afb42a6 + 5c0d0f7 commit 2445b9c

41 files changed

Lines changed: 5663 additions & 1991 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Sources/CheckoutCardManagement/CheckoutCardManager.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,17 @@ public final class CheckoutCardManager: CardManager {
110110
walletCardsList: walletCardsList) { [weak self] in
111111
switch $0 {
112112
case .success:
113-
let event = LogEvent.configurePushProvisioning(last4CardholderID: String(cardholderID.suffix(4)))
113+
let event = LogEvent.configurePushProvisioning(cardholderId: cardholderID)
114114
self?.logger?.log(event, startedAt: startTime)
115115
completionHandler(.success)
116116
case .failure(let networkError):
117-
self?.logger?.log(.failure(source: "Configure Push Provisioning", error: networkError), startedAt: startTime)
117+
self?.logger?.log(
118+
.failure(source: "Configure Push Provisioning",
119+
error: networkError,
120+
networkError: networkError,
121+
additionalInfo: ["cardholderId": cardholderID]),
122+
startedAt: startTime
123+
)
118124
completionHandler(.failure(.from(networkError)))
119125
}
120126
}
@@ -133,11 +139,16 @@ public final class CheckoutCardManager: CardManager {
133139
let cards = cards.compactMap {
134140
Card(networkCard: $0, manager: self)
135141
}
136-
let cardsSuffixes: [String] = cards.compactMap { $0.partIdentifier }
137-
self?.logger?.log(.cardList(idSuffixes: cardsSuffixes), startedAt: startTimestamp)
142+
let cardIds: [String] = cards.compactMap { $0.id }
143+
self?.logger?.log(.cardList(cardIds: cardIds), startedAt: startTimestamp)
138144
completionHandler(.success(cards))
139145
case .failure(let networkError):
140-
self?.logger?.log(.failure(source: "Get Cards", error: networkError), startedAt: startTimestamp)
146+
self?.logger?.log(
147+
.failure(source: "Get Cards",
148+
error: networkError,
149+
additionalInfo: [:]),
150+
startedAt: startTimestamp
151+
)
141152
completionHandler(.failure(.from(networkError)))
142153
}
143154
}

Sources/CheckoutCardManagement/LoggingSupport/CheckoutLogger.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import CheckoutEventLoggerKit
1010
import CheckoutCardNetwork
1111

1212
final class CheckoutLogger: NetworkLogger {
13-
1413
let sessionID: String
1514
private let eventLogger: CheckoutEventLogging
1615

@@ -26,11 +25,11 @@ final class CheckoutLogger: NetworkLogger {
2625

2726
/// Network Logger conformance, enabling to collect network level details if error is encountered
2827
func log(error: Error, additionalInfo: [String: String]) {
29-
let event = LogEvent.failure(source: additionalInfo["source"] ?? "", error: error)
28+
let event = LogEvent.failure(source: additionalInfo["source"] ?? "", error: error, additionalInfo: additionalInfo)
3029
eventLogger.log(event: LogFormatter.build(event: event,
31-
extraProperties: additionalInfo))
30+
extraProperties: additionalInfo))
3231
}
33-
32+
3433
/// Enable logger to dispatch events
3534
func setupRemoteLogging(environment: CardManagerEnvironment,
3635
serviceVersion: CardServiceVersion) {
Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//
22
// LogEvent.swift
3-
//
3+
//
44
//
55
// Created by Alex Ioja-Yang on 12/09/2022.
66
//
@@ -16,32 +16,35 @@ enum LogEvent {
1616
case initialized(design: CardManagementDesignSystem)
1717

1818
/// Describe a successful retrieval of the card list
19-
case cardList(idSuffixes: [String])
19+
case cardList(cardIds: [String])
2020

2121
/// Describe a successful call to retrieve a pin
22-
case getPin(idLast4: String, cardState: CardState)
22+
case getPin(cardId: String, cardState: CardState)
23+
24+
/// Describe a successful call to retrieve a card number
25+
case getPan(cardId: String, cardState: CardState)
2326

2427
/// Describe a successful call to retrieve a card number
25-
case getPan(idLast4: String, cardState: CardState)
28+
case copyPan(cardId: String, cardState: CardState)
2629

2730
/// Describe a successful call to retrieve a security code
28-
case getCVV(idLast4: String, cardState: CardState)
31+
case getCVV(cardId: String, cardState: CardState)
2932

3033
/// Describe a successful call to retrieve a pan and a security code
31-
case getPanCVV(idLast4: String, cardState: CardState)
34+
case getPanCVV(cardId: String, cardState: CardState)
3235

3336
/// Describe a successfull event where a card state change was completed
34-
case stateManagement(idLast4: String, originalState: CardState, requestedState: CardState, reason: String?)
37+
case stateManagement(cardId: String, originalState: CardState, requestedState: CardState, reason: String?)
3538

3639
/// Describe a successfull Configuration of Push Provisioning
37-
case configurePushProvisioning(last4CardholderID: String)
40+
case configurePushProvisioning(cardholderId: String)
3841

3942
/// Describe a Get Card Digitization State event
40-
case getCardDigitizationState(last4CardID: String, digitizationState: DigitizationState)
43+
case getCardDigitizationState(cardId: String, digitizationState: DigitizationState)
4144

4245
/// Describe a Push Provisioning event
43-
case pushProvisioning(last4CardID: String)
46+
case pushProvisioning(cardId: String)
4447

4548
/// Describe an unexpected but non critical failure
46-
case failure(source: String, error: Error)
49+
case failure(source: String, error: Error, networkError: CardNetworkError? = nil, additionalInfo: [String: Any])
4750
}

Sources/CheckoutCardManagement/LoggingSupport/LogFormatter.swift

Lines changed: 125 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import Foundation
99
import CheckoutEventLoggerKit
10+
import CheckoutCardNetwork
1011

1112
/// Formatter for internal Analytic Events
1213
enum LogFormatter {
@@ -41,6 +42,7 @@ enum LogFormatter {
4142
case .getCardDigitizationState: return "get_card_digitization_state"
4243
case .pushProvisioning: return "push_provisioning"
4344
case .failure: return "failure"
45+
case .copyPan: return "copy_pan"
4446
}
4547
}
4648

@@ -56,6 +58,7 @@ enum LogFormatter {
5658
.stateManagement,
5759
.configurePushProvisioning,
5860
.getCardDigitizationState,
61+
.copyPan,
5962
.pushProvisioning:
6063
return .info
6164
case .failure:
@@ -72,45 +75,147 @@ enum LogFormatter {
7275
"version": AnyCodable(Constants.productVersion),
7376
"design": AnyCodable(try? design.mapToLogDictionary())
7477
]
75-
case .cardList(let suffixes):
76-
dictionary = ["suffix_ids": AnyCodable(suffixes)]
77-
case .getPin(let idLast4, let state),
78-
.getPan(let idLast4, let state),
79-
.getCVV(let idLast4, let state),
80-
.getPanCVV(let idLast4, let state):
78+
case .cardList(let cardIds):
79+
dictionary = ["cardIds": AnyCodable(cardIds)]
80+
case .getPin(let cardId, let state),
81+
.getPan(let cardId, let state),
82+
.getCVV(let cardId, let state),
83+
.getPanCVV(let cardId, let state),
84+
.copyPan(let cardId, let state):
8185
dictionary = [
82-
"suffix_id": AnyCodable(idLast4),
86+
"cardId": AnyCodable(cardId),
8387
"card_state": AnyCodable(state.rawValue)
8488
]
85-
case .stateManagement(let idLast4, let originalState, let requestedState, let reason):
89+
case .stateManagement(let cardId, let originalState, let requestedState, let reason):
8690
dictionary = [
87-
"suffix_id": AnyCodable(idLast4),
91+
"cardId": AnyCodable(cardId),
8892
"from": AnyCodable(originalState.rawValue),
8993
"to": AnyCodable(requestedState.rawValue)
9094
]
9195
if let reason {
9296
dictionary["reason"] = AnyCodable(reason)
9397
}
94-
case .configurePushProvisioning(let last4CardholderID):
98+
case .configurePushProvisioning(let cardholderId):
9599
dictionary = [
96-
"cardholder": AnyCodable(last4CardholderID)
100+
"cardholder": AnyCodable(cardholderId)
97101
]
98-
case .getCardDigitizationState(let last4CardID, let digitizationState):
102+
case .getCardDigitizationState(let cardId, let digitizationState):
99103
dictionary = [
100-
"card": AnyCodable(last4CardID),
104+
"card": AnyCodable(cardId),
101105
"digitization_state": AnyCodable(digitizationState.rawValue)
102106
]
103-
case .pushProvisioning(let last4CardID):
107+
case .pushProvisioning(let cardId):
104108
dictionary = [
105-
"card": AnyCodable(last4CardID)
106-
]
107-
case .failure(let source, let error):
108-
dictionary = [
109-
"source": AnyCodable(source),
110-
"error": AnyCodable(error.localizedDescription)
109+
"cardId": AnyCodable(cardId)
111110
]
111+
case .failure(let source, let error, let networkError, let additionalInfo):
112+
if let networkError = networkError {
113+
let errorDetails = extractErrorDetails(from: networkError)
114+
dictionary = [
115+
"source": AnyCodable(source),
116+
"error_type": AnyCodable(errorDetails.type),
117+
"error_description": AnyCodable(errorDetails.description)
118+
]
119+
120+
if let additionalErrorInfo = errorDetails.additionalInfo {
121+
additionalErrorInfo.forEach { key, value in
122+
dictionary["error_\(key)"] = AnyCodable(value)
123+
}
124+
}
125+
} else {
126+
dictionary = [
127+
"source": AnyCodable(source),
128+
"error": AnyCodable(error.localizedDescription)
129+
]
130+
}
131+
132+
additionalInfo.forEach { (key: String, value: Any) in
133+
dictionary[key] = AnyCodable(value)
134+
}
112135
}
113136
dictionary.addTimeSince(startDate: startDate)
114137
return dictionary
115138
}
139+
140+
/// Extract structured information from CardNetworkError
141+
private static func extractErrorDetails(from error: Error) -> (type: String, description: String, additionalInfo: [String: Any]?) {
142+
guard let cardNetworkError = error as? CardNetworkError else {
143+
return (type: "unknown", description: error.localizedDescription, additionalInfo: nil)
144+
}
145+
146+
switch cardNetworkError {
147+
case .authenticationFailure:
148+
return (type: "authentication_failure", description: "Authentication failed", additionalInfo: nil)
149+
150+
case .deviceNotSupported:
151+
return (type: "device_not_supported", description: "Device does not support the operation", additionalInfo: nil)
152+
153+
case .insecureDevice:
154+
return (type: "insecure_device", description: "Device flagged as unsafe", additionalInfo: nil)
155+
156+
case .invalidRequest(let hint):
157+
return (type: "invalid_request", description: "Invalid request", additionalInfo: ["hint": hint])
158+
159+
case .invalidRequestInput:
160+
return (type: "invalid_request_input", description: "Invalid request input format", additionalInfo: nil)
161+
162+
case .misconfigured(let hint):
163+
return (type: "misconfigured", description: "Service connection misconfigured", additionalInfo: ["hint": hint])
164+
165+
case .serverIssue:
166+
return (type: "server_issue", description: "Server unable to respond", additionalInfo: nil)
167+
168+
case .unauthenticated:
169+
return (type: "unauthenticated", description: "Session expired or missing", additionalInfo: nil)
170+
171+
case .secureOperationsFailure:
172+
return (type: "secure_operations_failure", description: "Unable to handle secure operations", additionalInfo: nil)
173+
174+
case .parsingFailure:
175+
return (type: "parsing_failure", description: "Response format mismatch", additionalInfo: nil)
176+
177+
case .pushProvisioningFailure(let failure):
178+
let failureType = extractPushProvisioningFailureType(failure)
179+
return (type: "push_provisioning_failure", description: "Push provisioning failed", additionalInfo: ["failure_type": failureType])
180+
181+
case .fetchDigitizationStateFailure(let failure):
182+
let failureType = extractDigitizationStateFailureType(failure)
183+
return (type: "fetch_digitization_state_failure", description: "Failed to fetch digitization state", additionalInfo: ["failure_type": failureType])
184+
case .unableToCopy(let failure):
185+
let failureType = extractUnableToCopyFailureType(failure)
186+
return (type: "copy_failure", description: "Failed to copy to clipboard", additionalInfo: ["failure_type": failureType])
187+
}
188+
}
189+
190+
/// Extract push provisioning failure type
191+
private static func extractPushProvisioningFailureType(_ failure: CardNetworkError.PushProvisioningFailure) -> String {
192+
switch failure {
193+
case .cancelled:
194+
return "cancelled"
195+
case .configurationFailure:
196+
return "configuration_failure"
197+
case .operationFailure(let hint):
198+
return "operation_failure \(hint)"
199+
}
200+
}
201+
202+
/// Extract digitization state failure type
203+
private static func extractDigitizationStateFailureType(_ failure: CardNetworkError.DigitizationStateFailure) -> String {
204+
switch failure {
205+
case .configurationFailure:
206+
return "configuration_failure"
207+
case .operationFailure:
208+
return "operation_failure"
209+
}
210+
}
211+
212+
/// Extract digitization state failure type
213+
private static func extractUnableToCopyFailureType(_ failure: CardNetworkError.CopySensitiveDataError) -> String {
214+
switch failure {
215+
case .copyFailure:
216+
return "copy_operation_failure"
217+
case .dataNotViewed:
218+
return "pan_not_viewed"
219+
}
220+
}
116221
}

Sources/CheckoutCardManagement/Models/CardDetails/Card+Management.swift

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,18 @@ public extension Card {
3434
switch result {
3535
case .success(let cardDigitizationData):
3636
let digitizationData = DigitizationData.from(cardDigitizationData)
37-
let event = LogEvent.getCardDigitizationState(last4CardID: self?.partIdentifier ?? "",
37+
let event = LogEvent.getCardDigitizationState(cardId: self?.id ?? "",
3838
digitizationState: digitizationData.state)
3939
self?.manager?.logger?.log(event, startedAt: startTime)
4040
completionHandler(.success(digitizationData))
4141
case .failure(let networkError):
42-
self?.manager?.logger?.log(.failure(source: "Get Digitization State", error: networkError), startedAt: startTime)
42+
self?.manager?.logger?.log(
43+
.failure(source: "Get Digitization State",
44+
error: networkError,
45+
networkError: networkError,
46+
additionalInfo: ["cardId": self?.id ?? ""]),
47+
startedAt: startTime
48+
)
4349
completionHandler(.failure(.from(networkError)))
4450
}
4551
}
@@ -57,11 +63,17 @@ public extension Card {
5763
token: provisioningToken) { [weak self] result in
5864
switch result {
5965
case .success:
60-
let event = LogEvent.pushProvisioning(last4CardID: self?.partIdentifier ?? "")
66+
let event = LogEvent.pushProvisioning(cardId: self?.id ?? "")
6167
self?.manager?.logger?.log(event, startedAt: startTime)
6268
completionHandler(.success)
6369
case .failure(let networkError):
64-
self?.manager?.logger?.log(.failure(source: "Push Provisioning", error: networkError), startedAt: startTime)
70+
self?.manager?.logger?.log(
71+
.failure(
72+
source: "Push Provisioning",
73+
error: networkError,
74+
networkError: networkError,
75+
additionalInfo: ["cardId": self?.id ?? ""]),
76+
startedAt: startTime)
6577
completionHandler(.failure(.from(networkError)))
6678
}
6779
}
@@ -147,15 +159,20 @@ public extension Card {
147159
completionHandler: @escaping ((CheckoutCardManager.OperationResult) -> Void)) {
148160
switch result {
149161
case .success:
150-
let event = LogEvent.stateManagement(idLast4: partIdentifier,
162+
let event = LogEvent.stateManagement(cardId: id,
151163
originalState: state,
152164
requestedState: newState,
153165
reason: reasonString)
154166
manager?.logger?.log(event, startedAt: startTime)
155167
state = newState
156168
completionHandler(.success)
157169
case .failure(let networkError):
158-
manager?.logger?.log(.failure(source: operationSource, error: networkError), startedAt: startTime)
170+
manager?.logger?.log(
171+
.failure(source: operationSource,
172+
error: networkError,
173+
networkError: networkError,
174+
additionalInfo: ["cardId": id, "originalState": state, "newState": newState, "reason": reasonString ?? ""]),
175+
startedAt: startTime)
159176
completionHandler(.failure(.from(networkError)))
160177
}
161178
}

0 commit comments

Comments
 (0)