diff --git a/.env b/.env new file mode 100644 index 000000000..29dda11a9 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +ENV_BACKEND_URL="" +ENV_LOGGING_URL="" diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5816c0c0d..9924ef8f8 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,27 +4,27 @@ docs/ @akash-c-k LICENSE @akash-c-k README.md @akash-c-k SECURITY.md @akash-c-k -.gitignore @prafulkoppalkar @ArushKapoorJuspay +.gitignore @prafulkoppalkar @ArushKapoorJuspay @PritishBudhiraja -Hyperswitch-React-Demo-App/ @prafulkoppalkar @JeevaRamu0104 +Hyperswitch-React-Demo-App/ @prafulkoppalkar @JeevaRamu0104 @PritishBudhiraja Hyperswitch-React-Demo-App/Dockerfile @JeevaRamu0104 @prafulkoppalkar Hyperswitch-React-Demo-App/Makefile @JeevaRamu0104 @prafulkoppalkar -Hyperswitch-React-Demo-App/promptScript.js @prafulkoppalkar @ArushKapoorJuspay -Hyperswitch-React-Demo-App/webpack.common.js @prafulkoppalkar @ArushKapoorJuspay -Hyperswitch-React-Demo-App/webpack.dev.js @prafulkoppalkar @ArushKapoorJuspay +Hyperswitch-React-Demo-App/promptScript.js @prafulkoppalkar @ArushKapoorJuspay @PritishBudhiraja +Hyperswitch-React-Demo-App/webpack.common.js @prafulkoppalkar @ArushKapoorJuspay @PritishBudhiraja +Hyperswitch-React-Demo-App/webpack.dev.js @prafulkoppalkar @ArushKapoorJuspay @PritishBudhiraja -.husky/ @harsh-Juspay @prafulkoppalkar @PritishBudhiraja -.github/ @harsh-Juspay @prafulkoppalkar @PritishBudhiraja +.husky/ @prafulkoppalkar @PritishBudhiraja +.github/ @prafulkoppalkar @PritishBudhiraja aws/ @prafulkoppalkar -webpack.dev.js @prafulkoppalkar @ArushKapoorJuspay -webpack.common.js @prafulkoppalkar @ArushKapoorJuspay +webpack.dev.js @prafulkoppalkar @ArushKapoorJuspay @PritishBudhiraja +webpack.common.js @prafulkoppalkar @ArushKapoorJuspay @PritishBudhiraja src/orca-log-catcher @vsrivatsa-juspay @prafulkoppalkar src/orca-loader @prafulkoppalkar -src/LoaderConroller.res @prafulkoppalkar -src/Utilities @prafulkoppalkar -src/libraries @prafulkoppalkar -src/Hooks @prafulkoppalkar -src/Payment.res @prafulkoppalkar +src/LoaderConroller.res @prafulkoppalkar @PritishBudhiraja +src/Utilities @prafulkoppalkar @PritishBudhiraja +src/libraries @prafulkoppalkar @PritishBudhiraja +src/Hooks @prafulkoppalkar @PritishBudhiraja +src/Payment.res @prafulkoppalkar @PritishBudhiraja diff --git a/CHANGELOG.md b/CHANGELOG.md index 82f92550e..725aa586c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [0.50.1](https://github.com/juspay/hyperswitch-web/compare/v0.50.0...v0.50.1) (2024-05-06) + +# [0.50.0](https://github.com/juspay/hyperswitch-web/compare/v0.49.2...v0.50.0) (2024-05-06) + + +### Features + +* hideExpiredPaymentMethods prop added ([#350](https://github.com/juspay/hyperswitch-web/issues/350)) ([f2a8c42](https://github.com/juspay/hyperswitch-web/commit/f2a8c4295953e8743ad9d5f2c4d7df9c3eba6a96)) + +## [0.49.2](https://github.com/juspay/hyperswitch-web/compare/v0.49.1...v0.49.2) (2024-05-06) + ## [0.49.1](https://github.com/juspay/hyperswitch-web/compare/v0.49.0...v0.49.1) (2024-05-03) diff --git a/README.md b/README.md index a1137a51e..77477bae7 100644 --- a/README.md +++ b/README.md @@ -85,12 +85,21 @@ Ways to get started with Hyperswitch: Before you start the local setup, you will need an understanding of few keys - -### About Env Configs +### About Env Configs for Demo App -- `HYPERSWITCH_PUBLISHABLE_KEY` - Publishable key of your Hyperswitch Account. This key will start with `pk_dev_` for local development, `pk_snd_` for sandbox and `pk_prd_` for production. -- `HYPERSWITCH_SECRET_KEY` - API key of your Hyperswitch Account -- `HYPERSWITCH_SERVER_URL` - URL of your hosted Hyperswitch Backend server or you can use our Sandbox URL (https://sandbox.hyperswitch.io) or use your backend running locally (eg. http://localhost:8080). -- `HYPERSWITCH_CLIENT_URL` - URL of your hosted Hyperswitch SDK or you can use our Sandbox URL (https://beta.hyperswitch.io/v1) or use your app running locally (eg http://localhost:9050). +- **`HYPERSWITCH_PUBLISHABLE_KEY`:** The publishable key of your Hyperswitch account. This key will start with `pk_dev_` for local development, `pk_snd_` for sandbox, and `pk_prd_` for production. + +- **`HYPERSWITCH_SECRET_KEY`:** The API key of your Hyperswitch account. + +- **`HYPERSWITCH_SERVER_URL`:** The URL of your hosted Hyperswitch backend server. Alternatively, you can use our Sandbox URL (https://sandbox.hyperswitch.io) or specify your backend running locally (e.g., http://localhost:8080). + +- **`HYPERSWITCH_CLIENT_URL`:** The URL of your hosted Hyperswitch SDK. You can also use our Sandbox URL (https://beta.hyperswitch.io/v1) or specify your app running locally (e.g., http://localhost:9050). + +### About Env Configs for SDK + +- **`ENV_BACKEND_URL`:** Sets the endpoint for all the APIs used within the SDK to interact with the backend service. If you are running your own backend service, you can configure and specify its endpoint here for local setups. + +- **`ENV_LOGGING_URL`:** Specifies a custom logging endpoint where logs generated by the SDK can be sent. This allows you to view and manage logs according to your requirements. ### Setup Node diff --git a/package-lock.json b/package-lock.json index 5284658bb..daaa7785c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "orca-payment-page", - "version": "0.49.1", + "version": "0.50.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "orca-payment-page", - "version": "0.49.1", + "version": "0.50.1", "hasInstallScript": true, "dependencies": { "@aws-sdk/client-cloudfront": "^3.414.0", diff --git a/package.json b/package.json index 94edbafaf..1e1fa5612 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "orca-payment-page", - "version": "0.49.1", + "version": "0.50.1", "main": "index.js", "private": true, "dependencies": { diff --git a/src/Components/DynamicFields.res b/src/Components/DynamicFields.res index 9c21af638..01eab66c6 100644 --- a/src/Components/DynamicFields.res +++ b/src/Components/DynamicFields.res @@ -59,7 +59,7 @@ let make = ( ~isAllStoredCardsHaveName, (), ) - ->DynamicFieldsUtils.updateDynamicFields(billingAddress, ()) + ->DynamicFieldsUtils.updateDynamicFields(billingAddress, ~list, ~paymentMethod, ~isSavedCardFlow, ()) ->Belt.SortArray.stableSortBy(PaymentMethodsRecord.sortPaymentMethodFields) //<...>// }, (requiredFields, isAllStoredCardsHaveName, isSavedCardFlow)) @@ -149,7 +149,7 @@ let make = ( cardRef, icon, cardError, - _, + setCardError, maxCardLength, ) = switch cardProps { | Some(cardProps) => cardProps @@ -165,7 +165,7 @@ let make = ( expiryRef, _, expiryError, - _, + setExpiryError, ) = switch expiryProps { | Some(expiryProps) => expiryProps | None => defaultExpiryProps @@ -181,7 +181,7 @@ let make = ( cvcRef, _, cvcError, - _, + setCvcError, ) = switch cvcProps { | Some(cvcProps) => cvcProps | None => defaultCvcProps @@ -276,9 +276,17 @@ let make = ( ~isSavedCardFlow, ~isAllStoredCardsHaveName, ~setRequiredFieldsBody, + ~list, ) - let submitCallback = DynamicFieldsUtils.useSubmitCallback() + let submitCallback = DynamicFieldsUtils.useSubmitCallback( + ~cardNumber, + ~setCardError, + ~cardExpiry, + ~setExpiryError, + ~cvcNumber, + ~setCvcError, + ) useSubmitPaymentData(submitCallback) let bottomElement = @@ -326,7 +334,7 @@ let make = ( key={`outside-billing-${index->Int.toString}`} className="flex flex-col w-full place-content-between" style={ReactDOMStyle.make( - ~marginTop=index !== 0 || paymentMethod === "card" ? themeObj.spacingGridColumn : "", + ~marginTop=index !== 0 ? themeObj.spacingGridColumn : "", ~gridColumnGap=themeObj.spacingGridRow, (), )}> diff --git a/src/Components/SavedCardItem.res b/src/Components/SavedCardItem.res index d1a629ed4..7c8b35938 100644 --- a/src/Components/SavedCardItem.res +++ b/src/Components/SavedCardItem.res @@ -12,6 +12,7 @@ let make = ( ~setRequiredFieldsBody, ) => { let {themeObj, config, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) + let {hideExpiredPaymentMethods} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) let (cardBrand, setCardBrand) = Recoil.useRecoilState(RecoilAtoms.cardBrand) let ( isCVCValid, @@ -48,8 +49,10 @@ let make = ( let isCard = paymentItem.paymentMethod === "card" let isRenderCvv = isCard && paymentItem.requiresCvv + let expiryMonth = paymentItem.card.expiryMonth + let expiryYear = paymentItem.card.expiryYear - let expiryDate = Date.fromString(`${paymentItem.card.expiryYear}-${paymentItem.card.expiryMonth}`) + let expiryDate = Date.fromString(`${expiryYear}-${expiryMonth}`) expiryDate->Date.setMonth(expiryDate->Date.getMonth + 1) let currentDate = Date.make() let isCardExpired = isCard && expiryDate < currentDate @@ -59,135 +62,135 @@ let make = ( | None => "debit" } - + + } diff --git a/src/Payment.res b/src/Payment.res index 8d15eec44..94b14daee 100644 --- a/src/Payment.res +++ b/src/Payment.res @@ -228,13 +228,6 @@ let make = (~paymentMode, ~integrateError, ~logger) => { checkCardExpiry(getCardElementValue(iframeId, "card-expiry")) | _ => true } - let cardNetwork = { - if cardBrand != "" { - [("card_network", cardNumber->CardUtils.getCardBrand->JSON.Encode.string)] - } else { - [] - } - } if validFormat { let body = switch paymentMode->getPaymentMode { | Card => @@ -246,7 +239,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => { ~year, ~cardHolderName="", ~cvcNumber, - ~cardBrand=cardNetwork, + ~cardBrand, (), ) | CardNumberElement => @@ -258,7 +251,7 @@ let make = (~paymentMode, ~integrateError, ~logger) => { ~year, ~cardHolderName="", ~cvcNumber=localCvcNumber, - ~cardBrand=cardNetwork, + ~cardBrand, (), ) | _ => [] diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res index 5fb573243..c9c95b2ee 100644 --- a/src/Payments/CardPayment.res +++ b/src/Payments/CardPayment.res @@ -15,58 +15,21 @@ let make = ( open Utils open UtilityHooks - let {config, themeObj, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) - let {innerLayout} = config.appearance + let {themeObj, localeString} = Recoil.useRecoilValueFromAtom(RecoilAtoms.configAtom) let options = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) let loggerState = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) let (nickname, setNickname) = React.useState(_ => "") - let ( - isCardValid, - setIsCardValid, - cardNumber, - changeCardNumber, - handleCardBlur, - cardRef, - icon, - cardError, - setCardError, - maxCardLength, - ) = cardProps + let (_, _, cardNumber, _, _, _, _, _, _, _) = cardProps let cardBrand = React.useMemo(() => { cardNumber->CardUtils.getCardBrand }, [cardNumber]) - let ( - isExpiryValid, - setIsExpiryValid, - cardExpiry, - changeCardExpiry, - handleExpiryBlur, - expiryRef, - _, - expiryError, - setExpiryError, - ) = expiryProps - - let ( - isCVCValid, - setIsCVCValid, - cvcNumber, - _, - changeCVCNumber, - handleCVCBlur, - cvcRef, - _, - cvcError, - setCvcError, - ) = cvcProps let {displaySavedPaymentMethodsCheckbox} = Recoil.useRecoilValueFromAtom(RecoilAtoms.optionAtom) let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Card) let showFields = Recoil.useRecoilValueFromAtom(RecoilAtoms.showCardFieldsAtom) - let setComplete = Recoil.useSetRecoilState(RecoilAtoms.fieldsComplete) let (isSaveCardsChecked, setIsSaveCardsChecked) = React.useState(_ => false) let setUserError = message => { @@ -76,23 +39,15 @@ let make = ( let (requiredFieldsBody, setRequiredFieldsBody) = React.useState(_ => Dict.make()) let areRequiredFieldsValid = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsValid) + let areRequiredFieldsEmpty = Recoil.useRecoilValueFromAtom(RecoilAtoms.areRequiredFieldsEmpty) - let complete = isAllValid(isCardValid, isCVCValid, isExpiryValid, true, "payment") - let empty = cardNumber == "" || cardExpiry == "" || cvcNumber == "" - React.useEffect(() => { - setComplete(_ => complete) - None - }, [complete]) - - useHandlePostMessages(~complete=complete && areRequiredFieldsValid, ~empty, ~paymentType="card") + useHandlePostMessages( + ~complete=areRequiredFieldsValid, + ~empty=areRequiredFieldsEmpty, + ~paymentType="card", + ) let isGuestCustomer = useIsGuestCustomer() - let isCvcValidValue = CardUtils.getBoolOptionVal(isCVCValid) - let (cardEmpty, cardComplete, cardInvalid) = CardUtils.useCardDetails( - ~cvcNumber, - ~isCVCValid, - ~isCvcValidValue, - ) let isCustomerAcceptanceRequired = useIsCustomerAcceptanceRequired( ~displaySavedPaymentMethodsCheckbox, @@ -104,26 +59,9 @@ let make = ( let submitCallback = React.useCallback((ev: Window.event) => { let json = ev.data->JSON.parseExn let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper - let (month, year) = CardUtils.getExpiryDates(cardExpiry) let onSessionBody = [("customer_acceptance", PaymentBody.customerAcceptanceBody)] - let cardNetwork = { - if cardBrand != "" { - [("card_network", cardBrand->JSON.Encode.string)] - } else { - [] - } - } - let defaultCardBody = PaymentBody.cardPaymentBody( - ~cardNumber, - ~month, - ~year, - ~cardHolderName="", - ~cvcNumber, - ~cardBrand=cardNetwork, - ~nickname, - (), - ) + let defaultCardBody = PaymentBody.dynamicCardPaymentBody(~cardBrand, ~nickname, ()) let banContactBody = PaymentBody.bancontactBody() let cardBody = if isCustomerAcceptanceRequired { defaultCardBody->Array.concat(onSessionBody) @@ -131,11 +69,7 @@ let make = ( defaultCardBody } if confirm.doSubmit { - let validFormat = - (isBancontact || - (isCVCValid->Option.getOr(false) && - isCardValid->Option.getOr(false) && - isExpiryValid->Option.getOr(false))) && areRequiredFieldsValid + let validFormat = areRequiredFieldsValid if validFormat && (showFields || isBancontact) { intent( ~bodyArr={ @@ -150,32 +84,11 @@ let make = ( ~handleUserError=false, (), ) - } else { - if cardNumber === "" { - setCardError(_ => localeString.cardNumberEmptyText) - setUserError(localeString.enterFieldsText) - } - if cardExpiry === "" { - setExpiryError(_ => localeString.cardExpiryDateEmptyText) - setUserError(localeString.enterFieldsText) - } - if !isBancontact && cvcNumber === "" { - setCvcError(_ => localeString.cvcNumberEmptyText) - setUserError(localeString.enterFieldsText) - } - if !validFormat { - setUserError(localeString.enterValidDetailsText) - } + } else if !validFormat { + setUserError(localeString.enterValidDetailsText) } } - }, ( - areRequiredFieldsValid, - requiredFieldsBody, - empty, - complete, - isCustomerAcceptanceRequired, - nickname, - )) + }, (areRequiredFieldsValid, requiredFieldsBody, isCustomerAcceptanceRequired, nickname)) useSubmitPaymentData(submitCallback) let paymentMethod = isBancontact ? "bank_redirect" : "card" @@ -189,114 +102,12 @@ let make = ( let nicknameFieldClassName = conditionsForShowingSaveCardCheckbox ? "pt-2" : "pt-5" - let compressedLayoutStyleForCvcError = - innerLayout === Compressed && cvcError->String.length > 0 ? "!border-l-0" : "" -
- -
- {React.string(localeString.cardHeader)} -
-
- - String.length > 0 - ? "border-b-0" - : ""} - /> -
-
- -
-
- -
-
- String.length > 0 || - cvcError->String.length > 0 || - expiryError->String.length > 0}> -
- {React.string("Invalid input")} -
-
-
{ - switch (str, isBancontact) { - | ("user_email_address", _) => Email - | ("user_full_name", _) => FullName - | ("user_country", _) => Country - | ("user_bank", _) => Bank - | ("user_phone_number", _) => PhoneNumber - | ("user_address_line1", _) => AddressLine1 - | ("user_address_line2", _) => AddressLine2 - | ("user_address_city", _) => AddressCity - | ("user_address_pincode", _) => AddressPincode - | ("user_address_state", _) => AddressState - | ("user_blik_code", _) => BlikCode - | ("user_billing_name", _) => BillingName - | ("user_card_number", true) => CardNumber - | ("user_card_expiry_month", true) => CardExpiryMonth - | ("user_card_expiry_year", true) => CardExpiryYear - | ("user_card_cvc", true) => CardCvc +let getPaymentMethodsFieldTypeFromString = str => { + switch str { + | "user_email_address" => Email + | "user_full_name" => FullName + | "user_country" => Country + | "user_bank" => Bank + | "user_phone_number" => PhoneNumber + | "user_address_line1" => AddressLine1 + | "user_address_line2" => AddressLine2 + | "user_address_city" => AddressCity + | "user_address_pincode" => AddressPincode + | "user_address_state" => AddressState + | "user_blik_code" => BlikCode + | "user_billing_name" => BillingName + | "user_card_number" => CardNumber + | "user_card_expiry_month" => CardExpiryMonth + | "user_card_expiry_year" => CardExpiryYear + | "user_card_cvc" => CardCvc | _ => None } } @@ -558,7 +558,7 @@ let getPaymentMethodsFieldTypeFromDict = dict => { } } -let getFieldType = (dict, isBancontact) => { +let getFieldType = dict => { let fieldClass = dict ->Dict.get("field_type") @@ -570,7 +570,7 @@ let getFieldType = (dict, isBancontact) => { None | Number(_val) => None | Array(_arr) => None - | String(val) => val->getPaymentMethodsFieldTypeFromString(isBancontact) + | String(val) => val->getPaymentMethodsFieldTypeFromString | Object(dict) => dict->getPaymentMethodsFieldTypeFromDict } } @@ -614,6 +614,16 @@ let getIsAnyBillingDetailEmpty = (requiredFields: array) => { }) } +let filterCardDetailsFromSavedPaymentMethod = fieldType => { + switch fieldType { + | CardNumber + | CardExpiryMonth + | CardExpiryYear + | CardCvc => true + | _ => false + } +} + let getPaymentMethodFields = ( paymentMethod, requiredFields: array, @@ -631,6 +641,10 @@ let getPaymentMethodFields = ( isAllStoredCardsHaveName ) { None + } else if ( + isSavedCardFlow && requiredField.field_type->filterCardDetailsFromSavedPaymentMethod + ) { + None } else { requiredField.field_type } @@ -862,7 +876,7 @@ let getAchConnectors = (dict, str) => { ->getStrArray("elligible_connectors") } -let getDynamicFieldsFromJsonDict = (dict, isBancontact) => { +let getDynamicFieldsFromJsonDict = dict => { let requiredFields = Utils.getJsonFromDict(dict, "required_fields", JSON.Encode.null) ->Utils.getDictFromJson @@ -873,7 +887,7 @@ let getDynamicFieldsFromJsonDict = (dict, isBancontact) => { { required_field: requiredFieldsDict->Utils.getString("required_field", ""), display_name: requiredFieldsDict->Utils.getString("display_name", ""), - field_type: requiredFieldsDict->getFieldType(isBancontact), + field_type: requiredFieldsDict->getFieldType, value: requiredFieldsDict->Utils.getString("value", ""), } }) @@ -894,9 +908,7 @@ let getPaymentMethodTypes = (dict, str) => { bank_names: getBankNames(jsonDict, "bank_names"), bank_debits_connectors: getAchConnectors(jsonDict, "bank_debit"), bank_transfers_connectors: getAchConnectors(jsonDict, "bank_transfer"), - required_fields: jsonDict->getDynamicFieldsFromJsonDict( - paymentMethodType === "bancontact_card", - ), + required_fields: jsonDict->getDynamicFieldsFromJsonDict, surcharge_details: jsonDict->getSurchargeDetails, } }) @@ -1055,3 +1067,5 @@ let paymentMethodFieldToStrMapper = (field: paymentMethodsFields) => { | CardExpiryAndCvc => "CardExpiryAndCvc" } } + +let cardDetailsFields = [CardNumber, CardExpiryMonth, CardExpiryYear, CardCvc] diff --git a/src/Payments/PaymentMethodsWrapper.res b/src/Payments/PaymentMethodsWrapper.res index 17903fb8c..c56047c42 100644 --- a/src/Payments/PaymentMethodsWrapper.res +++ b/src/Payments/PaymentMethodsWrapper.res @@ -76,7 +76,8 @@ let make = ( ->Option.getOr(Bank.defaultBank) intent( ~bodyArr=PaymentBody.getPaymentBody( - ~paymentMethod=paymentMethodName, + ~paymentMethod=paymentMethodDetails.methodType, + ~paymentMethodType=paymentMethodName, ~country=countryCode.isoAlpha2, ~fullName=fullName.value, ~email=email.value, diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res index 7c7a5d5cc..80529bc9a 100644 --- a/src/Types/PaymentType.res +++ b/src/Types/PaymentType.res @@ -161,6 +161,7 @@ type options = { sdkHandleConfirmPayment: sdkHandleConfirmPayment, paymentMethodsHeaderText?: string, savedPaymentMethodsHeaderText?: string, + hideExpiredPaymentMethods: bool, } let defaultCardDetails = { scheme: None, @@ -289,6 +290,7 @@ let defaultOptions = { showCardFormByDefault: true, billingAddress: defaultBillingAddress, sdkHandleConfirmPayment: defaultSdkHandleConfirmPayment, + hideExpiredPaymentMethods: false, } let getLayout = (str, logger) => { switch str { @@ -935,6 +937,7 @@ let itemToObjMapper = (dict, logger) => { "sdkHandleConfirmPayment", "paymentMethodsHeaderText", "savedPaymentMethodsHeaderText", + "hideExpiredPaymentMethods", ], dict, "options", @@ -975,6 +978,7 @@ let itemToObjMapper = (dict, logger) => { ->getSdkHandleConfirmPaymentProps, paymentMethodsHeaderText: ?getOptionString(dict, "paymentMethodsHeaderText"), savedPaymentMethodsHeaderText: ?getOptionString(dict, "savedPaymentMethodsHeaderText"), + hideExpiredPaymentMethods: getBool(dict, "hideExpiredPaymentMethods", false), } } diff --git a/src/Utilities/DynamicFieldsUtils.res b/src/Utilities/DynamicFieldsUtils.res index f4a77f9d9..f0fabe294 100644 --- a/src/Utilities/DynamicFieldsUtils.res +++ b/src/Utilities/DynamicFieldsUtils.res @@ -52,6 +52,16 @@ let getBillingAddressPathFromFieldType = (fieldType: PaymentMethodsRecord.paymen } } +let getCardAddressPathFromFieldType = (fieldType: PaymentMethodsRecord.paymentMethodsFields) => { + switch fieldType { + | CardNumber => "payment_method_data.card.card_number" + | CardExpiryMonth => "payment_method_data.card.card_exp_month" + | CardExpiryYear => "payment_method_data.card.card_exp_year" + | CardCvc => "payment_method_data.card.card_cvc" + | _ => "" + } +} + let removeBillingDetailsIfUseBillingAddress = ( requiredFields: array, billingAddress: PaymentType.billingAddress, @@ -144,7 +154,6 @@ let useRequiredFieldsEmptyAndValid = ( | StateAndCity => state.value !== "" && city.value !== "" | CountryAndPincode(countryArr) => (country !== "" || countryArr->Array.length === 0) && postalCode.value !== "" - | AddressCity => city.value !== "" | AddressPincode => postalCode.value !== "" | AddressState => state.value !== "" @@ -377,6 +386,7 @@ let useRequiredFieldsBody = ( ~isSavedCardFlow, ~isAllStoredCardsHaveName, ~setRequiredFieldsBody, + ~list: PaymentMethodsRecord.list, ) => { let email = Recoil.useRecoilValueFromAtom(userEmailAddress) let fullName = Recoil.useRecoilValueFromAtom(userFullName) @@ -464,6 +474,23 @@ let useRequiredFieldsBody = ( } } + let addCardDetailsBodyIfFallback = requiredFieldsBody => { + if ( + (paymentMethodType === "debit" || paymentMethodType === "credit") && + list.payment_methods->Array.length === 0 && + !isSavedCardFlow + ) { + PaymentMethodsRecord.cardDetailsFields->Array.reduce(requiredFieldsBody, (acc, item) => { + let value = item->getFieldValueFromFieldType + let path = item->getCardAddressPathFromFieldType + acc->Dict.set(path, value->JSON.Encode.string) + acc + }) + } else { + requiredFieldsBody + } + } + React.useEffect(() => { let requiredFieldsBody = requiredFields @@ -492,6 +519,7 @@ let useRequiredFieldsBody = ( acc }) ->addBillingDetailsIfUseBillingAddress + ->addCardDetailsBodyIfFallback setRequiredFieldsBody(_ => requiredFieldsBody) None @@ -618,26 +646,50 @@ let combineCardExpiryAndCvc = arr => { } } +let addCardDetailsIfFallback = ( + fieldsArr, + ~list: PaymentMethodsRecord.list, + ~paymentMethod, + ~isSavedCardFlow, +) => { + if paymentMethod === "card" && list.payment_methods->Array.length === 0 && !isSavedCardFlow { + fieldsArr->Array.concat(PaymentMethodsRecord.cardDetailsFields) + } else { + fieldsArr + } +} + let updateDynamicFields = ( arr: array, billingAddress, + ~list: PaymentMethodsRecord.list, + ~paymentMethod, + ~isSavedCardFlow, (), ) => { arr ->Utils.removeDuplicate ->Array.filter(item => item !== None) ->addBillingAddressIfUseBillingAddress(billingAddress) + ->addCardDetailsIfFallback(~list, ~paymentMethod, ~isSavedCardFlow) ->combineStateAndCity ->combineCountryAndPostal ->combineCardExpiryMonthAndYear ->combineCardExpiryAndCvc } -let useSubmitCallback = () => { - let logger = Recoil.useRecoilValueFromAtom(loggerAtom) - let (line1, setLine1) = Recoil.useLoggedRecoilState(userAddressline1, "line1", logger) - let (line2, setLine2) = Recoil.useLoggedRecoilState(userAddressline2, "line2", logger) - let (state, setState) = Recoil.useLoggedRecoilState(userAddressState, "state", logger) +let useSubmitCallback = ( + ~cardNumber, + ~setCardError, + ~cardExpiry, + ~setExpiryError, + ~cvcNumber, + ~setCvcError, +) => { + let logger = Recoil.useRecoilValueFromAtom(RecoilAtoms.loggerAtom) + let (line1, setLine1) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressline1, "line1", logger) + let (line2, setLine2) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressline2, "line2", logger) + let (state, setState) = Recoil.useLoggedRecoilState(RecoilAtoms.userAddressState, "state", logger) let (postalCode, setPostalCode) = Recoil.useLoggedRecoilState( userAddressPincode, "postal_code", @@ -682,6 +734,15 @@ let useSubmitCallback = () => { errorString: localeString.cityEmptyText, }) } + if cardNumber === "" { + setCardError(_ => localeString.cardNumberEmptyText) + } + if cardExpiry === "" { + setExpiryError(_ => localeString.cardExpiryDateEmptyText) + } + if cvcNumber === "" { + setCvcError(_ => localeString.cvcNumberEmptyText) + } } - }, (line1, line2, state, city, postalCode)) + }, (line1, line2, state, city, postalCode, cardNumber, cardExpiry, cvcNumber)) } diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res index e98bdacf3..e6f75bcea 100644 --- a/src/Utilities/PaymentBody.res +++ b/src/Utilities/PaymentBody.res @@ -1,5 +1,40 @@ @val @scope("window") external btoa: string => string = "btoa" + +let billingDetailsTuple = ( + ~fullName, + ~email, + ~line1, + ~line2, + ~city, + ~state, + ~postalCode, + ~country, +) => { + ( + "billing_details", + [ + ("name", fullName->JSON.Encode.string), + ("email", email->JSON.Encode.string), + ( + "address", + [ + ("line1", line1->JSON.Encode.string), + ("line2", line2->JSON.Encode.string), + ("city", city->JSON.Encode.string), + ("state", state->JSON.Encode.string), + ("zip", postalCode->JSON.Encode.string), + ("country", country->JSON.Encode.string), + ] + ->Dict.fromArray + ->JSON.Encode.object, + ), + ] + ->Dict.fromArray + ->JSON.Encode.object, + ) +} + let cardPaymentBody = ( ~cardNumber, ~month, @@ -23,17 +58,48 @@ let cardPaymentBody = ( cardBody->Array.push(("nick_name", nickname->JSON.Encode.string))->ignore } + if cardBrand != "" { + cardBody->Array.push(("card_network", cardBrand->JSON.Encode.string))->ignore + } + [ ("payment_method", "card"->JSON.Encode.string), ( "payment_method_data", - [("card", cardBody->Array.concat(cardBrand)->Dict.fromArray->JSON.Encode.object)] + [("card", cardBody->Dict.fromArray->JSON.Encode.object)] ->Dict.fromArray ->JSON.Encode.object, ), ] } +let dynamicCardPaymentBody = (~cardBrand, ~nickname="", ()) => { + let cardBody = [] + + if nickname != "" { + cardBody->Array.push(("nick_name", nickname->JSON.Encode.string))->ignore + } + + if cardBrand != "" { + cardBody->Array.push(("card_network", cardBrand->JSON.Encode.string))->ignore + } + + let paymentMethodData = if cardBody->Array.length > 0 { + [ + ( + "payment_method_data", + [("card", cardBody->Dict.fromArray->JSON.Encode.object)] + ->Dict.fromArray + ->JSON.Encode.object, + ), + ] + } else { + [] + } + + [("payment_method", "card"->JSON.Encode.string)]->Array.concat(paymentMethodData) +} + let bancontactBody = () => [ ("payment_method", "bank_redirect"->JSON.Encode.string), ("payment_method_type", "bancontact_card"->JSON.Encode.string), @@ -181,27 +247,15 @@ let achBankDebitBody = ( ( "ach_bank_debit", [ - ( - "billing_details", - [ - ("name", cardHolderName->JSON.Encode.string), - ("email", email->JSON.Encode.string), - ( - "address", - [ - ("line1", line1->JSON.Encode.string), - ("line2", line2->JSON.Encode.string), - ("city", city->JSON.Encode.string), - ("state", state->JSON.Encode.string), - ("zip", postalCode->JSON.Encode.string), - ("country", country->JSON.Encode.string), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, + billingDetailsTuple( + ~fullName=cardHolderName, + ~email, + ~line1, + ~line2, + ~city, + ~state, + ~postalCode, + ~country, ), ("account_number", bank.accountNumber->JSON.Encode.string), ("bank_account_holder_name", bank.accountHolderName->JSON.Encode.string), @@ -243,27 +297,15 @@ let sepaBankDebitBody = ( ( "sepa_bank_debit", [ - ( - "billing_details", - [ - ("name", fullName->JSON.Encode.string), - ("email", email->JSON.Encode.string), - ( - "address", - [ - ("line1", line1->JSON.Encode.string), - ("line2", line2->JSON.Encode.string), - ("city", city->JSON.Encode.string), - ("state", state->JSON.Encode.string), - ("zip", postalCode->JSON.Encode.string), - ("country", country->JSON.Encode.string), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, + billingDetailsTuple( + ~fullName, + ~email, + ~line1, + ~line2, + ~city, + ~state, + ~postalCode, + ~country, ), ("iban", data.iban->JSON.Encode.string), ("bank_account_holder_name", data.accountHolderName->JSON.Encode.string), @@ -304,27 +346,15 @@ let bacsBankDebitBody = ( ( "bacs_bank_debit", [ - ( - "billing_details", - [ - ("name", bankAccountHolderName->JSON.Encode.string), - ("email", email->JSON.Encode.string), - ( - "address", - [ - ("line1", line1->JSON.Encode.string), - ("line2", line2->JSON.Encode.string), - ("city", city->JSON.Encode.string), - ("zip", zip->JSON.Encode.string), - ("state", state->JSON.Encode.string), - ("country", country->JSON.Encode.string), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, + billingDetailsTuple( + ~fullName=bankAccountHolderName, + ~email, + ~line1, + ~line2, + ~city, + ~state, + ~postalCode=zip, + ~country, ), ("bank_account_holder_name", bankAccountHolderName->JSON.Encode.string), ("sort_code", sortCode->JSON.Encode.string), @@ -365,27 +395,15 @@ let becsBankDebitBody = ( ( "becs_bank_debit", [ - ( - "billing_details", - [ - ("name", fullName->JSON.Encode.string), - ("email", email->JSON.Encode.string), - ( - "address", - [ - ("line1", line1->JSON.Encode.string), - ("line2", line2->JSON.Encode.string), - ("city", city->JSON.Encode.string), - ("state", state->JSON.Encode.string), - ("zip", postalCode->JSON.Encode.string), - ("country", country->JSON.Encode.string), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, + billingDetailsTuple( + ~fullName, + ~email, + ~line1, + ~line2, + ~city, + ~state, + ~postalCode, + ~country, ), ("bsb_number", data.sortCode->JSON.Encode.string), ("account_number", data.accountNumber->JSON.Encode.string), @@ -640,25 +658,6 @@ let applePayThirdPartySdkBody = (~connectors) => { ] } -let affirmBody = () => [ - ("payment_method", "pay_later"->JSON.Encode.string), - ("payment_method_type", "affirm"->JSON.Encode.string), - ("payment_experience", "redirect_to_url"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "pay_later", - [("affirm_redirect", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - let cryptoBody = (~currency) => [ ("payment_method", "crypto"->JSON.Encode.string), ("payment_method_type", "crypto_currency"->JSON.Encode.string), @@ -1008,96 +1007,6 @@ let interacBody = (~email, ~country) => [ ), ] -let mobilePayBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "mobile_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("mobile_pay", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let aliPayRedirectBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "ali_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("ali_pay_redirect", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let aliPayQrBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "ali_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("ali_pay_qr", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let weChatPayRedirectBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "we_chat_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("we_chat_pay_redirect", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let weChatPayQrBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "we_chat_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("we_chat_pay_qr", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - let trustlyBody = (~country) => [ ("payment_method", "bank_redirect"->JSON.Encode.string), ("payment_method_type", "trustly"->JSON.Encode.string), @@ -1121,24 +1030,6 @@ let trustlyBody = (~country) => [ ), ] -let finlandOB = () => [ - ("payment_method", "bank_redirect"->JSON.Encode.string), - ("payment_method_type", "online_banking_finland"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "bank_redirect", - [("online_banking_finland", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - let polandOB = (~bank) => [ ("payment_method", "bank_redirect"->JSON.Encode.string), ("payment_method_type", "online_banking_poland"->JSON.Encode.string), @@ -1208,41 +1099,6 @@ let slovakiaOB = (~bank) => [ ), ] -let walleyBody = () => [ - ("payment_method", "pay_later"->JSON.Encode.string), - ("payment_method_type", "walley"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "pay_later", - [("walley_redirect", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let payBrightBody = () => [ - ("payment_method", "pay_later"->JSON.Encode.string), - ("payment_method_type", "pay_bright"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "pay_later", - [("pay_bright_redirect", []->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] let mbWayBody = (~phoneNumber) => [ ("payment_method", "wallet"->JSON.Encode.string), ("payment_method_type", "mb_way"->JSON.Encode.string), @@ -1268,152 +1124,6 @@ let mbWayBody = (~phoneNumber) => [ ), ] -let twintBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "twint"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("twint_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let vippsBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "vipps"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("vipps_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let danaBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "dana"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("dana_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let goPayBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "go_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("go_pay_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] -let kakaoPayBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "kakao_pay"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("kakao_pay_redirect", Dict.make()->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let gcashBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "gcash"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("gcash_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] -let momoBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "momo"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("momo_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let touchNGoBody = () => [ - ("payment_method", "wallet"->JSON.Encode.string), - ("payment_method_type", "touch_n_go"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "wallet", - [("touch_n_go_redirect", Dict.make()->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - -let bizumBody = () => [ - ("payment_method", "bank_redirect"->JSON.Encode.string), - ("payment_method_type", "bizum"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "bank_redirect", - [("bizum", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] - let rewardBody = (~paymentMethodType) => [ ("payment_method", "reward"->JSON.Encode.string), ("payment_method_type", paymentMethodType->JSON.Encode.string), @@ -1464,32 +1174,7 @@ let thailandOBBody = (~bank) => [ ->JSON.Encode.object, ), ] -let almaBody = () => [ - ("payment_method", "pay_later"->JSON.Encode.string), - ("payment_method_type", "alma"->JSON.Encode.string), - ( - "payment_method_data", - [("pay_later", [("alma", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] -let atomeBody = () => [ - ("payment_method", "pay_later"->JSON.Encode.string), - ("payment_method_type", "atome"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "pay_later", - [("atome_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), -] let multibancoBody = (~email) => [ ("payment_method", "bank_transfer"->JSON.Encode.string), ("payment_method_type", "multibanco"->JSON.Encode.string), @@ -1520,72 +1205,67 @@ let multibancoBody = (~email) => [ ), ] -let cardRedirectBody = () => { - [ - ("payment_method", "card_redirect"->JSON.Encode.string), - ("payment_method_type", "card_redirect"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "card_redirect", - [("card_redirect", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] +let getPaymentMethodType = (paymentMethod, paymentMethodType) => { + switch paymentMethod { + | "bank_debit" => paymentMethodType->String.replace("_debit", "") + | "bank_transfer" => paymentMethodType->String.replace("_transfer", "") + | _ => paymentMethodType + } } -let openBankingUKBody = () => { - [ - ("payment_method", "bank_redirect"->JSON.Encode.string), - ("payment_method_type", "open_banking_uk"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "bank_redirect", - [("open_banking_uk", Dict.make()->JSON.Encode.object)] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] +let appendRedirectPaymentMethods = [ + "touch_n_go", + "momo", + "gcash", + "kakao_pay", + "go_pay", + "dana", + "vipps", + "twint", + "atome", + "pay_bright", + "walley", + "affirm", + "we_chat_pay", + "ali_pay", +] + +let appendPaymentMethodExperience = (paymentMethodType, isQrPaymentMethod) => { + if isQrPaymentMethod { + paymentMethodType ++ "_qr" + } else if appendRedirectPaymentMethods->Array.includes(paymentMethodType) { + paymentMethodType ++ "_redirect" + } else { + paymentMethodType + } } -let pixTransferBody = () => { - [ - ("payment_method", "bank_transfer"->JSON.Encode.string), - ("payment_method_type", "pix"->JSON.Encode.string), - ( - "payment_method_data", - [ - ( - "bank_transfer", - [("pix", Dict.make()->JSON.Encode.object)]->Dict.fromArray->JSON.Encode.object, - ), - ] - ->Dict.fromArray - ->JSON.Encode.object, - ), - ] +let paymentExperiencePaymentMethods = ["affirm"] + +let appendPaymentExperience = (paymentBodyArr, paymentMethodType) => { + if paymentExperiencePaymentMethods->Array.includes(paymentMethodType) { + paymentBodyArr->Array.concat([("payment_experience", "redirect_to_url"->JSON.Encode.string)]) + } else { + paymentBodyArr + } } -let localBankTransferBody = () => { +let dynamicPaymentBody = (paymentMethod, paymentMethodType, ~isQrPaymentMethod=false) => { + let paymentMethodType = paymentMethod->getPaymentMethodType(paymentMethodType) [ - ("payment_method", "bank_transfer"->JSON.Encode.string), - ("payment_method_type", "local_bank_transfer"->JSON.Encode.string), + ("payment_method", paymentMethod->JSON.Encode.string), + ("payment_method_type", paymentMethodType->JSON.Encode.string), ( "payment_method_data", [ ( - "bank_transfer", - [("local_bank_transfer", Dict.make()->JSON.Encode.object)] + paymentMethod, + [ + ( + paymentMethodType->appendPaymentMethodExperience(isQrPaymentMethod), + Dict.make()->JSON.Encode.object, + ), + ] ->Dict.fromArray ->JSON.Encode.object, ), @@ -1593,11 +1273,12 @@ let localBankTransferBody = () => { ->Dict.fromArray ->JSON.Encode.object, ), - ] + ]->appendPaymentExperience(paymentMethodType) } let getPaymentBody = ( ~paymentMethod, + ~paymentMethodType, ~fullName, ~email, ~country, @@ -1607,60 +1288,41 @@ let getPaymentBody = ( ~phoneNumber, ~currency, ) => { - switch paymentMethod { - | "affirm" => affirmBody() + switch paymentMethodType { | "afterpay_clearpay" => afterpayRedirectionBody(~fullName, ~email) | "crypto_currency" => cryptoBody(~currency) | "sofort" => sofortBody(~country, ~name=fullName, ~email) | "ideal" => iDealBody(~name=fullName, ~bankName=bank) | "eps" => epsBody(~name=fullName, ~bankName=bank) | "blik" => blikBody(~blikCode) - | "mobile_pay" => mobilePayBody() | "ali_pay" => switch paymentExperience { - | QrFlow => aliPayQrBody() + | QrFlow => dynamicPaymentBody(paymentMethod, paymentMethodType, ~isQrPaymentMethod=true) | RedirectToURL | _ => - aliPayRedirectBody() + dynamicPaymentBody(paymentMethod, paymentMethodType) } | "we_chat_pay" => switch paymentExperience { - | QrFlow => weChatPayQrBody() + | QrFlow => dynamicPaymentBody(paymentMethod, paymentMethodType, ~isQrPaymentMethod=true) | RedirectToURL | _ => - weChatPayRedirectBody() + dynamicPaymentBody(paymentMethod, paymentMethodType) } | "giropay" => giroPayBody(~name=fullName, ()) | "trustly" => trustlyBody(~country) - | "online_banking_finland" => finlandOB() | "online_banking_poland" => polandOB(~bank) | "online_banking_czech_republic" => czechOB(~bank) | "online_banking_slovakia" => slovakiaOB(~bank) - | "walley" => walleyBody() - | "pay_bright" => payBrightBody() | "mb_way" => mbWayBody(~phoneNumber) | "interac" => interacBody(~email, ~country) | "przelewy24" => p24Body(~email) - | "twint" => twintBody() - | "vipps" => vippsBody() - | "dana" => danaBody() - | "go_pay" => goPayBody() - | "kakao_pay" => kakaoPayBody() - | "gcash" => gcashBody() - | "momo" => momoBody() - | "touch_n_go" => touchNGoBody() - | "bizum" => bizumBody() | "online_banking_fpx" => fpxOBBody(~bank) | "online_banking_thailand" => thailandOBBody(~bank) - | "alma" => almaBody() - | "atome" => atomeBody() | "multibanco" => multibancoBody(~email) - | "classic" => rewardBody(~paymentMethodType=paymentMethod) - | "card_redirect" => cardRedirectBody() - | "open_banking_uk" => openBankingUKBody() - | "evoucher" => rewardBody(~paymentMethodType=paymentMethod) - | "pix_transfer" => pixTransferBody() - | "local_bank_transfer_transfer" => localBankTransferBody() - | _ => [] + | "classic" + | "evoucher" => + rewardBody(~paymentMethodType) + | _ => dynamicPaymentBody(paymentMethod, paymentMethodType) } } diff --git a/webpack.common.js b/webpack.common.js index ce9579d0d..e0d800850 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,28 +1,31 @@ const webpack = require("webpack"); const path = require("path"); +const dotenv = require("dotenv").config(); const tailwindcss = require("tailwindcss"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CopyPlugin = require("copy-webpack-plugin"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const TerserPlugin = require("terser-webpack-plugin"); -const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; +const BundleAnalyzerPlugin = + require("webpack-bundle-analyzer").BundleAnalyzerPlugin; const { sentryWebpackPlugin } = require("@sentry/webpack-plugin"); -const sdkEnv = process.env.sdkEnv; -const envSdkUrl = process.env.envSdkUrl; -const envBackendUrl = process.env.envBackendUrl; -const envLoggingUrl = process.env.envLoggingUrl; +const sdkEnv = process.env.sdkEnv ?? "local"; +const envSdkUrl = process.env.ENV_SDK_URL ?? ""; +const envBackendUrl = process.env.ENV_BACKEND_URL ?? ""; +const envLoggingUrl = process.env.ENV_LOGGING_URL ?? ""; //git rev-parse --abbrev-ref HEAD let repoVersion = require("./package.json").version; let majorVersion = "v" + repoVersion.split(".")[0]; let repoName = require("./package.json").name; -let repoPublicPath = sdkEnv === "local" ? "" : `/${repoVersion}/${majorVersion}`; +let repoPublicPath = + sdkEnv === "local" ? "" : `/${repoVersion}/${majorVersion}`; let sdkUrl; -if (envSdkUrl === undefined) { +if (envSdkUrl.length === 0) { sdkUrl = sdkEnv === "prod" ? "https://checkout.hyperswitch.io" @@ -36,7 +39,7 @@ if (envSdkUrl === undefined) { } let backendEndPoint; -if (envBackendUrl === undefined) { +if (envBackendUrl.length === 0) { backendEndPoint = sdkEnv === "prod" ? "https://checkout.hyperswitch.io/api" @@ -50,7 +53,7 @@ if (envBackendUrl === undefined) { } let confirmEndPoint; -if (envBackendUrl === undefined) { +if (envBackendUrl.length === 0) { confirmEndPoint = sdkEnv === "prod" ? "https://api.hyperswitch.io" @@ -64,7 +67,7 @@ if (envBackendUrl === undefined) { } let logEndpoint; -if (envLoggingUrl === undefined) { +if (envLoggingUrl.length === 0) { logEndpoint = sdkEnv === "prod" ? "https://api.hyperswitch.io/logs/sdk" @@ -98,6 +101,7 @@ module.exports = (publicPath = "auto") => { clean: true, publicPath: `${repoPublicPath}/`, }, + // TODO - Can be commented for faster build in local development optimization: { sideEffects: true, minimize: true, @@ -155,6 +159,7 @@ module.exports = (publicPath = "auto") => { // new webpack.HTMLInjectPlugin({ // publicPath: JSON.stringify(repoVersion), // }), + // TODO - Can be commented if sentry not needed. sentryWebpackPlugin({ org: "sentry", project: "hyperswitch-react-sdk", diff --git a/webpack.dev.js b/webpack.dev.js index d702a8003..993c77a9d 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,8 +1,8 @@ const path = require("path"); +const dotenv = require("dotenv").config(); const { merge } = require("webpack-merge"); const common = require("./webpack.common.js"); - -const sdkEnv = process.env.sdkEnv; +const sdkEnv = process.env.sdkEnv ?? "local"; let backendEndPoint = sdkEnv === "prod"