I'm working in PHP and building a subscription management system from scratch.I'm trying to figure out the required functions for the Subscription interface (OOP) that need to be implemented by classes that will keep track of subscription type, price, start/end dates of billing cycles, billing day of month, the start date of the subscription, the paid cycles count, etc.Requirement & Purpose: the entire system will be queried each day to check if each subscription needs to be billed or re-billed if failed previously.What is a good way to do this? Is what I have so far okay?Conceptually, I thought of the following, but am not sure if I am going in the right direction:/*** Run this daily ***/// Retrieve from DB$subscription = new Subscription_Magazine('SUBSCRIPTION_ID');// Call a utils functionif (SubscriptionUtils::isPaymentNeeded($subscription)){    // Bill one cycle    SubscriptionUtils::charge($subscription, $subscription->getPaymentMethod(), 1);}/*** Utils to handle logic ***/class SubscriptionUtils{    const RETRY_ONLY_AFTER = +1 day;    public static function isPaymentNeeded($subscription)    {        // Check if neither active nor past due        if (!$subscription->isActive() && !$subscription->isPastDue())        {            // Expired or canceled            return false;        }        // Check for recurring        if (!$subscription->isRecurring())        {            return false;        }        /* ? ? ?   |   |   |   |   |   |   |   ? ? ? */        /* ? ? ?  \|/ \|/ \|/ \|/ \|/ \|/ \|/  ? ? ? */        /* ? ? ?   V   V   V   V   V   V   V   ? ? ? */        // Get subscribe date, cycles paid count, and length of cycle        $regDate = $subscription->getSubscriptionStartDate(); //unix timestamp        $cyclesPaid = $subscription->getPaidCyclesCount(); //int        $cycleLength = $subscription->getCycleLength(); //+1 month / +6 months / +1 year        // Check if paid through date is more than one month from now        $paidThroughDate = self::modifyTimestamp($regDate, $cycleLength, $cyclesPaid);        $oneCycleFromNow = self::modifyTimestamp(time(), $cycleLength);        if ($paidThroughDate > $oneCycleFromNow)        {            return false;        }        // Check if we are on or passed the billing date        $currentDay = date(j); //int 1-31        $billingDay = $subscription->getBillingDayOfMonth(); //int 1-31        $daysInMonth = date(t); //int 28-31        // Use valid day        $thisBillingDay = $billingDay > $daysInMonth ? $daysInMonth : $billingDay;        if ($thisBillingDay < $billingDay)        {            return false;        }        // Check if we already tried recently        $transaction = $subscriptions->getLastTransaction();        if ($transaction instanceof Transaction && time() < modifyTimestamp($transaction->date(), self::RETRY_ONLY_AFTER))        {            return false;        }        //yes, return true that we need to charge now        return true;        /* ? ? ?   ^   ^   ^   ^   ^   ^   ^   ? ? ? */        /* ? ? ?  /|\ /|\ /|\ /|\ /|\ /|\ /|\  ? ? ? */        /* ? ? ?   |   |   |   |   |   |   |   ? ? ? */    }    public static function charge(Subscription $subscription, PaymentMethod $paymentMethod, $cyclesToCharge)    {        $transaction = new Transaction();        $transaction->add(One Month Magazine, $subscription->getPrice(), $cyclesToCharge);        $transaction->pay($paymentMethod);        $subscription->addTransaction($transaction);        if (!$transaction->success())        {            return false;        }        $subscription->paidCycleIncrement($cyclesToCharge);        return true;    }    public static function modifyTimestamp($timestamp, $modifyText, $repeatNum = 1)    {        //Trivia code        //Note: dates don't rollover.        //For example: if +1 month, then January 31st will result in February 28th (or 29th)    }    //...other stuff too}
Is this a good way to keep track of subscription cycles and figure out if we need to charge the subscriber?
design patterns;object oriented;object oriented design;interfaces;single responsibility