PluginProbe ʕ •ᴥ•ʔ
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 3.0.0-beta2
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v3.0.0-beta2
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 2 years ago AbandonedCheckoutProtocol.php 3 years ago Account.php 3 years ago AccountPortalSession.php 4 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 4 years ago Brand.php 4 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 4 years ago Checkout.php 1 year ago Click.php 2 years ago Collection.php 2 years ago CommissionStructure.php 2 years ago Component.php 4 years ago Coupon.php 1 year ago Customer.php 2 years ago CustomerLink.php 4 years ago CustomerNotificationProtocol.php 4 years 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 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 2 years ago Integration.php 2 years ago Invoice.php 4 years ago License.php 2 years ago LineItem.php 1 year ago ManualPaymentMethod.php 3 years ago Media.php 1 year ago Model.php 1 year ago ModelInterface.php 4 years ago Order.php 2 years ago OrderProtocol.php 4 years ago PaymentIntent.php 4 years ago PaymentMethod.php 4 years ago Payout.php 2 years ago PayoutGroup.php 2 years ago Period.php 3 years ago PortalProtocol.php 4 years ago PortalSession.php 4 years ago Price.php 1 year ago Processor.php 3 years ago Product.php 1 year ago ProductCollection.php 1 year ago ProductGroup.php 2 years ago ProductMedia.php 1 year ago Promotion.php 4 years ago ProvisionalAccount.php 2 years ago Purchase.php 2 years ago Referral.php 2 years ago ReferralItem.php 2 years ago Refund.php 4 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 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 4 years ago TaxZone.php 4 years ago Upload.php 4 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 2 years ago WebhookRegistration.php 2 years ago
Checkout.php
507 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 * Get the human discount attribute.
161 *
162 * @return string
163 */
164 public function getHumanDiscountAttribute() {
165 if ( empty( $this->discount->coupon ) ) {
166 return '';
167 }
168
169 if ( ! empty( $this->discount->coupon->percent_off ) ) {
170 if ( ! empty( $this->discount->coupon->amount_off ) && ! empty( $this->currency ) ) {
171 return Currency::format( $this->discount->coupon->amount_off, $this->currency );
172 }
173
174 return sprintf( __( '%1d%% off', 'surecart' ), $this->discount->coupon->percent_off | 0 );
175 }
176
177 return '';
178 }
179
180 /**
181 * Get the human discount with duration attribute.
182 *
183 * @return string
184 */
185 public function getHumanDiscountWithDurationAttribute() {
186 if ( ! $this->hasRecurring ) {
187 return $this->human_discount;
188 }
189
190 $duration = $this->discount->coupon->duration ?? '';
191 $duration_in_months = $this->discount->coupon->duration_in_months ?? 0;
192
193 switch ( $duration ) {
194 case 'once':
195 return sprintf( '%s %s', $this->human_discount, __( 'once', 'surecart' ) );
196 case 'repeating':
197 $months_label = sprintf( _n( '%d month', '%d months', $duration_in_months, 'surecart' ), $duration_in_months );
198 return sprintf( '%s for %s', $this->human_discount, $months_label );
199 default:
200 return $this->human_discount;
201 }
202 }
203
204 /**
205 * Set attributes during write actions.
206 *
207 * @return void
208 */
209 protected function setWriteAttributes() {
210 $this->setAttribute( 'ip_address', $this->getIPAddress() );
211 if ( isset( $_COOKIE['sc_click_id'] ) ) {
212 $this->setAttribute( 'last_click', $_COOKIE['sc_click_id'] );
213 }
214 }
215
216 /**
217 * Set the upsell funnel attribute
218 *
219 * @param object $value The data array.
220 * @return void
221 */
222 public function setUpsellFunnelAttribute( $value ) {
223 $this->setRelation( 'upsell_funnel', $value, UpsellFunnel::class );
224 }
225
226 /**
227 * Set the upsell funnel attribute
228 *
229 * @param object $value The data array.
230 * @return void
231 */
232 public function setCurrentUpsellAttribute( $value ) {
233 $this->setRelation( 'current_upsell', $value, Upsell::class );
234 }
235
236 /**
237 * Set the recommended bumps attribute
238 *
239 * @param object $value Subscription data array.
240 * @return void
241 */
242 public function setRecommendedBumpsAttribute( $value ) {
243 $this->setCollection( 'recommended_bumps', $value, Bump::class );
244 }
245
246 /**
247 * Create a new model
248 *
249 * @param array $attributes Attributes to create.
250 *
251 * @return $this|\WP_Error|false
252 */
253 protected function create( $attributes = [] ) {
254 $this->setWriteAttributes();
255 return parent::create( $attributes );
256 }
257
258 /**
259 * Update the model
260 *
261 * @param array $attributes Attributes to create.
262 *
263 * @return $this|\WP_Error|false
264 */
265 protected function update( $attributes = [] ) {
266 $this->setWriteAttributes();
267
268 return parent::update( $attributes );
269 }
270
271 /**
272 * Get the IP address of the user
273 *
274 * TOD0: Move this to a helper class.
275 *
276 * @return string
277 */
278 protected function getIPAddress() {
279 return $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';
280 }
281
282 /**
283 * Set the product attribute
284 *
285 * @param object $value Product properties.
286 * @return void
287 */
288 public function setLineItemsAttribute( $value ) {
289 $this->setCollection( 'line_items', $value, LineItem::class );
290 }
291
292 /**
293 * Finalize the session for checkout.
294 *
295 * @return $this|\WP_Error
296 */
297 protected function manuallyPay() {
298 if ( $this->fireModelEvent( 'manually_paying' ) === false ) {
299 return false;
300 }
301
302 if ( empty( $this->attributes['id'] ) ) {
303 return new \WP_Error( 'not_saved', 'Please create the checkout session.' );
304 }
305
306 $finalized = \SureCart::request(
307 $this->endpoint . '/' . $this->attributes['id'] . '/manually_pay/',
308 [
309 'method' => 'PATCH',
310 'query' => $this->query,
311 'body' => [
312 $this->object_name => $this->getAttributes(),
313 ],
314 ]
315 );
316
317 if ( is_wp_error( $finalized ) ) {
318 return $finalized;
319 }
320
321 $this->resetAttributes();
322
323 $this->fill( $finalized );
324
325 $this->fireModelEvent( 'manually_paid' );
326
327 return $this;
328 }
329
330 /**
331 * Cancel an checkout
332 *
333 * @return $this|\WP_Error
334 */
335 protected function cancel() {
336 if ( $this->fireModelEvent( 'cancelling' ) === false ) {
337 return false;
338 }
339
340 if ( empty( $this->attributes['id'] ) ) {
341 return new \WP_Error( 'not_saved', 'Please create the order.' );
342 }
343
344 $cancelled = $this->makeRequest(
345 [
346 'method' => 'PATCH',
347 'query' => $this->query,
348 'body' => [
349 $this->object_name => $this->getAttributes(),
350 ],
351 ],
352 $this->endpoint . '/' . $this->attributes['id'] . '/cancel/'
353 );
354
355 if ( is_wp_error( $cancelled ) ) {
356 return $cancelled;
357 }
358
359 $this->resetAttributes();
360
361 $this->fill( $cancelled );
362
363 $this->fireModelEvent( 'cancelled' );
364
365 return $this;
366 }
367
368 /**
369 * Offer a bump.
370 *
371 * @param string|\SureCart\Models\Bump $bump The bump object.
372 *
373 * @return true|\WP_Error
374 */
375 protected function offerBump( $bump ) {
376 if ( $this->fireModelEvent( 'offering_bump' ) === 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( $bump, Bump::class ) ? $bump->id : $bump;
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_bump/' . $id
399 );
400
401 if ( is_wp_error( $offered ) ) {
402 return $offered;
403 }
404
405 return true;
406 }
407
408 /**
409 * Offer an upsell.
410 *
411 * @param string|\SureCart\Models\Upsell $upsell The upsell object.
412 *
413 * @return true|\WP_Error
414 */
415 protected function offerUpsell( $upsell ) {
416 if ( $this->fireModelEvent( 'offering_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 $offered = $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'] . '/offer_upsell/' . $id
439 );
440
441 if ( is_wp_error( $offered ) ) {
442 return $offered;
443 }
444
445 return true;
446 }
447
448 /**
449 * Decline an upsell.
450 *
451 * @param string|\SureCart\Models\Upsell $upsell The upsell object.
452 *
453 * @return $this|\WP_Error
454 */
455 protected function declineUpsell( $upsell ) {
456 if ( $this->fireModelEvent( 'declining_upsell' ) === false ) {
457 return false;
458 }
459
460 if ( empty( $this->attributes['id'] ) ) {
461 return new \WP_Error( 'not_saved', 'Please create the checkout.' );
462 }
463
464 $id = is_a( $upsell, Upsell::class ) ? $upsell->id : $upsell;
465
466 if ( empty( $id ) ) {
467 return new \WP_Error( 'not_saved', 'Please pass an upsell.' );
468 }
469
470 $declined = $this->makeRequest(
471 [
472 'method' => 'PATCH',
473 'query' => $this->query,
474 'body' => [
475 $this->object_name => $this->getAttributes(),
476 ],
477 ],
478 $this->endpoint . '/' . $this->attributes['id'] . '/decline_upsell/' . $id
479 );
480
481 if ( is_wp_error( $declined ) ) {
482 return $declined;
483 }
484
485 $this->resetAttributes();
486
487 $this->fill( $declined );
488
489 $this->fireModelEvent( 'declined' );
490
491 return $this;
492 }
493
494 /**
495 * Get the billing address attribute
496 *
497 * @return array|null The billing address.
498 */
499 public function getBillingAddressDisplayAttribute() {
500 if ( $this->billing_matches_shipping ) {
501 return $this->shipping_address;
502 }
503
504 return $this->attributes['billing_address'] ?? null;
505 }
506 }
507