PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
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 1.3.21 1.3.20 1.3.19 All 49 releases
← All changes | app/Hooks/Handlers/CustomCheckout/CustomCheckout.php +97 -38 1.3.28 → 1.6.5 View file →
@@ -42,8 +42,15 @@
42 42 if ($totalDue <= 0) {
43 43 die('No due amount found!');
44 44 }
45 45
46 + // Post-tax credits (plan upgrade) live inside manual_discount_total on the order
47 + // but must NOT be redistributed as item-level manual discounts on re-pay — that
48 + // would shrink the tax base. They are re-applied post-tax via checkout_data below.
49 + $prorateCredit = (int) Arr::get($order->config, 'prorate_credit', 0);
50 + $upgradeDiscount = (int) Arr::get($order->config, 'upgrade_discount', 0);
51 + $itemManualDiscountTotal = max(0, (int) $order->manual_discount_total - $prorateCredit - $upgradeDiscount);
52 +
46 53 if ($order->type === Status::ORDER_TYPE_SUBSCRIPTION) {
47 54 $orderItem = $order->order_items->filter(function ($item) {
48 55 return !in_array($item->payment_type, ['signup_fee', 'fee']);
49 56 })->first();
@@ -78,49 +85,18 @@
78 85 Arr::set($newItem, 'post_title', $subscriptionItemData->variation->product->post_title);
79 86 Arr::set($newItem, 'variation_type', $subscriptionItemData->variation->product_detail->variation_type);
80 87 }
81 88
82 - if ($order->coupon_discount_total > 0 || $order->manual_discount_total > 0) {
89 + if ($order->coupon_discount_total > 0 || $itemManualDiscountTotal > 0) {
83 90 Arr::set($newItem, 'coupon_discount', (int) $order->coupon_discount_total);
84 - Arr::set($newItem, 'manual_discount', (int) $order->manual_discount_total);
91 + Arr::set($newItem, 'manual_discount', $itemManualDiscountTotal);
85 92 }
86 93
87 94 $instantCart = CartHelper::generateCartFromCustomVariation($newItem, $orderItem->quantity);
88 95
89 96 } else {
90 - $items = [];
91 - $productItems = $order->order_items->filter(function ($orderItem) {
92 - return !in_array($orderItem->payment_type, ['fee', 'signup_fee']);
93 - });
94 - foreach ($productItems as $orderItem) {
95 - $itemData = ProductItemService::getItem([
96 - 'order_id' => $orderItem->order_id,
97 - 'product_id' => $orderItem->post_id,
98 - 'variation_id' => $orderItem->object_id,
99 - ]);
100 - if (!$itemData || !$itemData->variation) {
101 - die('Failed to load product data for custom checkout!');
102 - }
103 - $item = $itemData->variation->toArray();
104 -
105 -
106 - Arr::set($item, 'coupon_discount', (string)($orderItem->discount_total)); // item discount total is always coupon discount + manual discount
107 - Arr::set($item, 'tax_amount', $orderItem->tax_amount);
108 - Arr::set($item, 'post_title', $orderItem->post_title);
109 -
110 - if ($order->manual_discount_total) {
111 - $subtotal = Arr::get($orderItem, 'subtotal');
112 - $manualDiscount = ($subtotal * $order->manual_discount_total) / $order->subtotal;
113 - $couponDiscount = max(0, $orderItem->discount_total - $manualDiscount);
114 - Arr::set($item, 'manual_discount', $manualDiscount);
115 - Arr::set($item, 'coupon_discount', $couponDiscount);
116 - }
117 -
118 - $items[] = CartHelper::generateCartItemCustomItem($item, $orderItem->quantity);
119 - }
120 -
121 97 $instantCart = new Cart();
122 - $instantCart->cart_data = $items;
98 + $instantCart->cart_data = static::buildOneTimeCartItems($order, $itemManualDiscountTotal);
123 99 }
124 100
125 101 $primaryBillingAddress = [];
126 102 if ($order->billing_address) {
@@ -154,9 +130,9 @@
154 130 'meta' => Arr::get($otherInfo, 'meta', []),
155 131 ];
156 132 }
157 133
158 - $instantCart->checkout_data = [
134 + $checkoutData = [
159 135 'is_locked' => 'yes',
160 136 'disable_coupons' => 'yes',
161 137 'custom_checkout' => 'yes',
162 138 'form_data' => $primaryBillingAddress,
@@ -162,10 +138,10 @@
162 138 'form_data' => $primaryBillingAddress,
163 139 'fees' => $originalFees,
164 140 'custom_checkout_data' => [
165 141 'coupon_discount_total' => $order->coupon_discount_total,
166 - 'manual_discount_total' => $order->manual_discount_total,
167 - 'discount_total' => $order->coupon_discount_total + $order->manual_discount_total,
142 + 'manual_discount_total' => $itemManualDiscountTotal,
143 + 'discount_total' => $order->coupon_discount_total + $itemManualDiscountTotal,
168 144 'shipping_total' => $order->shipping_total,
169 145 ],
170 146 '__cart_notices' => [
171 147 [
@@ -170,13 +146,39 @@
170 146 '__cart_notices' => [
171 147 [
172 148 'id' => 'custom_payment_notice',
173 149 'type' => 'info',
174 - 'content' => 'You are making payment for your order (#' . $order->uuid . ').',
150 + 'content' => sprintf(
151 + /* translators: %1$s: the order UUID. */
152 + __('You are making payment for your order (#%1$s).', 'fluent-cart'),
153 + $order->uuid
154 + ),
175 155 ]
176 156 ]
177 157 ];
178 158
159 + if ($order->type === Status::ORDER_TYPE_RENEWAL) {
160 + $checkoutData['renewal_order'] = [
161 + 'due_date' => $order->getMeta('due_date'),
162 + ];
163 + }
164 +
165 + if ($prorateCredit > 0) {
166 + $checkoutData['prorate_credit'] = [
167 + 'amount' => $prorateCredit,
168 + 'title' => __('Prorate Credit', 'fluent-cart'),
169 + ];
170 + }
171 +
172 + if ($upgradeDiscount > 0) {
173 + $checkoutData['upgrade_discount'] = [
174 + 'amount' => $upgradeDiscount,
175 + 'title' => __('Upgrade Discount', 'fluent-cart'),
176 + ];
177 + }
178 +
179 + $instantCart->checkout_data = $checkoutData;
180 +
179 181 $instantCart->save();
180 182
181 183 $cartHash = $instantCart->cart_hash;
182 184
@@ -188,6 +190,63 @@
188 190 );
189 191
190 192 wp_redirect($checkoutUrl);
191 193 exit();
194 + }
195 +
196 + /**
197 + * Build cart items for a one-time (non-subscription) order re-pay, priced at the
198 + * order's own unit_price rather than the current catalogue price.
199 + */
200 + protected static function buildOneTimeCartItems(Order $order, $itemManualDiscountTotal)
201 + {
202 + $items = [];
203 + $productItems = $order->order_items->filter(function ($orderItem) {
204 + return !in_array($orderItem->payment_type, ['fee', 'signup_fee']);
205 + });
206 +
207 + foreach ($productItems as $orderItem) {
208 + $itemData = ProductItemService::getItem([
209 + 'order_id' => $orderItem->order_id,
210 + 'product_id' => $orderItem->post_id,
211 + 'variation_id' => $orderItem->object_id,
212 + ]);
213 + if (!$itemData || !$itemData->variation) {
214 + die('Failed to load product data for custom checkout!');
215 + }
216 + $item = $itemData->variation->toArray();
217 +
218 + Arr::set($item, 'coupon_discount', (string)($orderItem->discount_total)); // item discount total is always coupon discount + manual discount
219 + Arr::set($item, 'tax_amount', $orderItem->tax_amount);
220 + Arr::set($item, 'post_title', $orderItem->post_title);
221 + Arr::set($item, 'variation_type', $itemData->variation->product_detail->variation_type);
222 +
223 + // Use the order item's unit_price, not the current catalogue price — the order
224 + // may carry a price agreed at purchase time (adjusted unit_price, since expired
225 + // catalogue price) that differs from what the product sells for today.
226 + Arr::set($item, 'item_price', $orderItem->unit_price);
227 +
228 + // For renewal invoices, also strip trial_days so CheckoutProcessor doesn't
229 + // apply trial (0) pricing on a renewal.
230 + if ($order->type === Status::ORDER_TYPE_RENEWAL) {
231 + $otherInfo = Arr::get($item, 'other_info', []);
232 + $otherInfo['trial_days'] = 0;
233 + Arr::set($item, 'other_info', $otherInfo);
234 + }
235 +
236 + if ($itemManualDiscountTotal) {
237 + // Use the order item's own stored split rather than proportionally
238 + // redistributing the order-level manual_discount_total — a manual
239 + // discount concentrated on one item must stay on that item, or this
240 + // reconstruction both keeps it there AND leaves a duplicate slice
241 + // behind as "coupon_discount" on it.
242 + $manualDiscount = max(0, (int) $orderItem->discount_total - (int) $orderItem->coupon_discount);
243 + Arr::set($item, 'manual_discount', $manualDiscount);
244 + Arr::set($item, 'coupon_discount', (int) $orderItem->coupon_discount);
245 + }
246 +
247 + $items[] = CartHelper::generateCartItemCustomItem($item, $orderItem->quantity);
248 + }
249 +
250 + return $items;
192 251 }
193 252 }