PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 2.13.0
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v2.13.0
4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 1.1.9 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.11.1 1.11.2 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.7.0 1.7.1 1.7.2 1.8.0 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 2.0.0 2.0.1 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.10.0 2.10.1 2.11.0 2.11.1 2.11.2 2.11.3 2.11.4 2.12.0 2.13.0 2.14.0 2.14.1 2.15.0 2.15.1 2.16.0 2.16.1 2.16.2 2.16.3 2.17.0 2.17.1 2.17.2 2.18.0 2.19.0 2.19.2 2.19.3 2.19.4 2.2.0 2.2.1 2.20.0 2.20.1 2.20.2 2.20.3 2.20.4 2.20.5 2.20.6 2.21.0 2.22.0 2.22.1 2.23.0 2.24.0 2.25.0 2.25.1 2.25.2 2.26.0 2.27.0 2.27.1 2.28.0 2.29.0 2.29.1 2.29.2 2.29.3 2.29.4 2.3.0 2.3.1 2.30.0 2.31.0 2.31.1 2.31.2 2.31.3 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.40.0 2.40.1 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.9.0 3.0.0 3.0.0-RC1 3.0.0-RC2 3.0.0-beta1 3.0.0-beta2 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.10.0 3.10.1 3.11.0 3.12.0 3.13.0 3.13.1 3.13.2 3.13.3 3.13.4 3.14.0 3.15.0 3.15.1 3.15.2 3.15.3 3.15.4 3.15.5 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.16.6 3.16.7 3.16.8 3.17.0 3.17.1 3.17.2 3.17.3 3.17.4 3.17.5 3.17.6 3.18.0 3.19.0 3.19.1 3.19.2 3.2.0 3.2.1 3.2.2 3.20.0 3.20.1 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.4.3 3.5.0 3.5.1 3.5.2 3.5.3 3.6.0 3.6.1 3.6.2 3.7.0 3.7.1 3.7.2 3.7.3 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.9.0 4.0.0 4.0.1 4.0.2 4.0.3 trunk 4.1.0 0.2.19.1 4.1.1 1.0.0 4.2.0 1.0.1 1.0.2
surecart / app / src / Models / Subscription.php
surecart / app / src / Models Last commit date
Traits 2 years ago AbandonedCheckout.php 3 years ago AbandonedCheckoutProtocol.php 3 years ago Account.php 3 years ago AccountPortalSession.php 3 years ago Activation.php 3 years ago ApiToken.php 3 years ago BalanceTransaction.php 3 years ago Brand.php 3 years ago Bump.php 3 years ago BuyLink.php 3 years ago CancellationAct.php 3 years ago CancellationReason.php 3 years ago Charge.php 3 years ago Checkout.php 2 years ago Collection.php 3 years ago Component.php 3 years ago Coupon.php 3 years ago Customer.php 2 years ago CustomerLink.php 3 years ago CustomerNotificationProtocol.php 3 years ago DatabaseModel.php 2 years ago Download.php 3 years ago Event.php 3 years ago Form.php 3 years ago Fulfillment.php 3 years ago FulfillmentItem.php 3 years ago IncomingWebhook.php 2 years ago Integration.php 3 years ago Invoice.php 3 years ago License.php 3 years ago LineItem.php 2 years ago ManualPaymentMethod.php 3 years ago Media.php 3 years ago Model.php 2 years ago ModelInterface.php 3 years ago Order.php 3 years ago OrderProtocol.php 3 years ago PaymentIntent.php 3 years ago PaymentMethod.php 3 years ago Period.php 3 years ago PortalProtocol.php 3 years ago PortalSession.php 3 years ago Price.php 3 years ago Processor.php 3 years ago Product.php 2 years ago ProductCollection.php 2 years ago ProductGroup.php 2 years ago ProductMedia.php 3 years ago Promotion.php 3 years ago ProvisionalAccount.php 3 years ago Purchase.php 3 years ago Refund.php 3 years ago RegisteredWebhook.php 2 years ago ReturnItem.php 2 years ago ReturnReason.php 2 years ago ReturnRequest.php 2 years ago ShippingMethod.php 3 years ago ShippingProfile.php 3 years ago ShippingProtocol.php 3 years ago ShippingRate.php 3 years ago ShippingZone.php 3 years ago Statistic.php 3 years ago Subscription.php 2 years ago SubscriptionProtocol.php 3 years ago TaxProtocol.php 3 years ago TaxRegistration.php 3 years ago TaxZone.php 3 years ago Upload.php 3 years ago User.php 2 years ago Variant.php 2 years ago VariantOption.php 2 years ago VariantValue.php 2 years ago VerificationCode.php 3 years ago Webhook.php 2 years ago WebhookRegistration.php 2 years ago
Subscription.php
505 lines
1 <?php
2
3 namespace SureCart\Models;
4
5 use SureCart\Models\Traits\HasCustomer;
6 use SureCart\Models\Traits\HasPrice;
7 use SureCart\Models\Traits\HasPurchase;
8
9 /**
10 * Subscription model
11 */
12 class Subscription extends Model {
13 use HasCustomer, HasPrice, HasPurchase;
14
15 /**
16 * Rest API endpoint
17 *
18 * @var string
19 */
20 protected $endpoint = 'subscriptions';
21
22 /**
23 * Object name
24 *
25 * @var string
26 */
27 protected $object_name = 'subscription';
28
29 /**
30 * Update the model.
31 *
32 * @param array $attributes Attributes to update.
33 * @return $this|false
34 */
35 protected function update( $attributes = [] ) {
36 // find existing subscription with purchase record.
37 $existing = ( new Subscription() )->with( [ 'purchase' ] )->find( $attributes['id'] ?? $this->attributes['id'] );
38
39 // do the update and also get the purchase record.
40 $this->with( [ 'purchase' ] );
41 $updated = parent::update( $attributes );
42 if ( is_wp_error( $updated ) ) {
43 return $updated;
44 }
45
46 // do the purchase updated event.
47 if ( ! empty( $updated->purchase ) ) {
48 do_action(
49 'surecart/purchase_updated',
50 $updated->purchase,
51 [
52 'data' => [
53 'object' => $updated->purchase->toArray(),
54 'previous_attributes' => array_filter(
55 [
56 // conditionally have the previous product and quantity as the previous attributes.
57 'product' => $updated->purchase->product_id !== $existing->purchase->product_id ? ( $existing->purchase->product_id ?? null ) : null,
58 'quantity' => $updated->purchase->quantity !== $existing->purchase->quantity ? ( $existing->purchase->quantity ?? 1 ) : null,
59 ]
60 ),
61 ],
62 ]
63 );
64 }
65
66 return $this;
67 }
68
69 /**
70 * Cancel a subscription
71 *
72 * @param string $id Model id.
73 * @return $this|\WP_Error
74 */
75 protected function cancel( $id = null ) {
76 if ( $id ) {
77 $this->setAttribute( 'id', $id );
78 }
79
80 if ( $this->fireModelEvent( 'canceling' ) === false ) {
81 return false;
82 }
83
84 if ( empty( $this->attributes['id'] ) ) {
85 return new \WP_Error( 'not_saved', 'Please create the subscription.' );
86 }
87
88 $attributes = $this->attributes;
89 unset( $attributes['id'] );
90
91 $canceled = $this->with(
92 [
93 'purchase',
94 ]
95 )->makeRequest(
96 [
97 'method' => 'PATCH',
98 'query' => $this->query,
99 'body' => [
100 $this->object_name => $attributes,
101 ],
102 ],
103 $this->endpoint . '/' . $this->attributes['id'] . '/cancel/'
104 );
105
106 if ( is_wp_error( $canceled ) ) {
107 return $canceled;
108 }
109
110 $this->resetAttributes();
111
112 $this->fill( $canceled );
113
114 $this->fireModelEvent( 'canceled' );
115
116 // purchase revoked event.
117 if ( ! empty( $this->purchase->revoked ) ) {
118 do_action( 'surecart/purchase_revoked', $this->purchase );
119 }
120
121 return $this;
122 }
123
124 /**
125 * Complete a subscription
126 *
127 * @param string $id Model id.
128 * @return $this|\WP_Error
129 */
130 protected function complete( $id = null ) {
131 if ( $id ) {
132 $this->setAttribute( 'id', $id );
133 }
134
135 if ( $this->fireModelEvent( 'completeing' ) === false ) {
136 return false;
137 }
138
139 if ( empty( $this->attributes['id'] ) ) {
140 return new \WP_Error( 'not_saved', 'Please create the subscription.' );
141 }
142
143 $completed = $this->with(
144 [
145 'purchase',
146 ]
147 )->makeRequest(
148 [
149 'method' => 'PATCH',
150 'query' => $this->query,
151 ],
152 $this->endpoint . '/' . $this->attributes['id'] . '/complete/'
153 );
154
155 if ( is_wp_error( $completed ) ) {
156 return $completed;
157 }
158
159 $this->resetAttributes();
160
161 $this->fill( $completed );
162
163 $this->fireModelEvent( 'completed' );
164
165 return $this;
166 }
167
168 /**
169 * Restore a subscription
170 *
171 * @param string $id Model id.
172 * @return $this|\WP_Error
173 */
174 protected function restore( $id = null ) {
175 if ( $id ) {
176 $this->setAttribute( 'id', $id );
177 }
178
179 if ( $this->fireModelEvent( 'restoring' ) === false ) {
180 return false;
181 }
182
183 if ( empty( $this->attributes['id'] ) ) {
184 return new \WP_Error( 'not_saved', 'Please create the subscription.' );
185 }
186
187 $restored = \SureCart::request(
188 $this->endpoint . '/' . $this->attributes['id'] . '/restore/',
189 [
190 'method' => 'PATCH',
191 'query' => $this->query,
192 ]
193 );
194
195 if ( is_wp_error( $restored ) ) {
196 return $restored;
197 }
198
199 $this->resetAttributes();
200
201 $this->fill( $restored );
202
203 $this->fireModelEvent( 'restored' );
204
205 return $this;
206 }
207
208 /**
209 * Renew a subscription.
210 *
211 * @param string $id Model id.
212 * @return $this|\WP_Error
213 */
214 protected function renew( $id = null ) {
215 if ( $id ) {
216 $this->setAttribute( 'id', $id );
217 }
218
219 if ( $this->fireModelEvent( 'renewing' ) === false ) {
220 return false;
221 }
222
223 if ( empty( $this->attributes['id'] ) ) {
224 return new \WP_Error( 'not_saved', 'Please create the subscription.' );
225 }
226
227 $renewed = \SureCart::request(
228 $this->endpoint . '/' . $this->attributes['id'],
229 [
230 'method' => 'PATCH',
231 'query' => $this->query,
232 'body' => [
233 $this->object_name => [
234 'cancel_at_period_end' => false,
235 ],
236 ],
237 ]
238 );
239
240 if ( is_wp_error( $renewed ) ) {
241 return $renewed;
242 }
243
244 $this->resetAttributes();
245
246 $this->fill( $renewed );
247
248 $this->fireModelEvent( 'renewed' );
249
250 return $this;
251 }
252
253 /**
254 * Preserve a subscription.
255 *
256 * @param string $id Model id.
257 * @return $this|\WP_Error
258 */
259 protected function preserve( $id = null ) {
260 if ( $id ) {
261 $this->setAttribute( 'id', $id );
262 }
263
264 if ( $this->fireModelEvent( 'preserving' ) === false ) {
265 return false;
266 }
267
268 if ( empty( $this->attributes['id'] ) ) {
269 return new \WP_Error( 'not_saved', 'Please create the subscription.' );
270 }
271
272 $preserved = $this->makeRequest(
273 [
274 'method' => 'PATCH',
275 'query' => $this->query,
276 ],
277 $this->endpoint . '/' . $this->attributes['id'] . '/preserve/'
278 );
279
280 if ( is_wp_error( $preserved ) ) {
281 return $preserved;
282 }
283
284 $this->resetAttributes();
285
286 $this->fill( $preserved );
287
288 $this->fireModelEvent( 'preserved' );
289
290 return $this;
291 }
292
293 /**
294 * Preview the upcoming invoice.
295 *
296 * @param string $args Arguments
297 * @return $this|\WP_Error
298 */
299 protected function upcomingPeriod( $args = [] ) {
300 if ( ! empty( $args['id'] ) ) {
301 $this->setAttribute( 'id', $args['id'] );
302 unset( $args['id'] );
303 }
304
305 if ( $this->fireModelEvent( 'previewingUpcomingPeriod' ) === false ) {
306 return false;
307 }
308
309 if ( empty( $this->attributes['id'] ) ) {
310 return new \WP_Error( 'not_saved', 'Please create the subscription' );
311 }
312
313 $upcoming_period = $this->makeRequest(
314 [
315 'method' => 'PATCH',
316 'query' => $this->query,
317 'body' => [
318 $this->object_name => $args,
319 ],
320 ],
321 $this->endpoint . '/' . $this->attributes['id'] . '/upcoming_period/'
322 );
323
324 if ( is_wp_error( $upcoming_period ) ) {
325 return $upcoming_period;
326 }
327
328 $this->resetAttributes();
329
330 $this->fill( $upcoming_period );
331
332 $this->fireModelEvent( 'previewedUpcomingPeriod' );
333
334 return $this;
335 }
336
337 /**
338 * Pay off a subscription
339 *
340 * @param string $id Model id.
341 * @return $this|\WP_Error
342 */
343 protected function payOff( $id = null ) {
344 if ( $id ) {
345 $this->setAttribute( 'id', $id );
346 }
347
348 if ( $this->fireModelEvent( 'payingOff' ) === false ) {
349 return false;
350 }
351
352 if ( empty( $this->attributes['id'] ) ) {
353 return new \WP_Error( 'not_saved', 'Please create the subscription' );
354 }
355
356 $paid_off = $this->makeRequest(
357 [
358 'method' => 'PATCH',
359 'query' => $this->query,
360 ],
361 $this->endpoint . '/' . $this->attributes['id'] . '/pay_off/'
362 );
363
364 if ( is_wp_error( $paid_off ) ) {
365 return $paid_off;
366 }
367
368 $this->resetAttributes();
369
370 $this->fill( $paid_off );
371
372 $this->fireModelEvent( 'paidOff' );
373
374 return $this;
375 }
376
377 /**
378 * Is this subscription a lifetime one?
379 *
380 * @return boolean
381 */
382 protected function isLifetime() {
383 return $this->attributes['id'] && empty( $this->attributes['current_period_end_at'] );
384 }
385
386 /**
387 * Can the user upgrade this subscription?
388 *
389 * @return boolean
390 */
391 protected function canBeSwitched() {
392 return apply_filters( 'surecart/subscription/can_be_changed', $this->checkIfCanBeSwitched(), $this );
393 }
394
395 /**
396 * Can the subscription be changed?
397 *
398 * @return boolean
399 */
400 private function checkIfCanBeSwitched() {
401 // updates are not enabled for the account.
402 if ( empty( \SureCart::account()->portal_protocol->subscription_updates_enabled ) ) {
403 return false;
404 }
405 // already set to canceling.
406 if ( $this->attributes['cancel_at_period_end'] ) {
407 return false;
408 }
409 // can't update canceled, incomplete, or past due subscriptions.
410 if ( in_array( $this->attributes['status'], [ 'canceled', 'incomplete', 'past_due' ] ) ) {
411 return false;
412 }
413 // must not be lifetime.
414 if ( $this->isLifetime() ) {
415 return false;
416 }
417 return true;
418 }
419
420 /**
421 * Can we cancel the subscription?
422 *
423 * @return boolean
424 */
425 public function canBeCanceled() {
426 return apply_filters( 'surecart/subscription/can_be_canceled', $this->checkIfCanBeSwitched(), $this );
427 }
428
429 /**
430 * Should delay subscription cancellation?
431 *
432 * @return boolean
433 */
434 public function shouldDelayCancellation(): bool {
435 $protocol = \SureCart::account()->subscription_protocol;
436
437 if ( ! $protocol->cancel_window_enabled || empty( $protocol->cancel_window_days ) || empty( $this->attributes['current_period_end_at'] ) ) {
438 return false;
439 }
440
441 $cancel_window_days = $protocol->cancel_window_days;
442 $now = (new \DateTime())->format('Y-m-d');
443 $end = new \DateTime();
444 $end->setTimestamp( $this->attributes['current_period_end_at'] );
445 $end = $end->modify( "-{$cancel_window_days} days" );
446 $end = $end->format('Y-m-d');
447
448 return $now < $end;
449 }
450
451 /**
452 * Can the subscription be canceled?
453 *
454 * @return boolean
455 */
456 private function checkIfCanBeCanceled() {
457 // updates are not enabled for the account.
458 if ( empty( \SureCart::account()->portal_protocol->subscription_cancellations_enabled ) ) {
459 return false;
460 }
461
462 // can't cancel canceled, incomplete, or past due subscriptions.
463 if ( in_array( $this->attributes['status'], [ 'canceled', 'incomplete', 'past_due' ] ) ) {
464 return false;
465 }
466
467 return true;
468 }
469
470 /**
471 * Can we update the quantity?
472 *
473 * @return boolean
474 */
475 protected function canUpdateQuantity() {
476 return apply_filters( 'surecart/subscription/can_update_quantity', $this->checkIfCanBeSwitched(), $this );
477 }
478
479 /**
480 * Check if we can update the quantity.
481 *
482 * @return boolean
483 */
484 private function checkIfCanUpdateQuantity() {
485 // quantity changes are not enabled for this account.
486 if ( empty( \SureCart::account()->portal_protocol->subscription_quantity_updates_enabled ) ) {
487 return false;
488 }
489 return true;
490 }
491
492 /**
493 * Get stats for the subscription
494 *
495 * @param array $args Array of arguments for the statistics.
496 *
497 * @return \SureCart\Models\Statistic;
498 */
499 protected function stats( $args = [] ) {
500 $stat = new Statistic();
501 return $stat->where( $args )->find( 'subscriptions' );
502 }
503 }
504
505