Create Invoice Form
A form for creating invoices in Request Network
Last updated
Was this helpful?
A form for creating invoices in Request Network
Last updated
Was this helpful?
The Create Invoice Form allows end-users to create an invoice using the Request Network. It is built using Svelte but compiled to a Web Component, making it usable in any web environment, regardless of the framework.
To install the component, use npm:
npm install @requestnetwork/create-invoice-form
Configure the Create Invoice Form web component by creating a reference to it, setting its properties, and passing the reference as a prop.
Initialize the RequestNetwork
object using an Ethers Signer
or Viem WalletClient
.
Use the config object to pass additional configuration options. Please replace the builderId
with your own, arbitrarily chosen ID. This is used to track how many invoices your application creates.
Use a context provider to reinitialize the Request Network instance when the wallet changes.
A list of custom currencies to extend the default currency list.
Specify types to avoid TypeScript errors.
config
IConfig
Additional configuration parameters
config.builderId
string
Unique builder ID, arbitrarily chosen, used for metrics
config.dashboardLink
string
Path to dashboard page
config.logo
string
Path to logo file
config.colors.main
string
Hex color code for primary buttons and labels
config.colors.secondary
string
Hex color code for for borders and accents
requestNetwork
The RequestNetwork instance
wagmiConfig
WagmiConfig
Wallet connector config
currencies
Currency[]
A list of custom currencies
Follow the instructions below to add the Create Invoice Form to a React or Next.js app. For a video explaining how to integrate, see the
Try it out
Demo Video
Integration Video
View on NPM
View Source
import React, {
createContext,
useContext,
useState,
ReactNode,
useEffect,
} from "react";
import { initializeRequestNetwork } from "./initializeRN";
import type { RequestNetwork } from "@requestnetwork/request-client.js";
import { useAccount, useWalletClient } from "wagmi";
interface ContextType {
requestNetwork: RequestNetwork | null;
}
const Context = createContext<ContextType | undefined>(undefined);
export const Provider = ({ children }: { children: ReactNode }) => {
const { data: walletClient } = useWalletClient();
const { address, isConnected, chainId } = useAccount();
const [requestNetwork, setRequestNetwork] = useState<RequestNetwork | null>(
null
);
useEffect(() => {
if (walletClient && isConnected && address && chainId) {
initializeRequestNetwork(setRequestNetwork, walletClient);
}
}, [walletClient, chainId, address, isConnected]);
return (
<Context.Provider
value={{
requestNetwork,
}}
>
{children}
</Context.Provider>
);
};
export const useAppContext = () => {
const context = useContext(Context);
if (!context) {
throw new Error("useAppContext must be used within a Context Provider");
}
return context;
};
import {
bsc,
celo,
base,
fuse,
zksync,
fantom,
coreDao,
polygon,
mainnet,
sepolia,
arbitrum,
moonbeam,
optimism,
avalanche,
gnosis,
} from "wagmi/chains";
import { http } from "wagmi";
import {
coinbaseWallet,
injectedWallet,
ledgerWallet,
metaMaskWallet,
safeWallet,
trustWallet,
walletConnectWallet,
} from "@rainbow-me/rainbowkit/wallets";
import { getDefaultConfig } from "@rainbow-me/rainbowkit";
export const rainbowKitConfig = getDefaultConfig({
appName: "Request Invoicing",
projectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID as string,
chains: [
bsc,
celo,
base,
fuse,
zksync,
gnosis,
fantom,
coreDao,
polygon,
mainnet,
sepolia,
arbitrum,
moonbeam,
optimism,
avalanche,
],
transports: {
[arbitrum.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_ARBITRUM_ONE ||
"https://arbitrum.llamarpc.com"
),
[avalanche.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_AVALANCHE || "https://avalanche.drpc.org"
),
[base.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_BASE || "https://base.llamarpc.com"
),
[bsc.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_BSC || "https://bsc.llamarpc.com"
),
[celo.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_CELO || "https://forno.celo.org"
),
[coreDao.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_CORE || "https://rpc.coredao.org"
),
[fantom.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_FANTOM ||
"https://endpoints.omniatech.io/v1/fantom/mainnet/public"
),
[fuse.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_FUSE || "https://fuse.drpc.org"
),
[mainnet.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_ETHEREUM || "https://eth.llamarpc.com"
),
[polygon.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_POLYGON || "https://1rpc.io/matic"
),
[moonbeam.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_MOONBEAM ||
"https://moonbeam-rpc.publicnode.com"
),
[optimism.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_OPTIMISM ||
"https://optimism.llamarpc.com"
),
[sepolia.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_SEPOLIA || "https://sepolia.drpc.org"
),
[gnosis.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_XDAI || "https://gnosis.drpc.org"
),
[zksync.id]: http(
process.env.NEXT_PUBLIC_RPC_URL_ZKSYNCERA ||
"https://mainnet.era.zksync.io"
),
},
wallets: [
{
groupName: "Recommended",
wallets: [injectedWallet, metaMaskWallet, walletConnectWallet],
},
{
groupName: "Others",
wallets: [safeWallet, coinbaseWallet, ledgerWallet, trustWallet],
},
],
ssr: true,
});
import { Types } from "@requestnetwork/request-client.js";
export const currencies = [
{
symbol: "FAU",
address: "0x370DE27fdb7D1Ff1e1BaA7D11c5820a324Cf623C",
network: "sepolia",
decimals: 18,
type: Types.RequestLogic.CURRENCY.ERC20,
},
{
symbol: "ETH",
address: "eth",
network: "sepolia",
decimals: 18,
type: Types.RequestLogic.CURRENCY.ETH,
},
];
import { IConfig } from "./types";
export const config: IConfig = {
builderId: "request-network", // Replace with your builder ID, arbitrarily chosen, used to identify your app
dashboardLink: "/",
logo: "/assets/logo-sm.svg",
colors: {
main: "#0BB489",
secondary: "#58E1A5",
},
};
import Head from "next/head";
import dynamic from "next/dynamic";
import { config } from "@/utils/config";
import { useAppContext } from "@/utils/context";
import { currencies } from "@/utils/currencies";
import { rainbowKitConfig as wagmiConfig } from "@/utils/wagmiConfig";
import { Spinner } from "@/components/ui";
const CreateInvoiceForm = dynamic(
() => import("@requestnetwork/create-invoice-form/react"),
{ ssr: false, loading: () => <Spinner /> }
);
export default function CreateInvoice() {
const { requestNetwork } = useAppContext();
return (
<>
<Head>
<title>Request Invoicing - Create an Invoice</title>
</Head>
<div className="container m-auto w-[100%]">
<CreateInvoiceForm
config={config}
currencies={currencies}
wagmiConfig={wagmiConfig}
requestNetwork={requestNetwork}
/>
</div>
</>
);
}
import { RequestNetwork } from "@requestnetwork/request-client.js";
import { Web3SignatureProvider } from "@requestnetwork/web3-signature";
import { getTheGraphClient } from "@requestnetwork/payment-detection";
export const initializeRequestNetwork = (setter: any, walletClient: any) => {
try {
const web3SignatureProvider = new Web3SignatureProvider(walletClient);
const requestNetwork = new RequestNetwork({
nodeConnectionConfig: {
baseURL: "https://gnosis.gateway.request.network/",
},
signatureProvider: web3SignatureProvider,
httpConfig: {
getConfirmationMaxRetry: 120,
},
paymentOptions: {
getSubgraphClient: (chain: string) => {
// Ternary because cannot dynamically access environment variables in the browser
const paymentsSubgraphUrl =
chain === "arbitrum-one"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_ARBITRUM_ONE || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-arbitrum-one/api"
: chain === "avalanche"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_AVALANCHE || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-avalanche/api"
: chain === "base"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_BASE || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-base/api"
: chain === "bsc"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_BSC || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-bsc/api"
: chain === "celo"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_CELO || "https://api.studio.thegraph.com/query/67444/request-payments-celo/version/latest"
: chain === "core"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_CORE || "https://thegraph.coredao.org/subgraphs/name/requestnetwork/request-payments-core"
: chain === "fantom"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_FANTOM || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-fantom/api"
: chain === "fuse"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_FUSE || "https://api.studio.thegraph.com/query/67444/request-payments-fuse/version/latest"
: chain === "mainnet"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_MAINNET || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-mainnet/api"
: chain === "matic"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_MATIC || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-matic/api"
: chain === "moonbeam"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_MOONBEAM || "https://api.studio.thegraph.com/query/67444/request-payments-moonbeam/version/latest"
: chain === "optimism"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_OPTIMISM || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-optimism/api"
: chain === "sepolia"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_SEPOLIA || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-sepolia/api"
: chain === "xdai"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_XDAI || "https://api.studio.thegraph.com/query/67444/request-payments-xdai/version/latest"
: chain === "zksyncera"
? process.env.NEXT_PUBLIC_PAYMENTS_SUBGRAPH_URL_ZKSYNCERA || "https://subgraph.satsuma-prod.com/e2e4905ab7c8/request-network--434873/request-payments-zksyncera/api"
: undefined;
if (!paymentsSubgraphUrl) {
throw new Error(
`Cannot get subgraph client for unknown chain: ${chain}`
);
}
return getTheGraphClient(chain, paymentsSubgraphUrl);
},
},
});
setter(requestNetwork);
} catch (error) {
console.error("Failed to initialize the Request Network:", error);
setter(null);
}
};
import { IConfig } from "@/utils/types";
import { Config as WagmiConfig } from "wagmi";
import type { RequestNetwork } from "@requestnetwork/request-client.js";
declare global {
namespace JSX {
interface IntrinsicElements {
"invoice-dashboard": InvoiceDashboardElement;
"create-invoice-form": CreateInvoiceFormElement;
}
}
}
interface InvoiceDashboardElement {
ref?: React.Ref<InvoiceDashboardProps>;
}
interface CreateInvoiceFormElement {
ref?: React.Ref<CreateInvoiceFormProps>;
}
interface InvoiceDashboardProps extends HTMLElement {
config: IConfig;
wagmiConfig: WagmiConfig;
requestNetwork: RequestNetwork;
currencies: any;
}
interface CreateInvoiceFormProps extends HTMLElement {
config: IConfig;
wagmiConfig: WagmiConfig;
requestNetwork: RequestNetwork;
currencies: any;
}