Merge pull request 'DX audit and fix (PHP)' (#22) from agent/dx-audit-and-fix--laravel-php-package into dev
Reviewed-on: #22
This commit is contained in:
commit
b51084b8db
95 changed files with 458 additions and 207 deletions
63
Boot.php
63
Boot.php
|
|
@ -8,11 +8,32 @@ use Core\Events\AdminPanelBooting;
|
||||||
use Core\Events\ApiRoutesRegistering;
|
use Core\Events\ApiRoutesRegistering;
|
||||||
use Core\Events\ConsoleBooting;
|
use Core\Events\ConsoleBooting;
|
||||||
use Core\Events\WebRoutesRegistering;
|
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\ProvisionSocialHostSubscription;
|
||||||
use Core\Mod\Commerce\Listeners\RewardAgentReferralOnSubscription;
|
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\BTCPayGateway;
|
||||||
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
|
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
|
||||||
use Core\Mod\Commerce\Services\PaymentGateway\StripeGateway;
|
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\Event;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
@ -46,9 +67,9 @@ class Boot extends ServiceProvider
|
||||||
|
|
||||||
// Laravel event listeners (not lifecycle events)
|
// Laravel event listeners (not lifecycle events)
|
||||||
Event::subscribe(ProvisionSocialHostSubscription::class);
|
Event::subscribe(ProvisionSocialHostSubscription::class);
|
||||||
Event::listen(\Core\Mod\Commerce\Events\SubscriptionCreated::class, RewardAgentReferralOnSubscription::class);
|
Event::listen(SubscriptionCreated::class, RewardAgentReferralOnSubscription::class);
|
||||||
Event::listen(\Core\Mod\Commerce\Events\SubscriptionRenewed::class, Listeners\ResetUsageOnRenewal::class);
|
Event::listen(SubscriptionRenewed::class, Listeners\ResetUsageOnRenewal::class);
|
||||||
Event::listen(\Core\Mod\Commerce\Events\OrderPaid::class, Listeners\CreateReferralCommission::class);
|
Event::listen(OrderPaid::class, Listeners\CreateReferralCommission::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
|
|
@ -59,24 +80,24 @@ class Boot extends ServiceProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
// Core Services
|
// Core Services
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\CommerceService::class);
|
$this->app->singleton(CommerceService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\SubscriptionService::class);
|
$this->app->singleton(SubscriptionService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\InvoiceService::class);
|
$this->app->singleton(InvoiceService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\PermissionMatrixService::class);
|
$this->app->singleton(PermissionMatrixService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\CouponService::class);
|
$this->app->singleton(CouponService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\TaxService::class);
|
$this->app->singleton(TaxService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\CurrencyService::class);
|
$this->app->singleton(CurrencyService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\ContentOverrideService::class);
|
$this->app->singleton(ContentOverrideService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\DunningService::class);
|
$this->app->singleton(DunningService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\SkuParserService::class);
|
$this->app->singleton(SkuParserService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\SkuBuilderService::class);
|
$this->app->singleton(SkuBuilderService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\CreditNoteService::class);
|
$this->app->singleton(CreditNoteService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\PaymentMethodService::class);
|
$this->app->singleton(PaymentMethodService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\UsageBillingService::class);
|
$this->app->singleton(UsageBillingService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\ReferralService::class);
|
$this->app->singleton(ReferralService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\FraudService::class);
|
$this->app->singleton(FraudService::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\CheckoutRateLimiter::class);
|
$this->app->singleton(CheckoutRateLimiter::class);
|
||||||
$this->app->singleton(\Core\Mod\Commerce\Services\WebhookRateLimiter::class);
|
$this->app->singleton(WebhookRateLimiter::class);
|
||||||
|
|
||||||
// Payment Gateways
|
// Payment Gateways
|
||||||
$this->app->singleton('commerce.gateway.btcpay', function ($app) {
|
$this->app->singleton('commerce.gateway.btcpay', function ($app) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Console;
|
namespace Core\Mod\Commerce\Console;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Console;
|
namespace Core\Mod\Commerce\Console;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
@ -164,7 +165,7 @@ class PlantSubscriberTrees extends Command
|
||||||
protected function hasPlantedThisMonth(int $workspaceId, string $month): bool
|
protected function hasPlantedThisMonth(int $workspaceId, string $month): bool
|
||||||
{
|
{
|
||||||
// Parse the month string (YYYY-MM format)
|
// Parse the month string (YYYY-MM format)
|
||||||
$date = \Carbon\Carbon::createFromFormat('Y-m', $month);
|
$date = Carbon::createFromFormat('Y-m', $month);
|
||||||
$startOfMonth = $date->copy()->startOfMonth();
|
$startOfMonth = $date->copy()->startOfMonth();
|
||||||
$endOfMonth = $date->copy()->endOfMonth();
|
$endOfMonth = $date->copy()->endOfMonth();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Console;
|
namespace Core\Mod\Commerce\Console;
|
||||||
|
|
||||||
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Core\Mod\Commerce\Services\DunningService;
|
use Core\Mod\Commerce\Services\DunningService;
|
||||||
use Core\Mod\Commerce\Services\SubscriptionService;
|
use Core\Mod\Commerce\Services\SubscriptionService;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
@ -269,7 +272,7 @@ class ProcessDunning extends Command
|
||||||
$this->info('Stage 5: Expired Subscriptions');
|
$this->info('Stage 5: Expired Subscriptions');
|
||||||
|
|
||||||
if ($dryRun) {
|
if ($dryRun) {
|
||||||
$count = \Core\Mod\Commerce\Models\Subscription::query()
|
$count = Subscription::query()
|
||||||
->active()
|
->active()
|
||||||
->whereNotNull('cancelled_at')
|
->whereNotNull('cancelled_at')
|
||||||
->where('current_period_end', '<=', now())
|
->where('current_period_end', '<=', now())
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Console;
|
namespace Core\Mod\Commerce\Console;
|
||||||
|
|
||||||
|
use Core\Mod\Commerce\Models\ExchangeRate;
|
||||||
use Core\Mod\Commerce\Services\CurrencyService;
|
use Core\Mod\Commerce\Services\CurrencyService;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
|
|
||||||
|
|
@ -30,7 +31,7 @@ class RefreshExchangeRates extends Command
|
||||||
$this->line("Provider: {$provider}");
|
$this->line("Provider: {$provider}");
|
||||||
|
|
||||||
// Check if rates need refresh
|
// Check if rates need refresh
|
||||||
if (! $this->option('force') && ! \Core\Mod\Commerce\Models\ExchangeRate::needsRefresh()) {
|
if (! $this->option('force') && ! ExchangeRate::needsRefresh()) {
|
||||||
$this->info('Rates are still fresh. Use --force to refresh anyway.');
|
$this->info('Rates are still fresh. Use --force to refresh anyway.');
|
||||||
|
|
||||||
return self::SUCCESS;
|
return self::SUCCESS;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Console;
|
namespace Core\Mod\Commerce\Console;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Console;
|
namespace Core\Mod\Commerce\Console;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,9 @@ use Core\Mod\Commerce\Services\CommerceService;
|
||||||
use Core\Mod\Commerce\Services\InvoiceService;
|
use Core\Mod\Commerce\Services\InvoiceService;
|
||||||
use Core\Mod\Commerce\Services\SubscriptionService;
|
use Core\Mod\Commerce\Services\SubscriptionService;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Core\Tenant\Services\EntitlementService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -38,7 +40,7 @@ class CommerceController extends Controller
|
||||||
{
|
{
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
|
|
||||||
if (! $user instanceof \Core\Tenant\Models\User) {
|
if (! $user instanceof User) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +202,7 @@ class CommerceController extends Controller
|
||||||
return response()->json(['error' => 'No workspace found'], 404);
|
return response()->json(['error' => 'No workspace found'], 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
$entitlements = app(\Core\Tenant\Services\EntitlementService::class);
|
$entitlements = app(EntitlementService::class);
|
||||||
$summary = $entitlements->getUsageSummary($workspace);
|
$summary = $entitlements->getUsageSummary($workspace);
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Data;
|
namespace Core\Mod\Commerce\Data;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Coupon;
|
use Core\Mod\Commerce\Models\Coupon;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Events;
|
namespace Core\Mod\Commerce\Events;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Events;
|
namespace Core\Mod\Commerce\Events;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Events;
|
namespace Core\Mod\Commerce\Events;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Events;
|
namespace Core\Mod\Commerce\Events;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Listeners;
|
namespace Core\Mod\Commerce\Listeners;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Events\SubscriptionCancelled;
|
use Core\Mod\Commerce\Events\SubscriptionCancelled;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Listeners;
|
namespace Core\Mod\Commerce\Listeners;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Events\SubscriptionRenewed;
|
use Core\Mod\Commerce\Events\SubscriptionRenewed;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Mcp\Tools;
|
namespace Core\Mod\Commerce\Mcp\Tools;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\Coupon;
|
use Core\Mod\Commerce\Models\Coupon;
|
||||||
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
use Illuminate\Contracts\JsonSchema\JsonSchema;
|
||||||
use Laravel\Mcp\Request;
|
use Laravel\Mcp\Request;
|
||||||
|
|
@ -59,7 +62,7 @@ class CreateCoupon extends Tool
|
||||||
'duration' => $duration,
|
'duration' => $duration,
|
||||||
'max_uses' => $maxUses,
|
'max_uses' => $maxUses,
|
||||||
'max_uses_per_workspace' => 1,
|
'max_uses_per_workspace' => 1,
|
||||||
'valid_until' => $validUntil ? \Carbon\Carbon::parse($validUntil) : null,
|
'valid_until' => $validUntil ? Carbon::parse($validUntil) : null,
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
'applies_to' => 'all',
|
'applies_to' => 'all',
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Mcp\Tools;
|
namespace Core\Mod\Commerce\Mcp\Tools;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Mcp\Tools;
|
namespace Core\Mod\Commerce\Mcp\Tools;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Invoice;
|
use Core\Mod\Commerce\Models\Invoice;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Mcp\Tools;
|
namespace Core\Mod\Commerce\Mcp\Tools;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Contracts\Orderable;
|
use Core\Mod\Commerce\Contracts\Orderable;
|
||||||
|
use Core\Mod\Commerce\Database\Factories\CouponFactory;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
@ -27,8 +31,8 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property int $used_count
|
* @property int $used_count
|
||||||
* @property string $duration
|
* @property string $duration
|
||||||
* @property int|null $duration_months
|
* @property int|null $duration_months
|
||||||
* @property \Carbon\Carbon|null $valid_from
|
* @property Carbon|null $valid_from
|
||||||
* @property \Carbon\Carbon|null $valid_until
|
* @property Carbon|null $valid_until
|
||||||
* @property bool $is_active
|
* @property bool $is_active
|
||||||
*/
|
*/
|
||||||
class Coupon extends Model
|
class Coupon extends Model
|
||||||
|
|
@ -36,9 +40,9 @@ class Coupon extends Model
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use LogsActivity;
|
use LogsActivity;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\CouponFactory
|
protected static function newFactory(): CouponFactory
|
||||||
{
|
{
|
||||||
return \Core\Mod\Commerce\Database\Factories\CouponFactory::new();
|
return CouponFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -14,7 +17,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
* @property int $workspace_id
|
* @property int $workspace_id
|
||||||
* @property int $order_id
|
* @property int $order_id
|
||||||
* @property float $discount_amount
|
* @property float $discount_amount
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property Carbon $created_at
|
||||||
*/
|
*/
|
||||||
class CouponUsage extends Model
|
class CouponUsage extends Model
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -31,9 +34,9 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property string $status
|
* @property string $status
|
||||||
* @property float $amount_used
|
* @property float $amount_used
|
||||||
* @property int|null $applied_to_order_id
|
* @property int|null $applied_to_order_id
|
||||||
* @property \Carbon\Carbon|null $issued_at
|
* @property Carbon|null $issued_at
|
||||||
* @property \Carbon\Carbon|null $applied_at
|
* @property Carbon|null $applied_at
|
||||||
* @property \Carbon\Carbon|null $voided_at
|
* @property Carbon|null $voided_at
|
||||||
* @property int|null $issued_by
|
* @property int|null $issued_by
|
||||||
* @property int|null $voided_by
|
* @property int|null $voided_by
|
||||||
* @property array|null $metadata
|
* @property array|null $metadata
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
|
|
@ -15,7 +16,7 @@ use Illuminate\Support\Facades\Cache;
|
||||||
* @property string $target_currency
|
* @property string $target_currency
|
||||||
* @property float $rate
|
* @property float $rate
|
||||||
* @property string $source
|
* @property string $source
|
||||||
* @property \Carbon\Carbon $fetched_at
|
* @property Carbon $fetched_at
|
||||||
*/
|
*/
|
||||||
class ExchangeRate extends Model
|
class ExchangeRate extends Model
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
@ -20,8 +21,8 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
* @property int|null $low_stock_threshold
|
* @property int|null $low_stock_threshold
|
||||||
* @property string|null $bin_location
|
* @property string|null $bin_location
|
||||||
* @property string|null $zone
|
* @property string|null $zone
|
||||||
* @property \Carbon\Carbon|null $last_counted_at
|
* @property Carbon|null $last_counted_at
|
||||||
* @property \Carbon\Carbon|null $last_restocked_at
|
* @property Carbon|null $last_restocked_at
|
||||||
* @property int|null $unit_cost
|
* @property int|null $unit_cost
|
||||||
* @property array|null $metadata
|
* @property array|null $metadata
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -24,7 +25,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
* @property string|null $notes
|
* @property string|null $notes
|
||||||
* @property int|null $user_id
|
* @property int|null $user_id
|
||||||
* @property int|null $unit_cost
|
* @property int|null $unit_cost
|
||||||
* @property \Carbon\Carbon $created_at
|
* @property Carbon $created_at
|
||||||
*/
|
*/
|
||||||
class InventoryMovement extends Model
|
class InventoryMovement extends Model
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Core\Mod\Commerce\Database\Factories\InvoiceFactory;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -23,9 +27,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
* @property float $total
|
* @property float $total
|
||||||
* @property float $amount_paid
|
* @property float $amount_paid
|
||||||
* @property float $amount_due
|
* @property float $amount_due
|
||||||
* @property \Carbon\Carbon $issue_date
|
* @property Carbon $issue_date
|
||||||
* @property \Carbon\Carbon $due_date
|
* @property Carbon $due_date
|
||||||
* @property \Carbon\Carbon|null $paid_at
|
* @property Carbon|null $paid_at
|
||||||
* @property string|null $billing_name
|
* @property string|null $billing_name
|
||||||
* @property array|null $billing_address
|
* @property array|null $billing_address
|
||||||
* @property string|null $tax_id
|
* @property string|null $tax_id
|
||||||
|
|
@ -35,9 +39,9 @@ class Invoice extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\InvoiceFactory
|
protected static function newFactory(): InvoiceFactory
|
||||||
{
|
{
|
||||||
return \Core\Mod\Commerce\Database\Factories\InvoiceFactory::new();
|
return InvoiceFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
@ -93,7 +97,7 @@ class Invoice extends Model
|
||||||
/**
|
/**
|
||||||
* Get the issued_at attribute (alias for issue_date).
|
* Get the issued_at attribute (alias for issue_date).
|
||||||
*/
|
*/
|
||||||
public function getIssuedAtAttribute(): ?\Carbon\Carbon
|
public function getIssuedAtAttribute(): ?Carbon
|
||||||
{
|
{
|
||||||
return $this->issue_date;
|
return $this->issue_date;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Contracts\Orderable;
|
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\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -38,7 +43,7 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property int|null $coupon_id
|
* @property int|null $coupon_id
|
||||||
* @property array|null $billing_address
|
* @property array|null $billing_address
|
||||||
* @property array|null $metadata
|
* @property array|null $metadata
|
||||||
* @property \Carbon\Carbon|null $paid_at
|
* @property Carbon|null $paid_at
|
||||||
* @property-read Orderable|null $orderable
|
* @property-read Orderable|null $orderable
|
||||||
*/
|
*/
|
||||||
class Order extends Model
|
class Order extends Model
|
||||||
|
|
@ -46,9 +51,9 @@ class Order extends Model
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use LogsActivity;
|
use LogsActivity;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\OrderFactory
|
protected static function newFactory(): OrderFactory
|
||||||
{
|
{
|
||||||
return \Core\Mod\Commerce\Database\Factories\OrderFactory::new();
|
return OrderFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
@ -291,7 +296,7 @@ class Order extends Model
|
||||||
*/
|
*/
|
||||||
public function getFormattedTotalAttribute(): string
|
public function getFormattedTotalAttribute(): string
|
||||||
{
|
{
|
||||||
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
|
$currencyService = app(CurrencyService::class);
|
||||||
|
|
||||||
return $currencyService->format($this->total, $this->display_currency);
|
return $currencyService->format($this->total, $this->display_currency);
|
||||||
}
|
}
|
||||||
|
|
@ -301,7 +306,7 @@ class Order extends Model
|
||||||
*/
|
*/
|
||||||
public function getFormattedSubtotalAttribute(): string
|
public function getFormattedSubtotalAttribute(): string
|
||||||
{
|
{
|
||||||
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
|
$currencyService = app(CurrencyService::class);
|
||||||
|
|
||||||
return $currencyService->format($this->subtotal, $this->display_currency);
|
return $currencyService->format($this->subtotal, $this->display_currency);
|
||||||
}
|
}
|
||||||
|
|
@ -311,7 +316,7 @@ class Order extends Model
|
||||||
*/
|
*/
|
||||||
public function getFormattedTaxAmountAttribute(): string
|
public function getFormattedTaxAmountAttribute(): string
|
||||||
{
|
{
|
||||||
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
|
$currencyService = app(CurrencyService::class);
|
||||||
|
|
||||||
return $currencyService->format($this->tax_amount, $this->display_currency);
|
return $currencyService->format($this->tax_amount, $this->display_currency);
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +326,7 @@ class Order extends Model
|
||||||
*/
|
*/
|
||||||
public function getFormattedDiscountAmountAttribute(): string
|
public function getFormattedDiscountAmountAttribute(): string
|
||||||
{
|
{
|
||||||
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
|
$currencyService = app(CurrencyService::class);
|
||||||
|
|
||||||
return $currencyService->format($this->discount_amount, $this->display_currency);
|
return $currencyService->format($this->discount_amount, $this->display_currency);
|
||||||
}
|
}
|
||||||
|
|
@ -341,7 +346,7 @@ class Order extends Model
|
||||||
return $amount;
|
return $amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return \Core\Mod\Commerce\Models\ExchangeRate::convert(
|
return ExchangeRate::convert(
|
||||||
$amount,
|
$amount,
|
||||||
$this->display_currency,
|
$this->display_currency,
|
||||||
$baseCurrency
|
$baseCurrency
|
||||||
|
|
@ -363,7 +368,7 @@ class Order extends Model
|
||||||
return $amount;
|
return $amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
return \Core\Mod\Commerce\Models\ExchangeRate::convert(
|
return ExchangeRate::convert(
|
||||||
$amount,
|
$amount,
|
||||||
$baseCurrency,
|
$baseCurrency,
|
||||||
$this->display_currency
|
$this->display_currency
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Core\Mod\Commerce\Database\Factories\PaymentFactory;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -33,9 +36,9 @@ class Payment extends Model
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\PaymentFactory
|
protected static function newFactory(): PaymentFactory
|
||||||
{
|
{
|
||||||
return \Core\Mod\Commerce\Database\Factories\PaymentFactory::new();
|
return PaymentFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
|
@ -88,7 +91,7 @@ class PaymentMethod extends Model
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$expiry = \Carbon\Carbon::createFromDate($this->exp_year, $this->exp_month)->endOfMonth();
|
$expiry = Carbon::createFromDate($this->exp_year, $this->exp_month)->endOfMonth();
|
||||||
|
|
||||||
return $expiry->isPast();
|
return $expiry->isPast();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
|
@ -23,7 +24,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
* @property bool $locked
|
* @property bool $locked
|
||||||
* @property string $source
|
* @property string $source
|
||||||
* @property int|null $set_by_entity_id
|
* @property int|null $set_by_entity_id
|
||||||
* @property \Carbon\Carbon|null $trained_at
|
* @property Carbon|null $trained_at
|
||||||
* @property string|null $trained_route
|
* @property string|null $trained_route
|
||||||
*/
|
*/
|
||||||
class PermissionMatrix extends Model
|
class PermissionMatrix extends Model
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -27,7 +28,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
* @property int|null $user_id
|
* @property int|null $user_id
|
||||||
* @property string $status
|
* @property string $status
|
||||||
* @property bool $was_trained
|
* @property bool $was_trained
|
||||||
* @property \Carbon\Carbon|null $trained_at
|
* @property Carbon|null $trained_at
|
||||||
*/
|
*/
|
||||||
class PermissionRequest extends Model
|
class PermissionRequest extends Model
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Concerns\HasContentOverrides;
|
use Core\Mod\Commerce\Concerns\HasContentOverrides;
|
||||||
|
use Core\Mod\Commerce\Services\CurrencyService;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -287,7 +288,7 @@ class Product extends Model
|
||||||
public function formatPrice(int $amount, ?string $currency = null): string
|
public function formatPrice(int $amount, ?string $currency = null): string
|
||||||
{
|
{
|
||||||
$currency = $currency ?? $this->currency;
|
$currency = $currency ?? $this->currency;
|
||||||
$currencyService = app(\Core\Mod\Commerce\Services\CurrencyService::class);
|
$currencyService = app(CurrencyService::class);
|
||||||
|
|
||||||
return $currencyService->format($amount, $currency, isCents: true);
|
return $currencyService->format($amount, $currency, isCents: true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -30,13 +31,13 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property string|null $ip_address
|
* @property string|null $ip_address
|
||||||
* @property string|null $user_agent
|
* @property string|null $user_agent
|
||||||
* @property string|null $tracking_id
|
* @property string|null $tracking_id
|
||||||
* @property \Carbon\Carbon|null $clicked_at
|
* @property Carbon|null $clicked_at
|
||||||
* @property \Carbon\Carbon|null $signed_up_at
|
* @property Carbon|null $signed_up_at
|
||||||
* @property \Carbon\Carbon|null $first_purchase_at
|
* @property Carbon|null $first_purchase_at
|
||||||
* @property \Carbon\Carbon|null $qualified_at
|
* @property Carbon|null $qualified_at
|
||||||
* @property \Carbon\Carbon|null $disqualified_at
|
* @property Carbon|null $disqualified_at
|
||||||
* @property string|null $disqualification_reason
|
* @property string|null $disqualification_reason
|
||||||
* @property \Carbon\Carbon|null $matured_at
|
* @property Carbon|null $matured_at
|
||||||
*/
|
*/
|
||||||
class Referral extends Model
|
class Referral extends Model
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -24,8 +25,8 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property int $cookie_days
|
* @property int $cookie_days
|
||||||
* @property int|null $max_uses
|
* @property int|null $max_uses
|
||||||
* @property int $uses_count
|
* @property int $uses_count
|
||||||
* @property \Carbon\Carbon|null $valid_from
|
* @property Carbon|null $valid_from
|
||||||
* @property \Carbon\Carbon|null $valid_until
|
* @property Carbon|null $valid_until
|
||||||
* @property bool $is_active
|
* @property bool $is_active
|
||||||
* @property string|null $campaign_name
|
* @property string|null $campaign_name
|
||||||
* @property array|null $metadata
|
* @property array|null $metadata
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -26,10 +27,10 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property float $commission_amount
|
* @property float $commission_amount
|
||||||
* @property string $currency
|
* @property string $currency
|
||||||
* @property string $status
|
* @property string $status
|
||||||
* @property \Carbon\Carbon|null $matures_at
|
* @property Carbon|null $matures_at
|
||||||
* @property \Carbon\Carbon|null $matured_at
|
* @property Carbon|null $matured_at
|
||||||
* @property int|null $payout_id
|
* @property int|null $payout_id
|
||||||
* @property \Carbon\Carbon|null $paid_at
|
* @property Carbon|null $paid_at
|
||||||
* @property string|null $notes
|
* @property string|null $notes
|
||||||
*/
|
*/
|
||||||
class ReferralCommission extends Model
|
class ReferralCommission extends Model
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
@ -27,10 +28,10 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property float|null $btc_amount
|
* @property float|null $btc_amount
|
||||||
* @property float|null $btc_rate
|
* @property float|null $btc_rate
|
||||||
* @property string $status
|
* @property string $status
|
||||||
* @property \Carbon\Carbon|null $requested_at
|
* @property Carbon|null $requested_at
|
||||||
* @property \Carbon\Carbon|null $processed_at
|
* @property Carbon|null $processed_at
|
||||||
* @property \Carbon\Carbon|null $completed_at
|
* @property Carbon|null $completed_at
|
||||||
* @property \Carbon\Carbon|null $failed_at
|
* @property Carbon|null $failed_at
|
||||||
* @property string|null $notes
|
* @property string|null $notes
|
||||||
* @property string|null $failure_reason
|
* @property string|null $failure_reason
|
||||||
* @property int|null $processed_by
|
* @property int|null $processed_by
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
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\SubscriptionCreated;
|
||||||
use Core\Mod\Commerce\Events\SubscriptionUpdated;
|
use Core\Mod\Commerce\Events\SubscriptionUpdated;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
|
@ -26,12 +30,12 @@ use Spatie\Activitylog\Traits\LogsActivity;
|
||||||
* @property string $gateway_customer_id
|
* @property string $gateway_customer_id
|
||||||
* @property string|null $gateway_price_id
|
* @property string|null $gateway_price_id
|
||||||
* @property string $status
|
* @property string $status
|
||||||
* @property \Carbon\Carbon $current_period_start
|
* @property Carbon $current_period_start
|
||||||
* @property \Carbon\Carbon $current_period_end
|
* @property Carbon $current_period_end
|
||||||
* @property \Carbon\Carbon|null $trial_ends_at
|
* @property Carbon|null $trial_ends_at
|
||||||
* @property bool $cancel_at_period_end
|
* @property bool $cancel_at_period_end
|
||||||
* @property \Carbon\Carbon|null $cancelled_at
|
* @property Carbon|null $cancelled_at
|
||||||
* @property \Carbon\Carbon|null $ended_at
|
* @property Carbon|null $ended_at
|
||||||
* @property array|null $metadata
|
* @property array|null $metadata
|
||||||
*/
|
*/
|
||||||
class Subscription extends Model
|
class Subscription extends Model
|
||||||
|
|
@ -39,9 +43,9 @@ class Subscription extends Model
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use LogsActivity;
|
use LogsActivity;
|
||||||
|
|
||||||
protected static function newFactory(): \Core\Mod\Commerce\Database\Factories\SubscriptionFactory
|
protected static function newFactory(): SubscriptionFactory
|
||||||
{
|
{
|
||||||
return \Core\Mod\Commerce\Database\Factories\SubscriptionFactory::new();
|
return SubscriptionFactory::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -231,7 +235,7 @@ class Subscription extends Model
|
||||||
$this->update(['status' => 'past_due']);
|
$this->update(['status' => 'past_due']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renew(\Carbon\Carbon $periodStart, \Carbon\Carbon $periodEnd): void
|
public function renew(Carbon $periodStart, Carbon $periodEnd): void
|
||||||
{
|
{
|
||||||
$this->update([
|
$this->update([
|
||||||
'status' => 'active',
|
'status' => 'active',
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
@ -13,10 +15,10 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
* @property int $subscription_id
|
* @property int $subscription_id
|
||||||
* @property int $meter_id
|
* @property int $meter_id
|
||||||
* @property int $quantity
|
* @property int $quantity
|
||||||
* @property \Carbon\Carbon $period_start
|
* @property Carbon $period_start
|
||||||
* @property \Carbon\Carbon $period_end
|
* @property Carbon $period_end
|
||||||
* @property string|null $stripe_usage_record_id
|
* @property string|null $stripe_usage_record_id
|
||||||
* @property \Carbon\Carbon|null $synced_at
|
* @property Carbon|null $synced_at
|
||||||
* @property bool $billed
|
* @property bool $billed
|
||||||
* @property int|null $invoice_item_id
|
* @property int|null $invoice_item_id
|
||||||
* @property array|null $metadata
|
* @property array|null $metadata
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
|
@ -17,8 +20,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||||
* @property string $type
|
* @property string $type
|
||||||
* @property float $rate
|
* @property float $rate
|
||||||
* @property bool $is_digital_services
|
* @property bool $is_digital_services
|
||||||
* @property \Carbon\Carbon $effective_from
|
* @property Carbon $effective_from
|
||||||
* @property \Carbon\Carbon|null $effective_until
|
* @property Carbon|null $effective_until
|
||||||
* @property bool $is_active
|
* @property bool $is_active
|
||||||
* @property string|null $stripe_tax_rate_id
|
* @property string|null $stripe_tax_rate_id
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -16,7 +19,7 @@ use Illuminate\Support\Str;
|
||||||
* @property int $meter_id
|
* @property int $meter_id
|
||||||
* @property int $workspace_id
|
* @property int $workspace_id
|
||||||
* @property int $quantity
|
* @property int $quantity
|
||||||
* @property \Carbon\Carbon $event_at
|
* @property Carbon $event_at
|
||||||
* @property string|null $idempotency_key
|
* @property string|null $idempotency_key
|
||||||
* @property int|null $user_id
|
* @property int|null $user_id
|
||||||
* @property string|null $action
|
* @property string|null $action
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Models;
|
namespace Core\Mod\Commerce\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
|
@ -21,8 +22,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
* @property int|null $http_status_code
|
* @property int|null $http_status_code
|
||||||
* @property int|null $order_id
|
* @property int|null $order_id
|
||||||
* @property int|null $subscription_id
|
* @property int|null $subscription_id
|
||||||
* @property \Carbon\Carbon $received_at
|
* @property Carbon $received_at
|
||||||
* @property \Carbon\Carbon|null $processed_at
|
* @property Carbon|null $processed_at
|
||||||
*/
|
*/
|
||||||
class WebhookEvent extends Model
|
class WebhookEvent extends Model
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Invoice;
|
use Core\Mod\Commerce\Models\Invoice;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Refund;
|
use Core\Mod\Commerce\Models\Refund;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Notifications;
|
namespace Core\Mod\Commerce\Notifications;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Contracts\Orderable;
|
use Core\Mod\Commerce\Contracts\Orderable;
|
||||||
use Core\Mod\Commerce\Data\FraudAssessment;
|
use Core\Mod\Commerce\Data\FraudAssessment;
|
||||||
|
use Core\Mod\Commerce\Events\OrderPaid;
|
||||||
use Core\Mod\Commerce\Exceptions\CheckoutRateLimitException;
|
use Core\Mod\Commerce\Exceptions\CheckoutRateLimitException;
|
||||||
use Core\Mod\Commerce\Exceptions\FraudBlockedException;
|
use Core\Mod\Commerce\Exceptions\FraudBlockedException;
|
||||||
use Core\Mod\Commerce\Models\Coupon;
|
use Core\Mod\Commerce\Models\Coupon;
|
||||||
|
|
@ -13,9 +14,12 @@ use Core\Mod\Commerce\Models\Invoice;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Mod\Commerce\Models\OrderItem;
|
use Core\Mod\Commerce\Models\OrderItem;
|
||||||
use Core\Mod\Commerce\Models\Payment;
|
use Core\Mod\Commerce\Models\Payment;
|
||||||
|
use Core\Mod\Commerce\Models\Refund;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
|
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
|
||||||
|
use Core\Tenant\Models\Boost;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Services\EntitlementService;
|
use Core\Tenant\Services\EntitlementService;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
@ -123,7 +127,7 @@ class CommerceService
|
||||||
$order = Order::create([
|
$order = Order::create([
|
||||||
'orderable_type' => get_class($orderable),
|
'orderable_type' => get_class($orderable),
|
||||||
'orderable_id' => $orderable->id,
|
'orderable_id' => $orderable->id,
|
||||||
'user_id' => $orderable instanceof \Core\Tenant\Models\User ? $orderable->id : null,
|
'user_id' => $orderable instanceof User ? $orderable->id : null,
|
||||||
'order_number' => Order::generateOrderNumber(),
|
'order_number' => Order::generateOrderNumber(),
|
||||||
'status' => 'pending',
|
'status' => 'pending',
|
||||||
'billing_cycle' => $billingCycle,
|
'billing_cycle' => $billingCycle,
|
||||||
|
|
@ -361,7 +365,7 @@ class CommerceService
|
||||||
$order = Order::create([
|
$order = Order::create([
|
||||||
'orderable_type' => get_class($orderable),
|
'orderable_type' => get_class($orderable),
|
||||||
'orderable_id' => $orderable->id,
|
'orderable_id' => $orderable->id,
|
||||||
'user_id' => $orderable instanceof \Core\Tenant\Models\User ? $orderable->id : null,
|
'user_id' => $orderable instanceof User ? $orderable->id : null,
|
||||||
'order_number' => Order::generateOrderNumber(),
|
'order_number' => Order::generateOrderNumber(),
|
||||||
'status' => 'pending',
|
'status' => 'pending',
|
||||||
'billing_cycle' => 'onetime',
|
'billing_cycle' => 'onetime',
|
||||||
|
|
@ -437,7 +441,7 @@ class CommerceService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provision boosts for user-level orders
|
// Provision boosts for user-level orders
|
||||||
if ($order->orderable instanceof \Core\Tenant\Models\User) {
|
if ($order->orderable instanceof User) {
|
||||||
foreach ($order->items as $item) {
|
foreach ($order->items as $item) {
|
||||||
if ($item->item_type === 'boost') {
|
if ($item->item_type === 'boost') {
|
||||||
$quantity = $item->metadata['quantity'] ?? $item->quantity ?? 1;
|
$quantity = $item->metadata['quantity'] ?? $item->quantity ?? 1;
|
||||||
|
|
@ -450,28 +454,28 @@ class CommerceService
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch OrderPaid event for referral tracking and other listeners
|
// Dispatch OrderPaid event for referral tracking and other listeners
|
||||||
event(new \Core\Mod\Commerce\Events\OrderPaid($order, $payment));
|
event(new OrderPaid($order, $payment));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provision a boost for a user.
|
* Provision a boost for a user.
|
||||||
*/
|
*/
|
||||||
public function provisionBoostForUser(\Core\Tenant\Models\User $user, string $featureCode, int $quantity = 1, array $metadata = []): \Core\Tenant\Models\Boost
|
public function provisionBoostForUser(User $user, string $featureCode, int $quantity = 1, array $metadata = []): Boost
|
||||||
{
|
{
|
||||||
// Use ADD_LIMIT for quantity-based boosts, ENABLE for boolean boosts
|
// Use ADD_LIMIT for quantity-based boosts, ENABLE for boolean boosts
|
||||||
$boostType = $quantity > 1 || $this->isQuantityBasedFeature($featureCode)
|
$boostType = $quantity > 1 || $this->isQuantityBasedFeature($featureCode)
|
||||||
? \Core\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT
|
? Boost::BOOST_TYPE_ADD_LIMIT
|
||||||
: \Core\Tenant\Models\Boost::BOOST_TYPE_ENABLE;
|
: Boost::BOOST_TYPE_ENABLE;
|
||||||
|
|
||||||
return \Core\Tenant\Models\Boost::create([
|
return Boost::create([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'workspace_id' => null,
|
'workspace_id' => null,
|
||||||
'feature_code' => $featureCode,
|
'feature_code' => $featureCode,
|
||||||
'boost_type' => $boostType,
|
'boost_type' => $boostType,
|
||||||
'duration_type' => \Core\Tenant\Models\Boost::DURATION_PERMANENT,
|
'duration_type' => Boost::DURATION_PERMANENT,
|
||||||
'limit_value' => $boostType === \Core\Tenant\Models\Boost::BOOST_TYPE_ADD_LIMIT ? $quantity : null,
|
'limit_value' => $boostType === Boost::BOOST_TYPE_ADD_LIMIT ? $quantity : null,
|
||||||
'status' => \Core\Tenant\Models\Boost::STATUS_ACTIVE,
|
'status' => Boost::STATUS_ACTIVE,
|
||||||
'starts_at' => now(),
|
'starts_at' => now(),
|
||||||
'metadata' => $metadata,
|
'metadata' => $metadata,
|
||||||
]);
|
]);
|
||||||
|
|
@ -599,7 +603,7 @@ class CommerceService
|
||||||
Payment $payment,
|
Payment $payment,
|
||||||
?float $amount = null,
|
?float $amount = null,
|
||||||
?string $reason = null
|
?string $reason = null
|
||||||
): \Core\Mod\Commerce\Models\Refund {
|
): Refund {
|
||||||
$amountCents = $amount
|
$amountCents = $amount
|
||||||
? (int) ($amount * 100)
|
? (int) ($amount * 100)
|
||||||
: (int) (($payment->amount - $payment->amount_refunded) * 100);
|
: (int) (($payment->amount - $payment->amount_refunded) * 100);
|
||||||
|
|
@ -660,7 +664,7 @@ class CommerceService
|
||||||
// For BTCPay, payment will be 'pending' as it requires customer action
|
// For BTCPay, payment will be 'pending' as it requires customer action
|
||||||
// This is expected - automatic retry won't work for crypto payments
|
// This is expected - automatic retry won't work for crypto payments
|
||||||
if ($payment->status === 'pending' && $paymentMethod->gateway === 'btcpay') {
|
if ($payment->status === 'pending' && $paymentMethod->gateway === 'btcpay') {
|
||||||
\Illuminate\Support\Facades\Log::info('BTCPay invoice created for retry - requires customer payment', [
|
Log::info('BTCPay invoice created for retry - requires customer payment', [
|
||||||
'invoice_id' => $invoice->id,
|
'invoice_id' => $invoice->id,
|
||||||
'payment_id' => $payment->id,
|
'payment_id' => $payment->id,
|
||||||
]);
|
]);
|
||||||
|
|
@ -668,7 +672,7 @@ class CommerceService
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
\Illuminate\Support\Facades\Log::error('Invoice payment retry failed', [
|
Log::error('Invoice payment retry failed', [
|
||||||
'invoice_id' => $invoice->id,
|
'invoice_id' => $invoice->id,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ use Core\Mod\Commerce\Models\CouponUsage;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -217,7 +218,7 @@ class CouponService
|
||||||
/**
|
/**
|
||||||
* Get usage history for a coupon.
|
* Get usage history for a coupon.
|
||||||
*/
|
*/
|
||||||
public function getUsageHistory(Coupon $coupon, int $limit = 50): \Illuminate\Database\Eloquent\Collection
|
public function getUsageHistory(Coupon $coupon, int $limit = 50): Collection
|
||||||
{
|
{
|
||||||
return $coupon->usages()
|
return $coupon->usages()
|
||||||
->with(['workspace', 'order'])
|
->with(['workspace', 'order'])
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\CreditNote;
|
use Core\Mod\Commerce\Models\CreditNote;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Barryvdh\DomPDF\Facade\Pdf;
|
use Barryvdh\DomPDF\Facade\Pdf;
|
||||||
|
|
@ -9,8 +11,11 @@ use Core\Mod\Commerce\Models\InvoiceItem;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Mod\Commerce\Models\Payment;
|
use Core\Mod\Commerce\Models\Payment;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
use Illuminate\Support\Facades\Mail;
|
use Illuminate\Support\Facades\Mail;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice generation and management service.
|
* Invoice generation and management service.
|
||||||
|
|
@ -174,7 +179,7 @@ class InvoiceService
|
||||||
/**
|
/**
|
||||||
* Get PDF download response.
|
* Get PDF download response.
|
||||||
*/
|
*/
|
||||||
public function downloadPdf(Invoice $invoice): \Symfony\Component\HttpFoundation\StreamedResponse
|
public function downloadPdf(Invoice $invoice): StreamedResponse
|
||||||
{
|
{
|
||||||
$path = $this->getPdf($invoice);
|
$path = $this->getPdf($invoice);
|
||||||
|
|
||||||
|
|
@ -219,7 +224,7 @@ class InvoiceService
|
||||||
/**
|
/**
|
||||||
* Get invoices for a workspace.
|
* Get invoices for a workspace.
|
||||||
*/
|
*/
|
||||||
public function getForWorkspace(Workspace $workspace, int $limit = 25): \Illuminate\Pagination\LengthAwarePaginator
|
public function getForWorkspace(Workspace $workspace, int $limit = 25): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
return $workspace->invoices()
|
return $workspace->invoices()
|
||||||
->with('items')
|
->with('items')
|
||||||
|
|
@ -230,7 +235,7 @@ class InvoiceService
|
||||||
/**
|
/**
|
||||||
* Get unpaid invoices for a workspace.
|
* Get unpaid invoices for a workspace.
|
||||||
*/
|
*/
|
||||||
public function getUnpaidForWorkspace(Workspace $workspace): \Illuminate\Database\Eloquent\Collection
|
public function getUnpaidForWorkspace(Workspace $workspace): Collection
|
||||||
{
|
{
|
||||||
return $workspace->invoices()
|
return $workspace->invoices()
|
||||||
->pending()
|
->pending()
|
||||||
|
|
@ -241,7 +246,7 @@ class InvoiceService
|
||||||
/**
|
/**
|
||||||
* Get overdue invoices for a workspace.
|
* Get overdue invoices for a workspace.
|
||||||
*/
|
*/
|
||||||
public function getOverdueForWorkspace(Workspace $workspace): \Illuminate\Database\Eloquent\Collection
|
public function getOverdueForWorkspace(Workspace $workspace): Collection
|
||||||
{
|
{
|
||||||
return $workspace->invoices()
|
return $workspace->invoices()
|
||||||
->pending()
|
->pending()
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ use Core\Mod\Commerce\Models\Payment;
|
||||||
use Core\Mod\Commerce\Models\PaymentMethod;
|
use Core\Mod\Commerce\Models\PaymentMethod;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Http\Client\Response;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
|
@ -633,7 +634,7 @@ class BTCPayGateway implements PaymentGatewayContract
|
||||||
/**
|
/**
|
||||||
* Extract a safe error message from a failed response.
|
* Extract a safe error message from a failed response.
|
||||||
*/
|
*/
|
||||||
protected function sanitiseErrorMessage(\Illuminate\Http\Client\Response $response): string
|
protected function sanitiseErrorMessage(Response $response): string
|
||||||
{
|
{
|
||||||
$json = $response->json();
|
$json = $response->json();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services\PaymentGateway;
|
namespace Core\Mod\Commerce\Services\PaymentGateway;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services\PaymentGateway;
|
namespace Core\Mod\Commerce\Services\PaymentGateway;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Mod\Commerce\Models\Payment;
|
use Core\Mod\Commerce\Models\Payment;
|
||||||
use Core\Mod\Commerce\Models\PaymentMethod;
|
use Core\Mod\Commerce\Models\PaymentMethod;
|
||||||
|
|
@ -9,7 +12,14 @@ use Core\Mod\Commerce\Models\Refund;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Facades\Log;
|
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\StripeClient;
|
||||||
|
use Stripe\Webhook;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stripe payment gateway implementation.
|
* Stripe payment gateway implementation.
|
||||||
|
|
@ -143,36 +153,36 @@ class StripeGateway implements PaymentGatewayContract
|
||||||
'session_id' => $session->id,
|
'session_id' => $session->id,
|
||||||
'checkout_url' => $session->url,
|
'checkout_url' => $session->url,
|
||||||
];
|
];
|
||||||
} catch (\Stripe\Exception\CardException $e) {
|
} catch (CardException $e) {
|
||||||
Log::warning('Stripe checkout failed: card error', [
|
Log::warning('Stripe checkout failed: card error', [
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'code' => $e->getStripeCode(),
|
'code' => $e->getStripeCode(),
|
||||||
]);
|
]);
|
||||||
throw new \RuntimeException('Payment card error: '.$e->getMessage(), 0, $e);
|
throw new \RuntimeException('Payment card error: '.$e->getMessage(), 0, $e);
|
||||||
} catch (\Stripe\Exception\RateLimitException $e) {
|
} catch (RateLimitException $e) {
|
||||||
Log::error('Stripe checkout failed: rate limit', [
|
Log::error('Stripe checkout failed: rate limit', [
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
]);
|
]);
|
||||||
throw new \RuntimeException('Payment service temporarily unavailable. Please try again.', 0, $e);
|
throw new \RuntimeException('Payment service temporarily unavailable. Please try again.', 0, $e);
|
||||||
} catch (\Stripe\Exception\InvalidRequestException $e) {
|
} catch (InvalidRequestException $e) {
|
||||||
Log::error('Stripe checkout failed: invalid request', [
|
Log::error('Stripe checkout failed: invalid request', [
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
'param' => $e->getStripeParam(),
|
'param' => $e->getStripeParam(),
|
||||||
]);
|
]);
|
||||||
throw new \RuntimeException('Unable to create checkout session. Please contact support.', 0, $e);
|
throw new \RuntimeException('Unable to create checkout session. Please contact support.', 0, $e);
|
||||||
} catch (\Stripe\Exception\AuthenticationException $e) {
|
} catch (AuthenticationException $e) {
|
||||||
Log::critical('Stripe authentication failed - check API keys', [
|
Log::critical('Stripe authentication failed - check API keys', [
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
]);
|
]);
|
||||||
throw new \RuntimeException('Payment service configuration error. Please contact support.', 0, $e);
|
throw new \RuntimeException('Payment service configuration error. Please contact support.', 0, $e);
|
||||||
} catch (\Stripe\Exception\ApiConnectionException $e) {
|
} catch (ApiConnectionException $e) {
|
||||||
Log::error('Stripe checkout failed: connection error', [
|
Log::error('Stripe checkout failed: connection error', [
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
]);
|
]);
|
||||||
throw new \RuntimeException('Unable to connect to payment service. Please try again.', 0, $e);
|
throw new \RuntimeException('Unable to connect to payment service. Please try again.', 0, $e);
|
||||||
} catch (\Stripe\Exception\ApiErrorException $e) {
|
} catch (ApiErrorException $e) {
|
||||||
Log::error('Stripe checkout failed: API error', [
|
Log::error('Stripe checkout failed: API error', [
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
|
|
@ -338,10 +348,10 @@ class StripeGateway implements PaymentGatewayContract
|
||||||
'gateway_customer_id' => $customerId,
|
'gateway_customer_id' => $customerId,
|
||||||
'gateway_price_id' => $priceId,
|
'gateway_price_id' => $priceId,
|
||||||
'status' => $this->mapSubscriptionStatus($stripeSubscription->status),
|
'status' => $this->mapSubscriptionStatus($stripeSubscription->status),
|
||||||
'current_period_start' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_start),
|
'current_period_start' => Carbon::createFromTimestamp($stripeSubscription->current_period_start),
|
||||||
'current_period_end' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_end),
|
'current_period_end' => Carbon::createFromTimestamp($stripeSubscription->current_period_end),
|
||||||
'trial_ends_at' => $stripeSubscription->trial_end
|
'trial_ends_at' => $stripeSubscription->trial_end
|
||||||
? \Carbon\Carbon::createFromTimestamp($stripeSubscription->trial_end)
|
? Carbon::createFromTimestamp($stripeSubscription->trial_end)
|
||||||
: null,
|
: null,
|
||||||
'metadata' => ['stripe_subscription' => $stripeSubscription->toArray()],
|
'metadata' => ['stripe_subscription' => $stripeSubscription->toArray()],
|
||||||
]);
|
]);
|
||||||
|
|
@ -376,8 +386,8 @@ class StripeGateway implements PaymentGatewayContract
|
||||||
'gateway_price_id' => $options['price_id'] ?? $subscription->gateway_price_id,
|
'gateway_price_id' => $options['price_id'] ?? $subscription->gateway_price_id,
|
||||||
'status' => $this->mapSubscriptionStatus($stripeSubscription->status),
|
'status' => $this->mapSubscriptionStatus($stripeSubscription->status),
|
||||||
'cancel_at_period_end' => $stripeSubscription->cancel_at_period_end,
|
'cancel_at_period_end' => $stripeSubscription->cancel_at_period_end,
|
||||||
'current_period_start' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_start),
|
'current_period_start' => Carbon::createFromTimestamp($stripeSubscription->current_period_start),
|
||||||
'current_period_end' => \Carbon\Carbon::createFromTimestamp($stripeSubscription->current_period_end),
|
'current_period_end' => Carbon::createFromTimestamp($stripeSubscription->current_period_end),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return $subscription->fresh();
|
return $subscription->fresh();
|
||||||
|
|
@ -544,7 +554,7 @@ class StripeGateway implements PaymentGatewayContract
|
||||||
public function verifyWebhookSignature(string $payload, string $signature): bool
|
public function verifyWebhookSignature(string $payload, string $signature): bool
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
\Stripe\Webhook::constructEvent($payload, $signature, $this->webhookSecret);
|
Webhook::constructEvent($payload, $signature, $this->webhookSecret);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\PaymentMethod;
|
use Core\Mod\Commerce\Models\PaymentMethod;
|
||||||
use Core\Mod\Commerce\Services\PaymentGateway\StripeGateway;
|
use Core\Mod\Commerce\Services\PaymentGateway\StripeGateway;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
|
|
@ -253,7 +256,7 @@ class PaymentMethodService
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$expiry = \Carbon\Carbon::createFromDate(
|
$expiry = Carbon::createFromDate(
|
||||||
$paymentMethod->exp_year,
|
$paymentMethod->exp_year,
|
||||||
$paymentMethod->exp_month
|
$paymentMethod->exp_month
|
||||||
)->endOfMonth();
|
)->endOfMonth();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Payment;
|
use Core\Mod\Commerce\Models\Payment;
|
||||||
use Core\Mod\Commerce\Models\Refund;
|
use Core\Mod\Commerce\Models\Refund;
|
||||||
use Core\Mod\Commerce\Notifications\RefundProcessed;
|
use Core\Mod\Commerce\Notifications\RefundProcessed;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
|
@ -160,7 +163,7 @@ class RefundService
|
||||||
/**
|
/**
|
||||||
* Get refund history for a payment.
|
* Get refund history for a payment.
|
||||||
*/
|
*/
|
||||||
public function getRefundsForPayment(Payment $payment): \Illuminate\Database\Eloquent\Collection
|
public function getRefundsForPayment(Payment $payment): Collection
|
||||||
{
|
{
|
||||||
return $payment->refunds()->latest()->get();
|
return $payment->refunds()->latest()->get();
|
||||||
}
|
}
|
||||||
|
|
@ -168,7 +171,7 @@ class RefundService
|
||||||
/**
|
/**
|
||||||
* Get all refunds for a workspace.
|
* Get all refunds for a workspace.
|
||||||
*/
|
*/
|
||||||
public function getRefundsForWorkspace(int $workspaceId): \Illuminate\Database\Eloquent\Collection
|
public function getRefundsForWorkspace(int $workspaceId): Collection
|
||||||
{
|
{
|
||||||
return Refund::query()
|
return Refund::query()
|
||||||
->whereHas('payment', function ($query) use ($workspaceId) {
|
->whereHas('payment', function ($query) use ($workspaceId) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
@ -9,6 +11,7 @@ use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Models\WorkspacePackage;
|
use Core\Tenant\Models\WorkspacePackage;
|
||||||
use Core\Tenant\Services\EntitlementService;
|
use Core\Tenant\Services\EntitlementService;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
|
@ -414,7 +417,7 @@ class SubscriptionService
|
||||||
/**
|
/**
|
||||||
* Get subscriptions expiring soon (for renewal reminders).
|
* Get subscriptions expiring soon (for renewal reminders).
|
||||||
*/
|
*/
|
||||||
public function getExpiringSoon(int $days = 7): \Illuminate\Database\Eloquent\Collection
|
public function getExpiringSoon(int $days = 7): Collection
|
||||||
{
|
{
|
||||||
return Subscription::query()
|
return Subscription::query()
|
||||||
->active()
|
->active()
|
||||||
|
|
@ -428,7 +431,7 @@ class SubscriptionService
|
||||||
/**
|
/**
|
||||||
* Get subscriptions that have failed payment and need dunning.
|
* Get subscriptions that have failed payment and need dunning.
|
||||||
*/
|
*/
|
||||||
public function getFailedPayments(): \Illuminate\Database\Eloquent\Collection
|
public function getFailedPayments(): Collection
|
||||||
{
|
{
|
||||||
return Subscription::query()
|
return Subscription::query()
|
||||||
->where('status', 'past_due')
|
->where('status', 'past_due')
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Contracts\Orderable;
|
use Core\Mod\Commerce\Contracts\Orderable;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\Services;
|
namespace Core\Mod\Commerce\Services;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
|
@ -15,6 +17,7 @@ use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Stripe\StripeClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Usage-based billing service.
|
* Usage-based billing service.
|
||||||
|
|
@ -331,7 +334,7 @@ class UsageBillingService
|
||||||
Subscription $subscription,
|
Subscription $subscription,
|
||||||
SubscriptionUsage $usage
|
SubscriptionUsage $usage
|
||||||
): void {
|
): void {
|
||||||
$stripe = new \Stripe\StripeClient(config('commerce.gateways.stripe.secret'));
|
$stripe = new StripeClient(config('commerce.gateways.stripe.secret'));
|
||||||
|
|
||||||
// Find the subscription item for this price
|
// Find the subscription item for this price
|
||||||
$stripeSubscription = $stripe->subscriptions->retrieve(
|
$stripeSubscription = $stripe->subscriptions->retrieve(
|
||||||
|
|
@ -417,7 +420,7 @@ class UsageBillingService
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stripe = new \Stripe\StripeClient($secret);
|
$stripe = new StripeClient($secret);
|
||||||
|
|
||||||
// Create or update product in Stripe
|
// Create or update product in Stripe
|
||||||
$product = $stripe->products->create([
|
$product = $stripe->products->create([
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Core\Mod\Commerce\Services;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Core\Mod\Commerce\Models\WebhookEvent;
|
use Core\Mod\Commerce\Models\WebhookEvent;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Http\Response;
|
use Illuminate\Http\Response;
|
||||||
|
|
@ -311,7 +312,7 @@ class WebhookLogger
|
||||||
/**
|
/**
|
||||||
* Get recent failed events for debugging.
|
* Get recent failed events for debugging.
|
||||||
*/
|
*/
|
||||||
public function getRecentFailures(string $gateway, int $limit = 10): \Illuminate\Database\Eloquent\Collection
|
public function getRecentFailures(string $gateway, int $limit = 10): Collection
|
||||||
{
|
{
|
||||||
return WebhookEvent::forGateway($gateway)
|
return WebhookEvent::forGateway($gateway)
|
||||||
->failed()
|
->failed()
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Admin;
|
namespace Core\Mod\Commerce\View\Modal\Admin;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\Coupon;
|
use Core\Mod\Commerce\Models\Coupon;
|
||||||
use Core\Mod\Commerce\Services\CouponService;
|
use Core\Mod\Commerce\Services\CouponService;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
|
|
@ -334,8 +336,8 @@ class CouponManager extends Component
|
||||||
'max_uses_per_workspace' => $this->bulk_max_uses_per_workspace,
|
'max_uses_per_workspace' => $this->bulk_max_uses_per_workspace,
|
||||||
'duration' => $this->bulk_duration,
|
'duration' => $this->bulk_duration,
|
||||||
'duration_months' => $this->bulk_duration === 'repeating' ? $this->bulk_duration_months : null,
|
'duration_months' => $this->bulk_duration === 'repeating' ? $this->bulk_duration_months : null,
|
||||||
'valid_from' => $this->bulk_valid_from ? \Carbon\Carbon::parse($this->bulk_valid_from) : null,
|
'valid_from' => $this->bulk_valid_from ? Carbon::parse($this->bulk_valid_from) : null,
|
||||||
'valid_until' => $this->bulk_valid_until ? \Carbon\Carbon::parse($this->bulk_valid_until) : null,
|
'valid_until' => $this->bulk_valid_until ? Carbon::parse($this->bulk_valid_until) : null,
|
||||||
'is_active' => $this->bulk_is_active,
|
'is_active' => $this->bulk_is_active,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -399,8 +401,8 @@ class CouponManager extends Component
|
||||||
'max_uses_per_workspace' => $this->max_uses_per_workspace,
|
'max_uses_per_workspace' => $this->max_uses_per_workspace,
|
||||||
'duration' => $this->duration,
|
'duration' => $this->duration,
|
||||||
'duration_months' => $this->duration === 'repeating' ? $this->duration_months : null,
|
'duration_months' => $this->duration === 'repeating' ? $this->duration_months : null,
|
||||||
'valid_from' => $this->valid_from ? \Carbon\Carbon::parse($this->valid_from) : null,
|
'valid_from' => $this->valid_from ? Carbon::parse($this->valid_from) : null,
|
||||||
'valid_until' => $this->valid_until ? \Carbon\Carbon::parse($this->valid_until) : null,
|
'valid_until' => $this->valid_until ? Carbon::parse($this->valid_until) : null,
|
||||||
'is_active' => $this->is_active,
|
'is_active' => $this->is_active,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -587,7 +589,7 @@ class CouponManager extends Component
|
||||||
[
|
[
|
||||||
'lines' => array_filter([
|
'lines' => array_filter([
|
||||||
['bold' => $c->name],
|
['bold' => $c->name],
|
||||||
$c->description ? ['muted' => \Illuminate\Support\Str::limit($c->description, 30)] : null,
|
$c->description ? ['muted' => Str::limit($c->description, 30)] : null,
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
['lines' => $discountLines],
|
['lines' => $discountLines],
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ use Core\Mod\Commerce\Models\Coupon;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Illuminate\Contracts\View\View;
|
use Illuminate\Contracts\View\View;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
|
|
@ -42,7 +43,7 @@ class Dashboard extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Computed]
|
#[Computed]
|
||||||
public function recentOrders(): \Illuminate\Database\Eloquent\Collection
|
public function recentOrders(): Collection
|
||||||
{
|
{
|
||||||
return Order::with('workspace')
|
return Order::with('workspace')
|
||||||
->latest()
|
->latest()
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Admin;
|
namespace Core\Mod\Commerce\View\Modal\Admin;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\Referral;
|
use Core\Mod\Commerce\Models\Referral;
|
||||||
use Core\Mod\Commerce\Models\ReferralCode;
|
use Core\Mod\Commerce\Models\ReferralCode;
|
||||||
use Core\Mod\Commerce\Models\ReferralCommission;
|
use Core\Mod\Commerce\Models\ReferralCommission;
|
||||||
|
|
@ -309,8 +310,8 @@ class ReferralManager extends Component
|
||||||
'commission_rate' => $this->codeCommissionRate,
|
'commission_rate' => $this->codeCommissionRate,
|
||||||
'cookie_days' => $this->codeCookieDays,
|
'cookie_days' => $this->codeCookieDays,
|
||||||
'max_uses' => $this->codeMaxUses,
|
'max_uses' => $this->codeMaxUses,
|
||||||
'valid_from' => $this->codeValidFrom ? \Carbon\Carbon::parse($this->codeValidFrom) : null,
|
'valid_from' => $this->codeValidFrom ? Carbon::parse($this->codeValidFrom) : null,
|
||||||
'valid_until' => $this->codeValidUntil ? \Carbon\Carbon::parse($this->codeValidUntil) : null,
|
'valid_until' => $this->codeValidUntil ? Carbon::parse($this->codeValidUntil) : null,
|
||||||
'is_active' => $this->codeIsActive,
|
'is_active' => $this->codeIsActive,
|
||||||
'campaign_name' => $this->codeCampaignName,
|
'campaign_name' => $this->codeCampaignName,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ namespace Core\Mod\Commerce\View\Modal\Admin;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
use Core\Mod\Commerce\Services\SubscriptionService;
|
use Core\Mod\Commerce\Services\SubscriptionService;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Title;
|
use Livewire\Attributes\Title;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
@ -150,7 +151,7 @@ class SubscriptionManager extends Component
|
||||||
$count = Subscription::whereIn('id', $this->selected)
|
$count = Subscription::whereIn('id', $this->selected)
|
||||||
->whereNotNull('current_period_end')
|
->whereNotNull('current_period_end')
|
||||||
->update([
|
->update([
|
||||||
'current_period_end' => \Illuminate\Support\Facades\DB::raw('DATE_ADD(current_period_end, INTERVAL 30 DAY)'),
|
'current_period_end' => DB::raw('DATE_ADD(current_period_end, INTERVAL 30 DAY)'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
session()->flash('message', __('commerce::commerce.bulk.period_extended', ['count' => $count, 'days' => 30]));
|
session()->flash('message', __('commerce::commerce.bulk.period_extended', ['count' => $count, 'days' => 30]));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Coupon;
|
use Core\Mod\Commerce\Models\Coupon;
|
||||||
|
|
@ -12,9 +14,11 @@ use Core\Mod\Commerce\Services\CurrencyService;
|
||||||
use Core\Mod\Commerce\Services\TaxService;
|
use Core\Mod\Commerce\Services\TaxService;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Database\Eloquent\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Livewire\Attributes\Computed;
|
use Livewire\Attributes\Computed;
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
|
use Livewire\Attributes\On;
|
||||||
use Livewire\Attributes\Url;
|
use Livewire\Attributes\Url;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
|
|
@ -143,7 +147,7 @@ class CheckoutPage extends Component
|
||||||
/**
|
/**
|
||||||
* Handle currency change event from CurrencySelector component.
|
* Handle currency change event from CurrencySelector component.
|
||||||
*/
|
*/
|
||||||
#[\Livewire\Attributes\On('currency-changed')]
|
#[On('currency-changed')]
|
||||||
public function onCurrencyChanged(string $currency): void
|
public function onCurrencyChanged(string $currency): void
|
||||||
{
|
{
|
||||||
$this->displayCurrency = $currency;
|
$this->displayCurrency = $currency;
|
||||||
|
|
@ -181,7 +185,7 @@ class CheckoutPage extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
#[Computed]
|
#[Computed]
|
||||||
public function packages(): \Illuminate\Database\Eloquent\Collection
|
public function packages(): Collection
|
||||||
{
|
{
|
||||||
return Package::active()
|
return Package::active()
|
||||||
->public()
|
->public()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
@ -9,6 +11,7 @@ use Illuminate\Auth\Events\Registered;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Hash;
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
use Livewire\Attributes\Layout;
|
use Livewire\Attributes\Layout;
|
||||||
use Livewire\Attributes\Validate;
|
use Livewire\Attributes\Validate;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
@ -156,9 +159,9 @@ class CheckoutSuccess extends Component
|
||||||
*/
|
*/
|
||||||
protected function generateUniqueSlug(string $name): string
|
protected function generateUniqueSlug(string $name): string
|
||||||
{
|
{
|
||||||
$baseSlug = \Illuminate\Support\Str::slug($name);
|
$baseSlug = Str::slug($name);
|
||||||
if (str_contains($baseSlug, '@')) {
|
if (str_contains($baseSlug, '@')) {
|
||||||
$baseSlug = \Illuminate\Support\Str::slug(\Illuminate\Support\Str::before($name, '@'));
|
$baseSlug = Str::slug(Str::before($name, '@'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$slug = $baseSlug;
|
$slug = $baseSlug;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Services\CommerceService;
|
use Core\Mod\Commerce\Services\CommerceService;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\PaymentMethod;
|
use Core\Mod\Commerce\Models\PaymentMethod;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription as SubscriptionModel;
|
use Core\Mod\Commerce\Models\Subscription as SubscriptionModel;
|
||||||
use Core\Mod\Commerce\Notifications\SubscriptionCancelled;
|
use Core\Mod\Commerce\Notifications\SubscriptionCancelled;
|
||||||
use Core\Mod\Commerce\Services\CommerceService;
|
use Core\Mod\Commerce\Services\CommerceService;
|
||||||
use Core\Mod\Commerce\Services\SubscriptionService;
|
use Core\Mod\Commerce\Services\SubscriptionService;
|
||||||
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
@ -123,7 +126,7 @@ class Subscription extends Component
|
||||||
|
|
||||||
// Notify user
|
// Notify user
|
||||||
$user = Auth::user();
|
$user = Auth::user();
|
||||||
if ($user instanceof \Core\Tenant\Models\User) {
|
if ($user instanceof User) {
|
||||||
$user->notify(new SubscriptionCancelled($this->activeSubscription));
|
$user->notify(new SubscriptionCancelled($this->activeSubscription));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
namespace Core\Mod\Commerce\View\Modal\Web;
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,22 @@
|
||||||
<?php
|
<?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;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -10,25 +27,25 @@ use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
// Billing (user-facing hub pages)
|
// Billing (user-facing hub pages)
|
||||||
Route::prefix('hub/billing')->name('hub.billing.')->group(function () {
|
Route::prefix('hub/billing')->name('hub.billing.')->group(function () {
|
||||||
Route::get('/', \Core\Mod\Commerce\View\Modal\Web\Dashboard::class)->name('index');
|
Route::get('/', Dashboard::class)->name('index');
|
||||||
Route::get('/invoices', \Core\Mod\Commerce\View\Modal\Web\Invoices::class)->name('invoices');
|
Route::get('/invoices', Invoices::class)->name('invoices');
|
||||||
Route::get('/invoices/{invoice}/pdf', [\Core\Mod\Commerce\Controllers\InvoiceController::class, 'pdf'])->name('invoices.pdf');
|
Route::get('/invoices/{invoice}/pdf', [InvoiceController::class, 'pdf'])->name('invoices.pdf');
|
||||||
Route::get('/invoices/{invoice}/view', [\Core\Mod\Commerce\Controllers\InvoiceController::class, 'view'])->name('invoices.view');
|
Route::get('/invoices/{invoice}/view', [InvoiceController::class, 'view'])->name('invoices.view');
|
||||||
Route::get('/payment-methods', \Core\Mod\Commerce\View\Modal\Web\PaymentMethods::class)->name('payment-methods');
|
Route::get('/payment-methods', PaymentMethods::class)->name('payment-methods');
|
||||||
Route::get('/subscription', \Core\Mod\Commerce\View\Modal\Web\Subscription::class)->name('subscription');
|
Route::get('/subscription', Subscription::class)->name('subscription');
|
||||||
Route::get('/change-plan', \Core\Mod\Commerce\View\Modal\Web\ChangePlan::class)->name('change-plan');
|
Route::get('/change-plan', ChangePlan::class)->name('change-plan');
|
||||||
Route::get('/affiliates', \Core\Mod\Commerce\View\Modal\Web\ReferralDashboard::class)->name('affiliates');
|
Route::get('/affiliates', ReferralDashboard::class)->name('affiliates');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Commerce management (admin only - Hades tier)
|
// Commerce management (admin only - Hades tier)
|
||||||
Route::prefix('hub/commerce')->name('hub.commerce.')->group(function () {
|
Route::prefix('hub/commerce')->name('hub.commerce.')->group(function () {
|
||||||
Route::get('/', \Core\Mod\Commerce\View\Modal\Admin\Dashboard::class)->name('dashboard');
|
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('/orders', OrderManager::class)->name('orders');
|
||||||
Route::get('/subscriptions', \Core\Mod\Commerce\View\Modal\Admin\SubscriptionManager::class)->name('subscriptions');
|
Route::get('/subscriptions', SubscriptionManager::class)->name('subscriptions');
|
||||||
Route::get('/coupons', \Core\Mod\Commerce\View\Modal\Admin\CouponManager::class)->name('coupons');
|
Route::get('/coupons', CouponManager::class)->name('coupons');
|
||||||
Route::get('/entities', \Core\Mod\Commerce\View\Modal\Admin\EntityManager::class)->name('entities');
|
Route::get('/entities', EntityManager::class)->name('entities');
|
||||||
Route::get('/permissions', \Core\Mod\Commerce\View\Modal\Admin\PermissionMatrixManager::class)->name('permissions');
|
Route::get('/permissions', PermissionMatrixManager::class)->name('permissions');
|
||||||
Route::get('/products', \Core\Mod\Commerce\View\Modal\Admin\ProductManager::class)->name('products');
|
Route::get('/products', ProductManager::class)->name('products');
|
||||||
Route::get('/credit-notes', \Core\Mod\Commerce\View\Modal\Admin\CreditNoteManager::class)->name('credit-notes');
|
Route::get('/credit-notes', CreditNoteManager::class)->name('credit-notes');
|
||||||
Route::get('/referrals', \Core\Mod\Commerce\View\Modal\Admin\ReferralManager::class)->name('referrals');
|
Route::get('/referrals', ReferralManager::class)->name('referrals');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
// Console commands are registered via Core modules
|
// Console commands are registered via Core modules
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Invoice;
|
use Core\Mod\Commerce\Models\Invoice;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
use Core\Mod\Commerce\Models\Payment;
|
use Core\Mod\Commerce\Models\Payment;
|
||||||
|
|
@ -9,9 +11,10 @@ use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Models\WorkspacePackage;
|
use Core\Tenant\Models\WorkspacePackage;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Coupon;
|
use Core\Mod\Commerce\Models\Coupon;
|
||||||
use Core\Mod\Commerce\Models\CouponUsage;
|
use Core\Mod\Commerce\Models\CouponUsage;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
@ -7,8 +9,9 @@ use Core\Mod\Commerce\Services\CouponService;
|
||||||
use Core\Tenant\Models\Package;
|
use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$this->user = User::factory()->create();
|
$this->user = User::factory()->create();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Models\Invoice;
|
use Core\Mod\Commerce\Models\Invoice;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
@ -12,10 +14,11 @@ use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Models\WorkspacePackage;
|
use Core\Tenant\Models\WorkspacePackage;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Events\SubscriptionRenewed;
|
use Core\Mod\Commerce\Events\SubscriptionRenewed;
|
||||||
use Core\Mod\Commerce\Jobs\ProcessSubscriptionRenewal;
|
use Core\Mod\Commerce\Jobs\ProcessSubscriptionRenewal;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
@ -11,10 +13,11 @@ use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Models\WorkspacePackage;
|
use Core\Tenant\Models\WorkspacePackage;
|
||||||
use Core\Tenant\Services\EntitlementService;
|
use Core\Tenant\Services\EntitlementService;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Event;
|
use Illuminate\Support\Facades\Event;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\Payment;
|
use Core\Mod\Commerce\Models\Payment;
|
||||||
use Core\Mod\Commerce\Models\Refund;
|
use Core\Mod\Commerce\Models\Refund;
|
||||||
use Core\Mod\Commerce\Notifications\RefundProcessed;
|
use Core\Mod\Commerce\Notifications\RefundProcessed;
|
||||||
use Core\Mod\Commerce\Services\CommerceService;
|
use Core\Mod\Commerce\Services\CommerceService;
|
||||||
|
use Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract;
|
||||||
use Core\Mod\Commerce\Services\RefundService;
|
use Core\Mod\Commerce\Services\RefundService;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Notification::fake();
|
Notification::fake();
|
||||||
|
|
@ -36,7 +40,7 @@ beforeEach(function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Mock the gateway
|
// Mock the gateway
|
||||||
$mockGateway = Mockery::mock(\Core\Mod\Commerce\Services\PaymentGateway\PaymentGatewayContract::class);
|
$mockGateway = Mockery::mock(PaymentGatewayContract::class);
|
||||||
$mockGateway->shouldReceive('refund')->andReturn([
|
$mockGateway->shouldReceive('refund')->andReturn([
|
||||||
'success' => true,
|
'success' => true,
|
||||||
'refund_id' => 're_test_123',
|
'refund_id' => 're_test_123',
|
||||||
|
|
@ -92,15 +96,15 @@ describe('RefundService', function () {
|
||||||
|
|
||||||
it('throws exception for refund exceeding available amount', function () {
|
it('throws exception for refund exceeding available amount', function () {
|
||||||
expect(fn () => $this->service->refund($this->payment, 150.00))
|
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 () {
|
it('throws exception for zero or negative amount', function () {
|
||||||
expect(fn () => $this->service->refund($this->payment, 0))
|
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))
|
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 () {
|
it('throws exception for non-succeeded payments', function () {
|
||||||
|
|
@ -114,7 +118,7 @@ describe('RefundService', function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(fn () => $this->service->refund($pendingPayment, 50.00))
|
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 () {
|
it('allows multiple partial refunds up to full amount', function () {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Core\Mod\Commerce\Exceptions\PauseLimitExceededException;
|
use Core\Mod\Commerce\Exceptions\PauseLimitExceededException;
|
||||||
use Core\Mod\Commerce\Models\Subscription;
|
use Core\Mod\Commerce\Models\Subscription;
|
||||||
|
|
@ -10,9 +12,10 @@ use Core\Tenant\Models\Package;
|
||||||
use Core\Tenant\Models\User;
|
use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Models\WorkspacePackage;
|
use Core\Tenant\Models\WorkspacePackage;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Cache::flush();
|
Cache::flush();
|
||||||
|
|
@ -462,7 +465,7 @@ describe('Proration calculations', function () {
|
||||||
$this->subscription->workspacePackage->setRelation('package', null);
|
$this->subscription->workspacePackage->setRelation('package', null);
|
||||||
|
|
||||||
expect(fn () => $this->service->previewPlanChange($this->subscription, $this->agencyPackage))
|
expect(fn () => $this->service->previewPlanChange($this->subscription, $this->agencyPackage))
|
||||||
->toThrow(\InvalidArgumentException::class, 'no current package');
|
->toThrow(InvalidArgumentException::class, 'no current package');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Models\TaxRate;
|
use Core\Mod\Commerce\Models\TaxRate;
|
||||||
use Core\Mod\Commerce\Services\TaxService;
|
use Core\Mod\Commerce\Services\TaxService;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
$this->workspace = Workspace::factory()->create([
|
$this->workspace = Workspace::factory()->create([
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@ declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Services\WebhookRateLimiter;
|
use Core\Mod\Commerce\Services\WebhookRateLimiter;
|
||||||
use Illuminate\Cache\RateLimiter;
|
use Illuminate\Cache\RateLimiter;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// WebhookRateLimiter Unit Tests
|
// WebhookRateLimiter Unit Tests
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
use Core\Mod\Commerce\Controllers\Webhooks\BTCPayWebhookController;
|
use Core\Mod\Commerce\Controllers\Webhooks\BTCPayWebhookController;
|
||||||
use Core\Mod\Commerce\Controllers\Webhooks\StripeWebhookController;
|
use Core\Mod\Commerce\Controllers\Webhooks\StripeWebhookController;
|
||||||
use Core\Mod\Commerce\Models\Order;
|
use Core\Mod\Commerce\Models\Order;
|
||||||
|
|
@ -19,10 +21,12 @@ use Core\Tenant\Models\User;
|
||||||
use Core\Tenant\Models\Workspace;
|
use Core\Tenant\Models\Workspace;
|
||||||
use Core\Tenant\Models\WorkspacePackage;
|
use Core\Tenant\Models\WorkspacePackage;
|
||||||
use Core\Tenant\Services\EntitlementService;
|
use Core\Tenant\Services\EntitlementService;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Notification;
|
use Illuminate\Support\Facades\Notification;
|
||||||
use WebhookPayloadValidationException;
|
use WebhookPayloadValidationException;
|
||||||
|
|
||||||
uses(\Illuminate\Foundation\Testing\RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
Notification::fake();
|
Notification::fake();
|
||||||
|
|
@ -124,7 +128,7 @@ describe('StripeWebhookController', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$request->headers->set('Stripe-Signature', 't=123,v1=abc');
|
$request->headers->set('Stripe-Signature', 't=123,v1=abc');
|
||||||
|
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
@ -177,7 +181,7 @@ describe('StripeWebhookController', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -213,7 +217,7 @@ describe('StripeWebhookController', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -270,7 +274,7 @@ describe('StripeWebhookController', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -332,7 +336,7 @@ describe('StripeWebhookController', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -368,7 +372,7 @@ describe('StripeWebhookController', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -460,7 +464,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$request->headers->set('BTCPay-Sig', 'valid_signature');
|
$request->headers->set('BTCPay-Sig', 'valid_signature');
|
||||||
|
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
@ -500,7 +504,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -523,7 +527,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -548,7 +552,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -575,7 +579,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -602,7 +606,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -629,7 +633,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -656,7 +660,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -683,7 +687,7 @@ describe('BTCPayWebhookController', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -840,7 +844,7 @@ describe('WebhookLogger service', function () {
|
||||||
it('extracts relevant headers', function () {
|
it('extracts relevant headers', function () {
|
||||||
$logger = new WebhookLogger;
|
$logger = new WebhookLogger;
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$request->headers->set('Stripe-Signature', 't=123,v1=secret_signature_here');
|
$request->headers->set('Stripe-Signature', 't=123,v1=secret_signature_here');
|
||||||
$request->headers->set('Content-Type', 'application/json');
|
$request->headers->set('Content-Type', 'application/json');
|
||||||
$request->headers->set('User-Agent', 'Stripe/1.0');
|
$request->headers->set('User-Agent', 'Stripe/1.0');
|
||||||
|
|
@ -1235,7 +1239,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$request->headers->set('BTCPay-Sig', 'valid_signature');
|
$request->headers->set('BTCPay-Sig', 'valid_signature');
|
||||||
|
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
@ -1262,7 +1266,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(400)
|
expect($response->getStatusCode())->toBe(400)
|
||||||
|
|
@ -1282,7 +1286,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
|
||||||
|
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(400)
|
expect($response->getStatusCode())->toBe(400)
|
||||||
|
|
@ -1313,7 +1317,7 @@ describe('BTCPayWebhookController payload validation integration', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -1388,7 +1392,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -1423,7 +1427,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
// First request - should process
|
// First request - should process
|
||||||
$request1 = new \Illuminate\Http\Request;
|
$request1 = new Request;
|
||||||
$response1 = $controller->handle($request1);
|
$response1 = $controller->handle($request1);
|
||||||
expect($response1->getStatusCode())->toBe(200);
|
expect($response1->getStatusCode())->toBe(200);
|
||||||
|
|
||||||
|
|
@ -1437,7 +1441,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
|
||||||
// Second request with same event ID - should be rejected as duplicate
|
// Second request with same event ID - should be rejected as duplicate
|
||||||
$webhookLogger2 = new WebhookLogger;
|
$webhookLogger2 = new WebhookLogger;
|
||||||
$controller2 = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger2);
|
$controller2 = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger2);
|
||||||
$request2 = new \Illuminate\Http\Request;
|
$request2 = new Request;
|
||||||
$response2 = $controller2->handle($request2);
|
$response2 = $controller2->handle($request2);
|
||||||
|
|
||||||
expect($response2->getStatusCode())->toBe(200)
|
expect($response2->getStatusCode())->toBe(200)
|
||||||
|
|
@ -1517,7 +1521,7 @@ describe('Webhook Idempotency (Replay Attack Protection)', function () {
|
||||||
$webhookLogger
|
$webhookLogger
|
||||||
);
|
);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -1574,7 +1578,7 @@ describe('BTCPay Payment Amount Verification', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -1610,7 +1614,7 @@ describe('BTCPay Payment Amount Verification', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -1653,7 +1657,7 @@ describe('BTCPay Payment Amount Verification', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
@ -1690,7 +1694,7 @@ describe('BTCPay Payment Amount Verification', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200)
|
expect($response->getStatusCode())->toBe(200)
|
||||||
|
|
@ -1727,7 +1731,7 @@ describe('BTCPay Payment Amount Verification', function () {
|
||||||
$webhookLogger = new WebhookLogger;
|
$webhookLogger = new WebhookLogger;
|
||||||
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
$controller = new BTCPayWebhookController($mockGateway, $mockCommerce, $webhookLogger);
|
||||||
|
|
||||||
$request = new \Illuminate\Http\Request;
|
$request = new Request;
|
||||||
$response = $controller->handle($request);
|
$response = $controller->handle($request);
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBe(200);
|
expect($response->getStatusCode())->toBe(200);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace Tests;
|
namespace Tests;
|
||||||
|
|
||||||
|
use Core\Mod\Commerce\Boot;
|
||||||
use Orchestra\Testbench\TestCase as BaseTestCase;
|
use Orchestra\Testbench\TestCase as BaseTestCase;
|
||||||
|
|
||||||
abstract class TestCase extends BaseTestCase
|
abstract class TestCase extends BaseTestCase
|
||||||
|
|
@ -11,7 +12,7 @@ abstract class TestCase extends BaseTestCase
|
||||||
protected function getPackageProviders($app): array
|
protected function getPackageProviders($app): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
\Core\Mod\Commerce\Boot::class,
|
Boot::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UseCase: Commerce Admin CRUD (Basic Flow)
|
* UseCase: Commerce Admin CRUD (Basic Flow)
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue