diff --git a/Gateway/Helper/Util.php b/Gateway/Helper/Util.php
index de359129..5848d5d2 100644
--- a/Gateway/Helper/Util.php
+++ b/Gateway/Helper/Util.php
@@ -18,8 +18,10 @@
namespace Astound\Affirm\Gateway\Helper;
+use Magento\Framework\Math\Random;
+
/**
- * Class Action
+ * Class Util
*/
class Util
{
@@ -28,6 +30,22 @@ class Util
*/
const MONEY_FORMAT = "%.2f";
+ /**
+ * Idempotency key
+ */
+ const IDEMPOTENCY_KEY = "Idempotency-Key";
+
+ /**
+ * Constructor
+ *
+ * @param Random $random
+ */
+ public function __construct(
+ Random $random
+ ) {
+ $this->_random = $random;
+ }
+
/**
* Format to cents
*
@@ -71,4 +89,15 @@ protected static function formatMoney($amount)
{
return sprintf(self::MONEY_FORMAT, $amount);
}
+
+ /**
+ * Generate identifying strings to get idempotent responses
+ *
+ * @return string
+ */
+ public function generateIdempotencyKey()
+ {
+ return $this->_random->getUniqueHash();
+ }
+
}
diff --git a/Gateway/Http/Client/ClientService.php b/Gateway/Http/Client/ClientService.php
index 0d4c5ed7..3fcd439d 100644
--- a/Gateway/Http/Client/ClientService.php
+++ b/Gateway/Http/Client/ClientService.php
@@ -21,6 +21,8 @@
use Magento\Payment\Gateway\Http\ClientInterface;
use Magento\Payment\Gateway\Http\ClientException;
use Magento\Payment\Gateway\Http\TransferInterface;
+use Astound\Affirm\Gateway\Helper\Request\Action;
+use Astound\Affirm\Gateway\Helper\Util;
use Magento\Payment\Model\Method\Logger;
use Magento\Payment\Gateway\Http\ConverterInterface;
use Magento\Framework\HTTP\ZendClientFactory;
@@ -66,23 +68,42 @@ class ClientService implements ClientInterface
*/
protected $httpClientFactory;
+ /**
+ * Action
+ *
+ * @var Action
+ */
+ protected $action;
+
+ /**
+ * Util
+ *
+ * @var Util
+ */
+ protected $util;
+
/**
* Constructor
*
* @param Logger $logger
* @param ConverterInterface $converter,
* @param ZendClientFactory $httpClientFactory
+ * @param Action $action
*/
public function __construct(
Logger $logger,
AffirmLogger $affirmLogger,
ConverterInterface $converter,
- ZendClientFactory $httpClientFactory
+ ZendClientFactory $httpClientFactory,
+ Action $action,
+ Util $util
) {
$this->logger = $logger;
$this->affirmLogger = $affirmLogger;
$this->converter = $converter;
$this->httpClientFactory = $httpClientFactory;
+ $this->action = $action;
+ $this->util = $util;
}
/**
@@ -102,6 +123,10 @@ public function placeRequest(TransferInterface $transferObject)
$client = $this->httpClientFactory->create();
$client->setUri($requestUri);
$client->setAuth($transferObject->getAuthUsername(), $transferObject->getAuthPassword());
+ if (strpos($transferObject->getUri(), $this->action::API_TRANSACTIONS_PATH) !== false) {
+ $idempotencyKey = $this->util->generateIdempotencyKey();
+ $client->setHeaders([Util::IDEMPOTENCY_KEY => $idempotencyKey]);
+ }
if (!empty($transferObject->getBody())) {
$data = $transferObject->getBody();
$data = json_encode($data, JSON_UNESCAPED_SLASHES);
@@ -113,7 +138,7 @@ public function placeRequest(TransferInterface $transferObject)
$response = $this->converter->convert($rawResponse);
} catch (\Exception $e) {
$log['error'] = $e->getMessage();
- $this->logger->error($log);
+ $this->logger->debug($log);
throw new ClientException(__($e->getMessage()));
} finally {
$log['uri'] = $requestUri;
diff --git a/Gateway/Request/AbstractDataBuilder.php b/Gateway/Request/AbstractDataBuilder.php
index da8b9343..f3c8963e 100644
--- a/Gateway/Request/AbstractDataBuilder.php
+++ b/Gateway/Request/AbstractDataBuilder.php
@@ -21,6 +21,7 @@
use Magento\Payment\Gateway\ConfigInterface;
use Magento\Payment\Gateway\Request\BuilderInterface;
use Magento\Store\Model\StoreManagerInterface;
+use Astound\Affirm\Model\Config;
/**
* Class AbstractDataBuilder
@@ -56,10 +57,12 @@ abstract class AbstractDataBuilder implements BuilderInterface
*/
public function __construct(
ConfigInterface $config,
- StoreManagerInterface $storeManager
+ StoreManagerInterface $storeManager,
+ Config $configAffirm
) {
$this->config = $config;
$this->_storeManager = $storeManager;
+ $this->affirmPaymentConfig = $configAffirm;
}
/**
diff --git a/Gateway/Request/CaptureRequest.php b/Gateway/Request/CaptureRequest.php
index 2990a187..e37aabe0 100644
--- a/Gateway/Request/CaptureRequest.php
+++ b/Gateway/Request/CaptureRequest.php
@@ -19,6 +19,7 @@
namespace Astound\Affirm\Gateway\Request;
use Magento\Payment\Gateway\Data\PaymentDataObjectInterface;
+use Astound\Affirm\Gateway\Helper\Util;
/**
* Class CaptureRequest
@@ -50,10 +51,18 @@ public function build(array $buildSubject)
if (!$storeId) {
$storeId = null;
}
+ if ($this->affirmPaymentConfig->getPartialCapture()) {
+ $_amount = $buildSubject['amount'] ? Util::formatToCents($buildSubject['amount']) : null;
+ $_body = [
+ 'amount' => $_amount
+ ];
+ } else {
+ $_body = [];
+ }
return [
- 'body' => [],
'path' => "{$transactionId}/capture",
- 'storeId' => $storeId
+ 'storeId' => $storeId,
+ 'body' => $_body
];
}
}
diff --git a/Gateway/Validator/Client/PaymentActionsValidator.php b/Gateway/Validator/Client/PaymentActionsValidator.php
index a86da41e..c14c495d 100644
--- a/Gateway/Validator/Client/PaymentActionsValidator.php
+++ b/Gateway/Validator/Client/PaymentActionsValidator.php
@@ -41,9 +41,9 @@ public function validate(array $validationSubject)
if (!$validationResult) {
$errorMessages = (isset($response[self::ERROR_MESSAGE])) ?
- [__('Affirm error code:') . $response[self::RESPONSE_CODE] . __(' error: ') .
- __($response[self::ERROR_MESSAGE])]:
+ [__($response[self::ERROR_MESSAGE]) . __(' Affirm status code: ') . $response[self::RESPONSE_CODE]]:
[__('Transaction has been declined, please, try again later.')];
+ throw new \Magento\Framework\Validator\Exception(__($errorMessages[0]));
}
return $this->createResult($validationResult, $errorMessages);
diff --git a/Model/Config.php b/Model/Config.php
index b362ce66..47a68c45 100644
--- a/Model/Config.php
+++ b/Model/Config.php
@@ -652,4 +652,14 @@ public function getCheckoutFlowType()
{
return $this->getConfigData('checkout_flow_type');
}
+
+ /**
+ * Get partial capture
+ *
+ * @return bool
+ */
+ public function getPartialCapture()
+ {
+ return $this->getConfigData('partial_capture');
+ }
}
diff --git a/Model/Plugin/Payment/CanCapturePartial.php b/Model/Plugin/Payment/CanCapturePartial.php
new file mode 100644
index 00000000..d1ac3dac
--- /dev/null
+++ b/Model/Plugin/Payment/CanCapturePartial.php
@@ -0,0 +1,48 @@
+affirmPaymentConfig = $configAffirm;
+ }
+
+ /**
+ * Plugin to verify if Partial Capture is enabled in config
+ *
+ * @param Magento\Sales\Model\Order\Payment $subject
+ * @param callable $result
+ * @return bool
+ */
+ public function afterCanCapturePartial(Payment $subject, $result)
+ {
+ if (!$this->affirmPaymentConfig->getPartialCapture()) {
+ return false;
+ }
+ return $result;
+ }
+}
diff --git a/composer.json b/composer.json
index 024d578e..c9780cdb 100644
--- a/composer.json
+++ b/composer.json
@@ -2,7 +2,7 @@
"name": "affirm/magento2",
"description": "Affirm's extension for the Magento 2 https://www.affirm.com/",
"type": "magento2-module",
- "version": "3.0.15",
+ "version": "3.1.0",
"license": [
"BSD-3-Clause"
],
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index 40287162..2b226ab4 100644
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -74,7 +74,12 @@
Astound\Affirm\Model\Adminhtml\Source\PaymentAction
-
+
+
+ authorize
+ Magento\Config\Model\Config\Source\Yesno
+
+
Magento\Config\Model\Config\Source\Yesno
diff --git a/etc/config.xml b/etc/config.xml
index 289988b8..19002655 100644
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -23,6 +23,7 @@
0.01
50000
authorize
+ 0
0
1
US,AS,GU,MP,PR,VI
@@ -30,7 +31,7 @@
USD
1
1
- 0
+ 1
1
1
1
diff --git a/etc/di.xml b/etc/di.xml
index 1341c869..68271125 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -285,6 +285,9 @@
+
+
+
diff --git a/etc/module.xml b/etc/module.xml
index cf70d953..5262389d 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -10,7 +10,7 @@
*/
-->
-
+