STRATO-apps/wordpress_03/app/wp-content/plugins/paid-memberships-pro/classes/class.memberorder.php
SHA-256: 6c55a99f70929e44e11821e62c824fff37bec0df7797dc953f659ee968704195
<?php
class MemberOrder
{
/**
* The Member Order ID
*
* @since 2.9
*
* @var int
*/
private $id = 0;
/**
* The Member Order Identifier, also used as invoie number
*
* @since 2.9
*
* @var string
*/
private $code = '';
/**
* User ID
*
* @since 2.9
*
* @var int
*/
private $user_id = 0;
/**
* Level ID
*
* @since 2.9
*
* @var int
*/
private $membership_id = 0;
/**
* Session ID
*
* @since 2.9
*
* @var string
*/
private $session_id = '';
/**
* PayPal Token
*
* @since 2.9
*
* @var string
*/
private $paypal_token = '';
/**
* Contain a billing address object
*
* @since 2.9
*
* @var object
*/
private $billing = '';
/**
* Subtotal value
*
* @since 2.9
*
* @var float
*/
private $subtotal = 0.00;
/**
* Tax Amount
*
* @since 2.9
*
* @var float
*/
private $tax = 0.00;
/**
* Total order amount
*
* @since 2.9
*
* @var float
*/
private $total = 0.00;
/**
* The gateway name or label used (Stripe, Check etc)
*
* @since 2.9
*
* @var string
*/
private $payment_type = '';
/**
* The Card Type used (Visa etc)
*
* @since 2.9
*
* @var string
*/
private $cardtype = '';
/**
* Account or Card Number (only shows last 4 digits)
*
* @since 2.9
*
* @var string
*/
private $accountnumber = '';
/**
* Card Expiration Month (02)
*
* @since 2.9
*
* @var string
*/
private $expirationmonth = '';
/**
* Expiration Year (22)
*
* @since 2.9
*
* @var string
*/
private $expirationyear = '';
/**
* The Order Status
*
* @since 2.9
*
* @var string
*/
private $status = '';
/**
* The Gateway identifier (stripe, paypalexpress etc)
*
* @since 2.9
*
* @var string
*/
private $gateway = '';
/**
* The Gateway Environment (live, sandbox)
*
* @since 2.9
*
* @var string
*/
private $gateway_environment = '';
/**
* The payment Transaction ID
*
* @since 2.9
*
* @var string
*/
private $payment_transaction_id = '';
/**
* The Subscription Transaction ID
*
* @since 2.9
*
* @var string
*/
private $subscription_transaction_id = '';
/**
* The time the order was created as a Unix timestamp.
*
* @since 2.9
*
* @var int
*/
private $timestamp = 0;
/**
* The Affiliate ID
*
* @since 2.9
*
* @var int
*/
private $affiliate_id = 0;
/**
* The Affiliate Sub ID
*
* @since 2.9
*
* @var string
*/
private $affiliate_subid = '';
/**
* The Order notes
*
* @since 2.9
*
* @var string
*/
private $notes = '';
/**
* The Checkout ID - used to track multiple orders during a single checkout
*
* @since 2.9
*
* @var string
*/
private $checkout_id = '';
/**
* The discount code ID that was used for this order.
*
* This property is being added in PMPro v3.0 with the intention
* of adding a new column to the pmpro_membership_orders table duringe next
* major release. For now, we need to have code to "fake" this property.
* For now, this property will be initialized to `null` and will be set to an int
* when this property is accessed.
*
* Step 1 (v3.0): Create abstracted function for modifying/searching the discount code ID.
* Step 2 (At least 1 year later): Update Add Ons to use the new abstracted functions.
* Step 3 (Next major release after all Add Ons updated): Add new column to the pmpro_membership_orders
* table for the discount code ID, update the abstracted functions to use the new column, create
* migration script to move the discount code ID from the pmpro_discount_codes_uses table to the
* new column in the pmpro_membership_orders table, and finally, drop the pmpro_discount_codes_uses table.
*
* Search "@DISCOUNT_CODE_ID_TODO" to find the places where we need to update
* the code to use the new column when we add it.
*
* `0` means no discount code was used, any other int is the ID of the discount code used.
*
* @since 3.0
*
* @var int|null
*/
private $discount_code_id = null;
/**
* Defines an array of optionally used properties
*
* @since 2.9
*
* @var array
*/
private $other_properties = array();
/**
* Constructor
*/
function __construct($id = NULL)
{
//set up the gateway
$this->setGateway(get_option("pmpro_gateway"));
//set up the billing address structure
$this->billing = new stdClass();
$this->billing->name = '';
$this->billing->street = '';
$this->billing->street2 = '';
$this->billing->city = '';
$this->billing->state = '';
$this->billing->zip = '';
$this->billing->country = '';
$this->billing->phone = '';
//get data if an id was passed
if ( $id ) {
if ( is_numeric( $id ) ) {
$morder = $this->getMemberOrderByID( $id );
} else {
$morder = $this->getMemberOrderByCode( $id );
}
// If we found an order, create a subscription if needed.
if ( ! empty( $this->id ) ) {
$this->get_subscription();
}
} else {
$morder = $this->getEmptyMemberOrder(); //blank constructor
}
$this->original_status = $this->status;
return $morder;
}
/**
* Get Magic Method
*
* @since 2.9
*
* @param string $property The property we want to get
*
* @return mixed|void
*/
public function __get( $property ) {
/**
* Special case. We want to add `discount_code_id` as a property/db column in the future.
* For now, we should support `discount_code_id` as a "property" by adding it here and
* querying the pmpro_discount_codes_uses table for the discount code ID.
*
* @DISCOUNT_CODE_ID_TODO
*/
if ( $property == 'discount_code_id' ) {
if ( null === $this->discount_code_id ) {
// Get the discount code ID from the pmpro_discount_codes_uses table.
$this->getDiscountCode( true );
}
return $this->discount_code_id;
}
if ( $property == 'other_properties' ) {
return; //We don't want the actual other_properties array to be changed
}
if ( property_exists( $this, $property ) ) {
return $this->{$property};
}
if ( isset( $this->other_properties[ $property ] ) ) {
return $this->other_properties[ $property ];
}
/**
* Support some legacy properties. These should be removed in the next major release.
*/
switch ( $property ) {
case 'ExpirationDate':
_doing_it_wrong( __METHOD__, esc_html__( 'ExpirationDate is deprecated. Use expirationmonth and expirationyear instead.', 'paid-memberships-pro' ), '3.2' );
return $this->expirationmonth . $this->expirationyear;
case 'ExpirationDate_YdashM':
_doing_it_wrong( __METHOD__, esc_html__( 'ExpirationDate_YdashM is deprecated. Use expirationyear and expirationmonth instead.', 'paid-memberships-pro' ), '3.2' );
return $this->expirationyear . '-' . $this->expirationmonth;
case 'membership_name':
_doing_it_wrong( __METHOD__, esc_html__( 'membership_name is deprecated. Use pmpro_getLevel() instead.', 'paid-memberships-pro' ), '3.2' );
$level = pmpro_getLevel( $this->membership_id );
return empty( $level->name ) ? '' : $level->name;
case 'InitialPayment':
_doing_it_wrong( __METHOD__, esc_html__( 'InitialPayment is deprecated. Use subtotal instead.', 'paid-memberships-pro' ), '3.2' );
return $this->subtotal;
case 'PaymentAmount':
_doing_it_wrong( __METHOD__, esc_html__( 'PaymentAmount is deprecated. Get billing_amount from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return empty( $level->billing_amount ) ? 0 : $level->billing_amount;
case 'BillingPeriod':
_doing_it_wrong( __METHOD__, esc_html__( 'BillingPeriod is deprecated. Get cycle_period from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return empty( $level->cycle_period ) ? '' : $level->cycle_period;
case 'BillingFrequency':
_doing_it_wrong( __METHOD__, esc_html__( 'BillingFrequency is deprecated. Get cycle_number from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return empty( $level->cycle_number ) ? 0 : $level->cycle_number;
case 'TrialBillingPeriod':
_doing_it_wrong( __METHOD__, esc_html__( 'TrialBillingPeriod is deprecated. Get cycle_period from $this->getMembershipLevelAtCheckout() if the level is a trial instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return pmpro_isLevelTrial( $level ) ? $level->cycle_period : '';
case 'TrialBillingFrequency':
_doing_it_wrong( __METHOD__, esc_html__( 'TrialBillingFrequency is deprecated. Get cycle_number from $this->getMembershipLevelAtCheckout() if the level is a trial instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return pmpro_isLevelTrial( $level ) ? $level->cycle_number : 0;
case 'TrialBillingCycles':
_doing_it_wrong( __METHOD__, esc_html__( 'TrialBillingCycles is deprecated. Get trial_limit from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return $level->trial_limit;
case 'TrialAmount':
_doing_it_wrong( __METHOD__, esc_html__( 'TrialAmount is deprecated. Get trial_amount from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return pmpro_round_price( $level->trial_amount );
case 'TotalBillingCycles':
_doing_it_wrong( __METHOD__, esc_html__( 'TotalBillingCycles is deprecated. Get billing_limit from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
return empty( $level->billing_limit ) ? 0 : $level->billing_limit;
case 'ProfileStartDate':
_doing_it_wrong( __METHOD__, esc_html__( 'ProfileStartDate is deprecated. Use the pmpro_calculate_profile_start_date() instead.', 'paid-memberships-pro' ), '3.2' );
return pmpro_calculate_profile_start_date( $this, 'Y-m-d\TH:i:s' );
case 'CVV2':
_doing_it_wrong( __METHOD__, esc_html__( 'CVV2 is deprecated. Use the CVV from $_REQUEST instead.', 'paid-memberships-pro' ), '3.2' );
return empty( $_REQUEST['CVV'] ) ? '' : sanitize_text_field( $_REQUEST['CVV'] );
case 'FirstName':
_doing_it_wrong( __METHOD__, esc_html__( 'FirstName is deprecated. Use the the billing name instead.', 'paid-memberships-pro' ), '3.2' );
$nameparts = pnp_split_full_name( $this->billing->name );
return empty( $nameparts['fname'] ) ? '' : $nameparts['fname'];
case 'LastName':
_doing_it_wrong( __METHOD__, esc_html__( 'LastName is deprecated. Use the the billing name instead.', 'paid-memberships-pro' ), '3.2' );
$nameparts = pnp_split_full_name( $this->billing->name );
return empty( $nameparts['lname'] ) ? '' : $nameparts['lname'];
case 'Address1':
_doing_it_wrong( __METHOD__, esc_html__( 'Address1 is deprecated. Use the the billing address instead.', 'paid-memberships-pro' ), '3.2' );
return empty( $this->billing->street ) ? '' : $this->billing->street;
case 'Address2':
_doing_it_wrong( __METHOD__, esc_html__( 'Address2 is deprecated. Use the the billing address instead.', 'paid-memberships-pro' ), '3.2' );
return empty( $this->billing->street2 ) ? '' : $this->billing->street2;
case 'Email':
_doing_it_wrong( __METHOD__, esc_html__( 'Email is deprecated. Use the the user email instead.', 'paid-memberships-pro' ), '3.2' );
$user = get_userdata( $this->user_id );
return empty( $user->user_email ) ? '' : $user->user_email;
case 'initial_amount':
_doing_it_wrong( __METHOD__, esc_html__( 'initial_amount is deprecated. Use the subtotal and then calculate the tax instead.', 'paid-memberships-pro' ), '3.2' );
$initial_tax = $this->getTaxForPrice( $this->subtotal );
return pmpro_round_price((float)$this->subtotal + (float)$initial_tax);
case 'subscription_amount':
_doing_it_wrong( __METHOD__, esc_html__( 'subscription_amount is deprecated. Use the billing_amount from $this->getMembershipLevelAtCheckout() instead.', 'paid-memberships-pro' ), '3.2' );
$level = $this->getMembershipLevelAtCheckout();
$subscription_tax = $this->getTaxForPrice( $level->billing_amount );
return pmpro_round_price( (float)$level->billing_amount + (float)$subscription_tax );
}
}
/**
* Set Magic Method
*
* @since 2.9
*
* @param string $property The property we want to reference
* @param string $value The value we want to set for $property
*
* @return mixed|null
*/
public function __set( $property, $value ) {
if ( $property == 'other_properties' ) {
return; //We don't want the actual other_properties array to be changed
}
/**
* Special case. We want to add `discount_code_id` as a property/db column in the future.
* For now, `discount_code_id` may be null or an int. But if being updated, we always want it to be an int.
*
* @DISCOUNT_CODE_ID_TODO
*/
if ( $property == 'discount_code_id' ) {
$this->discount_code_id = (int) $value;
return;
}
if ( property_exists( $this, $property ) ) {
// Perform validation as needed here.
if ( is_int( $this->{$property} ) ) {
$value = (int) $value;
} elseif ( is_float( $this->{$property} ) ) {
$value = (float) $value;
}
$this->{$property} = $value;
} else {
$this->other_properties[ $property ] = $value;
}
}
/**
* Is Set Magic Method
*
* @since 2.9
*
* @param string $property The property we want to reference
*
* @return bool
*/
public function __isset( $property ) {
return property_exists( $this, $property ) || isset( $this->other_properties[ $property ] );
}
/**
* Unset Magic Method.
*
* @since 2.9.1
*
* @param string $property The property we want to unset.
*/
public function __unset( $property ) {
if ( property_exists( $this, $property ) ) {
unset( $this->{$property} );
} else {
unset( $this->other_properties[ $property ] );
}
}
/**
* Get a specific order by ID, code, or an array of arguments
*
* @since 2.9
*
* @param mixed $args Specify an order ID, code, or array of arguments to find an order for.
*
*/
public static function get_order( $args = NULL ) {
// At least one argument is required.
if ( empty( $args ) ) {
return null;
}
if ( is_numeric( $args ) ) {
// If its numeric we assume you're trying to get an ID.
$args = array(
'id' => $args,
);
} elseif ( is_string( $args ) ) {
// If it is a string but not numeric, we assume it's a string and should be a code.
$args = array(
'code' => $args,
);
}
// Invalid arguments.
if ( ! is_array( $args ) ) {
return null;
}
// Force returning of one order.
$args['limit'] = 1;
// Get the orders using query arguments.
$orders = self::get_orders( $args );
// Check if we found any orders.
if ( empty( $orders ) ) {
return null;
}
// Get the first order in the array.
return reset( $orders );
}
/**
* Get orders based on various parameters
*
* @since 2.9
*
* @param array $args Specify what you'd like to filter the query by
*
*/
public static function get_orders( array $args = array() ) {
global $wpdb;
// Check if we are going to return the count of orders.
$return_count = isset( $args['return_count'] ) ? (bool) $args['return_count'] : false;
$sql_query = $return_count ? "SELECT COUNT(*) FROM `$wpdb->pmpro_membership_orders` `o`" : "SELECT `o`.`id` FROM `$wpdb->pmpro_membership_orders` `o`";
$prepared = array();
$where = array();
$orderby = isset( $args['orderby'] ) ? $args['orderby'] : '`o`.`timestamp` DESC';
$limit = isset( $args['limit'] ) ? (int) $args['limit'] : 100;
// Detect unsupported orderby usage (in the future we may support better syntax).
if ( $orderby !== preg_replace( '/[^a-zA-Z0-9\s,.`]/', ' ', $orderby ) ) {
return array();
}
/*
* Now filter the query based on the arguments provided.
*/
// Filter by ID(s).
if ( isset( $args['id'] ) ) {
if ( ! is_array( $args['id'] ) ) {
$where[] = '`o`.`id` = %d';
$prepared[] = $args['id'];
} else {
$where[] = '`o`.`id` IN ( ' . implode( ', ', array_fill( 0, count( $args['id'] ), '%d' ) ) . ' )';
$prepared = array_merge( $prepared, $args['id'] );
}
}
// Filter by user ID(s).
if ( isset( $args['user_id'] ) ) {
if ( ! is_array( $args['user_id'] ) ) {
$where[] = '`o`.`user_id` = %d';
$prepared[] = $args['user_id'];
} else {
$where[] = '`o`.`user_id` IN ( ' . implode( ', ', array_fill( 0, count( $args['user_id'] ), '%d' ) ) . ' )';
$prepared = array_merge( $prepared, $args['user_id'] );
}
}
// Filter by membership level ID(s).
if ( isset( $args['membership_level_id'] ) ) {
if ( ! is_array( $args['membership_level_id'] ) ) {
$where[] = '`o`.`membership_id` = %d';
$prepared[] = $args['membership_level_id'];
} else {
$where[] = '`o`.`membership_id` IN ( ' . implode( ', ', array_fill( 0, count( $args['membership_level_id'] ), '%d' ) ) . ' )';
$prepared = array_merge( $prepared, $args['membership_level_id'] );
}
}
// Filter by status(es).
if ( isset( $args['status'] ) ) {
if ( ! is_array( $args['status'] ) ) {
$where[] = '`o`.`status` = %s';
$prepared[] = $args['status'];
} else {
$where[] = '`o`.`status` IN ( ' . implode( ', ', array_fill( 0, count( $args['status'] ), '%s' ) ) . ' )';
$prepared = array_merge( $prepared, $args['status'] );
}
}
// Filter by subscription transaction ID(s).
if ( isset( $args['subscription_transaction_id'] ) ) {
if ( ! is_array( $args['subscription_transaction_id'] ) ) {
$where[] = '`o`.`subscription_transaction_id` = %s';
$prepared[] = $args['subscription_transaction_id'];
} else {
$where[] = '`o`.`subscription_transaction_id` IN ( ' . implode( ', ', array_fill( 0, count( $args['subscription_transaction_id'] ), '%s' ) ) . ' )';
$prepared = array_merge( $prepared, $args['subscription_transaction_id'] );
}
}
// Filter by gateway(s).
if ( isset( $args['gateway'] ) ) {
if ( ! is_array( $args['gateway'] ) ) {
$where[] = '`o`.`gateway` = %s';
$prepared[] = $args['gateway'];
} else {
$where[] = '`o`.`gateway` IN ( ' . implode( ', ', array_fill( 0, count( $args['gateway'] ), '%s' ) ) . ' )';
$prepared = array_merge( $prepared, $args['gateway'] );
}
}
// Filter by gateway environment(s).
if ( isset( $args['gateway_environment'] ) ) {
if ( ! is_array( $args['gateway_environment'] ) ) {
$where[] = '`o`.`gateway_environment` = %s';
$prepared[] = $args['gateway_environment'];
} else {
$where[] = '`o`.`gateway_environment` IN ( ' . implode( ', ', array_fill( 0, count( $args['gateway_environment'] ), '%s' ) ) . ' )';
$prepared = array_merge( $prepared, $args['gateway_environment'] );
}
}
// Filter by billing amount(s).
if ( isset( $args['total'] ) ) {
if ( ! is_array( $args['total'] ) ) {
$where[] = '`o`.`total` = %f';
$prepared[] = $args['total'];
} else {
$where[] = '`o`.`total` IN ( ' . implode( ', ', array_fill( 0, count( $args['total'] ), '%f' ) ) . ' )';
$prepared = array_merge( $prepared, $args['total'] );
}
}
// Filter by payment transaction ID
if ( isset( $args['payment_transaction_id'] ) ) {
if ( ! is_array( $args['payment_transaction_id'] ) ) {
$where[] = '`o`.`payment_transaction_id` = %s';
$prepared[] = $args['payment_transaction_id'];
} else {
$where[] = '`o`.`payment_transaction_id` IN ( ' . implode( ', ', array_fill( 0, count( $args['payment_transaction_id'] ), '%f' ) ) . ' )';
$prepared = array_merge( $prepared, $args['payment_transaction_id'] );
}
}
// Filter by discount code ID
/**
* Special case. Eventually, we want to add `discount_code_id` as a property/db column. But
* for now, we need to query the pmpro_discount_codes_uses table for the discount code ID.
*
* @DISCOUNT_CODE_ID_TODO
*/
if ( isset( $args['discount_code_id'] ) ) {
$sql_query .= " LEFT JOIN `$wpdb->pmpro_discount_codes_uses` `dcu` ON `dcu`.`order_id` = `o`.`id`";
if ( ! is_array( $args['discount_code_id'] ) ) {
$where[] = '`dcu`.`code_id` = %d';
$prepared[] = $args['discount_code_id'];
} else {
$where[] = '`dcu`.`code_id` IN ( ' . implode( ', ', array_fill( 0, count( $args['discount_code_id'] ), '%d' ) ) . ' )';
$prepared = array_merge( $prepared, $args['discount_code_id'] );
}
}
// Filter by date range by start date. (YYYY-MM-DD HH:MM:SS UTC)
if ( isset( $args['start_date'] ) ) {
$where[] = '`o`.`timestamp` >= %s';
$prepared[] = $args['start_date'];
}
// Filter by date range by end date. (YYYY-MM-DD HH:MM:SS UTC)
if ( isset( $args['end_date'] ) ) {
$where[] = '`o`.`timestamp` <= %s';
$prepared[] = $args['end_date'];
}
// Maybe filter the data.
if ( $where ) {
$sql_query .= ' WHERE ' . implode( ' AND ', $where );
}
if ( ! $return_count ) {
// Handle the order of data.
$sql_query .= ' ORDER BY ' . $orderby;
// Maybe limit the data.
if ( $limit ) {
$sql_query .= ' LIMIT %d';
$prepared[] = $limit;
}
}
// Maybe prepare the query.
if ( $prepared ) {
$sql_query = $wpdb->prepare( $sql_query, $prepared );
}
// If we're returning a count, return the count.
if ( $return_count ) {
return (int) $wpdb->get_var( $sql_query );
}
// Not returning a count, so get the order IDs.
$member_order_ids = $wpdb->get_col( $sql_query );
if ( empty( $member_order_ids ) ) {
return array();
}
$member_orders = array();
foreach ( $member_order_ids as $member_order_id ) {
$morder = new MemberOrder( $member_order_id );
// Make sure the subscription object is valid.
if ( ! empty( $morder->id ) ) {
$member_orders[] = $morder;
}
}
return $member_orders;
}
/**
* Returns an empty (but complete) order object.
*
* @return stdClass $order - a 'clean' order object
*
* @since: 1.8.6.8
*/
function getEmptyMemberOrder()
{
//defaults
$this->code = $this->getRandomCode();
$this->status = "success";
$this->gateway = get_option("pmpro_gateway");
$this->gateway_environment = get_option("pmpro_gateway_environment");
return $this;
}
/**
* Retrieve a member order from the DB by ID
*/
function getMemberOrderByID($id)
{
global $wpdb;
if(!$id)
return false;
$dbobj = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->pmpro_membership_orders WHERE id = %d LIMIT 1", $id ) );
if($dbobj)
{
$this->id = $dbobj->id;
$this->code = $dbobj->code;
$this->session_id = $dbobj->session_id;
$this->user_id = $dbobj->user_id;
$this->membership_id = $dbobj->membership_id;
$this->paypal_token = $dbobj->paypal_token;
$this->billing = new stdClass();
$this->billing->name = $dbobj->billing_name;
$this->billing->street = $dbobj->billing_street;
$this->billing->street2 = $dbobj->billing_street2;
$this->billing->city = $dbobj->billing_city;
$this->billing->state = $dbobj->billing_state;
$this->billing->zip = $dbobj->billing_zip;
$this->billing->country = $dbobj->billing_country;
$this->billing->phone = $dbobj->billing_phone;
$this->subtotal = $dbobj->subtotal;
$this->tax = (float)$dbobj->tax;
$this->total = $dbobj->total;
$this->payment_type = $dbobj->payment_type;
$this->cardtype = $dbobj->cardtype;
$this->accountnumber = trim($dbobj->accountnumber);
$this->expirationmonth = $dbobj->expirationmonth;
$this->expirationyear = $dbobj->expirationyear;
$this->status = $dbobj->status;
$this->gateway = $dbobj->gateway;
$this->gateway_environment = $dbobj->gateway_environment;
$this->payment_transaction_id = $dbobj->payment_transaction_id;
$this->subscription_transaction_id = $dbobj->subscription_transaction_id;
$this->timestamp = strtotime( $dbobj->timestamp );
$this->affiliate_id = $dbobj->affiliate_id;
$this->affiliate_subid = $dbobj->affiliate_subid;
$this->notes = $dbobj->notes;
$this->checkout_id = $dbobj->checkout_id;
//reset the gateway
if(empty($this->nogateway))
$this->setGateway();
return $this->id;
}
else
return false; //didn't find it in the DB
}
/**
* Get the first order for this subscription.
* Useful to find the original order from a recurring order.
* @since 2.5
* @return mixed Order object if found or false if not.
*/
function get_original_subscription_order( $subscription_id = '' ){
global $wpdb;
// Default to use the subscription ID on this order object.
if ( empty( $subscription_id ) && ! empty( $this->subscription_transaction_id ) ) {
$subscription_id = $this->subscription_transaction_id;
}
// Must have a subscription ID.
if ( empty( $subscription_id ) ) {
return false;
}
// Get some other values from this order to narrow the search.
if ( ! empty( $this->user_id ) ) {
$user_id = $this->user_id;
} else {
$user_id = '';
}
if ( ! empty( $this->gateway ) ) {
$gateway = $this->gateway;
} else {
$gateway = '';
}
if ( ! empty( $this->gateway_environment ) ) {
$gateway_environment = $this->gateway_environment;
} else {
$gateway_environment = '';
}
// Double check for a user_id, gateway and gateway environment.
$sql = $wpdb->prepare(
"SELECT ID
FROM $wpdb->pmpro_membership_orders
WHERE `subscription_transaction_id` = %s
AND `user_id` = %d
AND `gateway` = %s
AND `gateway_environment` = %s
ORDER BY id ASC
LIMIT 1",
array(
$subscription_id,
$user_id,
$gateway,
$gateway_environment
)
);
$order_id = $wpdb->get_var( $sql );
if ( ! empty( $order_id ) ) {
return new MemberOrder( $order_id );
} else {
return false;
}
}
/**
* Is this order a 'renewal'?
* We currently define a renewal as any order from a user who has
* a previous paid (non-$0) order.
*/
function is_renewal() {
global $wpdb;
// If our property is already set, use that.
if ( isset( $this->is_renewal ) ) {
return $this->is_renewal;
}
// Can't tell if this is a renewal without a user.
if ( empty( $this->user_id ) ) {
$this->is_renewal = false;
return $this->is_renewal;
}
// Can't tell if this is a renewal without a timestamp.
if ( empty( $this->timestamp ) ) {
$this->is_renewal = false;
return $this->is_renewal;
}
// Check the DB.
$sqlQuery = "SELECT `id`
FROM $wpdb->pmpro_membership_orders
WHERE `user_id` = '" . esc_sql( $this->user_id ) . "'
AND `id` <> '" . esc_sql( $this->id ) . "'
AND `gateway_environment` = '" . esc_sql( $this->gateway_environment ) . "'
AND `total` > 0
AND `total` IS NOT NULL
AND status NOT IN('refunded', 'review', 'token', 'error')
AND timestamp < '" . esc_sql( date( 'Y-m-d H:i:s', $this->timestamp ) ) . "'
LIMIT 1";
$older_order_id = $wpdb->get_var( $sqlQuery );
if ( ! empty( $older_order_id ) ) {
$this->is_renewal = true;
} else {
$this->is_renewal = false;
}
return $this->is_renewal;
}
/**
* Set up the Gateway class to use with this order.
*
* @param string $gateway Name/label for the gateway to set.
*
*/
function setGateway($gateway = NULL) {
//set the gateway property
if(isset($gateway)) {
$this->gateway = $gateway;
}
//which one to load?
$classname = "PMProGateway"; //default test gateway
if(!empty($this->gateway) && $this->gateway != "free") {
$classname .= "_" . $this->gateway; //adding the gateway suffix
}
if(class_exists($classname) && isset($this->gateway)) {
$this->Gateway = new $classname($this->gateway);
} else {
$this->Gateway = null; //null out any current gateway
new WP_Error("PMPro1001", "Could not locate the gateway class file with class name = " . $classname . ".");
}
/**
* Allow changing the gateway object for this member order
*
* @param PMProGateway $gateway_object Gateway object.
* @param MemberOrder $this Member order object.
*
* @since 3.0.3
*/
$this->Gateway = apply_filters( 'pmpro_order_gateway_object', $this->Gateway, $this );
if(!empty($this->Gateway)) {
return $this->Gateway;
} else {
//gateway wasn't setup
return false;
}
}
/**
* Get the most recent order for a user.
*
* @param int $user_id ID of user to find order for.
* @param string|string[] $status Limit search to only orders with this status. Defaults to "success".
* @param int|int[] $membership_id Limit search to only orders for this membership level. Defaults to NULL to find orders for any level.
* @param string $gateway Limit search to only orders with this gateway. Defaults to NULL to find orders for any gateway.
* @param string $gateway_environment Limit search to only orders with this gateway environment. Defaults to NULL to find orders for any gateway environment.
*
* @return MemberOrder
*/
function getLastMemberOrder($user_id = NULL, $status = 'success', $membership_id = NULL, $gateway = NULL, $gateway_environment = NULL)
{
global $current_user, $wpdb;
if(!$user_id)
$user_id = $current_user->ID;
if(!$user_id)
return false;
//build query
$this->sqlQuery = "SELECT id FROM $wpdb->pmpro_membership_orders WHERE user_id = '" . esc_sql( $user_id ) . "' ";
if(!empty($status) && is_array($status)) {
$this->sqlQuery .= "AND status IN('" . implode("','", array_map( 'esc_sql', $status ) ) . "') ";
} elseif(!empty($status)) {
$this->sqlQuery .= "AND status = '" . esc_sql($status) . "' ";
}
if(!empty($membership_id) && is_array($membership_id)) {
$this->sqlQuery .= "AND membership_id IN(" . implode( ",", array_map( 'esc_sql', $membership_id ) ) . ") ";
} elseif(!empty($membership_id)) {
$this->sqlQuery .= "AND membership_id = '" . esc_sql( $membership_id ) . "' ";
}
if(!empty($gateway))
$this->sqlQuery .= "AND gateway = '" . esc_sql($gateway) . "' ";
if(!empty($gateway_environment))
$this->sqlQuery .= "AND gateway_environment = '" . esc_sql($gateway_environment) . "' ";
$this->sqlQuery .= "ORDER BY timestamp DESC LIMIT 1";
//get id
$id = $wpdb->get_var($this->sqlQuery);
return $this->getMemberOrderByID($id);
}
/*
Returns the order using the given order code.
*/
function getMemberOrderByCode($code)
{
global $wpdb;
$id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = %s LIMIT 1", $code ) );
if($id)
return $this->getMemberOrderByID($id);
else
return false;
}
/*
Returns the last order using the given payment_transaction_id.
*/
function getMemberOrderByPaymentTransactionID($payment_transaction_id)
{
//did they pass a trans id?
if(empty($payment_transaction_id))
return false;
global $wpdb;
$id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE payment_transaction_id = %s LIMIT 1", $payment_transaction_id ) );
if($id)
return $this->getMemberOrderByID($id);
else
return false;
}
/**
* Returns the last order using the given subscription_transaction_id.
*/
function getLastMemberOrderBySubscriptionTransactionID($subscription_transaction_id)
{
//did they pass a sub id?
if(empty($subscription_transaction_id))
return false;
global $wpdb;
$id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE subscription_transaction_id = %s ORDER BY id DESC LIMIT 1", $subscription_transaction_id ) );
if($id)
return $this->getMemberOrderByID($id);
else
return false;
}
/**
* Returns the last order using the given paypal token.
*/
function getMemberOrderByPayPalToken($token)
{
global $wpdb;
$id = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE paypal_token = %s LIMIT 1", $token ) );
if($id)
return $this->getMemberOrderByID($id);
else
return false;
}
/**
* Get a discount code object for the code used in this order.
*
* @param bool $force If true, it will query the database again.
*
*/
function getDiscountCode( $force = false ) {
// If the order doesn't have an ID yet, we don't want to search the database for a use matching a null order ID.
if ( empty( $this->id ) ) {
return null;
}
// If we already have the discount code, return it.
if ( ! empty ($this->discount_code ) && ! $force ) {
return $this->discount_code;
}
global $wpdb;
$this->discount_code = $wpdb->get_row( $wpdb->prepare( "SELECT dc.* FROM $wpdb->pmpro_discount_codes dc LEFT JOIN $wpdb->pmpro_discount_codes_uses dcu ON dc.id = dcu.code_id WHERE dcu.order_id = %d LIMIT 1", $this->id ) );
//filter @since v1.7.14
$this->discount_code = apply_filters("pmpro_order_discount_code", $this->discount_code, $this);
/**
* Special case. We want to add `discount_code_id` as a property/db column in the future.
* For now, save the discount code ID as a property on the order object.
*
* @DISCOUNT_CODE_ID_TODO
*/
$this->discount_code_id = ! empty( $this->discount_code ) ? $this->discount_code->id : 0;
return $this->discount_code;
}
/**
* Update the discount code used in this order.
*
* @param int $discount_code_id The ID of the discount code to update.
*
*/
function updateDiscountCode( $discount_code_id ) {
global $wpdb;
// Assumes one discount code per order
$sqlQuery = $wpdb->prepare("
SELECT id FROM $wpdb->pmpro_discount_codes_uses
WHERE order_id = %d
LIMIT 1",
$this->id
);
$discount_codes_uses_id = $wpdb->get_var( $sqlQuery );
// INSTEAD: Delete the code use if found
if ( empty( $discount_code_id ) ) {
if ( ! empty( $discount_codes_uses_id ) ) {
$wpdb->delete(
$wpdb->pmpro_discount_codes_uses,
array( 'id' => $discount_codes_uses_id ),
array( '%d' )
);
}
} else {
if ( ! empty( $discount_codes_uses_id ) ) {
// Update existing row
$wpdb->update(
$wpdb->pmpro_discount_codes_uses,
array( 'code_id' => $discount_code_id, 'user_id' => $this->user_id, 'order_id' => $this->id ),
array( 'id' => $discount_codes_uses_id ),
array( '%d', '%d', '%d' ),
array( '%d' )
);
} else {
// Insert a new row
$wpdb->insert(
$wpdb->pmpro_discount_codes_uses,
array( 'code_id' => $discount_code_id, 'user_id' => $this->user_id, 'order_id' => $this->id ),
array( '%d', '%d', '%d' )
);
}
}
// Make sure to reset properties on this object
return $this->getDiscountCode( true );
}
/**
* Get a user object for the user associated with this order.
*/
function getUser()
{
global $wpdb;
if(!empty($this->user))
return $this->user;
$this->user = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->users WHERE ID = %d LIMIT 1", $this->user_id ) );
// Fix the timestamp for local time
if ( ! empty( $this->user ) && ! empty( $this->user->user_registered ) ) {
$this->user->user_registered = strtotime( get_date_from_gmt( $this->user->user_registered, 'Y-m-d H:i:s' ) );
}
return $this->user;
}
/**
* Get a membership level object for the level associated with this order.
*
* @param bool $force If true, it will query the database again.
*
*/
function getMembershipLevel($force = false)
{
global $wpdb;
if(!empty($this->membership_level) && empty($force))
return $this->membership_level;
//check if there is an entry in memberships_users first
if(!empty($this->user_id))
{
$sqlQuery = $wpdb->prepare( "SELECT l.id as level_id, l.name, l.description, l.allow_signups, l.expiration_number, l.expiration_period, mu.*, UNIX_TIMESTAMP(CONVERT_TZ(mu.startdate, '+00:00', @@global.time_zone)) as startdate, UNIX_TIMESTAMP(CONVERT_TZ(mu.enddate, '+00:00', @@global.time_zone)) as enddate, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_membership_levels l LEFT JOIN $wpdb->pmpro_memberships_users mu ON l.id = mu.membership_id WHERE mu.status = 'active' AND l.id = %d AND mu.user_id = %d LIMIT 1", $this->membership_id, $this->user_id );
$this->membership_level = $wpdb->get_row( $sqlQuery );
//fix the membership level id
if(!empty($this->membership_level->level_id))
$this->membership_level->id = $this->membership_level->level_id;
}
//okay, do I have a discount code to check? (if there is no membership_level->membership_id value, that means there was no entry in memberships_users)
if(!empty($this->discount_code) && empty($this->membership_level->membership_id))
{
if(!empty($this->discount_code->code))
$discount_code = $this->discount_code->code;
else
$discount_code = $this->discount_code;
$sqlQuery = $wpdb->prepare( "SELECT l.id, cl.*, l.name, l.description, l.allow_signups FROM $wpdb->pmpro_discount_codes_levels cl LEFT JOIN $wpdb->pmpro_membership_levels l ON cl.level_id = l.id LEFT JOIN $wpdb->pmpro_discount_codes dc ON dc.id = cl.code_id WHERE dc.code = %s AND cl.level_id = %d LIMIT 1", $discount_code, $this->membership_id );
$this->membership_level = $wpdb->get_row($sqlQuery);
}
//just get the info from the membership table (sigh, I really need to standardize the column names for membership_id/level_id) but we're checking if we got the information already or not
if(empty($this->membership_level->membership_id) && empty($this->membership_level->level_id))
{
$this->membership_level = $wpdb->get_row( $wpdb->prepare( "SELECT l.* FROM $wpdb->pmpro_membership_levels l WHERE l.id = %d LIMIT 1", $this->membership_id ) );
}
// Round prices to avoid extra decimals.
if( ! empty( $this->membership_level ) ) {
$this->membership_level->initial_payment = pmpro_round_price( $this->membership_level->initial_payment );
$this->membership_level->billing_amount = pmpro_round_price( $this->membership_level->billing_amount );
$this->membership_level->trial_amount = pmpro_round_price( $this->membership_level->trial_amount );
}
return $this->membership_level;
}
/**
* Get a membership level object at checkout
* for the level associated with this order.
*
* @since 2.0.2
* @param bool $force If true, it will reset the property.
*
*/
function getMembershipLevelAtCheckout($force = false) {
if ( empty( $this->membership_level ) || $force ) {
$this->membership_level = pmpro_getLevelAtCheckout( empty( $this->membership_id ) ? null : $this->membership_id );
}
// Fix the membership level id.
if(!empty( $this->membership_level) && !empty($this->membership_level->level_id)) {
$this->membership_level->id = $this->membership_level->level_id;
}
// Round prices to avoid extra decimals.
if( ! empty( $this->membership_level ) ) {
$this->membership_level->initial_payment = pmpro_round_price( $this->membership_level->initial_payment );
$this->membership_level->billing_amount = pmpro_round_price( $this->membership_level->billing_amount );
$this->membership_level->trial_amount = pmpro_round_price( $this->membership_level->trial_amount );
}
return $this->membership_level;
}
/**
* Apply tax rules for the price given.
*/
function getTaxForPrice($price)
{
//get options
$tax_state = get_option("pmpro_tax_state");
$tax_rate = get_option("pmpro_tax_rate");
//default
$tax = 0.00;
//calculate tax
if($tax_state && $tax_rate)
{
//we have values, is this order in the tax state?
if(!empty($this->billing) && trim(strtoupper($this->billing->state)) == trim(strtoupper($tax_state)))
{
//return value, pass through filter
$tax = round((float)$price * (float)$tax_rate, 2);
}
}
//set values array for filter
$values = array("price" => $price, "tax_state" => $tax_state, "tax_rate" => $tax_rate);
if(!empty($this->billing->street))
$values['billing_street'] = $this->billing->street;
if(!empty($this->billing->street2))
$values['billing_street2'] = $this->billing->street2;
if(!empty($this->billing->state))
$values['billing_state'] = $this->billing->state;
if(!empty($this->billing->city))
$values['billing_city'] = $this->billing->city;
if(!empty($this->billing->zip))
$values['billing_zip'] = $this->billing->zip;
if(!empty($this->billing->country))
$values['billing_country'] = $this->billing->country;
//filter
$tax = (float)apply_filters("pmpro_tax", $tax, $values, $this);
return $tax;
}
/**
* Get the tax amount for this order.
*/
function getTax($force = false)
{
if(!empty($this->tax) && !$force)
return $this->tax;
//reset
$this->tax = $this->getTaxForPrice($this->subtotal);
return $this->tax;
}
/**
* Get the timestamp for this order.
*
* @param bool $gmt whether to return GMT time or local timestamp.
* @return int timestamp.
*/
function getTimestamp( $gmt = false ) {
return $gmt ? $this->timestamp : strtotime( get_date_from_gmt( date( 'Y-m-d H:i:s', $this->timestamp ) ) );
}
/**
* Change the timestamp of an order by passing in year, month, day, time.
*
* $time should be adjusted for local timezone.
*
* NOTE: This function should no longer be used. Instead, set the timestamp
* for the order directly and call the MemberOrder->saveOrder() function.
* This function is no longer used on the /adminpages/orders.php page.
*/
function updateTimestamp($year, $month, $day, $time = NULL)
{
if(empty($this->id))
return false; //need a saved order
if ( empty( $time ) ) {
// Just save the order date.
$date = $year . '-' . $month . '-' . $day . ' 00:00:00';
} else {
$date = get_gmt_from_date( $year . '-' . $month . '-' . $day . ' ' . $time, 'Y-m-d H:i:s' );
}
global $wpdb;
$this->sqlQuery = $wpdb->prepare( "UPDATE $wpdb->pmpro_membership_orders SET timestamp = %s WHERE id = %d LIMIT 1", $date, $this->id );
do_action('pmpro_update_order', $this);
if($wpdb->query($this->sqlQuery) !== "false") {
$this->timestamp = strtotime( $date );
do_action('pmpro_updated_order', $this);
return $this->getMemberOrderByID($this->id);
} else {
return false;
}
}
/**
* Save/update the values of the order in the database.
*/
function saveOrder()
{
global $wpdb;
// Perform calculations before each order save.
//get a random code to use for the public ID
if(empty($this->code))
$this->code = $this->getRandomCode();
// Calculate datetime/timestamp. Datetime is what actually gets saved.
if( empty( $this->datetime ) && empty( $this->timestamp ) ) {
$this->timestamp = time();
$this->datetime = date("Y-m-d H:i:s", $this->timestamp);
} elseif( empty( $this->datetime ) && ! empty( $this->timestamp ) && is_numeric( $this->timestamp ) ) {
$this->datetime = date("Y-m-d H:i:s", $this->timestamp); //get datetime from timestamp
} elseif( empty( $this->datetime ) && ! empty( $this->timestamp ) ) {
$this->datetime = $this->timestamp; //must have a datetime in it
$this->timestamp = strtotime( $this->datetime ); //fixing the timestamp
}
// Calculate checkout_id if necessary.
if(empty($this->checkout_id) || intval($this->checkout_id)<1) {
$highestval = $wpdb->get_var("SELECT MAX(checkout_id) FROM $wpdb->pmpro_membership_orders");
$this->checkout_id = intval($highestval)+1;
}
// Deprecating cancelled status with subscriptions table update. Change to success.
if ( $this->status == 'cancelled' ) {
$this->status = 'success';
}
//build query
if(!empty($this->id))
{
//set up actions
$before_action = "pmpro_update_order";
$after_action = "pmpro_updated_order";
//update
$this->sqlQuery = "UPDATE $wpdb->pmpro_membership_orders
SET `code` = '" . esc_sql( $this->code ) . "',
`session_id` = '" . esc_sql( $this->session_id ) . "',
`user_id` = " . intval($this->user_id) . ",
`membership_id` = " . intval($this->membership_id) . ",
`paypal_token` = '" . esc_sql( $this->paypal_token ) . "',
`billing_name` = '" . esc_sql($this->billing->name) . "',
`billing_street` = '" . esc_sql($this->billing->street) . "',
`billing_street2` = '" . esc_sql($this->billing->street2) . "',
`billing_city` = '" . esc_sql($this->billing->city) . "',
`billing_state` = '" . esc_sql($this->billing->state) . "',
`billing_zip` = '" . esc_sql($this->billing->zip) . "',
`billing_country` = '" . esc_sql($this->billing->country) . "',
`billing_phone` = '" . esc_sql($this->billing->phone) . "',
`subtotal` = '" . esc_sql( $this->subtotal ) . "',
`tax` = '" . esc_sql( $this->tax ) . "',
`total` = '" . esc_sql( $this->total ) . "',
`payment_type` = '" . esc_sql( $this->payment_type ) . "',
`cardtype` = '" . esc_sql( $this->cardtype ) . "',
`accountnumber` = '" . esc_sql( $this->accountnumber ) . "',
`expirationmonth` = '" . esc_sql( $this->expirationmonth ) . "',
`expirationyear` = '" . esc_sql( $this->expirationyear ) . "',
`status` = '" . esc_sql($this->status) . "',
`gateway` = '" . esc_sql( $this->gateway ) . "',
`gateway_environment` = '" . esc_sql( $this->gateway_environment ) . "',
`payment_transaction_id` = '" . esc_sql($this->payment_transaction_id) . "',
`subscription_transaction_id` = '" . esc_sql($this->subscription_transaction_id) . "',
`timestamp` = '" . esc_sql($this->datetime) . "',
`affiliate_id` = '" . esc_sql($this->affiliate_id) . "',
`affiliate_subid` = '" . esc_sql($this->affiliate_subid) . "',
`notes` = '" . esc_sql($this->notes) . "',
`checkout_id` = " . intval($this->checkout_id) . "
WHERE id = '" . esc_sql( $this->id ) . "'
LIMIT 1";
}
else
{
//set up actions
$before_action = "pmpro_add_order";
$after_action = "pmpro_added_order";
//only on inserts, we might want to set the expirationmonth and expirationyear from ExpirationDate/
// This will be removed in a future version.
if( (empty($this->expirationmonth) || empty($this->expirationyear)) && !empty($this->ExpirationDate)) {
_doing_it_wrong( 'MemberOrder::saveOrder', 'ExpirationDate is deprecated. Use expirationmonth and expirationyear.', '3.2' );
$this->expirationmonth = substr($this->ExpirationDate, 0, 2);
$this->expirationyear = substr($this->ExpirationDate, 2, 4);
}
//insert
$this->sqlQuery = "INSERT INTO $wpdb->pmpro_membership_orders
(`code`, `session_id`, `user_id`, `membership_id`, `paypal_token`, `billing_name`, `billing_street`, `billing_street2`, `billing_city`, `billing_state`, `billing_zip`, `billing_country`, `billing_phone`, `subtotal`, `tax`, `total`, `payment_type`, `cardtype`, `accountnumber`, `expirationmonth`, `expirationyear`, `status`, `gateway`, `gateway_environment`, `payment_transaction_id`, `subscription_transaction_id`, `timestamp`, `affiliate_id`, `affiliate_subid`, `notes`, `checkout_id`)
VALUES('" . esc_sql( $this->code ) . "',
'" . esc_sql( session_id() ) . "',
" . intval($this->user_id) . ",
" . intval($this->membership_id) . ",
'" . esc_sql( $this->paypal_token ) . "',
'" . esc_sql(trim($this->billing->name)) . "',
'" . esc_sql(trim($this->billing->street)) . "',
'" . esc_sql(trim($this->billing->street2)) . "',
'" . esc_sql($this->billing->city) . "',
'" . esc_sql($this->billing->state) . "',
'" . esc_sql($this->billing->zip) . "',
'" . esc_sql($this->billing->country) . "',
'" . esc_sql( cleanPhone($this->billing->phone) ) . "',
'" . esc_sql( $this->subtotal ) . "',
'" . esc_sql( $this->tax ) . "',
'" . esc_sql( $this->total ) . "',
'" . esc_sql( $this->payment_type ) . "',
'" . esc_sql( $this->cardtype ) . "',
'" . esc_sql( hideCardNumber($this->accountnumber, false) ) . "',
'" . esc_sql( $this->expirationmonth ) . "',
'" . esc_sql( $this->expirationyear ) . "',
'" . esc_sql($this->status) . "',
'" . esc_sql( $this->gateway ) . "',
'" . esc_sql( $this->gateway_environment ) . "',
'" . esc_sql($this->payment_transaction_id) . "',
'" . esc_sql($this->subscription_transaction_id) . "',
'" . esc_sql($this->datetime) . "',
'" . esc_sql($this->affiliate_id) . "',
'" . esc_sql($this->affiliate_subid) . "',
'" . esc_sql($this->notes) . "',
" . intval($this->checkout_id) . "
)";
}
do_action($before_action, $this);
if($wpdb->query($this->sqlQuery) !== false)
{
if(empty($this->id))
$this->id = $wpdb->insert_id;
/**
* Special case. We want to add `discount_code_id` as a property/db column in the future.
* For now, if we are saving an order with a non-null discount_code_id property,
* call updateDiscountCode to save the discount code use in the current pmpro_discount_codes_uses table.
*
* @DISCOUNT_CODE_ID_TODO
*/
if ( null !== $this->discount_code_id ) {
$this->updateDiscountCode( $this->discount_code_id );
}
do_action($after_action, $this);
// Create a subscription if we need to.
$subscription = $this->get_subscription();
// Check if the subscription has reached its billing limit.
if ( ! empty( $subscription ) && 'active' === $subscription->get_status() && $subscription->billing_limit_reached() ) {
// Cancel the subscription.
$subscription->cancel_at_gateway();
}
//Lets only run this once the update has been run successfully.
if ( $this->status !== $this->original_status ) {
/**
* Runs when the order status changes
*
* @param $this object The current member order object
* @param $original_status The original status before changing to the new status
*
* @since 2.9
*/
do_action( 'pmpro_order_status_' . $this->status, $this, $this->original_status );
//Set the original status to the new status
$this->original_status = $this->status;
}
return $this->getMemberOrderByID($this->id);
}
else
{
return false;
}
}
/**
* Get a random code to use as the order code.
*/
function getRandomCode() {
global $wpdb, $current_user;
// We mix this with the seed to make sure we get unique codes.
static $count = 0;
$count++;
if( defined( 'AUTH_KEY' ) && defined( 'SECURE_AUTH_KEY' ) ) {
$auth_code = AUTH_KEY;
$secure_auth_code = SECURE_AUTH_KEY;
} else {
//Generate our own random string and hash it
$auth_code = md5( rand() );
$secure_auth_code = md5( rand() );
}
while( empty( $code ) ) {
$current_user_id = ! empty( $current_user->ID ) ? $current_user->ID : 0;
$scramble = md5( $auth_code . microtime() . $secure_auth_code . $current_user_id . $count );
$code = substr( $scramble, 0, 10 );
$code = apply_filters( 'pmpro_random_code', $code, $this ); //filter
$check = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM $wpdb->pmpro_membership_orders WHERE code = %s LIMIT 1", $code ) );
if( $check || is_numeric( $code ) ) {
$code = NULL;
}
}
return strtoupper( $code );
}
/**
* Update the status of the order in the database.
*/
function updateStatus( $newstatus ) {
global $wpdb;
if( empty( $this->id ) ) {
return false;
}
if ( 'cancelled' === $newstatus ) {
// Only cancel subscription, not order.
return $this->cancel();
}
$this->status = $newstatus;
$this->sqlQuery = $wpdb->prepare( "UPDATE $wpdb->pmpro_membership_orders SET status = %s WHERE id = %d LIMIT 1", $newstatus, $this->id );
do_action( 'pmpro_update_order', $this );
if ( $wpdb->query( $this->sqlQuery ) !== false ) {
do_action('pmpro_updated_order', $this);
return true;
} else {
return false;
}
}
/**
* Call the process step of the gateway class.
*/
function process()
{
if (is_object($this->Gateway)) {
return $this->Gateway->process($this);
}
}
/**
* For offsite gateways with a confirm step.
*
* @since 1.8
* @deprecated 3.2
*/
function confirm()
{
_deprecated_function( __FUNCTION__, '3.2' );
if (is_object($this->Gateway)) {
return $this->Gateway->confirm($this);
}
}
/**
* Call the cancel step of the order's subscription if needed.
*/
function cancel() {
// Only need to cancel on the gateway if there is a subscription.
$subscription = $this->get_subscription();
if ( empty( $subscription ) ) {
return true;
}
// Cancel the subscription.
return $subscription->cancel_at_gateway();
}
/**
* Call the update method of the gateway class.
*/
function updateBilling()
{
if (is_object($this->Gateway)) {
return $this->Gateway->update( $this );
}
}
/**
* Call the getSubscriptionStatus method of the gateway class.
*
* @deprecated 3.2
*/
function getGatewaySubscriptionStatus()
{
_deprecated_function( __FUNCTION__, '3.2' );
if (is_object($this->Gateway)) {
return $this->Gateway->getSubscriptionStatus( $this );
}
}
/**
* Call the getTransactionStatus method of the gateway class.
*
* @deprecated 3.2
*/
function getGatewayTransactionStatus()
{
_deprecated_function( __FUNCTION__, '3.2' );
if (is_object($this->Gateway)) {
return $this->Gateway->getTransactionStatus( $this );
}
}
/**
* Get TOS consent information.
* @since 1.9.5
* @deprecated 3.2 - Use pmpro_get_consent_log_entry_for_order() insetad.
*/
function get_tos_consent_log_entry() {
_deprecated_function( __METHOD__, '3.2', 'pmpro_get_consent_log_entry_for_order()' );
if ( empty( $this->id ) ) {
return false;
}
$consent_log = pmpro_get_consent_log( $this->user_id );
foreach( $consent_log as $entry ) {
if( $entry['order_id'] == $this->id ) {
return $entry;
}
}
return false;
}
/**
* Sets the billing address fields on the order object.
* Checks the last order for the same sub or pulls from user meta.
* @since 2.5.5
*/
function find_billing_address() {
if ( empty( $this->billing ) || empty( $this->billing->street ) ) {
// We do not already have a billing address.
$last_subscription_order = new MemberOrder();
$last_subscription_order->getLastMemberOrderBySubscriptionTransactionID( $this->subscription_transaction_id );
if ( ! empty( $last_subscription_order->billing ) && ! empty( $last_subscription_order->billing->street ) ) {
// Last order in subscription has biling information. Pull data from there.
$this->billing = new stdClass();
$this->billing->name = $last_subscription_order->billing->name;
$this->billing->street = $last_subscription_order->billing->street;
$this->billing->street2 = $last_subscription_order->billing->street2;
$this->billing->city = $last_subscription_order->billing->city;
$this->billing->state = $last_subscription_order->billing->state;
$this->billing->zip = $last_subscription_order->billing->zip;
$this->billing->country = $last_subscription_order->billing->country;
$this->billing->phone = $last_subscription_order->billing->phone;
}
}
}
/**
* Delete an order and associated data.
*/
function deleteMe()
{
if(empty($this->id))
return false;
global $wpdb;
$this->sqlQuery = $wpdb->prepare( "DELETE FROM $wpdb->pmpro_membership_orders WHERE id = %d LIMIT 1", $this->id );
if($wpdb->query($this->sqlQuery) !== false)
{
do_action("pmpro_delete_order", $this->id, $this);
return true;
}
else
return false;
}
/*
* Generates a test order on the fly for orders.
*/
function get_test_order() {
global $current_user;
//$test_order = $this->getEmptyMemberOrder();
$all_levels = pmpro_getAllLevels();
if ( ! empty( $all_levels ) ) {
$first_level = array_shift( $all_levels );
$this->membership_id = $first_level->id;
$this->subtotal = $first_level->initial_payment;
$this->total = $first_level->initial_payment;
} else {
$this->membership_id = 1;
$this->subtotal = 1;
$this->total = 1;
}
$this->user_id = $current_user->ID;
$this->cardtype = "Visa";
$this->accountnumber = "4111111111111111";
$this->expirationmonth = date( 'm', current_time( 'timestamp' ) );
$this->expirationyear = ( intval( date( 'Y', current_time( 'timestamp' ) ) ) + 1 );
$this->billing = new stdClass();
$this->billing->name = 'Jane Doe';
$this->billing->street = '123 Street';
$this->billing->street2 = 'Apt 1';
$this->billing->city = 'City';
$this->billing->state = 'ST';
$this->billing->country = 'US';
$this->billing->zip = '12345';
$this->billing->phone = '5558675309';
$this->gateway_environment = 'sandbox';
$this->timestamp = time();
$this->notes = __( 'This is a test order created by Paid Memberships Pro.', 'paid-memberships-pro' );
return apply_filters( 'pmpro_test_order_data', $this );
}
/**
* Get the PMPro_Subscription object for this order.
* If the subscription doesn't exist, it will be created.
*
* @return PMPro_Subscription|null The subscription object or null if the order is not a subscription.
*/
public function get_subscription() {
// Make sure that this order is a part of a subscription.
if ( empty( $this->subscription_transaction_id ) || empty( $this->gateway ) || empty( $this->gateway_environment ) ) {
// We don't have all the info needed for a subscription. Bail.
return null;
}
$get_subscription_args = array(
'subscription_transaction_id' => $this->subscription_transaction_id,
'gateway' => $this->gateway,
'gateway_environment' => $this->gateway_environment,
);
$existing_subscription = PMPro_Subscription::get_subscription( $get_subscription_args );
if ( ! empty( $existing_subscription ) ) {
// We already have a subscription for this order, return it.
return $existing_subscription;
}
// Only create a subscription if this order is complete.
if ( 'success' !== $this->status ) {
// The order is not complete, so it isn't a valid subscription payment.
return null;
}
if ( pmpro_is_checkout() ) {
// We are processing a checkout. Get the level from the checkout.
$subscription_level = $this->getMembershipLevelAtCheckout();
} else {
// Not at checkout. Get the level from the database.
$subscription_level = $this->getMembershipLevel();
}
if ( empty( $subscription_level ) ) {
// We couldn't get level information, so we shouldn't create a subscription.
return null;
}
$create_subscription_args = array(
'user_id' => $this->user_id,
'membership_level_id' => $this->membership_id,
'gateway' => $this->gateway,
'gateway_environment' => $this->gateway_environment,
'subscription_transaction_id' => $this->subscription_transaction_id,
'status' => 'active',
'billing_amount' => empty( $subscription_level->billing_amount ) ? 0.00 : $subscription_level->billing_amount,
'cycle_number' => empty( $subscription_level->cycle_number ) ? 0 : $subscription_level->cycle_number,
'cycle_period' => empty( $subscription_level->cycle_period ) ? 'Month' : $subscription_level->cycle_period,
'billing_limit' => empty( $subscription_level->billing_limit ) ? 0 : $subscription_level->billing_limit,
'trial_amount' => empty( $subscription_level->trial_amount ) ? 0.00 : $subscription_level->trial_amount,
'trial_limit' => empty( $subscription_level->trial_limit ) ? 0 : $subscription_level->trial_limit,
'startdate' => date_i18n( 'Y-m-d H:i:s', $this->timestamp ),
'next_payment_date' => pmpro_calculate_profile_start_date( $this, 'Y-m-d H:i:s' ),
);
$new_subscription = PMPro_Subscription::create( $create_subscription_args );
return ! empty( $new_subscription ) ? $new_subscription : null;
}
/**
* Does this order have any billing address fields set?
* @since 2.8
* @return bool True if ANY billing address field is non-empty.
* False if ALL billing address fields are empty.
*/
function has_billing_address() {
// Avoid a warning if no billing object at all.
if ( empty( $this->billing ) ) {
return false;
}
// Check billing fields.
if ( ! empty( $this->billing->name )
|| ! empty( $this->billing->street )
|| ! empty( $this->billing->street2 )
|| ! empty( $this->billing->city )
|| ! empty( $this->billing->state )
|| ! empty( $this->billing->country )
|| ! empty( $this->billing->zip )
|| ! empty( $this->billing->phone ) ) {
return true;
}
return false;
}
/**
* Get the formatted total for this order.
*
* @since 3.1
*
* @return string
*/
public function get_formatted_total() {
$formatted_total = pmpro_formatPrice( $this->total );
/**
* Filter the formatted total for this order.
*
* @since 3.1
*
* @param string $formatted_total The formatted total for this order.
* @param MemberOrder $this The order object.
*/
return apply_filters( 'pmpro_order_formatted_total', $formatted_total, $this );
}
/**
* Get the formatted subtotal for this order.
*
* @since 3.1
*
* @return string
*/
public function get_formatted_subtotal() {
$formatted_subtotal = pmpro_formatPrice( $this->subtotal );
/**
* Filter the formatted subtotal for this order.
*
* @since 3.1
*
* @param string $formatted_subtotal The formatted subtotal for this order.
* @param MemberOrder $this The order object.
*/
return apply_filters( 'pmpro_order_formatted_subtotal', $formatted_subtotal, $this );
}
/**
* Get the formatted tax for this order.
*
* @since 3.1
*
* @return string
*/
public function get_formatted_tax() {
$formatted_tax = pmpro_formatPrice( $this->tax );
/**
* Filter the formatted tax for this order.
*
* @since 3.1
*
* @param string $formatted_tax The formatted tax for this order.
* @param MemberOrder $this The order object.
*/
return apply_filters( 'pmpro_order_formatted_tax', $formatted_tax, $this );
}
/**
* Add a new entry to the order notes.
*
* @since 3.6.1
*
* @param string $note The note to add.
*/
public function add_order_note( $note ) {
// Sanitize the note.
// Use wp_kses_post() if you want default post tags; otherwise keep $allowedposttags.
global $allowedposttags;
$note = wp_kses( $note, $allowedposttags );
// Append the note with site-local timestamp.
$now_local = wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) );
$this->notes .= ( empty( $this->notes ) ? '' : "\n\n" ) . $now_local . ': ' . $note;
// Trim whitespace.
$this->notes = trim( $this->notes );
}
} // End of Class