LLMS_Transaction
LifterLMS order transactions
Source Source
File: includes/models/model.llms.transaction.php
class LLMS_Transaction extends LLMS_Post_Model { /** * DB Post Type. * * @var string */ protected $db_post_type = 'llms_transaction'; /** * Model Name/Type. * * @var string */ protected $model_post_type = 'transaction'; /** * Post model properties. * * @var array */ protected $properties = array( 'api_mode' => 'text', 'amount' => 'float', 'currency' => 'text', 'gateway_completed_date' => 'text', 'gateway_customer_id' => 'text', 'gateway_fee_amount' => 'float', 'gateway_source_id' => 'text', 'gateway_source_description' => 'text', 'gateway_transaction_id' => 'text', 'order_id' => 'absint', 'payment_type' => 'text', 'payment_gateway' => 'text', 'refund_amount' => 'float', 'refund_data' => 'array', ); /** * Determines if the transaction can be refunded. * * Status must not be "failed" and total refunded amount must be less than order amount. * * @since 3.0.0 * @since 7.0.0 Made the return value filterable via the `llms_transaction_can_be_refunded` hook. * * @return boolean */ public function can_be_refunded() { $can_be_refunded = true; if ( in_array( $this->get( 'status' ), array( 'llms-txn-failed', 'llms-txn-pending' ), true ) ) { $can_be_refunded = false; } elseif ( $this->get_refundable_amount( array(), 'float' ) <= 0 ) { $can_be_refunded = false; } /** * Filters whether or not a transaction can be refunded. * * @since 7.0.0 * * @param boolean $can_be_refunded Whether the transaction can be refunded. * @param LLMS_Transaction $transaction The transaction object. */ return apply_filters( 'llms_transaction_can_be_refunded', $can_be_refunded, $this ); } /** * Generates a refund ID based on the refund method. * * When manually processing a refund an ID is generated, if processing via a gateway the data * is passed to the {@see LLMS_Transaction::process_refund_via_gateway()} and ultimately passed * to the gateway for processing (via the gateway's API). For custom methods processing is handled * via the `llms_{$method}_refund_id` filter. * * @since 7.0.0 * * @param string $method Refund processing method ID. * @param float $amount The amount to refund. * @param string $note Refund notes. * @return string|boolean|WP_Error Returns the generated refund ID string or an error object. If a falsy value * is returned the refund processing will fail with a generic error message. */ protected function generate_refund_id( $method, $amount, $note = '' ) { if ( 'manual' === $method ) { /** * Filters the refund id for manual refunds. * * The default refund ID is a microtime string generated by `uniqid()`. * * @since 3.0.0 * * @param string $refund_id The refund ID. */ $refund_id = apply_filters( 'llms_manual_refund_id', (string) uniqid() ); } elseif ( 'gateway' === $method ) { $refund_id = $this->process_refund_via_gateway( $amount, $note ); } else { /** * Filters the refund ID for custom refund methods. * * This filter should return a string representing the refund ID as generated by the custom refund method. * * The dynamic portion of this hook, `{$method}`, represents the ID of the custom refund method. * * @since 3.0.0 * * @param string|boolean|WP_Error $refund_id The generated refund ID or an error object. Returning a falsy value * will result in the default error handling and no refund being recorded. * @param string $method The method ID. * @param LLMS_Transaction $transaction The transaction object. * @param float $amount The refund amount. * @param string $note The user-submitted refund note. */ $refund_id = apply_filters( "llms_{$method}_refund_id", false, $method, $this, $amount, $note ); } return $refund_id; } /** * Retrieves the amount of the transaction that can be refunded. * * @since 3.0.0 * @return float */ public function get_refundable_amount() { $amount = $this->get_price( 'amount', array(), 'float' ); $refunded = $this->get_price( 'refund_amount', array(), 'float' ); return $amount - $refunded; } /** * Retrieves the array of default arguments to pass to {@see LLMS_Transaction::create()} when creating a new post. * * @since 3.0.0 * @since 3.37.6 Add a default date information using `llms_current_time()`. * Remove ordering placeholders from strftime(). * @since 5.9.0 Remove usage of deprecated `strftime()`. * * @param int $order_id LLMS_Order ID of the related order. * @return array */ protected function get_creation_args( $order_id = 0 ) { $date = llms_current_time( 'mysql' ); $title = sprintf( // Translators: %1$d = Order ID; %2$s = Transaction creation date. __( 'Transaction for Order #%1$d – %2$s', 'lifterlms' ), $order_id, date_format( date_create( $date ), 'M d, Y @ h:i A' ) ); // This filter is documented in includes/abstracts/abstract.llms.post.model.php. return apply_filters( "llms_{$this->model_post_type}_get_creation_args", array( 'comment_status' => 'closed', 'ping_status' => 'closed', 'post_author' => 0, 'post_content' => '', 'post_date' => $date, 'post_excerpt' => '', 'post_password' => uniqid( 'order_' ), 'post_status' => 'llms-' . apply_filters( 'llms_default_order_status', 'txn-pending' ), 'post_title' => $title, 'post_type' => $this->get( 'db_post_type' ), ), $this ); } /** * Get the total amount of the transaction after deducting refunds * * @param array $price_args optional array of arguments that can be passed to llms_price() * @param string $format optional format conversion method [html|raw|float] * @return mixed * @since 3.0.0 */ public function get_net_amount( $price_args = array(), $format = 'html' ) { $amount = $this->get_price( 'amount', array(), 'float' ); $refund = $this->get_price( 'refund_amount', array(), 'float' ); return llms_price( $amount - $refund, $price_args, $format ); } /** * Retrieves an instance of LLMS_Order for the transaction's parent order. * * @since 3.0.0 * * @return LLMS_Order */ public function get_order() { return new LLMS_Order( $this->get( 'order_id' ) ); } /** * Retrieves the payment gateway instance for the transactions payment gateway. * * @since 3.0.0 * * @return LLMS_Payment_Gateway|WP_Error */ public function get_gateway() { $gateways = llms()->payment_gateways(); $gateway = $gateways->get_gateway_by_id( $this->get( 'payment_gateway' ) ); if ( $gateway && $gateway->is_enabled() || is_admin() ) { return $gateway; } // Translators: %s = The payment gateway ID. return new WP_Error( 'error', sprintf( __( 'Payment gateway %s could not be located or is no longer enabled', 'lifterlms' ), $this->get( 'payment_gateway' ) ) ); } /** * Retrieves the title of the refund method using during refund processing. * * This method records the method used to process a refund in the refund order note. * * @since 7.0.0 * * @param string $method The refund method ID. * @return string */ protected function get_refund_method_title( $method ) { switch ( $method ) { case 'manual': $method_title = __( 'manual refund', 'lifterlms' ); break; case 'gateway': $method_title = $this->get_gateway()->get_admin_title(); break; default: /** * Filters the refund method title for custom refund methods. * * The dynamic portion of this hook, `{$method}`, represents the ID of the custom refund method. * * @since 3.0.0 * @deprecated 7.0.0 Replaced with `llms_{$method}_refund_title`. * * @param string $method The method ID. */ $method_title = apply_filters_deprecated( "llms_{$method}_title", array( $method ), '7.0.0', "llms_{$method}_refund_title" ); if ( $method_title !== $method ) { return $method_title; } /** * Filters the refund method title for custom refund methods. * * The dynamic portion of this hook, `{$method}`, represents the ID of the custom refund method. * * @since Unknown * * @param string $method The method ID. * @param LLMS_Transaction $transaction The transaction object. */ $method_title = apply_filters( "llms_{$method}_refund_title", $method, $this ); } return $method_title; } /** * Retrieves a single refund by ID. * * @since 7.0.0 * * @param string $id The refund ID. * @return array|boolean { * An array of refund data. Returns `false` if the ID isn't found. * * @type string id The refund ID * @type string date The date the refund was recorded in MySQL date format `Y-m-d H:i:s`. * @type string method The processing method ID. Defaults are "manual" or "gateway". Custom values can be implemented via hooks. * @type float amount The amount of the refund. * } */ public function get_refund( $id ) { $refunds = $this->get_refunds(); return $refunds[ $id ] ?? false; } /** * Retrieves a list of refunds against the transaction. * * @since 7.0.0 * * @return array[] An array of refund arrays as described by {@see LLMS_Transaction::get_refund()}. */ public function get_refunds() { return $this->get_array( 'refund_data' ); } /** * Processes a refund against the transaction. * * This method is called called from the admin panel by clicking a refund (manual or gateway) button. * * @since 3.0.0 * @since 7.0.0 Refactored code into multiple methods. * * @see LLMS_Meta_Box_Order_Transactions::save_refund() * * @param float $amount Amount to refund. * @param string $note Optional note to record as an order note. This is passed to the gateway to do store in the gateway if available. * @param string $method Method used to refund, either "manual" (available for all transactions) or "gateway" (when supported by the gateway that processed the transaction). * @return string|WP_Error A refund ID on success or an error object. */ public function process_refund( $amount, $note = '', $method = 'manual' ) { // Ensure the transaction is still eligible for a refund. if ( ! $this->can_be_refunded() ) { return new WP_Error( 'llms-txn-refund-not-eligible', __( 'The selected transaction is not eligible for a refund.', 'lifterlms' ) ); } $amount = floatval( $amount ); // Ensure we can refund the requested amount. $refundable = $this->get_refundable_amount(); if ( $amount > $refundable ) { return new WP_Error( 'llms-txn-refund-amount-too-high', sprintf( // Translators: %1$s = The requested refund amount; %2$s = the available refundable amount. __( 'Requested refund amount was %1$s, the maximum possible refund for this transaction is %2$s.', 'lifterlms' ), llms_price( $amount ), llms_price( $refundable ) ) ); } $id = $this->generate_refund_id( $method, $amount, $note ); if ( is_string( $id ) ) { $this->record_refund( compact( 'amount', 'id', 'method' ), $note ); } elseif ( ! is_wp_error( $id ) ) { $id = new WP_Error( 'llms-txn-refund-unknown-error', __( 'An unknown error occurred while processing the refund.', 'lifterlms' ) ); } return $id; } /** * Processes a refund via the gateway that processed the transaction. * * @since 7.0.0 * * @param float $amount The refund amount. * @param string $note Refund order note. * @return string|WP_Error The refund ID or an error object. */ protected function process_refund_via_gateway( $amount, $note = '' ) { $gateway = $this->get_gateway(); if ( is_wp_error( $gateway ) ) { return new WP_Error( 'llms-txn-refund-gateway-invalid', sprintf( // Translators: %s = the payment gateway ID. __( 'Selected gateway "%s" is inactive or invalid.', 'lifterlms' ), $this->get( 'payment_gateway' ) ) ); } if ( ! $gateway->supports( 'refunds' ) ) { return new WP_Error( 'llms-txn-refund-gateway-support', sprintf( // Translators: %s = the payment gateway admin title. __( 'Selected gateway "%s" does not support refunds.', 'lifterlms' ), $gateway->get_admin_title() ) ); } return $gateway->process_refund( $this, $amount, $note ); } /** * Records a refund against the transaction. * * This method performs no validations and assumes that the refund has already been verified against * the refund method and current transaction restrictions. * * If the refund data isn't validated, try using {@see LLMS_Transaction::process_refund()} instead. * * @since 7.0.0 * * @param array $refund { * Refund arguments. * * @type float $amount The refund amount. * @type string $id The generated refund ID. * @type string $method The refund processing method ID. * @type string $date The refund date in MySQL date format. If not supplied, the current time is used. * } * @param string $note User-submitted refund note to add to the order alongside the refund. * @return void */ public function record_refund( $refund, $note = '' ) { $refund = wp_parse_args( $refund, array( 'amount' => 0.00, 'id' => '', 'method' => '', 'date' => llms_current_time( 'mysql' ), ) ); // Record the note. $this->record_refund_note( $note, $refund['amount'], $refund['id'], $refund['method'] ); // Update the refunded amount. $refund_amount = $this->get( 'refund_amount' ); $new_amount = ! $refund_amount ? $refund['amount'] : $refund_amount + $refund['amount']; $this->set( 'refund_amount', $new_amount ); // Record refund metadata. $refund_data = $this->get_refunds(); /** * Filters the stored refund data before saving it. * * @since Unknown * * @param array $refund { * An associative array of refund data. * * @type float $amount The refund amount. * @type string $date The refund date in MySQL date format: `Y-m-d H:i:s`. * @type string $id The refund ID. * @type string $method The refund method ID. * } * @param LLMS_Transaction $transaction The transaction object. * @param float $amount The refund amount. * @param string $method The refund method ID */ $refund_data[ $refund['id'] ] = apply_filters( 'llms_transaction_refund_data', $refund, $this, $refund['amount'], $refund['method'] ); $this->set( 'refund_data', $refund_data ); // Update status. $this->set( 'status', 'llms-txn-refunded' ); } /** * Records an order note associated with a refund. * * @since 7.0.0 * * @param string $note User-submitted refund note data to add to the order alongside the refund. * @param float $amount The refund amount. * @param string $refund_id The generated refund ID. * @param string $method The refund processing method ID. * @return int The WP_Comment ID of the recorded order note. */ private function record_refund_note( $note, $amount, $refund_id, $method ) { /** * Filters user-submitted transaction refund order note. * * @since Unknown. * * @param string $note The user-submitted order note text. * @param LLMS_Transaction $transaction The transaction object. * @param float $amount The refund amount. * @param string $method The ID of the refund method. */ $orig_note = apply_filters( 'llms_transaction_refund_note', $note, $this, $amount, $method ); $note = sprintf( // Translators: %1$s = The refund amount; %2$d the transaction ID; %3$s The refund method name; %4$s = the refund ID. __( 'Refunded %1$s for transaction #%2$d via %3$s [Refund ID: %4$s]', 'lifterlms' ), wp_strip_all_tags( llms_price( $amount ) ), $this->get( 'id' ), $this->get_refund_method_title( $method ), $refund_id ); if ( $orig_note ) { $note .= "\r\n"; $note .= __( 'Refund Notes: ', 'lifterlms' ); $note .= "\r\n"; $note .= $orig_note; } // Record the note. return $this->get_order()->add_note( $note, true ); } /** * Translation wrapper for {@see LLMS_Transaction::get()` which enables l10n of database values. * * @since 3.0.0 * * @param string $key Key to retrieve. * @return string */ public function translate( $key ) { $val = $this->get( $key ); switch ( $key ) { case 'payment_type': if ( 'single' === $val ) { $val = __( 'Single', 'lifterlms' ); } elseif ( 'recurring' === $val ) { $val = __( 'Recurring', 'lifterlms' ); } elseif ( 'trial' === $val ) { $val = __( 'Trial', 'lifterlms' ); } break; default: $val = $val; } return $val; } }
Expand full source code Collapse full source code View on GitHub
Properties Properties
The following post and post meta properties are accessible for this class. See LLMS_Post_Model::get() and LLMS_Post_Model::set() for more information.
- $api_mode
-
(string) API Mode of the gateway when the transaction was made [test|live].
- $amount
-
(float) Transaction charge amount.
- $currency
-
(string) Transaction's currency code.
- $gateway_completed_date
-
(string) Datetime string when the transaction was completed by the gateway (if gateway supports).
- $gateway_customer_id
-
(string) Gateway's unique ID for the customer who placed the order.
- $gateway_fee_amount
-
(float) Fee charged to the user by the gateway for the transaction (if gateway supports).
- $gateway_source_id
-
(string) Source Identifier from the gateway -- eg: credit card id or account id.
- $gateway_source_description
-
(string) Short description of the source from the gateway. EG: Visa 1234.
- $gateway_transaction_id
-
(string) Gateway's unique ID for the transaction.
- $order_id
-
(int) ID of the related LLMS_Order.
- $payment_type
-
(string) Type of payment. [recurring|single|trial].
- $payment_gateway
-
(string) LifterLMS Payment Gateway ID (eg "paypal" or "stripe").
- $refund_amount
-
(float) Amount refunded, will always be 0 until a refund is actually recorded.
- $refund_data
-
(array) Array of arrays. Contains refund data for each refund recorded for this transaction.
Methods Methods
- can_be_refunded — Determines if the transaction can be refunded.
- generate_refund_id — Generates a refund ID based on the refund method.
- get_creation_args — Retrieves the array of default arguments to pass to {@see LLMS_Transaction::create()} when creating a new post.
- get_gateway — Retrieves the payment gateway instance for the transactions payment gateway.
- get_net_amount — Get the total amount of the transaction after deducting refunds
- get_order — Retrieves an instance of LLMS_Order for the transaction's parent order.
- get_property_type — Get a property's data type for scrubbing used by $this->scrub() to determine how to scrub the property
- get_refund — Retrieves a single refund by ID.
- get_refund_method_title — Retrieves the title of the refund method using during refund processing.
- get_refundable_amount — Retrieves the amount of the transaction that can be refunded.
- get_refunds — Retrieves a list of refunds against the transaction.
- process_refund — Processes a refund against the transaction.
- process_refund_via_gateway — Processes a refund via the gateway that processed the transaction.
- record_refund — Records a refund against the transaction.
- record_refund_note — Records an order note associated with a refund.
- translate — Translation wrapper for {@see LLMS_Transaction::get()` which enables l10n of database values.
Changelog Changelog
Version | Description |
---|---|
3.0.0 | Introduced. |