captaincore-manager/app/Account.php
2025-09-30 06:33:03 -04:00

930 lines
No EOL
42 KiB
PHP

<?php
namespace CaptainCore;
class Account {
protected $account_id = "";
public function __construct( $account_id = "", $admin = false ) {
if ( ( new User )->verify_accounts( [ $account_id ] ) ) {
$this->account_id = $account_id;
}
if ( $admin ) {
$this->account_id = $account_id;
}
}
public function get() {
if ( empty( $this->account_id ) ) {
return;
}
$account = ( new Accounts )->get( $this->account_id );
$account->defaults = empty( $account->defaults ) ? (object) [] : json_decode( $account->defaults );
$account->plan = empty( $account->plan ) ? (object) [] : json_decode( $account->plan );
$account->metrics = empty( $account->metrics ) ? (object) [] : json_decode( $account->metrics );
return $account;
}
public function get_raw() {
self::calculate_totals();
// Fetch site from database
$account = ( new Accounts )->get( $this->account_id );
// Shared with permissions
$account_ids = self::shared_with();
$account_ids[] = $this->account_id;
// Fetch relating data
$account->users = ( new AccountUser )->where( [ "account_id" => $this->account_id ] );
$account->domains = ( new AccountDomain )->where( [ "account_id" => $this->account_id ] );
$account->sites = ( new AccountSite )->where( [ "account_id" => $this->account_id ] );
return $account;
}
public function sync() {
$command = "account sync {$this->account_id}";
// Disable https when debug enabled
if ( defined( 'CAPTAINCORE_DEBUG' ) ) {
add_filter( 'https_ssl_verify', '__return_false' );
}
$data = [
'timeout' => 45,
'headers' => [
'Content-Type' => 'application/json; charset=utf-8',
'token' => CAPTAINCORE_CLI_TOKEN
],
'body' => json_encode( [ "command" => $command ]),
'method' => 'POST',
'data_format' => 'body'
];
// Add command to dispatch server
$response = wp_remote_post( CAPTAINCORE_CLI_ADDRESS . "/run/background", $data );
if ( is_wp_error( $response ) ) {
$error_message = $response->get_error_message();
return "Something went wrong: $error_message";
}
return $response["body"];
}
public function assign_sites( $site_ids = [] ) {
$accountsite = new AccountSite();
// Fetch current records
$current_site_ids = array_column ( $accountsite->where( [ "account_id" => $this->account_id ] ), "site_id" );
// Removed current records not found new records.
foreach ( array_diff( $current_site_ids, $site_ids ) as $site_id ) {
$records = $accountsite->where( [ "account_id" => $this->account_id, "site_id" => $site_id ] );
foreach ( $records as $record ) {
$accountsite->delete( $record->account_site_id );
}
}
// Add new records
foreach ( array_diff( $site_ids, $current_site_ids ) as $site_id ) {
$accountsite->insert( [ "account_id" => $this->account_id, "site_id" => $site_id ] );
}
}
public function invoices() {
$invoices = wc_get_orders( [ 'meta_key' => 'captaincore_account_id', 'meta_value' => $this->account_id, 'posts_per_page' => "-1" ] );
foreach ( $invoices as $key => $invoice ) {
$order = wc_get_order( $invoice );
$item_count = $order->get_item_count();
$invoices[$key] = [
"order_id" => $order->id,
"name" => get_the_author_meta( 'display_name', $order->user_id ),
"date" => wc_format_datetime( $order->get_date_created() ),
"status" => wc_get_order_status_name( $order->get_status() ),
"total" => $order->get_total(),
];
}
return $invoices;
}
public function fetch() {
if ( $this->account_id == "" ) {
return [];
}
$user_id = get_current_user_id();
$account = $this->account();
$record = [
"timeline" => $this->process_logs(),
"account" => $account,
"invites" => $this->invites(),
"users" => $this->users(),
"domains" => $this->domains(),
"sites" => $this->sites(),
"usage_breakdown" => $this->usage_breakdown(),
"owner" => false,
];
if ( $user_id == $account["plan"]->billing_user_id ) {
$record["owner"] = true;
}
if ( ( new User )->is_admin() ) {
$record["invoices"] = $this->invoices();
}
return $record;
}
public function account() {
$account = Accounts::get( $this->account_id );
$defaults = json_decode( $account->defaults );
$plan = empty( $account->plan ) ? (object) [] : json_decode( $account->plan );
$plan->name = empty( $plan->name ) ? "" : $plan->name;
$plan->addons = empty( $plan->addons ) ? [] : $plan->addons;
$plan->charges = empty( $plan->charges ) ? [] : $plan->charges;
$plan->credits = empty( $plan->credits ) ? [] : $plan->credits;
$plan->limits = empty( $plan->limits ) ? (object) [ "storage" => 0, "visits" => 0, "sites" => 0 ] : $plan->limits;
$plan->interval = empty( $plan->interval ) ? "12" : $plan->interval;
$plan->billing_user_id = empty( $plan->billing_user_id ) ? 0 : (int) $plan->billing_user_id;
// Patch in maintenance only sites
$sites = Sites::where( [ "account_id" => $this->account_id, "status" => "active" ] );
$maintenance_sites = [];
foreach ( $sites as $site ) {
if ( ! empty( $site->provider_id ) && ( $site->provider_id != "1" ) ) {
$maintenance_sites[] = $site;
}
}
if ( ! empty( $maintenance_sites ) ) {
$maintenance_sites_addons = (object) [
"name" => "Managed WordPress sites",
"price" => 2,
"quantity" => count( $maintenance_sites ),
"required" => true
];
array_unshift($plan->addons, $maintenance_sites_addons );
}
if ( ! is_array( $defaults->users ) ) {
$defaults->users = [];
}
return [
"account_id" => $this->account_id,
"name" => html_entity_decode( $account->name ),
"plan" => $plan,
"metrics" => json_decode( $account->metrics ),
"defaults" => $defaults,
];
}
public function invites() {
$invites = new Invites();
return $invites->where( [ "account_id" => $this->account_id, "accepted_at" => "0000-00-00 00:00:00" ] );
}
public function domains() {
$account_ids = self::shared_with();
if ( ! empty ( $this->account_id ) ) {
$account_ids[] = $this->account_id;
}
$results = ( new AccountDomain )->fetch_domains( [ "account_id" => $account_ids ] );
return $results;
}
public function billing_sites() {
if ( $this->account_id == "" ) {
return [];
}
// Fetch sites assigned as owners
$site_ids = [];
$maintenance_site_id = [];
$sites = Sites::where( [ "account_id" => $this->account_id, "status" => "active" ] );
foreach ( $sites as $site ) {
if ( empty ( $site->provider_id ) || $site->provider_id == "1" ) {
$site_ids[] = $site->site_id;
continue;
}
$maintenance_site_id[] = $site->site_id;
}
$results = [];
$site_ids = array_unique( $site_ids );
foreach ($site_ids as $site_id) {
$site = Sites::get( $site_id );
$details = json_decode( $site->details );
$results[] = [
"site_id" => $site_id,
"name" => $site->name,
"visits" => $details->visits,
"storage" => $details->storage,
];
}
usort( $results, "sort_by_name" );
return $results;
}
public function sites() {
if ( $this->account_id == "" ) {
return [];
}
// Fetch sites assigned as owners
$all_site_ids = [];
$site_ids = array_column( ( new Sites )->where( [ "account_id" => $this->account_id, "status" => "active" ] ), "site_id" );
foreach ( $site_ids as $site_id ) {
$all_site_ids[] = $site_id;
}
// Fetch customer sites
$site_ids = array_column( ( new Sites )->where( [ "customer_id" => $this->account_id, "status" => "active" ] ), "site_id" );
foreach ( $site_ids as $site_id ) {
$all_site_ids[] = $site_id;
}
// Fetch sites assigned as shared access
$site_ids = ( new AccountSite )->select_active_sites( 'site_id', [ "account_id" => $this->account_id ] );
foreach ( $site_ids as $site_id ) {
$all_site_ids[] = $site_id;
}
$results = [];
$all_site_ids = array_unique( $all_site_ids );
foreach ($all_site_ids as $site_id) {
$site = ( new Sites )->get( $site_id );
$details = json_decode( $site->details );
$results[] = [
"site_id" => $site_id,
"name" => $site->name,
"visits" => $details->visits,
"storage" => $details->storage,
];
}
usort( $results, "sort_by_name" );
return $results;
}
public function process_logs() {
$Parsedown = new \Parsedown();
$site_ids = array_column( self::sites(), "site_id" );
$fetch_process_logs = ( new ProcessLogSite )->fetch_process_logs( [ "site_id" => $site_ids ] );
$process_logs = [];
foreach ( $fetch_process_logs as $result ) {
$sites_for_process = ( new ProcessLogSite )->fetch_sites_for_process_log( [ "process_log_id" => $result->process_log_id ] );
// Filter out sites which account doesn't have access to.
foreach ($sites_for_process as $key => $site) {
if ( in_array( $site->site_id, $site_ids ) ) {
continue;
}
unset( $sites_for_process[$key] );
}
$websites = [];
foreach ($sites_for_process as $site_for_process) {
$websites[] = $site_for_process;
}
$item = ( new ProcessLogs )->get( $result->process_log_id );
$item->created_at = strtotime( $item->created_at );
$item->name = $result->name;
$item->description_raw = $item->description;
$item->description = $Parsedown->text( $item->description );
$item->author = get_the_author_meta( 'display_name', $item->user_id );
$item->author_avatar = "https://www.gravatar.com/avatar/" . md5( get_the_author_meta( 'email', $item->user_id ) ) . "?s=80&d=mp";
$item->websites = $websites;
$process_logs[] = $item;
}
return $process_logs;
}
public function shared_with() {
// Fetch sites assigned as owners
$all_site_ids = [];
$site_ids = array_column( ( new Sites )->where( [ "account_id" => $this->account_id, "status" => "active" ] ), "site_id" );
foreach ( $site_ids as $site_id ) {
$all_site_ids[] = $site_id;
}
// Fetch sites assigned as shared access
$site_ids = ( new AccountSite )->select_active_sites( 'site_id', [ "account_id" => $this->account_id ] );
foreach ( $site_ids as $site_id ) {
$all_site_ids[] = $site_id;
}
$all_site_ids = array_unique( $all_site_ids );
$account_ids = [];
foreach ($all_site_ids as $site_id) {
$account_ids[] = ( new Sites )->get( $site_id )->customer_id;
}
return array_unique( $account_ids );
}
public function users() {
$permissions = ( new AccountUser )->where( [ "account_id" => $this->account_id ] );
$results = [];
foreach( $permissions as $permission ) {
$user = get_userdata( $permission->user_id );
$results[] = [
"user_id" => $user->ID,
"name" => $user->display_name,
"email" => $user->user_email,
"level" => empty( $permission->level ) ? "" : ucfirst( $permission->level ),
];
}
return $results;
}
public function usage_breakdown() {
$account = self::get();
$site_ids = array_column( ( new Sites )->where( [ "account_id" => $this->account_id, "status" => "active" ] ), "site_id" );
$hosting_plan = $account->plan->name;
$addons = empty( $account->plan->usage->addons ) ? [] : $account->plan->usage->addons;
$storage = empty( $account->plan->usage->storage ) ? 0 : $account->plan->usage->storage;
$visits = $account->plan->usage->visits;
$visits_plan_limit = $account->plan->limits->visits;
$storage_limit = empty( $account->plan->limits->storage ) ? 0 : $account->plan->limits->storage;
$sites_limit = $account->plan->limits->sites;
if ( ! empty( $visits ) && ! empty( $visits_plan_limit ) ) {
$visits_percent = round( $visits / $visits_plan_limit * 100, 0 );
}
$storage_gbs = round( $storage / 1024 / 1024 / 1024, 1 );
$storage_percent = empty ( $storage_limit ) ? 0 : round( $storage_gbs / $storage_limit * 100, 0 );
$result_sites = [];
foreach ( $site_ids as $site_id ) {
$site = ( new Site( $site_id ))->fetch();
$website_for_customer_storage = empty( $site->storage ) ? 0 : $site->storage;
$website_for_customer_visits = empty( $site->visits ) ? 0 : $site->visits;
if ( empty( $site->provider_id ) || $site->provider_id == "1" ) {
$result_sites[] = [
'site_id' => $site->site_id,
'name' => $site->name,
'storage' => $website_for_customer_storage,
'visits' => $website_for_customer_visits
];
continue;
}
$result_maintenance_sites[] = [
'site_id' => $site->site_id,
'name' => $site->name,
'storage' => $website_for_customer_storage,
'visits' => $website_for_customer_visits
];
}
if ( empty( $visits_percent ) ) {
$visits_percent = 0;
}
return [
'sites' => $result_sites,
'maintenance_sites' => $result_maintenance_sites,
'total' => [
"{$storage_percent}% storage<br /><strong>{$storage_gbs}GB/{$storage_limit}GB</strong>",
"{$visits_percent}% traffic<br /><strong>" . number_format( $visits ) . "</strong> <small>Yearly Estimate</small>"
]
];
}
public function invite_delete( $invite_id ) {
$invite = ( new Invites )->get( $invite_id );
if ( $invite->account_id == $this->account_id ) {
( new Invites )->delete( $invite_id );
}
}
public function invite( $email ) {
// Add account ID to current user
if ( email_exists( $email ) ) {
$user = get_user_by( 'email', $email );
$accounts = array_column( ( new AccountUser )->where( [ "user_id" => $user->ID ] ), "account_id" );
$accounts[] = $this->account_id;
( new User( $user->ID, true ) )->assign_accounts( array_unique( $accounts ) );
$this->calculate_totals();
return [ "message" => "Account already exists. Adding permissions for existing user." ];
}
$time_now = date("Y-m-d H:i:s");
$token = bin2hex( openssl_random_pseudo_bytes( 24 ) );
$new_invite = [
'email' => $email,
'account_id' => $this->account_id,
'created_at' => $time_now,
'updated_at' => $time_now,
'token' => $token
];
$invite = new Invites();
$invite_id = $invite->insert( $new_invite );
// Send out invite email
$invite_url = home_url() . "/account/?account={$this->account_id}&token={$token}";
$name = ( new Accounts )->get( $this->account_id )->name;
$subject = "Hosting account invite";
$body = "You've been granted access to account '$name'. Click here to accept:<br /><br /><a href=\"{$invite_url}\">$invite_url</a>";
$headers = [ 'Content-Type: text/html; charset=UTF-8' ];
wp_mail( $email, $subject, $body, $headers );
return [ "message" => "Invite has been sent." ];
}
public function calculate_totals() {
$metrics = [
"sites" => count( $this->billing_sites() ),
"users" => count( $this->users() ),
"domains" => count( $this->domains() ),
"outstanding_invoices" => count( $this->outstanding_invoices() )
];
( new Accounts )->update( [ "metrics" => json_encode( $metrics ) ], [ "account_id" => $this->account_id ] );
return [ "message" => "Account metrics updated." ];
}
public function calculate_usage() {
$account = self::get();
$sites = $this->billing_sites();
if ( empty( $account->plan ) || ( is_object( $account->plan ) && empty( get_object_vars( $account->plan ) ) ) ) {
$account->plan = (object) [ "usage" => (object) [ "storage" => "", "visits" => "", "sites" => "" ] ];
}
// Initialize totals
$total_storage = 0;
$total_visits = 0;
// Loop through each site to sum up production usage
foreach ( $sites as $site_data ) {
$site_id = $site_data['site_id'];
// Fetch all environments for the current site
$environments = ( new Environments )->where( [ "site_id" => $site_id ] );
// Loop through environments to find and sum production data
foreach ( $environments as $environment ) {
if ( $environment->environment == "Production" ) {
$total_storage += (int) $environment->storage;
$total_visits += (int) $environment->visits;
}
}
}
// Update the plan object with the calculated totals.
$account->plan->usage->storage = $total_storage;
$account->plan->usage->visits = $total_visits;
$account->plan->usage->sites = count( $sites );
Accounts::update( [ "plan" => json_encode( $account->plan ) ], [ "account_id" => $this->account_id ] );
}
public function process_renewals() {
$response = [];
$accounts = ( new Accounts )->select( 'account_id' );
foreach ( $accounts as $account_id ) {
$account = ( new Accounts )->get( $account_id );
$plan = json_decode( $account->plan );
if ( $plan->next_renewal != "" ) {
$result = (object) [
'account_id' => $account->account_id,
'renewal' => $plan->next_renewal,
'plan' => (int) $plan->billing_user_id,
];
}
$response[] = $result;
}
return $response;
}
public function generate_order() {
$configurations = ( new Configurations )->get();
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
if ( $plan->auto_switch == "true" ) {
self::auto_switch_plan();
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
}
$units = [];
$units[1] = "monthly";
$units[3] = "quarterly";
$units[6] = "biannually";
$units[12] = "yearly";
if ( ! empty( $plan->interval ) ) {
$plan_interval = $units[ $plan->interval ];
}
$customer = new \WC_Customer( $plan->billing_user_id );
$address = $customer->get_billing();
$order = wc_create_order( [ 'customer_id' => $plan->billing_user_id ] );
$order->update_meta_data( "captaincore_account_id", $this->account_id );
$site_names = array_column( self::billing_sites(), "name" );
sort( $site_names );
$site_names = implode( ", ", $site_names );
if ( ! empty( $address ) ) {
$order->set_address( $address, 'billing' );
}
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->hosting_plan ), 1 );
$order->get_items()[ $line_item_id ]->set_subtotal( $plan->price );
$order->get_items()[ $line_item_id ]->set_total( $plan->price );
if ( ! empty( $plan->interval ) ) {
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", "{$plan->name} - $plan_interval\n\n" . $site_names );
} else {
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", "{$plan->name}\n\n" . $site_names );
}
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = $plan->price;
// Patch in maintenance only sites
$sites = Sites::where( [ "account_id" => $this->account_id, "status" => "active" ] );
$maintenance_sites = [];
foreach ( $sites as $site ) {
if ( ! empty( $site->provider_id ) && ( $site->provider_id != "1" ) ) {
$maintenance_sites[] = $site;
}
}
if ( ! empty( $maintenance_sites ) ) {
$maintenance_sites_addons = (object) [
"name" => "Managed WordPress sites",
"price" => 2,
"quantity" => count( $maintenance_sites ),
"required" => true
];
array_unshift($plan->addons, $maintenance_sites_addons );
}
if ( $plan->addons && count( $plan->addons ) > 0 ) {
foreach ( $plan->addons as $item ) {
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->addons ), $item->quantity );
$order->get_items()[ $line_item_id ]->set_subtotal( $item->price * $item->quantity );
$order->get_items()[ $line_item_id ]->set_total( $item->price * $item->quantity );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", $item->name );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = (int) $calculated_total + ((int) $item->price * (int) $item->quantity );
}
}
if ( ! empty( $plan->charges ) && count( $plan->charges ) > 0 ) {
foreach ( $plan->charges as $item ) {
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->charges ), $item->quantity );
$order->get_items()[ $line_item_id ]->set_subtotal( $item->price * $item->quantity );
$order->get_items()[ $line_item_id ]->set_total( $item->price * $item->quantity );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", $item->name );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = $calculated_total + ( $item->price * $item->quantity );
}
}
if ( ! empty( $plan->credits ) && count( $plan->credits ) > 0 ) {
foreach ( $plan->credits as $item ) {
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->credits ), $item->quantity );
$order->get_items()[ $line_item_id ]->set_subtotal( -1 * abs( $item->price * $item->quantity ) );
$order->get_items()[ $line_item_id ]->set_total( -1 * abs( $item->price * $item->quantity ) );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", $item->name );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = $calculated_total + ( -1 * abs( $item->price * $item->quantity ) );
}
}
if ( $plan->usage->sites > $plan->limits->sites ) {
$price = $configurations->usage_pricing->sites->cost;
if ( $plan->interval != $configurations->usage_pricing->sites->interval ) {
$price = $configurations->usage_pricing->sites->cost / ($configurations->usage_pricing->sites->interval / $plan->interval );
}
$quantity = (int) $plan->usage->sites - (int) $plan->limits->sites;
$total = $price * $quantity;
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->usage ), $quantity );
$order->get_items()[ $line_item_id ]->set_subtotal( $total );
$order->get_items()[ $line_item_id ]->set_total( $total );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", "Extra Sites" );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = $calculated_total + $total;
}
if ( (int) $plan->usage->storage > ( (int) $plan->limits->storage * 1024 * 1024 * 1024 ) ) {
$price = $configurations->usage_pricing->storage->cost;
if ( $plan->interval != $configurations->usage_pricing->storage->interval ) {
$price = $configurations->usage_pricing->storage->cost / ( $configurations->usage_pricing->storage->interval / $plan->interval );
}
$extra_storage = ( $plan->usage->storage / 1024 / 1024 / 1024 ) - $plan->limits->storage;
$quantity = ceil ( $extra_storage / $configurations->usage_pricing->storage->quantity );
$total = $price * $quantity;
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->usage ), $quantity );
$order->get_items()[ $line_item_id ]->set_subtotal( $total );
$order->get_items()[ $line_item_id ]->set_total( $total );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", "Extra Storage" );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = $calculated_total + $total;
}
if ( $plan->usage->visits > $plan->limits->visits ) {
$price = $configurations->usage_pricing->traffic->cost;
if ( $plan->interval != $configurations->usage_pricing->traffic->interval ) {
$price = $configurations->usage_pricing->traffic->cost / ( $configurations->usage_pricing->traffic->interval / $plan->interval );
}
$quantity = ceil ( ( (int) $plan->usage->visits - (int) $plan->limits->visits ) / (int) $configurations->usage_pricing->traffic->quantity );
$total = $price * $quantity;
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->usage ), $quantity );
$order->get_items()[ $line_item_id ]->set_subtotal( $total );
$order->get_items()[ $line_item_id ]->set_total( $total );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", "Extra Visits" );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$calculated_total = $calculated_total + $total;
}
// Adjust credits if overpayment received
if ( $calculated_total < 0 ) {
$over_payment = 1 * abs( $calculated_total );
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->charges ), 1 );
$order->get_items()[ $line_item_id ]->set_subtotal( $over_payment );
$order->get_items()[ $line_item_id ]->set_total( $over_payment );
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", "Overpayment will be applied to next invoice as credit." );
$order->get_items()[ $line_item_id ]->save_meta_data();
$order->get_items()[ $line_item_id ]->save();
$plan->over_payment = $over_payment;
( new Accounts )->update( [ "plan" => json_encode( $plan ) ], [ "account_id" => $this->account_id ] );
}
$order->calculate_totals();
$default_payment = ( new \WC_Payment_Tokens )->get_customer_default_token( $plan->billing_user_id );
if ( $order->get_total() > 0 && $plan->auto_pay == "true" && ! empty( $default_payment ) ) {
$payment_id = $default_payment->get_id();
( new User( $plan->billing_user_id, true ) )->pay_invoice( $order->get_id(), $payment_id );
return;
}
if ( $order->get_total() == 0 ) {
$order->update_status( 'completed' );
}
WC()->mailer()->customer_invoice( $order );
$order->add_order_note( __( 'Order details sent to customer.', 'woocommerce' ), false, true );
do_action( 'woocommerce_after_resend_order_email', $order, 'customer_invoice' );
}
public function failed_notify() {
$orders = wc_get_orders( [
'limit' => -1,
'meta_key' => 'captaincore_account_id',
'meta_value' => $this->account_id,
'orderby' => 'date',
'order' => 'DESC',
'status' => 'failed'
] );
if ( count( $orders ) == 0 ) {
return;
}
$invoice_label = "invoice";
if ( count( $orders ) > 1 ) {
$invoice_label = "invoices";
}
foreach( $orders as $order ) {
$order->add_order_note( "Sent payment failure notice." );
}
$order_id = $orders[0]->ID;
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
$customer = new \WC_Customer( $plan->billing_user_id );
$address = $customer->get_billing();
$recipient = $address["email"];
$site_url = get_option('siteurl');
$site_title = get_option('blogname');
if ( empty( $recipient ) ) {
$user = get_user_by( 'id', $plan->billing_user_id );
$recipient = $user->user_email;
}
if ( ! empty( $plan->additional_emails ) ) {
$recipient .= ", {$plan->additional_emails}";
}
$from_name = apply_filters( 'woocommerce_email_from_name', get_option( 'woocommerce_email_from_name' ), $this );
$from_email = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this );
$headers = [
"Content-Type: text/html; charset=UTF-8",
"From: $from_name <$from_email>"
];
$link_style = "padding: 12px 32px; font-size: calc(18px / var(--type-scale-factor)); margin: 00px; display: inline-block; color: black; text-decoration: none; font-weight: bold; background-color: rgb(238, 238, 238) !important; border-color: rgb(238, 238, 238) !important;";
$subject = 'Invoice for renewal order #'.$order_id;
if ( ! empty( $address["first_name"] ) ) {
$message = "<p>". $address["first_name"] . ",</p><p></p>";
}
$message .= "<p>The automatic payment to renew your hosting plan with $site_title for account #{$this->account_id} has failed. To keep hosting services active, please pay outstanding $invoice_label:</p>";
foreach ( $orders as $key => $order ) {
$payment_link = captaincore_get_checkout_payment_url ( $order->get_checkout_payment_url() );
$message .= "<p><a href=\"$payment_link\" style=\"$link_style\">💰 Pay renewal order #{$order->ID} for {$order->get_formatted_order_total()} from {$order->get_date_created()->date('F jS Y')}</a></p>";
}
$message .= "</ul>";
$message .= "<a href=\"{$site_url}\">{$site_title}</a>";
wp_mail( $recipient, $subject, $message, $headers );
}
public function outstanding_notify() {
$orders = wc_get_orders( [
'limit' => -1,
'meta_key' => 'captaincore_account_id',
'meta_value' => $this->account_id,
'orderby' => 'date',
'order' => 'DESC',
'status' => [ 'wc-pending', 'failed' ]
] );
if ( count( $orders ) == 0 ) {
return;
}
$invoice_label = "invoice";
if ( count( $orders ) > 1 ) {
$invoice_label = "invoices";
}
foreach( $orders as $order ) {
$order->add_order_note( "Sent outstanding notice." );
}
$order_id = $orders[0]->ID;
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
$customer = new \WC_Customer( $plan->billing_user_id );
$address = $customer->get_billing();
$recipient = $address["email"];
$site_url = get_option('siteurl');
$site_title = get_option('blogname');
if ( empty( $recipient ) ) {
$user = get_user_by( 'id', $plan->billing_user_id );
$recipient = $user->user_email;
}
if ( ! empty( $plan->additional_emails ) ) {
$recipient .= ", {$plan->additional_emails}";
}
$from_name = apply_filters( 'woocommerce_email_from_name', get_option( 'woocommerce_email_from_name' ), $this );
$from_email = apply_filters( 'woocommerce_email_from_address', get_option( 'woocommerce_email_from_address' ), $this );
$first_name = get_user_meta( $plan->billing_user_id, 'first_name', true );
$store_address = get_option('woocommerce_store_address');
$store_address_2 = get_option('woocommerce_store_address_2');
$store_city = get_option('woocommerce_store_city');
$store_postcode = get_option('woocommerce_store_postcode');
$store_raw_country = get_option('woocommerce_default_country');
// Split the country and state
$split_country = explode(":", $store_raw_country);
$store_country = $split_country[0];
$countries = WC()->countries->countries;
$store_country_name = isset($countries[$store_country]) ? $countries[$store_country] : $store_country;
// Remove any text in parentheses
$store_country_name = preg_replace('/\s*\(.*?\)/', '', $store_country_name);
$store_state = isset($split_country[1]) ? $split_country[1] : '';
// Build the full address
$full_address = "$store_address<br />$store_address_2<br />$store_city, $store_state $store_postcode<br />$store_country_name";
$headers = [
"Content-Type: text/html; charset=UTF-8",
"From: $from_name <$from_email>"
];
$link_style = "padding: 12px 32px; font-size: calc(18px / var(--type-scale-factor)); margin: 00px; display: inline-block; color: black; text-decoration: none; font-weight: bold; background-color: rgb(238, 238, 238) !important; border-color: rgb(238, 238, 238) !important;";
$subject = "Payment overdue for services with $site_title";
if ( ! empty( $address["first_name"] ) ) {
$message = "<p>". $address["first_name"] . ",</p><p></p>";
}
if ( empty( $address["first_name"] ) && ! empty( $first_name ) ) {
$message = "<p>". $first_name . ",</p><p></p>";
}
$message .= "<p>There are outstanding payments relating to your hosting plan with $site_title for account {$account->name} #{$this->account_id}. To keep hosting services active, please pay outstanding $invoice_label. Let us know if you have any questions.</p>";
$message .= "<ul>";
foreach ( $orders as $key => $order ) {
$payment_link = captaincore_get_checkout_payment_url ( $order->get_checkout_payment_url() );
$message .= "<li><a href=\"$payment_link\">Renewal #{$order->ID} for {$order->get_formatted_order_total()} from {$order->get_date_created()->date('F jS Y')}</a></li>";
}
$message .= "</ul>";
$message .= "<a href=\"{$site_url}\">{$site_title}</a><br />";
$message .= "$full_address";
wp_mail( $recipient, $subject, $message, $headers );
}
public function outstanding_invoices() {
$orders = wc_get_orders( [
'limit' => -1,
'meta_key' => 'captaincore_account_id',
'meta_value' => $this->account_id,
'orderby' => 'date',
'order' => 'DESC',
'status' => [ 'wc-pending', 'failed' ]
] );
if ( empty ( $orders ) ) {
return [];
}
return $orders;
}
public function get_billing() {
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
$customer = new \WC_Customer( $plan->billing_user_id );
return (object) $customer->get_billing();
}
public function auto_switch_plan() {
$configurations = ( new Configurations )->get();
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
$estimates = [];
foreach( $configurations->hosting_plans as $hosting_plan ) {
$total = $hosting_plan->price;
if ( $plan->interval != $hosting_plan->interval ) {
$total = ( $hosting_plan->price / $hosting_plan->interval ) * $plan->interval;
$hosting_plan->interval = $plan->interval;
$hosting_plan->price = $total;
}
if ( ! empty( $plan->addons ) && count( $plan->addons ) > 0 ) {
foreach ( $plan->addons as $addon ) {
$total += $addon->quantity * $addon->price;
}
}
if ( empty( $plan->usage ) ) {
self::calculate_usage();
$account = ( new Accounts )->get( $this->account_id );
$plan = json_decode( $account->plan );
}
if ( $plan->usage->sites > $hosting_plan->limits->sites ) {
$price = $configurations->usage_pricing->sites->cost;
if ( $plan->interval != $configurations->usage_pricing->sites->interval ) {
$price = $configurations->usage_pricing->sites->cost / ($configurations->usage_pricing->sites->interval / $plan->interval );
}
$quantity = $plan->usage->sites - $hosting_plan->limits->sites;
$total += $price * $quantity;
}
if ( $plan->usage->storage > ( $hosting_plan->limits->storage * 1024 * 1024 * 1024 ) ) {
$price = $configurations->usage_pricing->storage->cost;
if ( $plan->interval != $configurations->usage_pricing->storage->interval ) {
$price = $configurations->usage_pricing->storage->cost / ( $configurations->usage_pricing->storage->interval / $plan->interval );
}
$extra_storage = ( $plan->usage->storage / 1024 / 1024 / 1024 ) - $hosting_plan->limits->storage;
$quantity = ceil ( $extra_storage / $configurations->usage_pricing->storage->quantity );
$total += $price * $quantity;
}
if ( $plan->usage->visits > $hosting_plan->limits->visits ) {
$price = $configurations->usage_pricing->traffic->cost;
if ( $plan->interval != $configurations->usage_pricing->traffic->interval ) {
$price = $configurations->usage_pricing->traffic->cost / ( $configurations->usage_pricing->traffic->interval / $plan->interval );
}
$quantity = ceil ( ( $plan->usage->visits - $hosting_plan->limits->visits ) / $configurations->usage_pricing->traffic->quantity );
$total += $price * $quantity;
}
$estimates[] = (object) [ "name" => $hosting_plan->name, "total" => $total ];
}
usort( $estimates, function($a, $b) {return $a->total - $b->total;});
$cheapest_estimate = $estimates[0];
if ( $plan->name != $cheapest_estimate->name ) {
foreach( $configurations->hosting_plans as $hosting_plan ) {
if ( $hosting_plan->name == $cheapest_estimate->name ) {
$new_hosting_plan = $hosting_plan;
}
}
$new_hosting_plan->auto_pay = empty( $plan->auto_pay ) ? "" : $plan->auto_pay;
$new_hosting_plan->auto_switch = empty( $plan->auto_switch ) ? "" : $plan->auto_switch;
$new_hosting_plan->addons = empty( $plan->addons ) ? "" : $plan->addons;
$new_hosting_plan->credits = empty( $plan->credits ) ? "" : $plan->credits;
$new_hosting_plan->charges = empty( $plan->charges ) ? "" : $plan->charges;
$new_hosting_plan->billing_user_id = empty( $plan->billing_user_id ) ? "" : $plan->billing_user_id;
$new_hosting_plan->next_renewal = empty( $plan->next_renewal ) ? "" : $plan->next_renewal;
( new Accounts )->update_plan( (array) $new_hosting_plan, $this->account_id );
}
return $estimates;
}
public function delete() {
( new Accounts )->delete( [ "account_id" => $this->account_id ] );
}
}