1+ "use client"
2+ import React , { useState , useEffect } from 'react' ;
3+ import { Scanner } from '@yudiel/react-qr-scanner' ;
4+ import { useAccount } from 'wagmi' ;
5+ import toast , { Toaster } from 'react-hot-toast' ;
6+ import { useRouter } from 'next/navigation' ;
7+
8+ const UpiQrScanner = ( ) => {
9+ const { isConnected } = useAccount ( ) ;
10+ const router = useRouter ( ) ;
11+ const [ scanResult , setScanResult ] = useState < string | null > ( null ) ;
12+ const [ jsonObject , setJsonObject ] = useState < any > ( null ) ;
13+ const [ error , setError ] = useState < string | null > ( null ) ;
14+ const [ isScanning , setIsScanning ] = useState ( false ) ;
15+
16+ // Check wallet connection before starting scanner
17+ useEffect ( ( ) => {
18+ if ( isConnected ) {
19+ setIsScanning ( true ) ;
20+ } else {
21+ setIsScanning ( false ) ;
22+ toast . error ( 'Please connect your wallet first to use QR scanner' , {
23+ duration : 4000 ,
24+ position : 'bottom-right' ,
25+ style : {
26+ background : '#dc3545' ,
27+ color : 'white' ,
28+ fontWeight : 'bold' ,
29+ } ,
30+ } ) ;
31+ }
32+ } , [ isConnected ] ) ;
33+
34+ const handleScan = ( result : any ) => {
35+ if ( result && result . length > 0 ) {
36+ const scannedText = result [ 0 ] ?. rawValue || result [ 0 ] ?. data || result ;
37+
38+ try {
39+ // Parse the UPI URL string into a JSON object
40+ const url = new URL ( scannedText ) ;
41+ const params = Object . fromEntries ( url . searchParams . entries ( ) ) ;
42+
43+ // Show success toast and immediately redirect
44+ toast . success ( 'UPI QR Code scanned! Redirecting to create order...' , {
45+ duration : 1500 ,
46+ position : 'bottom-right' ,
47+ style : {
48+ background : '#28a745' ,
49+ color : 'white' ,
50+ fontWeight : 'bold' ,
51+ } ,
52+ } ) ;
53+
54+ // Immediate redirect to maker dashboard with UPI data - no intermediate state
55+ const queryParams = new URLSearchParams ( {
56+ upiAddress : params . pa || '' ,
57+ payeeName : params . pn || '' ,
58+ amount : params . am || '' ,
59+ transactionRef : params . tr || '' ,
60+ merchantCode : params . mc || ''
61+ } ) . toString ( ) ;
62+
63+ // Redirect immediately without setting any intermediate state
64+ router . push ( `/maker-dashboard?tab=create&${ queryParams } ` ) ;
65+
66+ } catch ( err ) {
67+ // If it's not a URL, try to parse as UPI format
68+ if ( scannedText . toLowerCase ( ) . startsWith ( 'upi://' ) ) {
69+ try {
70+ const upiUrl = new URL ( scannedText ) ;
71+ const params = Object . fromEntries ( upiUrl . searchParams . entries ( ) ) ;
72+
73+ // Show success toast and immediately redirect
74+ toast . success ( 'UPI QR Code scanned! Redirecting to create order...' , {
75+ duration : 1500 ,
76+ position : 'bottom-right' ,
77+ style : {
78+ background : '#28a745' ,
79+ color : 'white' ,
80+ fontWeight : 'bold' ,
81+ } ,
82+ } ) ;
83+
84+ // Immediate redirect to maker dashboard with UPI data
85+ const queryParams = new URLSearchParams ( {
86+ upiAddress : params . pa || '' ,
87+ payeeName : params . pn || '' ,
88+ amount : params . am || '' ,
89+ transactionRef : params . tr || '' ,
90+ merchantCode : params . mc || ''
91+ } ) . toString ( ) ;
92+
93+ // Redirect immediately without setting any intermediate state
94+ router . push ( `/maker-dashboard?tab=create&${ queryParams } ` ) ;
95+
96+ } catch ( upiErr ) {
97+ setError ( 'Invalid UPI QR code format' ) ;
98+ setJsonObject ( null ) ;
99+ toast . error ( 'Invalid UPI QR code format' , {
100+ duration : 3000 ,
101+ position : 'bottom-right' ,
102+ style : {
103+ background : '#dc3545' ,
104+ color : 'white' ,
105+ fontWeight : 'bold' ,
106+ } ,
107+ } ) ;
108+ }
109+ } else {
110+ // For non-UPI QR codes, just show the raw data
111+ setJsonObject ( { rawData : scannedText } ) ;
112+ setError ( null ) ;
113+ setIsScanning ( false ) ;
114+ }
115+ }
116+ }
117+ } ;
118+
119+ const handleError = ( err : any ) => {
120+ console . error ( 'QR Scanner Error:' , err ) ;
121+ setError ( 'Error scanning QR code: ' + ( err ?. message || 'Unknown error' ) ) ;
122+ } ;
123+
124+ return (
125+ < div className = "min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 p-4" >
126+ < Toaster
127+ position = "bottom-right"
128+ toastOptions = { {
129+ duration : 3000 ,
130+ style : {
131+ borderRadius : '12px' ,
132+ fontSize : '14px' ,
133+ fontWeight : '500' ,
134+ maxWidth : '90vw' ,
135+ } ,
136+ } }
137+ />
138+
139+ < div className = "max-w-md mx-auto" >
140+ { /* Header */ }
141+ < div className = "text-center mb-8 pt-4" >
142+ < div className = "inline-flex items-center justify-center w-16 h-16 bg-gradient-to-r from-blue-500 to-indigo-600 rounded-full mb-4 shadow-lg" >
143+ < span className = "text-2xl" > 📱</ span >
144+ </ div >
145+ < h1 className = "text-3xl font-bold text-gray-800 mb-2" >
146+ UPI QR Scanner
147+ </ h1 >
148+ < p className = "text-gray-600 text-sm" >
149+ Scan UPI QR codes to process payments
150+ </ p >
151+ </ div >
152+
153+ { /* Wallet Connection Status */ }
154+ { ! isConnected && (
155+ < div className = "bg-blue-50 border-4 border-blue-300 rounded-xl p-6 mb-6 shadow-sm" >
156+ < div className = "flex items-center justify-center mb-4" >
157+ < div className = "w-12 h-12 bg-blue-100 rounded-full flex items-center justify-center" >
158+ < span className = "text-2xl" > 🔒</ span >
159+ </ div >
160+ </ div >
161+ < h3 className = "text-lg font-semibold text-blue-800 text-center mb-2" >
162+ Wallet Not Connected
163+ </ h3 >
164+ < p className = "text-blue-600 text-center text-sm leading-relaxed" >
165+ Connect your wallet to start scanning UPI QR codes and process payments securely
166+ </ p >
167+ </ div >
168+ ) }
169+
170+ { /* Camera Scanner */ }
171+ { isConnected && isScanning && ! scanResult && (
172+ < div className = "bg-white rounded-2xl shadow-lg overflow-hidden mb-6" >
173+ { /* Scanner Header */ }
174+ < div className = "bg-gradient-to-r from-blue-500 to-indigo-600 px-6 py-4" >
175+ < div className = "flex items-center justify-center space-x-2" >
176+ < span className = "text-white font-medium" > Scanning Active</ span >
177+ </ div >
178+ </ div >
179+
180+ { /* Instructions */ }
181+ < div className = "px-6 py-4 bg-blue-50 border-b" >
182+ < p className = "text-center text-blue-700 font-medium text-sm" >
183+ Point your camera at a UPI QR code
184+ </ p >
185+ </ div >
186+
187+ { /* Scanner Area */ }
188+ < div className = "p-4" >
189+ < div className = "relative rounded-xl overflow-hidden bg-black" >
190+ < Scanner
191+ onScan = { handleScan }
192+ onError = { handleError }
193+ constraints = { {
194+ facingMode : 'environment'
195+ } }
196+ styles = { {
197+ container : {
198+ width : '100%' ,
199+ height : '280px' ,
200+ }
201+ } }
202+ allowMultiple = { false }
203+ scanDelay = { 300 }
204+ />
205+ </ div >
206+
207+ { /* Tips */ }
208+ < div className = "mt-4 text-center" >
209+ < p className = "text-xs text-gray-500 mb-2" > 💡 Tips for better scanning:</ p >
210+ < div className = "flex justify-center space-x-4 text-xs text-gray-400" >
211+ < span > • Good lighting</ span >
212+ < span > • Steady hands</ span >
213+ < span > • Clear QR code</ span >
214+ </ div >
215+ </ div >
216+ </ div >
217+ </ div >
218+ ) }
219+
220+ { /* Error Display */ }
221+ { error && (
222+ < div className = "bg-red-50 border border-red-200 rounded-xl p-6 mb-6 shadow-sm" >
223+ < div className = "flex items-center justify-center mb-2" >
224+ < span className = "text-2xl mr-2" > ❌</ span >
225+ < span className = "text-red-800 font-semibold" > Scan Error</ span >
226+ </ div >
227+ < p className = "text-red-600 text-center text-sm" >
228+ { error }
229+ </ p >
230+ </ div >
231+ ) }
232+ </ div >
233+ </ div >
234+ ) ;
235+ } ;
236+
237+ export default UpiQrScanner ;
0 commit comments