77
88import Foundation
99import CheckoutEventLoggerKit
10+ import CheckoutCardNetwork
1011
1112/// Formatter for internal Analytic Events
1213enum 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}
0 commit comments