From 6337d7cef0c1a9708b64c704fafe1b7cc16b42ae Mon Sep 17 00:00:00 2001 From: MantisClone Date: Sun, 12 Oct 2025 20:53:41 -0400 Subject: [PATCH 01/21] feat: add interactive payment matching demo to landing page Adds interactive demo showing Request Network vs traditional payments, includes comparison table and sticky dialogs that account for Mintlify nav (135px offset). --- index.mdx | 188 ++++---- logo/icons/dai.png | Bin 0 -> 2671 bytes logo/icons/usdc.png | Bin 0 -> 892 bytes logo/icons/usdt.png | Bin 0 -> 674 bytes snippets/comparison-table.jsx | 242 +++++++++++ snippets/integrated-demo.jsx | 794 ++++++++++++++++++++++++++++++++++ 6 files changed, 1140 insertions(+), 84 deletions(-) create mode 100644 logo/icons/dai.png create mode 100644 logo/icons/usdc.png create mode 100644 logo/icons/usdt.png create mode 100644 snippets/comparison-table.jsx create mode 100644 snippets/integrated-demo.jsx diff --git a/index.mdx b/index.mdx index 986a08d..520143b 100644 --- a/index.mdx +++ b/index.mdx @@ -1,84 +1,104 @@ ---- -title: "Request Network Docs" -description: "A protocol for requests, payments, and **100% automated reconciliation**. Requests add business context to payments, **eliminating manual accounting** with cryptographic certainty." -sidebarTitle: "Welcome" -mode: "center" ---- - -## Interactive Demo - - - Experience how creating an invoice and getting paid is better than asking someone to send you money and hoping they remember to tell you they sent it. - - *Interactive demo will be embedded here* - - -## Get Started - -Choose your path based on what you want to build: - - - - Explore specific business scenarios: invoicing, payouts, payroll, checkout, and subscriptions - - - - The easiest way to integrate. Payment types, webhooks, and developer tools. - - - - Supported chains, currencies, smart contracts, and community resources - - - - - - Legacy SDK and protocol documentation for advanced users - - - - Stay updated with the latest features and improvements - - - -## Popular - - - - 1. [Get API Keys](/api-setup/getting-started) (2 minutes) - 2. [Create your first request](/use-cases/invoicing) (1 minute) - 3. [Test payment flow](/api-features/payment-types) (2 minutes) - - - - 1. [Try EasyInvoice Demo](/use-cases/invoicing) - See full invoicing workflow - 2. [Explore Use Cases](/use-cases/payouts) - Discover business applications - 3. [Fork & Customize](/use-cases/invoicing) - Make it your own - - - - 1. [Check Supported Chains](/resources/supported-chains-and-currencies) - 2. [Review Payment Types](/api-features/payment-types) - 3. [Explore Use Cases](/use-cases/invoicing) that match your needs - - \ No newline at end of file +--- +title: "Request Network Docs" +description: "A protocol for requests, payments, and **100% automated reconciliation**. Requests add business context to payments, **eliminating manual accounting** with cryptographic certainty." +sidebarTitle: "Welcome" +mode: "custom" +--- + +import { IntegratedDemo } from '/snippets/integrated-demo.jsx' +import { ComparisonTable } from '/snippets/comparison-table.jsx' + +
+
+

Request Network Docs

+

+ A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty. +

+
+ +
+ +
+ +
+ +
+
+ +
+

Get Started

+ +

+ Choose your path based on what you want to build: +

+ +
+ + + Explore specific business scenarios: invoicing, payouts, payroll, checkout, and subscriptions + + + + The easiest way to integrate. Payment types, webhooks, and developer tools. + + + + Supported chains, currencies, smart contracts, and community resources + + +
+ +
+ + + Legacy SDK and protocol documentation for advanced users + + + + Stay updated with the latest features and improvements + + +
+ +

Popular

+ + + + 1. [Get API Keys](/api-setup/getting-started) (2 minutes) + 2. [Create your first request](/use-cases/invoicing) (1 minute) + 3. [Test payment flow](/api-features/payment-types) (2 minutes) + + + + 1. [Try EasyInvoice Demo](/use-cases/invoicing) - See full invoicing workflow + 2. [Explore Use Cases](/use-cases/payouts) - Discover business applications + 3. [Fork & Customize](/use-cases/invoicing) - Make it your own + + + + 1. [Check Supported Chains](/resources/supported-chains-and-currencies) + 2. [Review Payment Types](/api-features/payment-types) + 3. [Explore Use Cases](/use-cases/invoicing) that match your needs + + +
diff --git a/logo/icons/dai.png b/logo/icons/dai.png new file mode 100644 index 0000000000000000000000000000000000000000..ef1b3c633ea84960b55feacca7ce5d9cd5ee46ee GIT binary patch literal 2671 zcmZvdc{r5a8^<4BTV9c!vcy=j)lhFG`7z9d>`S&JS@I%<`cXrRnn;^eLdnwNttJd+ zCfgWeXBx|pO35fHA?g*L^E@-pZ~FgtuIt?Qb*}S0_kCZVbD!(v_;|Ujm0vFpLC{(^ zSE8TTJN}+>e~9h<7nyTnClyQZBtTG2?w{x(Y4KX(xSxv?RMoFD13`a4J{|!ikGNzu z1NZZ;HQcllV_So6e+=7;{ZL18HCCyr7zDx3)!?f2Nc-N^N=?w|4cFsgrUjts!yFP^ zYlL$5AzeOjoeoSl1+3j-CB}4xE7zl4dy$$A2tyxcJ0WdExJKs}+iUr$GN{%El`1gX z0p>U(J^sR%fuK?uZrFVdlv<+yTCfex%XyVjvpv!ZKswRBt2RM7+ zW+yC=N^>n2e*0wIu6trqD&mjaaj?kt5O4WBbZGh1rYgft+u>Rhq*6t^g|z+$p_^jumVjZ7FwEd4JD6z+H|~UAYz8g%Fmp4; z3qt#Xu$Q5~Er!9>8vvVtbb5g{S4<>A#F|PCgl+h(uoSR&oQSf@S0A|M^!NSi0pcjz~|o&a>@Jkqlt8`z7rnqUp;pjr#*3_y7C z2*(YnQU^7=vW>)nk5`BLfAwMEMoAi=*K8F}W%kb==wu{sse z3=38r6Fmy#YRVAFP08KkY-+35Q3u`n)X_}%=XJMlaR;w;*+$3b}z*yE; zwF<@yM29o59%rmp4dVr)V>d<1)1ui9;Y7LU*CaL&jPXN7zoyV3vT%xqbOnj#dc&?w z=ZPQ2*%N+V{*aW6P)aB%At{tV5FkQ@C?Z6^$5;x{eA!sg3y>ez*6V1^3oY_(#@!q**ahxAXAc#h8Mn+ZC`U2}kxLK&< z5&1&HI4Ar%1HaIXX6||Xooy-S6yEwv>s(UZ_MJs#FitP1q@)=*u8Q&orVGBDN?G|J zKI#C4X5t01?MJ5tkFOvbN_WS-Kl_8vv6x>pl-!lICZBG6A&7P~jQyilVDv~}ygRy5 ztC=E$E7XO{SqsI|uiYm!w4&xN3S^~^xmRx~9M;sNUr?s6l!Snh!pA>VwA%T@{2~_e z@c|PL^-x5Q_gRjH-=_uddn`2F=u@#lRYq(0;iNK^yOPMi`0Gh|w^l2D$d)XZq$4V` zZ!-VLNpd;&Rm)wf@I=COq*LU|M!2*-87{Q{y`vDG{MJdRYo~r-(R}G|QrAcSq#v zCT_=N3)h@~c>Sg_$yhT$nxkN)=zWi$nczqoFu8?V`cKX{#ABWD}{Zt_Z4*IE<+(A zHra$iDV#OsNSfP3)km-1tSj*Kif&_pZ9Uugf`8z zoy>JJC)yFo0dxP1-kbO!85UlM#0g?sKD*kcJh^#16zAPJTX1%=>&dUrTf$n~Og~$% zCl-;530oC>qD0XKc!wU-$sm3GQa`zF!D-*XW{nWi4&NnW)R`97iXr`NvKjJ*Y+`an zyk3$jLtt`{IL#-Q7k-2c%`>zemMpd_UB40?ddX&@{ahbWyA%2<>pF28?V(Wm_GtM8 zhm@3&P6^pb(9eHU@+p&$u4TGcQ2ZrN%iHeLs|N<9mm`i(74%@snUrEjzvLchO+Xa_8E8+h3)63J{(p|det+IUW%M3GF)+wDkYi#Xf?X@D< z)EtjT**5z7`9!_E=zbTZwL7Hb0VT$%nR^C`IMQD;LCQ*llm!2}GF+8fpo2cqQ{Oft z#k@t~*E775p9gnWZWWw5x%eRFM=8zN>du3!boOV(*B$$g6`zW3d+5zMwe*fTS`_$!)H>7JXf2eQck!gNhbkyDwEF>4nUoC-uTc zwIh}pB~vNHIkQ#6NqTe8POB|#32$@2#De`#PL30Vhckf-`1&kHg^&Him}9 zZSZLph)B1>;UCujv(f27nVDXHP^4~rOUCr~1byQzHy&~RYkwtHuwQ{<+wyuxprlHR zS#<5`lS79lOC%GYx6J&dcb3(DpshZV>iDt1QDS;;yL4trNoFOQ8QA%0r0K9pBwp8K z-N|_6Cx22u&d6A$lW_R5^t1slJ_~^&KOK`gQhgUrm9L{Rr@AsF_L#lQ$T+>!@w(f? z`ZB)>ul!l=;#(zhRD~92WXuw$_)z_@!?&EGNcx3T?l~yiKtA<*8usSmqbl4ZoE}Tuy-)eLX2W>z zYVq|Dg#p6ig{1-QwD%gk`JU_7i~|Olier=HFAH`H{NaK7?%ro*l@6pSjLF|ShO5vM y+IrZ7QzuP{TYRBJ$U=9_Hr)bWN-(ticBjiT%B33y?oc|wnc(iW- literal 0 HcmV?d00001 diff --git a/logo/icons/usdc.png b/logo/icons/usdc.png new file mode 100644 index 0000000000000000000000000000000000000000..f472ec671e6ffaacad8680b491e1a3a05e69bab2 GIT binary patch literal 892 zcmV-?1B3jDP)Vw=5QSlmz!9983vdBWzy&w~35g{~gro!{grr;nkt2{0LbBul36Yo(oDdKhjfGe8 zw^iM>GqbbfU;2}MuY0$JZBN%Fu?!Q_*_}u!uRj}ia z_gt>ypGk1AMfE(3;Xw|q=i4hjd@Xv;+3|sE)pO5+4_UjOe-duKtEX?jZ@t#@@yoNT zhtEH6y}paiF$y=WD&{@KysGo;gqu~&u-7xgUPb3B<3=stpZ)f%x3J%{#re-aTm6>K zRl-eL_OM^Ihy9k$*TlswTi6|I3%f0yGsT6O8TRHf!(OIyhPWs*!(OJCXJ**TblzK3 zztv^fYboX}GwfwL_XdlR8TNXLdCv@ck~^Oj<+|A+5?_!0BW;T;q^D&{T4JhJ=lKYjDfeYZN9uijm_bgMI` zGJHk1qumP-^PXZpw_>h~U%(Ca=2Fb{U>6YK+A)f`?)?IyJu0|IRWa8^E+E^hhwCef zxh{GEHx9q^uj1OCVy*|dfXItqe|^+7_IirBuI2)6jIh&E%yo4a5dD3@PD?S@;Q}sw zQN=t{%rnJ2Q_Lgx`ejJWBO~l∈Fn+hBwp74w#2uA?ttz)og_oiSaFu%qP*7!>o& zUH;D(_i8ETk(&#sTtbAMmb>EmmJxRJ*6@`J7!>oK6dA&jj*E+ zPCpzu`!yCLBkWWZ^NJC6BAt7~ap6PJV1%8XV%{^tPNs9;!g6GWy_RC$GQ(b^^M<%6 zGQ-~7X4s1i|0o*a!ps(Sr`p19rgJuNab^$uyV}Ekrt_6>la@W~_w4cL#n-Jhna)+l zje54Q+p~q;p3b!sZdNhEPR|HC6`f}kZd!H9;is&<(K#mJ=4;pU&w>wGtDbvyeBfI2 zoO{KGA6n117#?J4%O7v|iHBNB&v9^ucf&)@ZTicMwc)|1>i%~Q2kPnieen4ORjcH65`X^1 z20zlX5Zm7mc9fL%dYMcnlgaR@s_&?sP6wE>JBp$&1&mQ|V4!%tV(Ru()utg(ygCO1 zq@Xi!;dC5gDf&z%UkwYVqcyl-iW-9pbr>3q-)T|2VQFfrYAh|(VQ4VEQf+Vng`vUt zO3Sjq65GMa3`2wQS+bz>f)g2r2IDhjJR!=HSPoY!mix;EYpH?b+r;>6JJG!HnYcNh zvE~{$-ixi^Ko7>-0377WC>lW4Fud_~N#|5mEC*V{>dd!UmZSd$;6(%bvl-^@jkm=@ zif`m!@f+!ahLp^su-+N;q9=W*X%n<<2g{{2mQ)=J98Xe{KTjtd`iu2$kUr22|Xa-B!!f0XvVvpln29iEVmhhCJ2ke0dsZ6 z{CJe&vDN;`j3(eB7SdUzZ|!=!VO=$7jM2I7QoDG8<0g=*bB*JzH7@=hT-3s#4R8uB zZfLLucmabL7^DGS!C;ztZ-93!|4G7Y1A>CVnR;u$L%<{$dTGEz!DJNDA4>2|(CV!) z3NWcqMg=D4SH@3Vk3de;hmjU9kP~)m5@6D { + const comparisonData = [ + { + method: "Request Network", + accuracy: 100, + implementation: "Easy", + linesOfCode: 6, + userExperience: "Good", + notes: "Built-in payment metadata", + isHighlighted: true, + }, + { + method: "Transfers API Webhook", + accuracy: 60, + implementation: "Hard", + linesOfCode: 15, + userExperience: "Okay", + notes: "Requires amount and currency matching", + }, + { + method: "Blockchain Node RPC Polling", + accuracy: 60, + implementation: "Very Hard", + linesOfCode: 30, + userExperience: "Okay", + notes: "Poor performance, requires polling", + }, + { + method: "New Address to receive each Payment", + accuracy: 100, + implementation: "Very Hard", + linesOfCode: 30, + userExperience: "Poor", + notes: "Requires gas, many addresses, often custodial", + }, + ]; + + // Icon components using inline SVG + const CheckIcon = ({ className = "h-4 w-4" }) => ( + + + + ); + + const XIcon = ({ className = "h-4 w-4" }) => ( + + + + + ); + + const AlertCircleIcon = ({ className = "h-4 w-4" }) => ( + + + + + + ); + + const Badge = ({ children, className = "" }) => ( + + {children} + + ); + + const getUXIcon = (ux) => { + switch (ux) { + case "Good": + return ; + case "Okay": + return ; + case "Poor": + return ; + default: + return null; + } + }; + + const getUXColor = (ux) => { + switch (ux) { + case "Good": + return "text-green-700 dark:text-green-300"; + case "Okay": + return "text-yellow-700 dark:text-yellow-300"; + case "Poor": + return "text-red-700 dark:text-red-300"; + default: + return "text-gray-700 dark:text-gray-300"; + } + }; + + return ( +
+
+

+ Blockchain Payment Detection Methods Compared +

+

+ Other methods require manual reconciliation or sacrifice implementation simplicity or user experience +

+
+ +
+
+ {/* Table Header */} +
+
Method
+
+ Automated Reconciliation +
+
Implementation
+
Lines of Code
+
User Experience
+
Notes
+
+ + {/* Table Rows */} +
+ {comparisonData.map((row, index) => ( +
+ {/* Method Name */} +
+ + {row.method} + +
+ + {/* Accuracy */} +
+
+ + {row.accuracy}% + +
+
+
+
+
+ + {/* Implementation Difficulty */} +
+ + {row.implementation} + +
+ + {/* Lines of Code */} +
+
+ {row.linesOfCode} +
+
+ + {/* User Experience */} +
+
+ {getUXIcon(row.userExperience)} + {row.userExperience} +
+
+ + {/* Notes */} +
+ {row.notes} +
+
+ ))} +
+
+
+ + {/* Summary Section */} +
+
+
+
+ +
+

+ Request Network Advantage +

+

+ The only solution that combines 100% automated reconciliation with easy implementation and great user + experience. Built-in payment metadata eliminates guesswork entirely. +

+
+
+
+ +
+
+ +
+

+ Alternative Trade-offs +

+

+ Other methods either sacrifice automated reconciliation (requiring manual review) or burden users with + poor UX and complex implementations. +

+
+
+
+
+
+
+ ); +}; diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx new file mode 100644 index 0000000..3bdfe2e --- /dev/null +++ b/snippets/integrated-demo.jsx @@ -0,0 +1,794 @@ +export const IntegratedDemo = () => { + // Constants - moved inside component + const CUSTOMER_DATA = [ + { name: "Johnson Corp" }, + { name: "Martinez LLC" }, + { name: "Chen Industries" }, + { name: "Patel Enterprises" }, + { name: "Kim Solutions" }, + ]; + + const CURRENCIES = ["USDC", "USDT", "DAI"]; + + // Helper functions + const generateAmount = () => { + const amount = Math.floor(Math.random() * 900) + 100; + const currency = CURRENCIES[Math.floor(Math.random() * CURRENCIES.length)]; + return { amount, currency }; + }; + + // Icon components + const SendIcon = ({ className = "h-4 w-4" }) => ( + + + + + ); + + const ExternalLinkIcon = ({ className = "h-3 w-3" }) => ( + + + + + + ); + + const CheckIcon = ({ className = "h-4 w-4" }) => ( + + + + ); + + const XIcon = ({ className = "h-4 w-4" }) => ( + + + + + ); + + const AlertCircleIcon = ({ className = "h-4 w-4" }) => ( + + + + + + ); + + const CheckCircle2Icon = ({ className = "h-4 w-4" }) => ( + + + + + ); + + const CryptoIcon = ({ currency, className = "w-4 h-4" }) => { + const iconMap = { + USDC: "/logo/icons/usdc.png", + USDT: "/logo/icons/usdt.png", + DAI: "/logo/icons/dai.png", + }; + + const iconSrc = iconMap[currency]; + if (!iconSrc) return null; + + return {`${currency}; + }; + + const getCurrencyDisplay = (amount, currency) => { + return ( + + + + {amount} {currency} + + + ); + }; + + // Simple Badge component + const Badge = ({ children, variant = "default", className = "" }) => { + const variantClasses = { + default: "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200", + destructive: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200", + secondary: "bg-gray-600 text-white dark:bg-gray-500 dark:text-white", + success: "bg-green-600 text-white dark:bg-green-700 dark:text-white", + }; + + return ( + + {children} + + ); + }; + + // Simple Button component + const Button = ({ children, onClick, disabled, variant = "default", size = "md", className = "" }) => { + const variantClasses = { + default: "bg-primary-600 hover:bg-primary-700 text-white", + outline: "bg-transparent border-2 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800", + }; + + const sizeClasses = { + md: "px-4 py-2 text-sm", + lg: "px-6 py-3 text-base", + }; + + return ( + + ); + }; + + // Component state + const rightCardRef = useRef(null); + const leftScrollRef = useRef(null); + const rightScrollRef = useRef(null); + const demoContainerRef = useRef(null); + + const [leftPayments, setLeftPayments] = useState([]); + const [rightRequests, setRightRequests] = useState([]); + const [isProcessing, setIsProcessing] = useState(false); + const [hasStarted, setHasStarted] = useState(false); + const [showDialog, setShowDialog] = useState(true); + const [hasSeenCollisionExplainer, setHasSeenCollisionExplainer] = useState(false); + const [showCollisionExplainer, setShowCollisionExplainer] = useState(false); + const [paymentCount, setPaymentCount] = useState(0); + const [requestCount, setRequestCount] = useState(0); + const [scrollOffset, setScrollOffset] = useState(0); + + // Sync scrolling + useEffect(() => { + const leftScroll = leftScrollRef.current; + const rightScroll = rightScrollRef.current; + + if (!leftScroll || !rightScroll) return; + + const handleLeftScroll = () => { + if (rightScroll) { + rightScroll.scrollTop = leftScroll.scrollTop; + } + }; + + const handleRightScroll = () => { + if (leftScroll) { + leftScroll.scrollTop = rightScroll.scrollTop; + } + }; + + leftScroll.addEventListener("scroll", handleLeftScroll); + rightScroll.addEventListener("scroll", handleRightScroll); + + return () => { + leftScroll.removeEventListener("scroll", handleLeftScroll); + rightScroll.removeEventListener("scroll", handleRightScroll); + }; + }, []); + + // Clear isNew flags after animation + useEffect(() => { + const timer = setTimeout(() => { + setLeftPayments((prev) => prev.map((p) => ({ ...p, isNew: false }))); + setRightRequests((prev) => prev.map((r) => ({ ...r, isNew: false }))); + }, 500); + + return () => clearTimeout(timer); + }, [leftPayments.length, rightRequests.length]); + + + + const handleStartDemo = () => { + setHasStarted(true); + setShowDialog(false); + }; + + const handleCreateRequest = () => { + const randomCustomer = CUSTOMER_DATA[Math.floor(Math.random() * CUSTOMER_DATA.length)]; + let amount; + let currency; + + setRequestCount(prev => { + const newCount = prev + 1; + + if (newCount % 3 === 0 && rightRequests.length > 0) { + const existingRequest = rightRequests[Math.floor(Math.random() * rightRequests.length)]; + amount = existingRequest.amount; + currency = existingRequest.currency; + } else { + const generated = generateAmount(); + amount = generated.amount; + currency = generated.currency; + } + + const timestamp = new Date(); + const id = `request-${Date.now()}`; + + const newRequest = { + id, + amount, + currency, + customer: randomCustomer.name, + status: "awaiting_payment", + timestamp, + isNew: true, + }; + + const placeholder = { + id: `placeholder-${id}`, + amount, + currency, + from: "", + timestamp, + status: "possibly_reconciled", + requestId: id, + txHash: "", + isNew: true, + isPlaceholder: true, + }; + + setRightRequests((prev) => [newRequest, ...prev]); + setLeftPayments((prev) => [placeholder, ...prev]); + + return newCount; + }); + }; + + const calculateLeftAccuracy = () => { + const nonPlaceholderPayments = leftPayments.filter((p) => !p.isPlaceholder); + if (nonPlaceholderPayments.length === 0) return "N/A"; + + const reconciledCount = nonPlaceholderPayments.filter((p) => p.status === "possibly_reconciled").length; + const percentage = Math.round((reconciledCount / nonPlaceholderPayments.length) * 100); + return `${percentage}%`; + }; + + const handleSimulatePayment = async () => { + setIsProcessing(true); + + const awaitingRequests = rightRequests.filter((r) => r.status === "awaiting_payment"); + + if (awaitingRequests.length === 0) { + setIsProcessing(false); + return; + } + + setPaymentCount(prev => prev + 1); + + const randomIndex = Math.floor(Math.random() * awaitingRequests.length); + const selectedRequest = awaitingRequests[randomIndex]; + + const paymentAmount = selectedRequest.amount; + const paymentCurrency = selectedRequest.currency; + + const randomAddress = `0x${Math.random().toString(16).substr(2, 40)}`; + const txHash = `0x${Math.random().toString(16).substr(2, 16)}`; + + const matchingPayments = leftPayments.filter( + (p) => !p.isPlaceholder && p.amount === paymentAmount && p.currency === paymentCurrency, + ); + + const hasCollision = matchingPayments.length > 0; + + const newPayment = { + id: `payment-${Date.now()}`, + amount: paymentAmount, + currency: paymentCurrency, + from: randomAddress, + timestamp: new Date(), + status: hasCollision ? "payment_collision" : "possibly_reconciled", + requestId: selectedRequest.id, + txHash, + isNew: true, + }; + + setLeftPayments((prev) => { + const placeholderIndex = prev.findIndex((p) => p.isPlaceholder && p.requestId === selectedRequest.id); + + if (placeholderIndex !== -1) { + const updated = [...prev]; + updated[placeholderIndex] = newPayment; + + if (hasCollision) { + return updated.map((p) => + !p.isPlaceholder && p.amount === paymentAmount && p.currency === paymentCurrency + ? { ...p, status: "payment_collision" } + : p, + ); + } + + return updated; + } + + return [newPayment, ...prev]; + }); + + setRightRequests((prev) => + prev.map((r) => + r.id === selectedRequest.id ? { ...r, status: "paid_reconciled", txHash, isNew: true } : r, + ), + ); + + setTimeout(() => { + triggerConfetti(); + }, 100); + + if (hasCollision) { + if (!hasSeenCollisionExplainer) { + setTimeout(() => { + setShowCollisionExplainer(true); + setHasSeenCollisionExplainer(true); + }, 1000); + } + } + + setIsProcessing(false); + }; + + const triggerConfetti = () => { + if (typeof window !== 'undefined' && window.confetti) { + if (rightCardRef.current) { + const rect = rightCardRef.current.getBoundingClientRect(); + const x = (rect.left + rect.width / 2) / window.innerWidth; + const y = (rect.top + rect.height / 2) / window.innerHeight; + + window.confetti({ + particleCount: 50, + spread: 60, + origin: { x, y }, + colors: ["#10b981", "#059669", "#047857"], + }); + } + } else if (typeof window !== 'undefined' && !window.confetti) { + const script = document.createElement("script"); + script.src = "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js"; + script.onload = () => { + if (window.confetti && rightCardRef.current) { + const rect = rightCardRef.current.getBoundingClientRect(); + const x = (rect.left + rect.width / 2) / window.innerWidth; + const y = (rect.top + rect.height / 2) / window.innerHeight; + + window.confetti({ + particleCount: 50, + spread: 60, + origin: { x, y }, + colors: ["#10b981", "#059669", "#047857"], + }); + } + }; + document.head.appendChild(script); + } + }; + + const handleClearTables = () => { + setHasStarted(false); + setPaymentCount(0); + setRequestCount(0); + + setRightRequests([]); + setLeftPayments([]); + setHasSeenCollisionExplainer(false); + setShowCollisionExplainer(false); + }; + + const awaitingCount = rightRequests.filter((r) => r.status === "awaiting_payment").length; + const canSimulatePayment = awaitingCount > 0; + const canCreateRequest = awaitingCount < 3; + const hasContent = leftPayments.length > 0 || rightRequests.length > 0; + + return ( +
+
+ {showDialog && !hasStarted && ( +
+
e.stopPropagation()} + style={{ + position: 'sticky', + top: '135px' + }} + > +
+

+ Request Network: Identify Every Payment +

+
+
+ +

+ Traditional blockchain payments are anonymous and hard to reconcile. +

+
+
+ +

+ Request Network adds identifiers that uniquely tie every payment to a request, making + reconciliation instant, automatic, and 100% accurate. +

+
+
+
+
+ +
+
+
+ )} + + {showCollisionExplainer && ( +
setShowCollisionExplainer(false)} + > +
e.stopPropagation()} + style={{ + position: 'sticky', + top: '135px' + }} + > +
+
+ +

+ Payment Collision Detected +

+
+
+
+
+ +
+

On the Left:

+

+ Two payments have the same amount and currency. Which payment belongs to which customer? + Manual review required. +

+
+
+
+
+
+ +
+

On the Right:

+

+ Each payment is automatically matched to its correct request using onchain identifiers. No + ambiguity, no manual work. +

+
+
+
+
+
+
+ +
+
+
+ )} + +
+

Identify Every Payment

+

+ See the difference between anonymous payments and identified payments +

+
+ +
+ + + + + +
+ +
+ {/* Left side - Traditional Payments */} +
+
+ + + + + +
+ +
+
+
+

+ Traditional Blockchain Payments +

+
+ + + Reconciled: {calculateLeftAccuracy()} + +
+
+

+ Anonymous transactions without business context +

+
+
+
+ {leftPayments.length === 0 ? ( +
+

Click "Create Request" to begin

+
+ ) : ( + leftPayments.map((payment) => { + if (payment.isPlaceholder) { + return ( +
+ Awaiting Payment +
+ ); + } + + const isCollision = payment.status === "payment_collision"; + + return ( +
+
+
+ {getCurrencyDisplay(payment.amount, payment.currency)} +
+ {isCollision ? ( + + Needs Review + + ) : ( + + Possibly Reconciled + + )} +
+
+
+ From +

+ {payment.from.slice(0, 10)}...{payment.from.slice(-8)} +

+
+ +
+
+ ); + }) + )} +
+
+
+
+ + {/* Right side - Request Network Payments */} +
+
+ + + + + +
+ +
+
+
+

+ Request Network Payments +

+
+ + + Reconciled: 100% + +
+
+

+ Payments with unique IDs for instant reconciliation +

+
+
+
+ {rightRequests.length === 0 ? ( +
+

Click "Create Request" to begin

+
+ ) : ( + rightRequests.map((request) => { + const isPaid = request.status === "paid_reconciled"; + + return ( +
+
+
+ {getCurrencyDisplay(request.amount, request.currency)} +
+ {isPaid ? ( + + Paid, Reconciled + + ) : ( + + Awaiting Payment + + )} +
+
+
+ Customer +

{request.customer}

+
+ {isPaid && request.txHash && ( + + )} +
+
+ ); + }) + )} +
+
+
+
+
+
+ + +
+ ); +}; From 2c38ce625d764b4250ce7563ef5baf77b2b9f790 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 14 Oct 2025 15:47:36 -0400 Subject: [PATCH 02/21] fix: landing page demo --- index.mdx | 2 +- snippets/comparison-table.jsx | 25 +-- snippets/integrated-demo.jsx | 351 ++++++++++++++++++++++++++-------- 3 files changed, 286 insertions(+), 92 deletions(-) diff --git a/index.mdx b/index.mdx index 520143b..dd00e1e 100644 --- a/index.mdx +++ b/index.mdx @@ -32,7 +32,7 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx' Choose your path based on what you want to build:

-
+
{ method: "Request Network", accuracy: 100, implementation: "Easy", - linesOfCode: 6, + linesOfCode: 10, userExperience: "Good", notes: "Built-in payment metadata", isHighlighted: true, @@ -12,24 +12,26 @@ export const ComparisonTable = () => { { method: "Transfers API Webhook", accuracy: 60, + accuracyDisplay: "< 100%", implementation: "Hard", - linesOfCode: 15, + linesOfCode: 50, userExperience: "Okay", notes: "Requires amount and currency matching", }, { method: "Blockchain Node RPC Polling", accuracy: 60, + accuracyDisplay: "< 100%", implementation: "Very Hard", - linesOfCode: 30, + linesOfCode: 150, userExperience: "Okay", notes: "Poor performance, requires polling", }, { - method: "New Address to receive each Payment", + method: "New wallet address per payment", accuracy: 100, implementation: "Very Hard", - linesOfCode: 30, + linesOfCode: 100, userExperience: "Poor", notes: "Requires gas, many addresses, often custodial", }, @@ -90,7 +92,7 @@ export const ComparisonTable = () => { }; return ( -
+

Blockchain Payment Detection Methods Compared @@ -110,7 +112,7 @@ export const ComparisonTable = () => {

Implementation
Lines of Code
-
User Experience
+
User Experience
Notes
@@ -121,8 +123,8 @@ export const ComparisonTable = () => { key={row.method} className={`grid grid-cols-6 gap-3 p-3 md:p-4 rounded-lg border-2 transition-all duration-200 ${ row.isHighlighted - ? "bg-green-50/50 dark:bg-green-950/20 border-green-300 dark:border-green-700 hover:bg-green-100/60 dark:hover:bg-green-950/30 hover:shadow-lg hover:border-green-400 dark:hover:border-green-600" - : "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/60 hover:shadow-lg hover:border-gray-300 dark:hover:border-gray-600" + ? "bg-green-50/50 dark:bg-green-950/20 border-green-300 dark:border-green-700 hover:bg-green-100/60 dark:hover:bg-green-950/30 hover:shadow-lg dark:hover:shadow-green-900/50 hover:border-green-400 dark:hover:border-green-600" + : "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/60 hover:shadow-lg dark:hover:shadow-gray-900/50 hover:border-gray-300 dark:hover:border-gray-600" }`} > {/* Method Name */} @@ -144,7 +146,7 @@ export const ComparisonTable = () => { row.accuracy === 100 ? "text-green-600 dark:text-green-400" : "text-red-600 dark:text-red-400" }`} > - {row.accuracy}% + {row.accuracyDisplay || `${row.accuracy}%`}
{ Alternative Trade-offs

- Other methods either sacrifice automated reconciliation (requiring manual review) or burden users with - poor UX and complex implementations. + Other methods sacrifice automated reconciliation (requiring manual review), burden users with poor UX, or require complex implementations.

diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 3bdfe2e..0dcda13 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -61,6 +61,21 @@ export const IntegratedDemo = () => { ); + const UserIcon = ({ className = "h-4 w-4" }) => ( + + + + + ); + + const WalletIcon = ({ className = "h-4 w-4" }) => ( + + + + + + ); + const CryptoIcon = ({ currency, className = "w-4 h-4" }) => { const iconMap = { USDC: "/logo/icons/usdc.png", @@ -101,8 +116,58 @@ export const IntegratedDemo = () => { ); }; + // Tooltip component + const Tooltip = ({ children, content }) => { + const [show, setShow] = React.useState(false); + const [position, setPosition] = React.useState('top'); + const tooltipRef = React.useRef(null); + const triggerRef = React.useRef(null); + + React.useEffect(() => { + if (show && triggerRef.current) { + const rect = triggerRef.current.getBoundingClientRect(); + const spaceAbove = rect.top; + const spaceBelow = window.innerHeight - rect.bottom; + + // If there's less than 120px above and more space below, show tooltip below + if (spaceAbove < 120 && spaceBelow > spaceAbove) { + setPosition('bottom'); + } else { + setPosition('top'); + } + } + }, [show]); + + return ( +
+
setShow(true)} + onMouseLeave={() => setShow(false)} + > + {children} +
+ {show && ( +
+ {content} +
+
+ )} +
+ ); + }; + // Simple Button component - const Button = ({ children, onClick, disabled, variant = "default", size = "md", className = "" }) => { + const Button = ({ children, onClick, disabled, variant = "default", size = "md", className = "", pulse = false }) => { const variantClasses = { default: "bg-primary-600 hover:bg-primary-700 text-white", outline: "bg-transparent border-2 border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800", @@ -117,7 +182,7 @@ export const IntegratedDemo = () => { ); @@ -135,6 +211,7 @@ export const IntegratedDemo = () => { // Component state const rightCardRef = useRef(null); + const leftCardRef = useRef(null); const leftScrollRef = useRef(null); const rightScrollRef = useRef(null); const demoContainerRef = useRef(null); @@ -149,6 +226,8 @@ export const IntegratedDemo = () => { const [paymentCount, setPaymentCount] = useState(0); const [requestCount, setRequestCount] = useState(0); const [scrollOffset, setScrollOffset] = useState(0); + const [isShaking, setIsShaking] = useState(false); + const [hasPrePopulated, setHasPrePopulated] = useState(false); // Sync scrolling useEffect(() => { @@ -193,6 +272,76 @@ export const IntegratedDemo = () => { const handleStartDemo = () => { setHasStarted(true); setShowDialog(false); + + // Only pre-populate on first start + if (!hasPrePopulated) { + setHasPrePopulated(true); + + // Randomly select which 2 of 3 requests will have collision + const collisionPairs = [[0, 1], [0, 2], [1, 2]]; + const selectedPair = collisionPairs[Math.floor(Math.random() * collisionPairs.length)]; + + // Generate collision amount that will be shared by 2 requests + const collisionAmountData = generateAmount(); + const collisionAmount = collisionAmountData.amount; + const collisionCurrency = collisionAmountData.currency; + + // Get 3 different customers for the 3 requests + const shuffledCustomers = [...CUSTOMER_DATA].sort(() => Math.random() - 0.5); + + // Create 3 requests + const newRequests = []; + const newPlaceholders = []; + + for (let i = 0; i < 3; i++) { + const timestamp = new Date(Date.now() + i); // Slight offset for unique timestamps + const id = `request-${Date.now()}-${i}`; + + // Determine if this request should be part of the collision pair + const isCollisionRequest = selectedPair.includes(i); + + // Use collision amount for collision pair, random amount for the third request + let amount, currency; + if (isCollisionRequest) { + amount = collisionAmount; + currency = collisionCurrency; + } else { + const uniqueAmount = generateAmount(); + amount = uniqueAmount.amount; + currency = uniqueAmount.currency; + } + + const newRequest = { + id, + amount, + currency, + customer: shuffledCustomers[i].name, + status: "awaiting_payment", + timestamp, + isNew: true, + }; + + const placeholder = { + id: `placeholder-${id}`, + amount, + currency, + from: "", + timestamp, + status: "possibly_reconciled", + requestId: id, + txHash: "", + isNew: true, + isPlaceholder: true, + }; + + newRequests.push(newRequest); + newPlaceholders.push(placeholder); + } + + setRightRequests(newRequests); + setLeftPayments(newPlaceholders); + setRequestCount(3); + } }; const handleCreateRequest = () => { @@ -326,11 +475,16 @@ export const IntegratedDemo = () => { }, 100); if (hasCollision) { + // Trigger shake animation + setIsShaking(true); + setTimeout(() => setIsShaking(false), 500); + + // Show collision explainer dialog only on first collision (after shake completes) if (!hasSeenCollisionExplainer) { setTimeout(() => { setShowCollisionExplainer(true); setHasSeenCollisionExplainer(true); - }, 1000); + }, 600); // After shake animation (500ms + 100ms buffer) } } @@ -376,6 +530,7 @@ export const IntegratedDemo = () => { setHasStarted(false); setPaymentCount(0); setRequestCount(0); + setHasPrePopulated(false); setRightRequests([]); setLeftPayments([]); @@ -387,40 +542,45 @@ export const IntegratedDemo = () => { const canSimulatePayment = awaitingCount > 0; const canCreateRequest = awaitingCount < 3; const hasContent = leftPayments.length > 0 || rightRequests.length > 0; + const shouldCreateRequestPulse = rightRequests.length === 0 || rightRequests.every(r => r.status === 'paid_reconciled'); return ( -
-
+
+
{showDialog && !hasStarted && (
e.stopPropagation()} style={{ position: 'sticky', - top: '135px' + top: '150px' }} >
-

- Request Network: Identify Every Payment +

+ Identify Every Payment

-
-
- -

- Traditional blockchain payments are anonymous and hard to reconcile. -

+
+
+
+ +

+ Traditional blockchain payments are anonymous and hard to reconcile. +

+
-
- -

- Request Network adds identifiers that uniquely tie every payment to a request, making - reconciliation instant, automatic, and 100% accurate. -

+
+
+ +

+ Request Network adds identifiers that uniquely tie every payment to a request, making + reconciliation instant, automatic, and 100% automated. +

+
@@ -434,57 +594,65 @@ export const IntegratedDemo = () => { )} {showCollisionExplainer && ( -
setShowCollisionExplainer(false)} - > -
e.stopPropagation()} - style={{ - position: 'sticky', - top: '135px' - }} - > -
-
- -

- Payment Collision Detected -

-
-
-
-
- -
-

On the Left:

-

- Two payments have the same amount and currency. Which payment belongs to which customer? - Manual review required. -

-
+
+
+ {/* Left panel overlay */} +
+
setShowCollisionExplainer(false)} + /> +
e.stopPropagation()} + style={{ + marginTop: '60px', + maxWidth: '600px' + }} + > +
+
+ +

+ Payment Collision Detected +

-
-
-
- -
-

On the Right:

-

- Each payment is automatically matched to its correct request using onchain identifiers. No - ambiguity, no manual work. -

+
+
+
+ +
+

The Problem:

+

+ Two payments have the same amount and currency. Which payment belongs to which customer? + Manual review required. +

+
+
+
+
+
+ +
+

Request Network Solution:

+

+ Each payment is automatically matched to its correct request using onchain identifiers. No + ambiguity, no manual work. +

+
+
+
+ +
-
- -
+ {/* Right panel - no overlay */} +
)} @@ -502,6 +670,7 @@ export const IntegratedDemo = () => { size="lg" variant="outline" disabled={isProcessing || !canCreateRequest} + pulse={shouldCreateRequestPulse && canCreateRequest && !isProcessing} > Create Request @@ -511,6 +680,7 @@ export const IntegratedDemo = () => { onClick={handleSimulatePayment} size="lg" disabled={isProcessing || !canSimulatePayment} + pulse={canSimulatePayment && !isProcessing} > {isProcessing ? "Processing..." : "Simulate Payment"} @@ -528,13 +698,14 @@ export const IntegratedDemo = () => {
{/* Left side - Traditional Payments */} -
+
-
+

@@ -615,18 +787,25 @@ export const IntegratedDemo = () => { {getCurrencyDisplay(payment.amount, payment.currency)}

{isCollision ? ( - - Needs Review - + + + Needs Review + + ) : ( - - Possibly Reconciled - + + + Possibly Reconciled + + )}
- From +
+ + From +

{payment.from.slice(0, 10)}...{payment.from.slice(-8)}

@@ -669,6 +848,7 @@ export const IntegratedDemo = () => { onClick={handleSimulatePayment} size="lg" disabled={isProcessing || !canSimulatePayment} + pulse={canSimulatePayment && !isProcessing} > {isProcessing ? "Processing..." : "Simulate Payment"} @@ -730,9 +910,11 @@ export const IntegratedDemo = () => { {getCurrencyDisplay(request.amount, request.currency)}
{isPaid ? ( - - Paid, Reconciled - + + + Paid, Reconciled + + ) : ( Awaiting Payment @@ -741,7 +923,10 @@ export const IntegratedDemo = () => {
- Customer +
+ + Customer +

{request.customer}

{isPaid && request.txHash && ( @@ -783,6 +968,14 @@ export const IntegratedDemo = () => { .animate-in { animation: fadeIn 0.5s ease-out; } + @keyframes shake { + 0%, 100% { transform: translateX(0); } + 10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); } + 20%, 40%, 60%, 80% { transform: translateX(5px); } + } + .animate-shake { + animation: shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97); + } /* Hide scrollbar but keep functionality */ .overflow-y-auto::-webkit-scrollbar { width: 0px; From 300dba4ea27086059dd82c92e43298df8eb17801 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 14 Oct 2025 16:07:03 -0400 Subject: [PATCH 03/21] feat: tweak landing page demo --- index.mdx | 2 +- snippets/integrated-demo.jsx | 80 +++++++++++++++++++++--------------- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/index.mdx b/index.mdx index dd00e1e..a9773a0 100644 --- a/index.mdx +++ b/index.mdx @@ -32,7 +32,7 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx' Choose your path based on what you want to build:

-
+
{ // Tooltip component const Tooltip = ({ children, content }) => { const [show, setShow] = React.useState(false); - const [position, setPosition] = React.useState('top'); + const [position, setPosition] = React.useState({ top: 0, left: 0, placement: 'top' }); const tooltipRef = React.useRef(null); const triggerRef = React.useRef(null); @@ -129,12 +129,14 @@ export const IntegratedDemo = () => { const spaceAbove = rect.top; const spaceBelow = window.innerHeight - rect.bottom; - // If there's less than 120px above and more space below, show tooltip below - if (spaceAbove < 120 && spaceBelow > spaceAbove) { - setPosition('bottom'); - } else { - setPosition('top'); - } + const placement = (spaceAbove < 100 && spaceBelow > spaceAbove) ? 'bottom' : 'top'; + + const left = rect.left + rect.width / 2; + const top = placement === 'top' + ? rect.top - 8 // Position above trigger with gap + : rect.bottom + 8; // Position below trigger with gap + + setPosition({ top, left, placement }); } }, [show]); @@ -150,13 +152,17 @@ export const IntegratedDemo = () => { {show && (
{content}
@@ -177,12 +183,14 @@ export const IntegratedDemo = () => { md: "px-4 py-2 text-sm", lg: "px-6 py-3 text-base", }; + + const pulseAnimation = variant === 'outline' ? 'animate-ring-pulse-outline' : 'animate-ring-pulse'; return ( @@ -545,7 +564,7 @@ export const IntegratedDemo = () => { const shouldCreateRequestPulse = rightRequests.length === 0 || rightRequests.every(r => r.status === 'paid_reconciled'); return ( -
+
{showDialog && !hasStarted && (
{ )} {showCollisionExplainer && ( -
-
- {/* Left panel overlay */} -
-
setShowCollisionExplainer(false)} - /> -
e.stopPropagation()} - style={{ - marginTop: '60px', - maxWidth: '600px' - }} - > +
setShowCollisionExplainer(false)} + > +
e.stopPropagation()} + style={{ + position: 'sticky', + top: '150px' + }} + >
@@ -649,10 +664,6 @@ export const IntegratedDemo = () => { Got it
-
-
- {/* Right panel - no overlay */} -
)} @@ -839,6 +850,7 @@ export const IntegratedDemo = () => { size="lg" variant="outline" disabled={isProcessing || !canCreateRequest} + pulse={shouldCreateRequestPulse && canCreateRequest && !isProcessing} > Create Request @@ -912,7 +924,7 @@ export const IntegratedDemo = () => { {isPaid ? ( - Paid, Reconciled + Reconciled ) : ( From 484e63f5f3ac86fc5fa8df6488b4d34280a04b11 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 14 Oct 2025 16:27:04 -0400 Subject: [PATCH 04/21] fix: more tweaks --- snippets/integrated-demo.jsx | 37 ++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index d6ff337..9c773f6 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -587,18 +587,23 @@ export const IntegratedDemo = () => {
-

- Traditional blockchain payments are anonymous and hard to reconcile. -

+
+

Anonymous Transactions:

+

+ Traditional blockchain payments lack business context or payment identifiers. +

+
-

- Request Network adds identifiers that uniquely tie every payment to a request, making - reconciliation instant, automatic, and 100% automated. -

+
+

Unique Identifiers:

+

+ Request Network adds unique identifiers to every payment, enabling instant, automatic, and 100% automated reconciliation. +

+
@@ -632,12 +637,12 @@ export const IntegratedDemo = () => { Payment Collision Detected
-
+
-

The Problem:

+

The Problem:

Two payments have the same amount and currency. Which payment belongs to which customer? Manual review required. @@ -649,7 +654,7 @@ export const IntegratedDemo = () => {

-

Request Network Solution:

+

Request Network Solution:

Each payment is automatically matched to its correct request using onchain identifiers. No ambiguity, no manual work. @@ -744,13 +749,13 @@ export const IntegratedDemo = () => {

-
+

Traditional Blockchain Payments

- - + + Reconciled: {calculateLeftAccuracy()}
@@ -881,13 +886,13 @@ export const IntegratedDemo = () => { className="border-2 border-green-300 dark:border-green-700 bg-green-50/30 dark:bg-green-950/20 rounded-lg" >
-
+

Request Network Payments

- - + + Reconciled: 100%
From 8932cad381ab596462468e38a6e91b14932610f6 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 14 Oct 2025 17:41:57 -0400 Subject: [PATCH 05/21] fix: landing page demo --- snippets/integrated-demo.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 9c773f6..a02bab6 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -749,11 +749,11 @@ export const IntegratedDemo = () => {
-
-

+
+

Traditional Blockchain Payments

-
+
Reconciled: {calculateLeftAccuracy()} @@ -886,11 +886,11 @@ export const IntegratedDemo = () => { className="border-2 border-green-300 dark:border-green-700 bg-green-50/30 dark:bg-green-950/20 rounded-lg" >
-
-

+
+

Request Network Payments

-
+
Reconciled: 100% From 147f2b88134fa6d62746a177e4c8a5815618174a Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 14 Oct 2025 18:06:22 -0400 Subject: [PATCH 06/21] fix: update user experience labels in comparison table --- snippets/comparison-table.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/snippets/comparison-table.jsx b/snippets/comparison-table.jsx index dda8e8f..e9e1201 100644 --- a/snippets/comparison-table.jsx +++ b/snippets/comparison-table.jsx @@ -15,7 +15,7 @@ export const ComparisonTable = () => { accuracyDisplay: "< 100%", implementation: "Hard", linesOfCode: 50, - userExperience: "Okay", + userExperience: "Compromised", notes: "Requires amount and currency matching", }, { @@ -24,7 +24,7 @@ export const ComparisonTable = () => { accuracyDisplay: "< 100%", implementation: "Very Hard", linesOfCode: 150, - userExperience: "Okay", + userExperience: "Compromised", notes: "Poor performance, requires polling", }, { @@ -69,8 +69,8 @@ export const ComparisonTable = () => { switch (ux) { case "Good": return ; - case "Okay": - return ; + case "Compromised": + return ; case "Poor": return ; default: @@ -82,8 +82,8 @@ export const ComparisonTable = () => { switch (ux) { case "Good": return "text-green-700 dark:text-green-300"; - case "Okay": - return "text-yellow-700 dark:text-yellow-300"; + case "Compromised": + return "text-orange-700 dark:text-orange-300"; case "Poor": return "text-red-700 dark:text-red-300"; default: From 9d6f72554caee032b0863336720b3aed7169d623 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Tue, 14 Oct 2025 20:43:26 -0400 Subject: [PATCH 07/21] feat: add implementation difficulty icons and colors to comparison table --- snippets/comparison-table.jsx | 79 ++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/snippets/comparison-table.jsx b/snippets/comparison-table.jsx index e9e1201..b08809e 100644 --- a/snippets/comparison-table.jsx +++ b/snippets/comparison-table.jsx @@ -91,6 +91,32 @@ export const ComparisonTable = () => { } }; + const getImplementationIcon = (difficulty) => { + switch (difficulty) { + case "Easy": + return ; + case "Hard": + return ; + case "Very Hard": + return ; + default: + return null; + } + }; + + const getImplementationColor = (difficulty) => { + switch (difficulty) { + case "Easy": + return "text-green-700 dark:text-green-300"; + case "Hard": + return "text-orange-700 dark:text-orange-300"; + case "Very Hard": + return "text-red-700 dark:text-red-300"; + default: + return "text-gray-700 dark:text-gray-300"; + } + }; + return (
@@ -140,51 +166,36 @@ export const ComparisonTable = () => { {/* Accuracy */}
-
- - {row.accuracyDisplay || `${row.accuracy}%`} - -
-
-
-
+ + {row.accuracyDisplay || `${row.accuracy}%`} +
{/* Implementation Difficulty */}
- - {row.implementation} - +
+ {getImplementationIcon(row.implementation)} + {row.implementation} +
{/* Lines of Code */}
-
- {row.linesOfCode} -
+ ~{row.linesOfCode} +
{/* User Experience */} From 65e3c7daf6b335c7f5524486c3bf91ae0ff6bdc9 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 22 Oct 2025 03:54:43 -0400 Subject: [PATCH 08/21] feat(demo): enhance landing page demo with auto-simulation and improved UX - Add auto-simulation of first 3 payments with 1.5s delays - Generate realistic 64-char Request IDs and proper Ethereum addresses/tx hashes - Add Request ID visibility in middle column on right side (consistent positioning) - Add TX icon to both left and right payment cards - Add tooltips for all truncated fields (addresses, tx hashes, Request IDs) - Add informative tooltips to reconciliation badges explaining the difference - Match wallet addresses between left (from) and right (customer) for consistency - Fix column widths to prevent layout shifts based on content length - Improve tooltip styling with gray background for better light mode visibility - Ensure collision detection and dialog trigger correctly after 2nd matching payment --- snippets/integrated-demo.jsx | 240 ++++++++++++++++++++++++++++------- 1 file changed, 195 insertions(+), 45 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index a02bab6..0690091 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -76,6 +76,13 @@ export const IntegratedDemo = () => { ); + const TxIcon = ({ className = "h-3 w-3" }) => ( + + + + + ); + const CryptoIcon = ({ currency, className = "w-4 h-4" }) => { const iconMap = { USDC: "/logo/icons/usdc.png", @@ -152,7 +159,7 @@ export const IntegratedDemo = () => { {show && (
{ {content}
)} @@ -314,7 +321,8 @@ export const IntegratedDemo = () => { for (let i = 0; i < 3; i++) { const timestamp = new Date(Date.now() + i); // Slight offset for unique timestamps - const id = `request-${Date.now()}-${i}`; + // Generate a realistic Request ID (64 character hex string like the example) + const id = Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join(''); // Determine if this request should be part of the collision pair const isCollisionRequest = selectedPair.includes(i); @@ -335,6 +343,7 @@ export const IntegratedDemo = () => { amount, currency, customer: shuffledCustomers[i].name, + customerAddress: `0x${Array.from({length: 40}, () => Math.floor(Math.random() * 16).toString(16)).join('')}`, status: "awaiting_payment", timestamp, isNew: true, @@ -360,9 +369,101 @@ export const IntegratedDemo = () => { setRightRequests(newRequests); setLeftPayments(newPlaceholders); setRequestCount(3); + + // Auto-simulate the first three payments after a short delay + setTimeout(() => { + autoSimulatePayments(newRequests); + }, 1500); // 1.5s delay before first payment } }; + const autoSimulatePayments = async (requests) => { + // Simulate payments for all three requests with delays between each + for (let i = 0; i < requests.length; i++) { + if (i > 0) { + await new Promise(resolve => setTimeout(resolve, 1500)); // 1.5s delay between payments + } + await simulatePaymentForRequest(requests[i]); + } + }; + + const simulatePaymentForRequest = async (selectedRequest) => { + return new Promise((resolve) => { + const paymentAmount = selectedRequest.amount; + const paymentCurrency = selectedRequest.currency; + + // Use the customer's address from the request (for consistency between left and right) + const randomAddress = selectedRequest.customerAddress; + // Generate valid 64-character tx hash (32 bytes in hex) + const txHash = `0x${Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join('')}`; + + setLeftPayments((prev) => { + const matchingPayments = prev.filter( + (p) => !p.isPlaceholder && p.amount === paymentAmount && p.currency === paymentCurrency, + ); + + const hasCollision = matchingPayments.length > 0; + + const newPayment = { + id: `payment-${Date.now()}`, + amount: paymentAmount, + currency: paymentCurrency, + from: randomAddress, + timestamp: new Date(), + status: hasCollision ? "payment_collision" : "possibly_reconciled", + requestId: selectedRequest.id, + txHash, + isNew: true, + }; + + const placeholderIndex = prev.findIndex((p) => p.isPlaceholder && p.requestId === selectedRequest.id); + + if (placeholderIndex !== -1) { + const updated = [...prev]; + updated[placeholderIndex] = newPayment; + + if (hasCollision) { + const collisionUpdated = updated.map((p) => + !p.isPlaceholder && p.amount === paymentAmount && p.currency === paymentCurrency + ? { ...p, status: "payment_collision" } + : p, + ); + + // Trigger collision effects + setTimeout(() => { + setIsShaking(true); + setTimeout(() => setIsShaking(false), 500); + + if (!hasSeenCollisionExplainer) { + setTimeout(() => { + setShowCollisionExplainer(true); + setHasSeenCollisionExplainer(true); + }, 600); + } + }, 100); + + return collisionUpdated; + } + + return updated; + } + + return [newPayment, ...prev]; + }); + + setRightRequests((prev) => + prev.map((r) => + r.id === selectedRequest.id ? { ...r, status: "paid_reconciled", txHash, isNew: true } : r, + ), + ); + + setTimeout(() => { + triggerConfetti(); + resolve(); + }, 100); + }); + }; + const handleCreateRequest = () => { const randomCustomer = CUSTOMER_DATA[Math.floor(Math.random() * CUSTOMER_DATA.length)]; let amount; @@ -382,13 +483,15 @@ export const IntegratedDemo = () => { } const timestamp = new Date(); - const id = `request-${Date.now()}`; + // Generate a realistic Request ID (64 character hex string) + const id = Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join(''); const newRequest = { id, amount, currency, customer: randomCustomer.name, + customerAddress: `0x${Array.from({length: 40}, () => Math.floor(Math.random() * 16).toString(16)).join('')}`, status: "awaiting_payment", timestamp, isNew: true, @@ -753,12 +856,14 @@ export const IntegratedDemo = () => {

Traditional Blockchain Payments

-
- - - Reconciled: {calculateLeftAccuracy()} - -
+ +
+ + + Reconciled: {calculateLeftAccuracy()} + +
+

Anonymous transactions without business context @@ -822,20 +927,27 @@ export const IntegratedDemo = () => { From

-

- {payment.from.slice(0, 10)}...{payment.from.slice(-8)} -

+ +

+ {payment.from.slice(0, 10)}...{payment.from.slice(-8)} +

+
@@ -890,12 +1002,14 @@ export const IntegratedDemo = () => {

Request Network Payments

-
- - - Reconciled: 100% - -
+ +
+ + + Reconciled: 100% + +
+

Payments with unique IDs for instant reconciliation @@ -922,7 +1036,7 @@ export const IntegratedDemo = () => { : "border-gray-300 dark:border-gray-700 bg-gray-100 dark:bg-gray-900" }`} > -

+
{getCurrencyDisplay(request.amount, request.currency)}
@@ -938,27 +1052,63 @@ export const IntegratedDemo = () => { )}
-
-
+
+
Customer
-

{request.customer}

+ +

{request.customer}

+
- {isPaid && request.txHash && ( -
- Tx - e.preventDefault()} - > - {request.txHash.slice(0, 8)}...{request.txHash.slice(-6)} - - +
+
+ + + + + + + + Request ID
- )} + +

+ {request.id.slice(0, 10)}...{request.id.slice(-6)} +

+
+
+
+ {isPaid && request.txHash ? ( + <> +
+ + Tx ID +
+ + e.preventDefault()} + > + {request.txHash.slice(0, 8)}...{request.txHash.slice(-6)} + + + + + ) : ( + <> +
+ + Tx +
+ + 0x000000...000000 + + + )} +
); From 9981cac98f1dadab1ca7a07658c18cf7a8fca42f Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 22 Oct 2025 04:16:39 -0400 Subject: [PATCH 09/21] fix(demo): improve responsive layout for Request Network payments - Hide Request ID on mobile/tablet to prevent collisions and improve readability - Change TX ID alignment from right to left for consistency with label - Use flexible column widths (flex-1) instead of fixed percentages - Add responsive gap sizing (gap-1 on mobile, gap-2 on tablet, gap-4 on desktop) - TX ID remains visible on all screen sizes when payment is received - Request ID visible only on desktop (md breakpoint and above) --- snippets/integrated-demo.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 0690091..46a671a 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -1052,8 +1052,8 @@ export const IntegratedDemo = () => { )}
-
-
+
+
Customer @@ -1062,7 +1062,7 @@ export const IntegratedDemo = () => {

{request.customer}

-
+
@@ -1079,7 +1079,7 @@ export const IntegratedDemo = () => {

-
+
{isPaid && request.txHash ? ( <>
From aecff5583a4036410b8f5a81878bf936d3e4d581 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 22 Oct 2025 04:41:52 -0400 Subject: [PATCH 10/21] feat(demo): improve auto-play UX with button states and consistent collision timing - Disable all buttons (Simulate Payment, Create Request, Clear) during auto-play to prevent race conditions - Ensure collision always happens on 3rd payment during initial demo sequence - Increase collision dialog delay from 600ms to 1.5s for better user experience - Add isAutoPlaying state to track auto-simulation phase --- snippets/integrated-demo.jsx | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 46a671a..369ae81 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -254,6 +254,7 @@ export const IntegratedDemo = () => { const [scrollOffset, setScrollOffset] = useState(0); const [isShaking, setIsShaking] = useState(false); const [hasPrePopulated, setHasPrePopulated] = useState(false); + const [isAutoPlaying, setIsAutoPlaying] = useState(false); // Sync scrolling useEffect(() => { @@ -303,11 +304,10 @@ export const IntegratedDemo = () => { if (!hasPrePopulated) { setHasPrePopulated(true); - // Randomly select which 2 of 3 requests will have collision - const collisionPairs = [[0, 1], [0, 2], [1, 2]]; - const selectedPair = collisionPairs[Math.floor(Math.random() * collisionPairs.length)]; + // For initial auto-play: collision always happens on 3rd payment (indices 1 and 2) + const collisionPair = [1, 2]; - // Generate collision amount that will be shared by 2 requests + // Generate collision amount that will be shared by 2nd and 3rd requests const collisionAmountData = generateAmount(); const collisionAmount = collisionAmountData.amount; const collisionCurrency = collisionAmountData.currency; @@ -325,9 +325,9 @@ export const IntegratedDemo = () => { const id = Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join(''); // Determine if this request should be part of the collision pair - const isCollisionRequest = selectedPair.includes(i); + const isCollisionRequest = collisionPair.includes(i); - // Use collision amount for collision pair, random amount for the third request + // Use collision amount for collision pair, random amount for the first request let amount, currency; if (isCollisionRequest) { amount = collisionAmount; @@ -370,7 +370,8 @@ export const IntegratedDemo = () => { setLeftPayments(newPlaceholders); setRequestCount(3); - // Auto-simulate the first three payments after a short delay + // Set auto-playing state and start auto-simulation + setIsAutoPlaying(true); setTimeout(() => { autoSimulatePayments(newRequests); }, 1500); // 1.5s delay before first payment @@ -438,7 +439,8 @@ export const IntegratedDemo = () => { setTimeout(() => { setShowCollisionExplainer(true); setHasSeenCollisionExplainer(true); - }, 600); + setIsAutoPlaying(false); // Re-enable buttons when dialog appears + }, 1500); // Increased delay to 1.5s after shake } }, 100); @@ -653,6 +655,7 @@ export const IntegratedDemo = () => { setPaymentCount(0); setRequestCount(0); setHasPrePopulated(false); + setIsAutoPlaying(false); setRightRequests([]); setLeftPayments([]); @@ -661,8 +664,8 @@ export const IntegratedDemo = () => { }; const awaitingCount = rightRequests.filter((r) => r.status === "awaiting_payment").length; - const canSimulatePayment = awaitingCount > 0; - const canCreateRequest = awaitingCount < 3; + const canSimulatePayment = awaitingCount > 0 && !isAutoPlaying; + const canCreateRequest = awaitingCount < 3 && !isAutoPlaying; const hasContent = leftPayments.length > 0 || rightRequests.length > 0; const shouldCreateRequestPulse = rightRequests.length === 0 || rightRequests.every(r => r.status === 'paid_reconciled'); @@ -809,7 +812,7 @@ export const IntegratedDemo = () => { onClick={handleClearTables} variant="outline" size="lg" - disabled={!hasContent} + disabled={!hasContent || isAutoPlaying} > Clear @@ -844,7 +847,7 @@ export const IntegratedDemo = () => { onClick={handleClearTables} variant="outline" size="lg" - disabled={!hasContent} + disabled={!hasContent || isAutoPlaying} > Clear @@ -987,7 +990,7 @@ export const IntegratedDemo = () => { onClick={handleClearTables} variant="outline" size="lg" - disabled={!hasContent} + disabled={!hasContent || isAutoPlaying} > Clear From f36acd26eccc984ecd91300e506051abbca2243e Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 22 Oct 2025 05:07:27 -0400 Subject: [PATCH 11/21] feat(demo): add auto-scroll to top and hide scrollbars - Implement auto-scroll to top when creating requests or simulating payments - Hide scrollbars in all browsers (Chrome, Safari, Brave, Firefox, IE/Edge) - Use custom .no-scrollbar class for reliable cross-browser compatibility - Scroll triggers via useEffect with counter-based state for reliability - Instant scroll ensures users always see newly created/paid items --- snippets/integrated-demo.jsx | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 369ae81..56a34fc 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -255,6 +255,20 @@ export const IntegratedDemo = () => { const [isShaking, setIsShaking] = useState(false); const [hasPrePopulated, setHasPrePopulated] = useState(false); const [isAutoPlaying, setIsAutoPlaying] = useState(false); + const [shouldScrollToTop, setShouldScrollToTop] = useState(0); + + // Auto-scroll to top when needed + useEffect(() => { + if (shouldScrollToTop > 0 && leftScrollRef.current && rightScrollRef.current) { + // Use instant scroll to ensure it works + setTimeout(() => { + if (leftScrollRef.current && rightScrollRef.current) { + leftScrollRef.current.scrollTop = 0; + rightScrollRef.current.scrollTop = 0; + } + }, 50); + } + }, [shouldScrollToTop]); // Sync scrolling useEffect(() => { @@ -459,6 +473,9 @@ export const IntegratedDemo = () => { ), ); + // Trigger scroll to top via useEffect + setShouldScrollToTop(prev => prev + 1); + setTimeout(() => { triggerConfetti(); resolve(); @@ -517,6 +534,9 @@ export const IntegratedDemo = () => { return newCount; }); + + // Trigger scroll to top via useEffect + setShouldScrollToTop(prev => prev + 1); }; const calculateLeftAccuracy = () => { @@ -594,6 +614,9 @@ export const IntegratedDemo = () => { ), ); + // Trigger scroll to top via useEffect + setShouldScrollToTop(prev => prev + 1); + setTimeout(() => { triggerConfetti(); }, 100); @@ -873,7 +896,7 @@ export const IntegratedDemo = () => {

-
+
{leftPayments.length === 0 ? (

Click "Create Request" to begin

@@ -1019,7 +1042,7 @@ export const IntegratedDemo = () => {

-
+
{rightRequests.length === 0 ? (

Click "Create Request" to begin

@@ -1147,9 +1170,12 @@ export const IntegratedDemo = () => { animation: shake 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97); } /* Hide scrollbar but keep functionality */ - .overflow-y-auto::-webkit-scrollbar { - width: 0px; - background: transparent; + .no-scrollbar { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } + .no-scrollbar::-webkit-scrollbar { + display: none; /* Chrome, Safari, Brave, and other WebKit browsers */ } `}
From 3292eb3203192d78d447ab014c1e85f45f86b435 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 22 Oct 2025 05:15:33 -0400 Subject: [PATCH 12/21] fix(demo): prevent layout shift when TX ID appears - Add ExternalLinkIcon to hidden TX ID placeholder - Update label from "Tx" to "Tx ID" to match visible state - Add flex layout to placeholder to match visible TX ID link - Ensures Request ID column stays perfectly aligned when payment occurs --- snippets/integrated-demo.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 56a34fc..f2eb4c1 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -1127,10 +1127,11 @@ export const IntegratedDemo = () => { <>
- Tx + Tx ID
- + 0x000000...000000 + )} From ebaf8a2b937d525cc721017b88b64339d489c917 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Wed, 22 Oct 2025 21:07:44 -0400 Subject: [PATCH 13/21] Improve dialog visual consistency and add warning badges for payment collisions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated both 'Identify Every Payment' and 'Payment Collision' dialogs to match the polished styling of advantage/tradeoff boxes from comparison table - Changed dialog structure to use semantic HTML (

/

headings instead of

with inline bold) - Added hover effects (hover:shadow-lg, hover:scale-[1.02]) to dialog boxes for visual consistency - Improved spacing with p-4 md:p-6 padding and proper title/body separation (mb-2) - Updated icon sizes to h-5 w-5 md:h-6 md:w-6 and text sizing to text-xs md:text-sm (body) and text-sm md:text-base (titles) - Centered dialog titles while keeping box content left-aligned - Added getLeftSideMatchCount() helper function to identify potential payment collisions - Display '⚠️ X matches' warning badges on left-side payments when 2+ payments have same amount and currency - Warning badges help users visually identify potential reconciliation problems before they occur - Reduced transition duration from 500ms to 200ms for snappier UI feedback --- snippets/integrated-demo.jsx | 77 +++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 31 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index f2eb4c1..98004ce 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -548,6 +548,15 @@ export const IntegratedDemo = () => { return `${percentage}%`; }; + // Helper function to count left-side payment matches (excluding placeholders) + const getLeftSideMatchCount = (amount, currency) => { + return leftPayments.filter(p => + !p.isPlaceholder && + p.amount === amount && + p.currency === currency + ).length; + }; + const handleSimulatePayment = async () => { setIsProcessing(true); @@ -708,28 +717,28 @@ export const IntegratedDemo = () => { top: '150px' }} > -

-

+
+

Identify Every Payment

-
-
+
+
- +
-

Anonymous Transactions:

-

+

Anonymous Transactions

+

Traditional blockchain payments lack business context or payment identifiers.

-
+
- +
-

Unique Identifiers:

-

+

Unique Identifiers

+

Request Network adds unique identifiers to every payment, enabling instant, automatic, and 100% automated reconciliation.

@@ -760,31 +769,28 @@ export const IntegratedDemo = () => { }} >
-
- -

- Payment Collision Detected -

-
+

+ Payment Collision Detected +

-
-
- +
+
+
-

The Problem:

-

+

The Problem

+

Two payments have the same amount and currency. Which payment belongs to which customer? Manual review required.

-
-
- +
+
+
-

Request Network Solution:

-

+

Request Network Solution

+

Each payment is automatically matched to its correct request using onchain identifiers. No ambiguity, no manual work.

@@ -917,11 +923,13 @@ export const IntegratedDemo = () => { } const isCollision = payment.status === "payment_collision"; + const matchCount = getLeftSideMatchCount(payment.amount, payment.currency); + const showWarning = matchCount >= 2; return (
{ }`} >
-
- {getCurrencyDisplay(payment.amount, payment.currency)} +
+
+ {getCurrencyDisplay(payment.amount, payment.currency)} +
+ {showWarning && ( + + ⚠️ {matchCount} matches + + )}
{isCollision ? ( @@ -1054,7 +1069,7 @@ export const IntegratedDemo = () => { return (
Date: Thu, 23 Oct 2025 14:37:56 -0400 Subject: [PATCH 14/21] Add narrative bridges to landing page demo - Add bridge sentence to intro: "See how unique Request IDs prevent payment collisions" - Add transition heading between demo and comparison table - Improves visual cohesiveness and narrative flow per evaluation feedback --- index.mdx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/index.mdx b/index.mdx index a9773a0..45fdab6 100644 --- a/index.mdx +++ b/index.mdx @@ -12,7 +12,7 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx'

Request Network Docs

- A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty. + A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty. See how unique Request IDs prevent payment collisions:

@@ -20,6 +20,10 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx'
+

+ How does Request Network compare to other approaches? +

+
From 582ca6fdfb9fbb11a9c24fb873dc9e15b997f0e7 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Thu, 23 Oct 2025 15:37:35 -0400 Subject: [PATCH 15/21] Fix heading colors for light/dark mode compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed headings from CSS variables to Tailwind classes (text-zinc-950 dark:text-white) - Updated strong tags in description to also use Tailwind color classes - Ensures proper contrast in both light mode (dark text) and dark mode (white text) - Theme changed to 'aspen' for better frame mode rendering - Moved landing page from Welcome tab to Use Cases tab under "🚀 Get Started" --- docs.json | 14 +++--- use-cases/welcome.mdx | 106 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 use-cases/welcome.mdx diff --git a/docs.json b/docs.json index f39b279..ec5a61f 100644 --- a/docs.json +++ b/docs.json @@ -1,6 +1,6 @@ { "$schema": "https://mintlify.com/docs.json", - "theme": "mint", + "theme": "aspen", "name": "Request Network Docs", "colors": { "primary": "#01B089", @@ -36,15 +36,17 @@ }, "navigation": { "tabs": [ - { - "tab": "Welcome", - "pages": ["index"] - }, { "tab": "Use Cases", "groups": [ { - "group": "📄 Primary Use Cases", + "group": "🚀 Get Started", + "pages": [ + "use-cases/welcome" + ] + }, + { + "group": "🎯 Use Cases", "pages": [ "use-cases/invoicing", "use-cases/payouts", diff --git a/use-cases/welcome.mdx b/use-cases/welcome.mdx new file mode 100644 index 0000000..fa71e1c --- /dev/null +++ b/use-cases/welcome.mdx @@ -0,0 +1,106 @@ +--- +title: "Request Network Docs" +description: "A protocol for requests, payments, and **100% automated reconciliation**. Requests add business context to payments, eliminating manual accounting with cryptographic certainty." +sidebarTitle: "Welcome" +mode: "frame" +--- + +import { IntegratedDemo } from '/snippets/integrated-demo.jsx' +import { ComparisonTable } from '/snippets/comparison-table.jsx' + +
+
+

Request Network Docs

+

+ A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty. See how unique Request IDs prevent payment collisions: +

+
+ +
+ +
+ +

+ How does Request Network compare to other approaches? +

+ +
+ +
+
+ +
+

Get Started

+ +

+ Choose your path based on what you want to build: +

+ + + Explore specific business scenarios: invoicing, payouts, payroll, checkout, and subscriptions + + + + The easiest way to integrate. Payment types, webhooks, and developer tools. + + + + Supported chains, currencies, smart contracts, and community resources + + +
+ +
+ + + Legacy SDK and protocol documentation for advanced users + + + + Stay updated with the latest features and improvements + + +
+ +

Popular

+ + + + 1. [Get API Keys](/api-setup/getting-started) (2 minutes) + 2. [Create your first request](/use-cases/invoicing) (1 minute) + 3. [Test payment flow](/api-features/payment-types) (2 minutes) + + + + 1. [Try EasyInvoice Demo](/use-cases/invoicing) - See full invoicing workflow + 2. [Explore Use Cases](/use-cases/payouts) - Discover business applications + 3. [Fork & Customize](/use-cases/invoicing) - Make it your own + + + + 1. [Check Supported Chains](/resources/supported-chains-and-currencies) + 2. [Review Payment Types](/api-features/payment-types) + 3. [Explore Use Cases](/use-cases/invoicing) that match your needs + + +
From 080b34b3e6a5b4b7a683779cdc8b9a2e10d4b17a Mon Sep 17 00:00:00 2001 From: MantisClone Date: Thu, 23 Oct 2025 15:59:10 -0400 Subject: [PATCH 16/21] Refactor to docs-style layout with external headings and improved spacing - Moved component titles external to welcome.mdx as left-aligned h2 headings - Removed internal centered titles from IntegratedDemo and ComparisonTable components - Added "Identify Every Payment" h2 heading before demo component - Improved heading spacing: marginTop 3rem, marginBottom 2rem (matches standard docs) - Increased content spacing: demo and table sections now use 4rem bottom margin - Removed "Popular" accordion section for cleaner page structure - Standardized h2 font size to 1.75rem across all headings - Components now function as pure display widgets without embedded titles --- snippets/comparison-table.jsx | 9 --------- snippets/integrated-demo.jsx | 7 ------- use-cases/welcome.mdx | 32 +++++++------------------------- 3 files changed, 7 insertions(+), 41 deletions(-) diff --git a/snippets/comparison-table.jsx b/snippets/comparison-table.jsx index b08809e..07c9591 100644 --- a/snippets/comparison-table.jsx +++ b/snippets/comparison-table.jsx @@ -119,15 +119,6 @@ export const ComparisonTable = () => { return (
-
-

- Blockchain Payment Detection Methods Compared -

-

- Other methods require manual reconciliation or sacrifice implementation simplicity or user experience -

-
-
{/* Table Header */} diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 98004ce..236e7a9 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -808,13 +808,6 @@ export const IntegratedDemo = () => {
)} -
-

Identify Every Payment

-

- See the difference between anonymous payments and identified payments -

-
-
-
+

+ Identify Every Payment +

+ +
-

+

How does Request Network compare to other approaches?

@@ -30,7 +34,7 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx'
-

Get Started

+

Get Started

Choose your path based on what you want to build: @@ -81,26 +85,4 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx'

- -

Popular

- - - - 1. [Get API Keys](/api-setup/getting-started) (2 minutes) - 2. [Create your first request](/use-cases/invoicing) (1 minute) - 3. [Test payment flow](/api-features/payment-types) (2 minutes) - - - - 1. [Try EasyInvoice Demo](/use-cases/invoicing) - See full invoicing workflow - 2. [Explore Use Cases](/use-cases/payouts) - Discover business applications - 3. [Fork & Customize](/use-cases/invoicing) - Make it your own - - - - 1. [Check Supported Chains](/resources/supported-chains-and-currencies) - 2. [Review Payment Types](/api-features/payment-types) - 3. [Explore Use Cases](/use-cases/invoicing) that match your needs - -
From b8749694779a74595100a22e20df3254646405f5 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Thu, 23 Oct 2025 16:57:03 -0400 Subject: [PATCH 17/21] Fix comparison table text colors with theme-aware inline styles - Add React state to detect light/dark theme dynamically - Replace Tailwind text-* classes with inline style objects - Implement theme-specific colors (darker for light, brighter for dark) - Fix orange text visibility in both modes - Request Network row now uses green text in both themes - Resolves Mintlify CSS override issues with text color classes --- snippets/comparison-table.jsx | 112 +++++++++++++++++++++++++--------- 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/snippets/comparison-table.jsx b/snippets/comparison-table.jsx index 07c9591..df0f017 100644 --- a/snippets/comparison-table.jsx +++ b/snippets/comparison-table.jsx @@ -1,4 +1,26 @@ +import { useEffect, useState } from 'react'; + export const ComparisonTable = () => { + const [isDark, setIsDark] = useState(false); + + useEffect(() => { + // Check initial theme + const checkTheme = () => { + setIsDark(document.documentElement.classList.contains('dark')); + }; + + checkTheme(); + + // Watch for theme changes + const observer = new MutationObserver(checkTheme); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'] + }); + + return () => observer.disconnect(); + }, []); + const comparisonData = [ { method: "Request Network", @@ -70,7 +92,7 @@ export const ComparisonTable = () => { case "Good": return ; case "Compromised": - return ; + return ; case "Poor": return ; default: @@ -81,13 +103,13 @@ export const ComparisonTable = () => { const getUXColor = (ux) => { switch (ux) { case "Good": - return "text-green-700 dark:text-green-300"; + return { color: isDark ? "#4ade80" : "#15803d" }; // dark: green-400, light: green-700 case "Compromised": - return "text-orange-700 dark:text-orange-300"; + return { color: isDark ? "#fb923c" : "#c2410c" }; // dark: orange-400, light: orange-700 case "Poor": - return "text-red-700 dark:text-red-300"; + return { color: isDark ? "#f87171" : "#b91c1c" }; // dark: red-400, light: red-700 default: - return "text-gray-700 dark:text-gray-300"; + return { color: isDark ? "#9ca3af" : "#4b5563" }; // dark: gray-400, light: gray-600 } }; @@ -96,7 +118,7 @@ export const ComparisonTable = () => { case "Easy": return ; case "Hard": - return ; + return ; case "Very Hard": return ; default: @@ -107,18 +129,38 @@ export const ComparisonTable = () => { const getImplementationColor = (difficulty) => { switch (difficulty) { case "Easy": - return "text-green-700 dark:text-green-300"; + return { color: isDark ? "#4ade80" : "#15803d" }; // dark: green-400, light: green-700 case "Hard": - return "text-orange-700 dark:text-orange-300"; + return { color: isDark ? "#fb923c" : "#c2410c" }; // dark: orange-400, light: orange-700 case "Very Hard": - return "text-red-700 dark:text-red-300"; + return { color: isDark ? "#f87171" : "#b91c1c" }; // dark: red-400, light: red-700 default: - return "text-gray-700 dark:text-gray-300"; + return { color: isDark ? "#9ca3af" : "#4b5563" }; // dark: gray-400, light: gray-600 + } + }; + + const getLinesOfCodeIcon = (linesOfCode) => { + if (linesOfCode <= 10) { + return ; + } else if (linesOfCode <= 50) { + return ; + } else { + return ; + } + }; + + const getLinesOfCodeColor = (linesOfCode) => { + if (linesOfCode <= 10) { + return { color: isDark ? "#4ade80" : "#15803d" }; // dark: green-400, light: green-700 + } else if (linesOfCode <= 50) { + return { color: isDark ? "#fb923c" : "#c2410c" }; // dark: orange-400, light: orange-700 + } else { + return { color: isDark ? "#f87171" : "#b91c1c" }; // dark: red-400, light: red-700 } }; return ( -
+
{/* Table Header */} @@ -140,8 +182,8 @@ export const ComparisonTable = () => { key={row.method} className={`grid grid-cols-6 gap-3 p-3 md:p-4 rounded-lg border-2 transition-all duration-200 ${ row.isHighlighted - ? "bg-green-50/50 dark:bg-green-950/20 border-green-300 dark:border-green-700 hover:bg-green-100/60 dark:hover:bg-green-950/30 hover:shadow-lg dark:hover:shadow-green-900/50 hover:border-green-400 dark:hover:border-green-600" - : "bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/60 hover:shadow-lg dark:hover:shadow-gray-900/50 hover:border-gray-300 dark:hover:border-gray-600" + ? "bg-green-50/50 dark:bg-green-900/40 border-green-300 dark:border-green-600 hover:bg-green-100/60 dark:hover:bg-green-900/50 hover:shadow-lg dark:hover:shadow-green-900/50 hover:border-green-400 dark:hover:border-green-500" + : "bg-white dark:bg-[#001410]/50 border-gray-200 dark:border-gray-700/50 hover:bg-gray-50 dark:hover:bg-[#001410]/70 hover:shadow-lg dark:hover:shadow-gray-900/50 hover:border-gray-300 dark:hover:border-gray-600" }`} > {/* Method Name */} @@ -168,32 +210,46 @@ export const ComparisonTable = () => { {/* Implementation Difficulty */}
-
+
{getImplementationIcon(row.implementation)} - {row.implementation} + + {row.implementation} +
{/* Lines of Code */}
- - ~{row.linesOfCode} - +
+ {getLinesOfCodeIcon(row.linesOfCode)} + + ~{row.linesOfCode} + +
{/* User Experience */}
-
+
{getUXIcon(row.userExperience)} - {row.userExperience} + + {row.userExperience} +
From 6806e8a59714ae5f4b1a06ae3010fcedfcb4dad6 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Thu, 23 Oct 2025 17:02:01 -0400 Subject: [PATCH 18/21] Remove index.mdx to use use-cases/welcome.mdx as home page - Deleted root index.mdx file - Preview URL will now load use-cases/welcome.mdx by default - Eliminates duplicate landing page content --- index.mdx | 108 ------------------------------------------------------ 1 file changed, 108 deletions(-) delete mode 100644 index.mdx diff --git a/index.mdx b/index.mdx deleted file mode 100644 index 45fdab6..0000000 --- a/index.mdx +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: "Request Network Docs" -description: "A protocol for requests, payments, and **100% automated reconciliation**. Requests add business context to payments, **eliminating manual accounting** with cryptographic certainty." -sidebarTitle: "Welcome" -mode: "custom" ---- - -import { IntegratedDemo } from '/snippets/integrated-demo.jsx' -import { ComparisonTable } from '/snippets/comparison-table.jsx' - -
-
-

Request Network Docs

-

- A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty. See how unique Request IDs prevent payment collisions: -

-
- -
- -
- -

- How does Request Network compare to other approaches? -

- -
- -
-
- -
-

Get Started

- -

- Choose your path based on what you want to build: -

- -
- - - Explore specific business scenarios: invoicing, payouts, payroll, checkout, and subscriptions - - - - The easiest way to integrate. Payment types, webhooks, and developer tools. - - - - Supported chains, currencies, smart contracts, and community resources - - -
- -
- - - Legacy SDK and protocol documentation for advanced users - - - - Stay updated with the latest features and improvements - - -
- -

Popular

- - - - 1. [Get API Keys](/api-setup/getting-started) (2 minutes) - 2. [Create your first request](/use-cases/invoicing) (1 minute) - 3. [Test payment flow](/api-features/payment-types) (2 minutes) - - - - 1. [Try EasyInvoice Demo](/use-cases/invoicing) - See full invoicing workflow - 2. [Explore Use Cases](/use-cases/payouts) - Discover business applications - 3. [Fork & Customize](/use-cases/invoicing) - Make it your own - - - - 1. [Check Supported Chains](/resources/supported-chains-and-currencies) - 2. [Review Payment Types](/api-features/payment-types) - 3. [Explore Use Cases](/use-cases/invoicing) that match your needs - - -
From 76d873ca8392afa7515ad36e0e07b8fe69cdb727 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Thu, 23 Oct 2025 18:58:53 -0400 Subject: [PATCH 19/21] Improve landing page content structure and text clarity Welcome page improvements: - Restructured intro: shortened main tagline, moved detailed explanation under "Identify Every Payment" - Updated comparison section: better title "Blockchain Payment Detection Comparison" with contextual subtext - Removed redundant "Get Started" intro text Demo visual refinements: - Applied dark green theme to dialogs for consistency - Balanced column prominence (reduced red, enhanced green) --- snippets/integrated-demo.jsx | 12 ++++++------ use-cases/welcome.mdx | 16 +++++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index 236e7a9..b65fb0a 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -703,14 +703,14 @@ export const IntegratedDemo = () => { return (
-
+
{showDialog && !hasStarted && (
e.stopPropagation()} style={{ position: 'sticky', @@ -761,7 +761,7 @@ export const IntegratedDemo = () => { onClick={() => setShowCollisionExplainer(false)} >
e.stopPropagation()} style={{ position: 'sticky', @@ -875,10 +875,10 @@ export const IntegratedDemo = () => {
-
+
-

+

Traditional Blockchain Payments

@@ -1029,7 +1029,7 @@ export const IntegratedDemo = () => {
diff --git a/use-cases/welcome.mdx b/use-cases/welcome.mdx index 4bb0c56..24c9f8c 100644 --- a/use-cases/welcome.mdx +++ b/use-cases/welcome.mdx @@ -12,7 +12,7 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx'

Request Network Docs

- A protocol for requests, payments, and 100% automated reconciliation. Requests add business context to payments, eliminating manual accounting with cryptographic certainty. See how unique Request IDs prevent payment collisions: + A protocol for requests, payments, and 100% automated reconciliation.

@@ -20,14 +20,22 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx' Identify Every Payment

+

+ Requests add business context to payments, eliminating manual accounting with cryptographic certainty. See how unique Request IDs prevent payment collisions: +

+

- How does Request Network compare to other approaches? + Blockchain Payment Detection Comparison

+

+ How does Request Network compare to other approaches? +

+
@@ -36,9 +44,7 @@ import { ComparisonTable } from '/snippets/comparison-table.jsx'

Get Started

-

- Choose your path based on what you want to build: -

+
Date: Thu, 23 Oct 2025 19:03:07 -0400 Subject: [PATCH 20/21] Swap summary boxes for consistent left-right visual pattern - Moved "Alternative Trade-offs" (red) to left side - Moved "Request Network Advantage" (green) to right side - Now matches demo columns: problems left, solution right --- snippets/comparison-table.jsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/snippets/comparison-table.jsx b/snippets/comparison-table.jsx index df0f017..b87c24f 100644 --- a/snippets/comparison-table.jsx +++ b/snippets/comparison-table.jsx @@ -266,30 +266,30 @@ export const ComparisonTable = () => { {/* Summary Section */}
-
+
- +
-

- Request Network Advantage +

+ Alternative Trade-offs

-

- The only solution that combines 100% automated reconciliation with easy implementation and great user - experience. Built-in payment metadata eliminates guesswork entirely. +

+ Other methods sacrifice automated reconciliation (requiring manual review), burden users with poor UX, or require complex implementations.

-
+
- +
-

- Alternative Trade-offs +

+ Request Network Advantage

-

- Other methods sacrifice automated reconciliation (requiring manual review), burden users with poor UX, or require complex implementations. +

+ The only solution that combines 100% automated reconciliation with easy implementation and great user + experience. Built-in payment metadata eliminates guesswork entirely.

From 0584eac183a3cd5e0ade78ee8a710028424e5047 Mon Sep 17 00:00:00 2001 From: MantisClone Date: Sat, 25 Oct 2025 22:41:26 -0400 Subject: [PATCH 21/21] Fix tooltip bug: Generate full-length addresses and tx hashes for new payments - Fixed handleSimulatePayment to generate proper 40-char wallet addresses (instead of short truncated values) - Fixed handleSimulatePayment to generate proper 64-char transaction hashes (instead of short truncated values) - Now matches the generation logic used in simulatePaymentForRequest for initial payments - Tooltips now show complete addresses and hashes for all payments, not just the first 3 --- snippets/integrated-demo.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/snippets/integrated-demo.jsx b/snippets/integrated-demo.jsx index b65fb0a..9a25f3e 100644 --- a/snippets/integrated-demo.jsx +++ b/snippets/integrated-demo.jsx @@ -575,8 +575,10 @@ export const IntegratedDemo = () => { const paymentAmount = selectedRequest.amount; const paymentCurrency = selectedRequest.currency; - const randomAddress = `0x${Math.random().toString(16).substr(2, 40)}`; - const txHash = `0x${Math.random().toString(16).substr(2, 16)}`; + // Generate valid 40-character wallet address (20 bytes in hex) + const randomAddress = `0x${Array.from({length: 40}, () => Math.floor(Math.random() * 16).toString(16)).join('')}`; + // Generate valid 64-character tx hash (32 bytes in hex) + const txHash = `0x${Array.from({length: 64}, () => Math.floor(Math.random() * 16).toString(16)).join('')}`; const matchingPayments = leftPayments.filter( (p) => !p.isPlaceholder && p.amount === paymentAmount && p.currency === paymentCurrency,