mirror of
https://gh.llkk.cc/https://github.com/CaptainCore/captaincore-manager.git
synced 2025-10-03 14:04:44 +08:00
📦 NEW: Billing features
This commit is contained in:
parent
4dea0b356b
commit
ab8e127841
8 changed files with 1486 additions and 77 deletions
127
app/Account.php
127
app/Account.php
|
@ -101,9 +101,10 @@ class Account {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
$user_id = get_current_user_id();
|
$user_id = get_current_user_id();
|
||||||
|
$account = $this->account();
|
||||||
$record = [
|
$record = [
|
||||||
"timeline" => $this->process_logs(),
|
"timeline" => $this->process_logs(),
|
||||||
"account" => $this->account(),
|
"account" => $account,
|
||||||
"invites" => $this->invites(),
|
"invites" => $this->invites(),
|
||||||
"users" => $this->users(),
|
"users" => $this->users(),
|
||||||
"domains" => $this->domains(),
|
"domains" => $this->domains(),
|
||||||
|
@ -112,7 +113,7 @@ class Account {
|
||||||
"owner" => false,
|
"owner" => false,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ( $user_id == $record["account"]["billing_user_id"] ) {
|
if ( $user_id == $account["plan"]->billing_user_id ) {
|
||||||
$record["owner"] = true;
|
$record["owner"] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,19 +121,19 @@ class Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function account() {
|
public function account() {
|
||||||
$account = ( new Accounts )->get( $this->account_id );
|
$account = ( new Accounts )->get( $this->account_id );
|
||||||
$defaults = json_decode( $account->defaults );
|
$defaults = json_decode( $account->defaults );
|
||||||
$plan = json_decode( $account->plan );
|
$plan = json_decode( $account->plan );
|
||||||
$plan->name = empty( $plan->name ) ? "" : $plan->name;
|
$plan->name = empty( $plan->name ) ? "" : $plan->name;
|
||||||
$plan->addons = empty( $plan->addons ) ? [] : $plan->addons;
|
$plan->addons = empty( $plan->addons ) ? [] : $plan->addons;
|
||||||
$plan->limits = empty( $plan->limits ) ? (object) [ "storage" => 0, "visits" => 0, "sites" => 0 ] : $plan->limits;
|
$plan->limits = empty( $plan->limits ) ? (object) [ "storage" => 0, "visits" => 0, "sites" => 0 ] : $plan->limits;
|
||||||
$plan->interval = empty( $plan->interval ) ? "12" : $plan->interval;
|
$plan->interval = empty( $plan->interval ) ? "12" : $plan->interval;
|
||||||
|
$plan->billing_user_id = empty( $plan->billing_user_id ) ? 0 : (int) $plan->billing_user_id;
|
||||||
if ( ! is_array( $defaults->users ) ) {
|
if ( ! is_array( $defaults->users ) ) {
|
||||||
$defaults->users = [];
|
$defaults->users = [];
|
||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
"account_id" => $this->account_id,
|
"account_id" => $this->account_id,
|
||||||
"billing_user_id" => (int) $account->billing_user_id,
|
|
||||||
"name" => html_entity_decode( $account->name ),
|
"name" => html_entity_decode( $account->name ),
|
||||||
"plan" => $plan,
|
"plan" => $plan,
|
||||||
"metrics" => json_decode( $account->metrics ),
|
"metrics" => json_decode( $account->metrics ),
|
||||||
|
@ -360,7 +361,7 @@ class Account {
|
||||||
$result = (object) [
|
$result = (object) [
|
||||||
'account_id' => $account->account_id,
|
'account_id' => $account->account_id,
|
||||||
'renewal' => $plan->next_renewal,
|
'renewal' => $plan->next_renewal,
|
||||||
'plan' => (int) $account->billing_user_id,
|
'plan' => (int) $plan->billing_user_id,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$response[] = $result;
|
$response[] = $result;
|
||||||
|
@ -370,34 +371,98 @@ class Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generate_order() {
|
public function generate_order() {
|
||||||
$account = ( new Accounts )->get( $this->account_id );
|
$configurations = ( new Configurations )->get();
|
||||||
$customer = new WC_Customer( $account->billing_user_id );
|
$account = ( new Accounts )->get( $this->account_id );
|
||||||
$address = $customer->get_billing_address();
|
$plan = json_decode( $account->plan );
|
||||||
$order = wc_create_order( [ 'customer_id' => $account->billing_user_id ] );
|
$customer = new \WC_Customer( $plan->billing_user_id );
|
||||||
$order->set_address( $address, 'billing' );
|
$address = $customer->get_billing();
|
||||||
$order->set_address( $address, 'shipping' );
|
$order = wc_create_order( [ 'customer_id' => $plan->billing_user_id ] );
|
||||||
$order->calculate_totals();
|
|
||||||
|
|
||||||
// Add addon product
|
$site_names = array_column( self::sites(), "name" );
|
||||||
$line_item_id = $order->add_product( get_product( '11062' ), 1 );
|
sort( $site_names );
|
||||||
|
$site_names = implode( ", ", $site_names );
|
||||||
|
|
||||||
// Assign price
|
if ( ! empty( $address ) ) {
|
||||||
$order->get_items()[ $line_item_id ]->set_total( '99' );
|
$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 );
|
||||||
|
$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();
|
$order->get_items()[ $line_item_id ]->save();
|
||||||
|
|
||||||
// Assign meta field
|
if ( $plan->addons && count( $plan->addons ) > 0 ) {
|
||||||
$order->get_items()[ $line_item_id ]->get_meta( "Details" );
|
foreach ( $plan->addons as $addon ) {
|
||||||
$order->get_items()[ $line_item_id ]->save_meta_data();
|
$line_item_id = $order->add_product( get_product( $configurations->woocommerce->addons ), $addon->quantity );
|
||||||
|
$order->get_items()[ $line_item_id ]->set_subtotal( $addon->price * $addon->quantity );
|
||||||
|
$order->get_items()[ $line_item_id ]->set_total( $addon->price * $addon->quantity );
|
||||||
|
$order->get_items()[ $line_item_id ]->add_meta_data( "Details", $addon->name );
|
||||||
|
$order->get_items()[ $line_item_id ]->save_meta_data();
|
||||||
|
$order->get_items()[ $line_item_id ]->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = $plan->usage->sites - $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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $plan->usage->storage > ( $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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ( ( $plan->usage->visits - $plan->limits->visits ) / $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();
|
||||||
|
}
|
||||||
|
|
||||||
// Set payment method
|
// Set payment method
|
||||||
// TODO fetch customer selected payment method
|
|
||||||
$order->set_payment_method( $payment_gateways['stripe'] );
|
|
||||||
|
|
||||||
$order->calculate_totals();
|
$order->calculate_totals();
|
||||||
|
|
||||||
|
if ( $plan->auto_pay == "true" ) {
|
||||||
// here we are adding some system notes to the order
|
$payment_id = ( new \WC_Payment_Tokens )->get_customer_default_token( $plan->billing_user_id )->get_id();
|
||||||
// $order->update_status( "processing", 'Imported Order From Funnel', TRUE );
|
( new User( $plan->billing_user_id, true ) )->pay_invoice( $order->get_id(), $payment_id );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WC()->mailer()->customer_invoice( $order );
|
||||||
|
$order->add_order_note( __( 'Order details manually sent to customer.', 'woocommerce' ), false, true );
|
||||||
|
do_action( 'woocommerce_after_resend_order_email', $order, 'customer_invoice' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
|
|
|
@ -55,6 +55,63 @@ class Accounts extends DB {
|
||||||
}
|
}
|
||||||
usort($accounts, function($a, $b) { return strcmp( strtolower($a->name), strtolower($b->name) ); });
|
usort($accounts, function($a, $b) { return strcmp( strtolower($a->name), strtolower($b->name) ); });
|
||||||
return $accounts;
|
return $accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function update_plan( $new_plan, $account_id ) {
|
||||||
|
$account = self::get( $account_id );
|
||||||
|
$plan = json_decode( $account->plan );
|
||||||
|
$total = $plan->price;
|
||||||
|
if ( count( $plan->addons ) > 0 ) {
|
||||||
|
foreach( $plan->addons as $addon ) {
|
||||||
|
$total = $total + $addon->price;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate credit or charge for paid plans when interval changes.
|
||||||
|
if ( $plan->status == "active" && $plan->interval != $new_plan["interval"] ) {
|
||||||
|
$now = new \DateTime();
|
||||||
|
$next_renewal = new \DateTime( $plan->next_renewal );
|
||||||
|
$remaining_time = $now->diff( $next_renewal );
|
||||||
|
$remaining_days = $remaining_time->format('%a');
|
||||||
|
$per_month_total = $total / $plan->interval;
|
||||||
|
$remaining_credit = ( $remaining_days / 30 ) * $per_month_total;
|
||||||
|
if ( $remaining_credit > 0 ) {
|
||||||
|
$plan->credit = $plan->credit + $remaining_credit;
|
||||||
|
}
|
||||||
|
if ( $remaining_credit < 0 ) {
|
||||||
|
$plan->charge = $plan->charge + $remaining_credit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( $plan->status == "" ) {
|
||||||
|
$plan->status == "pending";
|
||||||
|
}
|
||||||
|
$plan->name = $new_plan["name"];
|
||||||
|
$plan->price = $new_plan["price"];
|
||||||
|
$plan->addons = $new_plan["addons"];
|
||||||
|
$plan->limits = $new_plan["limits"];
|
||||||
|
$plan->auto_pay = $new_plan["auto_pay"];
|
||||||
|
$plan->interval = $new_plan["interval"];
|
||||||
|
$plan->next_renewal = $new_plan["next_renewal"];
|
||||||
|
$plan->billing_user_id = $new_plan["billing_user_id"];
|
||||||
|
|
||||||
|
self::update( [ "plan" => json_encode( $plan ) ], [ "account_id" => $account_id ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process_renewals() {
|
||||||
|
|
||||||
|
$accounts = self::with_renewals();
|
||||||
|
$now = strtotime( "now" );
|
||||||
|
foreach ( $accounts as $account ) {
|
||||||
|
$plan = json_decode( $account->plan );
|
||||||
|
$next_renewal = strtotime ( $plan->next_renewal );
|
||||||
|
if ( ! empty( $next_renewal ) && $next_renewal < $now ) {
|
||||||
|
echo "Processing renewal for {$account->name} as it's past {$plan->next_renewal}\n";
|
||||||
|
( new Account( $account->account_id, true ) )->generate_order();
|
||||||
|
$plan->next_renewal = date("Y-m-d H:i:s", strtotime( "+{$plan->interval} month", $next_renewal ) );
|
||||||
|
echo "Next renewal in {$plan->interval} months will be {$plan->next_renewal}\n";
|
||||||
|
self::update( [ "plan" => json_encode( $plan ) ], [ "account_id" => $account->account_id ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
22
app/DB.php
22
app/DB.php
|
@ -194,6 +194,28 @@ class DB {
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function renewals( $user_id ) {
|
||||||
|
global $wpdb;
|
||||||
|
$table = self::_table();
|
||||||
|
$sql = "SELECT *
|
||||||
|
FROM {$table}
|
||||||
|
WHERE {$table}.plan like '%,\"billing_user_id\":\"$user_id\"%'
|
||||||
|
order by {$table}.`name` ASC";
|
||||||
|
$results = $wpdb->get_results( $sql );
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function with_renewals() {
|
||||||
|
global $wpdb;
|
||||||
|
$table = self::_table();
|
||||||
|
$sql = "SELECT *
|
||||||
|
FROM {$table}
|
||||||
|
WHERE {$table}.plan like '%\"next_renewal\":\"%'
|
||||||
|
order by {$table}.`name` ASC";
|
||||||
|
$results = $wpdb->get_results( $sql );
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
static function fetch_domains( $conditions = [] ) {
|
static function fetch_domains( $conditions = [] ) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
$table = self::_table();
|
$table = self::_table();
|
||||||
|
|
249
app/User.php
249
app/User.php
|
@ -25,6 +25,50 @@ class User {
|
||||||
return $accounts;
|
return $accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function set_as_primary( $token ) {
|
||||||
|
\WC_Payment_Tokens::set_users_default( $this->user_id, intval( $token ) );
|
||||||
|
$billing = self::billing();
|
||||||
|
foreach ( $billing->subscriptions as $item ) {
|
||||||
|
$subscription = (object) $item;
|
||||||
|
if ( $subscription->type == "woocommerce" ) {
|
||||||
|
self::update_payment_method( $subscription->account_id, intval( $token ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function add_payment_method( $source_id ) {
|
||||||
|
$customer = new \WC_Stripe_Customer( $this->user_id );
|
||||||
|
$response = $customer->add_source( $source_id );
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete_payment_method( $token_id ) {
|
||||||
|
$token = \WC_Payment_Tokens::get( $token_id );
|
||||||
|
|
||||||
|
if ( is_null( $token ) || $this->user_id !== $token->get_user_id() ) {
|
||||||
|
return "Error";
|
||||||
|
}
|
||||||
|
$was_default = $token->is_default();
|
||||||
|
\WC_Payment_Tokens::delete( $token_id );
|
||||||
|
|
||||||
|
if ( $was_default ) {
|
||||||
|
$payment_tokens = \WC_Payment_Tokens::get_customer_tokens( $this->user_id );
|
||||||
|
|
||||||
|
// Set default if another payment token found.
|
||||||
|
if ( count( $payment_tokens ) > 0 ) {
|
||||||
|
$other_token_id = array_keys( $payment_tokens )[0];
|
||||||
|
self::set_as_primary( $other_token_id );
|
||||||
|
$billing = self::billing();
|
||||||
|
foreach ( $billing->subscriptions as $item ) {
|
||||||
|
$subscription = (object) $item;
|
||||||
|
if ( $subscription->type == "woocommerce" ) {
|
||||||
|
self::update_payment_method( $subscription->account_id, intval( $other_token_id ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function verify_accounts( $account_ids = [] ) {
|
public function verify_accounts( $account_ids = [] ) {
|
||||||
if ( self::is_admin() ) {
|
if ( self::is_admin() ) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -54,6 +98,207 @@ class User {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function billing() {
|
||||||
|
|
||||||
|
$customer = new \WC_Customer( $this->user_id );
|
||||||
|
$address = $customer->get_billing();
|
||||||
|
$invoices = wc_get_orders( [ 'customer' => $this->user_id ] );
|
||||||
|
foreach ( $invoices as $key => $invoice ) {
|
||||||
|
$order = wc_get_order( $invoice );
|
||||||
|
$item_count = $order->get_item_count();
|
||||||
|
$invoices[$key] = [
|
||||||
|
"order_id" => $order->id,
|
||||||
|
"date" => wc_format_datetime( $order->get_date_created() ),
|
||||||
|
"status" => wc_get_order_status_name( $order->get_status() ),
|
||||||
|
"total" => $order->get_total(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// Fetch CaptainCore subscriptions
|
||||||
|
$subscriptions = ( new Accounts )->renewals( $this->user_id );
|
||||||
|
foreach ( $subscriptions as $key => $subscription ) {
|
||||||
|
$plan = json_decode( $subscription->plan );
|
||||||
|
$plan->addons = ( empty( $plan->addons ) ) ? [] : $plan->addons;
|
||||||
|
$subscriptions[ $key ]->plan = $plan;
|
||||||
|
}
|
||||||
|
// Fetch WooCommerce subscriptions
|
||||||
|
$current_subscriptions = wcs_get_users_subscriptions( $this->user_id );
|
||||||
|
foreach ( $current_subscriptions as $key => $subscription ) {
|
||||||
|
$interval = $subscription->get_billing_period();
|
||||||
|
$line_items = $subscription->get_data()["line_items"];
|
||||||
|
$line_item = array_shift ( array_values ( $line_items ) );
|
||||||
|
$details = $line_item->get_meta_data();
|
||||||
|
foreach ( $details as $detail ) {
|
||||||
|
$item = $detail->get_data();
|
||||||
|
if ( isset( $item["key"]) && $item["key"] == "Details" ) {
|
||||||
|
$name = $item["value"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $interval == "month" ) {
|
||||||
|
$interval_count = "1";
|
||||||
|
} else {
|
||||||
|
$interval_count = "12";
|
||||||
|
}
|
||||||
|
$subscriptions[] = [
|
||||||
|
"account_id" => $subscription->id,
|
||||||
|
"name" => $name,
|
||||||
|
"type" => "woocommerce",
|
||||||
|
"plan" => (object) [
|
||||||
|
"name" => $line_item->get_name(),
|
||||||
|
"next_renewal" => empty( $subscription->get_date( "next_payment" ) ) ? "" : $subscription->get_date( "next_payment" ),
|
||||||
|
"price" => $subscription->get_total(),
|
||||||
|
"usage" => (object) [],
|
||||||
|
"limits" => (object) [],
|
||||||
|
"interval" => $interval_count,
|
||||||
|
"addons" => [],
|
||||||
|
],
|
||||||
|
"payment_method" => $subscription->get_payment_method_to_display( 'customer' ),
|
||||||
|
"status" => wcs_get_subscription_status_name( $subscription->get_status() ),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
$payment_methods = [];
|
||||||
|
$payment_tokens = \WC_Payment_Tokens::get_customer_tokens( $this->user_id );
|
||||||
|
foreach ( $payment_tokens as $payment_token ) {
|
||||||
|
$type = strtolower( $payment_token->get_type() );
|
||||||
|
$card_type = $payment_token->get_card_type();
|
||||||
|
$payment_methods[] = [
|
||||||
|
'method' => [
|
||||||
|
'brand' => ( ! empty( $card_type ) ? ucfirst( $card_type ) : esc_html__( 'Credit card', 'woocommerce' ) ),
|
||||||
|
'gateway' => $payment_token->get_gateway_id(),
|
||||||
|
'last4' => $payment_token->get_last4(),
|
||||||
|
],
|
||||||
|
'expires' => $payment_token->get_expiry_month() . '/' . substr( $payment_token->get_expiry_year(), -2 ),
|
||||||
|
'is_default' => $payment_token->is_default(),
|
||||||
|
'token' => $payment_token->get_id(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$billing = (object) [
|
||||||
|
"address" => $address,
|
||||||
|
"subscriptions" => $subscriptions,
|
||||||
|
"invoices" => $invoices,
|
||||||
|
"payment_methods" => $payment_methods,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $billing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update_payment_method( $invoice_id, $payment_id ) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$wc_token = \WC_Payment_Tokens::get( $payment_id );
|
||||||
|
$source_id = $wc_token->get_token();
|
||||||
|
$customer = new \WC_Stripe_Customer( $this->user_id );
|
||||||
|
$customer_id = $customer->get_id();
|
||||||
|
$source_object = \WC_Stripe_API::retrieve( 'sources/' . $source_id );
|
||||||
|
|
||||||
|
$prepared_source = (object) [
|
||||||
|
'token_id' => $payment_id,
|
||||||
|
'customer' => $customer_id,
|
||||||
|
'source' => $source_id,
|
||||||
|
'source_object' => $source_object,
|
||||||
|
];
|
||||||
|
|
||||||
|
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||||
|
$payment_method = isset( $available_gateways[ 'stripe' ] ) ? $available_gateways[ 'stripe' ] : false;
|
||||||
|
$order = wc_get_order( $invoice_id );
|
||||||
|
$payment_method->save_source_to_order( $order, $prepared_source );
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'result' => 'success',
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch ( \WC_Stripe_Exception $e ) {
|
||||||
|
return array(
|
||||||
|
'result' => 'fail',
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pay_invoice( $invoice_id, $payment_id ) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
$wc_token = \WC_Payment_Tokens::get( $payment_id );
|
||||||
|
$source_id = $wc_token->get_token();
|
||||||
|
$customer = new \WC_Stripe_Customer( $this->user_id );
|
||||||
|
$customer_id = $customer->get_id();
|
||||||
|
$source_object = \WC_Stripe_API::retrieve( 'sources/' . $source_id );
|
||||||
|
|
||||||
|
$prepared_source = (object) [
|
||||||
|
'token_id' => $payment_id,
|
||||||
|
'customer' => $customer_id,
|
||||||
|
'source' => $source_id,
|
||||||
|
'source_object' => $source_object,
|
||||||
|
];
|
||||||
|
|
||||||
|
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||||
|
$payment_method = isset( $available_gateways[ 'stripe' ] ) ? $available_gateways[ 'stripe' ] : false;
|
||||||
|
$order = wc_get_order( $invoice_id );
|
||||||
|
$order->set_payment_method( 'stripe' );
|
||||||
|
$payment_method->save_source_to_order( $order, $prepared_source );
|
||||||
|
$intent = $payment_method->create_intent( $order, $prepared_source );
|
||||||
|
// Confirm the intent after locking the order to make sure webhooks will not interfere.
|
||||||
|
if ( empty( $intent->error ) ) {
|
||||||
|
$payment_method->lock_order_payment( $order, $intent );
|
||||||
|
$intent = $payment_method->confirm_intent( $intent, $order, $prepared_source );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $intent->error ) ) {
|
||||||
|
$payment_method->maybe_remove_non_existent_customer( $intent->error, $order );
|
||||||
|
|
||||||
|
// We want to retry.
|
||||||
|
if ( $payment_method->is_retryable_error( $intent->error ) ) {
|
||||||
|
return $payment_method->retry_after_error( $intent, $order, $retry, $force_save_source, $previous_error, $use_order_source );
|
||||||
|
}
|
||||||
|
|
||||||
|
$payment_method->unlock_order_payment( $order );
|
||||||
|
$payment_method->throw_localized_message( $intent, $order );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! empty( $intent ) ) {
|
||||||
|
// Use the last charge within the intent to proceed.
|
||||||
|
$response = end( $intent->charges->data );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process valid response.
|
||||||
|
$payment_method->process_response( $response, $order );
|
||||||
|
|
||||||
|
// Remove cart.
|
||||||
|
if ( isset( WC()->cart ) ) {
|
||||||
|
WC()->cart->empty_cart();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlock the order.
|
||||||
|
$payment_method->unlock_order_payment( $order );
|
||||||
|
|
||||||
|
$order->update_status( 'completed' );
|
||||||
|
|
||||||
|
// Return thank you page redirect.
|
||||||
|
return array(
|
||||||
|
'result' => 'success',
|
||||||
|
'redirect' => $payment_method->get_return_url( $order ),
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch ( \WC_Stripe_Exception $e ) {
|
||||||
|
wc_add_notice( $e->getLocalizedMessage(), 'error' );
|
||||||
|
\WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
|
||||||
|
|
||||||
|
do_action( 'wc_gateway_stripe_process_payment_error', $e, $order );
|
||||||
|
|
||||||
|
/* translators: error message */
|
||||||
|
$order->update_status( 'failed' );
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'result' => 'fail',
|
||||||
|
'redirect' => '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function roles() {
|
public function roles() {
|
||||||
return $this->roles;
|
return $this->roles;
|
||||||
}
|
}
|
||||||
|
@ -266,4 +511,8 @@ class User {
|
||||||
delete_user_meta( $this->user_id, 'requested_sites' );
|
delete_user_meta( $this->user_id, 'requested_sites' );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function user_id() {
|
||||||
|
return $this->user_id;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
136
captaincore.php
136
captaincore.php
|
@ -3549,12 +3549,30 @@ function captaincore_local_action_callback() {
|
||||||
"total" => $order->get_formatted_line_subtotal( $item ),
|
"total" => $order->get_formatted_line_subtotal( $item ),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$payment_gateways = WC()->payment_gateways->payment_gateways();
|
||||||
|
$payment_method = $order->get_payment_method();
|
||||||
|
$payment_method_string = sprintf(
|
||||||
|
__( 'Payment via %s', 'woocommerce' ),
|
||||||
|
esc_html( isset( $payment_gateways[ $payment_method ] ) ? $payment_gateways[ $payment_method ]->get_title() : "Check" )
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( $order->get_date_paid() ) {
|
||||||
|
$paid_on = sprintf(
|
||||||
|
__( 'Paid on %1$s @ %2$s', 'woocommerce' ),
|
||||||
|
wc_format_datetime( $order->get_date_paid() ),
|
||||||
|
wc_format_datetime( $order->get_date_paid(), get_option( 'time_format' ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$response = [
|
$response = [
|
||||||
"order_id" => $order_data->id,
|
"order_id" => $order_data->id,
|
||||||
"created_at" => $order_data->date_created->getTimestamp(),
|
"created_at" => $order_data->date_created->getTimestamp(),
|
||||||
"status" => $order_data->status,
|
"status" => $order_data->status,
|
||||||
"line_items" => $order_line_items,
|
"line_items" => $order_line_items,
|
||||||
"total" => $order_data->total,
|
"payment_method" => $payment_method_string,
|
||||||
|
"paid_on" => $paid_on,
|
||||||
|
"total" => number_format( (float) $order_data->total, 2, '.', '' ),
|
||||||
];
|
];
|
||||||
echo json_encode( $response );
|
echo json_encode( $response );
|
||||||
}
|
}
|
||||||
|
@ -3707,8 +3725,70 @@ function captaincore_account_action_callback() {
|
||||||
$cmd = $_POST['command'];
|
$cmd = $_POST['command'];
|
||||||
$everyone_commands = [
|
$everyone_commands = [
|
||||||
'requestSite',
|
'requestSite',
|
||||||
|
'payInvoice',
|
||||||
|
'setAsPrimary',
|
||||||
|
'addPaymentMethod',
|
||||||
|
'deletePaymentMethod',
|
||||||
'deleteRequestSite',
|
'deleteRequestSite',
|
||||||
|
'cancelPlan',
|
||||||
|
'updateBilling',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ( $cmd == 'updateBilling' ) {
|
||||||
|
$request = (object) $_POST['value'];
|
||||||
|
$customer = new WC_Customer( $user->user_id() );
|
||||||
|
$customer->set_billing_address_1( $request->address_1 );
|
||||||
|
$customer->set_billing_address_2( $request->address_2 );
|
||||||
|
$customer->set_billing_city( $request->city );
|
||||||
|
$customer->set_billing_company( $request->company );
|
||||||
|
$customer->set_billing_country( $request->country );
|
||||||
|
$customer->set_billing_email( $request->email );
|
||||||
|
$customer->set_billing_first_name( $request->first_name );
|
||||||
|
$customer->set_billing_last_name( $request->last_name );
|
||||||
|
$customer->set_billing_phone( $request->phone );
|
||||||
|
$customer->set_billing_postcode( $request->postcode );
|
||||||
|
$customer->set_billing_state( $request->state );
|
||||||
|
$customer->save();
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( $cmd == 'cancelPlan' ) {
|
||||||
|
|
||||||
|
$current_subscription = (object) $_POST['value'];
|
||||||
|
$current_user = $user->fetch();
|
||||||
|
$billing = $user->billing();
|
||||||
|
if ( $current_subscription->account_id == "" || $current_subscription->name == "" ) {
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
foreach ( $billing->subscriptions as $subscription ) {
|
||||||
|
if ( $subscription->account_id == $current_subscription->account_id && $subscription->name == $current_subscription->name ) {
|
||||||
|
|
||||||
|
// Build email
|
||||||
|
$to = get_option( 'admin_email' );
|
||||||
|
$subject = "Request cancel plan '{$current_subscription->name}'";
|
||||||
|
$body = "Request cancel plan '{$current_subscription->name}' #{$current_subscription->account_id} from {$current_user['name']}, <a href='mailto:{$current_user['email']}'>{$current_user['email']}</a>.";
|
||||||
|
$headers = [ 'Content-Type: text/html; charset=UTF-8' ];
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
wp_mail( $to, $subject, $body, $headers );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $cmd == 'requestPlanChanges' ) {
|
||||||
|
$current_user = $user->fetch();
|
||||||
|
$subscription = (object) $_POST['value'];
|
||||||
|
|
||||||
|
// Build email
|
||||||
|
$to = get_option( 'admin_email' );
|
||||||
|
$subject = "Request plan change from {$current_user['name']} <{$current_user['email']}>";
|
||||||
|
$body = "Change subscription '{$subscription->name}' to {$subscription->plan['name']} and {$subscription->plan['interval']} interval.";
|
||||||
|
$headers = [ 'Content-Type: text/html; charset=UTF-8' ];
|
||||||
|
|
||||||
|
// Send email
|
||||||
|
wp_mail( $to, $subject, $body, $headers );
|
||||||
|
}
|
||||||
|
|
||||||
$account_id = intval( $_POST['account_id'] );
|
$account_id = intval( $_POST['account_id'] );
|
||||||
|
|
||||||
// Only proceed if have permission to particular site id.
|
// Only proceed if have permission to particular site id.
|
||||||
|
@ -3718,6 +3798,37 @@ function captaincore_account_action_callback() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( $cmd == 'payInvoice' ) {
|
||||||
|
// Pay with new credit card
|
||||||
|
if ( isset( $_POST['source_id'] ) ) {
|
||||||
|
$response = $user->add_payment_method( $_POST['source_id'] );
|
||||||
|
$payment_tokens = WC_Payment_Tokens::get_customer_tokens( $user->user_id() );
|
||||||
|
foreach ( $payment_tokens as $payment_token ) {
|
||||||
|
if( $payment_token->get_token() == $_POST['source_id'] ) {
|
||||||
|
$user->pay_invoice( $_POST['value'], $payment_token->get_id() );
|
||||||
|
$user->set_as_primary( $payment_token->get_id() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wp_die();
|
||||||
|
}
|
||||||
|
// Pay with existing credit card
|
||||||
|
$user->pay_invoice( $_POST['value'], $_POST['payment_id'] );
|
||||||
|
$user->set_as_primary( $_POST['payment_id'] );
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( $cmd == 'setAsPrimary' ) {
|
||||||
|
$user->set_as_primary( $_POST['value'] );
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( $cmd == 'addPaymentMethod' ) {
|
||||||
|
$response = $user->add_payment_method( $_POST['value'] );
|
||||||
|
echo json_encode( $response );
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( $cmd == 'deletePaymentMethod' ) {
|
||||||
|
$user->delete_payment_method( $_POST['value'] );
|
||||||
|
};
|
||||||
|
|
||||||
if ( $cmd == 'requestSite' ) {
|
if ( $cmd == 'requestSite' ) {
|
||||||
$user->request_site( $_POST['value'] );
|
$user->request_site( $_POST['value'] );
|
||||||
echo json_encode( $user->fetch_requested_sites() );
|
echo json_encode( $user->fetch_requested_sites() );
|
||||||
|
@ -4371,14 +4482,7 @@ function captaincore_ajax_action_callback() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $cmd == 'updatePlan' ) {
|
if ( $cmd == 'updatePlan' ) {
|
||||||
$account = ( new CaptainCore\Accounts )->get( $post_id );
|
( new CaptainCore\Accounts )->update_plan( $value["plan"], $post_id );
|
||||||
$plan = json_decode( $account->plan );
|
|
||||||
$plan->name = $value["plan"]["name"];
|
|
||||||
$plan->price = $value["plan"]["price"];
|
|
||||||
$plan->addons = $value["plan"]["addons"];
|
|
||||||
$plan->limits = $value["plan"]["limits"];
|
|
||||||
$plan->interval = $value["plan"]["interval"];
|
|
||||||
( new CaptainCore\Accounts )->update( [ "plan" => json_encode( $plan ) ], [ "account_id" => $account->account_id ] );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $cmd == 'updateSettings' ) {
|
if ( $cmd == 'updateSettings' ) {
|
||||||
|
@ -5541,8 +5645,7 @@ function captaincore_get_checkout_payment_url( $payment_url ) {
|
||||||
// https://captcore-sitename.com/checkout/order-pay/1918?pay_for_order=true&key=wc_order_576c79296c346&subscription_renewal=true
|
// https://captcore-sitename.com/checkout/order-pay/1918?pay_for_order=true&key=wc_order_576c79296c346&subscription_renewal=true
|
||||||
// Replace with
|
// Replace with
|
||||||
// https://captcore-sitename.com/checkout-express/1918/?pay_for_order=true&key=wc_order_576c79296c346&subscription_renewal=true
|
// https://captcore-sitename.com/checkout-express/1918/?pay_for_order=true&key=wc_order_576c79296c346&subscription_renewal=true
|
||||||
$home_url = esc_url( home_url( '/' ) );
|
$home_url = esc_url( home_url( '/' ) );
|
||||||
|
|
||||||
$new_payment_url = str_replace( $home_url . 'checkout/order-pay/', $home_url . 'checkout-express/', $payment_url );
|
$new_payment_url = str_replace( $home_url . 'checkout/order-pay/', $home_url . 'checkout-express/', $payment_url );
|
||||||
|
|
||||||
return $new_payment_url;
|
return $new_payment_url;
|
||||||
|
@ -5978,6 +6081,9 @@ function load_captaincore_template( $original_template ) {
|
||||||
add_filter('redirect_canonical', 'disable_404_redirection_for_captaincore');
|
add_filter('redirect_canonical', 'disable_404_redirection_for_captaincore');
|
||||||
function disable_404_redirection_for_captaincore($redirect_url) {
|
function disable_404_redirection_for_captaincore($redirect_url) {
|
||||||
global $wp;
|
global $wp;
|
||||||
|
if ( strpos( $wp->request, "checkout-express/" ) !== false ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ( strpos( $wp->request, "account/" ) !== false ) {
|
if ( strpos( $wp->request, "account/" ) !== false ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1827,6 +1827,10 @@ div.update_logs table tr td:nth-child(1) {
|
||||||
|
|
||||||
/* WooCommerce billing adjustments */
|
/* WooCommerce billing adjustments */
|
||||||
|
|
||||||
|
.theme--light.v-data-table.invoice>.v-data-table__wrapper>table>tbody>tr:hover:not(.v-data-table__expanded__content):not(.v-data-table__empty-wrapper) {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.woocommerce-order-details .shop_table.shop_table_responsive.my_account_orders.woocommerce-orders-table.woocommerce-MyAccount-subscriptions.woocommerce-orders-table--subscriptions,
|
.woocommerce-order-details .shop_table.shop_table_responsive.my_account_orders.woocommerce-orders-table.woocommerce-MyAccount-subscriptions.woocommerce-orders-table--subscriptions,
|
||||||
.woocommerce-order-details header,
|
.woocommerce-order-details header,
|
||||||
.woocommerce-order-details .order-again {
|
.woocommerce-order-details .order-again {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -13,9 +13,6 @@ $order_key = get_field( '_order_key', $order_id );
|
||||||
// Loads order
|
// Loads order
|
||||||
$order = new WC_Order( $order_id );
|
$order = new WC_Order( $order_id );
|
||||||
|
|
||||||
// Fetch payment url
|
|
||||||
$payment_url = $order->get_checkout_payment_url();
|
|
||||||
|
|
||||||
// Fetch user
|
// Fetch user
|
||||||
$user = get_user_by( 'id', $customer_id );
|
$user = get_user_by( 'id', $customer_id );
|
||||||
|
|
||||||
|
@ -26,7 +23,7 @@ if ( $user and $order_key == $key ) {
|
||||||
wp_set_auth_cookie( $user->ID );
|
wp_set_auth_cookie( $user->ID );
|
||||||
|
|
||||||
// Redirect to payment url
|
// Redirect to payment url
|
||||||
wp_redirect( $payment_url );
|
wp_redirect( "/account/billing/" . $order_id );
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue