diff --git a/packages/react-kit/src/components/modal/components/Redeem/Confirmation/Confirmation.tsx b/packages/react-kit/src/components/modal/components/Redeem/Confirmation/Confirmation.tsx index 6b079e5b0..bd6b3ff20 100644 --- a/packages/react-kit/src/components/modal/components/Redeem/Confirmation/Confirmation.tsx +++ b/packages/react-kit/src/components/modal/components/Redeem/Confirmation/Confirmation.tsx @@ -371,13 +371,13 @@ ${FormModel.formFields.phone.placeholder}: ${phoneField.value}`; console.error("Error while redeeming", error); // call postRedemptionSubmitted if error before the transaction is submitted OR postRedemptionConfirmed if error after if (isTxPending) { - postRedemptionSubmitted?.({ + postRedemptionConfirmed?.({ redemptionInfo, isError: true, error: { ...error } }); } else { - postRedemptionConfirmed?.({ + postRedemptionSubmitted?.({ redemptionInfo, isError: true, error: { ...error } diff --git a/packages/react-kit/src/components/modal/components/Redeem/Confirmation/ConfirmationView.tsx b/packages/react-kit/src/components/modal/components/Redeem/Confirmation/ConfirmationView.tsx index 7cf9cd6d6..8d86ec8a9 100644 --- a/packages/react-kit/src/components/modal/components/Redeem/Confirmation/ConfirmationView.tsx +++ b/packages/react-kit/src/components/modal/components/Redeem/Confirmation/ConfirmationView.tsx @@ -5,6 +5,7 @@ import Confirmation, { ConfirmationProps } from "./Confirmation"; import { NonModalProps, useNonModalContext } from "../../../nonModal/NonModal"; import Typography from "../../../../ui/Typography"; import { theme } from "../../../../../theme"; +import { useAccount } from "wagmi"; const colors = theme.colors.light; @@ -29,6 +30,7 @@ export function ConfirmationView({ ? getAddress(exchange.seller.assistant) : ""; const dispatch = useNonModalContext(); + const { address } = useAccount(); useEffect(() => { dispatch({ payload: { @@ -45,7 +47,13 @@ export function ConfirmationView({ }, [dispatch]); return ( <> - {exchange ? ( + {!exchange ? ( +

Exchange could not be retrieved.

+ ) : exchange.buyer?.wallet?.toLowerCase() !== address?.toLowerCase() ? ( +

You do not own this exchange.

+ ) : exchange.state !== "COMMITTED" ? ( +

Invalid exchange state.

+ ) : ( - ) : ( -

Exchange could not be retrieved

)} ); diff --git a/packages/react-kit/src/components/widgets/redemption/RedemptionWidget.tsx b/packages/react-kit/src/components/widgets/redemption/RedemptionWidget.tsx index f63edaf47..718208087 100644 --- a/packages/react-kit/src/components/widgets/redemption/RedemptionWidget.tsx +++ b/packages/react-kit/src/components/widgets/redemption/RedemptionWidget.tsx @@ -38,7 +38,7 @@ type RedemptionProps = { type WidgetProps = RedemptionProps & IpfsProviderProps & Omit & - RedemptionContextProps & + Omit & EnvironmentProviderProps & ConvertionRateProviderProps & Omit; diff --git a/packages/react-kit/src/components/widgets/redemption/provider/RedemptionProvider.tsx b/packages/react-kit/src/components/widgets/redemption/provider/RedemptionProvider.tsx index 9c4db3175..02a59f42a 100644 --- a/packages/react-kit/src/components/widgets/redemption/provider/RedemptionProvider.tsx +++ b/packages/react-kit/src/components/widgets/redemption/provider/RedemptionProvider.tsx @@ -4,7 +4,7 @@ import { RedemptionContext, RedemptionContextProps } from "./RedemptionContext"; export function RedemptionProvider({ children, ...rest -}: RedemptionContextProps & { children: ReactNode }) { +}: Omit & { children: ReactNode }) { const [widgetAction, setWidgetAction] = useState(rest.widgetAction); return ( { + try { + const response = await fetch(input, init); + const isTrueOrOkOrYes = (s: string) => + !!s && ["true", "ok", "yes"].includes(s.toLowerCase()); + let responseBody; + try { + responseBody = await response.json(); + } catch {} + const accepted = + response.ok && isTrueOrOkOrYes(responseBody?.accepted?.toString()); + if (!accepted) { + throw new Error(responseBody?.reason?.toString() || response.statusText); + } + return { + accepted, + resume: + step !== "deliveryInfo" + ? undefined + : isTrueOrOkOrYes(responseBody?.resume?.toString()), + reason: "" + }; + } catch (error) { + console.error(`An error happened when posting ${step}: ${error}`); + return { + accepted: false, + reason: (error as Error).toString(), + resume: step !== "deliveryInfo" ? undefined : false + }; + } +} + function postDeliveryInfoCallback( postDeliveryInfoUrl: string, postDeliveryInfoHeaders: { [key: string]: string } | undefined @@ -60,43 +110,18 @@ function postDeliveryInfoCallback( const signature = signer ? await signer.signMessage(JSON.stringify(message)) : undefined; - try { - const response = await fetch(postDeliveryInfoUrl, { - method: "POST", - body: JSON.stringify({ - message, - signature - }), - headers: { - "content-type": "application/json;charset=UTF-8", - ...postDeliveryInfoHeaders - } - }); - const isTrueOrOkOrYes = (s: string) => - !!s && ["true", "ok", "yes"].includes(s.toLowerCase()); - let responseBody; - try { - responseBody = await response.json(); - } catch {} - const accepted = - response.ok && isTrueOrOkOrYes(responseBody?.accepted?.toString()); - const resume = - accepted && isTrueOrOkOrYes(responseBody?.resume?.toString()); - const reason = accepted - ? "" - : responseBody?.reason?.toString() || response.statusText; - return { - accepted, - resume, - reason - }; - } catch (error) { - return { - accepted: false, - reason: (error as Error).toString(), - resume: false - }; - } + return fetchAndReadResponse("deliveryInfo", postDeliveryInfoUrl, { + method: "POST", + body: JSON.stringify({ + step: "deliveryInfo", + message, + signature + }), + headers: { + "content-type": "application/json;charset=UTF-8", + ...postDeliveryInfoHeaders + } + }) as Promise; }; } @@ -104,21 +129,29 @@ function postRedemptionSubmittedCallback( postRedemptionSubmittedUrl: string, postRedemptionSubmittedHeaders: { [key: string]: string } | undefined ) { - return async (message: RedeemTransactionSubmittedMessage) => { + return async ( + message: RedeemTransactionSubmittedMessage + ): Promise => { if (!postRedemptionSubmittedUrl) { throw new Error( "[postRedemptionSubmittedCallback] postRedemptionSubmittedUrl is not defined" ); } - // TODO: get response from server and throw exception in case of an error - await fetch(postRedemptionSubmittedUrl, { - method: "POST", - body: JSON.stringify(message), - headers: { - "content-type": "application/json;charset=UTF-8", - ...postRedemptionSubmittedHeaders + return fetchAndReadResponse( + "redemptionSubmitted", + postRedemptionSubmittedUrl, + { + method: "POST", + body: JSON.stringify({ + step: "redemptionSubmitted", + message + }), + headers: { + "content-type": "application/json;charset=UTF-8", + ...postRedemptionSubmittedHeaders + } } - }); + ) as Promise; }; } @@ -126,21 +159,29 @@ function postRedemptionConfirmedCallback( postRedemptionConfirmedUrl: string, postRedemptionConfirmedHeaders: { [key: string]: string } | undefined ) { - return async (message: RedeemTransactionConfirmedMessage) => { + return async ( + message: RedeemTransactionConfirmedMessage + ): Promise => { if (!postRedemptionConfirmedUrl) { throw new Error( - "[postDeliveryInfoCallback] postDeliveryInfoUrl is not defined" + "[postRedemptionConfirmedCallback] postRedemptionConfirmedUrl is not defined" ); } - // TODO: get response from server and throw exception in case of an error - await fetch(postRedemptionConfirmedUrl, { - method: "POST", - body: JSON.stringify(message), - headers: { - "content-type": "application/json;charset=UTF-8", - ...postRedemptionConfirmedHeaders + return fetchAndReadResponse( + "redemptionConfirmed", + postRedemptionConfirmedUrl, + { + method: "POST", + body: JSON.stringify({ + step: "redemptionConfirmed", + message + }), + headers: { + "content-type": "application/json;charset=UTF-8", + ...postRedemptionConfirmedHeaders + } } - }); + ) as Promise; }; } diff --git a/packages/react-kit/src/stories/widgets/Redemption.stories.tsx b/packages/react-kit/src/stories/widgets/Redemption.stories.tsx index c7c14068b..b47cd0074 100644 --- a/packages/react-kit/src/stories/widgets/Redemption.stories.tsx +++ b/packages/react-kit/src/stories/widgets/Redemption.stories.tsx @@ -95,7 +95,7 @@ RedemptionCallbacksRedeemConfirm.args = { ...Redemption.args, showRedemptionOverview: false, widgetAction: RedemptionWidgetAction.CONFIRM_REDEEM, - exchangeId: "133", + exchangeId: "149", deliveryInfo: process.env.REACT_APP_DELIVERY_ADDRESS_MOCK ? JSON.parse(process.env.REACT_APP_DELIVERY_ADDRESS_MOCK) : undefined, @@ -123,8 +123,56 @@ RedemptionCallbacksFailure2.args = { ...Redemption.args, showRedemptionOverview: false, widgetAction: RedemptionWidgetAction.REDEEM_FORM, - exchangeId: "133", + exchangeId: "149", postDeliveryInfoUrl: "http://localhost:3666/fail2", postRedemptionSubmittedUrl: "http://localhost:3666/submitted", postRedemptionConfirmedUrl: "http://localhost:3666/confirmed" }; + +export const RedemptionCallbacksFailure3: ComponentStory< + typeof RedemptionWidget +> = Template.bind({}); + +RedemptionCallbacksFailure3.args = { + ...Redemption.args, + showRedemptionOverview: false, + widgetAction: RedemptionWidgetAction.CONFIRM_REDEEM, + exchangeId: "149", + deliveryInfo: { + name: "TOTO", + streetNameAndNumber: "1 grand place", + city: "LILLE", + state: "NORD", + zip: "59000", + country: "FR", + email: "toto@mail.com", + phone: "+33123456789" + }, + postDeliveryInfoUrl: "http://localhost:3666/deliveryInfo", + postRedemptionSubmittedUrl: "http://localhost:3666/fail3", + postRedemptionConfirmedUrl: "http://localhost:3666/confirmed" +}; + +export const RedemptionCallbacksFailure4: ComponentStory< + typeof RedemptionWidget +> = Template.bind({}); + +RedemptionCallbacksFailure4.args = { + ...Redemption.args, + showRedemptionOverview: false, + widgetAction: RedemptionWidgetAction.CONFIRM_REDEEM, + exchangeId: "149", + deliveryInfo: { + name: "TOTO", + streetNameAndNumber: "1 grand place", + city: "LILLE", + state: "NORD", + zip: "59000", + country: "FR", + email: "toto@mail.com", + phone: "+33123456789" + }, + postDeliveryInfoUrl: "http://localhost:3666/deliveryInfo", + postRedemptionSubmittedUrl: "http://localhost:3666/submitted", + postRedemptionConfirmedUrl: "http://localhost:3666/fail4" +};