7
7
8
8
namespace Magento \Paypal \Model \Payflow ;
9
9
10
+ use Magento \Framework \Exception \LocalizedException ;
11
+ use Magento \Framework \Exception \State \InvalidTransitionException ;
10
12
use Magento \Payment \Helper \Formatter ;
11
13
use Magento \Payment \Model \InfoInterface ;
12
- use Magento \Paypal \Model \Payflowpro ;
13
- use Magento \Sales \Api \Data \OrderPaymentExtensionInterfaceFactory ;
14
- use Magento \Sales \Model \Order \Payment ;
15
- use Magento \Paypal \Model \Payflow \Service \Gateway ;
16
- use Magento \Framework \Exception \LocalizedException ;
17
- use Magento \Payment \Model \Method \TransparentInterface ;
18
14
use Magento \Payment \Model \Method \ConfigInterfaceFactory ;
19
- use Magento \Framework \Exception \State \InvalidTransitionException ;
15
+ use Magento \Payment \Model \Method \TransparentInterface ;
16
+ use Magento \Payment \Model \MethodInterface ;
17
+ use Magento \Paypal \Model \Payflow \Service \Gateway ;
20
18
use Magento \Paypal \Model \Payflow \Service \Response \Handler \HandlerInterface ;
21
19
use Magento \Paypal \Model \Payflow \Service \Response \Validator \ResponseValidator ;
20
+ use Magento \Paypal \Model \Payflowpro ;
21
+ use Magento \Sales \Api \Data \OrderPaymentExtensionInterfaceFactory ;
22
+ use Magento \Sales \Model \Order \Payment ;
22
23
use Magento \Vault \Api \Data \PaymentTokenInterface ;
23
24
use Magento \Vault \Api \Data \PaymentTokenInterfaceFactory ;
24
25
25
26
/**
26
- * Payflow Pro payment gateway model
27
+ * Payflow Pro payment gateway model (transparent redirect).
27
28
*
28
29
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
29
30
*/
@@ -35,6 +36,16 @@ class Transparent extends Payflowpro implements TransparentInterface
35
36
36
37
const CC_VAULT_CODE = 'payflowpro_cc_vault ' ;
37
38
39
+ /**
40
+ * Result code of account verification transaction request.
41
+ */
42
+ private const RESULT_CODE = 'result_code ' ;
43
+
44
+ /**
45
+ * Fraud Management Filters config setting.
46
+ */
47
+ private const CONFIG_FMF = 'fmf ' ;
48
+
38
49
/**
39
50
* @var string
40
51
*/
@@ -45,6 +56,13 @@ class Transparent extends Payflowpro implements TransparentInterface
45
56
*/
46
57
protected $ _infoBlockType = \Magento \Paypal \Block \Payment \Info::class;
47
58
59
+ /**
60
+ * Fetch transaction details availability option.
61
+ *
62
+ * @var bool
63
+ */
64
+ protected $ _canFetchTransactionInfo = false ;
65
+
48
66
/**
49
67
* @var ResponseValidator
50
68
*/
@@ -165,6 +183,14 @@ public function validate()
165
183
*/
166
184
public function authorize (InfoInterface $ payment , $ amount )
167
185
{
186
+ if ($ this ->isFraudDetected ($ payment )) {
187
+ $ this ->markPaymentAsFraudulent ($ payment );
188
+ return $ this ;
189
+ }
190
+
191
+ $ zeroAmountAuthorizationId = $ this ->getZeroAmountAuthorizationId ($ payment );
192
+ /** @var PaymentTokenInterface $vaultPaymentToken */
193
+ $ vaultPaymentToken = $ payment ->getExtensionAttributes ()->getVaultPaymentToken ();
168
194
/** @var Payment $payment */
169
195
$ request = $ this ->buildBasicRequest ();
170
196
@@ -177,9 +203,9 @@ public function authorize(InfoInterface $payment, $amount)
177
203
$ payPalCart = $ this ->payPalCartFactory ->create (['salesModel ' => $ order ]);
178
204
$ payPalCart ->getAmounts ();
179
205
180
- $ token = $ payment -> getAdditionalInformation ( self :: PNREF ) ;
206
+ $ parentTransactionId = $ vaultPaymentToken ? $ vaultPaymentToken -> getGatewayToken () : $ zeroAmountAuthorizationId ;
181
207
$ request ->setData ('trxtype ' , self ::TRXTYPE_AUTH_ONLY );
182
- $ request ->setData ('origid ' , $ token );
208
+ $ request ->setData ('origid ' , $ parentTransactionId );
183
209
$ request ->setData ('amt ' , $ this ->formatPrice ($ amount ));
184
210
$ request ->setData ('currency ' , $ order ->getBaseCurrencyCode ());
185
211
$ request ->setData ('itemamt ' , $ this ->formatPrice ($ payPalCart ->getSubtotal ()));
@@ -200,10 +226,15 @@ public function authorize(InfoInterface $payment, $amount)
200
226
201
227
$ this ->setTransStatus ($ payment , $ response );
202
228
203
- $ this ->createPaymentToken ($ payment , $ token );
229
+ if ($ vaultPaymentToken ) {
230
+ $ payment ->setParentTransactionId ($ vaultPaymentToken ->getGatewayToken ());
231
+ } else {
232
+ $ this ->createPaymentToken ($ payment , $ zeroAmountAuthorizationId );
233
+ }
204
234
205
235
$ payment ->unsAdditionalInformation (self ::CC_DETAILS );
206
236
$ payment ->unsAdditionalInformation (self ::PNREF );
237
+ $ payment ->unsAdditionalInformation (self ::RESULT_CODE );
207
238
208
239
return $ this ;
209
240
}
@@ -291,14 +322,126 @@ private function getPaymentExtensionAttributes(Payment $payment)
291
322
*/
292
323
public function capture (InfoInterface $ payment , $ amount )
293
324
{
325
+ if ($ this ->isFraudDetected ($ payment )) {
326
+ $ this ->markPaymentAsFraudulent ($ payment );
327
+ return $ this ;
328
+ }
329
+
294
330
/** @var Payment $payment */
295
- $ token = $ payment ->getAdditionalInformation (self ::PNREF );
331
+ $ zeroAmountAuthorizationId = $ this ->getZeroAmountAuthorizationId ($ payment );
332
+ /** @var PaymentTokenInterface $vaultPaymentToken */
333
+ $ vaultPaymentToken = $ payment ->getExtensionAttributes ()->getVaultPaymentToken ();
334
+ if ($ vaultPaymentToken && empty ($ zeroAmountAuthorizationId )) {
335
+ $ payment ->setAdditionalInformation (self ::PNREF , $ vaultPaymentToken ->getGatewayToken ());
336
+ if (!$ payment ->getParentTransactionId ()) {
337
+ $ payment ->setParentTransactionId ($ vaultPaymentToken ->getGatewayToken ());
338
+ }
339
+ }
296
340
parent ::capture ($ payment , $ amount );
297
341
298
- if ($ token && ! $ payment -> getAuthorizationTransaction () ) {
299
- $ this ->createPaymentToken ($ payment , $ token );
342
+ if ($ zeroAmountAuthorizationId && $ vaultPaymentToken === null ) {
343
+ $ this ->createPaymentToken ($ payment , $ zeroAmountAuthorizationId );
300
344
}
301
345
302
346
return $ this ;
303
347
}
348
+
349
+ /**
350
+ * Attempt to accept a pending payment.
351
+ *
352
+ * Order acquires a payment review state based on results of PayPal account verification transaction (zero-amount
353
+ * authorization). For accepting a payment should be created PayPal reference transaction with a real order amount.
354
+ * Fraud Protection Service filters do not screen reference transactions.
355
+ *
356
+ * @param InfoInterface $payment
357
+ * @return bool
358
+ * @throws InvalidTransitionException
359
+ * @throws LocalizedException
360
+ */
361
+ public function acceptPayment (InfoInterface $ payment )
362
+ {
363
+ if ($ this ->getConfigPaymentAction () === MethodInterface::ACTION_AUTHORIZE_CAPTURE ) {
364
+ $ invoices = iterator_to_array ($ payment ->getOrder ()->getInvoiceCollection ());
365
+ $ invoice = count ($ invoices ) ? reset ($ invoices ) : null ;
366
+ $ payment ->capture ($ invoice );
367
+ } else {
368
+ $ amount = $ payment ->getOrder ()->getBaseGrandTotal ();
369
+ $ payment ->authorize (true , $ amount );
370
+ }
371
+
372
+ return true ;
373
+ }
374
+
375
+ /**
376
+ * Deny a pending payment.
377
+ *
378
+ * Order acquires a payment review state based on results of PayPal account verification transaction (zero-amount
379
+ * authorization). This transaction type cannot be voided, so we do not send any request to payment gateway.
380
+ *
381
+ * @param InfoInterface $payment
382
+ * @return bool
383
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
384
+ */
385
+ public function denyPayment (InfoInterface $ payment )
386
+ {
387
+ return true ;
388
+ }
389
+
390
+ /**
391
+ * Marks payment as fraudulent.
392
+ *
393
+ * @param InfoInterface $payment
394
+ * @throws \Exception
395
+ */
396
+ private function markPaymentAsFraudulent (InfoInterface $ payment ): void
397
+ {
398
+ $ zeroAmountAuthorizationId = $ this ->getZeroAmountAuthorizationId ($ payment );
399
+ $ payment ->setTransactionId ($ zeroAmountAuthorizationId );
400
+ $ payment ->setIsTransactionClosed (0 );
401
+ $ payment ->setIsTransactionPending (true );
402
+ $ payment ->setIsFraudDetected (true );
403
+ $ this ->createPaymentToken ($ payment , $ zeroAmountAuthorizationId );
404
+ $ fraudulentMsg = 'Order is suspended as an account verification transaction is suspected to be fraudulent. ' ;
405
+ $ extensionAttributes = $ this ->getPaymentExtensionAttributes ($ payment );
406
+ $ extensionAttributes ->setNotificationMessage ($ fraudulentMsg );
407
+ $ payment ->unsAdditionalInformation (self ::CC_DETAILS );
408
+ $ payment ->unsAdditionalInformation (self ::PNREF );
409
+ $ payment ->unsAdditionalInformation (self ::RESULT_CODE );
410
+ }
411
+
412
+ /**
413
+ * Checks if fraud filters were triggered for the payment.
414
+ *
415
+ * For current PayPal PayflowPro transparent redirect integration
416
+ * Fraud Protection Service filters screen only account verification
417
+ * transaction (also known as zero dollar authorization).
418
+ * Following reference transaction with real dollar amount will not be screened
419
+ * by Fraud Protection Service.
420
+ *
421
+ * @param InfoInterface $payment
422
+ * @return bool
423
+ */
424
+ private function isFraudDetected (InfoInterface $ payment ): bool
425
+ {
426
+ $ resultCode = $ payment ->getAdditionalInformation (self ::RESULT_CODE );
427
+ $ isFmfEnabled = (bool )$ this ->getConfig ()->getValue (self ::CONFIG_FMF );
428
+ return $ isFmfEnabled && $ this ->getZeroAmountAuthorizationId ($ payment ) && in_array (
429
+ $ resultCode ,
430
+ [self ::RESPONSE_CODE_DECLINED_BY_FILTER , self ::RESPONSE_CODE_FRAUDSERVICE_FILTER ]
431
+ );
432
+ }
433
+
434
+ /**
435
+ * Returns zero dollar authorization transaction id.
436
+ *
437
+ * PNREF (transaction id) is available in payment additional information only right after
438
+ * PayPal account verification transaction (also known as zero dollar authorization).
439
+ *
440
+ * @param InfoInterface $payment
441
+ * @return string
442
+ */
443
+ private function getZeroAmountAuthorizationId (InfoInterface $ payment ): string
444
+ {
445
+ return (string )$ payment ->getAdditionalInformation (self ::PNREF );
446
+ }
304
447
}
0 commit comments