Skip to content

Commit

Permalink
chore: [IOBP-1044,IOBP-1054] Add missing outcome screen into payment …
Browse files Browse the repository at this point in the history
…onboarding (#6514)

## Short description
This PR adds two missing outcome screens into payment onboarding flow.

## List of changes proposed in this pull request
- Mapped outcome `PSP_ERROR_ONBOARDING` and `BE_KO` inside the payment
method onboarding flow;
- Handled the onboarding outcomes inside the
`usePaymentFailureSupportModal` hook;
- Refactored the `trackAddOnboardingPaymentMethod` function to keep
track of the analytics onboarding outcomes;

## How to test
- Checkout this dev-server PR:
pagopa/io-dev-api-server#444;
- Start the payment onboarding flow from the wallet screen;
- When you reach the in-app browser with the dropdown selection, choose
`PSP_ERROR_ONBOARDING` or `BE_KO` and check that you see the correct
screen;

## Preview
|PSP_ERROR_ONBOARDING|BE_KO|
|-|-|
|<img
src="https://github.com/user-attachments/assets/b148cc63-cdd0-46dd-a7eb-3891c69c940e"
width="350"/>|<img
src="https://github.com/user-attachments/assets/bbaa3eea-76f2-4a21-9f93-e7d4414f9538"
width="350"/>|
  • Loading branch information
Hantex9 authored Dec 9, 2024
1 parent 7d9c5cf commit 26d92d0
Show file tree
Hide file tree
Showing 8 changed files with 430 additions and 82 deletions.
9 changes: 9 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,15 @@ wallet:
title: Non abbiamo trovato alcun BANCOMAT Pay attivo
subtitle: "Contatta la tua Banca per attivare il servizio."
primaryAction: Chiudi
PSP_ERROR_ONBOARDING:
title: L’aggiunta del metodo non è andata a buon fine
subtitle: "Riprova più tardi. Se il problema persiste, ti invitiamo a verificare con la tua banca."
primaryAction: Chiudi
BE_KO:
title: "Stiamo riscontrando alcuni problemi con l'aggiunta del metodo"
subtitle: "Riprova più tardi."
primaryAction: Chiudi
secondaryAction: Contatta l'assistenza
alert:
supportedCardPageLinkError: An error occurred while opening the supported cards page.
msgErrorUpdateApp: "An error occurred while opening the app store"
Expand Down
9 changes: 9 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,15 @@ wallet:
title: Non abbiamo trovato alcun BANCOMAT Pay attivo
subtitle: "Contatta la tua Banca per attivare il servizio."
primaryAction: Chiudi
PSP_ERROR_ONBOARDING:
title: L’aggiunta del metodo non è andata a buon fine
subtitle: "Riprova più tardi. Se il problema persiste, ti invitiamo a verificare con la tua banca."
primaryAction: Chiudi
BE_KO:
title: "Stiamo riscontrando alcuni problemi con l'aggiunta del metodo"
subtitle: "Riprova più tardi."
primaryAction: Chiudi
secondaryAction: Contatta l'assistenza
alert:
supportedCardPageLinkError: Si è verificato un errore durante l'apertura della pagina di riferimento per le carte supportate.
msgErrorUpdateApp: "Si è verificato un errore durante l'apertura dello store delle app"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,15 @@ import {
} from "../types/PaymentOutcomeEnum";
import { WalletPaymentFailure } from "../types/WalletPaymentFailure";
import { formatPaymentNoticeNumber } from "../../common/utils";
import {
getWalletOnboardingOutcomeEnumByValue,
WalletOnboardingOutcome
} from "../../onboarding/types/OnboardingOutcomeEnum";

type PaymentFailureSupportModalParams = {
failure?: WalletPaymentFailure;
outcome?: WalletPaymentOutcome;
outcome?: WalletPaymentOutcome | WalletOnboardingOutcome;
isOnboarding?: boolean;
withPhoneAssistance?: boolean;
};

Expand All @@ -59,6 +64,7 @@ type PaymentFailureSupportModal = {
const usePaymentFailureSupportModal = ({
failure,
outcome,
isOnboarding = false,
withPhoneAssistance = false
}: PaymentFailureSupportModalParams): PaymentFailureSupportModal => {
const assistanceToolConfig = useIOSelector(assistanceToolConfigSelector);
Expand All @@ -69,7 +75,10 @@ const usePaymentFailureSupportModal = ({

const faultCodeDetail =
failure?.faultCodeDetail ||
(outcome && getWalletPaymentOutcomeEnumByValue(outcome)) ||
(outcome &&
(!isOnboarding
? getWalletPaymentOutcomeEnumByValue(outcome)
: getWalletOnboardingOutcomeEnumByValue(outcome))) ||
"";

const zendeskAssistanceLogAndStart = () => {
Expand Down Expand Up @@ -183,20 +192,26 @@ const usePaymentFailureSupportModal = ({
value={faultCodeDetail}
onPress={() => clipboardSetStringWithFeedback(faultCodeDetail)}
/>
<ListItemInfoCopy
label={I18n.t("wallet.payment.support.noticeNumber")}
accessibilityLabel={I18n.t("wallet.payment.support.noticeNumber")}
icon="docPaymentCode"
value={formattedPaymentNoticeNumber}
onPress={() => clipboardSetStringWithFeedback(paymentNoticeNumber)}
/>
<ListItemInfoCopy
label={I18n.t("wallet.payment.support.entityCode")}
accessibilityLabel={I18n.t("wallet.payment.support.entityCode")}
icon="entityCode"
value={organizationFiscalCode}
onPress={() => clipboardSetStringWithFeedback(organizationFiscalCode)}
/>
{!isOnboarding && (
<ListItemInfoCopy
label={I18n.t("wallet.payment.support.noticeNumber")}
accessibilityLabel={I18n.t("wallet.payment.support.noticeNumber")}
icon="docPaymentCode"
value={formattedPaymentNoticeNumber}
onPress={() => clipboardSetStringWithFeedback(paymentNoticeNumber)}
/>
)}
{!isOnboarding && (
<ListItemInfoCopy
label={I18n.t("wallet.payment.support.entityCode")}
accessibilityLabel={I18n.t("wallet.payment.support.entityCode")}
icon="entityCode"
value={organizationFiscalCode}
onPress={() =>
clipboardSetStringWithFeedback(organizationFiscalCode)
}
/>
)}
<VSpacer size={24} />
</>
),
Expand Down
130 changes: 130 additions & 0 deletions ts/features/payments/onboarding/analytics/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { mixpanelTrack } from "../../../../../mixpanel";
import {
getOnboardingPaymentMethodOutcomeEvent,
trackAddOnboardingPaymentMethod,
trackPaymentOnboardingErrorHelp
} from "../../analytics";
import { WalletOnboardingOutcomeEnum } from "../../types/OnboardingOutcomeEnum";

jest.mock("../../../../../mixpanel", () => ({
mixpanelTrack: jest.fn()
}));

jest.mock("../../../../../utils/analytics", () => ({
buildEventProperties: jest.fn().mockImplementation((source, type, props) => ({
source,
type,
...props
}))
}));

describe("Onboarding Analytics", () => {
describe("getOnboardingPaymentMethodOutcomeEvent", () => {
it.each([
[WalletOnboardingOutcomeEnum.SUCCESS, "ADD_PAYMENT_METHOD_UX_SUCCESS"],
[
WalletOnboardingOutcomeEnum.AUTH_ERROR,
"PAYMENT_ADD_METHOD_AUTHORIZATION_DENIED"
],
[
WalletOnboardingOutcomeEnum.CANCELED_BY_USER,
"PAYMENT_ADD_METHOD_CANCELED_BY_USER"
],
[
WalletOnboardingOutcomeEnum.ALREADY_ONBOARDED,
"PAYMENT_ADD_METHOD_DUPLICATE_ERROR"
],
[WalletOnboardingOutcomeEnum.BE_KO, "PAYMENT_99_ERROR"],
[
WalletOnboardingOutcomeEnum.BPAY_NOT_FOUND,
"PAYMENT_ADD_METHOD_BPAY_NOT_FOUND"
],
[
WalletOnboardingOutcomeEnum.PSP_ERROR_ONBOARDING,
"PAYMENT_ADD_METHOD_PSP_ERROR"
],
[WalletOnboardingOutcomeEnum.INVALID_SESSION, "PAYMENT_SESSION_TIMEOUT"],
[WalletOnboardingOutcomeEnum.TIMEOUT, "PAYMENT_SESSION_TIMEOUT"],
[WalletOnboardingOutcomeEnum.GENERIC_ERROR, "PAYMENT_GENERIC_ERROR"],
["UNKNOWN_OUTCOME", "PAYMENT_GENERIC_ERROR"]
])(
"returns correct event name for outcome %s",
(outcome, expectedEventName) => {
const eventName = getOnboardingPaymentMethodOutcomeEvent(
outcome as WalletOnboardingOutcomeEnum
);
expect(eventName).toBe(expectedEventName);
}
);
});

describe("trackAddOnboardingPaymentMethod", () => {
it("tracks the correct event with provided outcome and payment method", () => {
trackAddOnboardingPaymentMethod(
WalletOnboardingOutcomeEnum.SUCCESS,
"CreditCard"
);

expect(mixpanelTrack).toHaveBeenCalledWith(
"ADD_PAYMENT_METHOD_UX_SUCCESS",
expect.objectContaining({
source: "UX",
type: "screen_view",
payment_method_selected: "CreditCard",
payment_phase: "onboarding"
})
);
});

it("handles undefined payment method gracefully", () => {
trackAddOnboardingPaymentMethod(
WalletOnboardingOutcomeEnum.AUTH_ERROR,
undefined
);

expect(mixpanelTrack).toHaveBeenCalledWith(
"PAYMENT_ADD_METHOD_AUTHORIZATION_DENIED",
expect.objectContaining({
source: "UX",
type: "screen_view",
payment_method_selected: undefined,
payment_phase: "onboarding"
})
);
});
});

describe("trackPaymentOnboardingErrorHelp", () => {
it("tracks the correct event with error and additional props", () => {
trackPaymentOnboardingErrorHelp({
error: "PAYMENT_ERROR",
payment_method_selected: "CreditCard"
});

expect(mixpanelTrack).toHaveBeenCalledWith(
"PAYMENT_ERROR_HELP",
expect.objectContaining({
source: "UX",
type: "action",
payment_phase: "onboarding",
error: "PAYMENT_ERROR",
payment_method_selected: "CreditCard"
})
);
});

it("handles missing optional properties gracefully", () => {
trackPaymentOnboardingErrorHelp({ error: "PAYMENT_ERROR" });

expect(mixpanelTrack).toHaveBeenCalledWith(
"PAYMENT_ERROR_HELP",
expect.objectContaining({
source: "UX",
type: "action",
payment_phase: "onboarding",
error: "PAYMENT_ERROR"
})
);
});
});
});
100 changes: 39 additions & 61 deletions ts/features/payments/onboarding/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,76 +6,54 @@ export type PaymentOnboardingAnalyticsProps = {
payment_method_selected: string;
};

const trackSuccessOnboardingPaymentMethod = (
props: Partial<PaymentOnboardingAnalyticsProps>
export const getOnboardingPaymentMethodOutcomeEvent = (
outcome: WalletOnboardingOutcomeEnum
) => {
void mixpanelTrack(
"ADD_PAYMENT_METHOD_UX_SUCCESS",
buildEventProperties("UX", "screen_view", props)
);
};

const trackOnboardingPaymentMethodDenied = (
props: Partial<PaymentOnboardingAnalyticsProps>
) => {
void mixpanelTrack(
"PAYMENT_ADD_METHOD_AUTHORIZATION_DENIED",
buildEventProperties("UX", "screen_view", props)
);
switch (outcome) {
case WalletOnboardingOutcomeEnum.SUCCESS:
return "ADD_PAYMENT_METHOD_UX_SUCCESS";
case WalletOnboardingOutcomeEnum.AUTH_ERROR:
return "PAYMENT_ADD_METHOD_AUTHORIZATION_DENIED";
case WalletOnboardingOutcomeEnum.CANCELED_BY_USER:
return "PAYMENT_ADD_METHOD_CANCELED_BY_USER";
case WalletOnboardingOutcomeEnum.ALREADY_ONBOARDED:
return "PAYMENT_ADD_METHOD_DUPLICATE_ERROR";
case WalletOnboardingOutcomeEnum.BE_KO:
return "PAYMENT_99_ERROR";
case WalletOnboardingOutcomeEnum.BPAY_NOT_FOUND:
return "PAYMENT_ADD_METHOD_BPAY_NOT_FOUND";
case WalletOnboardingOutcomeEnum.PSP_ERROR_ONBOARDING:
return "PAYMENT_ADD_METHOD_PSP_ERROR";
case WalletOnboardingOutcomeEnum.INVALID_SESSION:
case WalletOnboardingOutcomeEnum.TIMEOUT:
return "PAYMENT_SESSION_TIMEOUT";
case WalletOnboardingOutcomeEnum.GENERIC_ERROR:
default:
return "PAYMENT_GENERIC_ERROR";
}
};

const trackAddOnboardingPaymentMethodCanceled = (
props: Partial<PaymentOnboardingAnalyticsProps>
export const trackAddOnboardingPaymentMethod = (
outcome: WalletOnboardingOutcomeEnum,
payment_method_selected: string | undefined
) => {
void mixpanelTrack(
"PAYMENT_ADD_METHOD_CANCELED_BY_USER",
buildEventProperties("UX", "screen_view", props)
getOnboardingPaymentMethodOutcomeEvent(outcome),
buildEventProperties("UX", "screen_view", {
payment_method_selected,
payment_phase: "onboarding"
})
);
};

const trackAddOnboardingPaymentMethodDuplicated = (
props: Partial<PaymentOnboardingAnalyticsProps>
export const trackPaymentOnboardingErrorHelp = (
props: Partial<PaymentOnboardingAnalyticsProps> & { error: string }
) => {
void mixpanelTrack(
"PAYMENT_ADD_METHOD_DUPLICATE_ERROR",
buildEventProperties("UX", "screen_view", props)
"PAYMENT_ERROR_HELP",
buildEventProperties("UX", "action", {
payment_phase: "onboarding",
...props
})
);
};

// This function will be used in the future when we will have 3DS
// const trackOnboardingPaymentMethod3dsError = (
// props: Partial<PaymentOnboardingAnalyticsProps>
// ) => {
// void mixpanelTrack(
// "PAYMENT_ADD_METHOD_3DS_ERROR",
// buildEventProperties("UX", "screen_view", props)
// );
// };

export const trackAddOnboardingPaymentMethod = (
outcome: WalletOnboardingOutcomeEnum,
payment_method_selected: string | undefined
) => {
switch (outcome) {
case WalletOnboardingOutcomeEnum.SUCCESS:
trackSuccessOnboardingPaymentMethod({
payment_method_selected
});
break;
case WalletOnboardingOutcomeEnum.AUTH_ERROR:
trackOnboardingPaymentMethodDenied({
payment_method_selected
});
break;
case WalletOnboardingOutcomeEnum.CANCELED_BY_USER:
trackAddOnboardingPaymentMethodCanceled({
payment_method_selected
});
break;
case WalletOnboardingOutcomeEnum.ALREADY_ONBOARDED:
trackAddOnboardingPaymentMethodDuplicated({
payment_method_selected
});
break;
}
};
Loading

0 comments on commit 26d92d0

Please sign in to comment.