Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: [IOBP-964] Add zendesk payment subcategories #6522

Merged
merged 27 commits into from
Dec 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f60dc84
feat: handleGetZendeskPaymentConfig
LeleDallas Dec 9, 2024
e5d167a
chore: remove zendesk hardcoded data
LeleDallas Dec 9, 2024
41eb1a9
feat: getZendeskPaymentConfig api call and store
LeleDallas Dec 9, 2024
40c77dc
refactor: create payment ticket based on fault code value
LeleDallas Dec 9, 2024
322af1c
refactor: update mock type
LeleDallas Dec 9, 2024
0cfa31e
test: add missing initial value
LeleDallas Dec 9, 2024
cd85f99
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 9, 2024
83f126f
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 9, 2024
46ccead
refactor: update endpoint string
LeleDallas Dec 9, 2024
b0b98f9
refactor: update getSubCategory function
LeleDallas Dec 9, 2024
0eca7a0
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 9, 2024
d1fcb78
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 10, 2024
23fc550
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 10, 2024
6175e92
chore: io-service-metadata v1.0.50
LeleDallas Dec 10, 2024
f8705a1
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 10, 2024
89d7ec1
refactor: add missing types
LeleDallas Dec 10, 2024
9076810
test: getSubCategoryFromFaultCode utils
LeleDallas Dec 10, 2024
973779a
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 10, 2024
910d90c
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 10, 2024
470afad
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 11, 2024
72ab77a
refactor: handle nullable subcategory case
LeleDallas Dec 11, 2024
a3cbfa3
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 11, 2024
b2015f1
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
Hantex9 Dec 11, 2024
773571b
refactor: change request
LeleDallas Dec 11, 2024
140d204
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 11, 2024
0959a51
refactor: rename reducer state const
LeleDallas Dec 11, 2024
b8cc047
Merge branch 'master' into IOBP-964-zendesk-subcategory-adjustment
LeleDallas Dec 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"io_session_manager_api": "https://raw.githubusercontent.com/pagopa/io-auth-n-identity-domain/[email protected]/apps/io-session-manager/api/internal.yaml",
"io_session_manager_public_api": "https://raw.githubusercontent.com/pagopa/io-auth-n-identity-domain/[email protected]/apps/io-session-manager/api/public.yaml",
"io_public_api": "https://raw.githubusercontent.com/pagopa/io-backend/v16.4.0-RELEASE/api_public.yaml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.49/definitions.yml",
"io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.50/definitions.yml",
"io_cgn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v16.4.0-RELEASE/api_cgn.yaml",
"io_cgn_merchants_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v16.4.0-RELEASE/api_cgn_operator_search.yaml",
"api_fci": "https://raw.githubusercontent.com/pagopa/io-backend/v16.4.0-RELEASE/api_io_sign.yaml",
Expand Down
23 changes: 22 additions & 1 deletion ts/api/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Municipality as MunicipalityMedadata } from "../../definitions/content/
import { SpidIdps } from "../../definitions/content/SpidIdps";
import { VersionInfo } from "../../definitions/content/VersionInfo";
import { Zendesk } from "../../definitions/content/Zendesk";
import { ZendeskSubcategoriesErrors } from "../../definitions/content/ZendeskSubcategoriesErrors";
import { CoBadgeServices } from "../../definitions/pagopa/cobadge/configuration/CoBadgeServices";
import { AbiListResponse } from "../../definitions/pagopa/walletv2/AbiListResponse";
import { contentRepoUrl } from "../config";
Expand Down Expand Up @@ -140,6 +141,22 @@ const getZendeskConfigT: GetZendeskConfigT = {
headers: () => ({}),
response_decoder: basicResponseDecoder(Zendesk)
};

type GetZendeskPaymentConfigT = IGetApiRequestType<
void,
never,
never,
BasicResponseType<ZendeskSubcategoriesErrors>
>;

const getZendeskPaymentConfig: GetZendeskPaymentConfigT = {
method: "get",
url: () => "/assistanceTools/payment/zendeskOutcomeMapping.json",
query: _ => ({}),
headers: () => ({}),
response_decoder: basicResponseDecoder(ZendeskSubcategoriesErrors)
};

/**
* A client for the static content
*/
Expand All @@ -157,6 +174,10 @@ export function ContentClient(fetchApi: typeof fetch = defaultRetryingFetch()) {
getCobadgeServices: createFetchRequestForApi(getCobadgeServicesT, options),
getVersionInfo: createFetchRequestForApi(getVersionInfoT, options),
getIdps: createFetchRequestForApi(getIdpsT, options),
getZendeskConfig: createFetchRequestForApi(getZendeskConfigT, options)
getZendeskConfig: createFetchRequestForApi(getZendeskConfigT, options),
getZendeskPaymentConfig: createFetchRequestForApi(
getZendeskPaymentConfig,
options
)
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
import { OrganizationFiscalCode } from "@pagopa/ts-commons/lib/strings";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import React from "react";
import React, { useEffect } from "react";
import { Linking } from "react-native";
import { ToolEnum } from "../../../../../definitions/content/AssistanceToolConfig";
import I18n from "../../../../i18n";
Expand All @@ -24,30 +24,34 @@ import {
addTicketCustomField,
appendLog,
assistanceToolRemoteConfig,
defaultZendeskPaymentCategory,
resetCustomFields,
zendeskCategoryId,
zendeskPaymentCategory,
zendeskPaymentFailure,
zendeskPaymentNav,
zendeskPaymentOrgFiscalCode,
zendeskPaymentStartOrigin
} from "../../../../utils/supportAssistance";
import {
getZendeskPaymentConfig,
zendeskSelectedCategory,
zendeskSupportStart
} from "../../../zendesk/store/actions";
import { zendeskMapSelector } from "../../../zendesk/store/reducers";
import { formatPaymentNoticeNumber } from "../../common/utils";
import { selectOngoingPaymentHistory } from "../../history/store/selectors";
import {
WalletOnboardingOutcome,
getWalletOnboardingOutcomeEnumByValue
} from "../../onboarding/types/OnboardingOutcomeEnum";
import { walletPaymentRptIdSelector } from "../store/selectors";
import {
WalletPaymentOutcome,
getWalletPaymentOutcomeEnumByValue
} from "../types/PaymentOutcomeEnum";
import { WalletPaymentFailure } from "../types/WalletPaymentFailure";
import { formatPaymentNoticeNumber } from "../../common/utils";
import {
getWalletOnboardingOutcomeEnumByValue,
WalletOnboardingOutcome
} from "../../onboarding/types/OnboardingOutcomeEnum";
import { getSubCategoryFromFaultCode } from "../utils";
import { isReady } from "../../../../common/model/RemoteValue";

type PaymentFailureSupportModalParams = {
failure?: WalletPaymentFailure;
Expand All @@ -71,8 +75,14 @@ const usePaymentFailureSupportModal = ({
const choosenTool = assistanceToolRemoteConfig(assistanceToolConfig);
const rptId = useIOSelector(walletPaymentRptIdSelector);
const paymentHistory = useIOSelector(selectOngoingPaymentHistory);
const zendeskPaymentCategory = useIOSelector(zendeskMapSelector);

const dispatch = useIODispatch();

useEffect(() => {
dispatch(getZendeskPaymentConfig.request());
}, [dispatch]);

const faultCodeDetail =
failure?.faultCodeDetail ||
(outcome &&
Expand All @@ -82,8 +92,25 @@ const usePaymentFailureSupportModal = ({
"";

const zendeskAssistanceLogAndStart = () => {
if (!isReady(zendeskPaymentCategory)) {
return;
}
const { payments } = zendeskPaymentCategory.value;
const subCategory = getSubCategoryFromFaultCode(payments, faultCodeDetail);

resetCustomFields();
addTicketCustomField(zendeskCategoryId, zendeskPaymentCategory.value);
// attach the main zendesk category to the ticket
addTicketCustomField(
zendeskCategoryId,
defaultZendeskPaymentCategory.value
);

if (subCategory) {
// if a subcategory is found, we attach its id and value to the ticket
const { value, zendeskSubCategoryId } = subCategory;
addTicketCustomField(zendeskSubCategoryId, value);
}

addTicketCustomField(zendeskPaymentOrgFiscalCode, organizationFiscalCode);
addTicketCustomField(zendeskPaymentNav, paymentNoticeNumber);
addTicketCustomField(zendeskPaymentFailure, faultCodeDetail);
Expand All @@ -100,7 +127,7 @@ const usePaymentFailureSupportModal = ({
assistanceForFci: false
})
);
dispatch(zendeskSelectedCategory(zendeskPaymentCategory));
dispatch(zendeskSelectedCategory(defaultZendeskPaymentCategory));
};

const handleAskAssistance = () => {
Expand Down
28 changes: 28 additions & 0 deletions ts/features/payments/checkout/utils/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
import {
getPaymentPhaseFromStep,
getPspFlagType,
getSubCategoryFromFaultCode,
isDueDateValid,
trimAndLimitValue
} from "..";
import { ZendeskSubCategoriesMap } from "../../../../../../definitions/content/ZendeskSubCategoriesMap";
import { WalletPaymentStepEnum } from "../../types";

const mockCategories: ZendeskSubCategoriesMap = {
subcategories: {
"12345": ["subcategory1"],
"67890": ["subcategory2"]
},
subcategoryId: "313"
};

describe("trimAndLimitValue", () => {
it("should remove all spaces from the string", () => {
const input = "a b c d e";
Expand Down Expand Up @@ -136,3 +146,21 @@ describe("getPaymentPhaseFromStep", () => {
expect(result).toBe("verifica");
});
});

describe("getSubCategoryFromFaultCode", () => {
it("should return the subcategory if the fault code is in the map", () => {
const result = getSubCategoryFromFaultCode(mockCategories, "subcategory1");
expect(result).toStrictEqual({
value: "12345",
zendeskSubCategoryId: "313"
});
});

it("should return nullable if the fault code is not in the map or is empty string", () => {
const result = getSubCategoryFromFaultCode(mockCategories, "subcategory3");
expect(result).toStrictEqual(null);

const emptyResult = getSubCategoryFromFaultCode(mockCategories, "");
expect(emptyResult).toStrictEqual(null);
});
});
20 changes: 20 additions & 0 deletions ts/features/payments/checkout/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from "lodash";
import { ZendeskSubCategoriesMap } from "../../../../../definitions/content/ZendeskSubCategoriesMap";
import { Bundle } from "../../../../../definitions/pagopa/ecommerce/Bundle";
import { PaymentMethodManagementTypeEnum } from "../../../../../definitions/pagopa/ecommerce/PaymentMethodManagementType";
import { PaymentMethodResponse } from "../../../../../definitions/pagopa/ecommerce/PaymentMethodResponse";
Expand Down Expand Up @@ -78,3 +79,22 @@ export const isDueDateValid = (date: string): string | undefined => {
tenYearsFromNow.setFullYear(tenYearsFromNow.getFullYear() + YEARS_TO_EXPIRE);
return new Date(date) > tenYearsFromNow ? undefined : formattedDate;
};

export const getSubCategoryFromFaultCode = (
data: ZendeskSubCategoriesMap,
statusCode: string
) => {
// check if there is a subcategory array that includes passed element
const subcategoryKey = Object.keys(data.subcategories).find(key =>
data.subcategories[key].includes(statusCode)
);
// if there is, return the mapped subcategory with the zendesk category id
if (subcategoryKey) {
return {
value: subcategoryKey,
zendeskSubCategoryId: data.subcategoryId
};
}
// if not, return nullable
return null;
};
4 changes: 4 additions & 0 deletions ts/features/zendesk/analytics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { zendeskEnabled } from "../../../config";
import { getNetworkErrorMessage } from "../../../utils/errors";
import {
getZendeskConfig,
getZendeskPaymentConfig,
zendeskSelectedCategory,
zendeskSupportCancel,
zendeskSupportCompleted,
Expand All @@ -22,6 +23,8 @@ const trackZendesk =
case getType(zendeskSupportCancel):
case getType(getZendeskConfig.request):
case getType(getZendeskConfig.success):
case getType(getZendeskPaymentConfig.success):
case getType(getZendeskPaymentConfig.request):
return mp.track(action.type);
case getType(zendeskSupportStart):
return mp.track(action.type, {
Expand All @@ -38,6 +41,7 @@ const trackZendesk =
category: action.payload.value
});
case getType(getZendeskConfig.failure):
case getType(getZendeskPaymentConfig.failure):
return mp.track(action.type, {
reason: getNetworkErrorMessage(action.payload)
});
Expand Down
10 changes: 9 additions & 1 deletion ts/features/zendesk/saga/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
zendeskStartPolling,
zendeskSupportCompleted,
zendeskSupportStart,
getZendeskToken
getZendeskToken,
getZendeskPaymentConfig
} from "../store/actions";
import { ContentClient } from "../../../api/content";
import { dismissSupport } from "../../../utils/supportAssistance";
Expand All @@ -33,6 +34,7 @@ import { isDevEnv } from "./../../../utils/environment";
import { zendeskSupport } from "./orchestration";
import { handleGetZendeskConfig } from "./networking/handleGetZendeskConfig";
import { handleHasOpenedTickets } from "./networking/handleHasOpenedTickets";
import { handleGetZendeskPaymentConfig } from "./networking/handleGetZendeskPaymentConfig";

const ZENDESK_GET_SESSION_POLLING_INTERVAL = ((isDevEnv ? 10 : 60) *
1000) as Millisecond;
Expand Down Expand Up @@ -89,6 +91,12 @@ export function* watchZendeskSupportSaga() {
contentClient.getZendeskConfig
);

yield* takeLatest(
getZendeskPaymentConfig.request,
handleGetZendeskPaymentConfig,
contentClient.getZendeskPaymentConfig
);

yield* takeLatest(zendeskRequestTicketNumber.request, handleHasOpenedTickets);
// close the Zendesk support UI when the identification is requested
// this is due since there is a modal clash (iOS only) see https://pagopa.atlassian.net/browse/IABT-1348?filter=10121
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { readableReport } from "@pagopa/ts-commons/lib/reporters";
import * as E from "fp-ts/lib/Either";
import { call, put } from "typed-redux-saga/macro";
import { ContentClient } from "../../../../api/content";
import { SagaCallReturnType } from "../../../../types/utils";
import { getGenericError, getNetworkError } from "../../../../utils/errors";
import { getZendeskPaymentConfig } from "../../store/actions";

// retrieve the zendesk config from the CDN
export function* handleGetZendeskPaymentConfig(
getZendeskPaymentConfigClient: ReturnType<
typeof ContentClient
>["getZendeskPaymentConfig"]
) {
try {
const getZendeskPaymentConfigResult: SagaCallReturnType<
typeof getZendeskPaymentConfigClient
> = yield* call(getZendeskPaymentConfigClient);
if (E.isRight(getZendeskPaymentConfigResult)) {
if (getZendeskPaymentConfigResult.right.status === 200) {
yield* put(
getZendeskPaymentConfig.success(
getZendeskPaymentConfigResult.right.value
)
);
} else {
yield* put(
getZendeskPaymentConfig.failure(
getGenericError(
Error(
`response status ${getZendeskPaymentConfigResult.right.status}`
)
)
)
);
}
} else {
getZendeskPaymentConfig.failure(
getGenericError(
Error(readableReport(getZendeskPaymentConfigResult.left))
)
);
}
} catch (e) {
yield* put(getZendeskPaymentConfig.failure(getNetworkError(e)));
}
}
2 changes: 2 additions & 0 deletions ts/features/zendesk/screens/ZendeskSupportHelpCenter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import ZENDESK_ROUTES from "../navigation/routes";
import {
ZendeskStartPayload,
getZendeskConfig,
getZendeskPaymentConfig,
getZendeskToken,
zendeskSupportCancel
} from "../store/actions";
Expand Down Expand Up @@ -330,6 +331,7 @@ const ZendeskSupportHelpCenter = () => {
*/
useEffect(() => {
dispatch(getZendeskConfig.request());
dispatch(getZendeskPaymentConfig.request());
}, [dispatch]);

// add the signatureRequestId to the ticket custom fields
Expand Down
13 changes: 12 additions & 1 deletion ts/features/zendesk/store/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
import { Zendesk } from "../../../../../definitions/content/Zendesk";
import { ZendeskCategory } from "../../../../../definitions/content/ZendeskCategory";
import { ZendeskSubCategory } from "../../../../../definitions/content/ZendeskSubCategory";
import { ZendeskSubcategoriesErrors } from "../../../../../definitions/content/ZendeskSubcategoriesErrors";
import {
ContextualHelpProps,
ContextualHelpPropsMarkdown
Expand Down Expand Up @@ -95,6 +96,15 @@ export const getZendeskConfig = createAsyncAction(
"ZENDESK_CONFIG_FAILURE"
)<void, Zendesk, NetworkError>();

/**
* Request the zendesk payment config
*/
export const getZendeskPaymentConfig = createAsyncAction(
"ZENDESK_PAYMENT_CONFIG_REQUEST",
"ZENDESK_PAYMENT_CONFIG_SUCCESS",
"ZENDESK_PAYMENT_CONFIG_FAILURE"
)<void, ZendeskSubcategoriesErrors, NetworkError>();

// user selected a category
export const zendeskSelectedCategory = createStandardAction(
"ZENDESK_SELECTED_CATEGORY"
Expand Down Expand Up @@ -127,4 +137,5 @@ export type ZendeskSupportActions =
| ActionType<typeof getZendeskConfig>
| ActionType<typeof zendeskSelectedCategory>
| ActionType<typeof zendeskRequestTicketNumber>
| ActionType<typeof zendeskSelectedSubcategory>;
| ActionType<typeof zendeskSelectedSubcategory>
| ActionType<typeof getZendeskPaymentConfig>;
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ import { ZendeskState } from "../index";

const INITIAL_STATE: ZendeskState = {
zendeskConfig: remoteUndefined,
ticketNumber: pot.none
ticketNumber: pot.none,
zendeskSubcategoriesErrorMap: remoteUndefined
};

const mockCategory: ZendeskCategory = {
Expand Down
Loading
Loading