388 lines
19 KiB
PHP
388 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* NOTICE OF LICENSE
|
|
*
|
|
* This source file is subject to the Academic Free License (AFL 3.0)
|
|
* that is bundled with this package in the file LICENSE.md
|
|
* It is also available through the world-wide-web at this URL:
|
|
* https://opensource.org/licenses/afl-3.0.php
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to support@qloapps.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade this module to a newer
|
|
* versions in the future. If you wish to customize this module for your needs
|
|
* please refer to https://store.webkul.com/customisation-guidelines for more information.
|
|
*
|
|
* @author Webkul IN
|
|
* @copyright Since 2010 Webkul
|
|
* @license https://opensource.org/licenses/afl-3.0.php Academic Free License 3.0
|
|
*/
|
|
|
|
class AdminPaypalCommerceTransactionController extends ModuleAdminController
|
|
{
|
|
public function __construct()
|
|
{
|
|
$this->identifier = 'id_paypal_commerce_order';
|
|
parent::__construct();
|
|
|
|
$this->bootstrap = true;
|
|
$this->list_no_link = true;
|
|
$this->_new_list_header_design = true;
|
|
|
|
$this->table = 'wk_paypal_commerce_order';
|
|
$this->className = 'WKPayPalCommerceOrder';
|
|
|
|
$this->_select = 'a.*, CONCAT(c.firstname, \' \', c.lastname) as customer_name, c.email,
|
|
CONCAT("[", GROUP_CONCAT(DISTINCT ord.`id_order`), "]") as id_orders';
|
|
|
|
$this->_join = ' LEFT JOIN `' . _DB_PREFIX_ . 'customer` c ON (c.id_customer = a.id_customer)';
|
|
$this->_join .= ' LEFT JOIN `' . _DB_PREFIX_ . 'orders` ord ON (a.`order_reference` = ord.`reference`)';
|
|
$this->_where = ' AND a.`id_cart` != 0';
|
|
$this->_orderBy = 'id_paypal_commerce_order';
|
|
$this->_defaultOrderWay = 'DESC';
|
|
|
|
$this->_group = ' GROUP BY a.`id_cart`';
|
|
|
|
$orderBy = Tools::getValue($this->table.'Orderby');
|
|
$orderWay = Tools::getValue($this->table.'Orderway');
|
|
|
|
if (!Validate::isOrderBy($orderBy)) {
|
|
$orderBy = 'id_paypal_commerce_order'; // fallback
|
|
}
|
|
|
|
if (!Validate::isOrderWay($orderWay)) {
|
|
$orderWay = 'DESC';
|
|
}
|
|
|
|
$this->_orderBy = pSQL($orderBy);
|
|
$this->_orderWay = pSQL($orderWay);
|
|
|
|
$this->toolbar_title = $this->l('PayPal Transactions');
|
|
|
|
$this->fields_list = array(
|
|
'order_reference' => array(
|
|
'title' => $this->l('Order Reference'),
|
|
'align' => 'center',
|
|
'havingFilter' => true,
|
|
'callback' => 'getOrderInfo',
|
|
'hint' => $this->l('Order reference in QloApps of the PayPal transaction'),
|
|
),
|
|
'pp_transaction_id' => array(
|
|
'title' => $this->l('PayPal Transaction ID'),
|
|
'align' => 'center',
|
|
'class' => 'fixed-width-xs',
|
|
'havingFilter' => true,
|
|
'hint' => $this->l('Id of the PayPal transaction'),
|
|
),
|
|
'pp_paid_total' => array(
|
|
'title' => $this->l('Transaction Total'),
|
|
'align' => 'center',
|
|
'havingFilter' => true,
|
|
'callback' => 'setCurrency',
|
|
'hint' => $this->l('Total amount paid in the PayPal transaction'),
|
|
),
|
|
'customer_name' => array(
|
|
'title' => $this->l('Customer'),
|
|
'align' => 'center',
|
|
'havingFilter' => true,
|
|
'align' => 'center',
|
|
'callback' => 'getCustomerInfo',
|
|
'havingFilter' => true,
|
|
'filter_key' => 'customer_name',
|
|
'hint' => $this->l('Customer in the QloApps who did PayPal transaction.'),
|
|
),
|
|
'pp_payment_status' => array(
|
|
'title' => $this->l('Status'),
|
|
'align' => 'center',
|
|
'class' => 'fixed-width-xs',
|
|
'hint' => $this->l('Payment Status'),
|
|
'orderBy' => false,
|
|
'hint' => $this->l('Current status of the PayPal transaction.'),
|
|
'callback' => 'getStatus',
|
|
'search' => false,
|
|
),
|
|
'order_date' => array(
|
|
'title' => $this->l('Order Date'),
|
|
'type' => 'datetime',
|
|
'align' => 'center',
|
|
'hint' => $this->l('Date of order creation.'),
|
|
)
|
|
);
|
|
}
|
|
|
|
public function getCustomerInfo($customerName, $row)
|
|
{
|
|
$safeCustomerName = Tools::safeOutput($customerName);
|
|
$safeEmail = Tools::safeOutput($row['email']);
|
|
return '<a href="'.$this->context->link->getAdminLink('AdminCustomers').'&id_customer='.(int)$row['id_customer'].'&viewcustomer">'.$safeCustomerName.'<br>'.'('.$safeEmail.')</a>';
|
|
}
|
|
|
|
public function getOrderInfo($reference, $row)
|
|
{
|
|
$safeReference = Tools::safeOutput($reference);
|
|
$str = $safeReference.'<br/>(';
|
|
$arrIdOrders = json_decode($row['id_orders']);
|
|
if (is_array($arrIdOrders)) {
|
|
$arrIdOrders = array_values(array_unique(array_map('intval', $arrIdOrders)));
|
|
foreach ($arrIdOrders as $key => $id_order) {
|
|
$str .= '<a target="_blank" href="'.$this->context->link->getAdminLink('AdminOrders').'&id_order='.$id_order.'&vieworder" >#'.$id_order.'</a>';
|
|
if ($key < count($arrIdOrders) - 1) {
|
|
$str .= ', ';
|
|
}
|
|
}
|
|
}
|
|
return $str.')';
|
|
}
|
|
|
|
/**
|
|
* Renders the "Status" list column as a colored badge for known PayPal
|
|
* payment statuses, falling back to plain escaped text for any other value.
|
|
* @param string $ppPaymentStatus Raw PayPal payment status stored on the transaction
|
|
* @return string HTML for the list cell
|
|
*/
|
|
public function getStatus($ppPaymentStatus)
|
|
{
|
|
$statusBadges = array(
|
|
WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_COMPLETED => array(
|
|
'class' => 'badge-success',
|
|
'label' => $this->l('Completed'),
|
|
),
|
|
WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_APPROVED => array(
|
|
'class' => 'badge-info',
|
|
'label' => $this->l('Approved'),
|
|
),
|
|
WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_PENDING => array(
|
|
'class' => 'badge-warning',
|
|
'label' => $this->l('Pending'),
|
|
),
|
|
WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_DENIED => array(
|
|
'class' => 'badge-danger',
|
|
'label' => $this->l('Denied'),
|
|
),
|
|
);
|
|
|
|
if (isset($statusBadges[$ppPaymentStatus])) {
|
|
return '<span class="badge '.$statusBadges[$ppPaymentStatus]['class'].'">'
|
|
.$statusBadges[$ppPaymentStatus]['label'].'</span>';
|
|
}
|
|
|
|
return Tools::safeOutput($ppPaymentStatus);
|
|
}
|
|
|
|
public function setCurrency($val, $row)
|
|
{
|
|
$objCart = new Cart($row['id_cart']);
|
|
$currency = new Currency($objCart->id_currency);
|
|
|
|
return Tools::displayPrice($val, $currency);
|
|
}
|
|
|
|
public function initPageHeaderToolbar()
|
|
{
|
|
if ($this->display == 'edit' || $this->display == 'add' || $this->display == 'view') {
|
|
$this->page_header_toolbar_btn['back_to_list'] = array(
|
|
'href' => Context::getContext()->link->getAdminLink(
|
|
'AdminPaypalCommerceTransaction',
|
|
true
|
|
),
|
|
'desc' => $this->l('Back to list'),
|
|
'icon' => 'process-icon-back'
|
|
);
|
|
}
|
|
parent::initPageHeaderToolbar();
|
|
}
|
|
|
|
public function renderList()
|
|
{
|
|
$this->addRowAction('view');
|
|
return parent::renderList();
|
|
}
|
|
|
|
public function initToolbar()
|
|
{
|
|
parent::initToolbar();
|
|
unset($this->toolbar_btn['new']);
|
|
}
|
|
|
|
public function renderView()
|
|
{
|
|
$idTrans = (int)Tools::getValue('id_paypal_commerce_order');
|
|
$objPPOrder = new WKPayPalCommerceOrder($idTrans);
|
|
if (Validate::isLoadedObject($objPPOrder)) {
|
|
$smartyVars = array();
|
|
$refundData = array();
|
|
$totalRefunded = 0;
|
|
$transactionData = WKPayPalCommerceOrder::getTransactionDetails((int)$idTrans);
|
|
$orderCurrency = new Currency((int)$transactionData['id_currency']);
|
|
$refundData = WkPaypalCommerceRefund::getRefundListByTransID((int)$idTrans);
|
|
$totalRefundedFormatted = WkPaypalCommerceRefund::getTotalRefundedAmount((int)$idTrans, true);
|
|
|
|
$totalRefunded = WkPaypalCommerceRefund::getTotalRefundedAmount((int)$idTrans, false);
|
|
$remainingRefund = (float)($transactionData['pp_paid_total'] - $totalRefunded);
|
|
$response = json_decode($transactionData['response'], true);
|
|
|
|
// Buyer making a payment in a different currency (ex: EUR) which is different from the default currency of merchant (Ex: USD), In all those cross currency cases, After Capture, transaction will fall into Pending state and will require merchant to manually go to his PayPal account and accept the payment.
|
|
if (isset($response['data']['purchase_units'][0]['payments']['captures'][0]['status_details']['reason'])
|
|
&& ($transactionData['pp_payment_status'] == WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_PENDING
|
|
|| $transactionData['pp_payment_status'] == WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_DENIED)
|
|
) {
|
|
if (isset($objPPOrder->ppStatusDetail[$response['data']['purchase_units'][0]['payments']['captures'][0]['status_details']['reason']])) {
|
|
$smartyVars['ppstatusDetailMsg'] = $objPPOrder->ppStatusDetail[$response['data']['purchase_units'][0]['payments']['captures'][0]['status_details']['reason']];
|
|
}
|
|
}
|
|
|
|
$orderLinks = array();
|
|
if ($transactionData['id_cart']) {
|
|
$orders = Order::getByReference($transactionData['order_reference']);
|
|
foreach ($orders as $order) {
|
|
$orderLinks[] = array(
|
|
'id_order' => (int) $order->id,
|
|
'link' => $this->context->link->getAdminLink('AdminOrders')
|
|
. '&id_order=' . (int) $order->id
|
|
. '&vieworder',
|
|
);
|
|
}
|
|
}
|
|
|
|
$smartyVars['transaction_data'] = $transactionData;
|
|
$smartyVars['order_links'] = $orderLinks;
|
|
$smartyVars['refund_data'] = $refundData;
|
|
$smartyVars['refunded_amount'] = $totalRefundedFormatted;
|
|
$smartyVars['remaining_refund'] = $remainingRefund;
|
|
$smartyVars['remaining_refund_format'] = Tools::displayPrice($remainingRefund, $orderCurrency);
|
|
$smartyVars['currency'] = $orderCurrency;
|
|
$smartyVars['WK_PAYPAL_COMMERCE_REFUND_TYPE_FULL'] = WkPaypalCommerceRefund::WK_PAYPAL_COMMERCE_REFUND_TYPE_FULL;
|
|
$smartyVars['WK_PAYPAL_COMMERCE_REFUND_TYPE_PARTIAL'] = WkPaypalCommerceRefund::WK_PAYPAL_COMMERCE_REFUND_TYPE_PARTIAL;
|
|
$smartyVars['WK_PAYPAL_COMMERCE_PAYMENT_STATUS_COMPLETED'] = WkPaypalCommerceWebhook::WK_PAYPAL_COMMERCE_PAYMENT_STATUS_COMPLETED;
|
|
|
|
$this->context->smarty->assign($smartyVars);
|
|
$this->base_tpl_view = 'view.tpl';
|
|
return parent::renderView();
|
|
} else {
|
|
Tools::redirectAdmin(self::$currentIndex . '&token=' . $this->token);
|
|
}
|
|
}
|
|
|
|
public function postProcess()
|
|
{
|
|
|
|
if (Tools::getValue('refund_paypal_form')
|
|
&& (Tools::getValue('token')) == Tools::getAdminTokenLite('AdminPaypalCommerceTransaction')
|
|
) {
|
|
$idTrans = (int)Tools::getValue('id_paypal_commerce_order');
|
|
$refundAmt = (float)Tools::getValue('refund_amount');
|
|
$refundReason = Tools::getValue('refund_reason');
|
|
$refundType = (int)Tools::getValue('refund_type');
|
|
|
|
if (!$refundType) {
|
|
$this->errors[] = $this->l('Invalid refund type.');
|
|
}
|
|
if (!Validate::isPrice($refundAmt)) {
|
|
$this->errors[] = $this->l('Invalid refund amount.');
|
|
}
|
|
if (!Validate::isMessage($refundReason)) {
|
|
$this->errors[] = $this->l('Enter valid refund reason (only alphanumeric allowed).');
|
|
}
|
|
|
|
if ((!count($this->errors))) {
|
|
$transactionData = WKPayPalCommerceOrder::getTransactionDetails((int)$idTrans);
|
|
if ($transactionData) {
|
|
$totalRefunded = WkPaypalCommerceRefund::getTotalRefundedAmount((int)$idTrans, false);
|
|
if ($remainingRefund = (float)($transactionData['pp_paid_total'] - $totalRefunded)) {
|
|
// if refund type is partial then check the amount to be refunded
|
|
if (($refundType == WkPaypalCommerceRefund::WK_PAYPAL_COMMERCE_REFUND_TYPE_PARTIAL)
|
|
&& ($refundAmt > $remainingRefund)
|
|
) {
|
|
$orderCurrency = new Currency((int)$transactionData['id_currency']);
|
|
$this->errors[] = $this->l('Invalid refund amount. Max. available amount for refund is').' '.Tools::displayPrice($remainingRefund, $orderCurrency);
|
|
}
|
|
|
|
// if no errors the proceed for the refund
|
|
if ((!count($this->errors))) {
|
|
if ($refundType == WkPaypalCommerceRefund::WK_PAYPAL_COMMERCE_REFUND_TYPE_FULL) {
|
|
$refundAmt = (float)($transactionData['pp_paid_total'] - $totalRefunded);
|
|
}
|
|
if ($transactionData['pp_paid_total'] == $refundAmt) {
|
|
$refundType = WkPaypalCommerceRefund::WK_PAYPAL_COMMERCE_REFUND_TYPE_FULL;
|
|
}
|
|
if ($refundAmt) {
|
|
$postData = array();
|
|
$postData['amount'] = array(
|
|
'currency_code' => $transactionData['pp_paid_currency'],
|
|
'value' => number_format(Tools::ps_round($refundAmt, 2), 2, '.', '')
|
|
);
|
|
$postData['transaction_id'] = $transactionData['pp_transaction_id'];
|
|
$postData['refund_reason'] = $refundReason;
|
|
$postData['auth_assertion'] = WkPaypalCommerceHelper::payPalAuthAssertion();
|
|
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Refund initiated...', true);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Environment: '. Configuration::get('MP_PAYPAL_PAYMENT_MODE'));
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Order Ref: '. $transactionData['order_reference']);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Customer ID: '. $transactionData['id_customer']);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'PayPal Transaction ID: '. $transactionData['pp_transaction_id']);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'PayPal Order ID: '. $transactionData['pp_order_id']);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Refund request data: ');
|
|
WkPaypalCommerceHelper::logMsg('refund', json_encode($postData));
|
|
|
|
$objPPCommerce = new PayPalCommerce();
|
|
$refundData = $objPPCommerce->orders->refund($postData);
|
|
|
|
// check if refund id is there
|
|
if (isset($refundData['data']['status']) && isset($refundData['data']['id'])) {
|
|
$refundID = $refundData['data']['id'];
|
|
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Refund success: ', true);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'PayPal Refund Id: '. $refundID);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Refund reponse data: ');
|
|
WkPaypalCommerceHelper::logMsg('refund', json_encode($refundData));
|
|
WkPaypalCommerceHelper::logMsg('refund', '----------------------- ', true);
|
|
|
|
$refundObj = new WkPaypalCommerceRefund();
|
|
$refundObj->order_trans_id = (int)$idTrans;
|
|
$refundObj->paypal_refund_id = $refundID;
|
|
$refundObj->refund_amount = (float)$refundAmt;
|
|
$refundObj->refund_type = (int)$refundType;
|
|
$refundObj->currency_code = $transactionData['pp_paid_currency'];
|
|
$refundObj->refund_reason = $refundReason;
|
|
$refundObj->response = json_encode($refundData);
|
|
$refundObj->refund_status = $refundData['data']['status'];
|
|
if ($refundObj->save()) {
|
|
$urlString = '&viewwk_paypal_commerce_order=&id_paypal_commerce_order=' . (int)Tools::getValue('id_paypal_commerce_order');
|
|
|
|
Tools::redirectAdmin(self::$currentIndex.$urlString.'&conf=4&token='.$this->token);
|
|
}
|
|
} else {
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Refund failed: ', true);
|
|
WkPaypalCommerceHelper::logMsg('refund', 'Refund reponse data: ');
|
|
WkPaypalCommerceHelper::logMsg('refund', json_encode($refundData));
|
|
WkPaypalCommerceHelper::logMsg('refund', '----------------------- ', true);
|
|
$this->errors[] = $refundData['data']['message'];
|
|
}
|
|
} else {
|
|
$this->errors[] = $this->l('Refund is already done for this transaction.');
|
|
}
|
|
}
|
|
} else {
|
|
$this->errors[] = $this->l('Refund is already done for this transaction.');
|
|
}
|
|
} else {
|
|
$this->errors[] = $this->l('Invalid request');
|
|
}
|
|
}
|
|
}
|
|
|
|
parent::postProcess();
|
|
}
|
|
|
|
public function setMedia()
|
|
{
|
|
parent::setMedia();
|
|
|
|
$this->addJS(_PS_MODULE_DIR_ . 'qlopaypalcommerce/views/js/admin/wk_paypal_transaction.js');
|
|
$this->addCSS(_PS_MODULE_DIR_ . 'qlopaypalcommerce/views/css/admin/wk_paypal_transaction.css');
|
|
}
|
|
}
|