import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" import { Environment, initialize, UserProfile, setPassportCloseCallback, registerBNPLPaymentCompleteHandler, getPreparedWindowToOpen, startServiceInstanceFlow, loadUserCredential, saveUserCredential, Offer, } from "credify-web-sdk" import "antd/dist/antd.css" import CredifyLogo from "../../resources/credify-group-logo-title.svg" import FullScreenLoading from "../../components/FullSceenLoading" import { useLocation } from "react-router-dom" import { getMarketApiUrl, setMarketEnvironment } from "../../config" import { LoginButton, LoginText, CredifyImage, CredifyImageWrapper, ServiceInstanceWrapper, } from "./styled" import CredifyLogo2 from "../../resources/credify.svg" import { initOffer, mockProducts } from "./mock-bnpl-data" import { ProductListStyled, IdWrapper, CurentText, IdInput, FetchInput, SliderWrapperStyled, BnplWrapper, BnplBackground, ProductContentWrapper, ProductContent, ProductBlockWrapper, CheckboxStyled, } from "./BnplPage.styled" import { BNPL_THEME_DEFAULT } from "./type" import PaymentSucceedModal from "../../components/modals/paymentSucceed/paymentSucceedModal" import spBackground from "../../assets/img/sp-bg-placeholder.png" import { OfferType, OrderLine } from "../../types/common" import { getDemoUser, lodgeNewIntent, updateUserProfile } from "../../apis/bnpl" import _ from "lodash" import { formatCurrency } from "../../utils/parsers" import InputNumberField from "../../components/input/number" import CheckboxField from "../../components/input/checkbox" import { createLodgeRequest, createOrderLines, getTotalAmount, getTotalQuantity, getUrlQueryParams, isSafari, openPopupWindow, } from "../../utils/helpers" import { PRODUCT_TYPE } from "credify-web-sdk" import { getOffers } from "../../apis" import ProductCodeSelect from "../../components/ProductCodeSelect" const MARKET_NAME = "bnpl-consumer" const DEFAULT_API_KEY = "UwbxwqQXnY66dtQm57Bt98la4AwSQodUeMyNThny4aQ7SVoN9IDrhctBCkgWdt6W" const DEFAULT_MARKET_ID = "8319dd85-7b57-4455-a8dc-5fb1d142a400" /** * This will be used for BNPL new flow */ function BnplPage() { const NONE_PRODUCT_CODE = "---none---"; const cacheUserProfile = loadUserCredential() const params = getUrlQueryParams(window.location.search) const orderId = params?.order_id const [user, setUser] = useState() const [withEkyc, setWithEkyc] = useState(true) const [isLoading, setLoading] = useState(false) const [idText, setIdText] = useState( orderId && cacheUserProfile?.id ? cacheUserProfile?.id : "" ) const location = useLocation() const inputRef = useRef(null) const [showModal, setShowModal] = useState(false) const currentCart: OfferType = initOffer const [products, setProducts] = useState<{ [key: string]: any }>(mockProducts) const [offers, setOffers] = useState([]) const [productCodes, setProductCodes] = useState< Array<{ label?: string; value?: string }> >([]) const [selectedProductCode, setSelectedProductCode] = useState("") const [openWithRedirect, setOpenWithRedirect] = useState(true) const [selectedLocale, setSelectedLocale] = useState("") const [useUICustomization, setUseUICustomization] = useState(false) const LANGUAGES: Array<{ label: string; value?: string }> = [ { label: "None", value: "", }, { label: "VN", value: "vi-VN" }, { label: "EN", value: "en-US" }, { label: "JP", value: "ja-JP" } ] const urlSearch = new URLSearchParams(location.search) const env = urlSearch.get("env") useEffect(() => { const apiKey = urlSearch.get("apiKey") || DEFAULT_API_KEY const marketId = urlSearch.get("marketId") || DEFAULT_MARKET_ID const useUICustomization = urlSearch.get("ui-customization") === "true"; setUseUICustomization(useUICustomization) initialize( env as Environment, apiKey, marketId, useUICustomization? BNPL_THEME_DEFAULT : undefined ) // Demo Market environment setMarketEnvironment(env as Environment) registerBNPLPaymentCompleteHandler(() => { // *** Successful Payment popup here setShowModal(true) }) loadData() // Handle flow redirect handleEventFromRedirect() }, []) useEffect(() => { setPassportCloseCallback(() => { loadData() }) }, [user]) useEffect(() => { if (!productCodes || productCodes.length === 0) return setSelectedProductCode(productCodes[0].value!) }, [productCodes.length]) const handleEventFromRedirect = async () => { if (!params) return if (orderId && cacheUserProfile?.id) { setShowModal(true) await getUserProfile() } } async function loadData() { console.log("Market Data is loading....") setLoading(true) const userProfile = await getUserProfile() const credifyId = userProfile?.credifyId || "" // TODO need to set access token when the BE requires in the future const result = await getOffers(MARKET_NAME, "", { localId: `${userProfile?.id || ""}`, phoneNumber: userProfile?.phoneNumber, countryCode: userProfile?.countryCode, credifyId, }) if (result?.success && result.data) { setOffers(result.data.offers || []) const productCodes = result.data.offers .filter((offer) => offer.campaign?.product?.code) .map((offer) => offer?.campaign?.product?.code as string) if (productCodes.length > 0) { const productCodesViewModel = productCodes.map((code) => ({ label: code, value: code, })) setProductCodes([ { label: "None", value: NONE_PRODUCT_CODE, }, ...productCodesViewModel ]) } } setLoading(false) console.log("Market Data finish loading....") } async function getUserProfile(): Promise { const _user = await getDemoUser( MARKET_NAME, //@ts-ignore inputRef?.current?.value.toString() ) if (!_user) return const userProfile: UserProfile = { id: `${_user.id}`, firstName: _user.firstName, lastName: _user.lastName, middleName: _user.middleName, fullName: _user.fullName, phoneNumber: _user.phoneNumber, countryCode: _user.phoneCountryCode, email: _user.email, credifyId: _user.credifyId, } console.log({ userProfile }) setUser(userProfile) setIdText(_user.id) saveUserCredential(JSON.stringify(userProfile)) return userProfile } async function pushClaimCB( externalId: string, resolve: (isSuccess: boolean) => void ) { console.log("Push claim token......") try { const res = await pushClaim(user!.id!, externalId) console.log({ res }) resolve(true) } catch (error) { resolve(false) } } async function pushClaim(localId: string, credifyId: string) { const body = { id: localId, credify_id: credifyId, } try { const pushClaimUrl = `${getMarketApiUrl()}/${MARKET_NAME}/push-claims` console.log({ pushClaimUrl }) const res = await fetch(pushClaimUrl, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(body), }) return res.json() } catch (error) { throw error } } function randomUser() { setIdText("") setTimeout(() => { loadData() }, 300) } const getIntentLink = (intentUrl: string) => { if (!window.location.host.includes("localhost")) return intentUrl let intentRegex = /.+(?=\/dop)/g const intentLocalHost = intentUrl.replace( intentRegex, "http://localhost:4200" ) return intentLocalHost } const handleClickBnplWithIntent = async () => { const locale = selectedLocale const selectedOffer = offers?.find((item) => item.campaign?.product?.code === selectedProductCode); const selectedProductType = selectedOffer?.campaign?.product?.productTypeCode; const currentProducts = _.clone(products) Object.keys(products).forEach((index) => { if (!currentProducts[index].selected) { delete currentProducts[index] } }) const updatedCart = _.clone(currentCart) updatedCart.total_amount.value = totalAmount.toString() let order_lines: OrderLine[] = [] as OrderLine[] order_lines = createOrderLines(currentProducts) updatedCart.order_lines = order_lines updatedCart.reference_id = `reference_id_${+new Date()}` updatedCart.is_business = selectedProductType === PRODUCT_TYPE.CORPORATE_FINANCING__UNSECURED_LOAN__PO_FINANCING; let windowReference: Window | null = null const isBrowserSafari = isSafari() // On this async/await function, to avoid Safari open new popup issue // All the `async` logic should behind the getPreparedWindowToOpen() function if (!openWithRedirect && isBrowserSafari) { windowReference = getPreparedWindowToOpen() } if (updatedCart) { setLoading(true) const offerCodes = offers.map((offer) => offer.code) as string[] console.log("updatedCart", updatedCart) const user = await getUserProfile() updatedCart.user_id = user?.id! console.log("user", user) const result = await lodgeNewIntent( MARKET_NAME, createLodgeRequest( "BNPL", updatedCart, user, selectedProductCode === NONE_PRODUCT_CODE ? undefined : [selectedProductCode], offerCodes.length > 0 ? offerCodes : undefined, useUICustomization ? BNPL_THEME_DEFAULT : undefined ) ) console.log("result", result) setLoading(false) let appUrl = getIntentLink(result?.appUrl) as string if (appUrl) { appUrl = locale ? `${appUrl}/${locale}` : appUrl if (openWithRedirect) { window.location.href = appUrl return } openPopupWindow(appUrl, windowReference!) } } } const onChangeQuantity = useCallback( (field: string, value: number) => { const product = field.split("_")[0] const fieldChange = field.split("_")[1] let updatedProduct = _.clone(products) if (value < 0) return updatedProduct[product][fieldChange] = value ? Number(value) : 0 setProducts(updatedProduct) }, [products] ) const onChangeProductInfo = useCallback( (e: any) => { const value = e.target.value const product = e.target.name.split("_")[0] const fieldChange = e.target.name.split("_")[1] let updatedProduct = _.clone(products) if (fieldChange === "quantity") { updatedProduct[product][fieldChange] = value ? Number(value) : 0 } else if (fieldChange === "selected") { updatedProduct[product][fieldChange] = e.target.checked } else { updatedProduct[product][fieldChange] = value ? formatCurrency(value) : "0" } setProducts(updatedProduct) }, [products] ) const renderProductBlock = useMemo(() => { return Object.keys(products).map((item, index) => { return (
 
{products[item].name}

{products[item].description}

onChangeProductInfo(e)} name={`${item}_price`} color="#3abb88" width={70} outline={false} currency="đ" />
onChangeProductInfo(e)} />
onChangeProductInfo(e)} name={`${item}_quantity`} color="#000000" width={40} outline={true} hasControl onChangeQuantity={onChangeQuantity} />
{index + 1}
X
) }) }, [onChangeProductInfo, onChangeQuantity, products]) const totalAmount = useMemo(() => { const sum = getTotalAmount(products) return sum }, [products]) const totalQuantity = useMemo(() => { const sum = getTotalQuantity(products) return sum }, [products]) const goToServiceInstance = ( e: React.MouseEvent ) => { e.preventDefault() startServiceInstanceFlow(MARKET_NAME, user, [ PRODUCT_TYPE.CONSUMER_FINANCING__UNSECURED_LOAN__BNPL, ]) } const updatePhoneNumber = async () => { // Input country code let countryCode, phoneNumber let inputCountryCode = prompt("Please enter country code:", "+84") if (inputCountryCode) { countryCode = inputCountryCode } // Input phone number let inputPhoneNumber = prompt("Please enter phone number:", "") if (inputPhoneNumber) { phoneNumber = inputPhoneNumber } if (countryCode && phoneNumber) { try { await updateUserProfile(MARKET_NAME, user?.id || "", { countryCode, phoneNumber, }) alert("Update user profile successful!") } catch (error) { alert("Update user profile fail!") } } else { alert("Country code or Phone number is invalid!") } } const handleChangeSelectProductCode = (productCode: string) => { setSelectedProductCode(productCode) } const handleSelectedLanguage = (language: string) => { setSelectedLocale(language) } return (
{renderProductBlock}
{formatCurrency(totalAmount.toString())} đ
0 ? "visible" : "hidden", }} onClick={handleClickBnplWithIntent} > NHẬN HÀNG TRƯỚC TRẢ SAU

Cùng  Creditfy Credify

console.log("next")}> TIẾP TỤC
BNPL with eKYC
 
Open with redirect
 
Random user UserId: setIdText(e.target.value)} />
{showModal && ( )}
) } export { BnplPage }