db_name = rcp_get_payments_db_name(); $this->meta_db_name = rcp_get_payment_meta_db_name(); $this->db_version = '1.5'; } /** * Add a payment to the database * * @access public * @param array $payment_data Array All of the payment data, such as amount, date, user ID, etc * @since 1.5 * @return int|false ID of the newly created payment, or false on failure. */ public function insert( $payment_data = array() ) { global $wpdb; $defaults = array( 'subscription' => '', 'object_id' => 0, 'object_type' => 'subscription', 'date' => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ), 'amount' => 0.00, // Total amount after fees/credits/discounts are added. 'user_id' => 0, 'customer_id' => 0, 'membership_id' => 0, 'payment_type' => '', 'transaction_type' => 'new', 'subscription_key' => '', 'transaction_id' => '', 'status' => 'complete', 'gateway' => '', 'subtotal' => 0.00, // Base price of the membership level. 'credits' => 0.00, // Proration credits. 'fees' => 0.00, // Fees. 'discount_amount' => 0.00, // Discount amount from discount code. 'discount_code' => '' ); $args = wp_parse_args( $payment_data, $defaults ); if( ! empty( $args['transaction_id'] ) && $this->payment_exists( $args['transaction_id'] ) ) { return false; } // Backwards compatibility: make sure we store the subscription ID as well. if ( empty( $args['object_id'] ) && ! empty( $args['subscription'] ) ) { $membership_level = rcp_get_membership_level_by( 'name', $args['subscription'] ); if ( $membership_level instanceof Membership_Level ) { $args['object_id'] = $membership_level->get_id(); } } // Backwards compatibility: include customer ID. if ( empty( $args['customer_id'] ) && ! empty( $args['user_id'] ) ) { $customer = rcp_get_customer_by_user_id( $args['user_id'] ); if ( ! empty( $customer ) ) { $args['customer_id'] = $customer->get_id(); } } // Backwards compatibility: update pending payment instead of creating a new one. if ( ! empty( $args['user_id'] ) && 'complete' == $args['status'] ) { $last_pending_payment = get_user_meta( $args['user_id'], 'rcp_pending_payment_id', true ); $pending_payment = ! empty( $last_pending_payment ) ? $this->get_payment( $last_pending_payment ) : false; if ( ! empty( $pending_payment ) && $args['amount'] == $pending_payment->amount && $args['subscription'] == $pending_payment->subscription ) { $this->update( $pending_payment->id, $args ); return $pending_payment->id; } } $add = $wpdb->insert( $this->db_name, $args, array( '%s', '%d', '%s', '%s', '%s', '%d', '%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s' ) ); // if insert was successful, return the payment ID if( $add ) { $payment_id = $wpdb->insert_id; // clear the payment caches delete_transient( 'rcp_earnings' ); delete_transient( 'rcp_payments_count' ); // Remove trialing status, if it exists delete_user_meta( $args['user_id'], 'rcp_is_trialing' ); /** * Triggers when the payment's status is changed. This is here to spoof a status * change when a payment is first inserted. * * @see RCP_Payment::update() - Action is also run here when status is changed. * * @param string $new_status New status being set. * @param int $payment_id ID of the payment. * * @since 2.9 */ do_action( 'rcp_update_payment_status', $args['status'], $payment_id ); do_action( 'rcp_update_payment_status_' . $args['status'], $payment_id ); if ( 'complete' == $args['status'] ) { /** * Runs only when a new payment is inserted as "complete". This is to * ensure backwards compatibility from before payments were inserted * as "pending" before payment is taken. * * @deprecated 2.9 - Use rcp_create_payment to run actions whenever a payment is * inserted, regardless of status. * * @see RCP_Payment::update() - Action is also run here when status is updated to complete. * * @param int $payment_id ID of the payment that was just inserted. * @param array $args Array of all payment information. * @param float $amount Amount the payment was for. */ do_action( 'rcp_insert_payment', $payment_id, $args, $args['amount'] ); } /** * Runs when a new payment is successfully inserted. * * @param int $payment_id ID of the payment that was just inserted. * @param array $args Array of all payment information. * * @since 2.9 */ do_action( 'rcp_create_payment', $payment_id, $args ); rcp_log( sprintf( 'New payment inserted. ID: %d; User ID: %d; Amount: %.2f; Subscription: %s; Status: %s', $payment_id, $args['user_id'], $args['amount'], $args['subscription'], $args['status'] ) ); return $payment_id; } else { rcp_log( 'Failed inserting new payment into database.', true ); } return false; } /** * Checks if a payment exists in the DB * * @param string $transaction_id The transaction ID of the payment record. * * @access public * @since 1.5 * @return bool */ public function payment_exists( $transaction_id = '' ) { global $wpdb; $found = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM " . $this->db_name . " WHERE `transaction_id`='%s' LIMIT 1;", $transaction_id ) ); return (bool) $found; } /** * Update a payment in the datbase. * * @param int $payment_id ID of the payment record to update. * @param array $payment_data Array of all payment data to update. * * @access public * @since 1.5 * @return int|false The number of rows updated, or false on error. */ public function update( $payment_id = 0, $payment_data = array() ) { global $wpdb; do_action( 'rcp_update_payment', $payment_id, $payment_data ); rcp_log( sprintf( 'Updating payment #%d with new data: %s', $payment_id, var_export( $payment_data, true ) ) ); if ( array_key_exists( 'status', $payment_data ) ) { delete_transient( md5( 'rcp_payments_count_' . serialize( array( 'user_id' => 0, 'status' => '', 's' => '' ) ) ) ); delete_transient( md5( 'rcp_payments_count_' . serialize( array( 'user_id' => 0, 'status' => 'pending', 's' => '' ) ) ) ); delete_transient( md5( 'rcp_payments_count_' . serialize( array( 'user_id' => 0, 'status' => 'complete', 's' => '' ) ) ) ); delete_transient( md5( 'rcp_payments_count_' . serialize( array( 'user_id' => 0, 'status' => 'refunded', 's' => '' ) ) ) ); } $updated = $wpdb->update( $this->db_name, $payment_data, array( 'id' => $payment_id ) ); if ( $updated && array_key_exists( 'status', $payment_data ) ) { $payment = $this->get_payment( $payment_id ); /** * Triggers when the payment's status is changed. * * @param string $new_status New status being set. * @param int $payment_id ID of the payment. * * @since 2.9 */ do_action( 'rcp_update_payment_status', $payment_data['status'], $payment_id ); do_action( 'rcp_update_payment_status_' . $payment_data['status'], $payment_id ); if ( 'complete' == $payment_data['status'] ) { $amount = ! empty( $payment->amount ) ? $payment->amount : 0.00; /** * Runs only when a payment is updated to "complete". This is to * ensure backwards compatibility from before payments were inserted * as "pending" before payment is taken. * * @deprecated 2.9 - Use rcp_create_payment to run actions whenever a payment is * inserted, regardless of status. * * @see RCP_Payments::insert() - Action is also run here. * * @param int $payment_id ID of the payment that was just updated. * @param array $args Array of payment information that was just updated. * @param float $amount Amount the payment was for. */ do_action( 'rcp_insert_payment', $payment_id, (array) $payment, $amount ); } } return $updated; } /** * Delete a payment from the datbase. * * @param int $payment_id ID of the payment to delete. * * @access public * @since 1.5 * @return void */ public function delete( $payment_id = 0 ) { global $wpdb; do_action( 'rcp_delete_payment', $payment_id ); rcp_log( sprintf( 'Deleted payment #%d.', $payment_id ) ); $wpdb->query( $wpdb->prepare( "DELETE FROM {$this->db_name} WHERE `id` = '%d';", absint( $payment_id ) ) ); } /** * Retrieve a specific payment * * @param int $payment_id ID of the payment to retrieve. * * @access public * @since 1.5 * @return object|null Database object or null if payment not found. */ public function get_payment( $payment_id = 0 ) { global $wpdb; $payment = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$this->db_name} WHERE id = %d", absint( $payment_id ) ) ); if ( is_object( $payment ) ) { if ( empty( $payment->status ) ) { $payment->status = 'complete'; } $payment = $this->backfill_payment_data( $payment ); } return $payment; } /** * Attempt to guess the payment gateway from the "Payment Type" * * @param int|object $_payment_id_or_object Payment ID or database object. * * @access private * @since 2.9 * @return string|false */ private function get_payment_gateway( $_payment_id_or_object ) { if ( is_object( $_payment_id_or_object ) ) { $payment = $_payment_id_or_object; } elseif ( is_numeric( $_payment_id_or_object ) ) { $payment = $this->get_payment( $_payment_id_or_object ); } if ( empty( $payment ) ) { return false; } $type = strtolower( $payment->payment_type ); $gateway = false; // If we already have a gateway in the DB, use that. if ( ! empty( $payment->gateway ) ) { return $payment->gateway; } // If "Payment Type" isn't set, we can't get the gateway. if ( empty( $type ) ) { return $gateway; } switch ( $type ) { case 'web_accept' : case 'paypal express one time' : case 'recurring_payment' : case 'subscr_payment' : case 'recurring_payment_profile_created' : $gateway = 'paypal'; break; case 'credit card' : case 'credit card one time' : if ( false !== strpos( $payment->transaction_id, 'ch_' ) ) { $gateway = 'stripe'; } elseif ( false !== strpos( $payment->transaction_id, 'anet_' ) ) { $gateway = 'authorizenet'; } elseif ( is_numeric( $payment->transaction_id ) ) { $gateway = 'twocheckout'; } break; case 'braintree credit card one time' : case 'braintree credit card initial payment' : case 'braintree credit card' : $gateway = 'braintree'; break; case 'manual' : $gateway = 'manual'; break; } return $gateway; } /** * Retrieve a specific payment by a field * * @param string $field Name of the field to check against. * @param mixed $value Value of the field. * * @access public * @since 1.8.2 * @return object */ public function get_payment_by( $field = 'id', $value = '' ) { global $wpdb; $query = $wpdb->prepare( "SELECT * FROM {$this->db_name} WHERE {$field} = %s", sanitize_text_field( $value ) ); $payment = $wpdb->get_row( $query ); if( is_object( $payment ) && empty( $payment->status ) ) { $payment->status = 'complete'; } return $payment; } /** * Retrieve payments from the database * * @param array $args Query arguments to override the defaults. * * @access public * @since 1.5 * @return array Array of objects. */ public function get_payments( $args = array() ) { global $wpdb; $defaults = array( 'number' => 20, 'offset' => 0, 'subscription' => 0, 'user_id' => 0, 'customer_id' => 0, 'membership_id' => 0, 'date' => array(), 'fields' => false, 'status' => '', 's' => '', 'order' => 'DESC', 'orderby' => 'id', 'object_type' => '', 'object_id' => '', 'transaction_type' => '', 'gateway' => '' ); $args = wp_parse_args( $args, $defaults ); $where = ' WHERE 1=1 '; $values = array(); // payments for a specific membership level if( ! empty( $args['subscription'] ) ) { $where .= " AND `subscription`= %s "; $values[] = $args['subscription']; } // payments for specific users if( ! empty( $args['user_id'] ) ) { if( is_array( $args['user_id'] ) ) { $user_ids = implode( ',', array_map( 'absint', $args['user_id'] ) ); } else { $user_ids = intval( $args['user_id'] ); } $where .= " AND `user_id` IN( {$user_ids} ) "; } // payments for specific customers if( ! empty( $args['customer_id'] ) ) { if( is_array( $args['customer_id'] ) ) { $customer_ids = implode( ',', array_map( 'absint', $args['customer_id'] ) ); } else { $customer_ids = intval( $args['customer_id'] ); } $where .= " AND `customer_id` IN( {$customer_ids} ) "; } // payments for specific memberships if( ! empty( $args['membership_id'] ) ) { if( is_array( $args['membership_id'] ) ) { $membership_ids = implode( ',', array_map( 'absint', $args['membership_id'] ) ); } else { $membership_ids = intval( $args['membership_id'] ); } $where .= " AND `membership_id` IN( {$membership_ids} ) "; } // payments for specific statuses if( ! empty( $args['status'] ) ) { if( is_array( $args['status'] ) ) { $status_count = count( $args['status'] ); $status_placeholder = array_fill( 0, $status_count, '%s' ); $statuses = implode( ', ', $status_placeholder ); $where .= "AND `status` IN ( $statuses )"; $values = $values + $args['status']; } else { $where .= "AND `status` = %s"; $values[] = $args['status']; } } // Setup the date query if( ! empty( $args['date'] ) && is_array( $args['date'] ) ) { if ( ! empty( $args['date']['start'] ) || ! empty( $args['date']['end'] ) ) { if ( ! empty( $args['date']['start'] ) ) { $start = date( 'Y-m-d 00:00:00', strtotime( $args['date']['start'] ) ); $where .= " AND `date` >= %s"; $values[] = $start; } if ( ! empty( $args['date']['end'] ) ) { $end = date( 'Y-m-d 23:59:59', strtotime( $args['date']['end'] ) ); $where .= " AND `date` <= %s"; $values[] = $end; } } else { $day = ! empty( $args['date']['day'] ) ? absint( $args['date']['day'] ) : null; $month = ! empty( $args['date']['month'] ) ? absint( $args['date']['month'] ) : null; $year = ! empty( $args['date']['year'] ) ? absint( $args['date']['year'] ) : null; $date_where = ''; $date_where .= ! is_null( $year ) ? $year . " = YEAR ( date ) " : ''; if ( ! is_null( $month ) ) { $date_where = $month . " = MONTH ( date ) AND " . $date_where; } if ( ! is_null( $day ) ) { $date_where = $day . " = DAY ( date ) AND " . $date_where; } $where .= " AND (" . $date_where . ")"; } } // Fields to return if( $args['fields'] ) { $fields = $args['fields']; } else { $fields = '*'; } // Search if( ! empty( $args['s'] ) ) { // Search by email if( is_email( $args['s'] ) ) { $user = get_user_by( 'email', $args['s'] ); if ( is_a( $user, 'WP_User' ) ) { $where .= " AND `user_id` = %d"; $values[] = $user->ID; } } else { // Search by subscription key if( strlen( $args['s'] ) == 32 ) { $where .= " AND `subscription_key` = %s"; $values[] = $args['s']; } elseif( rcp_get_membership_level_by( 'name', $args['s'] ) ) { // Matching membership level found so search for payments with this level $where .= " AND `subscription` = %s"; $values[] = $args['s']; } else { $where .= " AND `transaction_id` = %s"; $values[] = $args['s']; } } } if ( ! empty( $args['object_type'] ) ) { $where .= " AND `object_type` = %s"; $values[] = $args['object_type']; } if ( ! empty( $args['object_id'] ) ) { $where .= " AND `object_id` = %d AND `object_id` != 0"; $values[] = $args['object_id']; } if ( ! empty( $args['transaction_type'] ) ) { $where .= " AND `transaction_type` = %s"; $values[] = $args['transaction_type']; } if ( ! empty( $args['gateway'] ) ) { $where .= " AND `gateway` = %s"; $values[] = $args['gateway']; } if ( 'DESC' === strtoupper( $args['order'] ) ) { $order = 'DESC'; } else { $order = 'ASC'; } $columns = array( 'id', 'user_id', 'subscription', 'subscription_key', 'transaction_id', 'status', 'date' ); $orderby = array_key_exists( $args['orderby'], $columns ) ? $args['orderby'] : 'id'; $values[] = absint( $args['offset'] ); $values[] = absint( $args['number'] ); $payments = $wpdb->get_results( $wpdb->prepare( "SELECT {$fields} FROM " . $this->db_name . " {$where} ORDER BY {$orderby} {$order} LIMIT %d,%d;", $values ) ); foreach ( $payments as $key => $payment ) { if ( '*' === $fields ) { $payment = $this->backfill_payment_data( $payment ); } $payments[ $key ] = $payment; } return $payments; } /** * Backfills any missing payment data introduced in RCP 2.9+ * and updates the payment record accordingly. * * @access private * @since 2.9 * * @param stdClass $payment The payment object. * * @return stdClass The updated payment object. */ private function backfill_payment_data( $payment ) { $data_to_update = array(); /** Backfill the membership level ID. */ if ( empty( $payment->object_id ) && ! empty( $payment->subscription ) ) { $membership_level = rcp_get_membership_level_by( 'name', $payment->subscription ); if ( $membership_level instanceof Membership_Level ) { $payment->object_id = $membership_level->get_id(); $data_to_update['object_id'] = $membership_level->get_id(); } } /** Backfill the gateway */ if ( empty( $payment->gateway ) && ! empty( $payment->payment_type ) ) { $gateway = $this->get_payment_gateway( $payment ); if ( ! empty( $gateway ) ) { $payment->gateway = $gateway; $data_to_update['gateway'] = $gateway; } } /** Backfill empty subtotal */ if ( ! property_exists( $payment, 'subtotal' ) || '' === $payment->subtotal || null === $payment->subtotal ) { $payment->subtotal = $payment->amount; $data_to_update['subtotal'] = $payment->amount; } /** Backfill empty credits */ if ( ! property_exists( $payment, 'credits' ) || '' === $payment->credits || null === $payment->credits ) { $payment->credits = 0; $data_to_update['credits'] = 0; } /** Backfill empty fees */ if ( ! property_exists( $payment, 'fees' ) || '' === $payment->fees || null === $payment->fees ) { $payment->fees = 0; $data_to_update['fees'] = 0; } /** Backfill empty discount_amount */ if ( ! property_exists( $payment, 'discount_amount' ) || '' === $payment->discount_amount || null === $payment->discount_amount ) { $payment->discount_amount = 0; $data_to_update['discount_amount'] = 0; } /** Backfill customer ID */ if ( ! empty( $payment->user_id ) && empty( $payment->customer_id ) ) { $customer = rcp_get_customer_by_user_id( $payment->user_id ); if ( ! empty( $customer ) ) { $data_to_update['customer_id'] = $customer->get_id(); } } if ( ! empty( $data_to_update ) ) { $this->update( $payment->id, $data_to_update ); } return $payment; } /** * Count the total number of payments in the database * * @param array $args Query arguments to override the defaults. * * @access public * @since 1.5 * @return int */ public function count( $args = array() ) { global $wpdb; $defaults = array( 'user_id' => 0, 'date' => array(), 'status' => '', 's' => '', 'object_id' => '', 'object_type' => '', 'transaction_type' => '', 'gateway' => '' ); $args = wp_parse_args( $args, $defaults ); $where = ' WHERE 1=1 '; $values = array(); // Filter by user ID if( ! empty( $args['user_id'] ) ) { if( is_array( $args['user_id'] ) ) { $user_ids = implode( ',', array_map( 'absint', $args['user_id'] ) ); } else { $user_ids = intval( $args['user_id'] ); } $where .= " AND `user_id` IN( {$user_ids} ) "; } // Setup the date query if( ! empty( $args['date'] ) && is_array( $args['date'] ) ) { if ( ! empty( $args['date']['start'] ) || ! empty( $args['date']['end'] ) ) { if ( ! empty( $args['date']['start'] ) ) { $start = date( 'Y-m-d 00:00:00', strtotime( $args['date']['start'] ) ); $where .= " AND `date` >= %s"; $values[] = $start; } if ( ! empty( $args['date']['end'] ) ) { $end = date( 'Y-m-d 23:59:59', strtotime( $args['date']['end'] ) ); $where .= " AND `date` <= %s"; $values[] = $end; } } else { $day = ! empty( $args['date']['day'] ) ? absint( $args['date']['day'] ) : null; $month = ! empty( $args['date']['month'] ) ? absint( $args['date']['month'] ) : null; $year = ! empty( $args['date']['year'] ) ? absint( $args['date']['year'] ) : null; $date_where = ''; $date_where .= ! is_null( $year ) ? $year . " = YEAR ( date ) " : ''; if ( ! is_null( $month ) ) { $date_where = $month . " = MONTH ( date ) AND " . $date_where; } if ( ! is_null( $day ) ) { $date_where = $day . " = DAY ( date ) AND " . $date_where; } $where .= " AND (" . $date_where . ")"; } } // Filter by status if( ! empty( $args['status'] ) ) { if( is_array( $args['status'] ) ) { $status_count = count( $args['status'] ); $status_placeholder = array_fill( 0, $status_count, '%s' ); $statuses = implode( ', ', $status_placeholder ); $where .= "AND `status` IN ( $statuses )"; $values = $values + $args['status']; } else { $where .= "AND `status` = %s"; $values[] = $args['status']; } } // Search if( ! empty( $args['s'] ) ) { // Search by email if( is_email( $args['s'] ) ) { $user = get_user_by( 'email', $args['s'] ); if ( is_a( $user, 'WP_User' ) ) { $where .= " AND `user_id` = %d"; $values[] = $user->ID; } } else { // Search by subscription key if( strlen( $args['s'] ) == 32 ) { $where .= " AND `subscription_key` = %s"; $values[] = $args['s']; } elseif( rcp_get_membership_level_by( 'name', $args['s'] ) ) { // Matching membership level found so search for payments with this level $where .= " AND `subscription` = %s"; $values[] = $args['s']; } else { $where .= " AND `transaction_id` = %s"; $values[] = $args['s']; } } } // Object type if ( ! empty( $args['object_type'] ) ) { $where .= " AND `object_type` = %s"; $values[] = $args['object_type']; } // Object ID if ( ! empty( $args['object_id'] ) ) { $where .= " AND `object_id` = %d AND `object_id` != 0"; $values[] = $args['object_id']; } // Transaction type (new, renewal, upgrade, downgrade) if ( ! empty( $args['transaction_type'] ) ) { $where .= " AND `transaction_type` = %s"; $values[] = $args['transaction_type']; } // Gateway if ( ! empty( $args['gateway'] ) ) { $where .= " AND `gateway` = %s"; $values[] = $args['gateway']; } $key = md5( 'rcp_payments_count_' . serialize( $args ) ); $count = get_transient( $key ); if( $count === false ) { $query = "SELECT COUNT(ID) FROM " . $this->db_name . "{$where};"; if ( ! empty( $values ) ) { $query = $wpdb->prepare( $query, $values ); } $count = $wpdb->get_var( $query ); set_transient( $key, $count, 10800 ); } return absint( $count ); } /** * Calculate the total earnings of all payments in the database * * @param array $args Query arguments to override the defaults. * * @access public * @since 1.5 * @return float */ public function get_earnings( $args = array() ) { global $wpdb; $defaults = array( 'earnings' => 1, // Just for the cache key 'subscription' => 0, 'user_id' => 0, 'date' => array() ); $args = wp_parse_args( $args, $defaults ); $cache_args = $args; $cache_args['user_id'] = is_array( $args['user_id'] ) ? implode( ',', $args['user_id'] ) : $args['user_id']; $cache_args['date'] = implode( ',', $args['date'] ); $cache_key = md5( implode( ',', $cache_args ) ); $where = ' WHERE 1=1 '; // payments for a specific membership level if( ! empty( $args['subscription'] ) ) { $where .= " AND `subscription` = %s "; $values[] = $args['subscription']; } // payments for specific users if( ! empty( $args['user_id'] ) ) { if( is_array( $args['user_id'] ) ) $user_ids = implode( ',', array_map( 'absint', $args['user_id'] ) ); else $user_ids = intval( $args['user_id'] ); $where .= " AND `user_id` IN( {$user_ids} ) "; } // Setup the date query if( ! empty( $args['date'] ) && is_array( $args['date'] ) ) { if ( ! empty( $args['date']['start'] ) || ! empty( $args['date']['end'] ) ) { if ( ! empty( $args['date']['start'] ) ) { $start = date( 'Y-m-d 00:00:00', strtotime( $args['date']['start'] ) ); $where .= " AND `date` >= %s"; $values[] = $start; } if ( ! empty( $args['date']['end'] ) ) { $end = date( 'Y-m-d 23:59:59', strtotime( $args['date']['end'] ) ); $where .= " AND `date` <= %s"; $values[] = $end; } } else { $day = ! empty( $args['date']['day'] ) ? absint( $args['date']['day'] ) : null; $month = ! empty( $args['date']['month'] ) ? absint( $args['date']['month'] ) : null; $year = ! empty( $args['date']['year'] ) ? absint( $args['date']['year'] ) : null; $date_where = ''; $date_where .= ! is_null( $year ) ? $year . " = YEAR ( date ) " : ''; if ( ! is_null( $month ) ) { $date_where = $month . " = MONTH ( date ) AND " . $date_where; } if ( ! is_null( $day ) ) { $date_where = $day . " = DAY ( date ) AND " . $date_where; } $where .= " AND (" . $date_where . ")"; } } // Exclude refunded payments $where .= " AND ( `status` = 'complete' OR `status` IS NULL OR `status` = '' )"; $earnings = get_transient( $cache_key ); if( empty( $earnings ) ) { $query = "SELECT SUM(amount) FROM " . $this->db_name . " {$where};"; if ( ! empty( $values ) ) { $query = $wpdb->prepare( $query, $values ); } $earnings = $wpdb->get_var( $query ); set_transient( $cache_key, $earnings, 3600 ); } $earnings = empty( $earnings ) ? 0 : $earnings; return round( $earnings, 2 ); } /** * Calculate the total refunds of all payments in the database * * @param array $args Query arguments to override the defaults. * * @access public * @since 2.5 * @return float */ public function get_refunds( $args = array() ) { global $wpdb; $defaults = array( 'refunds' => 2, // Just for the cache key 'subscription' => 0, 'user_id' => 0, 'date' => array() ); $args = wp_parse_args( $args, $defaults ); $cache_args = $args; $cache_args['date'] = implode( ',', $args['date'] ); $cache_key = md5( implode( ',', $cache_args ) ); $where = ''; // refunds for a specific membership level if( ! empty( $args['subscription'] ) ) { $where .= "WHERE `subscription`= '{$args['subscription']}' "; } // refunds for specific users if( ! empty( $args['user_id'] ) ) { if( is_array( $args['user_id'] ) ) $user_ids = implode( ',', $args['user_id'] ); else $user_ids = intval( $args['user_id'] ); if( ! empty( $args['subscription'] ) ) { $where .= "`user_id` IN( {$user_ids} ) "; } else { $where .= "WHERE `user_id` IN( {$user_ids} ) "; } } // Setup the date query if( ! empty( $args['date'] ) && is_array( $args['date'] ) ) { $day = ! empty( $args['date']['day'] ) ? absint( $args['date']['day'] ) : null; $month = ! empty( $args['date']['month'] ) ? absint( $args['date']['month'] ) : null; $year = ! empty( $args['date']['year'] ) ? absint( $args['date']['year'] ) : null; $date_where = ''; $date_where .= ! is_null( $year ) ? $year . " = YEAR ( date ) " : ''; if( ! is_null( $month ) ) { $date_where = $month . " = MONTH ( date ) AND " . $date_where; } if( ! is_null( $day ) ) { $date_where = $day . " = DAY ( date ) AND " . $date_where; } if( ! empty( $args['user_id'] ) || ! empty( $args['subscription'] ) ) { $where .= "AND (" . $date_where . ") "; } else { $where .= "WHERE ( " . $date_where . " ) "; } } // Exclude refunded payments if( false !== strpos( $where, 'WHERE' ) ) { $where .= "AND ( `status` = 'refunded' )"; } else { $where .= "WHERE ( `status` = 'refunded' )"; } $refunds = get_transient( $cache_key ); if( $refunds === false ) { $refunds = $wpdb->get_var( "SELECT SUM(amount) FROM " . $this->db_name . " {$where};" ); set_transient( $cache_key, $refunds, 3600 ); } return round( $refunds, 2 ); } /** * Retrieves the last payment made by a user * * @param int $user_id ID of the user to check. * * @access public * @since 1.5 * @return int|float|false Amount of last payment or false if none is found. */ public function last_payment_of_user( $user_id = 0 ) { global $wpdb; $query = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM " . $this->db_name . " WHERE `user_id`='%d' ORDER BY id DESC LIMIT 1;", $user_id ) ); if( $query ) return $query[0]->amount; return false; } /** * Retrieve payment meta field for a payment. * * @param int $payment_id Payment ID. * @param string $meta_key The meta key to retrieve. * @param bool $single Whether to return a single value. * * @access public * @since 2.6 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true. */ public function get_meta( $payment_id = 0, $meta_key = '', $single = false ) { return get_metadata( 'rcp_payment', $payment_id, $meta_key, $single ); } /** * Add meta data field to a payment. * * @param int $payment_id Payment ID. * @param string $meta_key Metadata name. * @param mixed $meta_value Metadata value. * @param bool $unique Optional, default is false. Whether the same key should not be added. * * @access public * @since 2.6 * @since 3.5.10 Removing default values for $payment_id and $meta_key * * @return bool False for failure. True for success. */ public function add_meta( $payment_id, $meta_key, $meta_value, $unique = false ) { return add_metadata( 'rcp_payment', $payment_id, $meta_key, $meta_value, $unique ); } /** * Update payment meta field based on Payment ID. * * Use the $prev_value parameter to differentiate between meta fields with the * same key and Payment ID. * * If the meta field for the payment does not exist, it will be added. * * @param int $payment_id Payment ID. * @param string $meta_key Metadata key. * @param mixed $meta_value Metadata value. * @param mixed $prev_value Optional. Previous value to check before removing. * * @access public * @since 2.6 * @since 3.5.10 Removing default value from $payment_id and $meta_key * * @return bool False on failure, true if success. */ public function update_meta( $payment_id, $meta_key, $meta_value, $prev_value = '' ) { return update_metadata( 'rcp_payment', $payment_id, $meta_key, $meta_value, $prev_value ); } /** * Remove metadata matching criteria from a payment. * * You can match based on the key, or key and value. Removing based on key and * value, will keep from removing duplicate metadata with the same key. It also * allows removing all metadata matching key, if needed. * * @param int $payment_id Payment ID. * @param string $meta_key Metadata name. * @param mixed $meta_value Optional. Metadata value. * * @access public * @since 2.6 * @return bool False for failure. True for success. */ public function delete_meta( $payment_id = 0, $meta_key = '', $meta_value = '' ) { return delete_metadata( 'rcp_payment', $payment_id, $meta_key, $meta_value ); } }