@@ -122,6 +122,35 @@ export const useSecureTrial = () => {
122122 return sessionId ;
123123 } ;
124124
125+ // Check if device has ever used a trial (additional security layer)
126+ const hasUsedTrialBefore = ( ) : boolean => {
127+ try {
128+ // Check multiple storage mechanisms for trial usage evidence
129+ const localTrialId = localStorage . getItem ( "currentTrialId" ) ;
130+ const sessionId = localStorage . getItem ( "trial_session_id" ) ;
131+ const fallbackId = localStorage . getItem ( "ip_fallback_id" ) ;
132+
133+ // Also check sessionStorage (survives page refresh but not tab close)
134+ const sessionTrialUsed = sessionStorage . getItem ( "trial_ever_used" ) ;
135+
136+ // If any evidence of previous trial exists, consider it used
137+ return ! ! ( localTrialId || sessionId || fallbackId || sessionTrialUsed ) ;
138+ } catch ( error ) {
139+ // If we can't check, assume trial was used (security-first)
140+ return true ;
141+ }
142+ } ;
143+
144+ // Mark that this device/session has used a trial
145+ const markTrialAsUsed = ( ) : void => {
146+ try {
147+ sessionStorage . setItem ( "trial_ever_used" , "true" ) ;
148+ localStorage . setItem ( "trial_ever_used" , Date . now ( ) . toString ( ) ) ;
149+ } catch ( error ) {
150+ // Ignore storage errors
151+ }
152+ } ;
153+
125154 // Check if user already has an active trial
126155 const checkExistingTrial = async ( ) : Promise < TrialRecord | null > => {
127156 try {
@@ -254,10 +283,11 @@ export const useSecureTrial = () => {
254283 } ,
255284 ) ;
256285
257- // Store trial ID for later reference
286+ // Store trial ID for later reference and mark trial as used
258287 if ( typeof window !== "undefined" ) {
259288 localStorage . setItem ( "currentTrialId" , trialRecord . $id ) ;
260289 localStorage . removeItem ( "creating_trial" ) ; // Clear the creation flag
290+ markTrialAsUsed ( ) ; // Mark that this device has used a trial
261291 }
262292
263293 return trialRecord as unknown as TrialRecord ;
@@ -287,7 +317,7 @@ export const useSecureTrial = () => {
287317 } ,
288318 ) ;
289319 } catch ( error ) {
290- console . error ( "Error expiring trial:" , error ) ;
320+ // Ignore errors
291321 }
292322 } ;
293323
@@ -306,6 +336,17 @@ export const useSecureTrial = () => {
306336 }
307337
308338 try {
339+ // First check if this device has ever used a trial (additional security)
340+ if ( hasUsedTrialBefore ( ) ) {
341+ // Device has evidence of previous trial usage - block access immediately
342+ setTrialExpired ( true ) ;
343+ setTrialBlocked ( true ) ;
344+ setTimeRemaining ( 0 ) ;
345+ setTrialStartTime ( null ) ;
346+ setIsLoading ( false ) ;
347+ return ;
348+ }
349+
309350 // Check if user already has an active trial
310351 const existingTrial = await checkExistingTrial ( ) ;
311352
@@ -332,28 +373,56 @@ export const useSecureTrial = () => {
332373 }
333374 }
334375 } else {
335- // No existing trial, create a new one
336- const newTrial = await createTrial ( ) ;
337-
338- if ( newTrial ) {
339- setTrialStartTime ( newTrial . start_time ) ;
340- setTimeRemaining ( TRIAL_DURATION_MS ) ;
341- setTrialExpired ( false ) ;
342- setTrialBlocked ( false ) ;
376+ // No existing trial found on server
377+ // Check if device has evidence of previous trial usage
378+ if ( hasUsedTrialBefore ( ) ) {
379+ // Device shows evidence of previous trial - block access for security
380+ setTrialExpired ( true ) ;
381+ setTrialBlocked ( true ) ;
382+ setTimeRemaining ( 0 ) ;
383+ setTrialStartTime ( null ) ;
343384 } else {
344- // Failed to create trial - allow access but log error
345- setTrialStartTime ( Date . now ( ) ) ;
346- setTimeRemaining ( TRIAL_DURATION_MS ) ;
347- setTrialExpired ( false ) ;
348- setTrialBlocked ( false ) ;
385+ // Genuinely new device/session - create a new trial
386+ const newTrial = await createTrial ( ) ;
387+
388+ if ( newTrial ) {
389+ setTrialStartTime ( newTrial . start_time ) ;
390+ setTimeRemaining ( TRIAL_DURATION_MS ) ;
391+ setTrialExpired ( false ) ;
392+ setTrialBlocked ( false ) ;
393+ } else {
394+ // Failed to create trial - be conservative and block access
395+ setTrialExpired ( true ) ;
396+ setTrialBlocked ( true ) ;
397+ setTimeRemaining ( 0 ) ;
398+ setTrialStartTime ( null ) ;
399+ }
349400 }
350401 }
351402 } catch ( error ) {
352- // Don't block access on error - allow trial to proceed
353- setTrialStartTime ( Date . now ( ) ) ;
354- setTimeRemaining ( TRIAL_DURATION_MS ) ;
355- setTrialExpired ( false ) ;
356- setTrialBlocked ( false ) ;
403+ setTrialExpired ( true ) ;
404+ setTrialBlocked ( true ) ;
405+ setTimeRemaining ( 0 ) ;
406+ setTrialStartTime ( null ) ;
407+
408+ // Check localStorage for any existing trial state to prevent bypass
409+ const localTrialId = localStorage . getItem ( "currentTrialId" ) ;
410+ const localSessionId = localStorage . getItem ( "trial_session_id" ) ;
411+
412+ // If we have local trial data, assume trial was already used (security-first approach)
413+ if ( localTrialId || localSessionId ) {
414+ setTrialExpired ( true ) ;
415+ setTrialBlocked ( true ) ;
416+ setTimeRemaining ( 0 ) ;
417+ setTrialStartTime ( null ) ;
418+ } else {
419+ // Only allow new trial if no local evidence of previous trial exists
420+ // This is a fallback for genuine first-time users with network issues
421+ setTrialStartTime ( Date . now ( ) ) ;
422+ setTimeRemaining ( TRIAL_DURATION_MS ) ;
423+ setTrialExpired ( false ) ;
424+ setTrialBlocked ( false ) ;
425+ }
357426 } finally {
358427 setIsLoading ( false ) ;
359428 }
@@ -374,13 +443,14 @@ export const useSecureTrial = () => {
374443 setTrialExpired ( true ) ;
375444 setTrialBlocked ( true ) ;
376445 setTimeRemaining ( 0 ) ;
377- // Mark trial as expired in database
446+ // Mark trial as expired in database and locally
378447 if ( typeof window !== "undefined" ) {
379448 const trialId = localStorage . getItem ( "currentTrialId" ) ;
380449 if ( trialId ) {
381450 expireTrial ( trialId ) ;
382451 localStorage . removeItem ( "currentTrialId" ) ;
383452 }
453+ markTrialAsUsed ( ) ; // Ensure trial usage is marked locally
384454 }
385455 } else {
386456 setTimeRemaining ( remaining ) ;
0 commit comments