Skip to content

Commit cfad240

Browse files
committed
refactor: update subscription system to support new features and naming
1 parent 3cf1033 commit cfad240

16 files changed

+109
-48
lines changed

config/till.php

+7
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,12 @@
1919
'pricing_section_template' => 'till::livewire.pricing_section',
2020
'active_feature_icon' => null,
2121
'inactive_feature_icon' => null,
22+
'update_ledgers' => [
23+
'hourly' => true,
24+
'daily' => true,
25+
'weekly' => true,
26+
'monthly' => true,
27+
'yearly' => true,
28+
],
2229

2330
];

src/Actions/GetActivePlans.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace ArtisanBuild\Till\Actions;
44

5-
use ArtisanBuild\Till\Attributes\UnavailablePlan;
5+
use ArtisanBuild\Till\Attributes\ArchivedPlan;
66
use ReflectionClass;
77

88
class GetActivePlans
@@ -14,7 +14,7 @@ public function __invoke()
1414
return ($this->plans)()->filter(function ($plan) {
1515
$reflection = new ReflectionClass($plan);
1616

17-
return empty($reflection->getAttributes(UnavailablePlan::class));
17+
return empty($reflection->getAttributes(ArchivedPlan::class));
1818
});
1919

2020
}

src/Attributes/ArchivedPlan.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Attribute;
66

77
#[Attribute(Attribute::TARGET_CLASS)]
8-
class UnavailablePlan
8+
class ArchivedPlan
99
{
1010
public function __construct() {}
1111
}

src/Enums/TestPlans.php

+1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
enum TestPlans: string
66
{
77
case Unsubscribed = 'unsubscribed-plan';
8+
case CityTrollPlan = 'city-troll-plan';
89
case ExtraDefault = 'extra-default-plan';
910
}

src/Events/NewSubscriberAddedToDefaultPlan.php

+2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
use ArtisanBuild\Adverbs\Traits\SimpleApply;
66
use ArtisanBuild\Till\Actions\GetDefaultPlan;
77
use ArtisanBuild\Till\Actions\GetPlanById;
8+
use ArtisanBuild\Till\Attributes\ImpactsAbilities;
89
use ArtisanBuild\Till\Contracts\PlanInterface;
910
use ArtisanBuild\Till\States\SubscriberState;
1011
use ArtisanBuild\Till\Traits\HandlesSubscriptionChanges;
1112
use Carbon\CarbonInterface;
1213
use Thunk\Verbs\Attributes\Autodiscovery\StateId;
1314
use Thunk\Verbs\Event;
1415

16+
#[ImpactsAbilities]
1517
class NewSubscriberAddedToDefaultPlan extends Event
1618
{
1719
use HandlesSubscriptionChanges;

src/Events/SubcsriberChangedPlan.php

+2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
namespace ArtisanBuild\Till\Events;
44

5+
use ArtisanBuild\Till\Attributes\ImpactsAbilities;
56
use ArtisanBuild\Till\States\SubscriberState;
67
use ArtisanBuild\Till\Traits\HandlesSubscriptionChanges;
78
use Carbon\CarbonInterface;
89
use Thunk\Verbs\Attributes\Autodiscovery\StateId;
910
use Thunk\Verbs\Event;
1011

12+
#[ImpactsAbilities]
1113
class SubcsriberChangedPlan extends Event
1214
{
1315
use HandlesSubscriptionChanges;

src/Events/SubscriptionStarted.php

+2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
use ArtisanBuild\Adverbs\Traits\SimpleApply;
66
use ArtisanBuild\Till\Actions\GetPlanById;
7+
use ArtisanBuild\Till\Attributes\ImpactsAbilities;
78
use ArtisanBuild\Till\Contracts\PlanInterface;
89
use ArtisanBuild\Till\States\SubscriberState;
910
use ArtisanBuild\Till\Traits\HandlesSubscriptionChanges;
1011
use Carbon\CarbonInterface;
1112
use Thunk\Verbs\Attributes\Autodiscovery\StateId;
1213
use Thunk\Verbs\Event;
1314

15+
#[ImpactsAbilities]
1416
class SubscriptionStarted extends Event
1517
{
1618
use HandlesSubscriptionChanges;

src/Providers/TillServiceProvider.php

+7
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,14 @@
55
use ArtisanBuild\Till\Commands\CleanUpAfterTestingCommand;
66
use ArtisanBuild\Till\Commands\CreatePlanCommand;
77
use ArtisanBuild\Till\Commands\InstallCommand;
8+
use ArtisanBuild\Till\Listeners\AuthorizesLedgerTransactionsListener;
9+
use ArtisanBuild\Till\Listeners\ProcessesLedgerTransactionsListener;
10+
use ArtisanBuild\Till\Listeners\ResetsAbilities;
811
use ArtisanBuild\Till\Livewire\PricingSectionComponent;
912
use Illuminate\Support\ServiceProvider;
1013
use Livewire\Livewire;
1114
use Override;
15+
use Thunk\Verbs\Lifecycle\Dispatcher;
1216

1317
class TillServiceProvider extends ServiceProvider
1418
{
@@ -23,6 +27,9 @@ public function register(): void
2327

2428
public function boot(): void
2529
{
30+
app(Dispatcher::class)->register(new AuthorizesLedgerTransactionsListener);
31+
app(Dispatcher::class)->register(new ProcessesLedgerTransactionsListener);
32+
app(Dispatcher::class)->register(new ResetsAbilities);
2633
if ($this->app->runningInConsole()) {
2734
$this->commands([
2835
InstallCommand::class,

src/States/SubscriberState.php

+45
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
namespace ArtisanBuild\Till\States;
44

5+
use ArtisanBuild\Till\Actions\GetPlanById;
6+
use ArtisanBuild\Till\Contracts\PlanInterface;
7+
use ArtisanBuild\Till\SubscriptionPlans\Ledgers;
58
use Carbon\CarbonInterface;
9+
use Illuminate\Support\Facades\Context;
610
use Thunk\Verbs\State;
711

812
class SubscriberState extends State
@@ -12,4 +16,45 @@ class SubscriberState extends State
1216
public ?CarbonInterface $renews_at = null;
1317

1418
public ?CarbonInterface $expires_at = null;
19+
20+
public array $wallet = [];
21+
22+
public array $transactions = [];
23+
24+
public function plan(): PlanInterface
25+
{
26+
return app(GetPlanById::class)($this->plan_id);
27+
}
28+
29+
public function spend(Ledgers $ledger, int $amount = 1): void
30+
{
31+
foreach (range(1, $amount) as $times) {
32+
$this->transactions[] = ['ledger' => $ledger->name, 'at' => now()->timestamp];
33+
}
34+
if (Context::has('wallet'.$ledger->name) && data_get($this->wallet, $ledger->name) !== null) {
35+
$this->wallet[$ledger->name] -= Context::get('wallet'.$ledger->name);
36+
Context::forget('wallet'.$ledger->name);
37+
}
38+
}
39+
40+
public function can(string $action, array $arguments): bool
41+
{
42+
$arguments['state'] = $this;
43+
44+
return app($action)(...$arguments);
45+
}
46+
47+
public function deposit(string $ledger, int|float $amount = 1): void
48+
{
49+
if (array_key_exists($ledger, $this->wallet)) {
50+
$this->wallet[$ledger] += $amount;
51+
} else {
52+
$this->wallet[$ledger] = $amount;
53+
}
54+
}
55+
56+
public function withdraw(string $ledger, int|float $amount = 1): void
57+
{
58+
$this->wallet[$ledger] -= $amount;
59+
}
1560
}

src/SubscriptionPlans/Abilities/AddSeats.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace ArtisanBuild\Till\SubscriptionPlans\Abilities;
44

5+
use ArtisanBuild\Till\Actions\CacheAbility;
56
use Illuminate\Support\Facades\Auth;
67

78
class AddSeats
@@ -20,6 +21,6 @@ public function __invoke(?int $limit = null)
2021
return false;
2122
}
2223

23-
return Auth::user()->currentTeam->allUsers()->count() < $limit;
24+
return app(CacheAbility::class)('till-add-seats', Auth::user()->currentTeam->allUsers()->count() < $limit);
2425
}
2526
}

src/SubscriptionPlans/BasePlan.php

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public function getId(): string
2626
return $this->id;
2727
}
2828

29+
public function wallet(): array
30+
{
31+
return [];
32+
}
33+
2934
public function subscribeUrl()
3035
{
3136
return '#';

src/SubscriptionPlans/CityTrollPlan.php

+12-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace ArtisanBuild\Till\SubscriptionPlans;
44

55
use ArtisanBuild\Till\Attributes\TeamPlan;
6-
use ArtisanBuild\Till\Contracts\PlanInterface;
6+
use ArtisanBuild\Till\Enums\LedgerPeriods;
77
use ArtisanBuild\Till\Enums\PlanTerms;
8+
use ArtisanBuild\Till\SubscriptionPlans\Abilities\AddSeats;
89

910
#[TeamPlan]
10-
class StartupPlan extends BasePlan
11+
class CityTrollPlan extends BasePlan
1112
{
1213
/**
1314
* Prices
@@ -46,9 +47,9 @@ class StartupPlan extends BasePlan
4647
'inset' => '', // https://fluxui.dev/components/badge#inset
4748
];
4849

49-
public string $heading = 'Startup';
50+
public string $heading = 'City Troll';
5051

51-
public string $subheading = 'Everything you need for your growing company';
52+
public string $subheading = 'Everything you need to annoy your neighbors';
5253

5354
/**
5455
* Define Your Features
@@ -68,7 +69,13 @@ class StartupPlan extends BasePlan
6869
['text' => '5 Subtweets / Day', 'icon' => null],
6970
['text' => '10 Guy Replies / Week', 'icon' => null],
7071
['text' => '5 Rick Rolls / Month', 'icon' => null],
72+
];
7173

74+
public array $ledgers = [
75+
['ledger' => Ledgers::Memes, 'limit' => 5, 'period' => LedgerPeriods::Hour],
76+
['ledger' => Ledgers::SubTweets, 'limit' => 5, 'period' => LedgerPeriods::Day],
77+
['ledger' => Ledgers::GuyReplies, 'limit' => 5, 'period' => LedgerPeriods::Week],
78+
['ledger' => Ledgers::RickRolls, 'limit' => 5, 'period' => LedgerPeriods::Month],
7279
];
7380

7481
/**
@@ -85,7 +92,7 @@ class StartupPlan extends BasePlan
8592
* so it significantly simplifies the way we process and handle the data.
8693
*/
8794
public array $can = [
88-
['AddSeats', ['limit' => 1]],
95+
[AddSeats::class, ['limit' => 1]],
8996
];
9097

9198
/**

src/SubscriptionPlans/GlobalTrollPlan.php

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
namespace ArtisanBuild\Till\SubscriptionPlans;
44

55
use ArtisanBuild\Till\Attributes\TeamPlan;
6-
use ArtisanBuild\Till\Contracts\PlanInterface;
76
use ArtisanBuild\Till\Enums\PlanTerms;
87

98
#[TeamPlan]
10-
class AllInPlan extends BasePlan
9+
class GlobalTrollPlan extends BasePlan
1110
{
1211
/**
1312
* Prices
@@ -46,9 +45,9 @@ class AllInPlan extends BasePlan
4645
'inset' => '', // https://fluxui.dev/components/badge#inset
4746
];
4847

49-
public string $heading = 'All In';
48+
public string $heading = 'Global Troll';
5049

51-
public string $subheading = 'Unlimited Everything Forever!';
50+
public string $subheading = 'Beat Elon at his own game';
5251

5352
/**
5453
* Define Your Features
@@ -64,8 +63,10 @@ class AllInPlan extends BasePlan
6463
*/
6564
public array $features = [
6665
['text' => 'Unlimited Users', 'icon' => null],
67-
['text' => 'Unlimited Queries', 'icon' => null],
68-
['text' => 'Private Slack Support', 'icon' => null],
66+
['text' => 'Unlimited Memes', 'icon' => null],
67+
['text' => 'Unlimited Subtweets', 'icon' => null],
68+
['text' => 'Unlimited Guy Replies', 'icon' => null],
69+
['text' => 'Unlimited Rick Rolls', 'icon' => null],
6970
];
7071

7172
/**

src/SubscriptionPlans/NationalTrollPlan.php

+8-7
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33
namespace ArtisanBuild\Till\SubscriptionPlans;
44

55
use ArtisanBuild\Till\Attributes\TeamPlan;
6-
use ArtisanBuild\Till\Contracts\PlanInterface;
76
use ArtisanBuild\Till\Enums\PlanTerms;
87

98
#[TeamPlan]
10-
class ScalerPlan extends BasePlan
9+
class NationalTrollPlan extends BasePlan
1110
{
1211
/**
1312
* Prices
@@ -46,9 +45,9 @@ class ScalerPlan extends BasePlan
4645
'inset' => '', // https://fluxui.dev/components/badge#inset
4746
];
4847

49-
public string $heading = 'Scaler';
48+
public string $heading = 'National Troll';
5049

51-
public string $subheading = 'Amazing efficiency across multiple growing teams';
50+
public string $subheading = 'Be the main character in your own country';
5251

5352
/**
5453
* Define Your Features
@@ -63,9 +62,11 @@ class ScalerPlan extends BasePlan
6362
* leads to more maintainable apps.
6463
*/
6564
public array $features = [
66-
['text' => 'Ten Users', 'icon' => null],
67-
['text' => '2,500 Queries / Day', 'icon' => null],
68-
['text' => 'Email Support', 'icon' => null],
65+
['text' => '10 Users', 'icon' => null],
66+
['text' => '50 Memes / Hour', 'icon' => null],
67+
['text' => '50 Subtweets / Day', 'icon' => null],
68+
['text' => '100 Guy Replies / Week', 'icon' => null],
69+
['text' => '50 Rick Rolls / Month', 'icon' => null],
6970
];
7071

7172
/**

src/SubscriptionPlans/UnsubscribedPlan.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
use ArtisanBuild\Till\Attributes\DefaultPlan;
66
use ArtisanBuild\Till\Attributes\IndividualPlan;
77
use ArtisanBuild\Till\Attributes\TeamPlan;
8-
use ArtisanBuild\Till\Contracts\PlanInterface;
98
use ArtisanBuild\Till\Enums\PlanTerms;
109
use ArtisanBuild\Till\SubscriptionPlans\Abilities\AddSeats;
1110

1211
#[DefaultPlan]
1312
#[TeamPlan]
1413
#[IndividualPlan]
15-
class UnsubscribedPlan extends BasePlan implements PlanInterface
14+
class UnsubscribedPlan extends BasePlan
1615
{
1716
/**
1817
* Prices
@@ -79,6 +78,8 @@ class UnsubscribedPlan extends BasePlan implements PlanInterface
7978
[AddSeats::class, ['limit' => 1]],
8079
];
8180

81+
public array $wallet = [];
82+
8283
/**
8384
* Provider Keys
8485
* -------------

src/Traits/HandlesSubscriptionChanges.php

+3-24
Original file line numberDiff line numberDiff line change
@@ -2,38 +2,17 @@
22

33
namespace ArtisanBuild\Till\Traits;
44

5-
use ArtisanBuild\Till\Actions\GetPlanById;
5+
use ArtisanBuild\Till\Actions\RefreshSubscriptionCache;
66
use ArtisanBuild\Till\States\SubscriberState;
7-
use Carbon\CarbonInterface;
8-
use Illuminate\Support\Facades\Cache;
9-
use RuntimeException;
107

118
trait HandlesSubscriptionChanges
129
{
1310
public function handle(): array
1411
{
15-
/** @var SubscriberState $state */
1612
$state = $this->state(SubscriberState::class);
1713

18-
if (! property_exists($state, 'plan_id')) {
19-
throw new RuntimeException('State class must have a plan_id property');
20-
}
14+
assert($state instanceof SubscriberState);
2115

22-
$abilities = [];
23-
foreach (data_get(app(GetPlanById::class)($state->plan_id), 'can') as $ability) {
24-
$abilities[last(explode('\\', (string) $ability[0]))] = app($ability[0])(...$ability[1]);
25-
}
26-
Cache::put('subscription-'.$this->subscriber_id, $abilities, $this->getCacheExpiration());
27-
28-
return $abilities;
29-
}
30-
31-
protected function getCacheExpiration(): ?CarbonInterface
32-
{
33-
if ($this->expires_at && $this->renews_at) {
34-
return $this->expires_at->lt($this->renews_at) ? $this->expires_at : $this->renews_at;
35-
}
36-
37-
return $this->expires_at ?? $this->renews_at;
16+
return (new RefreshSubscriptionCache($state))();
3817
}
3918
}

0 commit comments

Comments
 (0)