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 | } |