|
| 1 | +--- |
| 2 | +title: Integrate Solana Pay with Web3Auth |
| 3 | +image: "guides/guides-banners/solana.png" |
| 4 | +description: |
| 5 | + Learn how to integrate Solana Pay QR code payments with Web3Auth embedded wallets in your React |
| 6 | + application. |
| 7 | +type: guide |
| 8 | +tags: [plug and play, web, solana, solana pay, react, ed25519] |
| 9 | +date: January 15, 2025 |
| 10 | +author: Web3Auth Team |
| 11 | +--- |
| 12 | + |
| 13 | +import SEO from "@site/src/components/SEO"; |
| 14 | +import TabItem from "@theme/TabItem"; |
| 15 | +import Tabs from "@theme/Tabs"; |
| 16 | + |
| 17 | +<SEO |
| 18 | + title="Integrate Solana Pay with Web3Auth" |
| 19 | + description="Learn how to integrate Solana Pay QR code payments with Web3Auth embedded wallets in your React application." |
| 20 | + image="https://web3auth.io/docs/guides/guides-banners/multi.png" |
| 21 | + slug="/guides/solana-pay" |
| 22 | +/> |
| 23 | + |
| 24 | +In this guide, we'll explore how to integrate Web3Auth's embedded wallets with Solana Pay to create |
| 25 | +a seamless payment experience for your users. By combining Web3Auth's familiar Web2-like social |
| 26 | +logins with Solana Pay's QR code functionality, you can enable users to make payments directly from |
| 27 | +their Web3Auth-powered embedded wallet. |
| 28 | + |
| 29 | +As an overview, this integration allows users to: |
| 30 | + |
| 31 | +- Log in using familiar Web2 social providers (Google, Apple, etc.) |
| 32 | +- Generate Solana Pay QR codes for transactions |
| 33 | +- Make payments using their Web3Auth embedded wallet |
| 34 | + |
| 35 | +For those who want to skip straight to the code, you can find the complete implementation examples |
| 36 | +in our |
| 37 | +[GitHub repository](https://github.com/Web3Auth/web3auth-examples/tree/main/other/solana-pay-example). |
| 38 | + |
| 39 | +## How to set up Web3Auth Dashboard |
| 40 | + |
| 41 | +If you haven't already, sign up on the Web3Auth platform. It is free and gives you access to the |
| 42 | +Web3Auth's base plan. After the basic setup, explore other features and functionalities offered by |
| 43 | +the Web3Auth Dashboard. It includes custom verifiers, whitelabeling, analytics, and more. Head to |
| 44 | +[Web3Auth's documentation](/docs/dashboard) page for detailed instructions on setting up the |
| 45 | +Web3Auth Dashboard. |
| 46 | + |
| 47 | +## Prerequisites and Setup |
| 48 | + |
| 49 | +Before diving into the code, ensure you have the necessary libraries installed and your Web3Auth |
| 50 | +project configured. |
| 51 | + |
| 52 | +### Installation |
| 53 | + |
| 54 | +You'll need the following libraries in your project: |
| 55 | + |
| 56 | +- **`@solana/pay`**: The core Solana Pay protocol library. |
| 57 | +- **`bignumber.js`**: For accurate handling of large numbers, especially when dealing with token |
| 58 | + amounts. |
| 59 | +- **`@solana/web3.js`**: For interacting with the Solana blockchain, such as fetching balances or |
| 60 | + constructing transactions. |
| 61 | + |
| 62 | +```bash npm2yarn |
| 63 | +npm install @solana/pay bignumber.js @solana/web3.js |
| 64 | +``` |
| 65 | + |
| 66 | +### Dashboard Configuration |
| 67 | + |
| 68 | +Web3Auth's embedded wallets enable users to log in using familiar Web2 social logins by using Shamir |
| 69 | +Secret Sharing (MPC) to ensure the wallet key is distributed and non-custodial. |
| 70 | + |
| 71 | +1. **Create a Project**: Go to the [Web3Auth dashboard](https://dashboard.web3auth.io/) and create a |
| 72 | + new project. |
| 73 | +2. **Copy Client ID**: Once created, copy your Client ID from the dashboard. This ID is crucial for |
| 74 | + initializing the Web3Auth SDK. |
| 75 | +3. **Enable Solana Chain**: In the dashboard, navigate to "Chains and Network" and enable Solana, |
| 76 | + Solana Devnet and Solana Testnet. Ensure all the RPC URLs are configured. |
| 77 | + |
| 78 | +## Integrating Web3Auth in React |
| 79 | + |
| 80 | +Once you have set up the Web3Auth Dashboard and created a new project, it's time to integrate |
| 81 | +Web3Auth in your React application. For the implementation, we'll use the |
| 82 | +[`@web3auth/modal`](https://www.npmjs.com/package/@web3auth/modal) SDK. This SDK facilitates |
| 83 | +integration with Web3Auth, allowing you to easily manage embedded wallets in your React application. |
| 84 | + |
| 85 | +### Initialize Web3Auth Provider |
| 86 | + |
| 87 | +Wrap your application components with a `Web3AuthProvider` to configure Web3Auth with your |
| 88 | +`Client ID`. |
| 89 | + |
| 90 | +```typescript title="src/main.tsx" |
| 91 | +import "./index.css"; |
| 92 | + |
| 93 | +import ReactDOM from "react-dom/client"; |
| 94 | +// focus-start |
| 95 | +import { Web3AuthProvider } from "@web3auth/modal/react"; |
| 96 | +import web3AuthContextConfig from "./web3authContext"; |
| 97 | +// focus-end |
| 98 | +import App from "./App"; |
| 99 | + |
| 100 | +ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( |
| 101 | + // focus-start |
| 102 | + <Web3AuthProvider config={web3AuthContextConfig}> |
| 103 | + <App /> |
| 104 | + </Web3AuthProvider> |
| 105 | + // focus-end |
| 106 | +); |
| 107 | +``` |
| 108 | + |
| 109 | +```typescript title="src/web3authContext.tsx" |
| 110 | +import { WEB3AUTH_NETWORK } from "@web3auth/modal"; |
| 111 | +import { type Web3AuthContextConfig } from "@web3auth/modal/react"; |
| 112 | + |
| 113 | +// Dashboard Registration |
| 114 | +const clientId = |
| 115 | + "BFcLTVqWlTSpBBaELDPSz4_LFgG8Nf8hEltPlf3QeUG_88GDrQSw82fSjjYj5x4F3ys3ghMq8-InU7Azx7NbFSs"; // get from https://dashboard.web3auth.io |
| 116 | + |
| 117 | +// focus-start |
| 118 | +// Instantiate SDK |
| 119 | +const web3AuthContextConfig: Web3AuthContextConfig = { |
| 120 | + web3AuthOptions: { |
| 121 | + clientId, |
| 122 | + web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET, |
| 123 | + }, |
| 124 | +}; |
| 125 | +// focus-end |
| 126 | + |
| 127 | +export default web3AuthContextConfig; |
| 128 | +``` |
| 129 | + |
| 130 | +### Accessing Wallet Information & Fetching User Balance |
| 131 | + |
| 132 | +Once Web3Auth is initialized, you can access wallet information and user details through the |
| 133 | +[Web3Auth Solana hooks](/docs/sdk/web/react/solana-hooks/). |
| 134 | + |
| 135 | +```typescript title="src/components/getBalance.tsx" |
| 136 | +// focus-next-line |
| 137 | +import { useSolanaWallet } from "@web3auth/modal/react/solana"; |
| 138 | +import { |
| 139 | + LAMPORTS_PER_SOL, |
| 140 | + PublicKey, |
| 141 | +} from "@solana/web3.js"; |
| 142 | +import { useEffect, useState } from "react"; |
| 143 | + |
| 144 | +export function Balance() { |
| 145 | + // focus-next-line |
| 146 | + const { accounts, connection } = useSolanaWallet(); |
| 147 | + const [balance, setBalance] = useState<number | null>(null); |
| 148 | + const [isLoading, setIsLoading] = useState(false); |
| 149 | + const [error, setError] = useState<string | null>(null); |
| 150 | + |
| 151 | + const fetchBalance = async () => { |
| 152 | + if (connection && accounts && accounts.length > 0) { |
| 153 | + try { |
| 154 | + setIsLoading(true); |
| 155 | + setError(null); |
| 156 | + // focus-start |
| 157 | + const publicKey = new PublicKey(accounts[0]); |
| 158 | + const balance = await connection.getBalance(publicKey); |
| 159 | + // focus-end |
| 160 | + setBalance(balance); |
| 161 | + } catch (err) { |
| 162 | + setError(err instanceof Error ? err.message : "Unknown error"); |
| 163 | + } finally { |
| 164 | + setIsLoading(false); |
| 165 | + } |
| 166 | + } |
| 167 | + }; |
| 168 | + |
| 169 | + useEffect(() => { |
| 170 | + fetchBalance(); |
| 171 | + }, [connection, accounts]); |
| 172 | + |
| 173 | + return ( |
| 174 | + <div> |
| 175 | + <h2>Balance</h2> |
| 176 | + <div> |
| 177 | + {balance !== null && `${balance / LAMPORTS_PER_SOL} SOL`} |
| 178 | + </div> |
| 179 | + {isLoading && <span className="loading">Loading...</span>} |
| 180 | + {error && <span className="error">Error: {error}</span>} |
| 181 | + <button onClick={fetchBalance} type="submit" className="card"> |
| 182 | + Fetch Balance |
| 183 | + </button> |
| 184 | + </div> |
| 185 | + ) |
| 186 | +} |
| 187 | +``` |
| 188 | + |
| 189 | +## Integrating Solana Pay |
| 190 | + |
| 191 | +Solana Pay enables the generation of transaction requests, typically as QR codes, for direct |
| 192 | +payments from Solana wallets. This section will show you how to create and display Solana Pay QR |
| 193 | +codes for payments. |
| 194 | + |
| 195 | +### Required Imports |
| 196 | + |
| 197 | +Ensure you import the necessary components from the installed libraries: |
| 198 | + |
| 199 | +```typescript |
| 200 | +// focus-start |
| 201 | +import { createQR } from "@solana/pay"; |
| 202 | +import { Keypair, PublicKey } from "@solana/web3.js"; |
| 203 | +import BigNumber from "bignumber.js"; |
| 204 | +import { useSolanaWallet } from "@web3auth/modal/react/solana"; |
| 205 | +// focus-end |
| 206 | +``` |
| 207 | + |
| 208 | +### Generating the Payment Request QR Code |
| 209 | + |
| 210 | +The core of Solana Pay integration involves creating a payment request URL and then rendering it as |
| 211 | +a QR code. Here's what you need to define: |
| 212 | + |
| 213 | +- **Recipient and Amount**: Define the `recipient` (a `PublicKey` of the merchant/receiver) and the |
| 214 | + `amount` (a `BigNumber` representing the payment value, e.g., 0.001 SOL). |
| 215 | +- **Reference**: Generate a unique `reference` for the payment. This acts as a unique identifier for |
| 216 | + the transaction. |
| 217 | +- **Optional Fields**: Include `label`, `message`, and `memo` for enhanced user experience. |
| 218 | + |
| 219 | +Here's how to implement a Solana Pay QR code generator component: |
| 220 | + |
| 221 | +```typescript title="src/components/solanaPay.tsx" |
| 222 | +import { Keypair, PublicKey } from "@solana/web3.js"; |
| 223 | +import { createQR, encodeURL } from "@solana/pay"; |
| 224 | +import BigNumber from "bignumber.js"; |
| 225 | +import { useEffect, useRef, useState } from "react"; |
| 226 | +import { createPortal } from "react-dom"; |
| 227 | +import { useSolanaWallet } from "@web3auth/modal/react/solana"; |
| 228 | + |
| 229 | +export function SolanaPay() { |
| 230 | + const { accounts } = useSolanaWallet(); |
| 231 | + const [isLoading, setIsLoading] = useState(false); |
| 232 | + const [error, setError] = useState<string | null>(null); |
| 233 | + const [amountToSend, setAmountToSend] = useState(0); |
| 234 | + const [showModal, setShowModal] = useState(false); |
| 235 | + const [qrUrl, setQrUrl] = useState<string>(""); |
| 236 | + const qrRef = useRef<HTMLDivElement>(null); |
| 237 | + |
| 238 | + const generateQrCode = () => { |
| 239 | + try { |
| 240 | + if (!accounts?.[0]) { |
| 241 | + setError("No wallet connected"); |
| 242 | + return; |
| 243 | + } |
| 244 | + |
| 245 | + setIsLoading(true); |
| 246 | + setError(null); |
| 247 | + // focus-start |
| 248 | + // set the parameter of the transfer |
| 249 | + const recipient = new PublicKey(accounts?.[0]!); |
| 250 | + const amount = new BigNumber(amountToSend); |
| 251 | + // reference should be a unique ID for the payment |
| 252 | + const reference = new Keypair().publicKey; |
| 253 | + // Label and message are optional. They will be shown in wallets when users scan it but won't show on chain |
| 254 | + const label = "MetaMask Embedded Wallet x Solana Pay Demo"; |
| 255 | + const message = "Thanks for Trying Solana Pay!"; |
| 256 | + // memo is optional and will be included in the onchain transaction |
| 257 | + const memo = "Thanks for Trying Solana Pay!"; |
| 258 | + // create the URL |
| 259 | + const url = encodeURL({ |
| 260 | + recipient, |
| 261 | + amount, |
| 262 | + reference, |
| 263 | + label, |
| 264 | + message, |
| 265 | + memo, |
| 266 | + }); |
| 267 | + |
| 268 | + setQrUrl(url.toString()); |
| 269 | + // focus-end |
| 270 | + setShowModal(true); |
| 271 | + } catch (err) { |
| 272 | + setError(err instanceof Error ? err.message : "Failed to generate QR code"); |
| 273 | + } finally { |
| 274 | + setIsLoading(false); |
| 275 | + } |
| 276 | + }; |
| 277 | + // Generate QR code when modal opens and URL is available |
| 278 | + useEffect(() => { |
| 279 | + if (showModal && qrUrl && qrRef.current) { |
| 280 | + qrRef.current.innerHTML = ""; |
| 281 | + try { |
| 282 | + const qr = createQR(qrUrl, 300, "white"); |
| 283 | + qr.append(qrRef.current); |
| 284 | + } catch (err) { |
| 285 | + setError("Failed to create QR code"); |
| 286 | + } |
| 287 | + } |
| 288 | + }, [showModal, qrUrl]); |
| 289 | + |
| 290 | + const closeModal = () => { |
| 291 | + setShowModal(false); |
| 292 | + setQrUrl(""); |
| 293 | + setError(null); |
| 294 | + }; |
| 295 | + |
| 296 | + return ( |
| 297 | + <> |
| 298 | + <div> |
| 299 | + <h2>Solana Pay QR</h2> |
| 300 | + <div className="flex flex-col items-center gap-4"> |
| 301 | + <input |
| 302 | + type="number" |
| 303 | + placeholder="Enter SOL amount" |
| 304 | + onChange={(e) => setAmountToSend(Number(e.target.value))} |
| 305 | + className="px-4 py-2 border rounded-lg text-black" |
| 306 | + step="0.01" |
| 307 | + min="0" |
| 308 | + /> |
| 309 | + <button |
| 310 | + onClick={generateQrCode} |
| 311 | + className="px-6 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600" |
| 312 | + disabled={isLoading || amountToSend <= 0} |
| 313 | + > |
| 314 | + {isLoading ? "Generating..." : "Generate Payment QR"} |
| 315 | + </button> |
| 316 | + |
| 317 | + {/* Error Display */} |
| 318 | + {error && !showModal && ( |
| 319 | + <div className="text-red-500 text-sm mt-2"> |
| 320 | + Error: {error} |
| 321 | + </div> |
| 322 | + )} |
| 323 | + </div> |
| 324 | + </div> |
| 325 | +... |
| 326 | +``` |
| 327 | +
|
| 328 | +## Testing and Best Practices |
| 329 | +
|
| 330 | +### Development Environment |
| 331 | +
|
| 332 | +- **DevNet for Testing**: Always develop and test on DevNet or TestNet. You can use the |
| 333 | + [Solana Faucet](https://faucet.solana.com/) to get test SOL for your new account. |
| 334 | +- **Environment Variables**: Store your Web3Auth Client ID and other sensitive configuration in |
| 335 | + environment variables. |
| 336 | +
|
| 337 | +### User Experience |
| 338 | +
|
| 339 | +- **User Interface**: For better user experience, display the QR code within a modal or a dedicated |
| 340 | + confirmation page, providing clear messages to the user. |
| 341 | +- **Loading States**: Implement proper loading states while generating QR codes and processing |
| 342 | + transactions. |
| 343 | +- **Error Handling**: Provide clear error messages when transactions fail or when the user's wallet |
| 344 | + doesn't have sufficient balance. |
| 345 | +
|
| 346 | +### Production Considerations |
| 347 | +
|
| 348 | +- **Tracking Payments**: For production, implement a server-side solution to track the unique |
| 349 | + payment `reference` and poll for transaction confirmation using websockets. This allows you to |
| 350 | + update your application's state and perform reconciliation in your database. |
| 351 | +- **Production RPCs**: For scalable production usage, use dedicated Solana RPC services from |
| 352 | + providers like QuickNode, as public RPCs may have rate limits. |
| 353 | +- **Security**: Validate all payment parameters server-side before processing transactions. |
| 354 | +
|
| 355 | +## Conclusion |
| 356 | +
|
| 357 | +This guide demonstrates how to integrate Web3Auth's embedded wallets with Solana Pay to create a |
| 358 | +seamless payment experience. By combining Web3Auth's familiar Web2-like social logins with Solana |
| 359 | +Pay's QR code functionality, you can enable users to make payments directly from their |
| 360 | +Web3Auth-powered embedded wallet. |
| 361 | +
|
| 362 | +The integration provides a smooth, familiar experience for your users while leveraging the power of |
| 363 | +the Solana blockchain for fast and low-cost transactions. |
| 364 | +
|
| 365 | +If you are interested in learning more about Web3Auth, please check out our |
| 366 | +[documentation for Web SDK](http://localhost:3000/docs/sdk/web/react) or explore our |
| 367 | +[Solana Pay integration example](https://github.com/Web3Auth/web3auth-examples/blob/main/other/solana-pay-example). |
0 commit comments