PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.5.4
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.5.4
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
fluent-cart / app / Hooks / Handlers / CustomCheckout / CustomCheckout.php

CustomCheckout.php in FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler 1.5.4, at app/Hooks/Handlers/CustomCheckout/CustomCheckout.php

217 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentCart\App\Hooks\Handlers\CustomCheckout;
4
5 use FluentCart\Api\StoreSettings;
6 use FluentCart\App\Helpers\CartHelper;
7 use FluentCart\App\Helpers\Status;
8 use FluentCart\App\Models\Cart;
9 use FluentCart\App\Models\Order;
10 use FluentCart\App\Services\ProductItemService;
11 use FluentCart\Framework\Support\Arr;
12
13 class CustomCheckout
14 {
15 public function register()
16 {
17 add_action('fluent_cart_action_custom_checkout', [$this, 'handleCustomCheckoutRedirect'], 10, 1);
18 }
19
20 /*
21 * 1. validation
22 * 2. create cart
23 * - subscription
24 * discount, signup etc.
25 * - onetime
26 * 3. redirect to the checkout with cart
27 */
28 public function handleCustomCheckoutRedirect($data)
29 {
30 $orderHash = sanitize_text_field(Arr::get($data, 'order_hash', ''));
31 $order = Order::query()->where('uuid', $orderHash)->first();
32 if (!$order) {
33 die('Invalid order!');
34 }
35
36 if ($order->payment_status === Status::PAYMENT_PAID || $order->status === Status::ORDER_COMPLETED) {
37 die('Order already completed!');
38 }
39
40 $totalDue = $order->total_amount - $order->paid;
41
42 if ($totalDue <= 0) {
43 die('No due amount found!');
44 }
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
53 if ($order->type === Status::ORDER_TYPE_SUBSCRIPTION) {
54 $orderItem = $order->order_items->filter(function ($item) {
55 return !in_array($item->payment_type, ['signup_fee', 'fee']);
56 })->first();
57
58 if (!$orderItem) {
59 die('Subscription order item not found!');
60 }
61
62 $subscriptionItemData = ProductItemService::getItem([
63 'order_id' => $orderItem->order_id,
64 'product_id' => $orderItem->post_id,
65 'variation_id' => $orderItem->object_id,
66 ]);
67
68 if (!$subscriptionItemData || !$subscriptionItemData->variation) {
69 die('Failed to load product data for custom checkout!');
70 }
71
72 $newItem = $subscriptionItemData->variation->toArray();
73 $isCustom = $subscriptionItemData->is_custom;
74
75 Arr::set($newItem, 'item_price', $orderItem->unit_price);
76
77 // Keep the variation's other_info as-is (trial_days, signup_fee, repeat_interval, times).
78 // The variation's other_info has the clean product-level config that CheckoutProcessor
79 // expects when processing the cart.
80
81 if ($isCustom) {
82 Arr::set($newItem, 'post_title', $subscriptionItemData->variation->post_title);
83 Arr::set($newItem, 'variation_type', $subscriptionItemData->variation->variation_type);
84 } else {
85 Arr::set($newItem, 'post_title', $subscriptionItemData->variation->product->post_title);
86 Arr::set($newItem, 'variation_type', $subscriptionItemData->variation->product_detail->variation_type);
87 }
88
89 if ($order->coupon_discount_total > 0 || $itemManualDiscountTotal > 0) {
90 Arr::set($newItem, 'coupon_discount', (int) $order->coupon_discount_total);
91 Arr::set($newItem, 'manual_discount', $itemManualDiscountTotal);
92 }
93
94 $instantCart = CartHelper::generateCartFromCustomVariation($newItem, $orderItem->quantity);
95
96 } else {
97 $items = [];
98 $productItems = $order->order_items->filter(function ($orderItem) {
99 return !in_array($orderItem->payment_type, ['fee', 'signup_fee']);
100 });
101 foreach ($productItems as $orderItem) {
102 $itemData = ProductItemService::getItem([
103 'order_id' => $orderItem->order_id,
104 'product_id' => $orderItem->post_id,
105 'variation_id' => $orderItem->object_id,
106 ]);
107 if (!$itemData || !$itemData->variation) {
108 die('Failed to load product data for custom checkout!');
109 }
110 $item = $itemData->variation->toArray();
111
112
113 Arr::set($item, 'coupon_discount', (string)($orderItem->discount_total)); // item discount total is always coupon discount + manual discount
114 Arr::set($item, 'tax_amount', $orderItem->tax_amount);
115 Arr::set($item, 'post_title', $orderItem->post_title);
116 Arr::set($item, 'variation_type', $itemData->variation->product_detail->variation_type);
117
118 if ($itemManualDiscountTotal) {
119 $subtotal = Arr::get($orderItem, 'subtotal');
120 $manualDiscount = ($subtotal * $itemManualDiscountTotal) / $order->subtotal;
121 $couponDiscount = max(0, $orderItem->discount_total - $manualDiscount);
122 Arr::set($item, 'manual_discount', $manualDiscount);
123 Arr::set($item, 'coupon_discount', $couponDiscount);
124 }
125
126 $items[] = CartHelper::generateCartItemCustomItem($item, $orderItem->quantity);
127 }
128
129 $instantCart = new Cart();
130 $instantCart->cart_data = $items;
131 }
132
133 $primaryBillingAddress = [];
134 if ($order->billing_address) {
135 $primaryBillingAddress = $order->billing_address
136 ->getFormattedDataForCheckout('billing_');
137 }
138 $instantCart->cart_group = 'instant';
139 $instantCart->first_name = $order->customer->first_name;
140 $instantCart->last_name = $order->customer->last_name;
141 $instantCart->email = $order->customer->email;
142 $instantCart->customer_id = $order->customer->customer_id;
143 $instantCart->user_id = $order->customer->user_id;
144 $instantCart->order_id = $order->id;
145 $instantCart->cart_hash = md5('custom_payment_cart_' . wp_generate_uuid4() . time());
146
147 // Preserve original order fees so the custom payment checkout
148 // shows exactly what was charged, not whatever the current filter returns
149 $originalFees = [];
150 $feeOrderItems = $order->order_items->filter(function ($item) {
151 return $item->payment_type === 'fee';
152 });
153
154 foreach ($feeOrderItems as $feeItem) {
155 $otherInfo = $feeItem->other_info ?? [];
156 $originalFees[] = [
157 'key' => Arr::get($otherInfo, 'fee_key', ''),
158 'label' => $feeItem->title,
159 'amount' => (int) $feeItem->unit_price,
160 'source' => Arr::get($otherInfo, 'source', 'custom'),
161 'taxable' => !empty(Arr::get($otherInfo, 'taxable')),
162 'meta' => Arr::get($otherInfo, 'meta', []),
163 ];
164 }
165
166 $checkoutData = [
167 'is_locked' => 'yes',
168 'disable_coupons' => 'yes',
169 'custom_checkout' => 'yes',
170 'form_data' => $primaryBillingAddress,
171 'fees' => $originalFees,
172 'custom_checkout_data' => [
173 'coupon_discount_total' => $order->coupon_discount_total,
174 'manual_discount_total' => $itemManualDiscountTotal,
175 'discount_total' => $order->coupon_discount_total + $itemManualDiscountTotal,
176 'shipping_total' => $order->shipping_total,
177 ],
178 '__cart_notices' => [
179 [
180 'id' => 'custom_payment_notice',
181 'type' => 'info',
182 'content' => 'You are making payment for your order (#' . $order->uuid . ').',
183 ]
184 ]
185 ];
186
187 if ($prorateCredit > 0) {
188 $checkoutData['prorate_credit'] = [
189 'amount' => $prorateCredit,
190 'title' => __('Prorate Credit', 'fluent-cart'),
191 ];
192 }
193
194 if ($upgradeDiscount > 0) {
195 $checkoutData['upgrade_discount'] = [
196 'amount' => $upgradeDiscount,
197 'title' => __('Upgrade Discount', 'fluent-cart'),
198 ];
199 }
200
201 $instantCart->checkout_data = $checkoutData;
202
203 $instantCart->save();
204
205 $cartHash = $instantCart->cart_hash;
206
207 $checkoutUrl = add_query_arg(
208 [
209 'fct_cart_hash' => $cartHash,
210 ],
211 (new StoreSettings())->getCheckoutPage()
212 );
213
214 wp_redirect($checkoutUrl);
215 exit();
216 }
217 }