1126 lines
23 KiB
PHP
1126 lines
23 KiB
PHP
<?php
|
|
/**
|
|
* The Payment model.
|
|
*
|
|
* @package WP_Ultimo
|
|
* @subpackage Models
|
|
* @since 2.0.0
|
|
*/
|
|
|
|
namespace WP_Ultimo\Models;
|
|
|
|
use WP_Ultimo\Models\Base_Model;
|
|
use WP_Ultimo\Database\Payments\Payment_Status;
|
|
use WP_Ultimo\Checkout\Line_Item;
|
|
use WP_Ultimo\Models\Product;
|
|
use WP_Ultimo\Models\Customer;
|
|
use WP_Ultimo\Models\Membership;
|
|
|
|
// Exit if accessed directly
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Payment model class. Implements the Base Model.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
class Payment extends Base_Model {
|
|
|
|
use Traits\Notable;
|
|
|
|
/**
|
|
* ID of the product of this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var int
|
|
*/
|
|
protected $product_id;
|
|
|
|
/**
|
|
* ID of the customer attached to this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var int
|
|
*/
|
|
protected $customer_id;
|
|
|
|
/**
|
|
* Membership ID.
|
|
*
|
|
* @since 2.0.0
|
|
* @var int
|
|
*/
|
|
protected $membership_id;
|
|
|
|
/**
|
|
* Parent payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var int
|
|
*/
|
|
protected $parent_id;
|
|
|
|
/**
|
|
* Currency for this payment. 3-letter currency code.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $currency;
|
|
|
|
/**
|
|
* Value before taxes, discounts, fees and etc.
|
|
*
|
|
* @since 2.0.0
|
|
* @var float
|
|
*/
|
|
protected $subtotal = 0;
|
|
|
|
/**
|
|
* Refund total in this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var float
|
|
*/
|
|
protected $refund_total = 0;
|
|
|
|
/**
|
|
* The total value in discounts.
|
|
*
|
|
* @since 2.0.0
|
|
* @var integer
|
|
*/
|
|
protected $discount_total = 0;
|
|
|
|
/**
|
|
* The amount, in currency, of the tax.
|
|
*
|
|
* @since 2.0.0
|
|
* @var float
|
|
*/
|
|
protected $tax_total = 0;
|
|
|
|
/**
|
|
* Discount code used.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $discount_code;
|
|
|
|
/**
|
|
* Total value of the payment.
|
|
*
|
|
* This takes into account fees, discounts, credits, etc.
|
|
*
|
|
* @since 2.0.0
|
|
* @var float
|
|
*/
|
|
protected $total = 0;
|
|
|
|
/**
|
|
* Status of the status.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $status;
|
|
|
|
/**
|
|
* Gateway used to process this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $gateway;
|
|
|
|
/**
|
|
* ID of the payment on the gateway, if it exists.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $gateway_payment_id;
|
|
|
|
/**
|
|
* Array containing representations of the line items on this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var Line_Item[]
|
|
*/
|
|
protected $line_items;
|
|
|
|
/**
|
|
* Sequential invoice number assigned to this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @var int
|
|
*/
|
|
protected $invoice_number;
|
|
|
|
/**
|
|
* Holds if we need to cancel the membership on refund.
|
|
*
|
|
* @since 2.0.0
|
|
* @var bool
|
|
*/
|
|
protected $cancel_membership_on_refund;
|
|
|
|
/**
|
|
* Query Class to the static query methods.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $query_class = \WP_Ultimo\Database\Payments\Payment_Query::class;
|
|
|
|
/**
|
|
* Adds magic methods to return formatted values automatically.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $name Method name.
|
|
* @param array $args List of arguments.
|
|
* @throws \BadMethodCallException Throws exception when method is not found.
|
|
* @return mixed
|
|
*/
|
|
public function __call($name, $args) {
|
|
|
|
$method_key = str_replace('_formatted', '', $name);
|
|
|
|
if (str_contains($name, '_formatted') && method_exists($this, $method_key)) {
|
|
return wu_format_currency($this->{"$method_key"}(), $this->get_currency());
|
|
}
|
|
|
|
throw new \BadMethodCallException($name);
|
|
}
|
|
|
|
/**
|
|
* Set the validation rules for this particular model.
|
|
*
|
|
* To see how to setup rules, check the documentation of the
|
|
* validation library we are using: https://github.com/rakit/validation
|
|
*
|
|
* @since 2.0.0
|
|
* @link https://github.com/rakit/validation
|
|
* @return array
|
|
*/
|
|
public function validation_rules(): array {
|
|
|
|
$currency = wu_get_setting('currency_symbol', 'USD');
|
|
|
|
$payment_types = new \WP_Ultimo\Database\Payments\Payment_Status();
|
|
|
|
$payment_types = $payment_types->get_allowed_list(true);
|
|
|
|
return [
|
|
'customer_id' => 'required|integer|exists:\WP_Ultimo\Models\Customer,id',
|
|
'membership_id' => 'required|integer|exists:\WP_Ultimo\Models\Membership,id',
|
|
'parent_id' => 'integer|default:',
|
|
'currency' => "default:{$currency}",
|
|
'subtotal' => 'required|numeric',
|
|
'refund_total' => 'numeric',
|
|
'tax_total' => 'numeric',
|
|
'discount_code' => 'alpha_dash',
|
|
'total' => 'required|numeric',
|
|
'status' => "required|in:{$payment_types}",
|
|
'gateway' => 'default:',
|
|
'gateway_payment_id' => 'default:',
|
|
'discount_total' => 'integer',
|
|
'invoice_number' => 'default:',
|
|
'cancel_membership_on_refund' => 'boolean|default:0',
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Gets the customer object associated with this payment.
|
|
*
|
|
* @todo Implement this.
|
|
* @since 2.0.0
|
|
* @return \WP_Ultimo\Models\Customer;
|
|
*/
|
|
public function get_customer() {
|
|
|
|
return wu_get_customer($this->get_customer_id());
|
|
}
|
|
|
|
/**
|
|
* Get the value of customer_id.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_customer_id(): int {
|
|
|
|
return absint($this->customer_id);
|
|
}
|
|
|
|
/**
|
|
* Set the value of customer_id.
|
|
*
|
|
* @since 2.0.0
|
|
* @param int $customer_id The ID of the customer attached to this payment.
|
|
* @return void
|
|
*/
|
|
public function set_customer_id($customer_id): void {
|
|
|
|
$this->customer_id = absint($customer_id);
|
|
}
|
|
|
|
/**
|
|
* Gets the membership object associated with this payment.
|
|
*
|
|
* @todo Implement this.
|
|
* @since 2.0.0
|
|
* @return \WP_Ultimo\Models\Membership|false
|
|
*/
|
|
public function get_membership() {
|
|
|
|
return wu_get_membership($this->get_membership_id());
|
|
}
|
|
|
|
/**
|
|
* Get membership ID.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_membership_id() {
|
|
|
|
return $this->membership_id;
|
|
}
|
|
|
|
/**
|
|
* Set membership ID.
|
|
*
|
|
* @since 2.0.0
|
|
* @param int $membership_id The ID of the membership attached to this payment.
|
|
* @return void
|
|
*/
|
|
public function set_membership_id($membership_id): void {
|
|
|
|
$this->membership_id = $membership_id;
|
|
}
|
|
|
|
/**
|
|
* Get parent payment ID.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_parent_id() {
|
|
|
|
return $this->parent_id;
|
|
}
|
|
|
|
/**
|
|
* Set parent payment ID.
|
|
*
|
|
* @since 2.0.0
|
|
* @param int $parent_id The ID from another payment that this payment is related to.
|
|
* @return void
|
|
*/
|
|
public function set_parent_id($parent_id): void {
|
|
|
|
$this->parent_id = $parent_id;
|
|
}
|
|
|
|
/**
|
|
* Get currency for this payment. 3-letter currency code.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_currency(): string {
|
|
|
|
// return $this->currency; For now, multi-currency is not yet supported.
|
|
return wu_get_setting('currency_symbol', 'USD');
|
|
}
|
|
|
|
/**
|
|
* Set currency for this payment. 3-letter currency code.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $currency The currency of this payment. It's a 3-letter code. E.g. 'USD'.
|
|
* @return void
|
|
*/
|
|
public function set_currency($currency): void {
|
|
|
|
$this->currency = $currency;
|
|
}
|
|
|
|
/**
|
|
* Get value before taxes, discounts, fees and etc.
|
|
*
|
|
* @since 2.0.0
|
|
* @return float
|
|
*/
|
|
public function get_subtotal(): float {
|
|
|
|
return $this->subtotal;
|
|
}
|
|
|
|
/**
|
|
* Set value before taxes, discounts, fees and etc.
|
|
*
|
|
* @since 2.0.0
|
|
* @param float $subtotal Value before taxes, discounts, fees and other changes.
|
|
* @return void
|
|
*/
|
|
public function set_subtotal($subtotal): void {
|
|
|
|
$this->subtotal = $subtotal;
|
|
}
|
|
|
|
/**
|
|
* Get refund total in this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return float
|
|
*/
|
|
public function get_refund_total(): float {
|
|
|
|
return $this->refund_total;
|
|
}
|
|
|
|
/**
|
|
* Set refund total in this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @param float $refund_total Total amount refunded.
|
|
* @return void
|
|
*/
|
|
public function set_refund_total($refund_total): void {
|
|
|
|
$this->refund_total = $refund_total;
|
|
}
|
|
|
|
/**
|
|
* Get the amount, in currency, of the tax.
|
|
*
|
|
* @since 2.0.0
|
|
* @return float
|
|
*/
|
|
public function get_tax_total(): float {
|
|
|
|
return (float) $this->tax_total;
|
|
}
|
|
|
|
/**
|
|
* Set the amount, in currency, of the tax.
|
|
*
|
|
* @since 2.0.0
|
|
* @param float $tax_total The amount, in currency, of the tax.
|
|
* @return void
|
|
*/
|
|
public function set_tax_total($tax_total): void {
|
|
|
|
$this->tax_total = $tax_total;
|
|
}
|
|
|
|
/**
|
|
* Get discount code used.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_discount_code() {
|
|
|
|
return $this->discount_code;
|
|
}
|
|
|
|
/**
|
|
* Set discount code used.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $discount_code Discount code used.
|
|
* @return void
|
|
*/
|
|
public function set_discount_code($discount_code): void {
|
|
|
|
$this->discount_code = $discount_code;
|
|
}
|
|
|
|
/**
|
|
* Get this takes into account fees, discounts, credits, etc.
|
|
*
|
|
* @since 2.0.0
|
|
* @return float
|
|
*/
|
|
public function get_total(): float {
|
|
|
|
return (float) $this->total;
|
|
}
|
|
|
|
/**
|
|
* Set this takes into account fees, discounts, credits, etc.
|
|
*
|
|
* @since 2.0.0
|
|
* @param float $total This takes into account fees, discounts and credits.
|
|
* @return void
|
|
*/
|
|
public function set_total($total): void {
|
|
|
|
$this->total = $total;
|
|
}
|
|
|
|
/**
|
|
* Returns the Label for a given severity level.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_status_label() {
|
|
|
|
$status = new Payment_Status($this->get_status());
|
|
|
|
return $status->get_label();
|
|
}
|
|
|
|
/**
|
|
* Gets the classes for a given class.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_status_class() {
|
|
|
|
$status = new Payment_Status($this->get_status());
|
|
|
|
return $status->get_classes();
|
|
}
|
|
|
|
/**
|
|
* Get status of the status.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_status() {
|
|
|
|
return $this->status;
|
|
}
|
|
|
|
/**
|
|
* Set status of the status.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $status The payment status: Can be 'pending', 'completed', 'refunded', 'partially-refunded', 'partially-paid', 'failed', 'cancelled' or other values added by third-party add-ons.
|
|
* @options \WP_Ultimo\Database\Payments\Payment_Status
|
|
* @return void
|
|
*/
|
|
public function set_status($status): void {
|
|
|
|
$this->status = $status;
|
|
}
|
|
|
|
/**
|
|
* Get gateway used to process this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_gateway() {
|
|
|
|
return $this->gateway;
|
|
}
|
|
|
|
/**
|
|
* Set gateway used to process this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $gateway ID of the gateway being used on this payment.
|
|
* @return void
|
|
*/
|
|
public function set_gateway($gateway): void {
|
|
|
|
$this->gateway = $gateway;
|
|
}
|
|
|
|
/**
|
|
* Returns the payment method used. Usually it is the public name of the gateway.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_payment_method() {
|
|
|
|
$gateway = $this->get_gateway();
|
|
|
|
if ( ! $gateway) {
|
|
return __('None', 'wp-ultimo');
|
|
}
|
|
|
|
$gateway_class = wu_get_gateway($gateway);
|
|
|
|
if ( ! $gateway_class) {
|
|
return __('None', 'wp-ultimo');
|
|
}
|
|
|
|
$title = $gateway_class->get_public_title();
|
|
|
|
return apply_filters("wu_gateway_{$gateway}_as_option_title", $title, $gateway_class);
|
|
}
|
|
|
|
/**
|
|
* Returns the product associated to this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return Product|false
|
|
*/
|
|
public function get_product() {
|
|
|
|
return wu_get_product($this->product_id);
|
|
}
|
|
|
|
/**
|
|
* Checks if this payment has line items.
|
|
*
|
|
* This is used to decide if we need to add the payment as a line-item of itself.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function has_line_items(): bool {
|
|
|
|
return ! empty($this->get_line_items());
|
|
}
|
|
|
|
/**
|
|
* Returns the line items for this payment.
|
|
*
|
|
* Line items are also \WP_Ultimo\Models\Payment objects, with the
|
|
* type 'line-item'.
|
|
*
|
|
* @since 2.0.0
|
|
* @return array
|
|
*/
|
|
public function get_line_items(): array {
|
|
|
|
if ($this->line_items === null) {
|
|
$line_items = (array) $this->get_meta('wu_line_items');
|
|
|
|
$this->line_items = array_filter($line_items);
|
|
}
|
|
|
|
return (array) $this->line_items;
|
|
}
|
|
|
|
/**
|
|
* Set the line items of this payment.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param Line_Item[] $line_items THe line items.
|
|
* @return void
|
|
*/
|
|
public function set_line_items(array $line_items): void {
|
|
|
|
$line_items = array_filter($line_items);
|
|
|
|
$this->meta['wu_line_items'] = $line_items;
|
|
|
|
$this->line_items = $line_items;
|
|
}
|
|
|
|
/**
|
|
* Add a new line item.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param Line_Item $line_item The line item.
|
|
* @return void
|
|
*/
|
|
public function add_line_item($line_item): void {
|
|
|
|
$line_items = $this->get_line_items();
|
|
|
|
if ( ! is_a($line_item, self::class)) {
|
|
return;
|
|
}
|
|
|
|
$line_items[ $line_item->get_id() ] = $line_item;
|
|
|
|
$this->set_line_items($line_items);
|
|
|
|
krsort($this->line_items);
|
|
}
|
|
|
|
/**
|
|
* Returns an array containing the subtotal per tax rate.
|
|
*
|
|
* @since 2.0.0
|
|
* @return array $tax_rate => $tax_total.
|
|
*/
|
|
public function get_tax_breakthrough(): array {
|
|
|
|
$line_items = $this->get_line_items();
|
|
|
|
$tax_brackets = [];
|
|
|
|
foreach ($line_items as $line_item) {
|
|
$tax_bracket = $line_item->get_tax_rate();
|
|
|
|
if ( ! $tax_bracket) {
|
|
continue;
|
|
}
|
|
|
|
if (isset($tax_brackets[ $tax_bracket ])) {
|
|
$tax_brackets[ $tax_bracket ] += $line_item->get_tax_total();
|
|
|
|
continue;
|
|
}
|
|
|
|
$tax_brackets[ $tax_bracket ] = $line_item->get_tax_total();
|
|
}
|
|
|
|
return $tax_brackets;
|
|
}
|
|
|
|
/**
|
|
* Recalculate payment totals.
|
|
*
|
|
* @todo needs refactoring to use line_items.
|
|
*
|
|
* @since 2.0.0
|
|
* @return Payment
|
|
*/
|
|
public function recalculate_totals(): Payment {
|
|
|
|
$line_items = $this->get_line_items();
|
|
|
|
$tax_total = 0;
|
|
|
|
$sub_total = 0;
|
|
|
|
$refund_total = 0;
|
|
|
|
$total = 0;
|
|
|
|
foreach ($line_items as $line_item) {
|
|
$line_item->recalculate_totals();
|
|
|
|
$tax_total += $line_item->get_tax_total();
|
|
|
|
$sub_total += $line_item->get_subtotal();
|
|
|
|
$total += $line_item->get_total();
|
|
|
|
if ($line_item->get_type() === 'refund') {
|
|
$refund_total += $line_item->get_subtotal();
|
|
}
|
|
}
|
|
|
|
$this->attributes(
|
|
[
|
|
'tax_total' => $tax_total,
|
|
'subtotal' => $sub_total,
|
|
'refund_total' => $refund_total,
|
|
'total' => $total,
|
|
]
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Checks if this payment is payable still.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function is_payable(): bool {
|
|
|
|
$payable_statuses = apply_filters(
|
|
'wu_payment_payable_statuses',
|
|
[
|
|
Payment_Status::PENDING,
|
|
Payment_Status::FAILED,
|
|
]
|
|
);
|
|
|
|
return $this->get_total() > 0 && in_array($this->get_status(), $payable_statuses, true);
|
|
}
|
|
|
|
/**
|
|
* Returns the link to pay for this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return false|string Returns false if the payment is not in a payable status.
|
|
*/
|
|
public function get_payment_url() {
|
|
|
|
if ( ! $this->is_payable()) {
|
|
return false;
|
|
}
|
|
|
|
$slug = $this->get_hash();
|
|
|
|
return add_query_arg(
|
|
[
|
|
'payment' => $slug,
|
|
],
|
|
wu_get_registration_url()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get iD of the product of this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_product_id() {
|
|
|
|
return $this->product_id;
|
|
}
|
|
|
|
/**
|
|
* Set iD of the product of this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @param int $product_id The ID of the product of this payment.
|
|
* @return void
|
|
*/
|
|
public function set_product_id($product_id): void {
|
|
|
|
$this->product_id = $product_id;
|
|
}
|
|
|
|
/**
|
|
* Generates the Invoice URL.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_invoice_url() {
|
|
|
|
$url_atts = [
|
|
'action' => 'invoice',
|
|
'reference' => $this->get_hash(),
|
|
'key' => wp_create_nonce('see_invoice'),
|
|
];
|
|
|
|
return add_query_arg($url_atts, get_site_url(wu_get_main_site_id()));
|
|
}
|
|
|
|
/**
|
|
* Get iD of the payment on the gateway, if it exists.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_gateway_payment_id() {
|
|
|
|
return $this->gateway_payment_id;
|
|
}
|
|
|
|
/**
|
|
* Set iD of the payment on the gateway, if it exists.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $gateway_payment_id The ID of the payment on the gateway, if it exists.
|
|
* @return void
|
|
*/
|
|
public function set_gateway_payment_id($gateway_payment_id): void {
|
|
|
|
$this->gateway_payment_id = $gateway_payment_id;
|
|
}
|
|
|
|
/**
|
|
* By default, we just use the to_array method, but you can rewrite this.
|
|
*
|
|
* @since 2.0.0
|
|
* @return array
|
|
*/
|
|
public function to_search_results() {
|
|
|
|
$search_result = $this->to_array();
|
|
|
|
$search_result['reference_code'] = $this->get_hash();
|
|
|
|
$line_items = array_map(fn($line_item) => $line_item->to_array(), $this->get_line_items());
|
|
|
|
$search_result['product_names'] = implode(', ', array_column($line_items, 'title'));
|
|
|
|
return $search_result;
|
|
}
|
|
|
|
/**
|
|
* Get the total value in discounts.
|
|
*
|
|
* @since 2.0.0
|
|
* @return integer
|
|
*/
|
|
public function get_discount_total() {
|
|
|
|
return (float) $this->discount_total;
|
|
}
|
|
|
|
/**
|
|
* Set the total value in discounts.
|
|
*
|
|
* @since 2.0.0
|
|
* @param integer $discount_total The total value of the discounts applied to this payment.
|
|
* @return void
|
|
*/
|
|
public function set_discount_total($discount_total): void {
|
|
|
|
$this->discount_total = (float) $discount_total;
|
|
}
|
|
|
|
/**
|
|
* Get the invoice number actually saved on the payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_saved_invoice_number() {
|
|
|
|
if ($this->invoice_number === null) {
|
|
$this->invoice_number = $this->get_meta('wu_invoice_number', '');
|
|
}
|
|
|
|
return $this->invoice_number;
|
|
}
|
|
|
|
/**
|
|
* Get sequential invoice number assigned to this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @return int
|
|
*/
|
|
public function get_invoice_number() {
|
|
|
|
if (wu_get_setting('invoice_numbering_scheme', 'reference_code') === 'reference_code') {
|
|
return $this->get_hash();
|
|
}
|
|
|
|
$provisional = false;
|
|
|
|
if ($this->invoice_number === null) {
|
|
$this->invoice_number = $this->get_meta('wu_invoice_number');
|
|
}
|
|
|
|
if ($this->invoice_number === false) {
|
|
$provisional = true;
|
|
|
|
$this->invoice_number = wu_get_setting('next_invoice_number');
|
|
}
|
|
|
|
$prefix = wu_get_setting('invoice_prefix', '');
|
|
|
|
$search = [
|
|
'%YEAR%',
|
|
'%MONTH%',
|
|
'%DAY%',
|
|
'%%YEAR%%',
|
|
'%%MONTH%%',
|
|
'%%DAY%%',
|
|
];
|
|
|
|
$replace = [
|
|
gmdate('Y'),
|
|
gmdate('m'),
|
|
gmdate('d'),
|
|
gmdate('Y'),
|
|
gmdate('m'),
|
|
gmdate('d'),
|
|
];
|
|
|
|
$prefix = str_replace($search, $replace, (string) $prefix);
|
|
|
|
return sprintf('%s%s %s', $prefix, $this->invoice_number, $provisional ? __('(provisional)', 'wp-ultimo') : '');
|
|
}
|
|
|
|
/**
|
|
* Set sequential invoice number assigned to this payment.
|
|
*
|
|
* @since 2.0.0
|
|
* @param int $invoice_number Sequential invoice number assigned to this payment.
|
|
* @return void
|
|
*/
|
|
public function set_invoice_number($invoice_number): void {
|
|
|
|
$this->meta['wu_invoice_number'] = $invoice_number;
|
|
|
|
$this->invoice_number = $invoice_number;
|
|
}
|
|
|
|
/**
|
|
* Remove all non-recurring items from the payment.
|
|
*
|
|
* This is usually used when creating a new pending payment for
|
|
* a membership that needs to be manually renewed.
|
|
*
|
|
* @since 2.0.0
|
|
* @return self
|
|
*/
|
|
public function remove_non_recurring_items() {
|
|
|
|
$line_items = $this->get_line_items();
|
|
|
|
foreach ($line_items as $line_item_id => $line_item) {
|
|
if ( ! $line_item->is_recurring()) {
|
|
unset($line_items[ $line_item_id ]);
|
|
}
|
|
}
|
|
|
|
$this->set_line_items($line_items);
|
|
|
|
$this->recalculate_totals();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get holds if we need to cancel the membership on refund..
|
|
*
|
|
* @since 2.0.0
|
|
* @return bool
|
|
*/
|
|
public function should_cancel_membership_on_refund() {
|
|
|
|
if ($this->cancel_membership_on_refund === null) {
|
|
$this->cancel_membership_on_refund = $this->get_meta('wu_cancel_membership_on_refund', false);
|
|
}
|
|
|
|
return $this->cancel_membership_on_refund;
|
|
}
|
|
|
|
/**
|
|
* Set holds if we need to cancel the membership on refund..
|
|
*
|
|
* @since 2.0.0
|
|
* @param bool $cancel_membership_on_refund Holds if we need to cancel the membership on refund.
|
|
* @return void
|
|
*/
|
|
public function set_cancel_membership_on_refund($cancel_membership_on_refund): void {
|
|
|
|
$this->meta['wu_cancel_membership_on_refund'] = $cancel_membership_on_refund;
|
|
|
|
$this->cancel_membership_on_refund = $cancel_membership_on_refund;
|
|
}
|
|
|
|
/**
|
|
* Handles a payment refund.
|
|
*
|
|
* This DOES NOT contact the gateway to refund a payment.
|
|
* It only updates the payment status to respond to a refund
|
|
* confirmation that originated from the gateway.
|
|
*
|
|
* An example of how that would work:
|
|
* 1. Admin issues a refund on the admin panel;
|
|
* 2. PayPal (for example), process the refund request
|
|
* and sends back a IPN (webhook call) telling WP Multisite WaaS
|
|
* that the refund was issued successfully;
|
|
* 3. The IPN handler listens for that event and calls this
|
|
* to reflect the refund in the original WU payment.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param boolean $amount The amount to refund.
|
|
* @param null|boolean $should_cancel_membership_on_refund If we should cancel a membership as well.
|
|
* @return void|bool
|
|
*/
|
|
public function refund($amount = false, $should_cancel_membership_on_refund = null) {
|
|
/*
|
|
* If no amount was passed,
|
|
* refund the full amount.
|
|
*/
|
|
if (empty($amount)) {
|
|
$amount = $this->get_total();
|
|
}
|
|
|
|
$amount = wu_to_float($amount);
|
|
|
|
/*
|
|
* Do the same for the behavior regarding memberships.
|
|
*/
|
|
if (is_null($should_cancel_membership_on_refund)) {
|
|
$should_cancel_membership_on_refund = $this->should_cancel_membership_on_refund();
|
|
}
|
|
|
|
/*
|
|
* First, deal with the status.
|
|
* The new status depends on the refund amount.
|
|
*
|
|
* If the amount is >= the total
|
|
* this is a total refund, otherwise,
|
|
* it is a partial refund.
|
|
*/
|
|
if ($amount >= $this->get_total()) {
|
|
$title = __('Full Refund', 'wp-ultimo');
|
|
|
|
$new_status = Payment_Status::REFUND;
|
|
} else {
|
|
$title = __('Partial Refund', 'wp-ultimo');
|
|
|
|
$new_status = Payment_Status::PARTIAL_REFUND;
|
|
}
|
|
|
|
$time = current_time('timestamp'); // phpcs:ignore
|
|
|
|
$formatted_value = date_i18n(get_option('date_format'), $time);
|
|
|
|
// translators: %s is the date of processing.
|
|
$description = sprintf(__('Processed on %s', 'wp-ultimo'), $formatted_value);
|
|
|
|
$line_item_data = [
|
|
'type' => 'refund',
|
|
'hash' => uniqid(),
|
|
'title' => $title,
|
|
'description' => $description,
|
|
'discountable' => false,
|
|
'taxable' => false,
|
|
'unit_price' => -$amount,
|
|
'quantity' => 1,
|
|
];
|
|
|
|
$refund_line_item = new Line_Item($line_item_data);
|
|
|
|
$this->add_line_item($refund_line_item);
|
|
|
|
$this->set_status($new_status);
|
|
|
|
$this->recalculate_totals();
|
|
|
|
$status = $this->save();
|
|
|
|
if (is_wp_error($status)) {
|
|
return $status;
|
|
}
|
|
|
|
/**
|
|
* Updating the payment went well.
|
|
* Let's deal with the membership, if needed.
|
|
*/
|
|
if ($should_cancel_membership_on_refund) {
|
|
$membership = $this->get_membership();
|
|
|
|
if ($membership) {
|
|
$membership->cancel();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Creates a copy of the given model adn resets it's id to a 'new' state.
|
|
*
|
|
* @since 2.0.0
|
|
* @return \WP_Ultimo\Model\Base_Model
|
|
*/
|
|
public function duplicate() {
|
|
|
|
$line_items = $this->get_line_items();
|
|
|
|
$new_payment = parent::duplicate();
|
|
|
|
$new_payment->set_line_items($line_items);
|
|
|
|
return $new_payment;
|
|
}
|
|
}
|