PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 3.2.1
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v3.2.1
4.7.0 4.6.6 4.6.5 4.6.4 4.6.3 4.6.2 4.6.1 4.6.0 4.5.1 4.5.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 / Checkout.php
surecart / app / src / Models Last commit date
Blocks 1 year ago Concerns 1 year ago Traits 1 year ago AbandonedCheckout.php 1 year ago AbandonedCheckoutProtocol.php 3 years ago Account.php 3 years ago AccountPortalSession.php 4 years ago Activation.php 1 year ago Affiliation.php 1 year ago AffiliationProduct.php 1 year ago AffiliationProtocol.php 2 years ago AffiliationRequest.php 1 year ago ApiToken.php 3 years ago BalanceTransaction.php 1 year ago Brand.php 4 years ago BulkAction.php 2 years ago Bump.php 1 year ago BuyLink.php 2 years ago CancellationAct.php 1 year ago CancellationReason.php 3 years ago Charge.php 1 year ago Checkout.php 1 year ago Click.php 1 year ago Collection.php 2 years ago CommissionStructure.php 2 years ago Component.php 4 years ago Coupon.php 1 year ago Customer.php 1 year ago CustomerLink.php 4 years ago CustomerNotificationProtocol.php 4 years ago CustomerPortalProtocol.php 1 year ago DatabaseModel.php 1 year ago Discount.php 1 year ago Download.php 4 years ago Event.php 4 years ago Export.php 2 years ago ExternalApiModel.php 1 year ago Form.php 3 years ago Fulfillment.php 1 year ago FulfillmentItem.php 1 year ago GalleryItem.php 1 year ago GalleryItemAttachment.php 1 year ago GalleryItemMedia.php 1 year ago GalleryItemProductMedia.php 1 year ago IncomingWebhook.php 1 year ago Integration.php 2 years ago IntegrationCatalog.php 1 year ago Invoice.php 1 year ago License.php 1 year ago LineItem.php 1 year ago ManualPaymentMethod.php 1 year ago Media.php 1 year ago Model.php 1 year ago ModelInterface.php 4 years ago Order.php 1 year ago OrderProtocol.php 4 years ago PaymentFailure.php 1 year ago PaymentInstrument.php 1 year ago PaymentIntent.php 4 years ago PaymentMethod.php 1 year ago Payout.php 1 year ago PayoutGroup.php 2 years ago Period.php 1 year ago PortalSession.php 4 years ago Price.php 1 year ago Processor.php 1 year ago Product.php 1 year ago ProductCollection.php 1 year ago ProductGroup.php 1 year ago ProductMedia.php 1 year ago Promotion.php 1 year ago ProvisionalAccount.php 2 years ago Purchase.php 2 years ago Referral.php 1 year ago ReferralItem.php 2 years ago Refund.php 1 year ago RegisteredWebhook.php 2 years ago ReturnItem.php 2 years ago ReturnReason.php 2 years ago ReturnRequest.php 1 year 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 4 years ago Subscription.php 1 year ago SubscriptionProtocol.php 4 years ago TaxOverride.php 2 years ago TaxProtocol.php 4 years ago TaxRegistration.php 1 year ago TaxZone.php 4 years ago Upload.php 4 years ago Upsell.php 1 year ago UpsellFunnel.php 1 year ago User.php 2 years ago Variant.php 1 year ago VariantOption.php 2 years ago VariantOptionValue.php 1 year ago VariantValue.php 2 years ago VerificationCode.php 3 years ago Webhook.php 1 year ago WebhookRegistration.php 2 years ago
Checkout.php
530 lines
1 <?php
2
3 namespace SureCart\Models;
4
5 use SureCart\Models\Traits\HasCustomer;
6 use SureCart\Models\Traits\HasSubscriptions;
7 use SureCart\Models\LineItem;
8 use SureCart\Models\Traits\CanFinalize;
9 use SureCart\Models\Traits\HasBillingAddress;
10 use SureCart\Models\Traits\HasDiscount;
11 use SureCart\Models\Traits\HasInvoice;
12 use SureCart\Models\Traits\HasPaymentFailures;
13 use SureCart\Models\Traits\HasPaymentIntent;
14 use SureCart\Models\Traits\HasPaymentMethod;
15 use SureCart\Models\Traits\HasProcessorType;
16 use SureCart\Models\Traits\HasPurchases;
17 use SureCart\Models\Traits\HasShippingAddress;
18 use SureCart\Support\Currency;
19 use SureCart\Support\TimeDate;
20
21 /**
22 * Order model
23 */
24 class Checkout extends Model {
25 use HasCustomer;
26 use HasSubscriptions;
27 use HasDiscount;
28 use HasShippingAddress;
29 use HasPaymentIntent;
30 use HasPaymentMethod;
31 use HasPurchases;
32 use CanFinalize;
33 use HasProcessorType;
34 use HasBillingAddress;
35 use HasPaymentFailures;
36 use HasInvoice;
37
38 /**
39 * Rest API endpoint
40 *
41 * @var string
42 */
43 protected $endpoint = 'checkouts';
44
45 /**
46 * Object name
47 *
48 * @var string
49 */
50 protected $object_name = 'checkout';
51
52 /**
53 * Need to pass the processor type on create
54 *
55 * @param array $attributes Optional attributes.
56 * @param string $type stripe, paypal, etc.
57 */
58 public function __construct( $attributes = [], $type = '' ) {
59 $this->processor_type = $type;
60 parent::__construct( $attributes );
61 }
62
63 /**
64 * Get the display subtotal amount attribute.
65 *
66 * @return string
67 */
68 public function getSubtotalDisplayAmountAttribute() {
69 return ! empty( $this->subtotal_amount ) ? Currency::format( $this->subtotal_amount, $this->currency ) : '';
70 }
71
72 /**
73 * Get the display subtotal amount attribute.
74 *
75 * @return string
76 */
77 public function getTotalDisplayAmountAttribute() {
78 return Currency::format( (int) $this->total_amount, $this->currency );
79 }
80
81 /**
82 * Get the display full amount attribute.
83 *
84 * @return string
85 */
86 public function getFullDisplayAmountAttribute() {
87 return ! empty( $this->full_amount ) ? Currency::format( $this->full_amount, $this->currency ) : '';
88 }
89
90 /**
91 * Get the display discount amount amount attribute.
92 *
93 * @return string
94 */
95 public function getDiscountDisplayAmountAttribute() {
96 return ! empty( $this->discount_amount ) ? Currency::format( $this->discount_amount, $this->currency ) : '';
97 }
98
99 /**
100 * Get the display bump amount attribute.
101 *
102 * @return string
103 */
104 public function getBumpDisplayAmountAttribute() {
105 return ! empty( $this->bump_amount ) ? Currency::format( $this->bump_amount, $this->currency ) : '';
106 }
107
108 /**
109 * Get the converted total amount attribute.
110 *
111 * @return string
112 */
113 public function getConvertedTotalAmountAttribute() {
114 if ( $this->is_zero_decimal || empty( $this->total_amount ) ) {
115 return $this->total_amount;
116 }
117 return $this->total_amount / 100;
118 }
119
120 /**
121 * Is the checkout an installment.
122 *
123 * @return bool
124 */
125 public function getIsInstallmentAttribute() {
126 return $this->full_amount !== $this->subtotal_amount;
127 }
128
129 /**
130 * Get the line items count attribute.
131 *
132 * @return int
133 */
134 public function getLineItemsCountAttribute() {
135 if ( empty( $this->line_items ) || empty( $this->line_items->data ) ) {
136 return 0;
137 }
138 return array_reduce(
139 $this->line_items->data ?? [],
140 function ( $count, $item ) {
141 return $count + ( $item->quantity ?? 0 );
142 },
143 0
144 );
145 }
146
147 /**
148 * Get the has recurring attribute.
149 *
150 * Do any line items have a recurring price?
151 *
152 * @return bool
153 */
154 public function getHasRecurringAttribute() {
155 return array_reduce(
156 $this->line_items->data ?? [],
157 function ( $carry, $item ) {
158 return $carry || isset( $item->price->recurring_interval );
159 },
160 false
161 );
162 }
163
164 /**
165 * Set attributes during write actions.
166 *
167 * @return void
168 */
169 protected function setWriteAttributes() {
170 $this->setAttribute( 'ip_address', $this->getIPAddress() );
171 if ( isset( $_COOKIE['sc_click_id'] ) ) {
172 $this->setAttribute( 'last_click', $_COOKIE['sc_click_id'] );
173 }
174 }
175
176 /**
177 * Set the upsell funnel attribute
178 *
179 * @param object $value The data array.
180 * @return void
181 */
182 public function setUpsellFunnelAttribute( $value ) {
183 $this->setRelation( 'upsell_funnel', $value, UpsellFunnel::class );
184 }
185
186 /**
187 * Set the upsell funnel attribute
188 *
189 * @param object $value The data array.
190 * @return void
191 */
192 public function setCurrentUpsellAttribute( $value ) {
193 $this->setRelation( 'current_upsell', $value, Upsell::class );
194 }
195
196 /**
197 * Set the recommended bumps attribute
198 *
199 * @param object $value Subscription data array.
200 * @return void
201 */
202 public function setRecommendedBumpsAttribute( $value ) {
203 $this->setCollection( 'recommended_bumps', $value, Bump::class );
204 }
205
206 /**
207 * Create a new model
208 *
209 * @param array $attributes Attributes to create.
210 *
211 * @return $this|\WP_Error|false
212 */
213 protected function create( $attributes = [] ) {
214 $this->setWriteAttributes();
215 return parent::create( $attributes );
216 }
217
218 /**
219 * Update the model
220 *
221 * @param array $attributes Attributes to create.
222 *
223 * @return $this|\WP_Error|false
224 */
225 protected function update( $attributes = [] ) {
226 $this->setWriteAttributes();
227
228 return parent::update( $attributes );
229 }
230
231 /**
232 * Get the IP address of the user
233 *
234 * TOD0: Move this to a helper class.
235 *
236 * @return string
237 */
238 protected function getIPAddress() {
239 return $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
240 }
241
242 /**
243 * Set the product attribute
244 *
245 * @param object $value Product properties.
246 * @return void
247 */
248 public function setLineItemsAttribute( $value ) {
249 $this->setCollection( 'line_items', $value, LineItem::class );
250 }
251
252 /**
253 * Finalize the session for checkout.
254 *
255 * @return $this|\WP_Error
256 */
257 protected function manuallyPay() {
258 if ( $this->fireModelEvent( 'manually_paying' ) === false ) {
259 return false;
260 }
261
262 if ( empty( $this->attributes['id'] ) ) {
263 return new \WP_Error( 'not_saved', 'Please create the checkout session.' );
264 }
265
266 $finalized = \SureCart::request(
267 $this->endpoint . '/' . $this->attributes['id'] . '/manually_pay/',
268 [
269 'method' => 'PATCH',
270 'query' => $this->query,
271 'body' => [
272 $this->object_name => $this->getAttributes(),
273 ],
274 ]
275 );
276
277 if ( is_wp_error( $finalized ) ) {
278 return $finalized;
279 }
280
281 $this->resetAttributes();
282
283 $this->fill( $finalized );
284
285 $this->fireModelEvent( 'manually_paid' );
286
287 return $this;
288 }
289
290 /**
291 * Cancel an checkout
292 *
293 * @return $this|\WP_Error
294 */
295 protected function cancel() {
296 if ( $this->fireModelEvent( 'cancelling' ) === false ) {
297 return false;
298 }
299
300 if ( empty( $this->attributes['id'] ) ) {
301 return new \WP_Error( 'not_saved', 'Please create the order.' );
302 }
303
304 $cancelled = $this->makeRequest(
305 [
306 'method' => 'PATCH',
307 'query' => $this->query,
308 'body' => [
309 $this->object_name => $this->getAttributes(),
310 ],
311 ],
312 $this->endpoint . '/' . $this->attributes['id'] . '/cancel/'
313 );
314
315 if ( is_wp_error( $cancelled ) ) {
316 return $cancelled;
317 }
318
319 $this->resetAttributes();
320
321 $this->fill( $cancelled );
322
323 $this->fireModelEvent( 'cancelled' );
324
325 return $this;
326 }
327
328 /**
329 * Offer a bump.
330 *
331 * @param string|\SureCart\Models\Bump $bump The bump object.
332 *
333 * @return true|\WP_Error
334 */
335 protected function offerBump( $bump ) {
336 if ( $this->fireModelEvent( 'offering_bump' ) === false ) {
337 return false;
338 }
339
340 if ( empty( $this->attributes['id'] ) ) {
341 return new \WP_Error( 'not_saved', 'Please create the checkout.' );
342 }
343
344 $id = is_a( $bump, Bump::class ) ? $bump->id : $bump;
345
346 if ( empty( $id ) ) {
347 return new \WP_Error( 'not_saved', 'Please pass an upsell.' );
348 }
349
350 $offered = $this->makeRequest(
351 [
352 'method' => 'PATCH',
353 'query' => $this->query,
354 'body' => [
355 $this->object_name => $this->getAttributes(),
356 ],
357 ],
358 $this->endpoint . '/' . $this->attributes['id'] . '/offer_bump/' . $id
359 );
360
361 if ( is_wp_error( $offered ) ) {
362 return $offered;
363 }
364
365 return true;
366 }
367
368 /**
369 * Offer an upsell.
370 *
371 * @param string|\SureCart\Models\Upsell $upsell The upsell object.
372 *
373 * @return true|\WP_Error
374 */
375 protected function offerUpsell( $upsell ) {
376 if ( $this->fireModelEvent( 'offering_upsell' ) === false ) {
377 return false;
378 }
379
380 if ( empty( $this->attributes['id'] ) ) {
381 return new \WP_Error( 'not_saved', 'Please create the checkout.' );
382 }
383
384 $id = is_a( $upsell, Upsell::class ) ? $upsell->id : $upsell;
385
386 if ( empty( $id ) ) {
387 return new \WP_Error( 'not_saved', 'Please pass an upsell.' );
388 }
389
390 $offered = $this->makeRequest(
391 [
392 'method' => 'PATCH',
393 'query' => $this->query,
394 'body' => [
395 $this->object_name => $this->getAttributes(),
396 ],
397 ],
398 $this->endpoint . '/' . $this->attributes['id'] . '/offer_upsell/' . $id
399 );
400
401 if ( is_wp_error( $offered ) ) {
402 return $offered;
403 }
404
405 return true;
406 }
407
408 /**
409 * Decline an upsell.
410 *
411 * @param string|\SureCart\Models\Upsell $upsell The upsell object.
412 *
413 * @return $this|\WP_Error
414 */
415 protected function declineUpsell( $upsell ) {
416 if ( $this->fireModelEvent( 'declining_upsell' ) === false ) {
417 return false;
418 }
419
420 if ( empty( $this->attributes['id'] ) ) {
421 return new \WP_Error( 'not_saved', 'Please create the checkout.' );
422 }
423
424 $id = is_a( $upsell, Upsell::class ) ? $upsell->id : $upsell;
425
426 if ( empty( $id ) ) {
427 return new \WP_Error( 'not_saved', 'Please pass an upsell.' );
428 }
429
430 $declined = $this->makeRequest(
431 [
432 'method' => 'PATCH',
433 'query' => $this->query,
434 'body' => [
435 $this->object_name => $this->getAttributes(),
436 ],
437 ],
438 $this->endpoint . '/' . $this->attributes['id'] . '/decline_upsell/' . $id
439 );
440
441 if ( is_wp_error( $declined ) ) {
442 return $declined;
443 }
444
445 $this->resetAttributes();
446
447 $this->fill( $declined );
448
449 $this->fireModelEvent( 'declined' );
450
451 return $this;
452 }
453
454 /**
455 * Get the billing address attribute
456 *
457 * @return array|null The billing address.
458 */
459 public function getBillingAddressDisplayAttribute() {
460 if ( $this->billing_matches_shipping ) {
461 return $this->shipping_address;
462 }
463
464 return $this->attributes['billing_address'] ?? null;
465 }
466
467 /**
468 * Get the human discount attribute.
469 *
470 * @return string
471 */
472 public function getHumanDiscountAttribute() {
473 if ( empty( $this->discount->coupon ) ) {
474 return '';
475 }
476
477 if ( ! empty( $this->discount->coupon->percent_off ) ) {
478 if ( ! empty( $this->discount->coupon->amount_off ) && ! empty( $this->currency ) ) {
479 return Currency::format( $this->discount->coupon->amount_off, $this->currency );
480 }
481
482 return sprintf( __( '%1d%% off', 'surecart' ), $this->discount->coupon->percent_off | 0 );
483 }
484
485 return '';
486 }
487
488 /**
489 * Get the human discount with duration attribute.
490 *
491 * @return string
492 */
493 public function getHumanDiscountWithDurationAttribute() {
494 if ( ! $this->hasRecurring ) {
495 return $this->human_discount;
496 }
497
498 $duration = $this->discount->coupon->duration ?? '';
499 $duration_in_months = $this->discount->coupon->duration_in_months ?? 0;
500
501 switch ( $duration ) {
502 case 'once':
503 return sprintf( '%s %s', $this->human_discount, __( 'once', 'surecart' ) );
504 case 'repeating':
505 $months_label = sprintf( _n( '%d month', '%d months', $duration_in_months, 'surecart' ), $duration_in_months );
506 return sprintf( '%s for %s', $this->human_discount, $months_label );
507 default:
508 return $this->human_discount;
509 }
510 }
511
512 /**
513 * If the shipping address is required.
514 *
515 * @return bool
516 */
517 public function getShippingAddressRequiredAttribute(): bool {
518 return in_array( $this->shipping_address_accuracy_requirement, [ 'tax', 'full' ], true );
519 }
520
521 /**
522 * Get the Paid at Date attribute.
523 *
524 * @return string
525 */
526 public function getPaidAtDateAttribute() {
527 return ! empty( $this->paid_at ) ? TimeDate::formatDate( $this->paid_at ) : '';
528 }
529 }
530