PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.8.0
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.8.0
1.8.0 1.7.1 1.7.0 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 All 52 releases
← All changes | app/Helpers/CheckoutProcessor.php +97 -1 1.7.0 → 1.8.0 View file →
@@ -22,8 +22,9 @@
22 22
23 23 // Raw Data
24 24 private $cartItems = [];
25 25 private $args = [];
26 + private $validationError;
26 27
27 28 // Order Related Data
28 29 private $formattedIOrderItems = [];
29 30 private $orderData = [];
@@ -62,14 +63,43 @@
62 63
63 64 private function prepareData()
64 65 {
65 66 $this->prepareOrderItems();
67 + if ($this->validationError) {
68 + return;
69 + }
70 +
66 71 $this->prepareOrderData();
72 + if ($this->validationError) {
73 + return;
74 + }
75 +
67 76 $this->prepareSubscriptionData();
68 77 }
69 78
79 + /**
80 + * (int) on an out-of-range float wraps, and the max(0, ...) clamps downstream turn a
81 + * wrapped amount into a free but payable order. Refuse the checkout instead.
82 + */
83 + private function isSafeAmount($value)
84 + {
85 + return is_numeric($value) && is_finite((float)$value) && abs((float)$value) < (float)PHP_INT_MAX;
86 + }
87 +
88 + private function unsafeAmountError()
89 + {
90 + return new \WP_Error(
91 + 'invalid_order_total',
92 + __('The order total is too large to process. Please reduce the quantity.', 'fluent-cart')
93 + );
94 + }
95 +
70 96 public function createDraftOrder($prevOrder = null)
71 97 {
98 + if ($this->validationError) {
99 + return $this->validationError;
100 + }
101 +
72 102 if ($prevOrder) {
73 103 return $this->getAdjustedOrder($prevOrder);
74 104 }
75 105
@@ -622,9 +652,16 @@
622 652
623 653 $discountTotal = (int)Arr::get($cartItem, 'manual_discount', 0) + (int)Arr::get($cartItem, 'coupon_discount', 0);
624 654 $shippingCharge = (int)Arr::get($cartItem, 'shipping_charge', 0);
625 655
626 - $subtotal = (int) Arr::get($cartItem, 'subtotal', $unitPrice * $quantity);
656 + $rawSubtotal = Arr::get($cartItem, 'subtotal', $unitPrice * $quantity);
657 + if (!$this->isSafeAmount($rawSubtotal)) {
658 + $this->validationError = $this->unsafeAmountError();
659 +
660 + return;
661 + }
662 +
663 + $subtotal = (int) $rawSubtotal;
627 664 $args = Arr::get($cartItem, 'other_info', []);
628 665 $paymentType = Arr::get($args, 'payment_type', 'default');
629 666
630 667 $postTitle = Arr::get($cartItem, 'product_title', '');
@@ -935,8 +972,24 @@
935 972 'recurring_discount' => $recurringDiscountAmount,
936 973 'total_discount' => $discountTotal
937 974 ]);
938 975
976 + // A shipping pricing rule discounts this order's postage only; renewals bill the full charge.
977 + $shippingDiscount = $isPhysicalProduct ? (int)Arr::get($this->args, 'shipping_discount', 0) : 0;
978 + if ($shippingDiscount > 0) {
979 + $renewalTotal = $recurringTotal + $shippingDiscount;
980 + // A fully discounted charge left its shipping tax out above as well.
981 + if ($shippingCharge <= 0) {
982 + $storeTaxBehavior = (int)Arr::get($this->args, 'store_tax_behavior', $taxBehavior);
983 + if ($taxBehavior === 1 || ($taxBehavior === 3 && $storeTaxBehavior === 1)) {
984 + $renewalTotal += (int)Arr::get($this->args, 'shipping_tax', 0);
985 + }
986 + }
987 +
988 + $subscriptionPricing = $this->billRenewalsFullShipping($subscriptionPricing, $recurringTotal, $renewalTotal, $shippingDiscount);
989 + $recurringTotal = $renewalTotal;
990 + }
991 +
939 992 // removable upon discussion
940 993 $subscriptionItem = [
941 994 'product_id' => $item['post_id'],
942 995 'current_payment_method' => Arr::get($this->orderData, 'payment_method'),
@@ -982,8 +1035,44 @@
982 1035
983 1036 $this->subscriptionData = $subscriptionData;
984 1037 }
985 1038
1039 + /**
1040 + * Raise the per-cycle price to the full postage while keeping the first payment.
1041 + *
1042 + * A gateway takes the signup fee alone during a trial, otherwise one cycle plus the
1043 + * signup fee. So without a trial the fee absorbs the difference, or the first cycle
1044 + * becomes a simulated trial, the same way a first-cycle coupon is expressed.
1045 + *
1046 + * @param array $pricing From convertToSubscriptionFormat(), built on the discounted postage.
1047 + * @param int $firstCycleTotal Recurring total on the discounted postage, in cents.
1048 + * @param int $renewalTotal Recurring total on the full postage, in cents.
1049 + * @param int $shippingDiscount In cents.
1050 + * @return array
1051 + */
1052 + private function billRenewalsFullShipping(array $pricing, $firstCycleTotal, $renewalTotal, $shippingDiscount)
1053 + {
1054 + $pricing['recurring_amount'] = (int)$pricing['recurring_amount'] + $shippingDiscount;
1055 +
1056 + if ((int)$pricing['trial_days'] > 0) {
1057 + return $pricing;
1058 + }
1059 +
1060 + $extra = $renewalTotal - $firstCycleTotal;
1061 + $signupFee = (int)$pricing['signup_fee'];
1062 +
1063 + if ($signupFee >= $extra) {
1064 + $pricing['signup_fee'] = $signupFee - $extra;
1065 + return $pricing;
1066 + }
1067 +
1068 + $pricing['trial_days'] = Helper::calculateAdjustedTrialDaysForInterval(0, $pricing['billing_interval']);
1069 + $pricing['is_trial_days_simulated'] = 'yes';
1070 + $pricing['signup_fee'] = $firstCycleTotal + $signupFee;
1071 +
1072 + return $pricing;
1073 + }
1074 +
986 1075 private function determineCollectionMethod(): string
987 1076 {
988 1077 if (SubscriptionManagementMode::isStoreManaged()) {
989 1078 $paymentMethod = Arr::get($this->orderData, 'payment_method', '');
@@ -1070,8 +1159,9 @@
1070 1159 'user_tz' => Arr::get($this->args, 'user_tz', ''),
1071 1160 'create_account_after_paid' => Arr::get($this->args, 'create_account_after_paid', 'no'),
1072 1161 'shipping_method_id' => Arr::get($this->args, 'shipping_method_id', 0),
1073 1162 'shipping_method_title' => Arr::get($this->args, 'shipping_method_title', ''),
1163 + 'shipping_discount' => (int) Arr::get($this->args, 'shipping_discount', 0),
1074 1164 'prorate_credit' => $this->prorateCreditTotal,
1075 1165 'upgrade_discount' => $this->upgradeDiscountTotal,
1076 1166 ],
1077 1167 ];
@@ -1125,8 +1215,14 @@
1125 1215 $orderData = apply_filters('fluent_cart/checkout/order_data', $orderData, [
1126 1216 'items' => $this->formattedIOrderItems,
1127 1217 'args' => $this->args,
1128 1218 ]);
1219 +
1220 + if (!$this->isSafeAmount(Arr::get($orderData, 'total_amount', 0))) {
1221 + $this->validationError = $this->unsafeAmountError();
1222 +
1223 + return;
1224 + }
1129 1225
1130 1226 $this->orderData = $orderData;
1131 1227 }
1132 1228