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