Files
wyczarujprezent.pl/modules/paypal/classes/Webhook/WebhookEventHandler.php
2024-10-28 22:14:22 +01:00

408 lines
11 KiB
PHP

<?php
/**
* 2007-2022 PayPal
*
* 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.txt.
* It is also available through the world-wide-web at this URL:
* http://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 license@prestashop.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
* versions in the future. If you wish to customize PrestaShop for your
* needs please refer to http://www.prestashop.com for more information.
*
* @author 2007-2022 PayPal
* @author 202 ecommerce <tech@202-ecommerce.com>
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* @copyright PayPal
*/
namespace PaypalAddons\classes\Webhook;
use Configuration;
use Context;
use Db;
use DbQuery;
use Employee;
use Exception;
use PayPal\Api\WebhookEvent;
use PaypalAddons\classes\Constants\WebHookType;
use PaypalAddons\services\ActualizeTotalPaid;
use PaypalAddons\services\PaymentTotalAmount;
use PaypalAddons\services\ServicePaypalOrder;
use PaypalAddons\services\StatusMapping;
use PaypalAddons\services\WebhookService;
use PaypalCapture;
use PaypalOrder;
use PaypalPPBTlib\Extensions\ProcessLogger\ProcessLoggerHandler;
use PaypalWebhook;
use Shop;
use Tools;
use Validate;
class WebhookEventHandler
{
protected $servicePaypalOrder;
protected $context;
public function __construct()
{
$this->servicePaypalOrder = new ServicePaypalOrder();
$this->context = Context::getContext();
}
public function handle(WebhookEvent $event)
{
if ($this->alreadyHandled($event)) {
return true;
}
$this->init();
ProcessLoggerHandler::openLogger();
$msg = 'Webhook event : ' . $this->jsonEncode([
'event_type' => $event->getEventType(),
'webhook_id' => $event->getId(),
'data' => $event->toArray(),
]);
$msg = Tools::substr($msg, 0, 999);
if ($event->resource->status != 'COMPLETED') {
ProcessLoggerHandler::logInfo(
$msg,
empty($event->getResource()->id) ? '' : $event->getResource()->id,
null,
null,
null,
'PayPal',
(int) Configuration::get('PAYPAL_SANDBOX')
);
ProcessLoggerHandler::closeLogger();
return true;
}
$paypalOrder = $this->initPaypalOrder($event);
if (Validate::isLoadedObject($paypalOrder) == false) {
return false;
}
$orders = $this->servicePaypalOrder->getPsOrders($paypalOrder);
$psOrderStatus = $this->getPsOrderStatus($event);
foreach ($orders as $order) {
//If there are several shops, then PayPal sends webhook event to each shop. The module should
//handle the event once.
if ($this->isMultishop()) {
if ($order->id_shop != $this->context->shop->id) {
return false;
}
}
ProcessLoggerHandler::logInfo(
$msg,
empty($event->getResource()->id) ? '' : $event->getResource()->id,
$order->id,
$order->id_cart,
$order->id_shop,
'PayPal',
(int) Configuration::get('PAYPAL_SANDBOX')
);
if ($psOrderStatus == 0) {
continue;
}
if ($order->current_state == $psOrderStatus) {
continue;
}
if ($event->getEventType() == WebHookType::CAPTURE_COMPLETED) {
if ($order->current_state != $this->getStatusMapping()->getWaitValidationStatus()) {
continue;
}
}
if ($this->getStatusMapping()->getRefundStatus() == $psOrderStatus) {
$paypalOrder->payment_status = 'refunded';
$paypalOrder->save();
}
$order->setCurrentState($psOrderStatus);
}
ProcessLoggerHandler::closeLogger();
if ($this->isCaptureAuthorization($event)) {
$capture = PaypalCapture::loadByOrderPayPalId($paypalOrder->id);
if (Validate::isLoadedObject($capture)) {
$capture->id_capture = empty($event->getResource()->id) ? '' : $event->getResource()->id;
$capture->result = empty($event->getResource()->status) ? '' : $event->getResource()->status;
$capture->capture_amount = $this->getAmount($event);
$capture->save();
}
$this->actualizeOrder($paypalOrder, $event);
}
if ($psOrderStatus == $this->getStatusMapping()->getCanceledStatus()) {
$this->removeEventInWaiting($paypalOrder);
}
$paypalWebhook = $this->getWebhookService()->createForOrder($paypalOrder, $psOrderStatus);
$paypalWebhook->id_webhook = $event->getId();
$paypalWebhook->event_type = $event->getEventType();
$paypalWebhook->data = $event->toJSON();
$paypalWebhook->date_completed = date(PaypalWebhook::DATE_FORMAT);
$paypalWebhook->save();
if ($psOrderStatus == $this->getStatusMapping()->getAcceptedStatus()) {
$this->servicePaypalOrder->setTransactionId($paypalOrder, $event->getResource()->id);
}
return true;
}
protected function alreadyHandled(WebhookEvent $event)
{
$query = (new DbQuery())
->from(PaypalWebhook::$definition['table'])
->where('id_webhook = \'' . pSQL($event->getId()) . '\'')
->select(PaypalWebhook::$definition['primary']);
try {
return (bool) Db::getInstance()->getValue($query);
} catch (Exception $e) {
return false;
}
}
protected function initPaypalOrder(WebhookEvent $event)
{
if (false == empty($event->getResource()->supplementary_data->related_ids->order_id)) {
$paymentId = $event->getResource()->supplementary_data->related_ids->order_id;
return $this->servicePaypalOrder->getPaypalOrderByPaymentId($paymentId);
}
$transaction = $this->getTransactionRef($event);
if (false == empty($transaction)) {
return $this->servicePaypalOrder->getPaypalOrderByTransaction($transaction);
}
return new PaypalOrder();
}
protected function getTransactionRef(WebhookEvent $event)
{
if ($this->isCaptureAuthorization($event)) {
return $this->getAuthorizationId($event);
}
if ($event->getEventType() == WebHookType::CAPTURE_REFUNDED) {
foreach ($event->getResource()->links as $link) {
if ($link->rel == 'up') {
return $this->getTransactionFromHref($link->href);
}
}
}
return empty($event->getResource()->id) ? '' : (string) $event->getResource()->id;
}
/**
* @param mixed $data
*
* @return bool
*/
protected function isCaptureAuthorization(WebhookEvent $event)
{
try {
return (bool) $this->getAuthorizationId($event);
} catch (Exception $e) {
return false;
}
}
/**
* @param mixed $data
*
* @return string
*/
protected function getAuthorizationId(WebhookEvent $event)
{
try {
return $event->getResource()->supplementary_data->related_ids->authorization_id;
} catch (Exception $e) {
return '';
}
}
/**
* @param mixed $data
*
* @return float
*/
protected function getAmount(WebhookEvent $event)
{
try {
return (float) $event->getResource()->amount->value;
} catch (Exception $e) {
return 0;
}
}
protected function getWebhookService()
{
return new WebhookService();
}
protected static function isInWhitelistForGeolocation()
{
return true;
}
protected function displayMaintenancePage()
{
}
protected function actualizeOrder(PaypalOrder $paypalOrder, WebhookEvent $webhookEvent)
{
$orders = $this->servicePaypalOrder->getPsOrders($paypalOrder);
if (count($orders) > 1) {
// todo: implement
return;
}
$order = array_shift($orders);
try {
$totalPaid = $webhookEvent->resource->amount->value;
} catch (Exception $e) {
return;
}
$this->getActualizeTotalPaid()->actualize($order, $totalPaid);
}
/**
* @return ActualizeTotalPaid
*/
protected function getActualizeTotalPaid()
{
return new ActualizeTotalPaid();
}
protected function removeEventInWaiting(PaypalOrder $paypalOrder)
{
$webhookEvents = $this->getWebhookService()->getPendingWebhooks($paypalOrder);
if (empty($webhookEvents)) {
return;
}
foreach ($webhookEvents as $webhookEvent) {
$webhookEvent->delete();
}
}
protected function jsonEncode($value)
{
$result = json_encode($value);
if (json_last_error() == JSON_ERROR_UTF8) {
$result = json_encode($this->utf8ize($value));
}
return $result;
}
protected function utf8ize($mixed)
{
if (is_array($mixed)) {
foreach ($mixed as $key => $value) {
$mixed[$key] = $this->utf8ize($value);
}
} elseif (is_string($mixed)) {
return utf8_encode($mixed);
}
return $mixed;
}
/**
* @param string $href
*
* @return string
*/
protected function getTransactionFromHref($href)
{
$tmp = explode('/', $href);
return (string) array_pop($tmp);
}
protected function getPsOrderStatus(WebhookEvent $event)
{
if ($event->getEventType() == WebHookType::CAPTURE_REFUNDED) {
$paymentTotal = $this->getPaymentTotal($event);
if ($paymentTotal > 0) {
return 0;
}
}
return $this->getStatusMapping()->getPsOrderStatusByEventType($event->getEventType());
}
protected function getPaymentTotal(WebhookEvent $event)
{
$paypalOrder = $this->initPaypalOrder($event);
return $this->getPaymentTotalAmountService()->get($paypalOrder);
}
protected function getPaymentTotalAmountService()
{
return new PaymentTotalAmount();
}
/** @return StatusMapping*/
protected function getStatusMapping()
{
return new StatusMapping();
}
protected function isMultishop()
{
return Shop::isFeatureActive();
}
protected function init()
{
if ($this->context->employee == null) {
$this->setEmployeeInContext();
}
}
protected function setEmployeeInContext()
{
$employees = Employee::getEmployeesByProfile(1);
if (false === empty($employees)) {
$employee = new Employee((int) $employees[0]);
$this->context->employee = $employee;
}
}
}