id = static::METHOD_CODE;
$this->has_fields = true;
$this->init_form_fields();
$this->init_settings();
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
if ($this->get_api_option('capture', 'no') == 'yes') {
add_action( 'woocommerce_order_status_processing', array( $this, 'capture_payment' ) );
}
add_action( 'woocommerce_order_status_completed' , array( $this, 'capture_payment' ) );
add_action( 'woocommerce_order_status_cancelled' , array( $this, 'cancel_payment' ) );
$this->icon = plugin_dir_url( dirname( __FILE__ ) ) . 'images/logo_green.png';
$this->title = $this->method_title = __($this->get_option('title', static::METHOD_NAME), 'tabby-checkout');
$this->method_description = static::METHOD_DESC;
$this->supports = array(
'products',
'refunds',
);
}
/**
* Check if the gateway is available for use.
*
* @return bool
*/
public function is_available() {
$is_available = parent::is_available();
if (!WC()->customer) {
$is_available = true;
} else {
if (!($country = WC()->customer->get_shipping_country())) {
$country = WC()->customer->get_billing_country();
}
if ($country && !WC_Tabby_Config::isAvailableForCountry($country)) {
$is_available = false;
}
if ($country == 'undefined') $is_available = true;
}
// only for supported currencies
if (!WC_Tabby_Config::isAvailableForCurrency()) $is_available = false;
// check for disabled_for_sku products
if (!WC_Tabby_Config::isEnabledForCartSKUs()) $is_available = false;
if (!WC()->cart || WC()->cart->is_empty()) return $is_available;
// if available, create session
if ($is_available && !is_admin()) {
$is_available = $this->get_is_available_from_api();
}
return $is_available;
}
public function init_form_fields() {
if (!$this->needs_setup()) {
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'tabby-checkout' ),
'type' => 'checkbox',
'label' => __( 'Enable ' . static::METHOD_NAME, 'tabby-checkout' ),
'default' => 'no'
),
'title' => array(
'title' => __( 'Title', 'tabby-checkout' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'tabby-checkout' ),
'default' => __( static::METHOD_NAME, 'tabby-checkout' ),
'desc_tip' => true,
),
'description_type' => array(
'title' => __( 'Method Description', 'tabby-checkout' ),
'type' => 'select',
'options' => [
0 => __('PromoCardWide' , 'tabby-checkout'),
1 => __('PromoCard' , 'tabby-checkout'),
2 => __('Text description' , 'tabby-checkout'),
3 => __('Blanc description', 'tabby-checkout')
],
'description' => __( 'This controls the description which the user sees during checkout.', 'tabby-checkout' ),
'default' => __( static::METHOD_DESCRIPTION_TYPE, 'tabby-checkout' ),
),
'card_theme' => array(
'title' => __( 'Promo Card Theme', 'tabby-checkout' ),
'type' => 'text',
'default' => 'black',
)
);
} else {
$this->form_fields = array(
'test' => array(
'type' => 'title',
'title' => __('Tabby API Public or Secret key is not configured.', 'tabby-checkout'),
/* translators: %s is replaced with the url of Tabby settings page */
'description' => sprintf(__('Please configure Tabby API settings here.', 'tabby-checkout'), admin_url( 'admin.php?page=wc-settings&tab=settings_tab_tabby' ))
)
);
}
}
public function get_description_config() {
$res = [];
$dtype = strpos(get_option('tabby_checkout_promo_theme', ''), ':') === false ? $this->get_option('description_type', static::METHOD_DESCRIPTION_TYPE) : 2;
switch ($dtype) {
case 0:
case 1:
$divId = static::TABBY_METHOD_CODE . 'Card';
$jsClass = 'TabbyCard';
if (static::TABBY_METHOD_CODE == 'creditCardInstallments') $jsClass = 'TabbyPaymentMethodSnippetCCI';
$res = [
'id' => $divId,
'class' => $jsClass,
'jsClass' => $jsClass,
'jsConf' => $this->getTabbyCardJsonConfig($divId)
];
break;
case 2:
$res = [
'class' => "tabbyDesc",
'html' => __(static::METHOD_DESC, 'tabby-checkout')
];
break;
default:
$res['class'] = 'empty';
}
$res['info'] = [
'style' => [
'verticalAlign' => 'middle',
'cursor' => 'pointer',
'width' => '16px',
'margin' => '5px'
],
'class' => 'info',
'data-tabby-info' => esc_attr(static::TABBY_METHOD_CODE),
'data-tabby-price' => esc_attr($this->formatAmount($this->get_order_total())),
'data-tabby-currency' => esc_attr(WC_Tabby_Config::getTabbyCurrency()),
'data-tabby-language' => esc_attr($this->get_lang()),
'data-tabby-installments-count' => WC_Tabby_Promo::getInstallmentsCount(),
'src' => plugin_dir_url( dirname( __FILE__ ) ) . 'images/info.png',
'alt' => 'Tabby'
];
return $res;
}
public function get_order_total($order = null) {
if ($order) return (float)$order->get_total();
return WC()->cart ? parent::get_order_total() : 0;
}
public function payment_fields() {
echo '';
echo '';
echo '';
$dtype = strpos(get_option('tabby_checkout_promo_theme', ''), ':') === false ? $this->get_option('description_type', static::METHOD_DESCRIPTION_TYPE) : 2;
switch ($dtype) {
case 0:
case 1:
$divId = static::TABBY_METHOD_CODE . 'Card';
$jsClass = 'TabbyCard';
if (static::TABBY_METHOD_CODE == 'creditCardInstallments') $jsClass = 'TabbyPaymentMethodSnippetCCI';
echo '
';
echo '';
break;
case 2:
echo '' . __(static::METHOD_DESC, 'tabby-checkout') . '
';
break;
}
echo '
';
}
public function getTabbyCardJsonConfig($divId) {
return json_encode([
'selector' => '#' . $divId,
'currency' => WC_Tabby_Config::getTabbyCurrency(),
'lang' => $this->get_lang(),
'price' => $this->formatAmount($this->get_order_total()),
'size' => $this->get_option('description_type', static::METHOD_DESCRIPTION_TYPE) == 0 ? 'wide' : 'narrow',
'theme' => $this->get_option('card_theme', 'black'),
'header' => false
]);
}
public function getTabbyConfig($order = null) {
global $wp;
$config = [];
$config['apiKey'] = $this->get_api_option('public_key');
$config['merchantCode'] = $this->getMerchantCode($order);
$config['locale'] = $this->get_lang();
$config['language']= $this->getLanguage();
$config['hideMethods'] = $this->get_api_option('hide_methods') == 'yes';
$config['localeSource'] = $this->get_api_option('locale_html') == 'yes' ? 'html' : '';
$config['debug'] = $this->get_api_option('debug') == 'yes' ? 1 : 0;
$config['notAvailableMessage'] = __('Sorry Tabby is unable to approve this purchase, please use an alternative payment method for your order.', 'tabby-checkout');
// used to ignore email on checkout
$config['ignoreEmail'] = apply_filters('tabby_checkout_ignore_email', false);
$config['buyer_history'] = null;
// buyer and shipping address for pay_for_order functionality
if (is_checkout_pay_page()) {
$order_id = $wp->query_vars['order-pay'];
$order = wc_get_order($order_id);
$customer = new \WC_Customer($order->get_customer_id());
$config['buyer'] = $this->getBuyerObject($order);
$config['shipping_address'] = $this->getShippingAddressObject($order);
$config['buyer_history'] = $this->getBuyerHistoryObject($customer);
} elseif ($order) {
$customer = new \WC_Customer($order->get_customer_id());
$config['buyer'] = $this->getBuyerObject($order);
$config['shipping_address'] = $this->getShippingAddressObject($order);
$config['buyer_history'] = $this->getBuyerHistoryObject($customer);
} elseif ($customer = WC()->customer) {
$config['buyer'] = $this->getFrontBuyerObject();
$config['shipping_address'] = $this->getShippingAddressObject($customer);
$config['buyer_history'] = $this->getBuyerHistoryObject($customer);
}
$config['payment'] = $this->getPaymentObject($order);
$config['merchantUrls'] = $this->getMerchantUrls($order);
return json_encode($config);
}
public function getBuyerHistoryObject($customer) {
if ($customer && $customer->get_date_created()) {
return [
'registered_since' => $customer->get_date_created()->date("c"),
'loyalty_level' => $this->getLoyaltyLevel($customer)
];
}
return null;
}
public function getLoyaltyLevel($customer) {
return count(wc_get_orders([
'customer' => $customer->get_email(),
'status' => ['wc-completed', 'wc-refunded']
]));
}
public function getBuyerObject($order) {
$billing = $order->get_address();
return array(
'email' => $billing['email'],
'name' => $billing['first_name'] . ' ' . $billing['last_name'],
'phone' => str_replace("+", "", $billing['phone'])
);
}
public function getShippingAddressObject($order) {
$street1 = $order->get_shipping_address_1();
$street2 = $order->get_shipping_address_2();
return array(
'address' => $street1 . (!empty($street2) ? (', ' . $street2) : ''),
'city' => $order->get_shipping_city()
);
}
public function getLanguage() {
return $this->get_api_option('popup_language', 'auto');
}
public function getMerchantCode($order = null) {
if ($order) {
$code = $order->get_billing_country() ?: $order->get_shipping_country();
} else {
$code = WC()->customer->get_billing_country() ?: WC()->customer->get_shipping_country();
}
if ($code == 'undefined' || empty($code)) $code = wc_get_base_location()['country'];
return $code;
}
public function getMerchantUrls($order) {
return [
'success' => is_checkout_pay_page() && $order ? $order->get_checkout_order_received_url() : ($order ? $order->get_checkout_order_received_url() : wc_get_endpoint_url( 'order-received', '', wc_get_checkout_url() )),
'cancel' => is_checkout_pay_page() && $order ? $order->get_checkout_payment_url() : wc_get_checkout_url(),
'failure' => is_checkout_pay_page() && $order ? $order->get_checkout_payment_url() : wc_get_checkout_url()
];
}
public static function is_classic_checkout_enabled() {
if ($checkout_id = get_option('woocommerce_checkout_page_id', false)) {
$checkout = get_post($checkout_id);
if (preg_match("/wp:woocommerce\/classic-shortcode|\[woocommerce_checkout\]/is", $checkout->post_content)) {
// old checkout enabled
return true;
};
};
return false;
}
public function get_is_available_from_api() {
// verify that new checkout active
if (self::is_classic_checkout_enabled()) {
// old checkout enabled, do not check with api
return true;
};
$config = json_decode(static::getTabbyConfig(), true);
// show module on checkout if there is no email/phone entered
if (empty($config['buyer']['email']) || empty($config['buyer']['phone'])) {
return true;
}
$config['payment']['buyer'] = $this->getFrontBuyerObject();
$config['payment']['order_history'] = WC_Tabby_AJAX::getOrderHistoryObject(
$config['payment']['buyer']['email'],
$config['payment']['buyer']['phone']
);
$config['payment']['buyer_history'] = $config['buyer_history'];
$config['payment']['shipping_address'] = $config['shipping_address'];
$modules = [];
$request = [
'payment' => $config['payment'],
'lang' => $this->get_lang(),
'merchant_code' => $config['merchantCode'],
'merchant_urls' => $config['merchantUrls']
];
$is_available = false;
$sha256 = hash('sha256', json_encode($this->get_cached_values($request)));
$tr_name = 'tabby_api_cache_' . $sha256;
if (($available_products = get_transient($tr_name)) === false) {
$result = WC_Tabby_Api::request('checkout', 'POST', $request);
if ($result && property_exists($result, 'status') && $result->status == 'created') {
$available_products = $result->configuration->available_products;
} else {
$available_products = new \StdClass();
}
set_transient($tr_name, $available_products, HOUR_IN_SECONDS);
}
if (is_object($available_products) && property_exists($available_products, static::TABBY_METHOD_CODE)) {
$is_available = true;
}
return $is_available;
}
protected function get_cached_values($request) {
return [
"lang" => $request["lang"],
"merchant_code" => $request["merchant_code"],
"amount" => $request["payment"]["amount"],
"currency" => $request["payment"]["currency"],
"email" => $request["payment"]["buyer"]["email"],
"phone" => $request["payment"]["buyer"]["phone"]
];
}
public function getFrontBuyerObject() {
$email = $name = $phone = null;
if (WC()->customer) {
$email = WC()->customer->get_billing_email()
? WC()->customer->get_billing_email()
: WC()->customer->get_email();
$fname = WC()->customer->get_billing_first_name()
? WC()->customer->get_billing_first_name()
: (WC()->customer->get_shipping_first_name()
? WC()->customer->get_shipping_first_name()
: WC()->customer->get_first_name());
$lname = WC()->customer->get_billing_last_name()
? WC()->customer->get_billing_last_name()
: (WC()->customer->get_shipping_last_name()
? WC()->customer->get_shipping_last_name()
: WC()->customer->get_last_name());
$phone = WC()->customer->get_billing_phone()
? WC()->customer->get_billing_phone()
: (WC()->customer->get_shipping_phone()
? WC()->customer->get_shipping_phone()
: '');
}
return array(
'email' => $email,
'name' => $fname . ' ' . $lname,
'phone' => str_replace("+", "", $phone)
);
}
public function getPaymentObject($order) {
return [
"amount" => $this->formatAmount($this->get_order_total($order)),
"currency" => WC_Tabby_Config::getTabbyCurrency(),
//"buyer_history" => $this->getBuyerHistoryObject(),
"description" => get_bloginfo("name") . ' Order',
"order" => $this->getOrderObject($order),
"meta" => [
"tabby_plugin_platform" => 'woocommerce',
"tabby_plugin_version" => MODULE_TABBY_CHECKOUT_VERSION
],
//"shipping_address" => $this-> getShippingAddressObject()
];
}
protected function getOrderObject($order) {
return [
'reference_id' => (string) (
$order == null
? md5(json_encode($this->getOrderItemsObject($order)))
: woocommerce_tabby_get_order_reference_id($order)
),
'shipping_amount' => $this->formatAmount(
$order == null
? (float)WC()->cart->get_shipping_total() + (float)WC()->cart->get_shipping_tax() :
($order->get_shipping_total() + $order->get_shipping_tax())
),
'discount_amount' => $this->formatAmount(
$order == null
? (float)WC()->cart->get_discount_total()
: $order->get_discount_total()
),
'tax_amount' => $this->formatAmount(
$order == null
? array_sum(WC()->cart->get_taxes())
: $order->get_total_tax()
),
'items' => $this->getOrderItemsObject($order)
];
}
protected function getOrderItemsObject($order) {
$items = [];
if ($order == null) {
foreach (WC()->cart->get_cart() as $item => $values) {
$items[] = $this->getOrdersItemsItemObject($values['data']->get_id(), $values['quantity']);
}
} else {
foreach ($order->get_items() as $item_id => $item_data) {
if (!$item_data->get_product()) continue;
$items[] = $this->getOrdersItemsItemObject($item_data->get_product()->get_id(), $item_data->get_quantity());
}
}
return $items;
}
protected function getOrdersItemsItemObject($product_id, $quantity) {
$_product = wc_get_product( $product_id );
$image_id = $_product->get_image_id();
$category = '';
$terms = get_the_terms( $product_id, 'product_cat' );
if (is_a($_product, 'WC_Product_Variation')) {
$terms = get_the_terms( $_product->get_parent_id(), 'product_cat' );
}
if ($terms !== false && !is_wp_error($terms)) {
foreach ($terms as $term) {
if ($term->taxonomy == 'product_cat') {
$category = $term->name;
break;
}
}
}
return [
'quantity' => (int)$quantity,
'title' => $_product->get_title(),
'category' => $category,
'reference_id' => '' . $_product->get_id(),
'description' => $_product->get_description(),
'image_url' => $image_id ? wp_get_attachment_image_url( $image_id, 'full') : wc_placeholder_img_src( 'full' ),
'product_url' => get_permalink( $_product->get_id() ),
'unit_price' => $this->formatAmount(wc_get_price_including_tax( $_product ))
];
}
public function process_admin_options() {
$saved = parent::process_admin_options();
}
public function process_payment( $order_id ) {
try {
global $woocommerce;
$order = new WC_Order( $order_id );
// note current order id in session
WC()->session->set( 'tabby_order_id', $order_id );
$redirect_url = $this->getTabbyRedirectUrl($order);
if (!is_wp_error($redirect_url)) {
// Add tracking cookie
if (isset($_COOKIE[self::TRACK_COOKIE_KEY])) {
$redirect_url .= '&' . self::TRACK_COOKIE_KEY . '=' . urlencode($_COOKIE[self::TRACK_COOKIE_KEY]);
}
return array(
'result' => 'success',
'redirect' => $redirect_url
);
} else {
wc_add_notice( $redirect_url->get_error_message(), 'error' );
return [
'result' => 'failure',
'message' => 'Sorry Tabby is unable to approve this purchase, please use an alternative payment method for your order.'
];
}
} catch (\Exception $e) {
$this->ddlog("error", "could not process payment", $e);
}
}
protected function getTabbyRedirectUrl($order) {
//return sanitize_url($_POST[$this->id . '_web_url']);
// create payment object
$config = json_decode(static::getTabbyConfig($order), true);
$request = [
'payment' => $config['payment'],
'lang' => $this->get_lang(),
'merchant_code' => $config['merchantCode'],
'merchant_urls' => $config['merchantUrls']
];
$request['payment']['order_history'] = WC_Tabby_AJAX::getOrderHistoryObject($order->get_billing_email(), $order->get_billing_phone());
$request['payment']['buyer'] = $config['buyer'];
$request['payment']['buyer_history'] = $config['buyer_history'];
$request['payment']['shipping_address'] = $config['shipping_address'];
$result = WC_Tabby_Api::request('checkout', 'POST', $request);
if ($result && property_exists($result, 'status') && $result->status == 'created') {
if (property_exists($result->configuration->available_products, static::TABBY_METHOD_CODE)) {
// register new payment id for order
$payment_id = $result->payment->id;
if ($this->get_tabby_payment_id($order->get_id()) != $payment_id) {
/* translators: %s is replaced with Tabby payment ID */
$order->add_order_note( sprintf( __( 'Payment assigned. ID: %s', 'tabby-checkout' ), $payment_id ) );
update_post_meta($order->get_id(), static::TABBY_PAYMENT_FIELD, $payment_id);
}
return $result->configuration->available_products->{static::TABBY_METHOD_CODE}[0]->web_url;
} else {
return new WP_Error( 'error', __( 'Api error, please try again later.', 'tabby-checkout' ) );;
}
} else {
return new WP_Error( 'error', __( 'Api error, please try again later.', 'tabby-checkout' ) );;
}
}
public function needs_setup() {
if ($this->get_api_option('public_key') && $this->get_api_option('secret_key')) {
return false;
}
return true;
}
public function get_api_option($option, $default = null) {
return get_option('tabby_checkout_' . $option, $default);
}
public function can_refund_order( $order ) {
return $order && $this->get_tabby_payment_id($order->get_id()) && !$this->needs_setup() && $this->get_order_capture_id($order->get_id());
}
public function get_order_capture_id($order_id) {
return get_post_meta($order_id, '_capture_id', true);
}
public function set_order_capture_id($order_id, $capture_id) {
return update_post_meta($order_id, '_capture_id', $capture_id);
}
public function authorize($order, $payment_id) {
try {
$logData = array(
"payment.id" => $payment_id,
"order.reference_id" => woocommerce_tabby_get_order_reference_id($order)
);
$this->ddlog("info", "authorize payment", null, $logData);
if ($this->get_tabby_payment_id($order->get_id()) != $payment_id) {
/* translators: %s is replaced with Tabby payment ID */
$order->add_order_note( sprintf( __( 'Payment assigned. ID: %s', 'tabby-checkout' ), $payment_id ) );
update_post_meta($order->get_id(), static::TABBY_PAYMENT_FIELD, $payment_id);
}
$res = $this->request($payment_id);
if (!empty($res)) {
if ($res->order->reference_id != woocommerce_tabby_get_order_reference_id($order)) {
$data = ["order" => [
"reference_id" => (string)woocommerce_tabby_get_order_reference_id($order)
]];
$result = $this->request($payment_id, 'PUT', $data);
$this->debug(['authorize - update order # - ', (array)$result]);
}
if ($res->status == 'CREATED') {
if ($this->get_tabby_payment_id($order->get_id()) != $payment_id) {
/* translators: %s is replaced with Tabby payment ID */
$order->add_order_note( sprintf( __( 'Payment created. ID: %s', 'tabby-checkout' ), $payment_id ) );
update_post_meta($order->get_id(), static::TABBY_PAYMENT_FIELD, $payment_id);
}
} elseif ($res->status == 'REJECTED') {
/* translators: %s is replaced with Tabby payment ID */
$order->add_order_note( sprintf( __( 'Payment %s is REJECTED', 'tabby-checkout' ), $payment_id ) );
return false;
} elseif ($res->status == 'EXPIRED') {
/* translators: %s is replaced with Tabby payment ID */
$order->add_order_note( sprintf( __( 'Payment %s is EXPIRED', 'tabby-checkout' ), $payment_id ) );
return false;
} elseif ($order->get_total() == $res->amount && $order->get_currency() == $res->currency) {
update_post_meta($order->get_id(), static::TABBY_STATUS_FIELD, static::STATUS_AUTH);
update_post_meta($order->get_id(), static::TABBY_PAYMENT_FIELD, $payment_id);
/* translators: %s is replaced with Tabby payment ID */
$order->add_order_note( sprintf( __( 'Payment authorized. ID: %s', 'tabby-checkout' ), $payment_id ) );
return true;
} else {
/* translators: %1$s is replaced with Tabby payment ID, %2$s is replaced with payment currency */
$order->set_status( 'failed', sprintf( __( 'Payment failed. ID: %1$s. Total missmatch. Transaction amount: %2$s', 'tabby-checkout' ), $payment_id, $res->amount . $res->currency ) );
$order->save();
return false;
}
}
} catch (\Exception $e) {
$this->ddlog("error", "could not authorize payment", $e);
}
return false;
}
public function is_payment_expired($order, $payment_id) {
$res = $this->request($payment_id);
$timeout = get_option( 'tabby_checkout_order_timeout' );
if ($res && $res->created_at && (time() - strtotime($res->created_at) > $timeout * 60)) {
$this->ddlog("info", "payment is expired", null, [
'payment.id' => $payment_id,
'created_at' => $res->created_at,
'timeout' => $timeout,
'order_created' => $order->get_date_created(),
'time' => gmdate("Y-m-d\TH:i:s\Z")
]);
return true;
}
return false;
}
public function get_tabby_payment_id($order_id) {
return get_post_meta($order_id, static::TABBY_PAYMENT_FIELD, true);
}
public function capture_payment($order_id ) {
try {
$order = wc_get_order( $order_id );
$gateway = wc_get_payment_gateway_by_order($order);
if (!($gateway instanceof WC_Gateway_Tabby_Checkout_Base)) return;
$payment_id = $this->get_tabby_payment_id($order->get_id());
if (!$payment_id) return;
$logData = array(
"payment.id" => $payment_id,
"order.reference_id" => woocommerce_tabby_get_order_reference_id($order)
);
if ($this->can_capture($order_id)) {
$this->ddlog("info", "capture payment", null, $logData);
$data = [
"amount" => $this->formatAmount($order->get_total()),
"tax_amount" => $this->formatAmount($order->get_total_tax()),
"shipping_amount" => $this->formatAmount($order->get_shipping_total()),
"created_at" => null
];
$data['items'] = [];
foreach ($order->get_items() as $item_id => $item_data) {
$data['items'][] = [
'title' => $item_data->get_name(),
'description' => $item_data->get_name(),
'quantity' => (int)$item_data->get_quantity(),
'unit_price' => $this->formatAmount($item_data->get_total() / $item_data->get_quantity()),
'reference_id' => ''.$item_data->get_product()->get_id()
];
}
$this->debug(['capture', $payment_id, $data]);
$result = $this->request($payment_id . '/captures', 'POST', $data);
$this->debug(['capture - result', (array)$result]);
if (property_exists($result, 'captures') && is_array($result->captures)) {
$txn = array_pop($result->captures);
$this->set_order_capture_id($order_id, $txn->id);
update_post_meta($order->get_id(), static::TABBY_STATUS_FIELD, static::STATUS_CAPTURED);
/* translators: %s is replaced with Tabby capture ID */
$order->add_order_note( sprintf( __( 'Payment captured. ID: %s', 'tabby-checkout' ), $txn->id ) );
} else {
throw new \Exception("No captures found");
}
}
} catch (\Exception $e) {
$this->ddlog("error", "could not capture payment", $e);
}
}
public function cancel_payment($order_id) {
try {
$order = wc_get_order($order_id );
if ($this->can_cancel($order_id)) {
$this->cancel($order);
} else {
if (get_post_meta($order->get_id(), static::TABBY_STATUS_FIELD, true) == static::STATUS_CAPTURED) {
$logData = array(
"order.reference_id" => woocommerce_tabby_get_order_reference_id($order)
);
$this->ddlog("info", "could not cancel payment, needs refund", null, $logData);
throw new \Exception( __( 'Order payment captured. Please refund order.', 'tabby-checkout') );
}
}
} catch (\Exception $e) {
$this->ddlog("error", "could not cancel payment", $e);
}
}
public function can_cancel($order_id) {
return get_post_meta($order_id, static::TABBY_STATUS_FIELD, true) == static::STATUS_AUTH;
}
public function cancel($order) {
if (get_post_meta($order->get_id(), static::TABBY_STATUS_FIELD, true) !== static::STATUS_CLOSED) {
$payment_id = $this->get_tabby_payment_id($order->get_id());
$logData = array(
"payment.id" => $payment_id,
"order.reference_id" => woocommerce_tabby_get_order_reference_id($order)
);
$this->ddlog("info", "cancel payment", null, $logData);
$this->request($payment_id . '/close', 'POST');
update_post_meta($order->get_id(), static::TABBY_STATUS_FIELD, static::STATUS_CLOSED);
$order->add_order_note(__( 'Tabby payment closed', 'tabby-checkout' ));
}
}
public function can_auth($order_id) {
return (!get_post_meta($order_id, static::TABBY_STATUS_FIELD, true)) ? true : false;
}
public function can_capture($order_id) {
$result = get_post_meta($order_id, static::TABBY_STATUS_FIELD, true) == static::STATUS_AUTH;
// check payment status on Tabby
$payment = $this->get_tabby_payment($order_id);
if (property_exists($payment, 'status')) {
$result = ($payment->status == 'AUTHORIZED');
// if captured, update internal status
if ($payment->status == 'CLOSED' && property_exists($payment, 'captures') && count($payment->captures) > 0) {
update_post_meta($order_id, static::TABBY_STATUS_FIELD, static::STATUS_CAPTURED);
}
}
return $result;
}
public function get_tabby_payment($order_id) {
$payment_id = $this->get_tabby_payment_id($order_id);
return $this->request($payment_id);
}
public function process_refund( $order_id, $amount = null, $reason = '' ) {
try {
$order = wc_get_order( $order_id );
$payment_id = $this->get_tabby_payment_id($order->get_id());
$refunds = $order->get_refunds();
$logData = array(
"payment.id" => $payment_id,
"order.reference_id" => woocommerce_tabby_get_order_reference_id($order)
);
$this->ddlog("info", "refund payment", null, $logData);
$refund = false;
// get first refund with same amount and reason
foreach ($refunds as $ref) {
if (($ref->get_reason() == $reason) && ($ref->get_amount() == $amount) && !$ref->get_refunded_payment()) $refund = $ref;
}
if ( ! $refund) throw new \Exception( __('Cannot find refund object', 'tabby-checkout') );
if ( ! $this->can_refund_order( $order ) ) {
return new WP_Error( 'error', __( 'Refund failed.', 'tabby-checkout' ) );
}
$data = [
"capture_id" => $this->get_order_capture_id($order->get_id()),
"amount" => $this->formatAmount($refund->get_amount()),
"reason" => $refund->get_reason()
];
$data['items'] = [];
foreach ($refund->get_items() as $item) {
if ($item->get_quantity() == 0) continue;
$data['items'][] = [
'title' => $item->get_name(),
'description' => $item->get_name(),
'quantity' => (int)$item->get_quantity(),
'unit_price' => $this->formatAmount($item->get_total() / $item->get_quantity()),
'reference_id' => $item->get_product()->get_id() . ''
];
}
$this->debug(['refund', $payment_id, $data]);
$result = $this->request($payment_id . '/refunds', 'POST', $data);
$this->debug(['refund - result', (array)$result]);
$txn = array_pop($result->refunds);
if (!$txn) {
throw new \Exception( __("Something wrong", 'tabby-checkout'));
}
if ($txn->id) {
$order->add_order_note(
/* translators: %1$s is replaced with payment amount, %2$s is replaced with Tabby refund ID */
sprintf( __( 'Refunded %1$s - Refund ID: %2$s', 'tabby-checkout' ), $txn->amount, $txn->id )
);
update_post_meta($order->get_id(), static::TABBY_STATUS_FIELD, static::STATUS_REFUNDED);
return true;
}
return isset( $result->status ) ? new WP_Error( 'error', $result->error ) : false;
} catch (\Exception $e) {
$this->ddlog("error", "could not refund payment", $e);
}
}
protected function formatAmount($amount) {
return number_format($amount, wc_get_price_decimals(), '.', '');
}
public function request($endpoint, $method = 'GET', $data = null) {
return WC_Tabby_Api::request('payments/' . $endpoint, $method, $data);
}
protected function get_lang() {
$lang = strtolower(substr(get_locale(), 0, 2));
if (!in_array($lang, ['ar', 'en'])) $lang = 'en';
return $lang;
}
protected function debug($data) {
WC_Tabby_Api::debug($data);
}
protected function ddlog($status = "error", $message = "Something went wrong", $e = null, $data = null) {
WC_Tabby_Api::ddlog($status, $message, $e, $data);
}
}