added crypto pm and activation key system

This commit is contained in:
Gitea
2025-05-16 11:24:08 +05:30
parent 23b5a45d0b
commit 93515e7845
11 changed files with 454 additions and 13 deletions

View File

@@ -0,0 +1,154 @@
<?php
namespace App\Filament\Pages;
use App\Models\ActivationKey;
use App\Models\Plan;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Filament\Tables\Actions\BulkAction;
use Filament\Tables\Columns\BooleanColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Filters\SelectFilter;
use Illuminate\Support\Collection;
use Response;
use Str;
use Filament\Notifications\Notification;
class GenerateActivationKeys extends Page implements HasForms, HasTable
{
use InteractsWithForms, InteractsWithTable;
protected static ?string $navigationIcon = 'heroicon-o-key';
protected static string $view = 'filament.pages.generate-activation-keys';
protected static ?string $navigationGroup = 'Admin';
protected static ?string $title = 'Activation Keys';
public $plan_id;
public $quantity = 1;
public function mount(): void
{
$this->form->fill();
}
protected function getFormSchema(): array
{
return [
Select::make('plan_id')
->label('Select Plan')
->options(Plan::all()->pluck('name', 'id'))
->required(),
TextInput::make('quantity')
->numeric()
->minValue(1)
->maxValue(100)
->default(1)
->required(),
];
}
public function generate()
{
$data = $this->form->getState();
$plan = Plan::findOrFail($data['plan_id']);
for ($i = 0; $i < $data['quantity']; $i++) {
ActivationKey::create([
'price_id' => $plan->pricing_id,
'activation_key' => strtoupper('Z'.Str::random(16)),
'is_activated' => false,
]);
}
Notification::make()
->title("{$data['quantity']} activation key(s) generated.")
->success()
->send();
$this->form->fill(); // Reset form
}
// === Table Setup ===
protected function getTableQuery(): \Illuminate\Database\Eloquent\Builder
{
return ActivationKey::query()->latest();
}
protected function getTableColumns(): array
{
return [
TextColumn::make('activation_key')
->label('Key')
->copyable(),
BooleanColumn::make('is_activated'),
TextColumn::make('user.email')
->label('Activated By'),
TextColumn::make('billing_interval')
->label('Interval')
->getStateUsing(function ($record) {
$isMonthly = \App\Models\Plan::where('pricing_id', $record->price_id)->value('monthly_billing');
return $isMonthly ? 'Monthly' : 'Yearly';
}),
TextColumn::make('created_at')
->dateTime(),
];
}
protected function getTableFilters(): array
{
return [
SelectFilter::make('is_activated')
->options([
true => 'Activated',
false => 'Not Activated',
]),
SelectFilter::make('price_id')
->label('Plan')
->options(
Plan::pluck('name', 'pricing_id')
),
];
}
protected function getTableBulkActions(): array
{
return [
BulkAction::make('Download Keys')
->action(fn (Collection $records) => $this->downloadKeys($records))
->deselectRecordsAfterCompletion()
->requiresConfirmation(),
];
}
public function downloadKeys(Collection $records)
{
$text = $records->pluck('activation_key')->implode("\n");
$filename = 'activation_keys_' . now()->timestamp . '.txt';
// Store the file in the 'public' directory or a subdirectory within 'public'
$path = public_path("activation/{$filename}");
// Make sure the 'activation' folder exists, create it if it doesn't
if (!file_exists(public_path('activation'))) {
mkdir(public_path('activation'), 0777, true);
}
// Write the contents to the file
file_put_contents($path, $text);
// Return the response that allows users to download the file directly
return response()->download($path)->deleteFileAfterSend(true);
}
}

View File

@@ -45,12 +45,21 @@ class PlanResource extends Resource
TextInput::make('description'),
TextInput::make('product_id')->required(),
TextInput::make('pricing_id')->required(),
TextInput::make('shoppy_product_id')->nullable(),
TextInput::make('price')->numeric()->required(),
TextInput::make('mailbox_limit')->numeric()->required(),
Select::make('monthly_billing')->options([
1 => 'Monthly',
0 => 'Yearly',
])->default(1)->required(),
])->required(),
Select::make('accept_stripe')->options([
1 => 'Activate',
0 => 'Disable',
])->required(),
Select::make('accept_shoppy')->options([
1 => 'Activate',
0 => 'Disable',
])->required(),
KeyValue::make('details')
->label('Plan Details (Optional)')
->keyPlaceholder('Name')

View File

@@ -35,7 +35,10 @@ class Dashboard extends Component
$user = auth()->user();
$userId = $user->id;
if ($user->subscribed()) {
$subscription = $user->subscriptions()->where(['stripe_status' => 'active'])->orderByDesc('updated_at')->first();
$subscription = $user->subscriptions()
//->where(['stripe_status' => 'active'])
->orderByDesc('updated_at')
->first();
if ($subscription !== null) {
$subscriptionId = $subscription->stripe_id;
$cacheKey = "stripe_check_executed_user_{$userId}_{$subscriptionId}";
@@ -74,7 +77,7 @@ class Dashboard extends Component
]);
}
}
Cache::put($cacheKey, true, now()->addHour());
Cache::put($cacheKey, true, now()->addMinute());
} catch (Exception $exception) {
\Log::error($exception->getMessage());
}
@@ -176,7 +179,7 @@ class Dashboard extends Component
$userPriceID = $result['items'][0]['stripe_price'];
$subscriptionEnd = $result['ends_at'];
$planName = null; // Default value if not found
$planName = null;
foreach (config('app.plans') as $plan) {
if ($plan['pricing_id'] === $userPriceID) {

View File

@@ -2,11 +2,14 @@
namespace App\Livewire\Dashboard;
use App\Models\ActivationKey;
use App\Models\Plan;
use Livewire\Component;
class Pricing extends Component
{
public $plans;
public $activation_key;
public function mount(): void
{
@@ -18,6 +21,70 @@ class Pricing extends Component
$this->redirect(route('checkout', $pricing_id));
}
public function activateKey(): void
{
$this->validate([
'activation_key' => 'required|alpha_num|max:30',
], [
'activation_key.required' => 'You must enter an activation key.',
'activation_key.alpha_num' => 'The activation key may only contain letters and numbers (no special characters).',
'activation_key.max' => 'The activation key must not exceed 30 characters.',
]);
$trimmedKey = trim($this->activation_key);
$activation = ActivationKey::where('activation_key', $trimmedKey)
->where('is_activated', false)
->first();
if ($activation) {
if ($activation->price_id !== null) {
$result = $this->addSubscription($activation->price_id);
}
if ($result === true) {
$activation->is_activated = true;
$activation->user_id = auth()->id();
$activation->save();
session()->flash('success', 'Activation key is valid and has been activated. Refresh page to see changes.');
$this->reset('activation_key');
} else {
session()->flash('error', 'Something went wrong. Kindly drop a mail at contact@zemail.me to activate your subscription manually.');
}
} else {
session()->flash('error', 'Invalid or already activated key.');
}
}
private function addSubscription($price_id): bool
{
try {
$plan = Plan::where('pricing_id', $price_id)->firstOrFail();
$user = auth()->user();
$user->createOrGetStripeCustomer();
$user->updateStripeCustomer([
'address' => [
'postal_code' => '10001',
'country' => 'US',
],
'name' => $user->name,
'email' => $user->email,
]);
$user->creditBalance($plan->price * 100, 'Premium Top-up for plan: ' . $plan->name);
$balance = $user->balance();
$user->newSubscription('default', $plan->pricing_id)->create();
if ($plan->monthly_billing == 1) {
$ends_at = now()->addMonth();
} else {
$ends_at = now()->addYear();
}
$user->subscription('default')->cancelAt($ends_at);
return true;
} catch (\Exception $e) {
\Log::error($e->getMessage());
return false;
}
}
public function render()
{
return view('livewire.dashboard.pricing');

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ActivationKey extends Model
{
use HasFactory;
protected $fillable = [
'user_id',
'activation_key',
'price_id',
'is_activated',
];
protected $casts = [
'is_activated' => 'boolean',
];
/**
* Relationship: the user who redeemed the activation key (optional).
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* Scope to filter unactivated keys
*/
public function scopeUnactivated($query)
{
return $query->where('is_activated', false);
}
/**
* Scope to filter activated keys
*/
public function scopeActivated($query)
{
return $query->where('is_activated', true);
}
}

View File

@@ -7,16 +7,20 @@ use Illuminate\Database\Eloquent\Model;
class Plan extends Model
{
protected $fillable = [
'name',
'description',
'product_id',
'pricing_id',
'price',
'mailbox_limit',
'monthly_billing',
'details'
'name',
'description',
'product_id',
'pricing_id',
'shoppy_product_id',
'accept_stripe',
'accept_shoppy',
'price',
'mailbox_limit',
'monthly_billing',
'details',
];
protected $casts = [
'details' => 'json',
'monthly_billing' => 'boolean',