← All changes
|
app/Hooks/Handlers/CustomCheckout/CustomCheckout.php
+97
-38
1.3.22
→
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 | } |