From c8022fafb0a49827e7c66cb147cc060018d889da Mon Sep 17 00:00:00 2001 From: Hugo Morosini Date: Tue, 22 Mar 2022 14:48:51 +0100 Subject: [PATCH] created a special page for multibuy --- pages/buy.tsx | 48 +++++ src/Coinify/CoinifyWidgetBuy.tsx | 298 +++++++++++++++++++++++++++++++ 2 files changed, 346 insertions(+) create mode 100644 pages/buy.tsx create mode 100644 src/Coinify/CoinifyWidgetBuy.tsx diff --git a/pages/buy.tsx b/pages/buy.tsx new file mode 100644 index 0000000..ef8f40d --- /dev/null +++ b/pages/buy.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { useRouter } from "next/router"; +import CoinifyWidgetBuy from "../src/Coinify/CoinifyWidgetBuy"; + +type QueryParams = { + accountId?: string; + accountAddress?: string; + language?: string; + fiatCurrencyId?: string; + cryptoCurrencyId?: string; + primaryColor?: string; + mode?: "onRamp" | "offRamp"; + fiatAmount?: string; + cryptoAmount?: string; +}; + +const Page = () => { + const router = useRouter(); + + const { + accountAddress, + language, + fiatCurrencyId, + cryptoCurrencyId, + primaryColor, + fiatAmount, + cryptoAmount, + } = router.query as QueryParams; + + if (accountAddress && cryptoCurrencyId) { + return ( + + ); + } + + return null; +}; + +export default Page; diff --git a/src/Coinify/CoinifyWidgetBuy.tsx b/src/Coinify/CoinifyWidgetBuy.tsx new file mode 100644 index 0000000..06d1055 --- /dev/null +++ b/src/Coinify/CoinifyWidgetBuy.tsx @@ -0,0 +1,298 @@ +// @flow + +import React, { useState, useEffect, useRef, useCallback } from "react"; +import styled from "styled-components"; +import querystring from "querystring"; +import { useApi } from "../providers/LedgerLiveSDKProvider"; + +type CoinifyConfig = { + host: string; + url: string; + partnerId: string; +}; + +const COINIFY_CONFIG: { [key: string]: CoinifyConfig } = { + sandbox: { + host: "https://trade-ui.sandbox.coinify.com", + url: "https://trade-ui.sandbox.coinify.com/widget", + partnerId: "191f0c7f-076d-459f-bf2d-833465bfadc2", + }, + prod: { + host: "https://trade-ui.coinify.com", + url: "https://trade-ui.coinify.com/widget", + partnerId: "191f0c7f-076d-459f-bf2d-833465bfadc2", + }, +}; + +const CustomIframe = styled.iframe` + border: none; + width: 100%; + height: 100%; + flex: 1; + transition: opacity 200ms ease-out; +`; + +type CoinifyWidgetConfig = { + primaryColor?: string; + partnerId: string; + cryptoCurrencies?: string | null; + defaultFiatCurrency?: string; + address?: string | null; + targetPage: string; + addressConfirmation?: boolean; + transferConfirmation?: boolean; + transferOutMedia?: string; + transferInMedia?: string; + confirmMessages?: boolean; + buyAmount?: string; + sellAmount?: string; +}; + +type Props = { + accountAddress: string; + currency: string; + fiatCurrencyId?: string; + mode: "onRamp" | "offRamp" | "history"; + cryptoAmount?: string; + fiatAmount?: string; + language?: string; + primaryColor?: string; +}; + +const CoinifyWidgetBuy = ({ + accountAddress, + currency, + fiatCurrencyId, + mode, + fiatAmount, + cryptoAmount, + primaryColor, +}: Props) => { + const api = useApi(); + + const env = new URLSearchParams(window.location.search).get("env") || "prod"; + + const tradeId = useRef(null); + const [widgetLoaded, setWidgetLoaded] = useState(false); + + const widgetRef: { current: null | HTMLIFrameElement } = useRef(null); + + const coinifyConfig = COINIFY_CONFIG[env]; + const widgetConfig: CoinifyWidgetConfig = { + primaryColor, + partnerId: coinifyConfig.partnerId, + cryptoCurrencies: currency ? currency : null, + defaultFiatCurrency: fiatCurrencyId ? fiatCurrencyId : undefined, + address: accountAddress, + targetPage: mode, + }; + + // FIXME: could use switch case? + if (mode === "onRamp") { + widgetConfig.transferOutMedia = "blockchain"; + widgetConfig.confirmMessages = true; + widgetConfig.buyAmount = fiatAmount; + widgetConfig.sellAmount = cryptoAmount; + } + + if (mode === "offRamp") { + widgetConfig.transferInMedia = "blockchain"; + widgetConfig.confirmMessages = true; + widgetConfig.buyAmount = fiatAmount; + widgetConfig.sellAmount = cryptoAmount; + } + + if (mode === "history") { + widgetConfig.transferOutMedia = ""; + widgetConfig.transferInMedia = ""; + } + + useEffect(() => { + if (!currency) return; + if (mode === "onRamp" && accountAddress) { + console.log(`Coinify Start OnRamp Widget | currencyName: ${currency}`); + } + if (mode === "offRamp" && accountAddress) { + console.log(`Coinify Start OffRamp Widget | currencyName: ${currency}`); + } + if (mode === "history") { + console.log("Coinify Start History Widget"); + } + }, [accountAddress, currency, mode]); + + const url = `${coinifyConfig.url}?${querystring.stringify(widgetConfig)}`; + + const handleOnResultBuy = useCallback( + (address: string) => { + if ( + !widgetRef?.current?.contentWindow || + !accountAddress || + mode !== "onRamp" + ) { + return; + } + + widgetRef.current.contentWindow.postMessage( + { + type: "event", + event: "trade.confirm-trade-prepared", + context: { + address, + confirmed: true, + }, + }, + coinifyConfig.host + ); + if (currency) { + console.log(`Coinify Confirm OnRamp End | currencyName: ${currency}`); + } + }, + [coinifyConfig.host, currency, accountAddress, mode] + ); + + const handleOnResult = useCallback(() => { + if (widgetRef?.current?.contentWindow) { + if (accountAddress && mode === "onRamp") { + widgetRef.current.contentWindow.postMessage( + { + type: "event", + event: "trade.confirm-trade-prepared", + context: { + address: accountAddress, + confirmed: true, + }, + }, + coinifyConfig.host + ); + if (currency) { + console.log(`Coinify Confirm Buy End | currencyName: ${currency}`); + } + } + if (tradeId.current && mode === "offRamp") { + widgetRef.current.contentWindow.postMessage( + { + type: "event", + event: "trade.confirm-trade-created", + context: { + confirmed: true, + transferInitiated: true, + tradeId: tradeId.current, + }, + }, + coinifyConfig.host + ); + if (currency) { + console.log(`Coinify Confirm Sell End | currencyName: ${currency}`); + } + } + } + }, [coinifyConfig.host, currency, accountAddress, mode]); + + const handleOnCancel = useCallback(() => { + if (widgetRef?.current?.contentWindow) { + if (mode === "onRamp" && accountAddress) { + widgetRef.current.contentWindow.postMessage( + { + type: "event", + event: "trade.confirm-trade-prepared", + context: { + address: accountAddress, + confirmed: false, + }, + }, + coinifyConfig.host + ); + } + if (mode === "offRamp") { + widgetRef.current.contentWindow.postMessage( + { + type: "event", + event: "trade.confirm-trade-created", + context: { + confirmed: false, + }, + }, + coinifyConfig.host + ); + } + } + }, [coinifyConfig.host, accountAddress, mode]); + + useEffect(() => { + if (!accountAddress) return; + + function onMessage(e: any) { + if (!e.isTrusted || e.origin !== coinifyConfig.host || !e.data) return; + const { type, event, context } = e.data; + + if (type !== "event") return; + switch (event) { + case "trade.trade-created": + tradeId.current = context.id; + if (mode === "onRamp" && widgetRef.current?.contentWindow) { + widgetRef.current.contentWindow.postMessage( + { + type: "event", + event: "trade.confirm-trade-created", + context: { + confirmed: true, + tradeId: tradeId.current, + }, + }, + coinifyConfig.host + ); + } + break; + case "trade.trade-prepared": + break; + case "trade.receive-account-changed": + if (accountAddress && context.address === accountAddress) { + // FIXME: VERIFY ADDRESS + + // FIXME: handle cancel / error + handleOnResultBuy(accountAddress); + + if (currency) { + console.log( + `Coinify Confirm Buy Start | currencyName: ${currency}` + ); + } + } else { + // Address mismatch, potential attack + } + break; + case "trade.trade-placed": + if (currency) { + console.log( + `Coinify Widget Event Trade Placed | currencyName: ${currency}` + ); + } + break; + } + } + + window.addEventListener("message", onMessage, false); + return () => window.removeEventListener("message", onMessage, false); + }, [ + accountAddress, + coinifyConfig, + currency, + handleOnCancel, + handleOnResult, + handleOnResultBuy, + mode, + api, + ]); + + return ( + setTimeout(() => setWidgetLoaded(true), 500)} + allow="camera" + /> + ); +}; + +export default CoinifyWidgetBuy;