Current File : /var/www/prestashop/modules/ps_checkout/src/PayPal/Card3DSecure.php |
<?php
/**
* Copyright since 2007 PrestaShop SA and Contributors
* PrestaShop is an International Registered Trademark & Property of PrestaShop SA
*
* NOTICE OF LICENSE
*
* This source file is subject to the Academic Free License version 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
* 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.
*
* @author PrestaShop SA and Contributors <contact@prestashop.com>
* @copyright Since 2007 PrestaShop SA and Contributors
* @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0
*/
namespace PrestaShop\Module\PrestashopCheckout\PayPal;
/**
* Parameters related to 3D Secure 2.0
* Recommended action based on `enrollment_status` and `authentication_status` parameters, a `liability_shift` determines how you might proceed with authentication
*
* @see https://developer.paypal.com/docs/checkout/advanced/customize/3d-secure/response-parameters/#enrollmentstatusauthentication_statusliabilityshiftrecommended-action
*/
class Card3DSecure
{
// Liability has shifted to the card issuer. Available only after order is authorized or captured.
const LIABILITY_SHIFT_YES = 'YES';
// Liability is with the merchant.
const LIABILITY_SHIFT_NO = 'NO';
// Liability may shift to the card issuer. Available only before order is authorized or captured.
const LIABILITY_SHIFT_POSSIBLE = 'POSSIBLE';
// The authentication system is not available.
const LIABILITY_SHIFT_UNKNOWN = 'UNKNOWN';
// Yes. The bank is participating in 3-D Secure protocol and will return the ACSUrl.
const ENROLLMENT_STATUS_YES = 'Y';
// No. The bank is not participating in 3-D Secure protocol.
const ENROLLMENT_STATUS_NO = 'N';
// Unavailable. The DS or ACS is not available for authentication at the time of the request.
const ENROLLMENT_STATUS_UNAVAILABLE = 'U';
// Bypass. The merchant authentication rule is triggered to bypass authentication.
const ENROLLMENT_STATUS_BYPASS = 'B';
// Successful authentication.
const AUTHENTICATION_RESULT_YES = 'Y';
// Failed authentication / account not verified / transaction denied.
const AUTHENTICATION_RESULT_NO = 'N';
// Unable to complete authentication.
const AUTHENTICATION_RESULT_UNABLE = 'U';
// Authentication rejected (merchant must not submit for authorization).
const AUTHENTICATION_RESULT_REJECTED = 'R';
// Successful attempts transaction.
const AUTHENTICATION_RESULT_ATTEMPTED = 'A';
// Challenge required for authentication.
const AUTHENTICATION_RESULT_CHALLENGE_REQUIRED = 'C';
// Challenge required; decoupled authentication confirmed.
const AUTHENTICATION_RESULT_DECOUPLED = 'D';
// Informational only; 3DS requester challenge preference acknowledged.
const AUTHENTICATION_RESULT_INFO = 'I';
// Continue with authorization at your own risk, meaning that the liability of any chargeback has not shifted from the merchant to the card issuer.
const NO_DECISION = 0;
// Continue with authorization.
const PROCEED = 1;
// Do not continue with authorization.
const REJECT = 2;
// Do not continue with authorization. Request cardholder to retry.
const RETRY = 3;
/**
* @param array $order
*
* @return int
*/
public function continueWithAuthorization(array $order)
{
$cardAuthenticationResult = $this->getAuthenticationResult($order);
if (!$cardAuthenticationResult) {
return static::NO_DECISION;
}
$liabilityShift = $this->getLiabilityShift($cardAuthenticationResult);
if ($liabilityShift === static::LIABILITY_SHIFT_POSSIBLE) {
return static::PROCEED;
}
if ($liabilityShift === static::LIABILITY_SHIFT_UNKNOWN) {
return static::RETRY;
}
$threeDSecure = $this->get3DSecure($cardAuthenticationResult);
if ($liabilityShift === static::LIABILITY_SHIFT_NO && $threeDSecure) {
return $this->noLiabilityShift($cardAuthenticationResult);
}
return static::NO_DECISION;
}
/**
* @param array $order
*
* @return bool
*/
public function is3DSecureAvailable(array $order)
{
$cardAuthenticationResult = $this->getAuthenticationResult($order);
if (!$cardAuthenticationResult) {
return false;
}
$threeDSecure = $this->get3DSecure($cardAuthenticationResult);
$enrollmentStatus = $this->getEnrollmentStatus($threeDSecure);
return $enrollmentStatus === self::ENROLLMENT_STATUS_YES || $enrollmentStatus === self::ENROLLMENT_STATUS_UNAVAILABLE;
}
/**
* @param array $order
*
* @return bool
*/
public function isLiabilityShifted(array $order)
{
$authenticationResult = $this->getAuthenticationResult($order);
$liabilityShift = $this->getLiabilityShift($authenticationResult);
$threeDSecure = $this->get3DSecure($authenticationResult);
$authenticationStatus = $this->getAuthenticationStatus($threeDSecure);
return ($liabilityShift === self::LIABILITY_SHIFT_POSSIBLE || $liabilityShift === self::LIABILITY_SHIFT_YES)
&& $authenticationStatus === self::AUTHENTICATION_RESULT_YES;
}
/**
* @param array{enrollment_status: string, authentication_status: string} $cardAuthenticationResult
*
* @return int
*/
private function noLiabilityShift(array $cardAuthenticationResult)
{
$threeDSecure = $this->get3DSecure($cardAuthenticationResult);
$enrollmentStatus = $this->getEnrollmentStatus($threeDSecure);
$authenticationStatus = $this->getAuthenticationStatus($threeDSecure);
if ($enrollmentStatus === static::ENROLLMENT_STATUS_BYPASS && !$authenticationStatus) {
return static::PROCEED;
}
if ($enrollmentStatus === static::ENROLLMENT_STATUS_UNAVAILABLE && !$authenticationStatus) {
return static::PROCEED;
}
if ($enrollmentStatus === static::ENROLLMENT_STATUS_NO && !$authenticationStatus) {
return static::PROCEED;
}
if ($authenticationStatus === static::AUTHENTICATION_RESULT_REJECTED) {
return static::REJECT;
}
if ($authenticationStatus === static::AUTHENTICATION_RESULT_NO) {
return static::REJECT;
}
if ($authenticationStatus === static::AUTHENTICATION_RESULT_UNABLE) {
return static::RETRY;
}
if (!$authenticationStatus) {
return static::RETRY;
}
return static::NO_DECISION;
}
/**
* @param array $order
*
* @return array|null
*/
private function getAuthenticationResult(array $order)
{
$fundingSource = isset($order['payment_source']) ? key($order['payment_source']) : '';
return isset($order['payment_source'][$fundingSource]['authentication_result']) ? $order['payment_source'][$fundingSource]['authentication_result'] : null;
}
/**
* @param array|null $authenticationResult
*
* @return string|null
*/
private function getLiabilityShift($authenticationResult)
{
return isset($authenticationResult['liability_shift']) ? $authenticationResult['liability_shift'] : null;
}
/**
* @param array|null $authenticationResult
*
* @return array|null
*/
private function get3DSecure($authenticationResult)
{
return isset($authenticationResult['three_d_secure']) ? $authenticationResult['three_d_secure'] : null;
}
/**
* @param array|null $threeDSecure
*
* @return string|null
*/
public function getAuthenticationStatus($threeDSecure)
{
return isset($threeDSecure['authentication_status']) ? $threeDSecure['authentication_status'] : null;
}
/**
* @param array|null $threeDSecure
*
* @return string|null
*/
private function getEnrollmentStatus($threeDSecure)
{
return isset($threeDSecure['enrollment_status']) ? $threeDSecure['enrollment_status'] : null;
}
}