Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
4.35% |
3 / 69 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
SubscriptionItem | |
4.35% |
3 / 69 |
|
0.00% |
0 / 12 |
240.04 | |
0.00% |
0 / 1 |
boot | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
1.02 | |||
subscription | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
incrementQuantity | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
incrementAndInvoice | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
decrementQuantity | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
updateQuantity | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
swap | |
0.00% |
0 / 23 |
|
0.00% |
0 / 1 |
6 | |||
swapAndInvoice | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
reportUsage | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
usageRecords | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
updateStripeSubscriptionItem | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
asStripeSubscriptionItem | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace App\Http\Models; |
4 | |
5 | use DateTimeInterface; |
6 | use Illuminate\Database\Eloquent\Relations\BelongsTo; |
7 | use Illuminate\Support\Collection; |
8 | use Laravel\Cashier\Cashier; |
9 | use Laravel\Cashier\Concerns\InteractsWithPaymentBehavior; |
10 | use Laravel\Cashier\Concerns\Prorates; |
11 | use Stripe\SubscriptionItem as StripeSubscriptionItem; |
12 | use Stripe\UsageRecord; |
13 | |
14 | /** |
15 | * @property \Laravel\Cashier\Subscription|null $subscription |
16 | */ |
17 | class SubscriptionItem extends Moloquent |
18 | { |
19 | use InteractsWithPaymentBehavior; |
20 | use Prorates; |
21 | |
22 | /** |
23 | * The attributes that are not mass assignable. |
24 | * |
25 | * @var array |
26 | */ |
27 | protected $guarded = []; |
28 | |
29 | /** |
30 | * The attributes that should be cast to native types. |
31 | * |
32 | * @var array |
33 | */ |
34 | protected $casts = [ |
35 | 'quantity' => 'integer', |
36 | ]; |
37 | |
38 | /** |
39 | * |
40 | * Recent update to laravel cashier is forcing the stripe_plan field into stripe_price. |
41 | * Here is an attempt to ensure there's still a stripe_plan field. |
42 | */ |
43 | public static function boot(): void |
44 | { |
45 | parent::boot(); |
46 | |
47 | self::creating(function($model) { |
48 | $model->stripe_plan = $model->stripe_price ?? $model->stripe_plan; |
49 | }); |
50 | } |
51 | |
52 | /** |
53 | * Get the subscription that the item belongs to. |
54 | */ |
55 | public function subscription(): BelongsTo |
56 | { |
57 | return $this->belongsTo(Cashier::$subscriptionModel); |
58 | } |
59 | |
60 | /** |
61 | * Increment the quantity of the subscription item. |
62 | * |
63 | * |
64 | * @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure |
65 | */ |
66 | public function incrementQuantity(int $count = 1): static |
67 | { |
68 | $this->updateQuantity($this->quantity + $count); |
69 | |
70 | return $this; |
71 | } |
72 | |
73 | /** |
74 | * Increment the quantity of the subscription item, and invoice immediately. |
75 | * |
76 | * |
77 | * @throws \Laravel\Cashier\Exceptions\IncompletePayment |
78 | * @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure |
79 | */ |
80 | public function incrementAndInvoice(int $count = 1): static |
81 | { |
82 | $this->alwaysInvoice(); |
83 | |
84 | $this->incrementQuantity($count); |
85 | |
86 | return $this; |
87 | } |
88 | |
89 | /** |
90 | * Decrement the quantity of the subscription item. |
91 | * |
92 | * |
93 | * @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure |
94 | */ |
95 | public function decrementQuantity(int $count = 1): static |
96 | { |
97 | $this->updateQuantity(max(1, $this->quantity - $count)); |
98 | |
99 | return $this; |
100 | } |
101 | |
102 | /** |
103 | * Update the quantity of the subscription item. |
104 | * |
105 | * |
106 | * @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure |
107 | */ |
108 | public function updateQuantity(int $quantity): static |
109 | { |
110 | $this->subscription->guardAgainstIncomplete(); |
111 | |
112 | $stripeSubscriptionItem = $this->asStripeSubscriptionItem(); |
113 | |
114 | $stripeSubscriptionItem->quantity = $quantity; |
115 | |
116 | $stripeSubscriptionItem->payment_behavior = $this->paymentBehavior(); |
117 | |
118 | $stripeSubscriptionItem->proration_behavior = $this->prorateBehavior(); |
119 | |
120 | $stripeSubscriptionItem->save(); |
121 | |
122 | $this->quantity = $quantity; |
123 | |
124 | $this->save(); |
125 | |
126 | if ($this->subscription->hasSinglePlan()) { |
127 | $this->subscription->quantity = $quantity; |
128 | |
129 | $this->subscription->save(); |
130 | } |
131 | |
132 | return $this; |
133 | } |
134 | |
135 | /** |
136 | * Swap the subscription item to a new Stripe plan. |
137 | * |
138 | * |
139 | * @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure |
140 | */ |
141 | public function swap(string $plan, array $options = []): static |
142 | { |
143 | $this->subscription->guardAgainstIncomplete(); |
144 | |
145 | $options = array_merge([ |
146 | 'plan' => $plan, |
147 | 'quantity' => $this->quantity, |
148 | 'payment_behavior' => $this->paymentBehavior(), |
149 | 'proration_behavior' => $this->prorateBehavior(), |
150 | 'tax_rates' => $this->subscription->getPlanTaxRatesForPayload($plan), |
151 | ], $options); |
152 | |
153 | $item = StripeSubscriptionItem::update( |
154 | $this->stripe_id, |
155 | $options, |
156 | $this->subscription->owner->stripe() |
157 | ); |
158 | |
159 | $this->fill([ |
160 | 'stripe_plan' => $plan, |
161 | 'quantity' => $item->quantity, |
162 | ])->save(); |
163 | |
164 | if ($this->subscription->hasSinglePlan()) { |
165 | $this->subscription->fill([ |
166 | 'stripe_plan' => $plan, |
167 | 'quantity' => $item->quantity, |
168 | ])->save(); |
169 | } |
170 | |
171 | return $this; |
172 | } |
173 | |
174 | /** |
175 | * Swap the subscription item to a new Stripe plan, and invoice immediately. |
176 | * |
177 | * |
178 | * @throws \Laravel\Cashier\Exceptions\IncompletePayment |
179 | * @throws \Laravel\Cashier\Exceptions\SubscriptionUpdateFailure |
180 | */ |
181 | public function swapAndInvoice(string $plan, array $options = []): static |
182 | { |
183 | $this->alwaysInvoice(); |
184 | |
185 | return $this->swap($plan, $options); |
186 | } |
187 | |
188 | /** |
189 | * Report usage for a metered product. |
190 | * |
191 | * @param \DateTimeInterface|int|null $timestamp |
192 | */ |
193 | public function reportUsage(int $quantity = 1, $timestamp = null): UsageRecord |
194 | { |
195 | $timestamp = $timestamp instanceof DateTimeInterface ? $timestamp->getTimestamp() : $timestamp; |
196 | |
197 | return StripeSubscriptionItem::createUsageRecord($this->stripe_id, [ |
198 | 'quantity' => $quantity, |
199 | 'action' => $timestamp ? 'set' : 'increment', |
200 | 'timestamp' => $timestamp ?? time(), |
201 | ], $this->subscription->owner->stripe()); |
202 | } |
203 | |
204 | /** |
205 | * Get the usage records for a metered product. |
206 | */ |
207 | public function usageRecords(array $options = []): Collection |
208 | { |
209 | return new Collection(StripeSubscriptionItem::allUsageRecordSummaries( |
210 | $this->stripe_id, |
211 | $options, |
212 | $this->subscription->owner->stripe() |
213 | )->data); |
214 | } |
215 | |
216 | /** |
217 | * Update the underlying Stripe subscription item information for the model. |
218 | */ |
219 | public function updateStripeSubscriptionItem(array $options = []): StripeSubscriptionItem |
220 | { |
221 | return StripeSubscriptionItem::update( |
222 | $this->stripe_id, |
223 | $options, |
224 | $this->subscription->owner->stripe() |
225 | ); |
226 | } |
227 | |
228 | /** |
229 | * Get the subscription as a Stripe subscription item object. |
230 | */ |
231 | public function asStripeSubscriptionItem(array $expand = []): StripeSubscriptionItem |
232 | { |
233 | return StripeSubscriptionItem::retrieve( |
234 | ['id' => $this->stripe_id, 'expand' => $expand], |
235 | $this->subscription->owner->stripe() |
236 | ); |
237 | } |
238 | } |