diff --git a/package-lock.json b/package-lock.json index a48e39f..80f0b7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "paypal-commercetools-client", - "version": "0.0.62", + "version": "0.0.63", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "paypal-commercetools-client", - "version": "0.0.62", + "version": "0.0.63", "license": "MIT", "dependencies": { "@heroicons/react": "^1.0.6", diff --git a/package.json b/package.json index 4ed8256..3459efe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "paypal-commercetools-client", - "version": "0.0.62", + "version": "0.0.63", "private": false, "type": "module", "license": "MIT", diff --git a/src/App.tsx b/src/App.tsx index a15b53f..cfc638d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -14,7 +14,7 @@ import { PayUponInvoiceProps } from "./types"; const CC_FRONTEND_EXTENSION_VERSION: string = "devmajidabbasi"; const FRONTASTIC_SESSION: string = - "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYXJ0SWQiOiJiNTQzNjU3MS04MDk2LTQ3ZmUtYjkzZC1lYWExNDgxZTExYTYiLCJhY2NvdW50Ijp7ImFjY291bnRJZCI6ImYyMmE0ZmUzLWMyYjgtNDgwMS04MjA4LTQxNGQyMDYyMGUwYiIsImVtYWlsIjoibWFqaWQuYWJiYXNpQG1lZGlhb3B0LmRlIiwic2FsdXRhdGlvbiI6IiIsImZpcnN0TmFtZSI6Ik1hamlkIiwibGFzdE5hbWUiOiJBYmJhc2kiLCJiaXJ0aGRheSI6IjE5ODktMDMtMDVUMDA6MDA6MDAuMDAwWiIsImNvbmZpcm1lZCI6dHJ1ZSwiYWRkcmVzc2VzIjpbeyJhZGRyZXNzSWQiOiJqYlRKWG0zTSIsImZpcnN0TmFtZSI6Ik1hamlkIiwibGFzdE5hbWUiOiJBYmJhc2kiLCJzdHJlZXROYW1lIjoiSG9jaHN0cmFcdTAwZGZlIDM3Iiwic3RyZWV0TnVtYmVyIjoiSG9jaHN0cmFcdTAwZGZlIDM3IiwicG9zdGFsQ29kZSI6IjEzMzU3IiwiY2l0eSI6IkRFIiwiY291bnRyeSI6IkRFIiwicGhvbmUiOiI1OTkzNTc1NjIiLCJpc0RlZmF1bHRCaWxsaW5nQWRkcmVzcyI6ZmFsc2UsImlzRGVmYXVsdFNoaXBwaW5nQWRkcmVzcyI6ZmFsc2V9LHsiYWRkcmVzc0lkIjoia3J6UjdtMFEiLCJmaXJzdE5hbWUiOiJNYWppZCIsImxhc3ROYW1lIjoiQWJiYXNpIiwic3RyZWV0TmFtZSI6IkNvdW50eSBTdC4gTWlhbWkiLCJzdHJlZXROdW1iZXIiOiI0MzIiLCJwb3N0YWxDb2RlIjoiMzMwMTgiLCJjaXR5IjoiVVMiLCJjb3VudHJ5IjoiREUiLCJwaG9uZSI6IjU5OTM1NzU2MiIsImlzRGVmYXVsdEJpbGxpbmdBZGRyZXNzIjp0cnVlLCJpc0RlZmF1bHRTaGlwcGluZ0FkZHJlc3MiOnRydWV9XX0sIndpc2hsaXN0SWQiOiJjZTM4MzVkMC02OWM0LTRlZWUtYTBjZC1hM2I4NjgyYTU2OTUifQ.A6UsbrTRFH1eKM6egNJQvnTckM4nBPU_N-sFIRcR9j0"; + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3aXNobGlzdElkIjoiY2UzODM1ZDAtNjljNC00ZWVlLWEwY2QtYTNiODY4MmE1Njk1IiwiY2FydElkIjoiYzBkYzNjODctZTAyYS00ZjczLThhZDItNzMwNzZmYjYyZmQyIiwiYWNjb3VudCI6eyJhY2NvdW50SWQiOiJmMjJhNGZlMy1jMmI4LTQ4MDEtODIwOC00MTRkMjA2MjBlMGIiLCJlbWFpbCI6Im1hamlkLmFiYmFzaUBtZWRpYW9wdC5kZSIsInNhbHV0YXRpb24iOiIiLCJmaXJzdE5hbWUiOiJNYWppZCIsImxhc3ROYW1lIjoiQWJiYXNpIiwiYmlydGhkYXkiOiIxOTg5LTAzLTA1VDAwOjAwOjAwLjAwMFoiLCJjb25maXJtZWQiOnRydWUsImFkZHJlc3NlcyI6W3siYWRkcmVzc0lkIjoiamJUSlhtM00iLCJmaXJzdE5hbWUiOiJNYWppZCIsImxhc3ROYW1lIjoiQWJiYXNpIiwic3RyZWV0TmFtZSI6IkhvY2hzdHJhXHUwMGRmZSAzNyIsInN0cmVldE51bWJlciI6IkhvY2hzdHJhXHUwMGRmZSAzNyIsInBvc3RhbENvZGUiOiIxMzM1NyIsImNpdHkiOiJERSIsImNvdW50cnkiOiJERSIsInBob25lIjoiNTk5MzU3NTYyIiwiaXNEZWZhdWx0QmlsbGluZ0FkZHJlc3MiOmZhbHNlLCJpc0RlZmF1bHRTaGlwcGluZ0FkZHJlc3MiOmZhbHNlfSx7ImFkZHJlc3NJZCI6ImtyelI3bTBRIiwiZmlyc3ROYW1lIjoiTWFqaWQiLCJsYXN0TmFtZSI6IkFiYmFzaSIsInN0cmVldE5hbWUiOiJDb3VudHkgU3QuIE1pYW1pIiwic3RyZWV0TnVtYmVyIjoiNDMyIiwicG9zdGFsQ29kZSI6IjMzMDE4IiwiY2l0eSI6IlVTIiwiY291bnRyeSI6IkRFIiwicGhvbmUiOiI1OTkzNTc1NjIiLCJpc0RlZmF1bHRCaWxsaW5nQWRkcmVzcyI6dHJ1ZSwiaXNEZWZhdWx0U2hpcHBpbmdBZGRyZXNzIjp0cnVlfV19fQ.6peiQLu6IWhokMswLh_uYz1YY_ergD0tOvRCiO-e1fk"; function App() { const [choosenPaymentMethod, setChoosenPaymentMethod] = useState(""); diff --git a/src/app/usePayment.tsx b/src/app/usePayment.tsx index 088e0b1..a8eebc5 100644 --- a/src/app/usePayment.tsx +++ b/src/app/usePayment.tsx @@ -21,6 +21,7 @@ import { CustomOrderData, ApproveVaultSetupTokenData, CreateInvoiceData, + OrderDataLinks, } from "../types"; import { createPayment, @@ -58,19 +59,20 @@ type PaymentContextT = { handleCreateOrder: (orderData?: CustomOrderData) => Promise; handleOnApprove: (data: CustomOnApproveData) => Promise; vaultOnly: boolean; - + oderDataLinks?: OrderDataLinks; handleCreateVaultSetupToken: ( - paymentSource: FUNDING_SOURCE, + paymentSource: FUNDING_SOURCE ) => Promise; handleApproveVaultSetupToken: ( - data: ApproveVaultSetupTokenData, + data: ApproveVaultSetupTokenData ) => Promise; + orderId?: string; }; const setRelevantData = ( orderData?: CustomOrderData, isInvoice?: boolean, - enableVaulting?: boolean, + enableVaulting?: boolean ) => { if (isInvoice) { return orderData as CreateInvoiceData; @@ -94,6 +96,8 @@ const PaymentContext = createContext({ Promise.resolve(""), handleApproveVaultSetupToken: (data?: ApproveVaultSetupTokenData) => Promise.resolve(), + oderDataLinks: undefined, + orderId: undefined, }); export const PaymentProvider: FC< @@ -121,12 +125,14 @@ export const PaymentProvider: FC< const [showResult, setShowResult] = useState(false); const [resultSuccess, setResultSuccess] = useState(); const [resultMessage, setResultMessage] = useState(); + const [oderDataLinks, setOderDataLinks] = useState(); + const [orderId, setOrderId] = useState(); const { settings } = useSettings(); const { t } = useTranslation(); const [paymentInfo, setPaymentInfo] = useState( - PaymentInfoInitialObject, + PaymentInfoInitialObject ); const { isLoading } = useLoader(); @@ -148,14 +154,14 @@ export const PaymentProvider: FC< }; const handleCreateVaultSetupToken = async ( - paymentSource: FUNDING_SOURCE, + paymentSource: FUNDING_SOURCE ) => { if (!createVaultSetupTokenUrl) return ""; const createVaultSetupTokenResult = await createVaultSetupToken( requestHeader, createVaultSetupTokenUrl, - paymentSource, + paymentSource ); return createVaultSetupTokenResult @@ -170,7 +176,7 @@ export const PaymentProvider: FC< const result = await approveVaultSetupToken( requestHeader, approveVaultSetupTokenUrl, - vaultSetupToken, + vaultSetupToken ); if (result) { setShowResult(true); @@ -188,7 +194,7 @@ export const PaymentProvider: FC< const relevantOrderData = setRelevantData( orderData, !!setRatepayMessage, - enableVaulting, + enableVaulting ); const createOrderResult = await createOrder( @@ -198,12 +204,12 @@ export const PaymentProvider: FC< latestPaymentVersion, { ...relevantOrderData, - }, + } ); if (createOrderResult) { const { orderData, paymentVersion } = createOrderResult; - const { id, status, payment_source, details } = orderData; + const { id, status, payment_source, details, links } = orderData; latestPaymentVersion = paymentVersion; if (setRatepayMessage) { if (paymentVersion) @@ -232,6 +238,15 @@ export const PaymentProvider: FC< setResultSuccess(true); purchaseCallback(orderData); return ""; + } else if ( + status === "PAYER_ACTION_REQUIRED" && + payment_source && + links + ) { + setPaymentInfo({ ...paymentInfo, version: paymentVersion }); + setOderDataLinks(links); + setOrderId(id); + return ""; } else { return id; } @@ -241,6 +256,7 @@ export const PaymentProvider: FC< const handleOnApprove = async (data: CustomOnApproveData) => { if (!onApproveUrl && !authorizeOrderUrl) return; + isLoading(true); const { orderID, saveCard } = data; @@ -257,7 +273,7 @@ export const PaymentProvider: FC< paymentInfo.id, latestPaymentVersion, orderID, - saveCard, + saveCard ); const { orderData } = onApproveResult as OnApproveResponse; @@ -272,6 +288,7 @@ export const PaymentProvider: FC< setResultMessage(orderData.message); } } + isLoading(false); }; const handleCreatePayment = async () => { @@ -282,7 +299,7 @@ export const PaymentProvider: FC< requestHeader, createPaymentUrl, cartInformation, - shippingMethodId, + shippingMethodId )) as CreatePaymentResponse; if (!createPaymentResult) { @@ -298,7 +315,7 @@ export const PaymentProvider: FC< getClientTokenUrl, createPaymentResult.id, createPaymentResult.version, - createPaymentResult.braintreeCustomerId, + createPaymentResult.braintreeCustomerId )) as ClientTokenResponse; setClientToken(clientTokenResult.clientToken); paymentVersion = clientTokenResult.paymentVersion; @@ -335,6 +352,8 @@ export const PaymentProvider: FC< vaultOnly, handleCreateVaultSetupToken, handleApproveVaultSetupToken, + oderDataLinks, + orderId, }; }, [ paymentInfo, @@ -349,6 +368,8 @@ export const PaymentProvider: FC< settings, createVaultSetupTokenUrl, approveVaultSetupTokenUrl, + oderDataLinks, + orderId, ]); return ( diff --git a/src/app/useSettings.tsx b/src/app/useSettings.tsx index 0d59c1e..9280310 100644 --- a/src/app/useSettings.tsx +++ b/src/app/useSettings.tsx @@ -15,6 +15,7 @@ import { PaymentTokens, } from "../types"; import { getSettings, getUserInfo, removePaymentToken } from "../services"; +import { useLoader } from "./useLoader"; type SettingsContextT = { handleGetSettings: () => void; @@ -43,9 +44,12 @@ export const SettingsProvider: FC< const [settings, setSettings] = useState(); const [userIdToken, setUserIdToken] = useState(); const [paymentTokens, setPaymentTokens] = useState(); + const { isLoading } = useLoader(); const value = useMemo(() => { const handleGetSettings = async () => { + isLoading(true); + if (getUserInfoUrl && !userIdToken) { const { userIdToken, paymentTokens } = (await getUserInfo( requestHeader, @@ -64,8 +68,10 @@ export const SettingsProvider: FC< setSettings(getSettingsResult); } + isLoading(false); }; const handleRemovePaymentToken = async (paymentTokenId: string) => { + isLoading(true); if (removePaymentTokenUrl) { await removePaymentToken( requestHeader, @@ -81,6 +87,7 @@ export const SettingsProvider: FC< setPaymentTokens({ ...paymentTokens }); } } + isLoading(false); }; return { diff --git a/src/components/HostedFields/HostedFieldsMask.tsx b/src/components/HostedFields/HostedFieldsMask.tsx index f81d80f..b9b887d 100644 --- a/src/components/HostedFields/HostedFieldsMask.tsx +++ b/src/components/HostedFields/HostedFieldsMask.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useRef, useState } from "react"; +import React, { useMemo, useEffect, useState } from "react"; import { PayPalHostedField, @@ -21,7 +21,8 @@ export const HostedFieldsMask: React.FC = ({ options, enableVaulting, }) => { - const { handleCreateOrder } = usePayment(); + const { handleCreateOrder, oderDataLinks, handleOnApprove, orderId } = + usePayment(); const { settings, paymentTokens } = useSettings(); const { clientToken } = usePayment(); const [addNew, setAddNew] = useState(false); @@ -41,6 +42,33 @@ export const HostedFieldsMask: React.FC = ({ return { hostedFieldsPayButtonClasses, hostedFieldsInputFieldClasses }; }, [settings]); + useEffect(() => { + let oderDataPayerAction = oderDataLinks?.filter( + (oderDataLink) => oderDataLink.rel === "payer-action" + ); + + if (oderDataPayerAction && oderDataPayerAction[0]) { + const newWindow = window.open( + oderDataPayerAction[0].href, + "3D Secure Check", + "width=300,height=500" + ); + let fireOderDataGetInterval: NodeJS.Timer; + const fireOderDataGet = async () => { + if (newWindow?.closed) { + clearInterval(fireOderDataGetInterval); + if (orderId) { + await handleOnApprove({ orderID: orderId }); + } + } + }; + + if (newWindow) { + fireOderDataGetInterval = setInterval(fireOderDataGet, 1000); + } + } + }, [oderDataLinks, orderId]); + return !settings ? ( <> ) : ( @@ -80,19 +108,25 @@ export const HostedFieldsMask: React.FC = ({ ); })} {vaultId && ( -
- +
+

+ If the 3D secure popup appears, you need to do the verification + and then close the window. +

+
+ +
)} diff --git a/src/components/RenderTemplate/RenderTemplate.tsx b/src/components/RenderTemplate/RenderTemplate.tsx index 900d598..fa195d3 100644 --- a/src/components/RenderTemplate/RenderTemplate.tsx +++ b/src/components/RenderTemplate/RenderTemplate.tsx @@ -34,15 +34,15 @@ export const RenderTemplate: FC< children, }) => { return ( - - - + + + {children} - - - + + + ); }; diff --git a/src/types/index.ts b/src/types/index.ts index 81ad81f..115afc9 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -58,6 +58,7 @@ export type CreateOrderResponse = { type: string; }; }; + links?: OrderDataLinks; }; paymentVersion: number; }; @@ -393,3 +394,11 @@ export type SettingsProviderProps = { }; export type RemovePaymentTokenRequest = { paymentTokenId: string }; + +type OrderDataLink = { + href: string; + rel: string; + method: string; +}; + +export type OrderDataLinks = OrderDataLink[];