817 lines
28 KiB
PHP
817 lines
28 KiB
PHP
<?php
|
|
|
|
class ASP_Product_Item {
|
|
public $currency;
|
|
public $name;
|
|
public $quantity;
|
|
public $description;
|
|
public $tax_amount;
|
|
|
|
protected $post_id = false;
|
|
protected $post;
|
|
protected $last_error = '';
|
|
protected $cust_quantity = false;
|
|
protected $tax;
|
|
protected $shipping;
|
|
protected $zero_cent = false;
|
|
protected $price;
|
|
protected $asp_main;
|
|
protected $coupon = false;
|
|
protected $price_with_discount;
|
|
protected $button_key = false;
|
|
protected $items = array();
|
|
protected $sess;
|
|
protected $overriden_data = false;
|
|
|
|
public function __construct( $post_id = false ) {
|
|
$this->asp_main = AcceptStripePayments::get_instance();
|
|
if ( false !== $post_id ) {
|
|
//let's try to load item from product
|
|
$this->post_id = $post_id;
|
|
$this->load_from_product();
|
|
|
|
$btn_uniq_id = isset( $_GET['btn_uniq_id'] ) ? sanitize_text_field( stripslashes ( $_GET['btn_uniq_id'] ) ) : '';
|
|
if ( ! empty( $btn_uniq_id ) ) {
|
|
ASP_Debug_Logger::log( 'Using the btn_uniq_id parameter. Value of btn_uniq_id: ' . $btn_uniq_id, true );
|
|
$this->sess = ASP_Session::get_instance();
|
|
$this->overriden_data = $this->sess->get_transient_data( 'overriden_data_' . $btn_uniq_id );
|
|
}
|
|
}
|
|
}
|
|
|
|
private function in_cents( $amount ) {
|
|
if ( ! $this->zero_cent ) {
|
|
$amount = round( $amount * 100, 2 );
|
|
} else {
|
|
$amount = round( $amount, 0 );
|
|
}
|
|
return $amount;
|
|
}
|
|
|
|
private function from_cents( $amount ) {
|
|
if ( ! $this->zero_cent ) {
|
|
$amount = round( $amount / 100, 2 );
|
|
} else {
|
|
$amount = round( $amount, 0 );
|
|
}
|
|
return $amount;
|
|
}
|
|
|
|
public function get_last_error() {
|
|
return $this->last_error;
|
|
}
|
|
|
|
public function get_product_id() {
|
|
return $this->post_id;
|
|
}
|
|
|
|
public function get_button_text() {
|
|
$button_text = get_post_meta( $this->post_id, 'asp_product_button_text', true );
|
|
if ( empty( $button_text ) ) {
|
|
$button_text = $this->asp_main->get_setting( 'button_text' );
|
|
}
|
|
return $button_text;
|
|
}
|
|
|
|
public function get_button_class() {
|
|
$button_class = get_post_meta( $this->post_id, 'asp_product_button_class', true );
|
|
return $button_class;
|
|
}
|
|
|
|
public function get_stock_items() {
|
|
$stock_items = get_post_meta( $this->post_id, 'asp_product_stock_items', true );
|
|
return $stock_items;
|
|
}
|
|
|
|
public function stock_control_enabled() {
|
|
$stock_control_enabled = get_post_meta( $this->post_id, 'asp_product_enable_stock', true );
|
|
return $stock_control_enabled;
|
|
}
|
|
|
|
public function get_tax() {
|
|
if ( false !== $this->overriden_data && isset( $this->overriden_data['tax'] ) ) {
|
|
return $this->overriden_data['tax'];
|
|
}
|
|
if ( ! empty( $this->tax ) ) {
|
|
return $this->tax;
|
|
}
|
|
$this->tax = get_post_meta( $this->post_id, 'asp_product_tax', true );
|
|
if ( empty( $this->tax ) ) {
|
|
$this->tax = 0;
|
|
}
|
|
return $this->tax;
|
|
}
|
|
|
|
public function set_tax( $tax ) {
|
|
$this->tax = $tax;
|
|
}
|
|
|
|
public function get_shipping( $in_cents = false ) {
|
|
if ( ! isset( $this->shipping ) ) {
|
|
$this->shipping = get_post_meta( $this->post_id, 'asp_product_shipping', true );
|
|
}
|
|
if ( empty( $this->shipping ) ) {
|
|
$this->shipping = 0;
|
|
}
|
|
if ( $in_cents ) {
|
|
return $this->in_cents( $this->shipping );
|
|
}
|
|
return $this->shipping;
|
|
}
|
|
|
|
public function set_shipping( $shipping, $in_cents = false ) {
|
|
$this->shipping = $in_cents ? $this->from_cents( $shipping ) : $shipping;
|
|
}
|
|
|
|
public function get_items_total( $in_cents = false, $with_discount = false ) {
|
|
$items_total = 0;
|
|
if ( ! empty( $this->items ) ) {
|
|
foreach ( $this->items as $item ) {
|
|
$items_total += $item['price'];
|
|
}
|
|
} else {
|
|
return 0;
|
|
}
|
|
|
|
if ( $with_discount && $this->coupon && 'perc' === $this->coupon['discount_type'] ) {
|
|
$items_total = $this->apply_discount_to_amount( $items_total, false );
|
|
}
|
|
|
|
return $in_cents ? $this->in_cents( $items_total ) : $items_total;
|
|
}
|
|
|
|
public function get_items() {
|
|
return $this->items;
|
|
}
|
|
|
|
public function add_item( $name, $price ) {
|
|
$this->items[] = array(
|
|
'name' => $name,
|
|
'price' => floatval( $price ),
|
|
);
|
|
}
|
|
|
|
public function get_thumb() {
|
|
$this->thumb = get_post_meta( $this->post_id, 'asp_product_thumbnail', true );
|
|
return $this->thumb;
|
|
}
|
|
|
|
public function get_name() {
|
|
$this->name = $this->post->post_title;
|
|
return $this->name;
|
|
}
|
|
|
|
public function get_description() {
|
|
$this->description = get_post_meta( $this->post_id, 'asp_product_description', true );
|
|
return $this->description;
|
|
}
|
|
|
|
public function get_quantity() {
|
|
$this->quantity = get_post_meta( $this->post_id, 'asp_product_quantity', true );
|
|
if ( ! is_numeric( $this->quantity ) ) {
|
|
$this->quantity = absint( $this->quantity );
|
|
}
|
|
if ( empty( $this->quantity ) ) {
|
|
$this->quantity = 1;
|
|
}
|
|
if ( $this->cust_quantity ) {
|
|
$this->quantity = $this->cust_quantity;
|
|
}
|
|
return $this->quantity;
|
|
}
|
|
|
|
public function get_coupon_discount_amount() {
|
|
$price = $this->get_price();
|
|
$items_total = $this->get_items_total();
|
|
$discount_amount = $this->get_discount_amount( $price + $items_total );
|
|
return $discount_amount;
|
|
}
|
|
|
|
public function get_price( $in_cents = false, $price_with_discount = false ) {
|
|
if ( is_null( $this->price ) ) {
|
|
$this->price = get_post_meta( $this->post_id, 'asp_product_price', true );
|
|
$this->price = empty( $this->price ) ? 0 : round( $this->price * 100 ) / 100;
|
|
}
|
|
if ( $price_with_discount && $this->coupon ) {
|
|
$discount_amount = $this->get_discount_amount( $this->price, $in_cents );
|
|
$this->price_with_discount = $this->price - $discount_amount;
|
|
}
|
|
if ( $in_cents ) {
|
|
if ( $price_with_discount && $this->coupon ) {
|
|
return $this->in_cents( $this->price_with_discount );
|
|
}
|
|
return $this->in_cents( $this->price );
|
|
}
|
|
if ( $price_with_discount && $this->coupon ) {
|
|
return $this->price_with_discount;
|
|
}
|
|
return $this->price;
|
|
}
|
|
|
|
public function get_surcharge()
|
|
{
|
|
$surcharge_amount = get_post_meta( $this->post_id, 'asp_surcharge_amount', true );
|
|
|
|
return !empty($surcharge_amount) && is_numeric($surcharge_amount) ? $surcharge_amount : 0;
|
|
}
|
|
|
|
public function get_surcharge_label()
|
|
{
|
|
$surcharge_label = get_post_meta( $this->post_id, 'asp_surcharge_label', true );
|
|
|
|
return !empty($surcharge_label) ? $surcharge_label : __("Surcharge", 'stripe-payments');
|
|
}
|
|
|
|
public function get_surcharge_type()
|
|
{
|
|
$surcharge_type = get_post_meta( $this->post_id, 'asp_surcharge_type', true );
|
|
|
|
return empty($surcharge_type) ? 'flat' : $surcharge_type;
|
|
}
|
|
|
|
/**
|
|
* Calculate the total surcharge amount based on the surcharge type and amount.
|
|
*
|
|
* @param bool $in_cents Whether to return the result in cents.
|
|
*
|
|
* @return float|int
|
|
*/
|
|
public function calculate_total_surcharge(bool $in_cents = false)
|
|
{
|
|
$surcharge_amount = $this->get_surcharge();
|
|
$surcharge_type = $this->get_surcharge_type();
|
|
|
|
if ( $surcharge_type == 'perc' ){
|
|
$calculate_from_amount = $this->get_total();
|
|
$surcharge_percentage = floatval($surcharge_amount);
|
|
$amount = ( $calculate_from_amount / 100 ) * $surcharge_percentage;
|
|
} else {
|
|
$amount = floatval($surcharge_amount);
|
|
}
|
|
|
|
return $in_cents ? $this->in_cents( $amount ) : $amount;
|
|
}
|
|
|
|
/**
|
|
* Returns min amount for donation product
|
|
*
|
|
* @param bool $in_cents Return amount in cents if set to `true`
|
|
*
|
|
* @return integer|float
|
|
*/
|
|
public function get_min_amount( $in_cents = false ) {
|
|
$min_amount = get_post_meta( $this->post_id, 'asp_product_min_amount', true );
|
|
$min_amount = empty( $min_amount ) ? 0 : $min_amount;
|
|
return $in_cents ? $this->in_cents( $min_amount ) : $min_amount;
|
|
}
|
|
|
|
/**
|
|
* Returns product type
|
|
*
|
|
* @return string
|
|
*/
|
|
public function get_type() {
|
|
$type = get_post_meta( $this->post_id, 'asp_product_type', true );
|
|
return $type;
|
|
}
|
|
|
|
public function apply_discount_to_amount( $amount, $in_cents = false ) {
|
|
if ( $this->coupon ) {
|
|
if ( 'perc' === $this->coupon['discount_type'] ) {
|
|
$perc = AcceptStripePayments::is_zero_cents( $this->get_currency() ) ? 0 : 2;
|
|
$discount_amount = round( $amount * ( $this->coupon['discount'] / 100 ), $perc );
|
|
} else {
|
|
$discount_amount = $this->coupon['discount'];
|
|
if ( $in_cents && ! AcceptStripePayments::is_zero_cents( $this->get_currency() ) ) {
|
|
$discount_amount = $discount_amount * 100;
|
|
}
|
|
}
|
|
if ( $in_cents ) {
|
|
$discount_amount = round( $discount_amount, 0 );
|
|
}
|
|
$amount = $amount - $discount_amount;
|
|
}
|
|
return $amount;
|
|
}
|
|
|
|
public function get_discount_amount( $total, $in_cents = false ) {
|
|
$discount_amount = 0;
|
|
if ( $this->coupon ) {
|
|
if ( 'perc' === $this->coupon['discount_type'] ) {
|
|
$perc = AcceptStripePayments::is_zero_cents( $this->get_currency() ) ? 0 : 2;
|
|
$discount_amount = round( $total * ( $this->coupon['discount'] / 100 ), $perc );
|
|
} else {
|
|
$discount_amount = $this->coupon['discount'];
|
|
if ( ! $this->coupon['per_order'] ) {
|
|
$discount_amount = $discount_amount * $this->get_quantity();
|
|
}
|
|
if ( $in_cents && ! AcceptStripePayments::is_zero_cents( $this->get_currency() ) ) {
|
|
$discount_amount = $discount_amount * 100;
|
|
}
|
|
}
|
|
if ( $in_cents ) {
|
|
$discount_amount = round( $discount_amount, 0 );
|
|
}
|
|
$this->coupon['discountAmount'] = $discount_amount;
|
|
}
|
|
return $discount_amount;
|
|
}
|
|
|
|
/*
|
|
* Returns the total amount for the product (including tax, shipping, item quantity, and discount if any)
|
|
*
|
|
* @param bool $in_cents Return amount in cents if set to `true`
|
|
*/
|
|
public function get_total( $in_cents = false ) {
|
|
$total = $this->get_price( $in_cents );
|
|
|
|
$items_total = $this->get_items_total( $in_cents );
|
|
|
|
$total += $items_total;
|
|
|
|
$total = $total * $this->get_quantity();
|
|
|
|
$total = $total - $this->get_discount_amount( $total, $in_cents );
|
|
|
|
if ( $this->get_tax() ) {
|
|
$total = $total + $this->get_tax_amount( $in_cents, true );
|
|
}
|
|
|
|
$shipping = $this->get_shipping( $in_cents );
|
|
|
|
if ( ! empty( $shipping ) ) {
|
|
$total = $total + $this->get_shipping( $in_cents );
|
|
}
|
|
|
|
if ( $total < 0 ) {
|
|
$total = 0;
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
|
|
public function set_price( $price, $in_cents = false ) {
|
|
//workaround for zero-cents currencies for Sub addon 2.0.10 or less
|
|
$this->zero_cent = AcceptStripePayments::is_zero_cents( $this->get_currency() );
|
|
if ( $this->zero_cent && class_exists( 'ASPSUB_main' ) && isset( $this->plan ) && version_compare( ASPSUB_main::ADDON_VER, '2.0.10' ) <= 0 ) {
|
|
$price = $price * 100;
|
|
}
|
|
//end workaround
|
|
if ( $in_cents ) {
|
|
$price = $this->from_cents( $price );
|
|
}
|
|
$this->price = $price;
|
|
|
|
}
|
|
|
|
public function set_quantity( $quantity ) {
|
|
$this->cust_quantity = $quantity;
|
|
}
|
|
|
|
public function get_tax_amount( $in_cents = false, $price_with_discount = false ) {
|
|
$total = $this->get_price( false );
|
|
|
|
$items_total = $this->get_items_total( false );
|
|
|
|
$total += $items_total;
|
|
|
|
$total = $total * $this->get_quantity();
|
|
|
|
$total = $total - $this->get_discount_amount( $total, false );
|
|
|
|
$this->tax_amount = AcceptStripePayments::get_tax_amount( $total, $this->get_tax(), $this->zero_cent );
|
|
if ( $in_cents ) {
|
|
return $this->in_cents( $this->tax_amount );
|
|
}
|
|
return $this->tax_amount;
|
|
}
|
|
|
|
public function get_currency() {
|
|
if ( empty( $this->currency ) ) {
|
|
$this->currency = get_post_meta( $this->post_id, 'asp_product_currency', true );
|
|
$this->currency = empty( $this->currency ) ? $this->asp_main->get_setting( 'currency_code' ) : $this->currency;
|
|
}
|
|
$this->zero_cent = AcceptStripePayments::is_zero_cents( $this->currency );
|
|
return $this->currency;
|
|
}
|
|
|
|
public function set_currency( $curr ) {
|
|
$this->currency = $curr;
|
|
$this->zero_cent = AcceptStripePayments::is_zero_cents( $curr );
|
|
}
|
|
|
|
public function is_variable() {
|
|
return $this->get_price() === 0;
|
|
}
|
|
|
|
public function is_currency_variable() {
|
|
if ( $this->get_type() !== 'donation' ) {
|
|
return false;
|
|
}
|
|
$currency_variable = get_post_meta( $this->post_id, 'asp_product_currency_variable', true );
|
|
return $currency_variable;
|
|
}
|
|
|
|
public function get_redir_url() {
|
|
$url = get_post_meta( $this->post_id, 'asp_product_thankyou_page', true );
|
|
if ( empty( $url ) ) {
|
|
$url = $this->asp_main->get_setting( 'checkout_url' );
|
|
}
|
|
return $url;
|
|
}
|
|
|
|
public function collect_billing_addr() {
|
|
return get_post_meta( $this->post_id, 'asp_product_collect_billing_addr', true );
|
|
}
|
|
|
|
public function gen_item_data() {
|
|
$ret = array();
|
|
$item_info = array(
|
|
'name' => $this->get_name(),
|
|
'amount' => $this->get_price( true ),
|
|
'currency' => $this->get_currency(),
|
|
'quantity' => $this->get_quantity(),
|
|
);
|
|
$thumb = $this->get_thumb();
|
|
if ( ! empty( $thumb ) ) {
|
|
$item_info['images'] = array( $this->get_thumb() );
|
|
}
|
|
$descr = $this->get_description();
|
|
if ( ! empty( $descr ) ) {
|
|
$item_info['description'] = $this->get_description();
|
|
}
|
|
$ret[] = $item_info;
|
|
$tax = $this->get_tax();
|
|
if ( ! empty( $tax ) ) {
|
|
$tax_str = apply_filters( 'asp_customize_text_msg', __( 'Tax', 'stripe-payments' ), 'tax_str' );
|
|
$tax_info = array(
|
|
'name' => sprintf( '%s (%s%%)', $tax_str, $this->get_tax() ),
|
|
'amount' => $this->get_tax_amount( true ),
|
|
'currency' => $this->get_currency(),
|
|
'quantity' => $this->get_quantity(),
|
|
);
|
|
$ret[] = $tax_info;
|
|
}
|
|
$ship = $this->get_shipping();
|
|
if ( ! empty( $ship ) ) {
|
|
$ship_str = apply_filters( 'asp_customize_text_msg', __( 'Shipping', 'stripe-payments' ), 'shipping_str' );
|
|
$tax_info = array(
|
|
'name' => sprintf( '%s', $ship_str ),
|
|
'amount' => $this->get_shipping( true ),
|
|
'currency' => $this->get_currency(),
|
|
'quantity' => $this->get_quantity(),
|
|
);
|
|
$ret[] = $tax_info;
|
|
}
|
|
return $ret;
|
|
}
|
|
|
|
public function get_coupon() {
|
|
return $this->coupon;
|
|
}
|
|
|
|
private function load_coupon( $coupon_code ) {
|
|
//let's find coupon
|
|
$coupon = get_posts(
|
|
array(
|
|
'meta_key' => 'asp_coupon_code',
|
|
'meta_value' => $coupon_code,
|
|
'posts_per_page' => 1,
|
|
'offset' => 0,
|
|
'post_type' => 'asp_coupons',
|
|
)
|
|
);
|
|
wp_reset_postdata();
|
|
if ( empty( $coupon ) ) {
|
|
//coupon not found
|
|
$this->last_error = __( 'Coupon not found.', 'stripe-payments' );
|
|
return false;
|
|
}
|
|
$coupon = $coupon[0];
|
|
//check if coupon is active
|
|
if ( ! get_post_meta( $coupon->ID, 'asp_coupon_active', true ) ) {
|
|
$this->last_error = __( 'Coupon is not active.', 'stripe-payments' );
|
|
return false;
|
|
}
|
|
//check if coupon start date has come
|
|
$start_date = get_post_meta( $coupon->ID, 'asp_coupon_start_date', true );
|
|
if ( empty( $start_date ) || strtotime( $start_date ) > time() ) {
|
|
$this->last_error = __( 'Coupon is not available yet.', 'stripe-payments' );
|
|
return false;
|
|
}
|
|
//check if coupon has expired
|
|
$exp_date = get_post_meta( $coupon->ID, 'asp_coupon_exp_date', true );
|
|
if ( ! empty( $exp_date ) && strtotime( $exp_date ) < time() ) {
|
|
$this->last_error = __( 'Coupon has expired.', 'stripe-payments' );
|
|
return false;
|
|
}
|
|
//check if redemption limit is reached
|
|
$red_limit = get_post_meta( $coupon->ID, 'asp_coupon_red_limit', true );
|
|
$red_count = get_post_meta( $coupon->ID, 'asp_coupon_red_count', true );
|
|
if ( ! empty( $red_limit ) && intval( $red_count ) >= intval( $red_limit ) ) {
|
|
$this->last_error = __( 'Coupon redemption limit is reached.', 'stripe-payments' );
|
|
return false;
|
|
}
|
|
|
|
$per_order = get_post_meta( $coupon->ID, 'asp_coupon_per_order', true );
|
|
|
|
$this->coupon = array(
|
|
'code' => $coupon_code,
|
|
'id' => $coupon->ID,
|
|
'discount' => get_post_meta( $coupon->ID, 'asp_coupon_discount', true ),
|
|
'discount_type' => get_post_meta( $coupon->ID, 'asp_coupon_discount_type', true ),
|
|
'per_order' => empty( $per_order ) ? 0 : 1,
|
|
);
|
|
return true;
|
|
}
|
|
|
|
public function check_coupon( $coupon_code = false ) {
|
|
if ( ! $coupon_code ) {
|
|
$this->last_error = 'No coupon code provided';
|
|
return false;
|
|
}
|
|
$coupon_code = trim( $coupon_code );
|
|
$this->load_coupon( $coupon_code );
|
|
if ( ! $this->coupon ) {
|
|
return false;
|
|
}
|
|
//check if coupon is allowed for the product
|
|
$only_for_allowed_products = get_post_meta( $this->coupon['id'], 'asp_coupon_only_for_allowed_products', true );
|
|
if ( $only_for_allowed_products ) {
|
|
$allowed_products = get_post_meta( $this->coupon['id'], 'asp_coupon_allowed_products', true );
|
|
if ( is_array( $allowed_products ) && ! in_array( strval( $this->post_id ), $allowed_products, true ) ) {
|
|
$this->last_error = __( 'Coupon is not allowed for this product.', 'stripe-payments' );
|
|
$this->coupon = false;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public function get_download_url() {
|
|
$post_url = isset( $_POST['asp_item_url'] ) ? sanitize_text_field( stripslashes ( $_POST['asp_item_url'] ) ) : '';
|
|
if ( $post_url ) {
|
|
$item_url = $post_url;
|
|
} else {
|
|
$item_url = get_post_meta( $this->post_id, 'asp_product_upload', true );
|
|
$item_url = $item_url ? $item_url : '';
|
|
|
|
if ( ! $item_url ) {
|
|
return '';
|
|
}
|
|
$item_url = base64_encode( $item_url );
|
|
}
|
|
$item_url = apply_filters(
|
|
'asp_item_url_process',
|
|
$item_url,
|
|
array(
|
|
'button_key' => $this->get_button_key(),
|
|
'product_id' => $this->post_id,
|
|
)
|
|
);
|
|
$item_url = base64_decode( $item_url );
|
|
return $item_url;
|
|
}
|
|
|
|
public function get_button_key() {
|
|
if ( ! $this->button_key ) {
|
|
// ASP_Debug_Logger::log("Get Button Key - Name:". $this->get_name() . ", Price:", $this->get_price(true));
|
|
$this->button_key = md5( htmlspecialchars_decode( $this->get_name() ) . $this->get_price( true ) );
|
|
}
|
|
return $this->button_key;
|
|
}
|
|
|
|
public function is_physical_product() {
|
|
if(!empty($this->get_meta('asp_is_physical_product'))) {
|
|
return true;
|
|
} else if (!empty($this->get_meta('asp_product_shipping'))) {
|
|
return true;
|
|
} else if (!empty($this->get_meta('asp_product_shipping_variations'))) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function load_from_product( $post_id = false ) {
|
|
if ( false === $post_id ) {
|
|
$post_id = $this->post_id;
|
|
}
|
|
if ( false === $post_id ) {
|
|
$this->last_error = __( 'No product ID provided.', 'stripe-payments' );
|
|
return false;
|
|
}
|
|
|
|
$post_id = intval( $post_id );
|
|
$this->post = get_post( $post_id );
|
|
if ( ! $this->post || ( get_post_type( $post_id ) !== ASPMain::$products_slug && get_post_type( $post_id ) !== ASPMain::$temp_prod_slug ) ) {
|
|
// translators: %d is product id
|
|
$this->last_error = sprintf( __( "Can't find product with ID %d", 'stripe-payments' ), $post_id );
|
|
ASP_Debug_Logger::log( "Can't find product with ID: " . $post_id, false );
|
|
return false;
|
|
}
|
|
$this->zero_cent = AcceptStripePayments::is_zero_cents( $this->get_currency() );
|
|
return true;
|
|
}
|
|
|
|
public function get_meta( $meta, $single = true ) {
|
|
return get_post_meta( $this->post_id, $meta, $single );
|
|
}
|
|
|
|
/**
|
|
* Calculate regional shipping amount by the provided region.
|
|
*
|
|
* @param $shipping_region array
|
|
* @param $in_cents bool
|
|
*
|
|
* @return float|int
|
|
*/
|
|
public function calculate_regional_shipping_amount($shipping_region, $in_cents = false) {
|
|
|
|
$regional_shipping_amount = 0;
|
|
$configured_variations = $this->get_meta('asp_product_shipping_variations');
|
|
// Evaluate variable shipping if enabled.
|
|
if (is_array($configured_variations) && count($configured_variations)) {
|
|
// Iterate through the available shipping variation option to find if there is amy matching.
|
|
foreach ($configured_variations as $variation) {
|
|
$variation['amount'] = (float) $variation['amount'];
|
|
$location = strtolower($variation['loc']);
|
|
switch ($variation['type']) {
|
|
case '0':
|
|
if (!empty($shipping_region['country']) && $location == strtolower($shipping_region['country']) )
|
|
// Matched by country, get shipping amount for this location.
|
|
$regional_shipping_amount += $variation['amount'];
|
|
break;
|
|
case '1':
|
|
if (!empty($shipping_region['state']) && $location == strtolower($shipping_region['state']) )
|
|
// Matched by state, get shipping amount for this location.
|
|
$regional_shipping_amount += $variation['amount'];
|
|
break;
|
|
case '2':
|
|
if (!empty($shipping_region['city']) && $location == strtolower($shipping_region['city']) )
|
|
// Matched by city, get shipping amount for this location.
|
|
$regional_shipping_amount += $variation['amount'];
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return $in_cents ? $this->in_cents($regional_shipping_amount) : $regional_shipping_amount;
|
|
}
|
|
|
|
/**
|
|
* Validates the total checkout amount for a product against amounts from ajax.
|
|
*
|
|
* @param int $amount The price to validate.
|
|
* @param int $quantity Product quantity.
|
|
* @param array $custom_inputs Custom input from customer such as applied coupon code, billing/shipping details, price variation etc.
|
|
*
|
|
* @return bool TRUE if validation is successful, FALSE otherwise.
|
|
*/
|
|
public function validate_total_amount( $amount, $quantity, $custom_inputs) {
|
|
//ASP_Debug_Logger::log("Amount submitted by customer (in cents): ". $amount, true);
|
|
$product_price = (float) $this->get_meta('asp_product_price');
|
|
$quantity = (int) $quantity;
|
|
$product_price_in_cents = $this->in_cents($product_price);
|
|
//ASP_Debug_Logger::log("Raw product price: ". $product_price_in_cents , true);
|
|
|
|
$this->set_price($product_price_in_cents, true);
|
|
|
|
$this->set_quantity( $quantity );
|
|
|
|
$price_variation = $custom_inputs['price_variation'];
|
|
if ( isset($price_variation) && !empty( $price_variation ) ) {
|
|
$variations_groups = $this->get_meta('asp_variations_groups');
|
|
$variations_names = $this->get_meta('asp_variations_names');
|
|
$variations_prices = $this->get_meta('asp_variations_prices');
|
|
|
|
$construct_final_price = ! empty( $this->get_meta('asp_product_hide_amount_input') );
|
|
|
|
$pvars = explode(',', $price_variation ); // Hold data like this: ['<group_name>-<applied_option>', ....]
|
|
|
|
$variation_price = 0;
|
|
foreach ($pvars as $pvar_str) {
|
|
$separator = '_';
|
|
// Use the last index of the separator to explode the $pvar_str. This is to avoid collision if $pvar_str contains (from the group name) more than one $separator.
|
|
$last_separator_index = strrpos($pvar_str, $separator);
|
|
|
|
$option_group = substr($pvar_str, 0, $last_separator_index); // Group name;
|
|
$applied_option = (int) substr($pvar_str, $last_separator_index + strlen($separator));// Applied option
|
|
|
|
$group_index = array_search($option_group, $variations_groups);
|
|
$applied_price = (float) $variations_prices[$group_index][$applied_option];
|
|
$applied_price = $this->in_cents( $applied_price );
|
|
$variation_price += $applied_price;
|
|
// ASP_Debug_Logger::log("Applied variation price for ". $variations_names[$group_index][$applied_option]. ' : ' .$applied_price, true);
|
|
}
|
|
// ASP_Debug_Logger::log("Applied total variation price : ". $variation_price, true);
|
|
|
|
$price_with_applied_variation = 0;
|
|
if ($construct_final_price) {
|
|
$price_with_applied_variation = $variation_price;
|
|
}else{
|
|
$price_with_applied_variation = $variation_price + $product_price_in_cents;
|
|
}
|
|
$this->set_price($price_with_applied_variation, true);
|
|
// ASP_Debug_Logger::log("Applied total variation price with base price : ". $price_with_applied_variation, true);
|
|
}
|
|
// ASP_Debug_Logger::log("Amount current total (after price variation): ". $this->get_total(true), true);
|
|
|
|
$tax_variations_type = $this->get_meta('asp_product_tax_variations_type');
|
|
$tax_variations_arr = $this->get_meta('asp_product_tax_variations');
|
|
$collect_billing_addr_enabled = $this->get_meta('asp_product_collect_billing_addr');
|
|
$collect_shipping_addr_enabled = $this->get_meta('asp_product_collect_shipping_addr');
|
|
// ASP_Debug_Logger::log("Applied tax amount (before tax variation apply): ". $this->get_tax_amount(true), true);
|
|
|
|
// Evaluate variable tax if enabled.
|
|
if (is_array($tax_variations_arr) && count($tax_variations_arr) && $collect_billing_addr_enabled === '1') {
|
|
$applied_tax_percentage = 0;
|
|
$tax_region = ($tax_variations_type === 's' && $collect_shipping_addr_enabled === '1') ? $custom_inputs['shipping_details']['address'] : $custom_inputs['billing_details']['address'];
|
|
|
|
// Iterate through the available tax variation option to find if there is amy matching.
|
|
foreach ($tax_variations_arr as $tax_variation) {
|
|
$tax_variation['amount'] = (float) $tax_variation['amount'];
|
|
switch ($tax_variation['loc']) {
|
|
case $tax_region['city']:
|
|
// Matched by city, get tax amount for this location.
|
|
$applied_tax_percentage += $tax_variation['amount'];
|
|
break;
|
|
case $tax_region['state']:
|
|
// Matched by state, get tax amount for this location.
|
|
$applied_tax_percentage += $tax_variation['amount'];
|
|
break;
|
|
case $tax_region['country']:
|
|
// Matched by country, get tax amount for this location.
|
|
$applied_tax_percentage += $tax_variation['amount'];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($applied_tax_percentage > 0) {
|
|
$this->set_tax($applied_tax_percentage);
|
|
}
|
|
|
|
}
|
|
//ASP_Debug_Logger::log("Applied tax amount (after tax variation): ". $this->get_tax_amount(true), true);
|
|
//ASP_Debug_Logger::log("Amount current total (after tax variation): ". $this->get_total(true), true);
|
|
|
|
$base_shipping_amount = get_post_meta( $this->post_id, 'asp_product_shipping', true );
|
|
if (empty($base_shipping_amount)){
|
|
$base_shipping_amount = 0;
|
|
} else {
|
|
$base_shipping_amount = $this->in_cents(floatval($base_shipping_amount));
|
|
}
|
|
$shipping_amount = $base_shipping_amount;
|
|
|
|
$collect_shipping_addr_enabled = $this->get_meta('asp_product_collect_shipping_addr');
|
|
if ( $collect_shipping_addr_enabled == '1'){
|
|
$regional_shipping_amount = $this->calculate_regional_shipping_amount($custom_inputs['shipping_details']['address'], true);
|
|
$shipping_amount += $regional_shipping_amount;
|
|
}
|
|
|
|
$this->set_shipping( $shipping_amount, true );
|
|
|
|
//ASP_Debug_Logger::log("Amount current total (after base shipping): ". $this->get_total(true), true);
|
|
|
|
$coupon_code = $custom_inputs['coupon_code'];
|
|
if ( !empty( $coupon_code ) ) {
|
|
// Get the coupon and evaluate it (if submitted).
|
|
if ( !$this->load_coupon( $coupon_code ) ) {
|
|
// Invalid coupon. The 'load_coupon' method will handle appending appropriate error message.
|
|
return false;
|
|
};
|
|
}
|
|
|
|
// Calculate the expected total amount.
|
|
$expected_total_amount = $this->get_total(true);
|
|
|
|
// Check if surcharge feature is enabled.
|
|
if ( !empty($this->get_surcharge()) ){
|
|
// Surcharge is enabled. Calculate the surcharge amount then adjust the expected total.
|
|
$surcharge_amount = round($this->calculate_total_surcharge(true));//This is in cents so round it to 0 decimal place.
|
|
|
|
ASP_Debug_Logger::log("The surcharge feature is enabled. Applied surcharge amount (in cents): ". $surcharge_amount, true);
|
|
$expected_total_amount += $surcharge_amount;
|
|
}
|
|
|
|
// Trigger a filter so addons can override it.
|
|
$expected_total_amount = apply_filters('asp_pre_api_submission_expected_amount', $expected_total_amount, $amount, $this->post_id);
|
|
|
|
// Check if the expected total amount matches the given amount.
|
|
if ( $amount != $expected_total_amount ){
|
|
ASP_Debug_Logger::log("Pre-API Submission validation amount mismatch. Expected amount (in cents): ". $expected_total_amount . ", Submitted amount (in cents): " . $amount, false);
|
|
// Set the last error message that will be displayed to the user.
|
|
$mismatch_err_msg = __( "Price validation failed. The submitted amount does not match the product's configured price. ", 'stripe-payments' );
|
|
$mismatch_err_msg .= "Expected: " . $this->from_cents($expected_total_amount) . ", Submitted: " . $this->from_cents($amount);
|
|
$this->last_error = $mismatch_err_msg;
|
|
return false;
|
|
}
|
|
|
|
ASP_Debug_Logger::log("Pre-API Submission validation successful. Expected: " . $this->from_cents($expected_total_amount) . ", Submitted: " . $this->from_cents($amount), true);
|
|
return true;
|
|
}
|
|
}
|