diff --git a/Block/Promotion/AslowasAbstract.php b/Block/Promotion/AslowasAbstract.php index f15d134a..d5738240 100644 --- a/Block/Promotion/AslowasAbstract.php +++ b/Block/Promotion/AslowasAbstract.php @@ -23,7 +23,7 @@ abstract class AslowasAbstract extends \Magento\Framework\View\Element\Template * * @var array */ - protected $data = ['logo', 'script', 'public_api_key']; + protected $data = ['logo', 'script', 'public_api_key', 'country_code', 'locale']; /** * Colors which could be set in "data-affirm-color". @@ -208,6 +208,8 @@ public function process() $config = $configProvider['payment'][ConfigProvider::CODE]; $this->setData('script', $config['script']); $this->setData('public_api_key', $config['apiKeyPublic']); + $this->setData('country_code', $config['countryCode']); + $this->setData('locale', $config['locale']); } // Set max and min options amounts from payment configuration $this->setData('min_order_total', $this->getPaymentConfigValue('min_order_total')); diff --git a/Block/Promotion/Banners.php b/Block/Promotion/Banners.php index 57e29626..ef0caf4a 100644 --- a/Block/Promotion/Banners.php +++ b/Block/Promotion/Banners.php @@ -232,6 +232,8 @@ public function getOptions() if ($config && isset($config['script']) && isset($config['apiKeyPublic'])) { $options['script'] = $config['script']; $options['public_api_key'] = $config['apiKeyPublic']; + $options['country_code'] = $config['countryCode']; + $options['locale'] = $config['locale']; } } if ($this->isProductPage()) { diff --git a/Block/Promotion/CartPage/Aslowas.php b/Block/Promotion/CartPage/Aslowas.php index b897089c..f1b6ae7c 100644 --- a/Block/Promotion/CartPage/Aslowas.php +++ b/Block/Promotion/CartPage/Aslowas.php @@ -21,7 +21,7 @@ class Aslowas extends AslowasAbstract * * @var array */ - protected $data = ['logo', 'script', 'public_api_key', 'min_order_total', 'max_order_total', 'element_id']; + protected $data = ['logo', 'script', 'public_api_key', 'min_order_total', 'max_order_total', 'element_id', 'country_code', 'locale']; /** * Checkout session diff --git a/Block/Promotion/Pixel/Confirm.php b/Block/Promotion/Pixel/Confirm.php index dd362c6e..d57a6a26 100644 --- a/Block/Promotion/Pixel/Confirm.php +++ b/Block/Promotion/Pixel/Confirm.php @@ -149,6 +149,8 @@ public function getOptions() if ($config && isset($config['script']) && isset($config['apiKeyPublic'])) { $options['script'] = $config['script']; $options['public_api_key'] = $config['apiKeyPublic']; + $options['country_code'] = $config['countryCode']; + $options['locale'] = $config['locale']; } } return $options; diff --git a/Block/Promotion/ProductPage/Aslowas.php b/Block/Promotion/ProductPage/Aslowas.php index 4dab4ebf..60eabc46 100644 --- a/Block/Promotion/ProductPage/Aslowas.php +++ b/Block/Promotion/ProductPage/Aslowas.php @@ -33,7 +33,7 @@ class Aslowas extends AslowasAbstract * @var array */ protected $data = ['logo', 'script', 'public_api_key', 'min_order_total', 'max_order_total', - 'selector', 'currency_rate', 'backorders_options', 'element_id']; + 'selector', 'currency_rate', 'backorders_options', 'element_id', 'country_code', 'locale']; /** * Validate block before showing on front diff --git a/Gateway/Helper/Util.php b/Gateway/Helper/Util.php index 5848d5d2..e1e389ea 100644 --- a/Gateway/Helper/Util.php +++ b/Gateway/Helper/Util.php @@ -35,6 +35,11 @@ class Util */ const IDEMPOTENCY_KEY = "Idempotency-Key"; + /** + * Country code + */ + const COUNTRY_CODE = "Country-Code"; + /** * Constructor * diff --git a/Gateway/Http/AbstractTransferFactory.php b/Gateway/Http/AbstractTransferFactory.php index a241feb8..dde4795a 100644 --- a/Gateway/Http/AbstractTransferFactory.php +++ b/Gateway/Http/AbstractTransferFactory.php @@ -29,6 +29,18 @@ */ abstract class AbstractTransferFactory implements TransferFactoryInterface { + /** + * Constants + */ + const MODE = 'mode'; + const SANDBOX = 'sandbox'; + const PRODUCTION = 'production'; + const COUNTRY_CODE_USA = 'USA'; + const COUNTRY_CODE_CAN = 'CAN'; + const SUFFIX_CANADA = '_ca'; + /** + + /** * Config * @@ -80,18 +92,20 @@ public function __construct( * Get public API key * * @param int $storeId + * @param string $country_code * @return string */ - protected function getPublicApiKey($storeId) + protected function getPublicApiKey($storeId, $country_code) { + $country_suffix = $this->getApiKeyNameByCountry($country_code); if(!empty($storeId)){ - return $this->config->getValue('mode', $storeId) == 'sandbox' - ? $this->config->getValue('public_api_key_sandbox', $storeId) - : $this->config->getValue('public_api_key_production', $storeId); + return $this->config->getValue(self::MODE, $storeId) == self::SANDBOX + ? $this->config->getValue('public_api_key_sandbox' . $country_suffix, $storeId) + : $this->config->getValue('public_api_key_production' . $country_suffix, $storeId); } else { - return $this->config->getValue('mode') == 'sandbox' - ? $this->config->getValue('public_api_key_sandbox') - : $this->config->getValue('public_api_key_production'); + return $this->config->getValue('mode') == self::SANDBOX + ? $this->config->getValue('public_api_key_sandbox' . $country_suffix) + : $this->config->getValue('public_api_key_production' . $country_suffix); } } @@ -100,18 +114,20 @@ protected function getPublicApiKey($storeId) * Get private API key * * @param int $storeId + * @param string $country_code * @return string */ - protected function getPrivateApiKey($storeId) + protected function getPrivateApiKey($storeId, $country_code) { + $country_suffix = $this->getApiKeyNameByCountry($country_code); if(!empty($storeId)){ - return $this->config->getValue('mode', $storeId) == 'sandbox' - ? $this->config->getValue('private_api_key_sandbox', $storeId) - : $this->config->getValue('private_api_key_production', $storeId); + return $this->config->getValue('mode', $storeId) == self::SANDBOX + ? $this->config->getValue('private_api_key_sandbox' . $country_suffix, $storeId) + : $this->config->getValue('private_api_key_production' . $country_suffix, $storeId); } else { - return $this->config->getValue('mode') == 'sandbox' - ? $this->config->getValue('private_api_key_sandbox') - : $this->config->getValue('private_api_key_production'); + return $this->config->getValue('mode') == self::SANDBOX + ? $this->config->getValue('private_api_key_sandbox' . $country_suffix) + : $this->config->getValue('private_api_key_production' . $country_suffix); } } @@ -124,4 +140,24 @@ protected function getStoreId() { return $this->_storeManager->getStore()->getId(); } + + /** + * Map country code to API key config suffix name + * + * @param string $country_code + * @return string + */ + protected function getApiKeyNameByCountry($country_code) + { + $_suffix = ''; + $countryCodeToSuffix = array( + self::COUNTRY_CODE_CAN => self::SUFFIX_CANADA, + self::COUNTRY_CODE_USA => '', + ); + + if (isset($country_code)) { + $_suffix = $countryCodeToSuffix[$country_code] ?: ''; + } + return $_suffix; + } } diff --git a/Gateway/Http/Client/ClientService.php b/Gateway/Http/Client/ClientService.php index 3fcd439d..415efcc5 100644 --- a/Gateway/Http/Client/ClientService.php +++ b/Gateway/Http/Client/ClientService.php @@ -123,16 +123,17 @@ public function placeRequest(TransferInterface $transferObject) $client = $this->httpClientFactory->create(); $client->setUri($requestUri); $client->setAuth($transferObject->getAuthUsername(), $transferObject->getAuthPassword()); + $headers = $transferObject->getHeaders(); if (strpos($transferObject->getUri(), $this->action::API_TRANSACTIONS_PATH) !== false) { $idempotencyKey = $this->util->generateIdempotencyKey(); - $client->setHeaders([Util::IDEMPOTENCY_KEY => $idempotencyKey]); + $headers[Util::IDEMPOTENCY_KEY] = $idempotencyKey; } + $client->setHeaders($headers); if (!empty($transferObject->getBody())) { $data = $transferObject->getBody(); $data = json_encode($data, JSON_UNESCAPED_SLASHES); $client->setRawData($data, 'application/json'); } - $response = $client->request($transferObject->getMethod()); $rawResponse = $response->getRawBody(); $response = $this->converter->convert($rawResponse); diff --git a/Gateway/Http/TransferFactory.php b/Gateway/Http/TransferFactory.php index db345e19..5cbbbd1e 100644 --- a/Gateway/Http/TransferFactory.php +++ b/Gateway/Http/TransferFactory.php @@ -37,12 +37,16 @@ public function create(array $request) $method = isset($request['method']) ? $request['method'] : ClientService::POST; // Admin actions will include store id in the request $storeId = isset($request['storeId']) ? $request['storeId'] : $this->getStoreId(); + $country_code = $request['country_code'] ?: 'USA'; return $this->transferBuilder ->setMethod($method) - ->setHeaders(['Content-Type' => 'application/json']) + ->setHeaders([ + 'Content-Type' => 'application/json', + 'Country-Code' => $country_code + ]) ->setBody($request['body']) - ->setAuthUsername($this->getPublicApiKey($storeId)) - ->setAuthPassword($this->getPrivateApiKey($storeId)) + ->setAuthUsername($this->getPublicApiKey($storeId, $country_code)) + ->setAuthPassword($this->getPrivateApiKey($storeId, $country_code)) ->setUri($this->getApiUrl($request['path'], $storeId)) ->build(); } diff --git a/Gateway/Request/AbstractDataBuilder.php b/Gateway/Request/AbstractDataBuilder.php index f3c8963e..2a09002f 100644 --- a/Gateway/Request/AbstractDataBuilder.php +++ b/Gateway/Request/AbstractDataBuilder.php @@ -34,6 +34,8 @@ abstract class AbstractDataBuilder implements BuilderInterface const CHECKOUT_TOKEN = 'checkout_token'; const TRANSACTION_ID = 'transaction_id'; const CHARGE_ID = 'charge_id'; + const COUNTRY_CODE = 'country_code'; + const DEFAULT_COUNTRY_CODE = 'USA'; /**#@-*/ /** diff --git a/Gateway/Request/AuthorizationRequest.php b/Gateway/Request/AuthorizationRequest.php index ea2cb37f..ce927a53 100644 --- a/Gateway/Request/AuthorizationRequest.php +++ b/Gateway/Request/AuthorizationRequest.php @@ -44,11 +44,13 @@ public function build(array $buildSubject) $paymentDataObject = $buildSubject['payment']; $payment = $paymentDataObject->getPayment(); $token = $payment->getAdditionalInformation(self::CHECKOUT_TOKEN); + $countryCode = $payment->getAdditionalInformation(self::COUNTRY_CODE) ?: self::DEFAULT_COUNTRY_CODE; return [ 'body' => [ self::TRANSACTION_ID => $token ], - 'path' => '' + 'path' => '', + 'country_code' => $countryCode ]; } } diff --git a/Gateway/Request/CaptureRequest.php b/Gateway/Request/CaptureRequest.php index e37aabe0..f37766c3 100644 --- a/Gateway/Request/CaptureRequest.php +++ b/Gateway/Request/CaptureRequest.php @@ -46,12 +46,13 @@ public function build(array $buildSubject) $payment = $paymentDataObject->getPayment(); $transactionId = $payment->getAdditionalInformation(self::TRANSACTION_ID) ?: $payment->getAdditionalInformation(self::CHARGE_ID); + $countryCode = $payment->getAdditionalInformation(self::COUNTRY_CODE) ?: self::DEFAULT_COUNTRY_CODE; $order = $payment->getOrder(); $storeId = isset($order) ? $order->getStoreId() : $this->_storeManager->getStore()->getId(); if (!$storeId) { $storeId = null; } - if ($this->affirmPaymentConfig->getPartialCapture()) { + if ($this->affirmPaymentConfig->getPartialCapture($countryCode)) { $_amount = $buildSubject['amount'] ? Util::formatToCents($buildSubject['amount']) : null; $_body = [ 'amount' => $_amount @@ -62,7 +63,8 @@ public function build(array $buildSubject) return [ 'path' => "{$transactionId}/capture", 'storeId' => $storeId, - 'body' => $_body + 'body' => $_body, + 'country_code' => $countryCode, ]; } } diff --git a/Gateway/Request/PreAuthorizationRequest.php b/Gateway/Request/PreAuthorizationRequest.php index 5de7eed4..a7fa8b89 100644 --- a/Gateway/Request/PreAuthorizationRequest.php +++ b/Gateway/Request/PreAuthorizationRequest.php @@ -49,10 +49,12 @@ public function build(array $buildSubject) $paymentDataObject = $buildSubject['payment']; $payment = $paymentDataObject->getPayment(); $token = $payment->getAdditionalInformation(self::CHECKOUT_TOKEN); + $countryCode = $payment->getAdditionalInformation(self::COUNTRY_CODE) ?: self::DEFAULT_COUNTRY_CODE; return [ 'body' => '', 'path' => $token, - 'method' => self::GET + 'method' => self::GET, + 'country_code' => $countryCode ]; } } diff --git a/Gateway/Request/RefundRequest.php b/Gateway/Request/RefundRequest.php index 0f1a0b81..fbf05fdc 100644 --- a/Gateway/Request/RefundRequest.php +++ b/Gateway/Request/RefundRequest.php @@ -46,7 +46,9 @@ public function build(array $buildSubject) $payment = $paymentDataObject->getPayment(); $transactionId = $payment->getAdditionalInformation(self::TRANSACTION_ID) ?: $payment->getAdditionalInformation(self::CHARGE_ID); - $amountInCents = Util::formatToCents($buildSubject['amount']); + $countryCode = $payment->getAdditionalInformation(self::COUNTRY_CODE) ?: self::DEFAULT_COUNTRY_CODE; + $creditMemoAmount = $payment->getCreditmemo()->getGrandTotal(); + $amountInCents = Util::formatToCents($creditMemoAmount); $order = $payment->getOrder(); if($order) { $storeId = $order->getStoreId(); @@ -59,7 +61,8 @@ public function build(array $buildSubject) 'amount' => $amountInCents ], 'path' => "{$transactionId}/refund", - 'storeId' => $storeId + 'storeId' => $storeId, + 'country_code' => $countryCode, ]; } } diff --git a/Gateway/Request/VoidRequest.php b/Gateway/Request/VoidRequest.php index 67ea249b..50e1aed3 100644 --- a/Gateway/Request/VoidRequest.php +++ b/Gateway/Request/VoidRequest.php @@ -45,6 +45,7 @@ public function build(array $buildSubject) $payment = $paymentDataObject->getPayment(); $transactionId = $payment->getAdditionalInformation(self::TRANSACTION_ID) ?: $payment->getAdditionalInformation(self::CHARGE_ID); + $countryCode = $payment->getAdditionalInformation(self::COUNTRY_CODE) ?: self::DEFAULT_COUNTRY_CODE; $order = $payment->getOrder(); if($order) { $storeId = $order->getStoreId(); @@ -55,7 +56,8 @@ public function build(array $buildSubject) return [ 'body' => [], 'path' => "{$transactionId}/void", - 'storeId' => $storeId + 'storeId' => $storeId, + 'country_code' => $countryCode, ]; } } diff --git a/Gateway/Validator/Client/PaymentActionsValidator.php b/Gateway/Validator/Client/PaymentActionsValidator.php index c14c495d..260f5e2d 100644 --- a/Gateway/Validator/Client/PaymentActionsValidator.php +++ b/Gateway/Validator/Client/PaymentActionsValidator.php @@ -27,12 +27,34 @@ class PaymentActionsValidator extends AbstractResponseValidator { /** - * @inheritdoc + * Validate response + * + * @param array $validationSubject + * @return \Magento\Payment\Gateway\Validator\ResultInterface */ public function validate(array $validationSubject) { $response = SubjectReader::readResponse($validationSubject); - $amount = SubjectReader::readAmount($validationSubject); + $amount = ''; + + if ( (isset($response['checkout_status']) && $response['checkout_status'] == 'confirmed') + || (isset($response['status']) && $response['status'] == 'authorized') + || (isset($response['type']) && $response['type'] == 'capture')) + { + // Pre-Auth/Auth/Capture uses amount_ordered from payment + $_payment = $validationSubject['payment']->getPayment(); + $payment_data = $_payment->getData(); + $amount = $payment_data['amount_ordered']; + } elseif ( (isset($response['type']) && $response['type'] == 'refund') ) { + // Refund (including partial) uses grand_total from creditmemo (credit memo invoice) + $_payment = $validationSubject['payment']->getPayment(); + $_creditMemo = $_payment->getData()['creditmemo']; + $amount = $_creditMemo->getGrandTotal(); + } else { + // Partial capture (US only) uses validationSubject + $amount = SubjectReader::readAmount($validationSubject); + } + $amountInCents = Util::formatToCents($amount); $errorMessages = []; diff --git a/Helper/Payment.php b/Helper/Payment.php index bf920dfa..0a217105 100644 --- a/Helper/Payment.php +++ b/Helper/Payment.php @@ -38,9 +38,10 @@ class Payment { /** - * Country code for address validation + * Region (country) code for address validation */ - const VALIDATE_COUNTRY = 'US'; + const VALID_REGIONS = array('US', 'CA'); + const DEFAULT_REGION = 'US'; /** * Affirm payment facade @@ -311,7 +312,7 @@ public function getConfigurableProductBackordersOptions(Product $product = null) public function validateVirtual() { if ($this->quote->getIsVirtual() && !$this->quote->getCustomerIsGuest()) { - $countryId = self::VALIDATE_COUNTRY; + $countryId = self::DEFAULT_REGION; // get customer addresses list $addresses = $this->quote->getCustomer()->getAddresses(); // get default shipping address for the customer @@ -321,12 +322,14 @@ public function validateVirtual() foreach ($addresses as $address) { if ($address->getId() == $defaultShipping) { $countryId = $address->getCountryId(); + if (in_array($countryId, self::VALID_REGIONS)) { + return true; + } else { + return false; + } break; } } - if ($countryId != self::VALIDATE_COUNTRY) { - return false; - } } } return true; diff --git a/Model/Adminhtml/Source/PaymentRegion.php b/Model/Adminhtml/Source/PaymentRegion.php new file mode 100644 index 00000000..82f302a7 --- /dev/null +++ b/Model/Adminhtml/Source/PaymentRegion.php @@ -0,0 +1,33 @@ + '', 'label' => '