Skip to content

Commit 3e7d29c

Browse files
committed
MAGETWO-63917: Implement CVV and AVS mapping for PayPal Payflow Pro/ PayPal Payments Pro
1 parent 7471197 commit 3e7d29c

File tree

5 files changed

+330
-0
lines changed

5 files changed

+330
-0
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Paypal\Model\Payflow;
7+
8+
use Magento\Paypal\Model\Config;
9+
use Magento\Paypal\Model\Info;
10+
use Magento\Payment\Api\PaymentVerificationInterface;
11+
use Magento\Sales\Api\Data\OrderPaymentInterface;
12+
13+
/**
14+
* Processes AVS codes mapping from PayPal Payflow transaction to
15+
* electronic merchant systems standard.
16+
*
17+
* @see https://developer.paypal.com/docs/classic/payflow/integration-guide/#credit-card-transaction-responses
18+
* @see http://www.emsecommerce.net/avs_cvv2_response_codes.htm
19+
*/
20+
class AvsEmsCodeMapper implements PaymentVerificationInterface
21+
{
22+
/**
23+
* Default code for mismatching mapping.
24+
*
25+
* @var string
26+
*/
27+
private static $unavailableCode = 'U';
28+
29+
/**
30+
* List of mapping AVS codes
31+
*
32+
* @var array
33+
*/
34+
private static $avsMap = [
35+
'YY' => 'Y',
36+
'NY' => 'A',
37+
'YN' => 'Z',
38+
'NN' => 'N'
39+
];
40+
41+
/**
42+
* Gets payment AVS verification code.
43+
* Returns null if payment does not contain any AVS details.
44+
* Throws an exception if specified order payment has different payment method code.
45+
*
46+
* @param OrderPaymentInterface $orderPayment
47+
* @return string
48+
* @throws \InvalidArgumentException
49+
*/
50+
public function getCode(OrderPaymentInterface $orderPayment)
51+
{
52+
if ($orderPayment->getMethod() !== Config::METHOD_PAYFLOWPRO) {
53+
throw new \InvalidArgumentException(
54+
'The "' . $orderPayment->getMethod() . '" does not supported by Payflow AVS mapper.'
55+
);
56+
}
57+
58+
$additionalInfo = $orderPayment->getAdditionalInformation();
59+
if (empty($additionalInfo[Info::PAYPAL_AVSADDR]) ||
60+
empty($additionalInfo[Info::PAYPAL_AVSZIP])
61+
) {
62+
return self::$unavailableCode;
63+
}
64+
65+
$streetCode = $additionalInfo[Info::PAYPAL_AVSADDR];
66+
$zipCode = $additionalInfo[Info::PAYPAL_AVSZIP];
67+
$key = $zipCode . $streetCode;
68+
69+
return isset(self::$avsMap[$key]) ? self::$avsMap[$key] : self::$unavailableCode;
70+
}
71+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Paypal\Model\Payflow;
7+
8+
use Magento\Paypal\Model\Config;
9+
use Magento\Paypal\Model\Info;
10+
use Magento\Payment\Api\PaymentVerificationInterface;
11+
use Magento\Sales\Api\Data\OrderPaymentInterface;
12+
13+
/**
14+
* Processes CVV codes mapping from PayPal Payflow transaction to
15+
* electronic merchant systems standard.
16+
*
17+
* @see https://developer.paypal.com/docs/classic/payflow/integration-guide/#credit-card-transaction-responses
18+
* @see http://www.emsecommerce.net/avs_cvv2_response_codes.htm
19+
*/
20+
class CvvEmsCodeMapper implements PaymentVerificationInterface
21+
{
22+
/**
23+
* Default code for mismatch mapping
24+
*
25+
* @var string
26+
*/
27+
private static $notProvidedCode = 'P';
28+
29+
/**
30+
* List of mapping CVV codes
31+
*
32+
* @var array
33+
*/
34+
private static $cvvMap = [
35+
'Y' => 'M',
36+
'N' => 'N'
37+
];
38+
39+
/**
40+
* Gets payment CVV verification code.
41+
* Returns null if payment does not contain any CVV details.
42+
* Throws an exception if specified order payment has different payment method code.
43+
*
44+
* @param OrderPaymentInterface $orderPayment
45+
* @return string
46+
* @throws \Exception
47+
*/
48+
public function getCode(OrderPaymentInterface $orderPayment)
49+
{
50+
if ($orderPayment->getMethod() !== Config::METHOD_PAYFLOWPRO) {
51+
throw new \InvalidArgumentException(
52+
'The "' . $orderPayment->getMethod() . '" does not supported by Payflow CVV mapper.'
53+
);
54+
}
55+
56+
$additionalInfo = $orderPayment->getAdditionalInformation();
57+
if (empty($additionalInfo[Info::PAYPAL_CVV2MATCH])) {
58+
return self::$notProvidedCode;
59+
}
60+
61+
$cvv = $additionalInfo[Info::PAYPAL_CVV2MATCH];
62+
63+
return isset(self::$cvvMap[$cvv]) ? self::$cvvMap[$cvv] : self::$notProvidedCode;
64+
}
65+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Paypal\Test\Unit\Model\Payflow;
7+
8+
use Magento\Paypal\Model\Payflow\AvsEmsCodeMapper;
9+
use Magento\Paypal\Model\Config;
10+
use Magento\Paypal\Model\Info;
11+
use Magento\Sales\Api\Data\OrderPaymentInterface;
12+
use PHPUnit_Framework_MockObject_MockObject as MockObject;
13+
14+
class AvsEmsCodeMapperTest extends \PHPUnit_Framework_TestCase
15+
{
16+
/**
17+
* @var AvsEmsCodeMapper
18+
*/
19+
private $mapper;
20+
21+
/**
22+
* @inheritdoc
23+
*/
24+
protected function setUp()
25+
{
26+
$this->mapper = new AvsEmsCodeMapper();
27+
}
28+
29+
/**
30+
* Checks different variations for AVS codes mapping.
31+
*
32+
* @covers \Magento\Paypal\Model\Payflow\AvsEmsCodeMapper::getCode
33+
* @param string $avsZip
34+
* @param string $avsStreet
35+
* @param string $expected
36+
* @dataProvider getCodeDataProvider
37+
*/
38+
public function testGetCode($avsZip, $avsStreet, $expected)
39+
{
40+
/** @var OrderPaymentInterface|MockObject $orderPayment */
41+
$orderPayment = $this->getMockBuilder(OrderPaymentInterface::class)
42+
->disableOriginalConstructor()
43+
->getMock();
44+
45+
$orderPayment->expects(self::once())
46+
->method('getMethod')
47+
->willReturn(Config::METHOD_PAYFLOWPRO);
48+
49+
$orderPayment->expects(self::once())
50+
->method('getAdditionalInformation')
51+
->willReturn([
52+
Info::PAYPAL_AVSZIP => $avsZip,
53+
Info::PAYPAL_AVSADDR => $avsStreet
54+
]);
55+
56+
self::assertEquals($expected, $this->mapper->getCode($orderPayment));
57+
}
58+
59+
/**
60+
* Checks a test case, when payment order is not Payflow payment method.
61+
*
62+
* @covers \Magento\Paypal\Model\Payflow\AvsEmsCodeMapper::getCode
63+
* @expectedException \InvalidArgumentException
64+
* @expectedExceptionMessage The "some_payment" does not supported by Payflow AVS mapper.
65+
*/
66+
public function testGetCodeWithException()
67+
{
68+
/** @var OrderPaymentInterface|MockObject $orderPayment */
69+
$orderPayment = $this->getMockBuilder(OrderPaymentInterface::class)
70+
->disableOriginalConstructor()
71+
->getMock();
72+
73+
$orderPayment->expects(self::exactly(2))
74+
->method('getMethod')
75+
->willReturn('some_payment');
76+
77+
$this->mapper->getCode($orderPayment);
78+
}
79+
80+
/**
81+
* Gets list of AVS codes.
82+
*
83+
* @return array
84+
*/
85+
public function getCodeDataProvider()
86+
{
87+
return [
88+
['avsZip' => null, 'avsStreet' => null, 'expected' => 'U'],
89+
['avsZip' => null, 'avsStreet' => 'Y', 'expected' => 'U'],
90+
['avsZip' => 'Y', 'avsStreet' => null, 'expected' => 'U'],
91+
['avsZip' => 'Y', 'avsStreet' => 'Y', 'expected' => 'Y'],
92+
['avsZip' => 'N', 'avsStreet' => 'Y', 'expected' => 'A'],
93+
['avsZip' => 'Y', 'avsStreet' => 'N', 'expected' => 'Z'],
94+
['avsZip' => 'N', 'avsStreet' => 'N', 'expected' => 'N'],
95+
['avsZip' => 'X', 'avsStreet' => 'Y', 'expected' => 'U'],
96+
['avsZip' => 'N', 'avsStreet' => 'X', 'expected' => 'U'],
97+
['avsZip' => '', 'avsStreet' => 'Y', 'expected' => 'U'],
98+
['avsZip' => 'N', 'avsStreet' => '', 'expected' => 'U']
99+
];
100+
}
101+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php
2+
/**
3+
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Paypal\Test\Unit\Model\Payflow;
7+
8+
use Magento\Paypal\Model\Payflow\CvvEmsCodeMapper;
9+
use Magento\Paypal\Model\Config;
10+
use Magento\Paypal\Model\Info;
11+
use Magento\Sales\Api\Data\OrderPaymentInterface;
12+
use PHPUnit_Framework_MockObject_MockObject as MockObject;
13+
14+
class CvvEmsCodeMapperTest extends \PHPUnit_Framework_TestCase
15+
{
16+
/**
17+
* @var CvvEmsCodeMapper
18+
*/
19+
private $mapper;
20+
21+
/**
22+
* @inheritdoc
23+
*/
24+
protected function setUp()
25+
{
26+
$this->mapper = new CvvEmsCodeMapper();
27+
}
28+
29+
/**
30+
* Checks different variations for cvv codes mapping.
31+
*
32+
* @covers \Magento\Paypal\Model\Payflow\CvvEmsCodeMapper::getCode
33+
* @param string $cvvCode
34+
* @param string $expected
35+
* @dataProvider getCodeDataProvider
36+
*/
37+
public function testGetCode($cvvCode, $expected)
38+
{
39+
/** @var OrderPaymentInterface|MockObject $orderPayment */
40+
$orderPayment = $this->getMockBuilder(OrderPaymentInterface::class)
41+
->disableOriginalConstructor()
42+
->getMock();
43+
44+
$orderPayment->expects(self::once())
45+
->method('getMethod')
46+
->willReturn(Config::METHOD_PAYFLOWPRO);
47+
48+
$orderPayment->expects(self::once())
49+
->method('getAdditionalInformation')
50+
->willReturn([Info::PAYPAL_CVV2MATCH => $cvvCode]);
51+
52+
self::assertEquals($expected, $this->mapper->getCode($orderPayment));
53+
}
54+
55+
/**
56+
* Checks a test case, when payment order is not Payflow payment method.
57+
*
58+
* @covers \Magento\Paypal\Model\Payflow\CvvEmsCodeMapper::getCode
59+
* @expectedException \InvalidArgumentException
60+
* @expectedExceptionMessage The "some_payment" does not supported by Payflow CVV mapper.
61+
*/
62+
public function testGetCodeWithException()
63+
{
64+
/** @var OrderPaymentInterface|MockObject $orderPayment */
65+
$orderPayment = $this->getMockBuilder(OrderPaymentInterface::class)
66+
->disableOriginalConstructor()
67+
->getMock();
68+
69+
$orderPayment->expects(self::exactly(2))
70+
->method('getMethod')
71+
->willReturn('some_payment');
72+
73+
$this->mapper->getCode($orderPayment);
74+
}
75+
76+
/**
77+
* Gets variations of cvv codes and expected mapping result.
78+
*
79+
* @return array
80+
*/
81+
public function getCodeDataProvider()
82+
{
83+
return [
84+
['cvvCode' => '', 'expected' => 'P'],
85+
['cvvCode' => null, 'expected' => 'P'],
86+
['cvvCode' => 'Y', 'expected' => 'M'],
87+
['cvvCode' => 'N', 'expected' => 'N'],
88+
['cvvCode' => 'X', 'expected' => 'P']
89+
];
90+
}
91+
}

app/code/Magento/Paypal/etc/config.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@
9191
<cc_year_length>2</cc_year_length>
9292
<can_authorize_vault>1</can_authorize_vault>
9393
<can_capture_vault>1</can_capture_vault>
94+
<avs_ems_adapter>Magento\Paypal\Model\Payflow\AvsEmsCodeMapper</avs_ems_adapter>
95+
<cvv_ems_adapter>Magento\Paypal\Model\Payflow\CvvEmsCodeMapper</cvv_ems_adapter>
9496
</payflowpro>
9597
<payflowpro_cc_vault>
9698
<model>PayflowProCreditCardVaultFacade</model>

0 commit comments

Comments
 (0)