diff --git a/packages/payment-widget/src/lib/components/buyer-info-form.svelte b/packages/payment-widget/src/lib/components/buyer-info-form.svelte new file mode 100644 index 00000000..503066f2 --- /dev/null +++ b/packages/payment-widget/src/lib/components/buyer-info-form.svelte @@ -0,0 +1,280 @@ + + +
+

Buyer Information

+
+
+ + + {#if errors.email}{errors.email}{/if} +
+
+
+ + + {#if errors.firstName}{errors.firstName}{/if} +
+
+ + + {#if errors.lastName}{errors.lastName}{/if} +
+
+
+
+ + +
+
+ + + {#if errors.phone}{errors.phone}{/if} +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + diff --git a/packages/payment-widget/src/lib/components/currency-selector.svelte b/packages/payment-widget/src/lib/components/currency-selector.svelte index bc5b75b6..68a88866 100644 --- a/packages/payment-widget/src/lib/components/currency-selector.svelte +++ b/packages/payment-widget/src/lib/components/currency-selector.svelte @@ -6,13 +6,12 @@ export let currencies: Currency[]; export let selectedCurrency: Currency | null = null; - export let currentPaymentStep: PaymentStep; export let web3Modal: Web3Modal | null; export let isConnected: boolean; + export let onCurrencySelected: () => void; function selectCurrency(currency: Currency) { selectedCurrency = currency; - currentPaymentStep = "confirmation"; } @@ -43,16 +42,20 @@ {/each} + diff --git a/packages/payment-widget/src/lib/components/payment-confirmation.svelte b/packages/payment-widget/src/lib/components/payment-confirmation.svelte index 4211339a..c718e63c 100644 --- a/packages/payment-widget/src/lib/components/payment-confirmation.svelte +++ b/packages/payment-widget/src/lib/components/payment-confirmation.svelte @@ -5,7 +5,13 @@ import InfoCircleIcon from "@requestnetwork/shared-icons/info-circle.svelte"; import type { Web3Modal } from "@web3modal/ethers5"; import { onDestroy, onMount } from "svelte"; - import type { Currency, PaymentStep } from "../types"; + import type { + Currency, + PaymentStep, + SellerInfo, + BuyerInfo, + ProductInfo, + } from "../types"; import { chains } from "../utils/chains"; import { NETWORK_LABEL } from "../utils/currencies"; import { @@ -17,16 +23,20 @@ export let selectedCurrency: Currency; export let amountInUSD: number; - export let sellerName: string | undefined; - export let productName: string | undefined; + export let sellerInfo: SellerInfo; + export let buyerInfo: BuyerInfo; + export let productInfo: ProductInfo | undefined; export let sellerAddress: string; export let currentPaymentStep: PaymentStep; export let web3Modal: Web3Modal | null; export let isConnected: boolean; export let builderId: string; export let persistRequest: boolean; + export let enableBuyerInfo: boolean; export let onPaymentSuccess: (request: any) => void; export let onPaymentError: (error: string) => void; + export let invoiceNumber: string | undefined; + const COUNTDOWN_INTERVAL = 30; let amountInCrypto: number = 0; @@ -182,7 +192,11 @@
void; export let onError: (error: string) => void; + export let buyerInfo: BuyerInfo | undefined = undefined; + export let enableBuyerInfo: boolean = true; + export let invoiceNumber: string | undefined = undefined; + // State let web3Modal: Web3Modal | null = null; let currencyDetails: ReturnType; let isCheckingConnection = false; let selectedCurrency: Currency | null = null; let connectionCheckInterval: ReturnType | null = null; let currentPaymentStep: PaymentStep = "currency"; + let currentBuyerInfo: BuyerInfo = buyerInfo || { + address: {}, + }; + let scrollPosition = 0; + // Effects $: currencyDetails = getSupportedCurrencies(supportedCurrencies); $: isConnected = false; @@ -58,6 +70,9 @@ } } + $: toggleBodyScroll(isModalOpen); + + // Methods async function checkConnectionStatus() { if (isCheckingConnection) return; isCheckingConnection = true; @@ -80,15 +95,6 @@ } } - onMount(() => { - web3Modal = initWalletConnector(); - - if (web3Modal) { - web3Modal.subscribeEvents(handleWeb3ModalEvents); - startConnectionCheck(); - } - }); - function handleWeb3ModalEvents(newEvent: EventsControllerState) { if (newEvent.data.event === "MODAL_LOADED") { checkWalletState(); @@ -101,8 +107,6 @@ } } - let scrollPosition = 0; - function toggleBodyScroll(isDisabled: boolean) { if (isDisabled) { scrollPosition = window.pageYOffset; @@ -119,16 +123,32 @@ } } - $: toggleBodyScroll(isModalOpen); + function checkWalletState() { + isConnected = web3Modal?.getIsConnected() ?? false; + } + + function handleCurrencySelection() { + if (enableBuyerInfo) { + currentPaymentStep = "buyer-info"; + } else { + currentPaymentStep = "confirmation"; + } + } + + // Lifecycles + onMount(() => { + web3Modal = initWalletConnector(); + + if (web3Modal) { + web3Modal.subscribeEvents(handleWeb3ModalEvents); + startConnectionCheck(); + } + }); onDestroy(() => { stopConnectionCheck(); toggleBodyScroll(false); }); - - function checkWalletState() { - isConnected = web3Modal?.getIsConnected() ?? false; - }
@@ -189,13 +209,18 @@ {web3Modal} currencies={currencyDetails.currencies} bind:selectedCurrency - bind:currentPaymentStep bind:isConnected + onCurrencySelected={handleCurrencySelection} + /> + {:else if currentPaymentStep === "buyer-info"} + {:else if selectedCurrency && currentPaymentStep === "confirmation"} {:else} diff --git a/packages/payment-widget/src/lib/react/PaymentWidget.d.ts b/packages/payment-widget/src/lib/react/PaymentWidget.d.ts index fad11bcf..6dea12ad 100644 --- a/packages/payment-widget/src/lib/react/PaymentWidget.d.ts +++ b/packages/payment-widget/src/lib/react/PaymentWidget.d.ts @@ -4,6 +4,7 @@ import type { ProductInfo, AmountInUSD, SupportedCurrencies, + BuyerInfo, } from "../types"; export interface PaymentWidgetProps { @@ -17,6 +18,9 @@ export interface PaymentWidgetProps { builderId?: string; onPaymentSuccess?: (request: any) => void; onError?: (error: string) => void; + buyerInfo?: BuyerInfo; + enableBuyerInfo?: boolean; + invoiceNumber?: string; } /** @@ -30,6 +34,7 @@ export interface PaymentWidgetProps { * - Supports multiple cryptocurrencies * - Handles transaction creation and management * - Provides real-time payment status updates + * - Supports full invoice information * * @param {PaymentWidgetProps} props - The component props * @returns {JSX.Element} @@ -38,7 +43,15 @@ export interface PaymentWidgetProps { * { * console.log(request); * }} diff --git a/packages/payment-widget/src/lib/types/index.ts b/packages/payment-widget/src/lib/types/index.ts index 990babcb..31e6289e 100644 --- a/packages/payment-widget/src/lib/types/index.ts +++ b/packages/payment-widget/src/lib/types/index.ts @@ -1,9 +1,36 @@ import type { CURRENCY_ID, NETWORK_LABEL } from "../utils/currencies"; -export type SellerInfo = { +export interface Address { + "street-address"?: string; + locality?: string; + region?: string; + "country-name"?: string; + "postal-code"?: string; +} + +export interface SellerInfo { logo?: string; name?: string; -}; + email?: string; + firstName?: string; + lastName?: string; + businessName?: string; + phone?: string; + address?: Address; + taxRegistration?: string; + companyRegistration?: string; +} + +export interface BuyerInfo { + email?: string; + firstName?: string; + lastName?: string; + businessName?: string; + phone?: string; + address?: Address; + taxRegistration?: string; + companyRegistration?: string; +} export type ProductInfo = { name?: string; @@ -27,4 +54,8 @@ export type Currency = { name?: string; }; -export type PaymentStep = "currency" | "confirmation" | "complete"; +export type PaymentStep = + | "currency" + | "buyer-info" + | "confirmation" + | "complete"; diff --git a/packages/payment-widget/src/lib/utils/request.ts b/packages/payment-widget/src/lib/utils/request.ts index 21142bab..e1790424 100644 --- a/packages/payment-widget/src/lib/utils/request.ts +++ b/packages/payment-widget/src/lib/utils/request.ts @@ -11,7 +11,7 @@ import { } from "@requestnetwork/request-client.js"; import { Web3SignatureProvider } from "@requestnetwork/web3-signature"; import { providers, utils } from "ethers"; -import type { Currency } from "../types"; +import type { BuyerInfo, Currency, ProductInfo, SellerInfo } from "../types"; import { chains } from "./chains"; export const prepareRequestParameters = ({ @@ -23,8 +23,10 @@ export const prepareRequestParameters = ({ amountInUSD, createdWith, builderId, - productName, - sellerName, + productInfo, + sellerInfo, + buyerInfo, + invoiceNumber, }: { currency: Currency; sellerAddress: string; @@ -34,14 +36,17 @@ export const prepareRequestParameters = ({ amountInUSD: number; builderId: string; createdWith: string; - productName: string | undefined; - sellerName: string | undefined; + productInfo: ProductInfo | undefined; + sellerInfo: SellerInfo; + buyerInfo: BuyerInfo; + invoiceNumber?: string; }) => { const isERC20 = currency.type === Types.RequestLogic.CURRENCY.ERC20; const currencyValue = isERC20 ? currency.address : "eth"; const amount = utils .parseUnits(amountInCrypto.toFixed(currency.decimals), currency.decimals) .toString(); + return { requestInfo: { currency: { @@ -69,7 +74,6 @@ export const prepareRequestParameters = ({ paymentAddress: sellerAddress, feeAddress: "0x0000000000000000000000000000000000000000", feeAmount: "0", - tokenAddress: currencyValue, }, }, contentData: { @@ -78,11 +82,43 @@ export const prepareRequestParameters = ({ version: "0.0.3", }, creationDate: new Date().toISOString(), - invoiceNumber: "rn-checkout", + invoiceNumber: invoiceNumber || "receipt", note: `Sale made with ${currency.symbol} on ${currency.network} for amount of ${amountInUSD} USD with an exchange rate of ${exchangeRate}`, + sellerInfo: { + email: sellerInfo?.email || undefined, + firstName: sellerInfo?.firstName || undefined, + lastName: sellerInfo?.lastName || undefined, + businessName: sellerInfo?.businessName || undefined, + phone: sellerInfo?.phone || undefined, + address: sellerInfo?.address + ? { + streetAddress: sellerInfo.address["street-address"] || undefined, + locality: sellerInfo.address.locality || undefined, + postalCode: sellerInfo.address["postal-code"] || undefined, + country: sellerInfo.address["country-name"] || undefined, + } + : undefined, + taxRegistration: sellerInfo?.taxRegistration || undefined, + }, + buyerInfo: { + email: buyerInfo?.email || undefined, + firstName: buyerInfo?.firstName || undefined, + lastName: buyerInfo?.lastName || undefined, + businessName: buyerInfo?.businessName || undefined, + phone: buyerInfo?.phone || undefined, + address: buyerInfo?.address + ? { + streetAddress: buyerInfo.address["street-address"] || undefined, + locality: buyerInfo.address.locality || undefined, + postalCode: buyerInfo.address["postal-code"] || undefined, + country: buyerInfo.address["country-name"] || undefined, + } + : undefined, + taxRegistration: buyerInfo?.taxRegistration || undefined, + }, invoiceItems: [ { - name: productName || "", + name: productInfo?.name || "Unnamed product", quantity: 1, unitPrice: amount, discount: "0", @@ -93,13 +129,9 @@ export const prepareRequestParameters = ({ currency: isERC20 ? currency.address : currency.symbol, }, ], - paymentTerms: { dueDate: new Date().toISOString(), }, - sellerInfo: { - businessName: sellerName || undefined, - }, miscellaneous: { exchangeRate: exchangeRate.toString(), amountInUSD: amountInUSD.toString(),