Commit ede4729c authored by Daniel Kazior's avatar Daniel Kazior
Browse files

Merge branch 'develop' into 'master'

Release 3.3.0

See merge request !40
parents 81115e81 8f0abb5e
......@@ -3,7 +3,7 @@
* Plugin Name: CrefoPay Payment Solution
* Plugin URI: https://repo.crefopay.de/crefopay/woocommerce
* Description: The complete solution for your eCommerce payments.
* Version: 3.2.1
* Version: 3.3.0
* Requires at least: 5.1
* Requires PHP: 7.2
* Author: Shakuras GbR
......@@ -124,11 +124,17 @@ define('PAYMENT_METHOD_SU', 'SU');
define('PHONE_REGEX', '/^(((((((00|\+)[0-9]{2}[ \\\\\-\/]?)|0)[1-9][0-9]{1,4})[ \\\\\-\/]?)|((((00|\+)[0-9]{2}\()|\(0)[1-9][0-9]{1,4}\)[ \\\\\-\/]?))[0-9]{1,7}([ \\\\\-\/]?[0-9]{1,5})?)$/');
/**
* define gklobal log level
*/
$options = get_option('igp_settings');
define( 'WC_LOG_THRESHOLD', $options['crefopay_log_level'] );
/**
* Main Class of the CrefoPay payment plugin
*
* @class CrefoPay_Payment_Solution
* @version 3.2.1
* @version 3.3.0
* @package CrefoPay-Payment-Solution
*/
class Crefopay_Payment_Solution
......
......@@ -13,7 +13,7 @@ require_once plugin_dir_path(__FILE__) . '../controllers/class-wc-api-callback-p
*
* @class WC_API_CrefoPay_Api_Callback
* @extends WC_API
* @version 3.2.1
* @version 3.3.0
* @package CrefoPay-Payment-Solution\Callbacks
*/
class WC_API_CrefoPay_Api_Callback extends WC_API
......@@ -29,6 +29,7 @@ class WC_API_CrefoPay_Api_Callback extends WC_API
public function __construct()
{
$this->logger = wc_get_logger('class-wc-api-callback');
add_action('woocommerce_api_success_callback', array($this, 'success_callback_handler'));
add_action('woocommerce_api_failure_callback', array($this, 'failure_callback_handler'));
}
......
......@@ -16,7 +16,7 @@ use Upg\Library\Mns\Handler;
*
* @class WC_API_Crefo_Notification_Callback
* @extends WC_API
* @version 3.2.1
* @version 3.3.0
* @package CrefoPay-Payment-Solution\Callbacks
*/
class WC_API_Crefo_Notification_Callback extends WC_API
......
......@@ -12,7 +12,7 @@ require_once plugin_dir_path(__FILE__) . '../controllers/class-wc-crefopay.php';
* Process redirect payments
*
* @class WC_API_CrefoPay_Api_Callback
* @version 3.2.1
* @version 3.3.0
* @package CrefoPay-Payment-Solution\Controllers
*/
class CrefoPay_API_Callback_Processor
......
<?php
/**
* CrefoPay Main
*
......@@ -10,6 +11,7 @@ defined('ABSPATH') or die('No script kiddies please!');
* require CrefoPay clientLibrary
*/
require_once plugin_dir_path(WP_PLUGIN_DIR . '/crefopay-payment-solution/crefopay-payment-solution.php') . 'vendor/autoload.php';
require_once plugin_dir_path(__FILE__) . '../controllers/class-wc-helper-categories-roles.php';
require_once plugin_dir_path(__FILE__) . '../gateways/class-wc-crefopay-gateway-exception.php';
require_once plugin_dir_path(__FILE__) . '../models/class-wc-crefopay-error.php';
require_once plugin_dir_path(__FILE__) . '../models/class-wc-crefopay-transaction.php';
......@@ -43,8 +45,10 @@ use Upg\Library\Request\Objects\Person;
use Upg\Library\Request\Refund as RefundRequest;
use Upg\Library\Request\RegisterUserPaymentInstrument as RegisterPaymentInstrument;
use Upg\Library\Request\Reserve as ReserveRequest;
use Upg\Library\Risk\RiskClass as RiskClass;
use Upg\Library\User\Type as UserType;
use Upg\Library\Validation\Validation;
use Upg\Library\Integration\Type as IntegrationType;
/**
* Class CrefoPay implements singleton pattern.
......@@ -52,7 +56,7 @@ use Upg\Library\Validation\Validation;
*
* @class CrefoPay
* @autoloads Upg\Library
* @version 3.2.1
* @version 3.3.0
* @package CrefoPay-Payment-Solution\Controllers
*/
class CrefoPay
......@@ -61,7 +65,6 @@ class CrefoPay
/**
* CrefoPay Constants
*/
const DEFAULT_RISK_CLASS = 1.0;
const LOG_LOCATION_CALLBACKS = ".wp-content/uploads/wc-logs/crefoCallbacks.log";
const LOG_LOCATION_MAIN = ".wp-content/uploads/wc-logs/crefoMain.log";
const LOG_LOCATION_MNS = ".wp-content/uploads/wc-logs/crefoMNS.log";
......@@ -214,7 +217,7 @@ class CrefoPay
$this->transaction
->setBasketValidity($this->options['crefopay_basket_validity'] . 'h')
->setContext(CreateTransactionRequest::CONTEXT_ONLINE)
->setIntegrationType(CreateTransactionRequest::INTEGRATION_TYPE_SECURE_FIELDS);
->setIntegrationType(IntegrationType::INTEGRATION_TYPE_SECURE_FIELDS);
}
/**
......@@ -269,9 +272,12 @@ class CrefoPay
public function getSecureFieldsUrl(string $type)
{
switch ($type) {
case 'frontend':return $this->_getCrefoPayDomain($this->options['crefopay_mode']) . '/libs/3.0';
case 'backend':return $this->_getCrefoPayDomain($this->options['crefopay_mode']) . '/secureFields';
default:return '';
case 'frontend':
return $this->_getCrefoPayDomain($this->options['crefopay_mode']) . '/libs/3.0';
case 'backend':
return $this->_getCrefoPayDomain($this->options['crefopay_mode']) . '/secureFields';
default:
return '';
}
}
......@@ -340,6 +346,31 @@ class CrefoPay
$this->request->setAutoCapture(true);
}
$helper = new CrefoPay_CRHelper();
// enable auto capture based on customer role
if ($helper->enable_auto_capture_by_role($this->options['crefopay_auto_capture_group'])) {
$this->request->setAutoCapture(true);
}
// enable auto capture based on product categories
if ($helper->enable_auto_capture_by_category($this->options['crefopay_auto_capture_category'])) {
$this->request->setAutoCapture(true);
}
switch ($helper->get_user_risk_class($this->options['crefopay_blacklist'], $this->options['crefopay_whitelist'])) {
// blacklist
case 'black':
$this->request->setUserRiskClass(RiskClass::RISK_CLASS_HIGH);
break;
// whitelist
case 'white':
$this->request->setUserRiskClass(RiskClass::RISK_CLASS_TRUSTED);
break;
default:
# code...
break;
}
// set the locale
if ($this->options['crefopay_default_locale'] == 'AL') {
$this->request->setLocale($this->_parseLocale(get_user_locale(wp_get_current_user())));
......@@ -414,7 +445,7 @@ class CrefoPay
switch ((int) $this->response->getData("resultCode")) {
case 1:
$this->redirectUrl = $this->response->getData("redirectUrl");
// no break
// no break
case 0:
$this->creationTime = time();
$this->logger->debug("CrefoPay transaktion creation successfully");
......@@ -438,8 +469,8 @@ class CrefoPay
if ($this->transaction->getIntegrationType() != 'API') {
$this->logger->info(
"Order ID already exists: integrationType " .
$this->transaction->getIntegrationType() .
" doesn't match."
$this->transaction->getIntegrationType() .
" doesn't match."
);
throw new WC_Crefo_Exception(new WC_Crefo_Error([
'message' => "PaymentMethod not correct.",
......@@ -499,7 +530,7 @@ class CrefoPay
}
} catch (ApiError $a) {
switch ($a->getCode()) {
// 2013: The bank account is already stored for this user.
// 2013: The bank account is already stored for this user.
case 2013:
$error = new WC_Crefo_Error([
'message' => $a->getMessage(),
......@@ -923,14 +954,9 @@ class CrefoPay
}
}
if ((int) $this->options['crefopay_overcapture_factor'] > 0) {
$amount->setAmount(round($amount->getAmount() * $this->options['crefopay_overcapture_factor']));
}
try {
$this->request = new ReserveRequest($this->config);
$this->request
->setAmount($amount)
->setOrderID($this->transaction->getOrderID())
->setPaymentMethod($paymentMethod);
......@@ -968,11 +994,11 @@ class CrefoPay
$this->response = $api->sendRequest();
$this->resultCode = (int) $this->response->getData("resultCode");
switch ($this->resultCode) {
// reservation complete
// reservation complete
case 0:
$this->logger->debug("CrefoPay transaktion successfully reserved");
break;
// redirect required
// redirect required
case 1:
$this->logger->debug("CrefoPay transaktion has to be redirected");
if ($this->_setRedirectUrl($this->response->getData("redirectUrl"))) {
......@@ -995,7 +1021,11 @@ class CrefoPay
throw new WC_Crefo_Exception(new WC_Crefo_Error($this->response->getData('errorDetails')), 'API');
}
} catch (ApiError $a) {
$this->logger->error("Reserve fails with ApiError: " . $a->getMessage() . '(' . $a->getCode() . ')');
if ($a->getCode() == '1002') {
$this->logger->info("Reserve fails with ApiError: " . $a->getMessage() . '(' . $a->getCode() . ')');
} else {
$this->logger->error("Reserve fails with ApiError: " . $a->getMessage() . '(' . $a->getCode() . ')');
}
$this->logger->debug(print_r($this->request, true));
$error = new WC_Crefo_Error([
'message' => $a->getMessage(),
......@@ -1033,33 +1063,62 @@ class CrefoPay
global $woocommerce;
$basket = array();
$cart = $woocommerce->cart;
$helper = new CrefoPay_CRHelper();
$ocCategories = [];
$totalOcAmount = 0;
$cartTotal = round($cart->total * 100);
if (!empty($this->options['crefopay_overcapture_categories'])) {
$categories = explode(',', $this->options['crefopay_overcapture_categories']);
foreach ($categories as $category) {
$ocCategories[] = trim($category);
}
}
$this->logger->debug("start cart data initialization");
try {
// Amount
$this->transaction->setAmount(new Amount(round($cart->total * 100)));
// Basket items
foreach ($cart->get_cart_contents() as $item) {
$itemAmount = round($item['line_total'] + $item['line_tax'], 2) * 100;
if (empty($this->options['crefopay_overcapture_unit'])) {
$ocAmount = 0;
} else {
$ocAmount = $helper->get_overcapture_basket_amount_by_categories_and_unit(
$item,
$ocCategories,
$this->options['crefopay_overcapture_unit']
);
}
$basketItemAmount = new Amount();
$basketItemAmount
->setAmount(round($item['line_total'] * 100));
if ($ocAmount > 0) {
$totalOcAmount += $ocAmount;
$itemTextPrefix = '(' . number_format($itemAmount / 100, 2) . ' + ' . round(((float) $this->options['crefopay_overcapture_factor'] - 1 ) * 100) . '%) ';
$basketItemAmount->setAmount($itemAmount + $ocAmount);
} else {
$itemTextPrefix = '';
$basketItemAmount->setAmount($itemAmount);
}
$basketItem = new BasketItem();
$basketItem
->setBasketItemAmount($basketItemAmount)
->setBasketItemCount($item['quantity'])
->setBasketItemText($item['data']->get_title())
->setBasketItemText($itemTextPrefix . $item['data']->get_title())
->setBasketItemType(BasketItemType::BASKET_ITEM_TYPE_DEFAULT);
$basket[] = $basketItem;
}
// Coupons
foreach ($cart->get_coupons() as $item) {
$this->logger->debug(print_r($item->get_data(), true));
$itemAmount = $this->get_coupon_amount($item, (int) $cartTotal);
$basketItemAmount = new Amount();
$basketItemAmount
->setAmount(round($item->get_amount() * 100));
$basketItemAmount->setAmount($itemAmount);
$basketItem = new BasketItem();
$basketItemText = empty($item->get_description()) ? __('COUPON', 'crefopay-payment-solution') : $item->get_description();
......@@ -1072,9 +1131,9 @@ class CrefoPay
}
// Shipping costs
$itemAmount = round($cart->get_shipping_total() * 100);
$basketItemAmount = new Amount();
$basketItemAmount
->setAmount(round($cart->get_shipping_total() * 100));
$basketItemAmount->setAmount($itemAmount);
$basketItem = new BasketItem();
$basketItem
......@@ -1086,6 +1145,10 @@ class CrefoPay
// Add whole basket to transaction
$this->transaction->setBasketItems($basket);
// Total amount calculation
$this->transaction->setAmount(new Amount($cartTotal + $totalOcAmount));
$this->logger->debug("cart data initialization completed\n" . print_r($basket, true));
} catch (\Exception $e) {
$this->logger->debug("cart data initialization failed with exception: " . $e->getMessage());
......@@ -1093,6 +1156,28 @@ class CrefoPay
}
}
/**
* Get the amount of a coupon
*
* @param WC_Coupon $coupon
* @param int $amount
*
* @return int
*/
private function get_coupon_amount($coupon, $amount)
{
switch ($coupon->get_discount_type()) {
case 'percent':
# coupon is in percent
$this->logger->info("Coupon type is percent");
return round($amount * ($coupon->get_amount()) / 100);
default:
# all other types
return round($coupon->get_amount() * 100);
}
}
/**
* Initialize Customer data
*
......@@ -1358,7 +1443,7 @@ class CrefoPay
case 'Mrs.':
return false;
// validation complete and business user
// validation complete and business user
default:
return true;
}
......
<?php
/**
* CrefoPay Helper for Categories and Roles
*
* prevent unauthorized access
*/
defined('ABSPATH') or die('No script kiddies please!');
class CrefoPay_CRHelper
{
/**
* Plugin Options
*
* @var array
*/
private $options;
/**
* Plugin Logger
*
* @var WooCommerce\WC_Logger
*/
private $logger;
/**
* Constructor
*/
public function __construct()
{
$this->options = get_option('igp_settings');
$this->logger = wc_get_logger('crefopay-helper-categories-roles');
}
/**
* Get category names
*
* @return array
*/
public function get_category_names(): array
{
$orderby = 'name';
$order = 'asc';
$hide_empty = false;
$cat_args = array(
'orderby' => $orderby,
'order' => $order,
'hide_empty' => $hide_empty,
);
$product_categories = get_terms('product_cat', $cat_args);
$values = array();
foreach ($product_categories as $cat) {
$values[$cat->slug] = $cat->name;
}
return $values;
}
/**
* Get role names
*
* @return array
*/
public function get_role_names(): array
{
if (function_exists('wp_roles')) {
$roles = wp_roles();
} else {
return [];
}
$values = array();
foreach ($roles->roles as $key => $value) {
$values[$key] = $value['name'];
}
return $values;
}
/**
* The function takes an item, overcapture categories and a unit to calculate the additional overcapture amount
*
* @param array $item
* @param array $categories
* @param string $unit
* @return int
*/
public function get_overcapture_basket_amount_by_categories_and_unit(array $item, array $categories, string $unit): int
{
$itemAmount = round($item['line_total'] + $item['line_tax'], 2) * 100;
$overcapture = false;
if (get_post_meta($item['product_id'], '_unit', true) == $unit) {
foreach ($categories as $category) {
// add to overcapture amount
if (has_term($category, 'product_cat', $item['product_id'])) {
$overcapture = true;
$this->logger->info("overcapture article");
}
}
} else {
$this->logger->info("non overcapture article");
}
if ($overcapture && (float) $this->options['crefopay_overcapture_factor'] > 1.0) {
$ocAmount = round($itemAmount * (float) $this->options['crefopay_overcapture_factor']);
return $ocAmount - $itemAmount;
} else {
return 0;
}
}
/**
* The function takes a role and compares it with the current user role(s).
*
* @param string $role
* @return boolean
*/
public function enable_auto_capture_by_role(string $role): bool
{
// Check if user is logged in
if (is_user_logged_in()) {
$user = wp_get_current_user();
// return true, if user role is marked for auto capture
return in_array($role, (array) $user->roles);
} else {
return false;
}
}
/**
* The function takes a category and compares it with the current basket;
* returns true, if all basket items are within the provided category.
*
* @param string $category
* @return boolean
*/
public function enable_auto_capture_by_category(string $category): bool
{
// Loop through all products in the Cart
foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
// return false if category is not part of this cart item
if (!has_term($category, 'product_cat', $cart_item['product_id'])) {
$this->logger->debug("Category ID for {$cart_item_key} => {$cart_item['product_id']} is not part of this cart item");
return false;
}
}
return true;
}
/**
* This function takes a blacklist and whitelist role and compares current user;
* returns 'black', 'white' or 'none' based on user group and config settings.
*
* @param string $blacklist
* @param string $whitelist
* @return string
*/
public function get_user_risk_class(string $blacklist, string $whitelist): string
{
// Check if user is logged in
if (is_user_logged_in()) {
$user = wp_get_current_user();
} else {
return 'none';
}
// check black list group before whitelist
if (in_array($blacklist, (array) $user->roles)) {
return 'black';
} elseif (in_array($whitelist, (array) $user->roles)) {
return 'white';
} else {
return 'none';
}
}
/**
* Returns a new WooCommerce order state based on special config.
*
* @param WooCommerce\WC_Order
* @return string
*/
public function get_newstate_by_categories($order): string
{
$product_categories = array();
$items = $order->get_items();
foreach ($items as $item) {
$terms = get_the_terms($item['product_id'], 'product_cat');
foreach ($terms as $term) {
if ($term->parent == 0) {
$product_cat_slug = $term->slug;
array_push($product_categories, $product_cat_slug);
} else {
$tmp = get_term_top_most_parent($term->parent, 'product_cat');
array_push($product_categories, $tmp->slug);
}
}
}
$product_categories = array_unique($product_categories);
$allowed_categories = explode(',', $this->options['crefopay_categories_for_orderstate']);
$allowed_categories = array_map('trim', $allowed_categories);
$this->logger->info("Now compare product categories with allowed categories...");
$this->logger->info("Allowed categories: " . print_r($allowed_categories, true));
$this->logger->info("Product categories: " . print_r($product_categories, true));
$containsSearch = count(array_intersect($product_categories, $allowed_categories)) == count($product_categories);
if ($containsSearch) {
$this->logger->info("All product categories are allowed categories");
return $this->options['crefopay_orderstate_by_categories'];