PageSourceSearch

https://class-act.netlify.app/assets/009_payment-DzUnEGKU.js

js class-act.netlify.app collected 2026-10-03 10:35:04 UTC 16,805 bytes, 406 lines download raw bytes

1import{j as e}from"./index-w8uym0Jr.js";import"./vendor-react-B5kyjNin.js";import"./vendor-auth-3IJ4rw9Z.js";import"./vendor-ui-zVkK_Prl.js";import"./vendor-payment-Cx_eeV9O.js";import"./vendor-framer-CGrNjTbd.js";function t(n){const r={code:"code",h1:"h1",h2:"h2",h3:"h3",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...n.components};return e.jsxs(e.Fragment,{children:[e.jsx(r.h1,{children:"Payment Integration"}),`
2`,e.jsx(r.p,{children:"This document outlines the payment processing system in ClassAct, which uses PayPal integration to handle ticket purchases."}),`
3`,e.jsx(r.h2,{children:"Overview"}),`
4`,e.jsx(r.p,{children:"ClassAct implements PayPal as the primary payment processor for ticket purchases. The integration uses the official React PayPal JS library to provide a secure and seamless payment experience."}),`
5`,e.jsx(r.h2,{children:"Configuration"}),`
6`,e.jsx(r.h3,{children:"Environment Variables"}),`
7`,e.jsxs(r.p,{children:["Payment integration requires the following environment variable to be set in your ",e.jsx(r.code,{children:".env"})," file:"]}),`
8`,e.jsx("div",{className:"command-example",children:e.jsx("div",{className:"output-line",children:e.jsx("pre",{children:"VITE_PAYPAL_CLIENT_ID=your_paypal_client_id"})})}),`
9`,e.jsxs("div",{className:"note-block",children:[e.jsx("div",{className:"note-icon",children:"⚠️"}),e.jsxs("div",{className:"note-content",children:[e.jsx("p",{children:e.jsx("strong",{children:"Security Note:"})}),e.jsx("p",{children:"Never hardcode PayPal credentials in your application code. Always use environment variables to store sensitive information."}),e.jsx("p",{children:"For development, you can obtain a sandbox client ID from the PayPal Developer Dashboard."})]})]}),`
10`,e.jsx(r.p,{children:"For local development, use PayPal's sandbox environment to avoid real transactions. The sandbox allows you to test the entire payment flow without processing actual payments."}),`
11`,e.jsx(r.h3,{children:"PayPal Provider Setup"}),`
12`,e.jsx(r.p,{children:"The application uses the PayPalScriptProvider to configure the PayPal SDK:"}),`
13`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-tsx",children:`/**\r
14 * @fileoverview PayPal configuration for ClassAct\r
15 * \r
16 * Features:\r
17 * - PayPal script provider setup\r
18 * - Currency and client ID configuration\r
19 * \r
20 * @requires @paypal/react-paypal-js\r
21 * @requires react\r
22 */\r
23\r
24import { PayPalScriptProvider } from '@paypal/react-paypal-js';\r
25import React from 'react';\r
26\r
27// Initial configuration for PayPal\r
28const paypalInitialOptions = {\r
29  'client-id': import.meta.env.VITE_PAYPAL_CLIENT_ID,\r
30  currency: 'USD',\r
31  intent: 'capture',\r
32  'data-client-token': 'abc123xyz==',\r
33  // Set to 'sandbox' during development, 'production' for live payments\r
34};\r
35\r
36export const PayPalProvider = ({ children }) => {\r
37  return (\r
38    <PayPalScriptProvider options={paypalInitialOptions}>\r
39      {children}\r
40    </PayPalScriptProvider>\r
41  );\r
42};
43`})}),`
44`,e.jsx(r.h2,{children:"Component Implementation"}),`
45`,e.jsx(r.h3,{children:"PayPal Buttons Component"}),`
46`,e.jsxs(r.p,{children:["The ",e.jsx(r.code,{children:"PayPalButtons"})," component handles the payment UI and transaction logic. Implement it as follows:"]}),`
47`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-tsx",children:`/**\r
48 * @fileoverview Checkout component with PayPal integration\r
49 * \r
50 * Features:\r
51 * - PayPal button rendering\r
52 * - Payment processing\r
53 * - Transaction confirmation\r
54 * \r
55 * @requires react\r
56 * @requires @paypal/react-paypal-js\r
57 * @requires ../utils/logger\r
58 */\r
59\r
60import React, { useState, useEffect } from 'react';\r
61import { PayPalButtons, usePayPalScriptReducer } from '@paypal/react-paypal-js';\r
62import { createLogger } from '../utils/logger';\r
63import { useNavigate, useParams } from 'react-router-dom';\r
64import { useAuth } from '../context/AuthContext';\r
65import { getEventById } from '../services/eventService';\r
66import { createTicket } from '../services/ticketService';\r
67import styles from './CheckoutPreview.module.scss';\r
68import toast from 'react-hot-toast';\r
69\r
70const logger = createLogger('CheckoutPreview', true);\r
71\r
72const CheckoutPreview = () => {\r
73  const [{ isPending }] = usePayPalScriptReducer();\r
74  const [event, setEvent] = useState(null);\r
75  const [loading, setLoading] = useState(true);\r
76  const [error, setError] = useState(null);\r
77  const { eventId } = useParams();\r
78  const { user } = useAuth();\r
79  const navigate = useNavigate();\r
80\r
81  // Load event details\r
82  useEffect(() => {\r
83    const loadEvent = async () => {\r
84      try {\r
85        setLoading(true);\r
86        const eventData = await getEventById(eventId);\r
87        setEvent(eventData);\r
88      } catch (err) {\r
89        logger.error('Failed to load event:', err);\r
90        setError('Failed to load event details. Please try again.');\r
91        toast.error('Failed to load event details');\r
92      } finally {\r
93        setLoading(false);\r
94      }\r
95    };\r
96\r
97    loadEvent();\r
98  }, [eventId]);\r
99\r
100  // Handle payment completion\r
101  const handlePaymentSuccess = async (details) => {\r
102    try {\r
103      logger.log('Payment completed successfully:', details);\r
104      \r
105      // Create ticket after successful payment\r
106      const ticketData = {\r
107        event_id: eventId,\r
108        user_id: user.id,\r
109        purchase_id: details.id\r
110      };\r
111      \r
112      const ticket = await createTicket(ticketData);\r
113      \r
114      // Show success notification\r
115      toast.success('Payment successful! Your ticket has been issued.');\r
116      \r
117      // Redirect to ticket view\r
118      navigate(\`/tickets/\${ticket.id}\`);\r
119    } catch (err) {\r
120      logger.error('Error processing ticket after payment:', err);\r
121      setError('Payment was successful but we had trouble creating your ticket. Please contact support.');\r
122      toast.error('Error processing ticket');\r
123    }\r
124  };\r
125\r
126  return (\r
127    <div className={styles.checkoutContainer}>\r
128      {loading ? (\r
129        <div className={styles.loading}>Loading event details...</div>\r
130      ) : error ? (\r
131        <div className={styles.error}>{error}</div>\r
132      ) : (\r
133        <div className={styles.checkoutCard}>\r
134          <h1>Checkout</h1>\r
135          \r
136          <div className={styles.eventDetails}>\r
137            <h2>{event.title}</h2>\r
138            <p className={styles.eventDate}>{new Date(event.date).toLocaleDateString()}</p>\r
139            <p className={styles.eventLocation}>{event.location}</p>\r
140          </div>\r
141          \r
142          <div className={styles.priceDetails}>\r
143            <span className={styles.priceLabel}>Ticket Price:</span>\r
144            <span className={styles.priceValue}>\${event.price.toFixed(2)}</span>\r
145          </div>\r
146          \r
147          <div className={styles.paymentContainer}>\r
148            {isPending ? (\r
149              <div className={styles.loading}>Loading PayPal buttons...</div>\r
150            ) : (\r
151              <PayPalButtons\r
152                style={{ layout: 'vertical' }}\r
153                createOrder={(data, actions) => {\r
154                  return actions.order.create({\r
155                    purchase_units: [\r
156                      {\r
157                        description: \`Ticket for \${event.title}\`,\r
158                        amount: {\r
159                          currency_code: 'USD',\r
160                          value: event.price.toString(),\r
161                        },\r
162                      },\r
163                    ],\r
164                  });\r
165                }}\r
166                onApprove={(data, actions) => {\r
167                  return actions.order.capture().then(handlePaymentSuccess);\r
168                }}\r
169                onError={(err) => {\r
170                  logger.error('PayPal error:', err);\r
171                  setError('Payment failed. Please try again.');\r
172                  toast.error('Payment failed');\r
173                }}\r
174              />\r
175            )}\r
176          </div>\r
177        </div>\r
178      )}\r
179    </div>\r
180  );\r
181};\r
182\r
183export default CheckoutPreview;
184`})}),`
185`,e.jsx(r.h2,{children:"Payment Flow"}),`
186`,e.jsx(r.p,{children:"The payment process follows these steps:"}),`
187`,e.jsxs(r.ol,{children:[`
188`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Event Selection"}),': User selects an event and clicks "Buy Ticket"']}),`
189`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Checkout Page"}),": User is taken to the checkout page showing event details and price"]}),`
190`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Payment Processing"}),": User completes payment using PayPal"]}),`
191`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Transaction Verification"}),": Application validates the payment with PayPal"]}),`
192`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Ticket Creation"}),": Upon successful payment, a ticket is created"]}),`
193`,e.jsxs(r.li,{children:[e.jsx(r.strong,{children:"Confirmation"}),": User receives confirmation and is redirected to view their ticket"]}),`
194`]}),`
195`,e.jsx(r.h2,{children:"Payment Service"}),`
196`,e.jsx(r.p,{children:"The payment service handles payment verification and tracking:"}),`
197`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-typescript",children:`/**\r
198 * @fileoverview Payment service for ClassAct\r
199 * \r
200 * Features:\r
201 * - Payment verification\r
202 * - Transaction recording\r
203 * - Payment history\r
204 * \r
205 * @requires ../services/supabaseClient\r
206 * @requires ../utils/logger\r
207 */\r
208\r
209import { supabase } from '../services/supabaseClient';\r
210import { createLogger } from '../utils/logger';\r
211\r
212const logger = createLogger('PaymentService', true);\r
213\r
214// Record payment transaction\r
215export const recordPayment = async (paymentData) => {\r
216  try {\r
217    logger.log('Recording payment transaction:', paymentData);\r
218    \r
219    const { data, error } = await supabase\r
220      .from('payments')\r
221      .insert({\r
222        user_id: paymentData.userId,\r
223        event_id: paymentData.eventId,\r
224        amount: paymentData.amount,\r
225        transaction_id: paymentData.transactionId,\r
226        provider: 'paypal',\r
227        status: paymentData.status,\r
228        payment_details: paymentData.details\r
229      })\r
230      .select();\r
231      \r
232    if (error) {\r
233      logger.error('Error recording payment:', error);\r
234      throw error;\r
235    }\r
236    \r
237    return data[0];\r
238  } catch (error) {\r
239    logger.error('Unexpected error recording payment:', error);\r
240    throw error;\r
241  }\r
242};\r
243\r
244// Get payment history for a user\r
245export const getUserPayments = async (userId) => {\r
246  try {\r
247    logger.log('Fetching payment history for user:', userId);\r
248    \r
249    const { data, error } = await supabase\r
250      .from('payments')\r
251      .select(\`\r
252        *,\r
253        events (title, date)\r
254      \`)\r
255      .eq('user_id', userId)\r
256      .order('created_at', { ascending: false });\r
257      \r
258    if (error) {\r
259      logger.error('Error fetching payment history:', error);\r
260      throw error;\r
261    }\r
262    \r
263    return data;\r
264  } catch (error) {\r
265    logger.error('Unexpected error fetching payment history:', error);\r
266    throw error;\r
267  }\r
268};\r
269\r
270// Verify payment status\r
271export const verifyPayment = async (transactionId) => {\r
272  try {\r
273    logger.log('Verifying payment status for transaction:', transactionId);\r
274    \r
275    // In a real implementation, you would make an API call to PayPal\r
276    // to verify the transaction status\r
277    \r
278    const { data, error } = await supabase\r
279      .from('payments')\r
280      .select('*')\r
281      .eq('transaction_id', transactionId)\r
282      .single();\r
283      \r
284    if (error) {\r
285      logger.error('Error verifying payment:', error);\r
286      throw error;\r
287    }\r
288    \r
289    return {\r
290      verified: data.status === 'completed',\r
291      data\r
292    };\r
293  } catch (error) {\r
294    logger.error('Unexpected error verifying payment:', error);\r
295    throw error;\r
296  }\r
297};
298`})}),`
299`,e.jsx(r.h2,{children:"Handling Payment Failures"}),`
300`,e.jsx(r.p,{children:"The application implements robust error handling for payment failures:"}),`
301`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-tsx",children:`// Inside checkout component\r
302const handlePaymentError = (error) => {\r
303  logger.error('Payment failed:', error);\r
304  \r
305  // Show error notification to user\r
306  toast.error('Payment failed. Please try again.');\r
307  \r
308  // Log detailed error information\r
309  logger.error('Payment error details:', {\r
310    eventId,\r
311    userId: user.id,\r
312    errorMessage: error.message,\r
313    errorDetails: error\r
314  });\r
315  \r
316  // Update UI to show error state\r
317  setStatus('error');\r
318  setErrorMessage('We encountered an issue processing your payment. Please try again or use a different payment method.');\r
319};
320`})}),`
321`,e.jsx(r.h2,{children:"Webhook Integration"}),`
322`,e.jsx(r.p,{children:"For production deployments, ClassAct implements PayPal webhooks to handle asynchronous payment events:"}),`
323`,e.jsx(r.pre,{children:e.jsx(r.code,{className:"language-typescript",children:`// Webhook handler (server-side implementation)\r
324export const handlePayPalWebhook = async (req, res) => {\r
325  try {\r
326    const { event_type, resource } = req.body;\r
327    \r
328    // Verify webhook signature (implementation details omitted)\r
329    \r
330    logger.log('Received PayPal webhook:', event_type);\r
331    \r
332    // Handle different event types\r
333    switch (event_type) {\r
334      case 'PAYMENT.CAPTURE.COMPLETED':\r
335        // Payment was successful\r
336        await handlePaymentSuccess(resource);\r
337        break;\r
338        \r
339      case 'PAYMENT.CAPTURE.DENIED':\r
340        // Payment was denied\r
341        await handlePaymentDenied(resource);\r
342        break;\r
343        \r
344      case 'PAYMENT.CAPTURE.REFUNDED':\r
345        // Payment was refunded\r
346        await handlePaymentRefund(resource);\r
347        break;\r
348        \r
349      default:\r
350        logger.log('Unhandled webhook event type:', event_type);\r
351    }\r
352    \r
353    res.status(200).send('Webhook received');\r
354  } catch (error) {\r
355    logger.error('Error handling PayPal webhook:', error);\r
356    res.status(400).send('Webhook Error');\r
357  }\r
358};
359`})}),`
360`,e.jsx(r.h2,{children:"Testing PayPal Integration"}),`
361`,e.jsx(r.p,{children:"For development and testing, PayPal provides sandbox credentials:"}),`
362`,e.jsxs(r.ol,{children:[`
363`,e.jsx(r.li,{children:"Create a PayPal Developer account at developer.paypal.com"}),`
364`,e.jsx(r.li,{children:"Create sandbox business and personal accounts"}),`
365`,e.jsx(r.li,{children:"Use sandbox client ID in development environment"}),`
366`,e.jsx(r.li,{children:"Test transactions using the sandbox personal account"}),`
367`]}),`
368`,e.jsx(r.p,{children:"Testing can be done using these PayPal Sandbox test accounts:"}),`
369`,e.jsx(r.pre,{children:e.jsx(r.code,{children:`Sandbox Business Account: [email protected] / password123\r
370Sandbox Personal Account: [email protected] / password123
371`})}),`
372`,e.jsx(r.h2,{children:"Security Considerations"}),`
373`,e.jsxs(r.ol,{children:[`
374`,e.jsxs(r.li,{children:[`
375`,e.jsxs(r.p,{children:[e.jsx(r.strong,{children:"Client-side Security"}),":"]}),`
376`,e.jsxs(r.ul,{children:[`
377`,e.jsx(r.li,{children:"Never store sensitive payment information"}),`
378`,e.jsx(r.li,{children:"Use PayPal SDK for all payment operations"}),`
379`,e.jsx(r.li,{children:"Validate transaction IDs server-side"}),`
380`]}),`
381`]}),`
382`,e.jsxs(r.li,{children:[`
383`,e.jsxs(r.p,{children:[e.jsx(r.strong,{children:"Server-side Verification"}),":"]}),`
384`,e.jsxs(r.ul,{children:[`
385`,e.jsx(r.li,{children:"Always verify payment status before creating tickets"}),`
386`,e.jsx(r.li,{children:"Implement idempotency to prevent duplicate transactions"}),`
387`,e.jsx(r.li,{children:"Store payment records for audit purposes"}),`
388`]}),`
389`]}),`
390`,e.jsxs(r.li,{children:[`
391`,e.jsxs(r.p,{children:[e.jsx(r.strong,{children:"Error Handling"}),":"]}),`
392`,e.jsxs(r.ul,{children:[`
393`,e.jsx(r.li,{children:"Provide clear error messages to users"}),`
394`,e.jsx(r.li,{children:"Log detailed error information for troubleshooting"}),`
395`,e.jsx(r.li,{children:"Implement recovery mechanisms for interrupted payments"}),`
396`]}),`
397`]}),`
398`]}),`
399`,e.jsx(r.h2,{children:"Best Practices"}),`
400`,e.jsxs(r.ol,{children:[`
401`,e.jsx(r.li,{children:"Always use the PayPal sandbox for development and testing"}),`
402`,e.jsx(r.li,{children:"Implement proper error handling and logging"}),`
403`,e.jsx(r.li,{children:"Store transaction IDs with ticket records for reference"}),`
404`,e.jsx(r.li,{children:"Provide clear payment status updates to users"}),`
405`,e.jsx(r.li,{children:"Include contact information for payment issues"}),`
406`]})]})}function d(n={}){const{wrapper:r}=n.components||{};return r?e.jsx(r,{...n,children:e.jsx(t,{...n})}):t(n)}export{d as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.