Compare commits

..

No commits in common. "dev" and "v0.0.2" have entirely different histories.
dev ... v0.0.2

95 changed files with 207 additions and 458 deletions

View file

@ -8,32 +8,11 @@ use Core\Events\AdminPanelBooting;
use Core\Events\ApiRoutesRegistering;
use Core\Events\ConsoleBooting;
use Core\Events\WebRoutesRegistering;
use Core\Mod\Commerce\Events\OrderPaid;
use Core\Mod\Commerce\Events\SubscriptionCreated;
use Core\Mod\Commerce\Events\SubscriptionRenewed;
use Core\Mod\Commerce\Listeners\ProvisionSocialHostSubscription;
use Core\Mod\Commerce\Listeners\RewardAgentReferralOnSubscription;
use Core\Mod\Commerce\Services\CheckoutRateLimiter;
use Core\Mod\Commerce\Services\CommerceService;
use Core\Mod\Commerce\Services\ContentOverrideService;
use Core\Mod\Commerce\Services\CouponService;
use Core\Mod\Commerce\Services\CreditNoteService;
use Core\Mod\Commerce\Services\CurrencyService;
use Core\Mod\Commerce\Services\DunningService;
use Core\Mod\Commerce\Services\FraudService;
use Core\Mod\Commerce\Services\InvoiceService;
use Core\Mod\Commerce\Services\PaymentGateway\BTCPayGateway;
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
use Core\Mod\Commerce\Services\PaymentGateway\StripeGateway;
use Core\Mod\Commerce\Services\PaymentMethodService;
use Core\Mod\Commerce\Services\PermissionMatrixService;
use Core\Mod\Commerce\Services\ReferralService;
use Core\Mod\Commerce\Services\SkuBuilderService;
use Core\Mod\Commerce\Services\SkuParserService;
use Core\Mod\Commerce\Services\SubscriptionService;
use Core\Mod\Commerce\Services\TaxService;
use Core\Mod\Commerce\Services\UsageBillingService;
use Core\Mod\Commerce\Services\WebhookRateLimiter;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
@ -67,9 +46,9 @@ class Boot extends ServiceProvider
// Laravel event listeners (not lifecycle events)
Event::subscribe(ProvisionSocialHostSubscription::class);
Event::listen(SubscriptionCreated::class, RewardAgentReferralOnSubscription::class);
Event::listen(SubscriptionRenewed::class, Listeners\ResetUsageOnRenewal::class);
Event::listen(OrderPaid::class, Listeners\CreateReferralCommission::class);
Event::listen(\Core\Mod\Commerce\Events\SubscriptionCreated::class, RewardAgentReferralOnSubscription::class);
Event::listen(\Core\Mod\Commerce\Events\SubscriptionRenewed::class, Listeners\ResetUsageOnRenewal::class);
Event::listen(\Core\Mod\Commerce\Events\OrderPaid::class, Listeners\CreateReferralCommission::class);
}
public function register(): void
@ -80,24 +59,24 @@ class Boot extends ServiceProvider
);
// Core Services
$this->app->singleton(CommerceService::class);
$this->app->singleton(SubscriptionService::class);
$this->app->singleton(InvoiceService::class);
$this->app->singleton(PermissionMatrixService::class);
$this->app->singleton(CouponService::class);
$this->app->singleton(TaxService::class);
$this->app->singleton(CurrencyService::class);
$this->app->singleton(ContentOverrideService::class);
$this->app->singleton(DunningService::class);
$this->app->singleton(SkuParserService::class);
$this->app->singleton(SkuBuilderService::class);
$this->app->singleton(CreditNoteService::class);
$this->app->singleton(PaymentMethodService::class);
$this->app->singleton(UsageBillingService::class);
$this->app->singleton(ReferralService::class);
$this->app->singleton(FraudService::class);
$this->app->singleton(CheckoutRateLimiter::class);
$this->app->singleton(WebhookRateLimiter::class);
$this->app->singleton(\Core\Mod\Commerce\Services\CommerceService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\SubscriptionService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\InvoiceService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\PermissionMatrixService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\CouponService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\TaxService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\CurrencyService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\ContentOverrideService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\DunningService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\SkuParserService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\SkuBuilderService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\CreditNoteService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\PaymentMethodService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\UsageBillingService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\ReferralService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\FraudService::class);
$this->app->singleton(\Core\Mod\Commerce\Services\CheckoutRateLimiter::class);
$this->app->singleton(\Core\Mod\Commerce\Services\WebhookRateLimiter::class);
// Payment Gateways
$this->app->singleton('commerce.gateway.btcpay', function ($app) {

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Console;
use Core\Mod\Commerce\Models\Order;

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Console;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Subscription;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
@ -165,7 +164,7 @@ class PlantSubscriberTrees extends Command
protected function hasPlantedThisMonth(int $workspaceId, string $month): bool
{
// Parse the month string (YYYY-MM format)
$date = Carbon::createFromFormat('Y-m', $month);
$date = \Carbon\Carbon::createFromFormat('Y-m', $month);
$startOfMonth = $date->copy()->startOfMonth();
$endOfMonth = $date->copy()->endOfMonth();

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Console;
use Core\Mod\Commerce\Models\Subscription;
use Core\Mod\Commerce\Services\DunningService;
use Core\Mod\Commerce\Services\SubscriptionService;
use Illuminate\Console\Command;
@ -272,7 +269,7 @@ class ProcessDunning extends Command
$this->info('Stage 5: Expired Subscriptions');
if ($dryRun) {
$count = Subscription::query()
$count = \Core\Mod\Commerce\Models\Subscription::query()
->active()
->whereNotNull('cancelled_at')
->where('current_period_end', '<=', now())

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Console;
use Core\Mod\Commerce\Models\ExchangeRate;
use Core\Mod\Commerce\Services\CurrencyService;
use Illuminate\Console\Command;
@ -31,7 +30,7 @@ class RefreshExchangeRates extends Command
$this->line("Provider: {$provider}");
// Check if rates need refresh
if (! $this->option('force') && ! ExchangeRate::needsRefresh()) {
if (! $this->option('force') && ! \Core\Mod\Commerce\Models\ExchangeRate::needsRefresh()) {
$this->info('Rates are still fresh. Use --force to refresh anyway.');
return self::SUCCESS;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Console;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Console;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -12,9 +12,7 @@ use Core\Mod\Commerce\Services\CommerceService;
use Core\Mod\Commerce\Services\InvoiceService;
use Core\Mod\Commerce\Services\SubscriptionService;
use Core\Tenant\Models\Package;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Services\EntitlementService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -40,7 +38,7 @@ class CommerceController extends Controller
{
$user = Auth::user();
if (! $user instanceof User) {
if (! $user instanceof \Core\Tenant\Models\User) {
return null;
}
@ -202,7 +200,7 @@ class CommerceController extends Controller
return response()->json(['error' => 'No workspace found'], 404);
}
$entitlements = app(EntitlementService::class);
$entitlements = app(\Core\Tenant\Services\EntitlementService::class);
$summary = $entitlements->getUsageSummary($workspace);
return response()->json([

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Data;
use Core\Mod\Commerce\Models\Coupon;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Events;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Events;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Events;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Events;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Listeners;
use Core\Mod\Commerce\Events\SubscriptionCancelled;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Listeners;
use Core\Mod\Commerce\Events\SubscriptionRenewed;

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Mcp\Tools;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Coupon;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Mcp\Request;
@ -62,7 +59,7 @@ class CreateCoupon extends Tool
'duration' => $duration,
'max_uses' => $maxUses,
'max_uses_per_workspace' => 1,
'valid_until' => $validUntil ? Carbon::parse($validUntil) : null,
'valid_until' => $validUntil ? \Carbon\Carbon::parse($validUntil) : null,
'is_active' => true,
'applies_to' => 'all',
]);

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Mcp\Tools;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Mcp\Tools;
use Core\Mod\Commerce\Models\Invoice;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Mcp\Tools;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,12 +1,8 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Mod\Commerce\Contracts\Orderable;
use Core\Mod\Commerce\Database\Factories\CouponFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
@ -31,8 +27,8 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property int $used_count
* @property string $duration
* @property int|null $duration_months
* @property Carbon|null $valid_from
* @property Carbon|null $valid_until
* @property \Carbon\Carbon|null $valid_from
* @property \Carbon\Carbon|null $valid_until
* @property bool $is_active
*/
class Coupon extends Model
@ -40,9 +36,9 @@ class Coupon extends Model
use HasFactory;
use LogsActivity;
protected static function newFactory(): CouponFactory
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\CouponFactory
{
return CouponFactory::new();
return \Core\Mod\Commerce\Database\Factories\CouponFactory::new();
}
protected $fillable = [

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -17,7 +14,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int $workspace_id
* @property int $order_id
* @property float $discount_amount
* @property Carbon $created_at
* @property \Carbon\Carbon $created_at
*/
class CouponUsage extends Model
{

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -34,9 +31,9 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property string $status
* @property float $amount_used
* @property int|null $applied_to_order_id
* @property Carbon|null $issued_at
* @property Carbon|null $applied_at
* @property Carbon|null $voided_at
* @property \Carbon\Carbon|null $issued_at
* @property \Carbon\Carbon|null $applied_at
* @property \Carbon\Carbon|null $voided_at
* @property int|null $issued_by
* @property int|null $voided_by
* @property array|null $metadata

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
@ -16,7 +15,7 @@ use Illuminate\Support\Facades\Cache;
* @property string $target_currency
* @property float $rate
* @property string $source
* @property Carbon $fetched_at
* @property \Carbon\Carbon $fetched_at
*/
class ExchangeRate extends Model
{

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
@ -21,8 +20,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property int|null $low_stock_threshold
* @property string|null $bin_location
* @property string|null $zone
* @property Carbon|null $last_counted_at
* @property Carbon|null $last_restocked_at
* @property \Carbon\Carbon|null $last_counted_at
* @property \Carbon\Carbon|null $last_restocked_at
* @property int|null $unit_cost
* @property array|null $metadata
*/

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -25,7 +24,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property string|null $notes
* @property int|null $user_id
* @property int|null $unit_cost
* @property Carbon $created_at
* @property \Carbon\Carbon $created_at
*/
class InventoryMovement extends Model
{

View file

@ -1,11 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Mod\Commerce\Database\Factories\InvoiceFactory;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -27,9 +23,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
* @property float $total
* @property float $amount_paid
* @property float $amount_due
* @property Carbon $issue_date
* @property Carbon $due_date
* @property Carbon|null $paid_at
* @property \Carbon\Carbon $issue_date
* @property \Carbon\Carbon $due_date
* @property \Carbon\Carbon|null $paid_at
* @property string|null $billing_name
* @property array|null $billing_address
* @property string|null $tax_id
@ -39,9 +35,9 @@ class Invoice extends Model
{
use HasFactory;
protected static function newFactory(): InvoiceFactory
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\InvoiceFactory
{
return InvoiceFactory::new();
return \Core\Mod\Commerce\Database\Factories\InvoiceFactory::new();
}
protected $fillable = [
@ -97,7 +93,7 @@ class Invoice extends Model
/**
* Get the issued_at attribute (alias for issue_date).
*/
public function getIssuedAtAttribute(): ?Carbon
public function getIssuedAtAttribute(): ?\Carbon\Carbon
{
return $this->issue_date;
}

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Illuminate\Database\Eloquent\Model;

View file

@ -1,13 +1,8 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Mod\Commerce\Contracts\Orderable;
use Core\Mod\Commerce\Database\Factories\OrderFactory;
use Core\Mod\Commerce\Services\CurrencyService;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -43,7 +38,7 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property int|null $coupon_id
* @property array|null $billing_address
* @property array|null $metadata
* @property Carbon|null $paid_at
* @property \Carbon\Carbon|null $paid_at
* @property-read Orderable|null $orderable
*/
class Order extends Model
@ -51,9 +46,9 @@ class Order extends Model
use HasFactory;
use LogsActivity;
protected static function newFactory(): OrderFactory
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\OrderFactory
{
return OrderFactory::new();
return \Core\Mod\Commerce\Database\Factories\OrderFactory::new();
}
protected $fillable = [
@ -296,7 +291,7 @@ class Order extends Model
*/
public function getFormattedTotalAttribute(): string
{
$currencyService = app(CurrencyService::class);
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
return $currencyService->format($this->total, $this->display_currency);
}
@ -306,7 +301,7 @@ class Order extends Model
*/
public function getFormattedSubtotalAttribute(): string
{
$currencyService = app(CurrencyService::class);
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
return $currencyService->format($this->subtotal, $this->display_currency);
}
@ -316,7 +311,7 @@ class Order extends Model
*/
public function getFormattedTaxAmountAttribute(): string
{
$currencyService = app(CurrencyService::class);
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
return $currencyService->format($this->tax_amount, $this->display_currency);
}
@ -326,7 +321,7 @@ class Order extends Model
*/
public function getFormattedDiscountAmountAttribute(): string
{
$currencyService = app(CurrencyService::class);
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
return $currencyService->format($this->discount_amount, $this->display_currency);
}
@ -346,7 +341,7 @@ class Order extends Model
return $amount;
}
return ExchangeRate::convert(
return \Core\Mod\Commerce\Models\ExchangeRate::convert(
$amount,
$this->display_currency,
$baseCurrency
@ -368,7 +363,7 @@ class Order extends Model
return $amount;
}
return ExchangeRate::convert(
return \Core\Mod\Commerce\Models\ExchangeRate::convert(
$amount,
$baseCurrency,
$this->display_currency

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Core\Tenant\Models\Package;

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Core\Mod\Commerce\Database\Factories\PaymentFactory;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -36,9 +33,9 @@ class Payment extends Model
{
use HasFactory;
protected static function newFactory(): PaymentFactory
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\PaymentFactory
{
return PaymentFactory::new();
return \Core\Mod\Commerce\Database\Factories\PaymentFactory::new();
}
protected $fillable = [

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -91,7 +88,7 @@ class PaymentMethod extends Model
return false;
}
$expiry = Carbon::createFromDate($this->exp_year, $this->exp_month)->endOfMonth();
$expiry = \Carbon\Carbon::createFromDate($this->exp_year, $this->exp_month)->endOfMonth();
return $expiry->isPast();
}

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -24,7 +23,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property bool $locked
* @property string $source
* @property int|null $set_by_entity_id
* @property Carbon|null $trained_at
* @property \Carbon\Carbon|null $trained_at
* @property string|null $trained_route
*/
class PermissionMatrix extends Model

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -28,7 +27,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int|null $user_id
* @property string $status
* @property bool $was_trained
* @property Carbon|null $trained_at
* @property \Carbon\Carbon|null $trained_at
*/
class PermissionRequest extends Model
{

View file

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Core\Mod\Commerce\Concerns\HasContentOverrides;
use Core\Mod\Commerce\Services\CurrencyService;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -288,7 +287,7 @@ class Product extends Model
public function formatPrice(int $amount, ?string $currency = null): string
{
$currency = $currency ?? $this->currency;
$currencyService = app(CurrencyService::class);
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
return $currencyService->format($amount, $currency, isCents: true);
}

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -31,13 +30,13 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property string|null $ip_address
* @property string|null $user_agent
* @property string|null $tracking_id
* @property Carbon|null $clicked_at
* @property Carbon|null $signed_up_at
* @property Carbon|null $first_purchase_at
* @property Carbon|null $qualified_at
* @property Carbon|null $disqualified_at
* @property \Carbon\Carbon|null $clicked_at
* @property \Carbon\Carbon|null $signed_up_at
* @property \Carbon\Carbon|null $first_purchase_at
* @property \Carbon\Carbon|null $qualified_at
* @property \Carbon\Carbon|null $disqualified_at
* @property string|null $disqualification_reason
* @property Carbon|null $matured_at
* @property \Carbon\Carbon|null $matured_at
*/
class Referral extends Model
{

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -25,8 +24,8 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property int $cookie_days
* @property int|null $max_uses
* @property int $uses_count
* @property Carbon|null $valid_from
* @property Carbon|null $valid_until
* @property \Carbon\Carbon|null $valid_from
* @property \Carbon\Carbon|null $valid_until
* @property bool $is_active
* @property string|null $campaign_name
* @property array|null $metadata

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -27,10 +26,10 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property float $commission_amount
* @property string $currency
* @property string $status
* @property Carbon|null $matures_at
* @property Carbon|null $matured_at
* @property \Carbon\Carbon|null $matures_at
* @property \Carbon\Carbon|null $matured_at
* @property int|null $payout_id
* @property Carbon|null $paid_at
* @property \Carbon\Carbon|null $paid_at
* @property string|null $notes
*/
class ReferralCommission extends Model

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -28,10 +27,10 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property float|null $btc_amount
* @property float|null $btc_rate
* @property string $status
* @property Carbon|null $requested_at
* @property Carbon|null $processed_at
* @property Carbon|null $completed_at
* @property Carbon|null $failed_at
* @property \Carbon\Carbon|null $requested_at
* @property \Carbon\Carbon|null $processed_at
* @property \Carbon\Carbon|null $completed_at
* @property \Carbon\Carbon|null $failed_at
* @property string|null $notes
* @property string|null $failure_reason
* @property int|null $processed_by

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Core\Tenant\Models\User;

View file

@ -1,11 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Mod\Commerce\Database\Factories\SubscriptionFactory;
use Core\Mod\Commerce\Events\SubscriptionCreated;
use Core\Mod\Commerce\Events\SubscriptionUpdated;
use Core\Tenant\Models\Workspace;
@ -30,12 +26,12 @@ use Spatie\Activitylog\Traits\LogsActivity;
* @property string $gateway_customer_id
* @property string|null $gateway_price_id
* @property string $status
* @property Carbon $current_period_start
* @property Carbon $current_period_end
* @property Carbon|null $trial_ends_at
* @property \Carbon\Carbon $current_period_start
* @property \Carbon\Carbon $current_period_end
* @property \Carbon\Carbon|null $trial_ends_at
* @property bool $cancel_at_period_end
* @property Carbon|null $cancelled_at
* @property Carbon|null $ended_at
* @property \Carbon\Carbon|null $cancelled_at
* @property \Carbon\Carbon|null $ended_at
* @property array|null $metadata
*/
class Subscription extends Model
@ -43,9 +39,9 @@ class Subscription extends Model
use HasFactory;
use LogsActivity;
protected static function newFactory(): SubscriptionFactory
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\SubscriptionFactory
{
return SubscriptionFactory::new();
return \Core\Mod\Commerce\Database\Factories\SubscriptionFactory::new();
}
/**
@ -235,7 +231,7 @@ class Subscription extends Model
$this->update(['status' => 'past_due']);
}
public function renew(Carbon $periodStart, Carbon $periodEnd): void
public function renew(\Carbon\Carbon $periodStart, \Carbon\Carbon $periodEnd): void
{
$this->update([
'status' => 'active',

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
@ -15,10 +13,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int $subscription_id
* @property int $meter_id
* @property int $quantity
* @property Carbon $period_start
* @property Carbon $period_end
* @property \Carbon\Carbon $period_start
* @property \Carbon\Carbon $period_end
* @property string|null $stripe_usage_record_id
* @property Carbon|null $synced_at
* @property \Carbon\Carbon|null $synced_at
* @property bool $billed
* @property int|null $invoice_item_id
* @property array|null $metadata

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@ -20,8 +17,8 @@ use Illuminate\Database\Eloquent\Model;
* @property string $type
* @property float $rate
* @property bool $is_digital_services
* @property Carbon $effective_from
* @property Carbon|null $effective_until
* @property \Carbon\Carbon $effective_from
* @property \Carbon\Carbon|null $effective_until
* @property bool $is_active
* @property string|null $stripe_tax_rate_id
*/

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Model;
@ -19,7 +16,7 @@ use Illuminate\Support\Str;
* @property int $meter_id
* @property int $workspace_id
* @property int $quantity
* @property Carbon $event_at
* @property \Carbon\Carbon $event_at
* @property string|null $idempotency_key
* @property int|null $user_id
* @property string|null $action

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Illuminate\Database\Eloquent\Model;

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\Models;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -22,8 +21,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
* @property int|null $http_status_code
* @property int|null $order_id
* @property int|null $subscription_id
* @property Carbon $received_at
* @property Carbon|null $processed_at
* @property \Carbon\Carbon $received_at
* @property \Carbon\Carbon|null $processed_at
*/
class WebhookEvent extends Model
{

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Order;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Invoice;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Refund;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Notifications;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -6,7 +6,6 @@ namespace Core\Mod\Commerce\Services;
use Core\Mod\Commerce\Contracts\Orderable;
use Core\Mod\Commerce\Data\FraudAssessment;
use Core\Mod\Commerce\Events\OrderPaid;
use Core\Mod\Commerce\Exceptions\CheckoutRateLimitException;
use Core\Mod\Commerce\Exceptions\FraudBlockedException;
use Core\Mod\Commerce\Models\Coupon;
@ -14,12 +13,9 @@ use Core\Mod\Commerce\Models\Invoice;
use Core\Mod\Commerce\Models\Order;
use Core\Mod\Commerce\Models\OrderItem;
use Core\Mod\Commerce\Models\Payment;
use Core\Mod\Commerce\Models\Refund;
use Core\Mod\Commerce\Models\Subscription;
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
use Core\Tenant\Models\Boost;
use Core\Tenant\Models\Package;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Services\EntitlementService;
use Illuminate\Database\Eloquent\Model;
@ -127,7 +123,7 @@ class CommerceService
$order = Order::create([
'orderable_type' => get_class($orderable),
'orderable_id' => $orderable->id,
'user_id' => $orderable instanceof User ? $orderable->id : null,
'user_id' => $orderable instanceof \Core\Tenant\Models\User ? $orderable->id : null,
'order_number' => Order::generateOrderNumber(),
'status' => 'pending',
'billing_cycle' => $billingCycle,
@ -365,7 +361,7 @@ class CommerceService
$order = Order::create([
'orderable_type' => get_class($orderable),
'orderable_id' => $orderable->id,
'user_id' => $orderable instanceof User ? $orderable->id : null,
'user_id' => $orderable instanceof \Core\Tenant\Models\User ? $orderable->id : null,
'order_number' => Order::generateOrderNumber(),
'status' => 'pending',
'billing_cycle' => 'onetime',
@ -441,7 +437,7 @@ class CommerceService
}
// Provision boosts for user-level orders
if ($order->orderable instanceof User) {
if ($order->orderable instanceof \Core\Tenant\Models\User) {
foreach ($order->items as $item) {
if ($item->item_type === 'boost') {
$quantity = $item->metadata['quantity'] ?? $item->quantity ?? 1;
@ -454,28 +450,28 @@ class CommerceService
}
// Dispatch OrderPaid event for referral tracking and other listeners
event(new OrderPaid($order, $payment));
event(new \Core\Mod\Commerce\Events\OrderPaid($order, $payment));
});
}
/**
* Provision a boost for a user.
*/
public function provisionBoostForUser(User $user, string $featureCode, int $quantity = 1, array $metadata = []): Boost
public function provisionBoostForUser(\Core\Tenant\Models\User $user, string $featureCode, int $quantity = 1, array $metadata = []): \Core\Tenant\Models\Boost
{
// Use ADD_LIMIT for quantity-based boosts, ENABLE for boolean boosts
$boostType = $quantity > 1 || $this->isQuantityBasedFeature($featureCode)
? Boost::BOOST_TYPE_ADD_LIMIT
: Boost::BOOST_TYPE_ENABLE;
? \Core\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT
: \Core\Tenant\Models\Boost::BOOST_TYPE_ENABLE;
return Boost::create([
return \Core\Tenant\Models\Boost::create([
'user_id' => $user->id,
'workspace_id' => null,
'feature_code' => $featureCode,
'boost_type' => $boostType,
'duration_type' => Boost::DURATION_PERMANENT,
'limit_value' => $boostType === Boost::BOOST_TYPE_ADD_LIMIT ? $quantity : null,
'status' => Boost::STATUS_ACTIVE,
'duration_type' => \Core\Tenant\Models\Boost::DURATION_PERMANENT,
'limit_value' => $boostType === \Core\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT ? $quantity : null,
'status' => \Core\Tenant\Models\Boost::STATUS_ACTIVE,
'starts_at' => now(),
'metadata' => $metadata,
]);
@ -603,7 +599,7 @@ class CommerceService
Payment $payment,
?float $amount = null,
?string $reason = null
): Refund {
): \Core\Mod\Commerce\Models\Refund {
$amountCents = $amount
? (int) ($amount * 100)
: (int) (($payment->amount - $payment->amount_refunded) * 100);
@ -664,7 +660,7 @@ class CommerceService
// For BTCPay, payment will be 'pending' as it requires customer action
// This is expected - automatic retry won't work for crypto payments
if ($payment->status === 'pending' && $paymentMethod->gateway === 'btcpay') {
Log::info('BTCPay invoice created for retry - requires customer payment', [
\Illuminate\Support\Facades\Log::info('BTCPay invoice created for retry - requires customer payment', [
'invoice_id' => $invoice->id,
'payment_id' => $payment->id,
]);
@ -672,7 +668,7 @@ class CommerceService
return false;
} catch (\Exception $e) {
Log::error('Invoice payment retry failed', [
\Illuminate\Support\Facades\Log::error('Invoice payment retry failed', [
'invoice_id' => $invoice->id,
'error' => $e->getMessage(),
]);

View file

@ -11,7 +11,6 @@ use Core\Mod\Commerce\Models\CouponUsage;
use Core\Mod\Commerce\Models\Order;
use Core\Tenant\Models\Package;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
/**
@ -218,7 +217,7 @@ class CouponService
/**
* Get usage history for a coupon.
*/
public function getUsageHistory(Coupon $coupon, int $limit = 50): Collection
public function getUsageHistory(Coupon $coupon, int $limit = 50): \Illuminate\Database\Eloquent\Collection
{
return $coupon->usages()
->with(['workspace', 'order'])

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Core\Mod\Commerce\Models\CreditNote;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Carbon\Carbon;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Barryvdh\DomPDF\Facade\Pdf;
@ -11,11 +9,8 @@ use Core\Mod\Commerce\Models\InvoiceItem;
use Core\Mod\Commerce\Models\Order;
use Core\Mod\Commerce\Models\Payment;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Symfony\Component\HttpFoundation\StreamedResponse;
/**
* Invoice generation and management service.
@ -179,7 +174,7 @@ class InvoiceService
/**
* Get PDF download response.
*/
public function downloadPdf(Invoice $invoice): StreamedResponse
public function downloadPdf(Invoice $invoice): \Symfony\Component\HttpFoundation\StreamedResponse
{
$path = $this->getPdf($invoice);
@ -224,7 +219,7 @@ class InvoiceService
/**
* Get invoices for a workspace.
*/
public function getForWorkspace(Workspace $workspace, int $limit = 25): LengthAwarePaginator
public function getForWorkspace(Workspace $workspace, int $limit = 25): \Illuminate\Pagination\LengthAwarePaginator
{
return $workspace->invoices()
->with('items')
@ -235,7 +230,7 @@ class InvoiceService
/**
* Get unpaid invoices for a workspace.
*/
public function getUnpaidForWorkspace(Workspace $workspace): Collection
public function getUnpaidForWorkspace(Workspace $workspace): \Illuminate\Database\Eloquent\Collection
{
return $workspace->invoices()
->pending()
@ -246,7 +241,7 @@ class InvoiceService
/**
* Get overdue invoices for a workspace.
*/
public function getOverdueForWorkspace(Workspace $workspace): Collection
public function getOverdueForWorkspace(Workspace $workspace): \Illuminate\Database\Eloquent\Collection
{
return $workspace->invoices()
->pending()

View file

@ -10,7 +10,6 @@ use Core\Mod\Commerce\Models\Payment;
use Core\Mod\Commerce\Models\PaymentMethod;
use Core\Mod\Commerce\Models\Subscription;
use Core\Tenant\Models\Workspace;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
@ -634,7 +633,7 @@ class BTCPayGateway implements PaymentGatewayContract
/**
* Extract a safe error message from a failed response.
*/
protected function sanitiseErrorMessage(Response $response): string
protected function sanitiseErrorMessage(\Illuminate\Http\Client\Response $response): string
{
$json = $response->json();

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services\PaymentGateway;
use Core\Mod\Commerce\Models\Order;

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services\PaymentGateway;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Order;
use Core\Mod\Commerce\Models\Payment;
use Core\Mod\Commerce\Models\PaymentMethod;
@ -12,14 +9,7 @@ use Core\Mod\Commerce\Models\Refund;
use Core\Mod\Commerce\Models\Subscription;
use Core\Tenant\Models\Workspace;
use Illuminate\Support\Facades\Log;
use Stripe\Exception\ApiConnectionException;
use Stripe\Exception\ApiErrorException;
use Stripe\Exception\AuthenticationException;
use Stripe\Exception\CardException;
use Stripe\Exception\InvalidRequestException;
use Stripe\Exception\RateLimitException;
use Stripe\StripeClient;
use Stripe\Webhook;
/**
* Stripe payment gateway implementation.
@ -153,36 +143,36 @@ class StripeGateway implements PaymentGatewayContract
'session_id' => $session->id,
'checkout_url' => $session->url,
];
} catch (CardException $e) {
} catch (\Stripe\Exception\CardException $e) {
Log::warning('Stripe checkout failed: card error', [
'order_id' => $order->id,
'error' => $e->getMessage(),
'code' => $e->getStripeCode(),
]);
throw new \RuntimeException('Payment card error: '.$e->getMessage(), 0, $e);
} catch (RateLimitException $e) {
} catch (\Stripe\Exception\RateLimitException $e) {
Log::error('Stripe checkout failed: rate limit', [
'order_id' => $order->id,
]);
throw new \RuntimeException('Payment service temporarily unavailable. Please try again.', 0, $e);
} catch (InvalidRequestException $e) {
} catch (\Stripe\Exception\InvalidRequestException $e) {
Log::error('Stripe checkout failed: invalid request', [
'order_id' => $order->id,
'error' => $e->getMessage(),
'param' => $e->getStripeParam(),
]);
throw new \RuntimeException('Unable to create checkout session. Please contact support.', 0, $e);
} catch (AuthenticationException $e) {
} catch (\Stripe\Exception\AuthenticationException $e) {
Log::critical('Stripe authentication failed - check API keys', [
'order_id' => $order->id,
]);
throw new \RuntimeException('Payment service configuration error. Please contact support.', 0, $e);
} catch (ApiConnectionException $e) {
} catch (\Stripe\Exception\ApiConnectionException $e) {
Log::error('Stripe checkout failed: connection error', [
'order_id' => $order->id,
]);
throw new \RuntimeException('Unable to connect to payment service. Please try again.', 0, $e);
} catch (ApiErrorException $e) {
} catch (\Stripe\Exception\ApiErrorException $e) {
Log::error('Stripe checkout failed: API error', [
'order_id' => $order->id,
'error' => $e->getMessage(),
@ -348,10 +338,10 @@ class StripeGateway implements PaymentGatewayContract
'gateway_customer_id' => $customerId,
'gateway_price_id' => $priceId,
'status' => $this->mapSubscriptionStatus($stripeSubscription->status),
'current_period_start' => Carbon::createFromTimestamp($stripeSubscription->current_period_start),
'current_period_end' => Carbon::createFromTimestamp($stripeSubscription->current_period_end),
'current_period_start' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_start),
'current_period_end' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_end),
'trial_ends_at' => $stripeSubscription->trial_end
? Carbon::createFromTimestamp($stripeSubscription->trial_end)
? \Carbon\Carbon::createFromTimestamp($stripeSubscription->trial_end)
: null,
'metadata' => ['stripe_subscription' => $stripeSubscription->toArray()],
]);
@ -386,8 +376,8 @@ class StripeGateway implements PaymentGatewayContract
'gateway_price_id' => $options['price_id'] ?? $subscription->gateway_price_id,
'status' => $this->mapSubscriptionStatus($stripeSubscription->status),
'cancel_at_period_end' => $stripeSubscription->cancel_at_period_end,
'current_period_start' => Carbon::createFromTimestamp($stripeSubscription->current_period_start),
'current_period_end' => Carbon::createFromTimestamp($stripeSubscription->current_period_end),
'current_period_start' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_start),
'current_period_end' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_end),
]);
return $subscription->fresh();
@ -554,7 +544,7 @@ class StripeGateway implements PaymentGatewayContract
public function verifyWebhookSignature(string $payload, string $signature): bool
{
try {
Webhook::constructEvent($payload, $signature, $this->webhookSecret);
\Stripe\Webhook::constructEvent($payload, $signature, $this->webhookSecret);
return true;
} catch (\Exception $e) {

View file

@ -1,10 +1,7 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\PaymentMethod;
use Core\Mod\Commerce\Services\PaymentGateway\StripeGateway;
use Core\Tenant\Models\User;
@ -256,7 +253,7 @@ class PaymentMethodService
return false;
}
$expiry = Carbon::createFromDate(
$expiry = \Carbon\Carbon::createFromDate(
$paymentMethod->exp_year,
$paymentMethod->exp_month
)->endOfMonth();

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
/**

View file

@ -1,14 +1,11 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Core\Mod\Commerce\Models\Payment;
use Core\Mod\Commerce\Models\Refund;
use Core\Mod\Commerce\Notifications\RefundProcessed;
use Core\Tenant\Models\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@ -163,7 +160,7 @@ class RefundService
/**
* Get refund history for a payment.
*/
public function getRefundsForPayment(Payment $payment): Collection
public function getRefundsForPayment(Payment $payment): \Illuminate\Database\Eloquent\Collection
{
return $payment->refunds()->latest()->get();
}
@ -171,7 +168,7 @@ class RefundService
/**
* Get all refunds for a workspace.
*/
public function getRefundsForWorkspace(int $workspaceId): Collection
public function getRefundsForWorkspace(int $workspaceId): \Illuminate\Database\Eloquent\Collection
{
return Refund::query()
->whereHas('payment', function ($query) use ($workspaceId) {

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Carbon\Carbon;
@ -11,7 +9,6 @@ use Core\Tenant\Models\Package;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Models\WorkspacePackage;
use Core\Tenant\Services\EntitlementService;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
@ -417,7 +414,7 @@ class SubscriptionService
/**
* Get subscriptions expiring soon (for renewal reminders).
*/
public function getExpiringSoon(int $days = 7): Collection
public function getExpiringSoon(int $days = 7): \Illuminate\Database\Eloquent\Collection
{
return Subscription::query()
->active()
@ -431,7 +428,7 @@ class SubscriptionService
/**
* Get subscriptions that have failed payment and need dunning.
*/
public function getFailedPayments(): Collection
public function getFailedPayments(): \Illuminate\Database\Eloquent\Collection
{
return Subscription::query()
->where('status', 'past_due')

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Core\Mod\Commerce\Contracts\Orderable;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\Services;
use Carbon\Carbon;
@ -17,7 +15,6 @@ use Core\Tenant\Models\Workspace;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Stripe\StripeClient;
/**
* Usage-based billing service.
@ -334,7 +331,7 @@ class UsageBillingService
Subscription $subscription,
SubscriptionUsage $usage
): void {
$stripe = new StripeClient(config('commerce.gateways.stripe.secret'));
$stripe = new \Stripe\StripeClient(config('commerce.gateways.stripe.secret'));
// Find the subscription item for this price
$stripeSubscription = $stripe->subscriptions->retrieve(
@ -420,7 +417,7 @@ class UsageBillingService
return null;
}
$stripe = new StripeClient($secret);
$stripe = new \Stripe\StripeClient($secret);
// Create or update product in Stripe
$product = $stripe->products->create([

View file

@ -7,7 +7,6 @@ namespace Core\Mod\Commerce\Services;
use Core\Mod\Commerce\Models\Order;
use Core\Mod\Commerce\Models\Subscription;
use Core\Mod\Commerce\Models\WebhookEvent;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\QueryException;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
@ -312,7 +311,7 @@ class WebhookLogger
/**
* Get recent failed events for debugging.
*/
public function getRecentFailures(string $gateway, int $limit = 10): Collection
public function getRecentFailures(string $gateway, int $limit = 10): \Illuminate\Database\Eloquent\Collection
{
return WebhookEvent::forGateway($gateway)
->failed()

View file

@ -4,11 +4,9 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Admin;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Coupon;
use Core\Mod\Commerce\Services\CouponService;
use Core\Tenant\Models\Package;
use Illuminate\Support\Str;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
@ -336,8 +334,8 @@ class CouponManager extends Component
'max_uses_per_workspace' => $this->bulk_max_uses_per_workspace,
'duration' => $this->bulk_duration,
'duration_months' => $this->bulk_duration === 'repeating' ? $this->bulk_duration_months : null,
'valid_from' => $this->bulk_valid_from ? Carbon::parse($this->bulk_valid_from) : null,
'valid_until' => $this->bulk_valid_until ? Carbon::parse($this->bulk_valid_until) : null,
'valid_from' => $this->bulk_valid_from ? \Carbon\Carbon::parse($this->bulk_valid_from) : null,
'valid_until' => $this->bulk_valid_until ? \Carbon\Carbon::parse($this->bulk_valid_until) : null,
'is_active' => $this->bulk_is_active,
];
@ -401,8 +399,8 @@ class CouponManager extends Component
'max_uses_per_workspace' => $this->max_uses_per_workspace,
'duration' => $this->duration,
'duration_months' => $this->duration === 'repeating' ? $this->duration_months : null,
'valid_from' => $this->valid_from ? Carbon::parse($this->valid_from) : null,
'valid_until' => $this->valid_until ? Carbon::parse($this->valid_until) : null,
'valid_from' => $this->valid_from ? \Carbon\Carbon::parse($this->valid_from) : null,
'valid_until' => $this->valid_until ? \Carbon\Carbon::parse($this->valid_until) : null,
'is_active' => $this->is_active,
];
@ -589,7 +587,7 @@ class CouponManager extends Component
[
'lines' => array_filter([
['bold' => $c->name],
$c->description ? ['muted' => Str::limit($c->description, 30)] : null,
$c->description ? ['muted' => \Illuminate\Support\Str::limit($c->description, 30)] : null,
]),
],
['lines' => $discountLines],

View file

@ -8,7 +8,6 @@ use Core\Mod\Commerce\Models\Coupon;
use Core\Mod\Commerce\Models\Order;
use Core\Mod\Commerce\Models\Subscription;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Title;
@ -43,7 +42,7 @@ class Dashboard extends Component
}
#[Computed]
public function recentOrders(): Collection
public function recentOrders(): \Illuminate\Database\Eloquent\Collection
{
return Order::with('workspace')
->latest()

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Admin;
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Referral;
use Core\Mod\Commerce\Models\ReferralCode;
use Core\Mod\Commerce\Models\ReferralCommission;
@ -310,8 +309,8 @@ class ReferralManager extends Component
'commission_rate' => $this->codeCommissionRate,
'cookie_days' => $this->codeCookieDays,
'max_uses' => $this->codeMaxUses,
'valid_from' => $this->codeValidFrom ? Carbon::parse($this->codeValidFrom) : null,
'valid_until' => $this->codeValidUntil ? Carbon::parse($this->codeValidUntil) : null,
'valid_from' => $this->codeValidFrom ? \Carbon\Carbon::parse($this->codeValidFrom) : null,
'valid_until' => $this->codeValidUntil ? \Carbon\Carbon::parse($this->codeValidUntil) : null,
'is_active' => $this->codeIsActive,
'campaign_name' => $this->codeCampaignName,
];

View file

@ -7,7 +7,6 @@ namespace Core\Mod\Commerce\View\Modal\Admin;
use Core\Mod\Commerce\Models\Subscription;
use Core\Mod\Commerce\Services\SubscriptionService;
use Core\Tenant\Models\Workspace;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Title;
use Livewire\Component;
@ -151,7 +150,7 @@ class SubscriptionManager extends Component
$count = Subscription::whereIn('id', $this->selected)
->whereNotNull('current_period_end')
->update([
'current_period_end' => DB::raw('DATE_ADD(current_period_end, INTERVAL 30 DAY)'),
'current_period_end' => \Illuminate\Support\Facades\DB::raw('DATE_ADD(current_period_end, INTERVAL 30 DAY)'),
]);
session()->flash('message', __('commerce::commerce.bulk.period_extended', ['count' => $count, 'days' => 30]));

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Order;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Coupon;
@ -14,11 +12,9 @@ use Core\Mod\Commerce\Services\CurrencyService;
use Core\Mod\Commerce\Services\TaxService;
use Core\Tenant\Models\Package;
use Core\Tenant\Models\Workspace;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Auth;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Attributes\On;
use Livewire\Attributes\Url;
use Livewire\Component;
@ -147,7 +143,7 @@ class CheckoutPage extends Component
/**
* Handle currency change event from CurrencySelector component.
*/
#[On('currency-changed')]
#[\Livewire\Attributes\On('currency-changed')]
public function onCurrencyChanged(string $currency): void
{
$this->displayCurrency = $currency;
@ -185,7 +181,7 @@ class CheckoutPage extends Component
}
#[Computed]
public function packages(): Collection
public function packages(): \Illuminate\Database\Eloquent\Collection
{
return Package::active()
->public()

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Order;
@ -11,7 +9,6 @@ use Illuminate\Auth\Events\Registered;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
@ -159,9 +156,9 @@ class CheckoutSuccess extends Component
*/
protected function generateUniqueSlug(string $name): string
{
$baseSlug = Str::slug($name);
$baseSlug = \Illuminate\Support\Str::slug($name);
if (str_contains($baseSlug, '@')) {
$baseSlug = Str::slug(Str::before($name, '@'));
$baseSlug = \Illuminate\Support\Str::slug(\Illuminate\Support\Str::before($name, '@'));
}
$slug = $baseSlug;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Services\CommerceService;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\PaymentMethod;

View file

@ -1,14 +1,11 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Subscription as SubscriptionModel;
use Core\Mod\Commerce\Notifications\SubscriptionCancelled;
use Core\Mod\Commerce\Services\CommerceService;
use Core\Mod\Commerce\Services\SubscriptionService;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
@ -126,7 +123,7 @@ class Subscription extends Component
// Notify user
$user = Auth::user();
if ($user instanceof User) {
if ($user instanceof \Core\Tenant\Models\User) {
$user->notify(new SubscriptionCancelled($this->activeSubscription));
}

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
namespace Core\Mod\Commerce\View\Modal\Web;
use Core\Mod\Commerce\Models\Subscription;

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
return [
/*

View file

@ -1,22 +1,5 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Controllers\InvoiceController;
use Core\Mod\Commerce\View\Modal\Admin\CouponManager;
use Core\Mod\Commerce\View\Modal\Admin\CreditNoteManager;
use Core\Mod\Commerce\View\Modal\Admin\EntityManager;
use Core\Mod\Commerce\View\Modal\Admin\OrderManager;
use Core\Mod\Commerce\View\Modal\Admin\PermissionMatrixManager;
use Core\Mod\Commerce\View\Modal\Admin\ProductManager;
use Core\Mod\Commerce\View\Modal\Admin\ReferralManager;
use Core\Mod\Commerce\View\Modal\Admin\SubscriptionManager;
use Core\Mod\Commerce\View\Modal\Web\ChangePlan;
use Core\Mod\Commerce\View\Modal\Web\Dashboard;
use Core\Mod\Commerce\View\Modal\Web\Invoices;
use Core\Mod\Commerce\View\Modal\Web\PaymentMethods;
use Core\Mod\Commerce\View\Modal\Web\ReferralDashboard;
use Core\Mod\Commerce\View\Modal\Web\Subscription;
use Illuminate\Support\Facades\Route;
/*
@ -27,25 +10,25 @@ use Illuminate\Support\Facades\Route;
// Billing (user-facing hub pages)
Route::prefix('hub/billing')->name('hub.billing.')->group(function () {
Route::get('/', Dashboard::class)->name('index');
Route::get('/invoices', Invoices::class)->name('invoices');
Route::get('/invoices/{invoice}/pdf', [InvoiceController::class, 'pdf'])->name('invoices.pdf');
Route::get('/invoices/{invoice}/view', [InvoiceController::class, 'view'])->name('invoices.view');
Route::get('/payment-methods', PaymentMethods::class)->name('payment-methods');
Route::get('/subscription', Subscription::class)->name('subscription');
Route::get('/change-plan', ChangePlan::class)->name('change-plan');
Route::get('/affiliates', ReferralDashboard::class)->name('affiliates');
Route::get('/', \Core\Mod\Commerce\View\Modal\Web\Dashboard::class)->name('index');
Route::get('/invoices', \Core\Mod\Commerce\View\Modal\Web\Invoices::class)->name('invoices');
Route::get('/invoices/{invoice}/pdf', [\Core\Mod\Commerce\Controllers\InvoiceController::class, 'pdf'])->name('invoices.pdf');
Route::get('/invoices/{invoice}/view', [\Core\Mod\Commerce\Controllers\InvoiceController::class, 'view'])->name('invoices.view');
Route::get('/payment-methods', \Core\Mod\Commerce\View\Modal\Web\PaymentMethods::class)->name('payment-methods');
Route::get('/subscription', \Core\Mod\Commerce\View\Modal\Web\Subscription::class)->name('subscription');
Route::get('/change-plan', \Core\Mod\Commerce\View\Modal\Web\ChangePlan::class)->name('change-plan');
Route::get('/affiliates', \Core\Mod\Commerce\View\Modal\Web\ReferralDashboard::class)->name('affiliates');
});
// Commerce management (admin only - Hades tier)
Route::prefix('hub/commerce')->name('hub.commerce.')->group(function () {
Route::get('/', Core\Mod\Commerce\View\Modal\Admin\Dashboard::class)->name('dashboard');
Route::get('/orders', OrderManager::class)->name('orders');
Route::get('/subscriptions', SubscriptionManager::class)->name('subscriptions');
Route::get('/coupons', CouponManager::class)->name('coupons');
Route::get('/entities', EntityManager::class)->name('entities');
Route::get('/permissions', PermissionMatrixManager::class)->name('permissions');
Route::get('/products', ProductManager::class)->name('products');
Route::get('/credit-notes', CreditNoteManager::class)->name('credit-notes');
Route::get('/referrals', ReferralManager::class)->name('referrals');
Route::get('/', \Core\Mod\Commerce\View\Modal\Admin\Dashboard::class)->name('dashboard');
Route::get('/orders', \Core\Mod\Commerce\View\Modal\Admin\OrderManager::class)->name('orders');
Route::get('/subscriptions', \Core\Mod\Commerce\View\Modal\Admin\SubscriptionManager::class)->name('subscriptions');
Route::get('/coupons', \Core\Mod\Commerce\View\Modal\Admin\CouponManager::class)->name('coupons');
Route::get('/entities', \Core\Mod\Commerce\View\Modal\Admin\EntityManager::class)->name('entities');
Route::get('/permissions', \Core\Mod\Commerce\View\Modal\Admin\PermissionMatrixManager::class)->name('permissions');
Route::get('/products', \Core\Mod\Commerce\View\Modal\Admin\ProductManager::class)->name('products');
Route::get('/credit-notes', \Core\Mod\Commerce\View\Modal\Admin\CreditNoteManager::class)->name('credit-notes');
Route::get('/referrals', \Core\Mod\Commerce\View\Modal\Admin\ReferralManager::class)->name('referrals');
});

View file

@ -1,5 +1,3 @@
<?php
declare(strict_types=1);
// Console commands are registered via Core modules

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Models\Invoice;
use Core\Mod\Commerce\Models\Order;
use Core\Mod\Commerce\Models\Payment;
@ -11,10 +9,9 @@ use Core\Tenant\Models\Package;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Models\WorkspacePackage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
Cache::flush();

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Models\Coupon;
use Core\Mod\Commerce\Models\CouponUsage;
use Core\Mod\Commerce\Models\Order;
@ -9,9 +7,8 @@ use Core\Mod\Commerce\Services\CouponService;
use Core\Tenant\Models\Package;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
$this->user = User::factory()->create();

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
use Carbon\Carbon;
use Core\Mod\Commerce\Models\Invoice;
use Core\Mod\Commerce\Models\Subscription;
@ -14,11 +12,10 @@ use Core\Tenant\Models\Package;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Models\WorkspacePackage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Notification;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
Cache::flush();

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Events\SubscriptionRenewed;
use Core\Mod\Commerce\Jobs\ProcessSubscriptionRenewal;
use Core\Mod\Commerce\Models\Subscription;
@ -13,11 +11,10 @@ use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Models\WorkspacePackage;
use Core\Tenant\Services\EntitlementService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Event;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
Cache::flush();

View file

@ -1,19 +1,15 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Models\Payment;
use Core\Mod\Commerce\Models\Refund;
use Core\Mod\Commerce\Notifications\RefundProcessed;
use Core\Mod\Commerce\Services\CommerceService;
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
use Core\Mod\Commerce\Services\RefundService;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
Notification::fake();
@ -40,7 +36,7 @@ beforeEach(function () {
]);
// Mock the gateway
$mockGateway = Mockery::mock(PaymentGatewayContract::class);
$mockGateway = Mockery::mock(\Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract::class);
$mockGateway->shouldReceive('refund')->andReturn([
'success' => true,
'refund_id' => 're_test_123',
@ -96,15 +92,15 @@ describe('RefundService', function () {
it('throws exception for refund exceeding available amount', function () {
expect(fn () => $this->service->refund($this->payment, 150.00))
->toThrow(InvalidArgumentException::class, 'exceeds maximum refundable');
->toThrow(\InvalidArgumentException::class, 'exceeds maximum refundable');
});
it('throws exception for zero or negative amount', function () {
expect(fn () => $this->service->refund($this->payment, 0))
->toThrow(InvalidArgumentException::class, 'greater than zero');
->toThrow(\InvalidArgumentException::class, 'greater than zero');
expect(fn () => $this->service->refund($this->payment, -50.00))
->toThrow(InvalidArgumentException::class, 'greater than zero');
->toThrow(\InvalidArgumentException::class, 'greater than zero');
});
it('throws exception for non-succeeded payments', function () {
@ -118,7 +114,7 @@ describe('RefundService', function () {
]);
expect(fn () => $this->service->refund($pendingPayment, 50.00))
->toThrow(InvalidArgumentException::class, 'only refund successful payments');
->toThrow(\InvalidArgumentException::class, 'only refund successful payments');
});
it('allows multiple partial refunds up to full amount', function () {

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
use Carbon\Carbon;
use Core\Mod\Commerce\Exceptions\PauseLimitExceededException;
use Core\Mod\Commerce\Models\Subscription;
@ -12,10 +10,9 @@ use Core\Tenant\Models\Package;
use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Models\WorkspacePackage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
Cache::flush();
@ -465,7 +462,7 @@ describe('Proration calculations', function () {
$this->subscription->workspacePackage->setRelation('package', null);
expect(fn () => $this->service->previewPlanChange($this->subscription, $this->agencyPackage))
->toThrow(InvalidArgumentException::class, 'no current package');
->toThrow(\InvalidArgumentException::class, 'no current package');
});
});

View file

@ -1,13 +1,10 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Models\TaxRate;
use Core\Mod\Commerce\Services\TaxService;
use Core\Tenant\Models\Workspace;
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
$this->workspace = Workspace::factory()->create([

View file

@ -4,10 +4,9 @@ declare(strict_types=1);
use Core\Mod\Commerce\Services\WebhookRateLimiter;
use Illuminate\Cache\RateLimiter;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
// ============================================================================
// WebhookRateLimiter Unit Tests

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
use Core\Mod\Commerce\Controllers\Webhooks\BTCPayWebhookController;
use Core\Mod\Commerce\Controllers\Webhooks\StripeWebhookController;
use Core\Mod\Commerce\Models\Order;
@ -21,12 +19,10 @@ use Core\Tenant\Models\User;
use Core\Tenant\Models\Workspace;
use Core\Tenant\Models\WorkspacePackage;
use Core\Tenant\Services\EntitlementService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Notification;
use WebhookPayloadValidationException;
uses(RefreshDatabase::class);
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
beforeEach(function () {
Notification::fake();
@ -128,7 +124,7 @@ describe('StripeWebhookController', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$request->headers->set('Stripe-Signature', 't=123,v1=abc');
$response = $controller->handle($request);
@ -181,7 +177,7 @@ describe('StripeWebhookController', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -217,7 +213,7 @@ describe('StripeWebhookController', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -274,7 +270,7 @@ describe('StripeWebhookController', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -336,7 +332,7 @@ describe('StripeWebhookController', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -372,7 +368,7 @@ describe('StripeWebhookController', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -464,7 +460,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$request->headers->set('BTCPay-Sig', 'valid_signature');
$response = $controller->handle($request);
@ -504,7 +500,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -527,7 +523,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -552,7 +548,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -579,7 +575,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -606,7 +602,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -633,7 +629,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -660,7 +656,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -687,7 +683,7 @@ describe('BTCPayWebhookController', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -844,7 +840,7 @@ describe('WebhookLogger service', function () {
it('extracts relevant headers', function () {
$logger = new WebhookLogger;
$request = new Request;
$request = new \Illuminate\Http\Request;
$request->headers->set('Stripe-Signature', 't=123,v1=secret_signature_here');
$request->headers->set('Content-Type', 'application/json');
$request->headers->set('User-Agent', 'Stripe/1.0');
@ -1239,7 +1235,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$request->headers->set('BTCPay-Sig', 'valid_signature');
$response = $controller->handle($request);
@ -1266,7 +1262,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(400)
@ -1286,7 +1282,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(400)
@ -1317,7 +1313,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -1392,7 +1388,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -1427,7 +1423,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
// First request - should process
$request1 = new Request;
$request1 = new \Illuminate\Http\Request;
$response1 = $controller->handle($request1);
expect($response1->getStatusCode())->toBe(200);
@ -1441,7 +1437,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
// Second request with same event ID - should be rejected as duplicate
$webhookLogger2 = new WebhookLogger;
$controller2 = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger2);
$request2 = new Request;
$request2 = new \Illuminate\Http\Request;
$response2 = $controller2->handle($request2);
expect($response2->getStatusCode())->toBe(200)
@ -1521,7 +1517,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
$webhookLogger
);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -1578,7 +1574,7 @@ describe('BTCPay Payment Amount Verification', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -1614,7 +1610,7 @@ describe('BTCPay Payment Amount Verification', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -1657,7 +1653,7 @@ describe('BTCPay Payment Amount Verification', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);
@ -1694,7 +1690,7 @@ describe('BTCPay Payment Amount Verification', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200)
@ -1731,7 +1727,7 @@ describe('BTCPay Payment Amount Verification', function () {
$webhookLogger = new WebhookLogger;
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
$request = new Request;
$request = new \Illuminate\Http\Request;
$response = $controller->handle($request);
expect($response->getStatusCode())->toBe(200);

View file

@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Tests;
use Core\Mod\Commerce\Boot;
use Orchestra\Testbench\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
@ -12,7 +11,7 @@ abstract class TestCase extends BaseTestCase
protected function getPackageProviders($app): array
{
return [
Boot::class,
\Core\Mod\Commerce\Boot::class,
];
}
}

View file

@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
/**
* UseCase: Commerce Admin CRUD (Basic Flow)
*