Document Status | CLOSED |
---|---|
Closing date | 2024-09-02 |
Document Owner(s) | |
Reviewers |
π οΈ Potential Solutions
Note: this section should be short and sweet, documenting notes about different solutions that were considered during ideation. Can just be a link to a whiteboard for example. Expected outcome: one solution is chosen and documented in more detail under Proposed Solution.
β Proposed Solution
Context
Currently, the way issues on our platform are presented in Amplitude occurs through error modal events. While this approach is functional, it has significant limitations as it focuses solely on the appearance of the modal, without providing a clear view of the true source of the problems. Error modals can represent both front-end and back-end failures, resulting from a variety of factors, such as configuration errors, RBI implementation, partner integrations, and user-induced errors. At present, there is no clear path to understand the fluctuations in error modals, which prevents the team from comprehending the friction points that users face on our platform.
To address these issues, a list of error codes has been proposed that should be documented and shared with the FZ team, enabling clear and standardized communication regarding the types of errors that may arise. Moreover, a collaborative effort between the front-end and back-end teams will be necessary to ensure that error codes are sent correctly and that the documentation is accessible and understandable. We will implement a protocol to send errors with an initial layer of granularity to mParticle, so they can be utilized in Amplitude, providing immediate insights into the causes of variations and enabling deeper analysis.
In other words:
Allow greater versatility to display errors;
Allow user-friendly messages;
Organized and centralized errors;
Provide a list of accessible errors;
Allow greater versatility in sending events to mParticle, showing or not the error modal;
More details:
Proposal
Description
The focus of the frontend (Whitelabel) is to receive the new error code and be able to generate the translation according to the error, displaying it in the modals of the Commit Order flow.
Additionally, it falls within the scope of the frontend to create a new event sending method for mParticle/Amplitude that enables sending these events either when the modal appears or independently, without the need to display the modal.
At this moment the focus will be just on PAYCOMET errors:
Erros description | Paycomet Error Code |
---|---|
Operation not allowed for the credit card type | 102 |
Card issuer could not validate card owner | 115 |
CVC2 block incorrect | 111 |
Operation not allowed for the credit card type | 130 |
Country (customer IP address) not allowed | 1192 |
Number of failed attempts (last 30 minutes) from the same ip address exceeded | 1202 |
When the same card has several payments in "flight" at the time that one is finalized, the rest are denied with this code. This restriction is for security | 1367 |
The backend will return to the frontend from CommitOrder the response with:
code: The new error code with Domains that will be translated to show the error description in the modal;
e.g.:
PAYMENT.2.001
,PAYMENT.2.100.001
,PAYMENT.4.006
OBS.: The number after the Domain (PAYMENT.2.001) is the severity, more details: /wiki/spaces/RWLAPPwiki/pages/4858741214
rbiErrorCode: The new error code without Domains;
e.g.:
2.001
,2.102.003
,4.004
rbiErrorDomain: Domain related to the flow
e.g.:
PAYMENT
,CHECKOUT
,CART
statusCode: Status code
e.g.:
500
,303
,404
statusCategory: Status category
e.g.:
5XX
,3XX
,4XX
Currently, there are two error handling methods: mapErrorsWithCodes
and parseGraphQLErrorCodes
, which return the IGQLParsedErrorCode
object used for all GraphQL errors.
We will need to implement new methods to handle the new error response without impacting the existing error flow. These new methods will be the main ones for handling GraphQL errors.
For example, the payment flow will use the new methods solely for the errors mentioned above, while all other methods will continue to use mapErrorsWithCodes
and parseGraphQLErrorCodes
.
After consolidating and validating the hypotheses regarding this feature, we will begin the migration to the new error model.
Workflow
Error Messages
Key | Description | Modal Title | Modal Message | Screen |
---|---|---|---|---|
Severity: 2 - NORMAL - Normal User Errors. | ||||
| Operation not allowed for the credit card type | βCard type invalidβ | βYour card is not enabled to allow this payment at the moment. Please, check with your bank and try again.β | |
| Represents errors generated when a transaction is suspected of being fraudulent. | βSuspicious card" | βWe identified a possible fraud related to the use of this card. Please, contact your bank.β | |
| Represents errors generated by internal fraud detection mechanisms within the system. | βSystem fraud" | βSorry, we identified potential fraud related to the system. Please try again later." | |
| Represents errors that occur during the validation of transaction data, such as missing mandatory fields, incorrectly formatted data, and other validation failures. | βValidation errorβ | βSorry, but some of the card details you entered are incorrect. Please try again.β | |
| Represents an error related to credit/debit card transactions. | βProblem with the card" | βWe had a problem with your credit/debit card. Please try again later." | |
| The payment transaction was rejected because the user informed an invalid card CVV. | βThe CVV is incorrect." | βThe payment transaction was declined. Please check the Card information, number, CVV, expire date and try again.β | |
| Represents errors that arise during the payment processing phase, such as failures in communicating with the payment gateway, billing errors, and other issues that may occur while processing a payment. | βCommunication error" | βWe encountered an issue communicating between our servers and your request. Please try again.β | |
| The payment transaction was rejected because card issuer could not validate card owner's identity. | βIdentity error" | βSorry, we were unable to verify the cardholder's identity. Please use another payment method.β | |
| Represents an error when In-Flight offline payments are rejected. An example of occurrence is when the same card has several payments in "flight" at the time and one is finalized, the rest might be denied. | |||
| Represents errors generated when a transaction is rejected as restricted by PSP or issuer. | |||
| Operation is not allowed for this card type. | βCard invalidβ | βThis card is not valid for this type of payment. Please contact your bank or choose another payment method.β | |
| Represents an error when the user's IP address is not from an allowed country. Ex.: Iberia payments only allowed from Spain and Portugal. | βLocation Invalid" | βYou cannot place orders from this country at this store. Please choose another store and try again.β | |
| Represents an error when the number of failed attempts from the same user has been exceeded. | βOrder limit exceeded" | βYou have exceeded the number of attempts to place an order. Please try again later.β | |
Severity: 4 - ABNORMAL | ||||
| General, unspecified payment error. | βFailed Paymentβ | βSomething went wrong with this payment. Please try again laterβ | |
| Represents errors related to security and authentication within the payment system. Impact: All the payment transactions are failing. Solution: Investigate security related settings, like secrets. | βFailed Paymentβ | βSomething went wrong with this payment. Please try again laterβ | |
| Represents errors arising from system configuration issues. Impact: All the payment transactions are failing. Solution: Review the configuration for the specific service and PSP. | βThe system failedβ | βWe are experiencing a problem with our systems, please try again laterβ | |
| Represents errors related to internal connection problems, such as failures in communication between microservices, internal network issues, or connectivity problems with the Payment Service Provider (PSP). Solution: Investigate communication-related infrastructure issues | βConnection error" | βWe are experiencing connection problems with our server. Please try again laterβ | |
| Represents errors associated with database operations within the system. | βFailed Paymentβ | βSomething went wrong with this payment. Please try again laterβ | |
| Represents errors caused by failures in third-party connections with external services necessary for the transaction, such as banks, payment gateways, identity verification services, etc. | βConnection errorβ | βWe are experiencing connection problems with our server. Please try again laterβ | |
| Represents errors that occur due to external factors not directly related to the internal infrastructure of the payment system. | βExternal errorβ | βWe are experiencing external connection issues. Please try again laterβ | |
| Represents errors that occur at the service layer, such as internal service failures, configuration issues, or other problems that prevent the service from functioning as expected. | βFailed Paymentβ | βSomething went wrong with this payment. Please try again laterβ | |
| Represents errors that occur during the internal processing of payment transactions. Ex.: Payment not found for the given rbiOrderId | βProcessing errorβ | βWe encountered an error while processing your request, please try again later.β |
Old Methods
Until that migration starts, we will deprecate the methods mapErrorsWithCodes
and parseGraphQLErrorCodes
to prevent the creation of new error flows using the "old" approach, encouraging the adoption of the new model.
In summary, the current error flows will function independently of the new error model.
For this, we will add a doc comment in the top methods:
mapErrorsWithCodes
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
parseGraphQLErrorCodes
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
New Methods
Creating new methods to validate the new errors is good because it will allow the validation of specific attributes (for example: err.extensions?.rbiErrorDomain
) without needing to use the feature flag or the optional creation.
How?
The new interface IRbiError
and new methods will be parseGraphQLRBIError
and parseGraphQLRBIError
:
IRbiError
mapRBIErrors
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
parseGraphQLRBIError
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
Looking especially for the mapRBIErrors
method:
The new error will have a unique attribute, which no error object within Whitelabel has had until now:
rbiErrorDomain
This attribute will be just in the new error model, that is, any error returned by the backend with this existing or populated field will mean that it must follow the new error flow and will have the specific interface of the new model.
But, for this feature there won't be a feature flag?
Not exactly, the backend has a feature flag to the feature.
Therefore, when the backend returns the response with this attribute, the frontend will know what flow follows.
Positive Points:
No need to create more than one feature flag to the same feature;
Use the effect of enabling the Feature Flag from the backend to the frontend;
Avoids errors of enabling a feature flag, for example, on the frontend and not enabling it on the backend, as has already happened in the case of the skip pre-auth feature flags.
Deprecating the old methods will prevent the creation of new error streams using the "old" approach.
Creating new methods will guarantee the segregation of the flows between old errors and new errors.
Negative points:
Method validation cannot change (
!!err.extensions?.rbiErrorDomain
) to ensure that the flow works;To use the new error model, we will need to create an error code on the backend first;
For some time, the coexistence of 2 error streams will be necessary for old errors and new errors;
OBS.: If necessary there is a feature flag on the frontend, we can add the validation on the method mapRBIErrors
.
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
In addition of creating the new methods mapRBIErrors and parseGraphQLRBIError, it will need to create the other methods:
isRBIError
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
This method will be created to validate if the error is an RBI error.
extractRBIErrorCodes
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
As the error is DOMAIN.SEVERITY.CODE.ERROR, for the translation, it will be necessary to create a method to split the error and get the translation.
For example:
If the error is:
PAYMENT.2.102.001
, the split will return the array with:PAYMENT.2.102.001
PAYMENT.2.102
PAYMENT.2
PAYMENT
This array will be processed for the method
getRBIErrorTranslation
, which will be discussed in the below topic.OBS.: This method needs to add in the return a function
.reverse()
to translation correct because normally the.split()
will return an array:PAYMENT
,PAYMENT.2
,PAYMENT.2.102
,PAYMENT.2.102.001
, so thereverse
is necessary to get the translation of the most detailed (PAYMENT.2.102.001
) error to the least detailed (PAYMENT
).
getRBIErrorTranslation
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
This method calls the extractRBIErrorCodes and will get the Lokalise the correct translation.
For example:
Example 1:
If the translation file (Lokalise) is:
"error.PAYMENT.title": "Translation for the error error.PAYMENT.title", "error.PAYMENT.default": "Translation for the error.PAYMENT.default", "error.PAYMENT.2.001": "Translation for the error 2.001", "error.PAYMENT.2.002": "Translation for the error 2.002", "error.PAYMENT.2.003": "Translation for the error 2.003", "error.PAYMENT.2.004": "Translation for the error 2.004", "error.PAYMENT.2.100": "Translation for the error 2.100",
And the
extractRBIErrorCodes
method returns the array:['PAYMENT.2.102.001', 'PAYMENT.2.102', 'PAYMENT.2', 'PAYMENT']
The
getRBIErrorTranslation
will search firstPAYMENT.2.102.001
, as this item does not exist, the method will look for the next validPAYMENT.2.102
, In this example there is the translation"error.PAYMENT.2.102": "Translation for the error 2.102"
, as the translation was found, so thegetRBIErrorTranslation
method will return theTranslation for the error 2.102
.OBS.: These translation labels are an example;
The label βerrorβ at the beginning of the Lokalise file and the parameters, will explained below topic:
Example 2:
Using the same Lokalise translation described above:
If the
extractRBIErrorCodes
method returns the array:['PAYMENT.4.008', 'PAYMENT.4', 'PAYMENT']
The
getRBIErrorTranslation
will search firstPAYMENT.4.008
, as this item does not exist, the method will look for the next validPAYMENT.4
, but there isnβt too, so the method will search for the nextPAYMENT
and there isnβt.In this example, will return the default translation,
"error.PAYMENT.default": "Translation for the error.PAYMENT.default"
.
fetchRBIErrorDetails
The last new method will be: fetchRBIErrorDetails
;
This method is responsible for orchestrating which label will need to be translated. If it will translate the modal title, description modal, etc.
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/index.ts
For example, if the translation needs to be the title, this method will call the getRBIErrorTranslation like this:
const title = getRBIErrorTranslation({ errorCode: rbiError.errorCode, errorTranslation: ErrorTranslation.TITLE, defaultTranslationId: `error.${rbiError.rbiErrorDomain}.${ErrorTranslation.TITLE}`, formatMessage, });
Parameters:
errorCode: The new error code with Domains that will be translated to show the error description in the modal;
e.g.:
PAYMENT.2.001
errorTranslation: If this translation is a title or description error, if it will be the description, it wonβt add the errorTranslation, because the own error
PAYMENT.2.001
key will be the description;Then, we will need to create a new enum ErrorTranslation:
defaultTranslationId: What default key will need to translate if the error code wonβt be found:
`error.PAYMENT.title`
for titles;`error.PAYMENT.default`
for descriptions;
formatMessage: Method used for Whitelabel to get the translation from Lokalise;
Other Changes
ErrorTranslation
Create a new enum to be used on fetchRBIErrorDetails
path:
intl-whitelabel-app/workspaces/frontend/src/utils/errors/types.ts
IDialogProps
Change the attribute
modalAppearanceEventMessage
tooptional
;path:
intl-whitelabel-app/workspaces/frontend/src/components/dialog/index.tsx
export interface IDialogProps extends IBaseProps { ... modalAppearanceEventMessage?: string; ... }
useErrorModal
The existing hook is the hook that processes the modal error component;
Path:
intl-whitelabel-app/workspaces/frontend/src/hooks/use-error-modal.tsx
For this feature, we will need to change this hook to accommodate the requirements to add to this doc;
Change the
openErrorDialog
method to receive the new error model;Create a new method
recordRbiError
to send the events to mParticle/Amplitude without open the modal;
Change types
Path:
intl-whitelabel-app/workspaces/frontend/src/hooks/use-error-modal.tsx
Create a new type:
export type RecordRBIErrorType = ({ rbiErrors }: { rbiErrors: IRbiError[] }) => void;
Change the type UseErrorDialogHook to create a new attribute
RecordRBIErrorType
:export type UseErrorDialogHook<P = any> = [ React.FC<Props<P>>, ModalCb, VoidFunction, recordRBIErrorType, ];
Change the interface
IUseErrorModalArgs
tooptional
:interface IUseErrorModalArgs<T> { ... modalAppearanceEventMessage?: string; }
Change the interface
IErrorModal
tooptional
and create a new attributerbiError
:export interface IErrorModal { ... message?: string | ReactNode; ... rbiError?: IRbiError[]; }
recordRBIErrorEvents
This method will send the new error events to mParticle/Amplitude;
The events will be:
event
Name | Value |
---|---|
|
|
|
|
attributes
Name | Value |
---|---|
|
|
|
|
|
|
| title: |
| description: |
If called to recordRBIErrorEvents
(Without Modal)
Wonβt be sent the
'Response Title'
and'Response Description'
openErrorDialog
This method is responsible for opening the modal error;
Path:
intl-whitelabel-app/workspaces/frontend/src/hooks/use-error-modal.tsx
For the code to get more legible, we will need to refactor it:
We will create a new errorModal method to receive the existing
if
code block inside theopenErrorDialog
parseErrorModal
We will create a new
parseErrorModal
method to receive the existingif
code block inside theopenErrorDialog
ErrorDialogComponent
Path:
intl-whitelabel-app/workspaces/frontend/src/hooks/use-error-modal.tsx
To accommodate the changes, will need to change the component adding the get to recover the error:
const pendingRbiError = delve(pendingData as T, 'error', null);
This component calls the other component Dialog
and this sends its own event to mParticle/Amplitude . We will need to change this calling to not send the event from Dialog
and send the event independently.
Then, we will need to validate the error type:
If the error is the new error modal (RBIError), the
modalAppearanceEvent
anderrorEventMessage
will beundefined
:
const modalAppearanceEvent = isRBIError(pendingRbiError) ? undefined : modalAppearanceEventMessageOverride.current || modalAppearanceEventMessage; const errorEventMessage = isRBIError(pendingRbiError) ? undefined : errorEventMessageOverride.current;
And change the
Dialog
calls:<Dialog heading={title || heading} body={dialogBody} image={image} onDismiss={dismissDialog} actions={actions} modalAppearanceEventMessage={modalAppearanceEvent} errorEventMessage={errorEventMessage} aria-label="error" {...rest} />
How to use the new error model?
After all these changes, when itβs needed to use the new error model:
import the useErrorModal
:
const [ErrorDialog, openErrorDialog, recordRBIErrorEvents] = useErrorModal();
Open the modal
const errorCodeRBI = parseGraphQLRBIError(error); openErrorDialog({ rbiError: errorCodeRBI });
Send the event without opening the modal
const errorCodeRBI = parseGraphQLRBIError(error); recordRBIErrorEvents({ rbiErrors: errorCodeRBI });
POC
More details on Github: Generic Errors, Paycomet Error Map, and PSP Error
Lokalise file:
Path:
intl-whitelabel-app/workspaces/frontend/src/state/translations/en.json
Potential Challenges
A potential challenge is:
Keeping updated sync of all translations (English, Spanish, Portuguese, etc);
Migrating the old error model to the new error model that will be made in the future, can be difficult due to the number of error flows that exist in Whitelabel.
π° Cost
Describe any additional cost this solution may imply.
ποΈ Configuration
Include here any configuration required (Sanity, LaunchDarkly flags, etc.) required to enable the solution.
π Metrics
After the feature, will it be possible to look at the event attributes, will be able to verify an increase of x% of detailed errors compared to previous deployment events;
ποΈ Delivery Plan
Link to the task or list of all the tasks required to deliver the solution, along with its progress.
π§βπ QA Plan
You can verify the test plan on the page:
β οΈ Call-outs
Tip: Document here any improvement or discussion regarding the feature
0 Comments