diff --git a/src/app/connect/pay/accept-direct-payments/page.mdx b/src/app/connect/pay/accept-direct-payments/page.mdx new file mode 100644 index 00000000..bf5428d8 --- /dev/null +++ b/src/app/connect/pay/accept-direct-payments/page.mdx @@ -0,0 +1,103 @@ +import { createMetadata, Callout, DocImage, Steps, Step } from "@doc"; +import DirectPaymentsFlow from "../assets/direct-payments-flow.png"; + +export const metadata = createMetadata({ + image: { + title: "thirdweb Pay - Accept Direct Payments", + icon: "thirdweb", + }, + title: "thirdweb Pay - Accept Direct Payments | thirdweb", + description: + "Combine Pay and Engine to create a smooth point of sale experience on any chain", +}); + +# Accept Direct Payments with Pay & Engine + +Learn how to accept fiat and crypto from your customers with Pay and trigger Engine actions, like minting NFTs or transferring tokens to user wallets. + +### Requirements + +- [A thirdweb Client ID](https://thirdweb.com/dashboard/settings/api-keys) +- [An Engine Instance](https://thirdweb.com/dashboard/engine) + +### Purchase Flow Overview + +Before jumping in, it's important to understand the full system, from initial purchase to the eventual transfer of assets. This is a four step process (with an optional fifth): + + + +1. **User Initiates Purchase:** User sends specified funds (ETH, MATIC, USD, etc.) to your backend wallet via thirdweb Pay. Use the `purchaseData` field to pass any item data needed in the webhook. + E.g., `{ nftId: 1 }`. +2. **Pay Processes Purchase:** thirdweb Pay processes the transaction and sends a [Purchase Complete webhook](/connect/pay/webhooks#purchase-complete) to your dapp’s server when the transaction is complete. +3. **Your Server Calls Engine:** Your server processes the webhook and sends a contract call to the thirdweb Engine backend wallet to perform the item purchase. +4. **Engine Executes the Transaction:** Engine backend wallet executes the transaction needed to deliver the token(s) to the user’s address. +5. **(Optional) User Is Notified :** Your server confirms the completed Engine transaction and notifies the user. + +Now, let's get started. + + + + + 1. If you haven't already, [deploy an Engine + instance](https://thirdweb.com/dashboard/engine) + + 2. Create or import a backend wallet for Engine in [your Engine dashboard](https://thirdweb.com/dashboard/engine). + + 3. Send the required funds to the backend wallet + * For example, if purchasing with Arb Nova USDC, then your backend wallet should hold this ERC20 token. + * If your backend wallet holds an ERC20 token, ensure to add a native token balance as well to cover gas costs. For example, for a transaction on Arbitrum Nova, your backend wallet should hold both Arb Nova USDC (for payment) and Arb Nova ETH (for gas). + * Your backend wallet should hold enough liquidity to handle any anticipated purchasing volume. + + 4. Make a plan to monitor and keep the float wallet topped up to avoid any issues with the service. + + + 1. [Integrate thirdweb Pay](https://portal.thirdweb.com/connect/pay/build-a-custom-experience) on the client side to enable fiat and crypto payments. + + + 2. Specify the required parameters for your Pay transaction. + + ```ts + const quote = await getBuyWithFiatQuote({ + client: client, // thirdweb client + fromCurrencySymbol: "USD", // fiat currency symbol + toChainId: arbnova.id, // arbitrum nova chain id + toAmount: "100", // amount of token to buy + toTokenAddress: USDC_TOKEN_ADDRESS, // address of payment token + fromAddress: "", + toAddress: "", + purchaseData: { + nftId: 1 + } + }); + ``` + + |Parameter|Type|Description| + |----|--|--| + |fromCurrencySymbol|`string`|The fiat currency symbol you'd like to accept (currently limited to USD and EUR) | + |toChainId|`string`|[Chain ID](https://thirdweb.com/chainlist) for the destination token.| + |toTokenAddress|`string`|Address of the destination token.| + |toAmount|`string`|The price of the asset denominated in your desination token.| + |fromAddress|`string`|The user's wallet address.| + |toAddress|`string`|The wallet you'd like funds to be sent to. This can be your business wallet or an Engine backend wallet. | + |purchaseData|`Object`|Any details you need to pass in order to track your transaction. If this is a secondary sale, you may want to include details like `{ nftId: 1, orderId: 1 }` | + + + + 1. Create a “Purchase Complete” webhook in [the Pay dashboard](https://thirdweb.com/dashboard/connect/pay). + 2. The [Pay webhook POST response](/connect/pay/webhooks#purchase-complete) will include the BuyWithCryptoStatus or BuyWithFiatStatus in the body. + * This will also include the purchaseData you previously passed (metadata identifying the customer information, etc.). + * A request will be sent on every status change. + 3. Listen for [completed purchase statuses](https://portal.thirdweb.com/connect/pay/build-a-custom-experience#Poll%20for%20Transaction%20Status). + * Note, if this is a two-step BuyWithFiat transaction (onramp, then swap), you should also listen for `CRYPTO_SWAP_COMPLETED` + 4. When a purchase is completed, determine the purchaser, order and validate the amounts. + - The fromAddress in the status will show purchasing address + - The purchaseData will contain more information you previously for the purchase details + - Validate the information. For example, 100 USDC was sent to your wallet. + 5. Send a contract call to Engine for the float wallet to purchase the item + - For example, send a transaction to the float wallet for `mintTo(fromAddress)` + - For secondary purchases, like a marketplace direct listing, you can use the appropriate Engine endpoint, passing the ultimate recipient of the NFT as the buyer + 6. Confirm the purchase was successful. + + + +Once you've completed all of these steps, you'll be ready to offer a great purchase UX for your users. diff --git a/src/app/connect/pay/assets/direct-payments-flow.png b/src/app/connect/pay/assets/direct-payments-flow.png new file mode 100644 index 00000000..47bdc1bd Binary files /dev/null and b/src/app/connect/pay/assets/direct-payments-flow.png differ diff --git a/src/app/connect/pay/webhooks/page.mdx b/src/app/connect/pay/webhooks/page.mdx new file mode 100644 index 00000000..debbfc8d --- /dev/null +++ b/src/app/connect/pay/webhooks/page.mdx @@ -0,0 +1,182 @@ +import { createMetadata, DocImage } from "@doc"; +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; + +export const metadata = createMetadata({ + title: "Webhooks | thirdweb Pay", + description: + "Configure Pay webhooks to get notified at any point during a transaction's lifecycle.", +}); + +# Webhooks + +Pay can be configured to send webhook events to notify your application any time an event happens on your transaction. Pay sends a response, via a HTTP request, to any endpoint URLs that you have provided us in [the Pay dashboard](https://thirdweb.com/dashboard/connect/pay). + +## Events + +To listen to events, create a webhook in [the Pay dashboard](https://thirdweb.com/dashboard/connect/pay). Webhook URLs must start with `https://`. + +| Event | Description | +| ------------------- | ----------------------------------- | +| `purchase_complete` | A transaction is confirmed onchain. | + +### Purchase Complete + +Triggered when a transaction is confirmed onchain. This event provides information about the new status of the order and its transactionHash, as well as other relevant information. + +Example Response: + + + + + Fiat Purchase + Crypto Purchase + + + +```json +{ + "data": { + "buyWithFiatStatus": { + "intentId": "f4cf8ab7-bb62-4b3b-a180-70fc7d72446c", + "status": "ON_RAMP_TRANSFER_COMPLETED", + "toAddress": "0xebfb127320fcbe8e07e5a03a4bfb782219f4735b", + "quote": { + "createdAt": "2024-06-18T23:46:46.024Z", + "fromCurrency": { + "amountUnits": "279", + "amount": "2.79", + "currencySymbol": "USD", + "decimals": 2, + "amountUSDCents": 279 + }, + "fromCurrencyWithFees": { + "amountUnits": "294", + "amount": "2.94", + "currencySymbol": "USD", + "decimals": 2, + "amountUSDCents": 279 + }, + "onRampToken": { + "chainId": 137, + "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "name": "Matic", + "symbol": "MATIC", + "decimals": 18, + "priceUSDCents": 54.797200000000004 + }, + "toToken": { + "chainId": 137, + "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "name": "Matic", + "symbol": "MATIC", + "decimals": 18, + "priceUSDCents": 54.797200000000004 + }, + "estimatedOnRampAmountWei": "5000000000000000000", + "estimatedOnRampAmount": "5", + "estimatedToTokenAmount": "5", + "estimatedToTokenAmountWei": "5000000000000000000", + "estimatedDurationSeconds": 30 + }, + "source": { + "completedAt": "2024-06-18T23:49:00.347Z", + "amount": "5", + "amountWei": "5000000000000000000", + "token": { + "chainId": 137, + "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "name": "Matic", + "symbol": "MATIC", + "decimals": 18, + "priceUSDCents": 54.797200000000004 + }, + "transactionHash": "0x4bb089f6a60b49235a817b52bf39bc078f1246df15731b85837526bb62cf4e70", + "explorerLink": "https://polygonscan.com/tx/0x4bb089f6a60b49235a817b52bf39bc078f1246df15731b85837526bb62cf4e70", + "amountUSDCents": 275 + } + } + } +} +``` + + + +```json +{ + "data": { + "buyWithCryptoStatus": { + "swapType": "SAME_CHAIN", + "source": { + "transactionHash": "0x74d6c619a09e78f03f4bd495f29d5937a2539d0bbe8973e7710dce3e88c30b8b", + "token": { + "chainId": 10, + "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "decimals": 18, + "name": "ETH", + "symbol": "ETH", + "priceUSDCents": 346529 + }, + "amountWei": "318486512146714", + "amount": "0.000318486512146714", + "amountUSDCents": 110, + "completedAt": "2024-06-18T23:44:07.000Z" + }, + "status": "COMPLETED", + "subStatus": "SUCCESS", + "fromAddress": "0xebfb127320fcbe8e07e5a03a4bfb782219f4735b", + "toAddress": "0xebfb127320fcbe8e07e5a03a4bfb782219f4735b", + "quote": { + "fromToken": { + "chainId": 10, + "tokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + "decimals": 18, + "name": "ETH", + "symbol": "ETH", + "priceUSDCents": 346529 + }, + "toToken": { + "chainId": 10, + "tokenAddress": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", + "decimals": 6, + "name": "USD Coin", + "symbol": "USDC", + "priceUSDCents": 99 + }, + "fromAmountWei": "318486512146714", + "fromAmount": "0.000318486512146714", + "toAmountWei": "1100000", + "toAmount": "1.1", + "toAmountMinWei": "1100000", + "toAmountMin": "1.1", + "estimated": { + "fromAmountUSDCents": 110, + "toAmountMinUSDCents": 109, + "toAmountUSDCents": 109, + "slippageBPS": 91, + "feesUSDCents": 32, + "gasCostUSDCents": 40, + "durationSeconds": 30 + }, + "createdAt": "2024-06-18T23:43:45.900Z" + }, + "destination": { + "transactionHash": "0x74d6c619a09e78f03f4bd495f29d5937a2539d0bbe8973e7710dce3e88c30b8b", + "token": { + "chainId": 10, + "tokenAddress": "0x0b2c639c533813f4aa9d7837caf62653d097ff85", + "decimals": 6, + "name": "USD Coin", + "symbol": "USDC", + "priceUSDCents": 99 + }, + "amountWei": "1100000", + "amount": "1.1", + "amountUSDCents": 109, + "completedAt": "2024-06-18T23:44:07.000Z" + } + } + } +} +``` + + diff --git a/src/app/connect/sidebar.tsx b/src/app/connect/sidebar.tsx index 8c6655bc..1af5d64e 100644 --- a/src/app/connect/sidebar.tsx +++ b/src/app/connect/sidebar.tsx @@ -482,6 +482,10 @@ export const sidebar: SideBar = { }, ], }, + { + name: "Webhooks", + href: `${paySlug}/webhooks`, + }, { name: "Enable Test Mode", href: `${paySlug}/test-mode`, @@ -490,6 +494,10 @@ export const sidebar: SideBar = { name: "Build a Custom Experience", href: `${paySlug}/build-a-custom-experience`, }, + { + name: "Accept Direct Payments", + href: `${paySlug}/accept-direct-payments`, + }, { name: "FAQs", href: `${paySlug}/faqs`,