# fluent-cart/1.6.4/app/Helpers/AdminOrderProcessor.php

FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler, version 1.6.4. 760 lines.

- Page: https://pluginprobe.com/plugins/fluent-cart/1.6.4/code/app/Helpers/AdminOrderProcessor.php
- Raw: https://pluginprobe.com/plugins/fluent-cart/1.6.4/raw/app/Helpers/AdminOrderProcessor.php
- Modified: 2026-09-11T13:28:26+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/fluent-cart/1.6.4/code/app/Helpers/AdminOrderProcessor.php#L10-L20`.

```php
<?php

namespace FluentCart\App\Helpers;

use FluentCart\Api\StoreSettings;
use FluentCart\App\Models\Cart;
use FluentCart\App\Models\Coupon;
use FluentCart\App\Models\Order;
use FluentCart\App\Models\OrderItem;
use FluentCart\App\Models\Product;
use FluentCart\App\Models\ProductVariation;
use FluentCart\App\Models\Subscription;
use FluentCart\App\Services\Payments\PaymentHelper;
use FluentCart\Framework\Support\Arr;
use FluentCart\App\Helpers\Helper;

class AdminOrderProcessor
{
    private $args = [];
    private $checkoutItems = [];

    // Order Related Data
    private $formattedIOrderItems = [];
    private $orderData = [];
    private $subscriptionData = [];

    //  Models

    private $orderModel;

    private $transactionModel;

    private $subscriptionModel;

    // Store Settings
    private $storeSettings;


    private $couponDiscountTotal = 0;

    private $manualDiscountTotal = 0;

    public function __construct($checkoutItems = [], $args = [])
    {
        $this->storeSettings = new StoreSettings();
        $this->checkoutItems = $checkoutItems;
        $this->checkoutItems = Helper::loadBundleChild($checkoutItems, ['*']);
        $this->args = $args;

        $this->prepareData();
    }

    private function prepareData()
    {
        $this->prepareOrderItems();
        $this->prepareOrderData();
        $this->prepareSubscriptionData();
    }

    private function prepareOrderItems()
    {
        $formattedItems = [];

        foreach ($this->checkoutItems as $checkoutItem) {
            $unitPrice = (int)Arr::get($checkoutItem, 'unit_price', 0);
            $quantity = (int)Arr::get($checkoutItem, 'quantity', 1);

            //TODO:  right now discount total from admin only comes from coupon applied discount, is same as coupon_discount
            $this->couponDiscountTotal += (int)Arr::get($checkoutItem, 'discount_total', 0);
            $this->manualDiscountTotal += (int)Arr::get($checkoutItem, 'manual_discount', 0);

            $discountTotal = (int)Arr::get($checkoutItem, 'manual_discount', 0) + (int)Arr::get($checkoutItem, 'discount_total', 0);

            $shippingCharge = (int)Arr::get($checkoutItem, 'shipping_charge', 0);
            $tax = 0;
            $subtotal = $unitPrice * $quantity;
            $args = Arr::get($checkoutItem, 'other_info', []);
            $paymentType = Arr::get($args, 'payment_type', 'default');

            $postTitle = Arr::get($checkoutItem, 'product_title', '');
            $variationTitle = Arr::get($checkoutItem, 'variation_title', '');

            if (!$postTitle) {
                $product = Product::query()->find(Arr::get($checkoutItem, 'post_id', 0));
                if ($product) {
                    $postTitle = $product->post_title;
                }
            }

            $variation = null;
            if (!$variationTitle) {
                $variation = ProductVariation::query()->find(Arr::get($checkoutItem, 'object_id', 0));
                if ($variation) {
                    $variationTitle = $variation->variation_title;
                }
            }

            if (!$variation) {
                $variation = ProductVariation::query()->find(Arr::get($checkoutItem, 'object_id', 0));
            }

            $fulfillmentType = Arr::get($checkoutItem, 'fulfillment_type', '');
            if (!$fulfillmentType && $variation) {
                $fulfillmentType = $variation->fulfillment_type;
            }
            if (!$fulfillmentType) {
                $fulfillmentType = 'digital';
            }

            // Snapshot the variant's attribute set so admin-created orders carry
            // the same pa_* attribute map as storefront orders.
            if (!isset($args['item_attributes'])) {
                $args['item_attributes'] = AttributeHelper::getProductItemAttributes(
                    Arr::get($checkoutItem, 'object_id', 0),
                    Arr::get($checkoutItem, 'post_id', 0)
                );
            }

            if (!isset($args['variation_type'])) {
                $args['variation_type'] = ($variation && $variation->product_detail) ? $variation->product_detail->variation_type : '';
            }

            $item = [
                'payment_type'     => $paymentType,
                'post_id'          => Arr::get($checkoutItem, 'post_id'),
                'object_id'        => Arr::get($checkoutItem, 'object_id'),
                'post_title'       => $postTitle,
                'title'            => $variationTitle,
                'fulfillment_type' => $fulfillmentType,
                'quantity'         => $quantity,
                'cost'             => (int)Arr::get($checkoutItem, 'cost', 0),
                'unit_price'       => $unitPrice,
                'subtotal'         => $subtotal,
                'tax_amount'       => $tax,
                'shipping_charge'  => $shippingCharge,
                'discount_total'   => $discountTotal,
                'coupon_discount'  => (int)Arr::get($checkoutItem, 'discount_total', 0),
                'other_info'       => $args,
                'line_meta'        => []
            ];

            $childItem = null;
            if ($paymentType === 'subscription' && Arr::get($checkoutItem, 'other_info.signup_fee', 0)) {
                // We have a signup fee for subscription
                $singupFeeAmount = (int)Arr::get($checkoutItem, 'other_info.signup_fee', 0);

                $childDiscontTotal = 0;
                $childCouponDiscount = 0;
                $signupFeeSubtotal = $singupFeeAmount * $quantity;
                $couponDiscount = $item['coupon_discount'];

                if ($discountTotal) {
                    // Distribute discount with signup fee and item
                    $childDiscontTotal = (int)($discountTotal / ($subtotal + $signupFeeSubtotal) * $signupFeeSubtotal);
                    $discountTotal -= $childDiscontTotal;
                    $childCouponDiscount = (int) round($couponDiscount * $signupFeeSubtotal / ($subtotal + $signupFeeSubtotal));
                    $couponDiscount -= $childCouponDiscount;
                }

                $childItem = [
                    'payment_type'     => 'signup_fee',
                    'post_id'          => $item['post_id'],
                    'object_id'        => $item['object_id'],
                    'post_title'       => $item['post_title'],
                    'title'            => Arr::get($checkoutItem, 'other_info.signup_fee_name', __('Signup Fee', 'fluent-cart')),
                    'fulfillment_type' => $item['fulfillment_type'],
                    'quantity'         => $quantity,
                    'cost'             => 0,
                    'unit_price'       => $singupFeeAmount,
                    'subtotal'         => $signupFeeSubtotal,
                    'tax_amount'       => 0,
                    'shipping_charge'  => 0,
                    'discount_total'   => $childDiscontTotal,
                    'coupon_discount'  => $childCouponDiscount,
                    'line_meta'        => []
                ];

                $item['discount_total'] = $discountTotal;
                $item['coupon_discount'] = $couponDiscount;
                $item['additional_items'] = [$childItem];

                Arr::set($item, 'other_info.signup_fee', $singupFeeAmount);
                Arr::set($item, 'other_info.signup_discount', $childDiscontTotal);
            }

            if (Arr::get($checkoutItem, 'other_info.is_bundle_product', 'no') == 'yes') {
                $bundleItems = Arr::get($checkoutItem, 'child_variants', []);

                foreach ($bundleItems as $bundleItem) {
                    $bundleChildItem = [
                        'payment_type'        => 'bundle',
                        'post_id'             => Arr::get($bundleItem, 'post_id', 0),
                        'object_id'           => Arr::get($bundleItem, 'id', 0),
                        'post_title'          => Arr::get($bundleItem, 'post_title', ''),
                        'title'               => Arr::get($bundleItem, 'variation_title', ''),
                        'fulfillment_type'    => Arr::get($bundleItem, 'fulfillment_type', 'digital'),
                        'quantity'            => $quantity,
                        'cost'                => 0,
                        'unit_price'          => 0,
                        'subtotal'            => 0,
                        'tax_amount'          => 0,
                        'shipping_charge'     => 0,
                        'discount_total'      => 0,
                        'other_info'          => [
                            'bundle_parent_product_id' => Arr::get($checkoutItem, 'post_id', 0),
                            'bundle_parent_variation_id' => Arr::get($checkoutItem, 'object_id', 0)
                        ],
                    ];

                    //TODO: if bundleItem price is included on for the bundle, then we need to set the price to the bundleItem price
                    // if (Arr::get($bundleItem, 'other_info.is_price_included', 'no') == 'yes') {
                    //     $bundleChildItem['unit_price'] = Arr::get($bundleItem, 'unit_price', 0);
                    //     $bundleChildItem['subtotal'] = Arr::get($bundleItem, 'subtotal', 0);
                    //     $bundleChildItem['tax_amount'] = Arr::get($bundleItem, 'tax_amount', 0);
                    //     $bundleChildItem['shipping_charge'] = Arr::get($bundleItem, 'shipping_charge', 0);
                    //     $bundleChildItem['discount_total'] = Arr::get($bundleItem, 'discount_total', 0);
                    // }

                    $item['bundle_items'][] = $bundleChildItem;

                }

            }


            $formattedItems[] = $item;
            if ($childItem) {
                $formattedItems[] = $childItem;
            }
        }

        $this->formattedIOrderItems = $formattedItems;
    }

    private function prepareOrderData()
    {
        $hasPhysical = array_filter($this->formattedIOrderItems, function ($item) {
            return $item['fulfillment_type'] === 'physical';
        });

        $hasSubscription = array_filter($this->formattedIOrderItems, function ($item) {
            return $item['payment_type'] === 'subscription';
        });


        $itemsSubtotal = array_reduce($this->formattedIOrderItems, function ($carry, $item) {
            if (Arr::get($item, 'other_info.trial_days', 0) > 0) {
                return $carry;
            }
            return $carry + $item['subtotal'];
        }, 0);

        $orderData = [
            'status'                => Status::ORDER_ON_HOLD,
            'fulfillment_type'      => $hasPhysical ? Status::FULFILLMENT_TYPE_PHYSICAL : Status::FULFILLMENT_TYPE_DIGITAL,
            'type'                  => $hasSubscription ? Status::ORDER_TYPE_SUBSCRIPTION : Status::ORDER_TYPE_PAYMENT, // revisit this on manual renewal
            'mode'                  => $this->storeSettings->get('order_mode'),
            'shipping_status'       => $hasPhysical ? 'unshipped' : '',
            'customer_id'           => '',
            'payment_method'        => Arr::get($this->args, 'payment_method', ''),
            'payment_status'        => Status::PAYMENT_PENDING,
            'payment_method_title'  => '',
            'currency'              => $this->storeSettings->get('currency'),
            'subtotal'              => $itemsSubtotal,
            'discount_tax'          => 0,
            'manual_discount_total' => $this->manualDiscountTotal,
            'coupon_discount_total' => $this->couponDiscountTotal,
            'shipping_tax'          => 0,
            'shipping_total'        => Arr::get($this->args, 'shipping_total', 0),
            'tax_total'             => 0,
//            'total_amount'          => $this->orderTotals['total_amount'],
            'total_paid'            => 0,
            'total_refund'          => 0,
            'rate'                  => 1,
            'note'                  => Arr::get($this->args, 'note', ''),
            'ip_address'            => Arr::get($this->args, 'ip_address', ''),
            'config'                => [
                'user_tz'                   => Arr::get($this->args, 'user_tz', ''),
                'source'                    => 'admin',
            ],
        ];

        $totalAmount = $orderData['subtotal'] - $orderData['coupon_discount_total'] - $orderData['manual_discount_total'] + $orderData['shipping_total'] + $orderData['tax_total'];
        $orderData['total_amount'] = $totalAmount > 0 ? $totalAmount : 0;

        /**
         * Filter the prepared order data before it is used for order creation.
         *
         * This mirrors the frontend checkout hook so integrations can adjust
         * order payload fields consistently for manual admin orders too.
         *
         * @param array $orderData Prepared order data array.
         * @param array $context {
         *     Additional context for the filter.
         *
         *     @type array $items Formatted order items with prices and quantities.
         *     @type array $args  Admin order arguments: customer data, payment
         *                        method, shipping, tax, notes, and IP data.
         * }
         */
        $orderData = apply_filters('fluent_cart/checkout/order_data', $orderData, [
            'items' => $this->formattedIOrderItems,
            'args'  => $this->args,
        ]);

        $this->orderData = $orderData;
    }


    public function createDraftOrder($prevOrder = null)
    {
        $customerId = Arr::get($this->args, 'customer_id', '');
        if (!$customerId) {
            return new \WP_Error('customer_id_missing', __('Customer ID is required to create a draft order.', 'fluent-cart'));
        }

        $orderData = $this->orderData;
        $orderData['customer_id'] = $customerId;
        $this->orderModel = \FluentCart\App\Models\Order::query()->create($orderData);

        if (!$this->orderModel) {
            return new \WP_Error('order_creation_failed', __('Failed to create order.', 'fluent-cart'));
        }

        // Let's create the order items
        $normalOrderItems = array_filter($this->formattedIOrderItems, function ($item) {
            return $item['payment_type'] != 'signup_fee';
        });


        foreach ($normalOrderItems as $orderItem) {
            $orderItem['order_id'] = $this->orderModel->id;
            $orderItem['line_total'] = $orderItem['subtotal'] - $orderItem['discount_total'];
            $additionalItems = [];
            $bundleItems = [];
            if ($orderItem['payment_type'] == 'subscription') {
                // this is a subscription type. We may have additional_items
                $additionalItems = Arr::get($orderItem, 'additional_items', []);
                unset($orderItem['additional_items']);
            }

            if (Arr::get($orderItem, 'other_info.is_bundle_product', 'no') == 'yes') {
                $bundleItems = Arr::get($orderItem, 'bundle_items', []);
                unset($orderItem['bundle_items']);
            }

            if (!empty($orderItem['coupon_discount'])) {
                $lineMeta = Arr::get($orderItem, 'line_meta', []);
                $lineMeta['coupon_discount'] = (int) $orderItem['coupon_discount'];
                $orderItem['line_meta'] = $lineMeta;
            }

            $createdItem = OrderItem::query()->create($orderItem);

            if ($additionalItems) {
                $additionalItemIds = [];
                foreach ($additionalItems as $additionalItem) {
                    $additionalItem['order_id'] = $this->orderModel->id;
                    $additionalItem['line_total'] = $additionalItem['subtotal'] - $additionalItem['discount_total'];
                    $mata = Arr::get($additionalItem, 'line_meta', []);
                    $mata['parent_item_id'] = $createdItem->id;
                    if (!empty($additionalItem['coupon_discount'])) {
                        $mata['coupon_discount'] = (int) $additionalItem['coupon_discount'];
                    }
                    $additionalItem['line_meta'] = $mata;
                    OrderItem::query()->create($additionalItem);
                }

                $createdItem->fill([
                    'line_meta' => array_merge(
                        $createdItem->line_meta,
                        [
                            'additional_item_ids' => $additionalItemIds
                        ]
                    )
                ])->save();
            }

            if ($bundleItems) {
                $bundleItemIds = [];
                foreach ($bundleItems as $bundleItem) {
                    $bundleItem['order_id'] = $this->orderModel->id;
                    $bundleItem['line_total'] = Arr::get($bundleItem, 'subtotal', 0) - Arr::get($bundleItem, 'discount_total', 0);
                    $bundleItem['payment_type'] = 'bundle';
                    $meta = Arr::get($bundleItem, 'line_meta', []);
                    $meta['bundle_parent_item_id'] = $createdItem->id;
                    if (!empty($bundleItem['coupon_discount'])) {
                        $meta['coupon_discount'] = (int) $bundleItem['coupon_discount'];
                    }
                    $bundleItem['line_meta'] = $meta;
                    $bundleItem = OrderItem::query()->create($bundleItem);
                    $bundleItemIds[] = $bundleItem->id;
                }

                $createdItem->fill([
                    'line_meta' => array_merge(
                        $createdItem->line_meta,
                        [
                            'bundle_item_ids' => $bundleItemIds
                        ]
                    )
                ])->save();
            }
        }

        // Let's create the subscription if exists
        if ($this->subscriptionData) {
            $subscriptionData = $this->subscriptionData;
            $subscriptionData['customer_id'] = $customerId;
            $subscriptionData['parent_order_id'] = $this->orderModel->id;
            $this->subscriptionModel = Subscription::query()->create($subscriptionData);

            // bill_count counting can't tell "billed cycle" from "something else
            // charged alongside it." Must be decided at creation: payment-method switching
            // also sets is_trial_days_simulated, so the flag alone can't be trusted at runtime.
            $isSimulated = Arr::get($subscriptionData, 'config.is_trial_days_simulated', 'no') === 'yes';
            $trialDays   = (int)Arr::get($subscriptionData, 'trial_days', 0);
            $billTimes   = (int)$this->subscriptionModel->bill_times;
            $orderTotal  = (int)$this->orderModel->total_amount;

            // Simulated trial, $0 first cycle: consumes a cycle but produces no
            // total > 0 transaction — add billed_cycles_offset so it still counts.
            if ($isSimulated
                && $billTimes > 0
                && (int)$this->subscriptionModel->signup_fee <= 0
                && !$orderTotal
            ) {
                $this->subscriptionModel->updateMeta('billed_cycles_offset', 1);
            }

            // Real trial with a signup fee: the initial charge is the signup fee only,
            // but it IS a total > 0 transaction linked to the subscription — mark
            // billed_cycles_deduction so it does NOT count as a cycle.
            if (!$isSimulated
                && $trialDays > 0
                && $billTimes > 0
                && $orderTotal > 0
            ) {
                $this->subscriptionModel->updateMeta('billed_cycles_deduction', 1);
            }
        }

        // Let's create the transaction
        $transactionData = [
            'order_id'            => $this->orderModel->id,
            'order_type'          => $this->orderModel->type,
            'transaction_type'    => Status::TRANSACTION_TYPE_CHARGE,
            'subscription_id'     => $this->subscriptionModel ? $this->subscriptionModel->id : NULL,
            'payment_method'      => $this->orderModel->payment_method,
            'payment_mode'        => $this->orderModel->mode,
            'payment_method_type' => '',
            'status'              => Status::PAYMENT_PENDING,
            'currency'            => $this->orderModel->currency,
            'total'               => $this->orderModel->total_amount,
            'rate'                => 1,
            'meta'                => [],
        ];

        $this->transactionModel = \FluentCart\App\Models\OrderTransaction::query()->create($transactionData);

        // insert the applied coupons
        $this->insertAppliedCoupons(Arr::get($this->args, 'applied_coupons', []));

        $cartHash = Arr::get($this->args, 'cart_hash', '');
        if ($cartHash) {
            $cart = Cart::query()->where('cart_hash', $cartHash)->first();
            if ($cart) {
                $cart->order_id = $this->orderModel->id;
                $cart->customer_id = $this->orderModel->customer_id;
                $cart->stage = 'intended';
                $cart->save();
                $actions = Arr::get($cart->checkout_data, '__after_draft_created_actions__', []);
                if ($actions) {
                    foreach ($actions as $actionName) {
                        $actionName = (string)$actionName;
                        if (has_action($actionName)) {
                            do_action($actionName, [
                                'order' => $this->orderModel,
                                'cart'  => $cart,
                            ]);
                        }
                    }
                }
            }
        }

        // We are almost done!
        return $this->orderModel;
    }


    private function insertAppliedCoupons($appliedCoupons, $removeOldCoupons = false): void
    {
        if ($removeOldCoupons) {
            $this->orderModel->appliedCoupons()->delete();
        }

        $couponCodes = array_keys($appliedCoupons);

        if (!empty($couponCodes)) {
            $coupons = Coupon::query()->whereIn('code', $couponCodes)->get()
                ->keyBy('code')
                ->toArray();

            foreach ($coupons as $code => &$coupon) {
                $coupon['coupon_id'] = $appliedCoupons[$code]['id'];
                $coupon['amount'] = $appliedCoupons[$code]['discount'];
            }
            $this->orderModel->appliedCoupons()->createMany($coupons);

            Coupon::query()
                ->whereIn('code', $couponCodes)
                ->increment('use_count', 1);
        }
    }

    public function getTransaction()
    {
        return $this->transactionModel;
    }

    public function getOrder()
    {
        return $this->orderModel;
    }

    public function getSubscription()
    {
        return $this->subscriptionModel;
    }
    private function prepareSubscriptionData()
    {
        $subscriptionItems = array_filter($this->formattedIOrderItems, function ($item) {
            return $item['payment_type'] === 'subscription';
        });

        $signupFeeItems = array_filter($this->formattedIOrderItems, function ($item) {
            return $item['payment_type'] === 'signup_fee';
        });

        if (!$subscriptionItems) {
            return;
        }

        if (count($subscriptionItems) > 1) {
            return;
        }

        $item = reset($subscriptionItems);
        $signupFeeItem = reset($signupFeeItems) ?? [];

        $totalSignup = Arr::get($item, 'other_info.signup_fee', 0) - Arr::get($item, 'other_info.signup_discount', 0);
        $firstPrice = Arr::get($item, 'subtotal') + $totalSignup - Arr::get($item, 'discount_total', 0);
        $recurringPrice = Arr::get($item, 'subtotal', 0) + Arr::get($item, 'tax_amount', 0);

        if ($firstPrice < $recurringPrice) {
            Arr::set($item, 'other_info.trial_days', PaymentHelper::getIntervalDays(Arr::get($item, 'other_info.repeat_interval')));
            Arr::set($item, 'other_info.signup_fee', $firstPrice);
            Arr::set($item, 'other_info.manage_setup_fee', 'yes');
            // times stays the full installment count — the simulated trial cycle is installment #1
        } else if ($firstPrice > $recurringPrice) {
            Arr::set($item, 'other_info.signup_fee', $firstPrice - $recurringPrice);
            Arr::set($item, 'other_info.manage_setup_fee', 'yes');
            Arr::set($item, 'other_info.trial_days', 0);
        }

        $subscriptionPricing = $this->convertToSubscriptionFormat([
            'initial_trial_days' => Arr::get($item, 'other_info.trial_days', 0),
            'repeat_interval'    => Arr::get($item, 'other_info.repeat_interval', 'monthly'),
            'times'              => Arr::get($item, 'other_info.times', 0),
            'recurring_amount'   => $item['subtotal'],
            'signup_fee'         => $signupFeeItem ? $signupFeeItem['subtotal'] : 0,
            'total_discount'     => $item['discount_total'] + Arr::get($signupFeeItem, 'discount_total', 0)
        ]);

        // removable upon discussion
        $subscriptionItem = [
            'product_id'             => $item['post_id'],
            'current_payment_method' => Arr::get($this->orderData, 'payment_method'),
            'object_id'              => $item['object_id'],
            'recurring_tax_total'    => 0,
            'recurring_total'        => $subscriptionPricing['recurring_amount'], //use price not line_total to ignore discount
            'item_name'              => $item['post_title'] . ' - ' . $item['title'],
            'bill_count'             => 0,
            'quantity'               => 1,
            'variation_id'           => Arr::get($item, 'object_id', 0),
            'status'                 => Status::SUBSCRIPTION_PENDING,
            'collection_method'      => Status::SUBSCRIPTION_METHOD_MANUAL,
            'config'                 => [
                'currency' => $this->orderData['currency'],
                'is_trial_days_simulated' => Arr::get($subscriptionPricing, 'is_trial_days_simulated', 'no'),
                // Snapshot the variant attribute map + variation type from the order
                // item so the subscription carries the same pa_* set behind its item_name.
                'item_attributes' => Arr::get($item, 'other_info.item_attributes', []),
                'variation_type'  => Arr::get($item, 'other_info.variation_type', ''),
            ]
        ];

        $this->subscriptionData = wp_parse_args($subscriptionPricing, $subscriptionItem);
    }

    /**
     * @param $inputData
     * @return array
     */
    private function convertToSubscriptionFormat($inputData)
    {

        /**
         *  Normal Subscription $100/month
         *  {
         *      'trial_days' => 0,
         *      'repeat_interval' => 'month',
         *      'times' => 0, // 0 means unlimited
         *      'recurring_amount' => 100,
         *      'signup_fee' => 0
         *  }
         *
         * 30 Days Trial $100/month
         * {
         *     'trial_days' => 30,
         *     'repeat_interval' => 'month',
         *     'times' => 0, // 0 means unlimited
         *     'recurring_amount' => 100,
         * }
         *
         * $100/month - 30 days Trial with $40 signup fee
         *  {
         *      'trial_days' => 30,
         *      'repeat_interval' => 'month',
         *      'times' => 0, // 0 means unlimited
         *      'recurring_amount' => 100,
         *      'signup_fee' => 40
         *  }
         *
         * $100 / month with $40 signup fee
         *   {
         *       'trial_days' => 0,
         *       'repeat_interval' => 'month',
         *       'times' => 0, // 0 means unlimited
         *       'recurring_amount' => 100,
         *       'signup_fee' => 40
         *   }
         *
         * $100 per month but 30% discount on first month
         *    {
         *        'trial_days' => 30,
         *        'repeat_interval' => 'month',
         *        'times' => 0, // 0 means unlimited
         *        'recurring_amount' => 100,
         *        'signup_fee' => 70
         *    }
         *
         *  $100 per month but 50% extra on first month
         *     {
         *         'trial_days' => 0,
         *         'repeat_interval' => 'month',
         *         'times' => 0, // 0 means unlimited
         *         'recurring_amount' => 100,
         *         'signup_fee' => 50
         *     }
         *
         *   $100 per month and 1 month trial and service_fee = $50
         *      {
         *          'trial_days' => 30,
         *          'repeat_interval' => 'month',
         *          'times' => 0, // 0 means unlimited
         *          'recurring_amount' => 100,
         *          'signup_fee' => 50
         *      }
         *
         *
         *    $100 per month, signup fee $200 - First Month 50% discount
         *
         *    $first Month = 100 + 200 = 300 - 50% discount = 150
         *       {
         *           'trial_days' => 30,
         *           'repeat_interval' => 'month',
         *           'times' => 0, // 0 means unlimited
         *           'recurring_amount' => 100,
         *           'signup_fee' => 150
         *       }
         *
         *      // prefered when signup_fee > recurring_amount
         *      {
         *            'trial_days' => 0,
         *            'repeat_interval' => 'month',
         *            'times' => 0, // 0 means unlimited
         *            'recurring_amount' => 100,
         *            'signup_fee' => 50
         *      }
         */


        // Extract and validate input data
        $trialDays = (int)($inputData['initial_trial_days'] ?? 0);
        $repeatInterval = strtolower(trim($inputData['repeat_interval'] ?? 'yearly'));
        $times = (int)($inputData['times'] ?? 0);
        $recurringAmount = (int)($inputData['recurring_amount'] ?? 0);
        $signupFee = (int)($inputData['signup_fee'] ?? 0);
        $totalDiscount = (int)($inputData['total_discount'] ?? 0);

        // Convert repeat_interval to standard format
        $intervalMap = Helper::getAvailableSubscriptionIntervalMaps();
        $standardInterval = Helper::translateIntervalToStandardFormat($repeatInterval);


        // Initialize result array
        $result = [
            'trial_days'       => $trialDays,
            'repeat_interval'  => $standardInterval,
            'times'            => $times,
            'recurring_amount' => $recurringAmount,
        ];


        // Calculate signup fee logic
        if ($totalDiscount > 0) {

            $firstCycleCost = $recurringAmount + $signupFee - $totalDiscount;

            if ($firstCycleCost < $recurringAmount) {
                $adjustedTrialDays = Helper::calculateAdjustedTrialDaysForInterval($trialDays, $repeatInterval);

                $result['trial_days'] = $adjustedTrialDays;
                $result['is_trial_days_simulated'] = 'yes';
                $result['signup_fee'] = $firstCycleCost;
                $result['manage_setup_fee'] = 'yes';
                // bill_times stays the full installment count. The simulated trial cycle IS the
                // first installment; gateways derive remaining remote cycles from is_trial_days_simulated.
            } else if ($firstCycleCost > $recurringAmount) {
                $result['trial_days'] = 0;
                $result['signup_fee'] = $firstCycleCost - $recurringAmount;
                $result['manage_setup_fee'] = 'yes';
            } else if ($firstCycleCost == $recurringAmount) {
                $result['trial_days'] = 0;
                $result['signup_fee'] = 0;
                $result['manage_setup_fee'] = 'no';
            }

        } else {
            $result['signup_fee'] = $signupFee;
        }

        // inverse the repeat interval to match the expected format

        $result['repeat_interval'] = array_flip($intervalMap)[$result['repeat_interval']] ?? 'yearly';


        return [
            'billing_interval' => $result['repeat_interval'],
            'bill_times'       => $result['times'],
            'trial_days'       => $result['trial_days'],
            'is_trial_days_simulated' => Arr::get($result, 'is_trial_days_simulated', 'no'),
            'recurring_amount' => $result['recurring_amount'],
            'signup_fee'       => $result['signup_fee'] ?? 0,
        ];
    }
}

```
