Skip to content

Instantly share code, notes, and snippets.

@rondorkerin
Created August 28, 2013 19:03
Show Gist options
  • Save rondorkerin/6369904 to your computer and use it in GitHub Desktop.
Save rondorkerin/6369904 to your computer and use it in GitHub Desktop.
payment
<?php
class Payment
{
private $provider;
private $test_mode;
public static $allowedProviders = array('authorize', 'paymenttest');
const FREE_PLAN = 1;
const BASIC_MONTHLY_PLAN = 2;
const SECONDARY_MONTHLY_PLAN = 3;
const UNLIMITED_MONTHLY_PLAN = 4;
const BASIC_YEARLY_PLAN = 5;
const SECONDARY_YEARLY_PLAN = 6;
const UNLIMITED_YEARLY_PLAN = 7;
// name-indexed payment plan array
private $paymentPlans = array();
// database IDs for specific billing plans
public static $paymentPlanIDs = array( self::FREE_PLAN => "FP",
self::BASIC_MONTHLY_PLAN => "BMP",
self::SECONDARY_MONTHLY_PLAN => "SMP",
self::UNLIMITED_MONTHLY_PLAN => "UMP",
self::BASIC_YEARLY_PLAN => "BYP",
self::SECONDARY_YEARLY_PLAN => "SYP",
self::UNLIMITED_YEARLY_PLAN => "UYP");
public function __construct()
{
$this->CI = get_instance();
$this->CI->load->model('emailvariable_model');
$this->CI->load->model('companyprofile_model');
$this->CI->load->model('lead_model');
$this->CI->load->model('companyplaninfo_model');
$this->CI->load->model('paymentplan_model');
$this->CI->load->model('companychargehistory_model');
$this->CI->load->model('companybillinginfo_model');
$this->CI->load->model('resellerplaninfo_model');
$this->emailVariable = $this->CI->emailvariable_model;
$this->companyProfile = $this->CI->companyprofile_model;
$this->lead = $this->CI->lead_model;
$this->companyPlanInfo = $this->CI->companyplaninfo_model;
$this->paymentPlan = $this->CI->paymentplan_model;
$this->companyBillingInfo = $this->CI->companybillinginfo_model;
$this->resellerPlanInfo = $this->CI->resellerplaninfo_model;
$this->companyChargeHistory = $this->CI->companychargehistory_model;
$this->test_mode = FALSE;
// grab payment plans and stick them in class-scoped array
// indexed by enums
$paymentPlans = $this->paymentPlan->getAll();
foreach ($paymentPlans as $plan) {
foreach (Payment::$paymentPlanIDs as $name => $planID) {
if ($plan['planID'] == $planID) {
$this->paymentPlans[$name] = $plan;
}
}
}
}
public function setProvider($providerName)
{
if (!in_array($providerName, Payment::$allowedProviders)) {
return 0;
}
if ($providerName == "authorize")
{
$this->CI->load->library('anetcim');
$this->provider = $this->CI->anetcim;
} else if ($providerName == "paymenttest") {
$this->CI->load->library('paymenttest');
$this->provider = $this->CI->paymenttest;
$this->test_mode = true;
}
return true;
}
/**
* This function should be run daily as a cron job
*/
public function updateBillingPlans()
{
$companies = $this->companyProfile->getAll();
foreach ($companies as $company) {
// don't deal with inactive companies
if (!$company['isActive']) {
continue;
}
$clientPlan = $this->companyPlanInfo->get($company['id']);
// TODO: Notify clients that they need to input billing info
if (!$clientPlan) {
continue;
}
$billingSchedule = $clientPlan['schedule'] == 1 ? "monthly" : "yearly";
$clientPlanID = $clientPlan['planID'];
$leadCount = $this->lead->getAllByCompany($company['id']);
$leadCount = count($leadCount);
// client is on the free plan. Upgrade them.
if ($clientPlanID == $this->paymentPlans[self::FREE_PLAN]['id']) {
if ($leadCount > $this->paymentPlans[self::FREE_PLAN]['threshold']) {
$today = new DateTime();
$companyPlanInfo = $this->companyPlanInfo->get($company['id']);
$companyPlanInfo['startDate'] = $today->format('Y-m-d H:i:s');
$this->companyPlanInfo->update($company['id'], $companyPlanInfo);
if ($leadCount <= $this->paymentPlans[self::BASIC_MONTHLY_PLAN]['threshold']) {
// upgrade to basic plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::BASIC_MONTHLY_PLAN]['id']);
} else if ($leadCount <= $this->paymentPlans[self::SECONDARY_MONTHLY_PLAN]['threshold']) {
// upgrade to secondary plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::SECONDARY_MONTHLY_PLAN]['id']);
} else {
// upgrade to unlimited plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::UNLIMITED_MONTHLY_PLAN]['id']);
}
}
}
// client is on the basic monthly plan
if ($clientPlanID == $this->paymentPlans[self::BASIC_MONTHLY_PLAN]['id']) {
if ($leadCount > $this->paymentPlans[self::BASIC_MONTHLY_PLAN]['threshold']) {
if ($leadCount <= $this->paymentPlans[self::SECONDARY_MONTHLY_PLAN]['threshold']) {
// upgrade to secondary plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::SECONDARY_MONTHLY_PLAN]['id']);
} else {
// upgrade to unlimited plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::UNLIMITED_MONTHLY_PLAN]['id']);
}
}
}
// client is on the secondary monthly plan
if ($clientPlanID == $this->paymentPlans[self::SECONDARY_MONTHLY_PLAN]['id']) {
if ($leadCount > $this->paymentPlans[self::SECONDARY_MONTHLY_PLAN]['threshold']) {
// upgrade to unlimited plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::UNLIMITED_MONTHLY_PLAN]['id']);
}
}
// client is on the basic yearly plan
if ($clientPlanID == $this->paymentPlans[self::BASIC_YEARLY_PLAN]['id']) {
if ($leadCount > $this->paymentPlans[self::BASIC_YEARLY_PLAN]['threshold']) {
if ($leadCount <= $this->paymentPlans[self::SECONDARY_YEARLY_PLAN]['threshold']) {
// upgrade to secondary plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::SECONDARY_YEARLY_PLAN]['id']);
} else {
// upgrade to unlimited plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::UNLIMITED_YEARLY_PLAN]['id']);
}
}
}
// client is on the secondary yearly plan
if ($clientPlanID == $this->paymentPlans[self::SECONDARY_YEARLY_PLAN]['id']) {
if ($leadCount > $this->paymentPlans[self::SECONDARY_YEARLY_PLAN]['threshold']) {
// upgrade to unlimited plan
$this->companyPlanInfo->upgradeToPlan($company['id'], $this->paymentPlans[self::UNLIMITED_YEARLY_PLAN]['id']);
}
}
}
}
/**
* This function should be run daily as a cron job
* This function assumes updateBillingPlans is done every day
* which means the company billing status will be correct
*/
public function processPayments()
{
// get the current date and whether it's the first of the month.
$today = new DateTime();
$firstOfTheMonth = $today->format('d') == '01' || $this->test_mode;
// if it's the first of the month, we need to bill resellers.
// we set up an associative array mapping reseller IDs to
// the total amount we will charge them
$resellerTotals = array();
$companies = $this->companyProfile->getAll();
foreach ($companies as $company) {
// we don't charge inactive clients
if (!$company['isActive']) {
continue;
}
$clientPlan = $this->companyPlanInfo->get($company['id']);
// we don't charge clients who are not on a billing plan
if (!$clientPlan) {
continue;
}
$billingSchedule = $clientPlan['schedule'] == 1 ? "monthly" : "yearly";
$clientPlanID = $clientPlan['planID'];
$paidByCompanyID = $clientPlan['paidByCompanyID'];
$paidByMarketingFirm = ($paidByCompanyID != $clientPlan['companyProfileID']);
if ($paidByMarketingFirm && !$firstOfTheMonth) {
// if the company is being handled by a marketing firm,
// we only process payments on the first of the month
continue;
}
// don't charge free accounts
if ($clientPlanID == $this->paymentPlans[self::FREE_PLAN]['id']) {
continue;
}
// all companies past this point should be paying customers
$companyBillingInfo = $this->companyBillingInfo->getByCompanyID($paidByCompanyID);
if (!$companyBillingInfo) {
log_message('error', "companybilling info was null for company {$paidByCompanyID}");
continue;
}
// ensure valid credit card
$cimnumber = $companyBillingInfo['cimNumber'];
$paymentID = $companyBillingInfo['customerPaymentProfileID'];
$validCreditCard = $this->provider->testCard($cimnumber, $paymentID);
if (!$validCreditCard) {
// TODO: Send a notification to company, to sharpspring admins
log_message('info', "company {$paidByCompanyID} has an invalid credit card.");
continue;
}
$lastChargeDate = $clientPlan['lastChargeDate'];
$lastChargeDate = $lastChargeDate ? new DateTime($lastChargeDate) : null;
$needsToPay = false;
if ($billingSchedule == 'monthly') {
// if user has never been charged
if (!$lastChargeDate) {
// if there's no last charge date, they're on the let-it-ride plan.
// if we've gotten to this point in the execution, the user has gone over their limit
// for free and we need to get them started paying today.
$needsToPay = true;
$chargeDate = $today;
} else {
// if a marketing firm is paying, we don't care about the last charge
// date because we charge the firm on the first of the month.
// OR they might be an individual account in which case we check that it's
// been a month
if ($lastChargeDate->diff($today)->m >= 1 || $paidByMarketingFirm) {
$needsToPay = true;
}
// check if it's been 12 months and we need to update discount
if($clientPlan['startDate']->diff($today)->y >= 1) {
$clientPlan['discount'] = $clientPlan['discountAfter12'];
}
$chargeDate = new DateTime($lastChargeDate->format('Y-m-d H:i:s'));
$chargeDate = $chargeDate->add(new DateInterval('P1M'));
}
} else if ($billingSchedule == 'yearly') {
// check if has freemonths
// check if its been >= 1 year + free months since payment
//set new lastpayment date to last year + 1 year.
$freeMonths = $clientPlan['freeMonths'];
if ($lastChargeDate) {
$needsToPay = $lastChargeDate->diff($today)->y >= 1 &&
$lastChargeDate->diff($today)->m >= $freeMonths;
$chargeDate = new DateTime($lastChargeDate->format('Y-m-d H:i:s'));
$chargeDate = $chargeDate->add(new DateInterval("P1Y{$freeMonths}M"));
$clientPlan['freeMonths'] = 0;
if ($needsToPay) {
// it's been 12 + free months and we need to switch the client's discount
$clientPlan['discount'] = $clientPlan['discountAfter12'];
}
}
}
if (!$needsToPay) {
continue;
}
// get discount as percentage
$discount = $clientPlan['discount'];
if ($discount) {
$price = $clientPlan['price'] * (1.0 - ((float)($clientPlan['discount']))/100.0);
} else {
$discount = 0;
$price = $clientPlan['price'];
}
if (!$paidByMarketingFirm) {
// ding their credit card
$this->provider->chargeClient($paidByCompanyID, $price);
} else {
if (!array_key_exists($paidByCompanyID, $resellerTotals)) {
$resellerTotals[$paidByCompanyID] = $price;
} else {
$resellerTotals[$paidByCompanyID] += $price;
}
}
// calculate the start/end date of the bill that's being calculated
if ($lastChargeDate) {
$billingCycleStartDate = new DateTime($chargeDate->format('Y-m-d H:i:s'));
$billingCycleEndDate = new DateTime($chargeDate->format('Y-m-d H:i:s'));
if ($billingSchedule == "monthly") {
$billingCycleEndDate = $billingCycleEndDate->add(new DateInterval('P1M'));
} else {
$billingCycleEndDate = $billingCycleEndDate->add(new DateInterval("P1Y"));
}
} else {
$billingCycleStartDate = new DateTime();
$billingCycleEndDate = new DateTime();
$billingCycleEndDate->add(new DateInterval('P1M'));
}
$billingCycleStartDate = $billingCycleStartDate->format('Y-m-d H:i:s');
$billingCycleEndDate = $billingCycleEndDate->format('Y-m-d H:i:s');
// TODO: Get MAX LEADS instead ofnumLeads
$this->CI->load->model('companysummary_model');
$this->companySummary = $this->CI->companysummary_model;
$summary = $this->companySummary->get($company['id']);
$this->companyChargeHistory->insert($company['id'], $paidByCompanyID,
$price, $company['companyName'], $clientPlan['schedule'], $clientPlan['price'],
$company['createTimestamp'], $billingCycleStartDate, $billingCycleEndDate,
$summary['numLeads'], $clientPlan['discount']);
$clientPlan['lastChargeDate'] = $chargeDate->format('Y-m-d H:i:s');
// update company plan info including:
// an updated last charged date, an updated start date.
$this->companyPlanInfo->update($clientPlan['companyProfileID'], $clientPlan);
}
// only bill resellers on the first of the month
if (!$firstOfTheMonth && !$this->test_mode) {
// if it's not the first of the month, don't bill.
return;
}
foreach($resellerTotals as $resellerID => $total) {
$resellerPlanInfo = $this->resellerPlanInfo->get($resellerID);
// get their credits
$credits = $resellerPlanInfo['creditBalance'];
if ($credits >= $total) {
$credits -= $total;
} else {
$leftOver = $total - $credits;
$credits = 0;
$this->provider->chargeClient($resellerID, $leftOver);
}
$this->resellerPlanInfo->setCreditBalance($resellerID, $credits);
}
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment