{React.string(localeString.saveCardDetails)}
diff --git a/src/Hooks/UtilityHooks.res b/src/Hooks/UtilityHooks.res
new file mode 100644
index 0000000..0a01363
--- /dev/null
+++ b/src/Hooks/UtilityHooks.res
@@ -0,0 +1,11 @@
+let useIsGuestCustomer = () => {
+ let {customerPaymentMethods} = RecoilAtoms.optionAtom->Recoil.useRecoilValueFromAtom
+
+ React.useMemo1(() => {
+ switch customerPaymentMethods {
+ | LoadedSavedCards(_, false)
+ | NoResult(false) => false
+ | _ => true
+ }
+ }, [customerPaymentMethods])
+}
diff --git a/src/LocaleString.res b/src/LocaleString.res
index 2fb5c49..57ff693 100644
--- a/src/LocaleString.res
+++ b/src/LocaleString.res
@@ -62,6 +62,7 @@ type localeStrings = {
completeNameEmptyText: string => string,
billingDetailsText: string,
socialSecurityNumberLabel: string,
+ saveWalletDetails: string,
}
let defaultLocale = {
@@ -139,6 +140,7 @@ let defaultLocale = {
completeNameEmptyText: str => `Please provide your complete ${str}`,
billingDetailsText: "Billing Details",
socialSecurityNumberLabel: "Social Security Number",
+ saveWalletDetails: "Wallets details will be saved upon selection",
}
type locale = {localeStrings: array
}
@@ -218,6 +220,7 @@ let localeStrings = [
completeNameEmptyText: str => `Please provide your complete ${str}`,
billingDetailsText: "Billing Details",
socialSecurityNumberLabel: "Social Security Number",
+ saveWalletDetails: "Wallets details will be saved upon selection",
},
{
locale: "he",
@@ -294,6 +297,7 @@ let localeStrings = [
completeNameEmptyText: str => `אנא ספק את המלא שלך ${str}`,
billingDetailsText: `פרטי תשלום`,
socialSecurityNumberLabel: `מספר ביטוח לאומי`,
+ saveWalletDetails: "פרטי הארנק יישמרו בעת בחירה",
},
{
locale: `fr`,
@@ -370,6 +374,7 @@ let localeStrings = [
completeNameEmptyText: str => `Veuillez fournir votre complet ${str}`,
billingDetailsText: `Détails de la facturation`,
socialSecurityNumberLabel: `Numéro de sécurité sociale`,
+ saveWalletDetails: "Les détails du portefeuille seront enregistrés lors de la sélection",
},
{
locale: "en-GB",
@@ -446,6 +451,7 @@ let localeStrings = [
completeNameEmptyText: str => `Please provide your complete ${str}`,
billingDetailsText: "Billing Details",
socialSecurityNumberLabel: "Social Security Number",
+ saveWalletDetails: "Wallets details will be saved upon selection",
},
{
locale: "ar",
@@ -522,6 +528,7 @@ let localeStrings = [
completeNameEmptyText: str => `يرجى تقديم كامل الخاص بك ${str}`,
billingDetailsText: `تفاصيل الفاتورة`,
socialSecurityNumberLabel: `رقم الضمان الاجتماعي`,
+ saveWalletDetails: "سيتم حفظ تفاصيل المحفظة عند الاختيار",
},
{
locale: "ja",
@@ -598,6 +605,7 @@ let localeStrings = [
completeNameEmptyText: str => `完全な情報を提供してください ${str}`,
billingDetailsText: `支払明細`,
socialSecurityNumberLabel: `社会保障番号`,
+ saveWalletDetails: "選択時にウォレットの詳細が保存されます",
},
{
locale: "de",
@@ -674,5 +682,6 @@ let localeStrings = [
completeNameEmptyText: str => `Bitte geben Sie Ihr vollständiges Formular an ${str}`,
billingDetailsText: `Rechnungsdetails`,
socialSecurityNumberLabel: `Sozialversicherungsnummer`,
+ saveWalletDetails: "Wallet-Details werden beim Auswählen gespeichert",
},
]
diff --git a/src/PaymentElement.res b/src/PaymentElement.res
index de12e41..151dd9f 100644
--- a/src/PaymentElement.res
+++ b/src/PaymentElement.res
@@ -20,7 +20,7 @@ let make = (
paymentMethodOrder,
layout,
customerPaymentMethods,
- disableSaveCards,
+ displaySavedPaymentMethodsCheckbox,
} = Recoil.useRecoilValueFromAtom(optionAtom)
let isApplePayReady = Recoil.useRecoilValueFromAtom(isApplePayReady)
let isGooglePayReady = Recoil.useRecoilValueFromAtom(isGooglePayReady)
@@ -73,12 +73,12 @@ let make = (
}, [customerPaymentMethods])
React.useEffect1(() => {
- if disableSaveCards {
+ if displaySavedPaymentMethodsCheckbox {
setShowFields(._ => true)
setLoadSavedCards(_ => LoadedSavedCards([], true))
}
None
- }, [disableSaveCards])
+ }, [displaySavedPaymentMethodsCheckbox])
React.useEffect1(() => {
let tokenobj =
diff --git a/src/Payments/ApplePay.res b/src/Payments/ApplePay.res
index f5956bc..e714c12 100644
--- a/src/Payments/ApplePay.res
+++ b/src/Payments/ApplePay.res
@@ -52,10 +52,18 @@ let make = (
? list->PaymentUtils.getConnectors(Wallets(ApplePay(SDK)))
: list->PaymentUtils.getConnectors(Wallets(ApplePay(Redirect)))
+ let isGuestCustomer = UtilityHooks.useIsGuestCustomer()
+
let processPayment = bodyArr => {
+ let requestBody = PaymentUtils.appendedCustomerAcceptance(
+ ~isGuestCustomer,
+ ~paymentType=list.payment_type,
+ ~body=bodyArr,
+ )
+
if isWallet {
intent(
- ~bodyArr,
+ ~bodyArr=requestBody,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
diff --git a/src/Payments/CardPayment.res b/src/Payments/CardPayment.res
index 2e66628..46e4cdd 100644
--- a/src/Payments/CardPayment.res
+++ b/src/Payments/CardPayment.res
@@ -57,6 +57,9 @@ let make = (
cvcError,
setCvcError,
) = cvcProps
+ let {customerPaymentMethods, displaySavedPaymentMethodsCheckbox} = Recoil.useRecoilValueFromAtom(
+ RecoilAtoms.optionAtom,
+ )
let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Card)
let (showFields, setShowFields) = Recoil.useRecoilState(RecoilAtoms.showCardFieldsAtom)
let setComplete = Recoil.useSetRecoilState(RecoilAtoms.fieldsComplete)
@@ -83,12 +86,13 @@ let make = (
}, (empty, complete))
let (savedMethods, isGuestCustomer) = React.useMemo1(() => {
- switch options.customerPaymentMethods {
+ switch customerPaymentMethods {
| LoadedSavedCards(savedMethods, isGuest) => (savedMethods, isGuest)
| NoResult(isGuest) => ([], isGuest)
| _ => ([], true)
}
- }, [options.customerPaymentMethods])
+ }, [customerPaymentMethods])
+
let isCvcValidValue = CardUtils.getBoolOptionVal(isCVCValid)
let (cardEmpty, cardComplete, cardInvalid) = CardUtils.useCardDetails(
~cvcNumber,
@@ -100,7 +104,8 @@ let make = (
let json = ev.data->Js.Json.parseExn
let confirm = json->getDictFromJson->ConfirmType.itemToObjMapper
let (month, year) = CardUtils.getExpiryDates(cardExpiry)
- let onSessionBody = [("setup_future_usage", "on_session"->Js.Json.string)]
+
+ let onSessionBody = [("customer_acceptance", PaymentBody.customerAcceptanceBody)]
let cardNetwork = {
if cardBrand != "" {
[("card_network", cardBrand->Js.Json.string)]
@@ -108,7 +113,7 @@ let make = (
[]
}
}
- let deafultCardBody = PaymentBody.cardPaymentBody(
+ let defaultCardBody = PaymentBody.cardPaymentBody(
~cardNumber,
~month,
~year,
@@ -117,9 +122,15 @@ let make = (
~cardBrand=cardNetwork,
)
let banContactBody = PaymentBody.bancontactBody()
- let cardBody = isSaveCardsChecked
- ? deafultCardBody->Js.Array2.concat(onSessionBody)
- : deafultCardBody
+ let cardBody = if displaySavedPaymentMethodsCheckbox {
+ if isSaveCardsChecked || list.payment_type === "setup_mandate" {
+ defaultCardBody->Js.Array2.concat(onSessionBody)
+ } else {
+ defaultCardBody
+ }
+ } else {
+ defaultCardBody->Js.Array2.concat(onSessionBody)
+ }
if confirm.doSubmit {
let validFormat =
(isBancontact ||
@@ -163,6 +174,11 @@ let make = (
let paymentMethod = isBancontact ? "bank_redirect" : "card"
let paymentMethodType = isBancontact ? "bancontact_card" : "debit"
+ let conditionsForShowingSaveCardCheckbox =
+ !isGuestCustomer &&
+ list.payment_type !== "setup_mandate" &&
+ options.displaySavedPaymentMethodsCheckbox &&
+ !isBancontact
@@ -248,7 +264,7 @@ let make = (
cvcProps={Some(cvcProps)}
isBancontact
/>
-
+
diff --git a/src/Payments/GPay.res b/src/Payments/GPay.res
index 661f7ff..163075b 100644
--- a/src/Payments/GPay.res
+++ b/src/Payments/GPay.res
@@ -33,6 +33,8 @@ let make = (
let areOneClickWalletsRendered = Recoil.useSetRecoilState(RecoilAtoms.areOneClickWalletsRendered)
+ let isGuestCustomer = UtilityHooks.useIsGuestCustomer()
+
let googlePayPaymentMethodType = switch PaymentMethodsRecord.getPaymentMethodTypeFromList(
~list,
~paymentMethod="wallet",
@@ -87,8 +89,14 @@ let make = (
if dict->Js.Dict.get("gpayResponse")->Belt.Option.isSome {
let metadata = dict->getJsonObjectFromDict("gpayResponse")
let obj = metadata->getDictFromJson->itemToObjMapper
+ let gPayBody = PaymentUtils.appendedCustomerAcceptance(
+ ~isGuestCustomer,
+ ~paymentType=list.payment_type,
+ ~body=PaymentBody.gpayBody(~payObj=obj, ~connectors),
+ )
+
let body = {
- PaymentBody.gpayBody(~payObj=obj, ~connectors)
+ gPayBody
->Js.Dict.fromArray
->Js.Json.object_
->OrcaUtils.flattenObject(true)
diff --git a/src/Payments/PayPal.res b/src/Payments/PayPal.res
index 041f7b0..9ed9616 100644
--- a/src/Payments/PayPal.res
+++ b/src/Payments/PayPal.res
@@ -29,6 +29,8 @@ let make = (~list: PaymentMethodsRecord.list) => {
}
let (buttonColor, textColor) =
options.wallets.style.theme == Light ? ("#0070ba", "#ffffff") : ("#ffc439", "#000000")
+ let isGuestCustomer = UtilityHooks.useIsGuestCustomer()
+
let intent = PaymentHelpers.usePaymentIntent(Some(loggerState), Paypal)
let onPaypalClick = _ev => {
loggerState.setLogInfo(
@@ -45,8 +47,15 @@ let make = (~list: PaymentMethodsRecord.list) => {
if result {
let (connectors, _) = list->PaymentUtils.getConnectors(Wallets(Paypal(Redirect)))
let body = PaymentBody.paypalRedirectionBody(~connectors)
+
+ let modifiedPaymentBody = PaymentUtils.appendedCustomerAcceptance(
+ ~isGuestCustomer,
+ ~paymentType=list.payment_type,
+ ~body,
+ )
+
intent(
- ~bodyArr=body,
+ ~bodyArr=modifiedPaymentBody,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
diff --git a/src/Payments/PaymentRequestButtonElement.res b/src/Payments/PaymentRequestButtonElement.res
index ba3682e..798d6bd 100644
--- a/src/Payments/PaymentRequestButtonElement.res
+++ b/src/Payments/PaymentRequestButtonElement.res
@@ -11,6 +11,30 @@ let paymentMode = str => {
| _ => NONE
}
}
+
+module WalletsSaveDetailsText = {
+ @react.component
+ let make = (~paymentType) => {
+ open RecoilAtoms
+ let {isGooglePay, isApplePay, isPaypal} = Recoil.useRecoilValueFromAtom(
+ areOneClickWalletsRendered,
+ )
+ let {localeString} = Recoil.useRecoilValueFromAtom(configAtom)
+ let isGuestCustomer = UtilityHooks.useIsGuestCustomer()
+
+
+
+
+
+ {localeString.saveWalletDetails->React.string}
+
+
+
+ }
+}
+
@react.component
let make = (~sessions, ~walletOptions, ~list: PaymentMethodsRecord.list) => {
open SessionsType
@@ -94,5 +118,6 @@ let make = (~sessions, ~walletOptions, ~list: PaymentMethodsRecord.list) => {
})
->React.array}
+
}
diff --git a/src/Payments/PaypalSDK.res b/src/Payments/PaypalSDK.res
index 727aab4..42601cb 100644
--- a/src/Payments/PaypalSDK.res
+++ b/src/Payments/PaypalSDK.res
@@ -37,6 +37,7 @@ let make = (~sessionObj: SessionsType.token, ~list: PaymentMethodsRecord.list) =
},
}
let handleCloseLoader = () => Utils.handlePostMessage([("fullscreen", false->Js.Json.boolean)])
+ let isGuestCustomer = UtilityHooks.useIsGuestCustomer()
let loadPaypalSdk = () => {
loggerState.setLogInfo(
~value="Paypal SDK Button Clicked",
@@ -94,8 +95,14 @@ let make = (~sessionObj: SessionsType.token, ~list: PaymentMethodsRecord.list) =
~token=payload.nonce,
~connectors,
)
+ let modifiedPaymentBody = PaymentUtils.appendedCustomerAcceptance(
+ ~isGuestCustomer,
+ ~paymentType=list.payment_type,
+ ~body,
+ )
+
intent(
- ~bodyArr=body,
+ ~bodyArr=modifiedPaymentBody,
~confirmParam={
return_url: options.wallets.walletReturnUrl,
publishableKey,
diff --git a/src/Types/PaymentType.res b/src/Types/PaymentType.res
index cfc706c..4c19014 100644
--- a/src/Types/PaymentType.res
+++ b/src/Types/PaymentType.res
@@ -136,7 +136,8 @@ type options = {
business: business,
customerPaymentMethods: savedCardsLoadState,
paymentMethodOrder: option>,
- disableSaveCards: bool,
+ displaySavedPaymentMethodsCheckbox: bool,
+ displaySavedPaymentMethods: bool,
fields: fields,
readOnly: bool,
terms: terms,
@@ -252,7 +253,8 @@ let defaultOptions = {
layout: ObjectLayout(defaultLayout),
paymentMethodOrder: None,
fields: defaultFields,
- disableSaveCards: false,
+ displaySavedPaymentMethodsCheckbox: true,
+ displaySavedPaymentMethods: true,
readOnly: false,
terms: defaultTerms,
branding: Auto,
@@ -872,7 +874,8 @@ let itemToObjMapper = (dict, logger) => {
"terms",
"wallets",
"showCardFormByDefault",
- "disableSaveCards",
+ "displaySavedPaymentMethodsCheckbox",
+ "displaySavedPaymentMethods",
"sdkHandleOneClickConfirmPayment",
"showCardFormByDefault",
],
@@ -891,7 +894,18 @@ let itemToObjMapper = (dict, logger) => {
"options.branding",
logger,
),
- disableSaveCards: getBoolWithWarning(dict, "disableSaveCards", false, ~logger),
+ displaySavedPaymentMethodsCheckbox: getBoolWithWarning(
+ dict,
+ "displaySavedPaymentMethodsCheckbox",
+ true,
+ ~logger,
+ ),
+ displaySavedPaymentMethods: getBoolWithWarning(
+ dict,
+ "displaySavedPaymentMethods",
+ true,
+ ~logger,
+ ),
readOnly: getBoolWithWarning(dict, "readOnly", false, ~logger),
terms: getTerms(dict, "terms", logger),
wallets: getWallets(dict, "wallets", logger),
diff --git a/src/Types/PostalCodeType.res b/src/Types/PostalCodeType.res
index f4226d0..90ab3d2 100644
--- a/src/Types/PostalCodeType.res
+++ b/src/Types/PostalCodeType.res
@@ -10,7 +10,6 @@ let defaultPostalCode = {
}
type themeDataModule = {default: array}
-open Promise
@val
-external importPostalCode: string => t = "import"
+external importPostalCode: string => Promise.t = "import"
diff --git a/src/Utilities/PaymentBody.res b/src/Utilities/PaymentBody.res
index b7a7b70..ef29c34 100644
--- a/src/Utilities/PaymentBody.res
+++ b/src/Utilities/PaymentBody.res
@@ -62,30 +62,27 @@ let savedCardBody = (~paymentToken, ~customerId, ~cvcNumber) => [
("card_cvc", cvcNumber->Js.Json.string),
]
-let mandateBody = paymentType => {
+let customerAcceptanceBody =
[
+ ("acceptance_type", "online"->Js.Json.string),
+ ("accepted_at", Js.Date.now()->Js.Date.fromFloat->Js.Date.toISOString->Js.Json.string),
(
- "mandate_data",
- [
- (
- "customer_acceptance",
- [
- ("acceptance_type", "online"->Js.Json.string),
- ("accepted_at", Js.Date.now()->Js.Date.fromFloat->Js.Date.toISOString->Js.Json.string),
- (
- "online",
- [("user_agent", BrowserSpec.navigator.userAgent->Js.Json.string)]
- ->Js.Dict.fromArray
- ->Js.Json.object_,
- ),
- ]
- ->Js.Dict.fromArray
- ->Js.Json.object_,
- ),
- ]
+ "online",
+ [("user_agent", BrowserSpec.navigator.userAgent->Js.Json.string)]
->Js.Dict.fromArray
->Js.Json.object_,
),
+ ]
+ ->Js.Dict.fromArray
+ ->Js.Json.object_
+
+let mandateBody = paymentType => {
+ [
+ (
+ "mandate_data",
+ [("customer_acceptance", customerAcceptanceBody)]->Js.Dict.fromArray->Js.Json.object_,
+ ),
+ ("customer_acceptance", customerAcceptanceBody),
("setup_future_usage", "off_session"->Js.Json.string),
("payment_type", {paymentType === "" ? Js.Json.null : paymentType->Js.Json.string}),
]
diff --git a/src/Utilities/PaymentUtils.res b/src/Utilities/PaymentUtils.res
index fb0564c..983a550 100644
--- a/src/Utilities/PaymentUtils.res
+++ b/src/Utilities/PaymentUtils.res
@@ -227,3 +227,13 @@ let getPaymentMethodName = (~paymentMethodType, ~paymentMethodName) => {
paymentMethodName
}
}
+
+let isAppendingCustomerAcceptance = (~isGuestCustomer, ~paymentType) => {
+ !isGuestCustomer && (paymentType === "new_mandate" || paymentType === "setup_mandate")
+}
+
+let appendedCustomerAcceptance = (~isGuestCustomer, ~paymentType, ~body) => {
+ isAppendingCustomerAcceptance(~isGuestCustomer, ~paymentType)
+ ? body->Array.concat([("customer_acceptance", PaymentBody.customerAcceptanceBody)])
+ : body
+}
diff --git a/src/Utilities/Utils.res b/src/Utilities/Utils.res
index afad299..fe3be5a 100644
--- a/src/Utilities/Utils.res
+++ b/src/Utilities/Utils.res
@@ -86,6 +86,14 @@ let getDecodedStringFromJson = (json, callbackFunc, defaultValue) => {
->Belt.Option.getWithDefault(defaultValue)
}
+let getDecodedBoolFromJson = (json, callbackFunc, defaultValue) => {
+ json
+ ->Js.Json.decodeObject
+ ->Belt.Option.flatMap(callbackFunc)
+ ->Belt.Option.flatMap(Js.Json.decodeBoolean)
+ ->Belt.Option.getWithDefault(defaultValue)
+}
+
let getRequiredString = (dict, key, default, ~logger) => {
let optionalStr = getOptionString(dict, key)
switch optionalStr {
@@ -845,3 +853,7 @@ let isOtherElements = componentType => {
}
let nbsp = `\u00A0`
+
+let callbackFuncForExtractingValFromDict = key => {
+ x => x->Js.Dict.get(key)
+}
diff --git a/src/orca-loader/Elements.res b/src/orca-loader/Elements.res
index 6ccb392..6df044b 100644
--- a/src/orca-loader/Elements.res
+++ b/src/orca-loader/Elements.res
@@ -220,7 +220,7 @@ let make = (
selectorString,
sdkHandleConfirmPayment,
sdkHandleOneClickConfirmPayment,
- disableSaveCards,
+ displaySavedPaymentMethods,
) => {
open Promise
@@ -714,7 +714,9 @@ let make = (
})
->ignore
fetchPaymentsList(mountedIframeRef)
- disableSaveCards ? () : fetchCustomerDetails(mountedIframeRef)
+ if displaySavedPaymentMethods {
+ fetchCustomerDetails(mountedIframeRef)
+ }
mountedIframeRef->Window.iframePostMessage(message)
}
diff --git a/src/orca-loader/LoaderPaymentElement.res b/src/orca-loader/LoaderPaymentElement.res
index df198cc..e342429 100644
--- a/src/orca-loader/LoaderPaymentElement.res
+++ b/src/orca-loader/LoaderPaymentElement.res
@@ -20,25 +20,22 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) =
}
let sdkHandleConfirmPayment =
- options
- ->Js.Json.decodeObject
- ->Belt.Option.flatMap(x => x->Js.Dict.get("sdkHandleConfirmPayment"))
- ->Belt.Option.flatMap(Js.Json.decodeBoolean)
- ->Belt.Option.getWithDefault(false)
+ options->getDecodedBoolFromJson(
+ callbackFuncForExtractingValFromDict("sdkHandleConfirmPayment"),
+ false,
+ )
let sdkHandleOneClickConfirmPayment =
- options
- ->Js.Json.decodeObject
- ->Belt.Option.flatMap(x => x->Js.Dict.get("sdkHandleOneClickConfirmPayment"))
- ->Belt.Option.flatMap(Js.Json.decodeBoolean)
- ->Belt.Option.getWithDefault(true)
-
- let disableSaveCards =
- options
- ->Js.Json.decodeObject
- ->Belt.Option.flatMap(x => x->Js.Dict.get("disableSaveCards"))
- ->Belt.Option.flatMap(Js.Json.decodeBoolean)
- ->Belt.Option.getWithDefault(false)
+ options->getDecodedBoolFromJson(
+ callbackFuncForExtractingValFromDict("sdkHandleOneClickConfirmPayment"),
+ true,
+ )
+
+ let displaySavedPaymentMethods =
+ options->getDecodedBoolFromJson(
+ callbackFuncForExtractingValFromDict("displaySavedPaymentMethods"),
+ true,
+ )
let on = (eventType, eventHandler) => {
switch eventType->eventTypeMapper {
@@ -308,7 +305,7 @@ let make = (componentType, options, setIframeRef, iframeRef, mountPostMessage) =
localSelectorString,
sdkHandleConfirmPayment,
sdkHandleOneClickConfirmPayment,
- disableSaveCards,
+ displaySavedPaymentMethods,
)
}
}
diff --git a/webpack.common.js b/webpack.common.js
index c828703..c6602f6 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -9,16 +9,17 @@ 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;
+
//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 = `/${repoVersion}/${majorVersion}`;
-
-const sdkEnv = process.env.sdkEnv;
-const envSdkUrl = process.env.envSdkUrl;
-const envBackendUrl = process.env.envBackendUrl;
+let repoPublicPath =
+ sdkEnv === "local" ? "" : `/${repoVersion}/${majorVersion}`;
let sdkUrl;