where('purchase_id',$purchaseId)->exists()){return;} $key='TNX-'.Str::upper(bin2hex(random_bytes(20))); DB::table('licenses')->insert(['user_id'=>$userId,'purchase_id'=>$purchaseId,'key_hash'=>hash('sha256',$key),'key_encrypted'=>Crypt::encryptString($key),'status'=>'active','device_limit'=>2,'created_at'=>now(),'updated_at'=>now()]); } public function activate(string $key,string $device): array { return DB::transaction(function()use($key,$device){ $license=DB::table('licenses')->where('key_hash',hash('sha256',$key))->lockForUpdate()->first(); abort_unless($license && $license->status==='active',403,'Invalid or revoked license.'); $deviceHash=hash('sha256',$device);$existing=DB::table('activations')->where('license_id',$license->id)->where('device_hash',$deviceHash)->first(); abort_if(!$existing && DB::table('activations')->where('license_id',$license->id)->count()>=$license->device_limit,409,'Device limit reached.'); $token=bin2hex(random_bytes(32)); DB::table('activations')->updateOrInsert(['license_id'=>$license->id,'device_hash'=>$deviceHash],['token_hash'=>hash('sha256',$token),'last_seen_at'=>now(),'created_at'=>now(),'updated_at'=>now()]); return ['activation_token'=>$token,'valid'=>true,'check_again_in'=>86400]; },3); } public function check(string $token,bool $deactivate=false): array { return DB::transaction(function()use($token,$deactivate){ $activation=DB::table('activations')->where('token_hash',hash('sha256',$token))->first(); abort_unless($activation,403,'Invalid activation.'); $license=DB::table('licenses')->where('id',$activation->license_id)->lockForUpdate()->first(); if($deactivate){DB::table('activations')->where('id',$activation->id)->delete();return ['deactivated'=>true];} abort_unless($license->status==='active',403,'License revoked.'); DB::table('activations')->where('id',$activation->id)->update(['last_seen_at'=>now()]); return ['valid'=>true,'check_again_in'=>86400]; },3); } }