We will be off from 27/1 (Monday) to 31/1 (Friday) (GMT +7) for our Tet Holiday (Lunar New Year) in our country

Commit 16ca2716 authored by Alessio Zampatti's avatar Alessio Zampatti Committed by Aleffio

PW-489 Implement terminal API (#279)

* PW-489: Added Initiate call before placing the order

* PW-489: Construct sync call request on Magento instead of on the Util Library, add encryptor on pos api key

* PW-489: Removed logger/comments

* PW-458: Implemented status call every 5 seconds, added timeout on Initiate call

* PW-458: Fixed terminalAPI guest checkout, added configuration for pos_timeout, return immediately if response from Initiate call

* PW-458: Do not show pos_timeout in config

* PW-458: Retrieve quoteid from the backend, added PHP library version requirement, using settimeout instead of setinterval, removed pos_timeout from adyen_pos_cloud xml, renamed Initiate button label back to Place Order, replaced the .done().fail() flow with .always() on the frontend
parent 752e76f3
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment Module
*
* Copyright (c) 2018 Adyen B.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Api;
interface AdyenInitiateTerminalApiInterface
{
/**
* Trigger sync call on terminal
* @return mixed
*/
public function initiate();
}
\ No newline at end of file
......@@ -50,6 +50,7 @@ class TransactionPosCloudSync implements ClientInterface
$client = new \Adyen\Client();
$client->setApplicationName("Magento 2 plugin");
$client->setXApiKey($apiKey);
$client->setTimeout(5);
if ($this->_adyenHelper->isDemoMode()) {
$client->setEnvironment(\Adyen\Environment::TEST);
} else {
......@@ -72,17 +73,51 @@ class TransactionPosCloudSync implements ClientInterface
public function placeRequest(\Magento\Payment\Gateway\Http\TransferInterface $transferObject)
{
$request = $transferObject->getBody();
if(!empty($request['response'])){
//Initiate has already a response
return $request['response'];
}
//always do status call and return the response of the status call
$service = new \Adyen\Service\PosPayment($this->_client);
$transactionType = \Adyen\TransactionType::GOODS_SERVICES;
$poiId = $this->_adyenHelper->getPoiId();
$json = Util::buildPosPaymentRequest($poiId, $request['amount']['value'], $request['amount']['currency'],
$request['reference'], $transactionType);
$params = json_decode($json, true); //Create associative array for passing along
$newServiceID = date("dHis");
//Provide receipt to the shopper
$jsonStatus='{
"SaleToPOIRequest": {
"MessageHeader": {
"ProtocolVersion": "3.0",
"MessageClass": "Service",
"MessageCategory": "TransactionStatus",
"MessageType": "Request",
"ServiceID": "' . $newServiceID . '",
"SaleID": "Magento2CloudStatus",
"POIID": "' . $poiId . '"
},
"TransactionStatusRequest": {
"MessageReference": {
"MessageCategory": "Payment",
"SaleID": "Magento2Cloud",
"ServiceID": "' . $request['serviceID'] . '"
},
"DocumentQualifier" : [
"CashierReceipt",
"CustomerReceipt"
],
"ReceiptReprintFlag" : true
}
}
}';
$params = json_decode($jsonStatus, true); //Create associative array for passing along
try {
$response = $service->runTenderSync($params);
} catch (\Adyen\AdyenException $e) {
$response['error'] = $e->getMessage();
}
return $response;
}
}
\ No newline at end of file
......@@ -38,6 +38,8 @@ class PosCloudBuilder implements BuilderInterface
*/
private $_adyenLogger;
protected $_quoteRepository;
/**
* PaymentDataBuilder constructor.
*
......@@ -46,10 +48,12 @@ class PosCloudBuilder implements BuilderInterface
*/
public function __construct(
\Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Adyen\Payment\Helper\Data $adyenHelper
\Adyen\Payment\Helper\Data $adyenHelper,
\Magento\Quote\Api\CartRepositoryInterface $quoteRepository
) {
$this->_adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper;
$this->_quoteRepository = $quoteRepository;
}
/**
......@@ -59,20 +63,16 @@ class PosCloudBuilder implements BuilderInterface
public function build(array $buildSubject)
{
$paymentDataObject = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($buildSubject);
$order = $paymentDataObject->getOrder();
$payment = $paymentDataObject->getPayment();
$fullOrder = $payment->getOrder();
$currencyCode = $fullOrder->getOrderCurrencyCode();
$grandTotal = $fullOrder->getGrandTotal();
$amount = [
'currency' => $currencyCode,
'value' => $grandTotal
];
$quote = $this->_quoteRepository->getActive($fullOrder->getQuoteId());
return [
"amount" => $amount,
"reference" => $order->getOrderIncrementId()
"response" => $quote->getPayment()->getAdditionalInformation("terminalResponse"),
"serviceID" => $quote->getPayment()->getAdditionalInformation("serviceID")
];
}
}
\ No newline at end of file
......@@ -63,7 +63,6 @@ class PaymentPosCloudHandler implements HandlerInterface
*/
public function handle(array $handlingSubject, array $response)
{
$payment = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($handlingSubject);
/** @var OrderPaymentInterface $payment */
......@@ -75,8 +74,12 @@ class PaymentPosCloudHandler implements HandlerInterface
// no not send order confirmation mail
$payment->getOrder()->setCanSendNewEmailFlag(false);
// set transaction
if (!empty($response['SaleToPOIResponse']['PaymentResponse']['PaymentResult']['PaymentAcquirerData']['AcquirerTransactionID']['TransactionID'])) {
// set transaction(status)
if (!empty($response['SaleToPOIResponse']['TransactionStatusResponse']['RepeatedMessageResponse']['RepeatedResponseMessageBody']['PaymentResponse']['PaymentResult']['PaymentAcquirerData']['AcquirerTransactionID']['TransactionID'])) {
$pspReference = $response['SaleToPOIResponse']['TransactionStatusResponse']['RepeatedMessageResponse']['RepeatedResponseMessageBody']['PaymentResponse']['PaymentResult']['PaymentAcquirerData']['AcquirerTransactionID']['TransactionID'];
$payment->setTransactionId($pspReference);
// set transaction(payment)
} elseif (!empty($response['SaleToPOIResponse']['PaymentResponse']['PaymentResult']['PaymentAcquirerData']['AcquirerTransactionID']['TransactionID'])) {
$pspReference = $response['SaleToPOIResponse']['PaymentResponse']['PaymentResult']['PaymentAcquirerData']['AcquirerTransactionID']['TransactionID'];
$payment->setTransactionId($pspReference);
} else {
......
......@@ -58,7 +58,13 @@ class PosCloudResponseValidator extends AbstractValidator
$isValid = true;
$response = \Magento\Payment\Gateway\Helper\SubjectReader::readResponse($validationSubject);
if ((!empty($response['SaleToPOIResponse']['PaymentResponse']['Response']['Result']) &&
// Check In Progress status call
if (!empty($response['SaleToPOIResponse']['TransactionStatusResponse']['Response']['Result']) && $response['SaleToPOIResponse']['TransactionStatusResponse']['Response']['Result'] == "Failure") {
$errorMsg = __('In Progress');
$errorMessages[] = $errorMsg;
throw new \Magento\Framework\Exception\LocalizedException(__($errorMsg));
}
elseif ((!empty($response['SaleToPOIResponse']['PaymentResponse']['Response']['Result']) &&
$response['SaleToPOIResponse']['PaymentResponse']['Response']['Result'] != 'Success'
) || empty($response['SaleToPOIResponse']['PaymentResponse']['Response']['Result'])
) {
......
......@@ -1188,10 +1188,10 @@ class Data extends AbstractHelper
{
switch ($this->isDemoMode()) {
case true:
$apiKey = $this->getAdyenPosCloudConfigData('api_key_test');
$apiKey = $this->_encryptor->decrypt(trim($this->getAdyenPosCloudConfigData('api_key_test')));
break;
default:
$apiKey = $this->getAdyenPosCloudConfigData('api_key_live');
$apiKey = $this->_encryptor->decrypt(trim($this->getAdyenPosCloudConfigData('api_key_live')));
break;
}
return $apiKey;
......
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment Module
*
* Copyright (c) 2018 Adyen B.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Model;
use Adyen\Payment\Api\AdyenInitiateTerminalApiInterface;
use Adyen\Payment\Model\Ui\AdyenPosCloudConfigProvider;
use Adyen\Util\Util;
use Magento\Payment\Gateway\Http\ClientInterface;
class AdyenInitiateTerminalApi implements AdyenInitiateTerminalApiInterface
{
private $_encryptor;
private $_adyenHelper;
private $_adyenLogger;
private $_recurringType;
private $_appState;
protected $_checkoutSession;
/**
* AdyenInitiateTerminalApi constructor.
* @param \Magento\Framework\Model\Context $context
* @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
* @param \Adyen\Payment\Helper\Data $adyenHelper
* @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
* @param RecurringType $recurringType
* @param array $data
*/
public function __construct(
\Magento\Framework\Model\Context $context,
\Magento\Framework\Encryption\EncryptorInterface $encryptor,
\Adyen\Payment\Helper\Data $adyenHelper,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Adyen\Payment\Model\RecurringType $recurringType,
\Magento\Checkout\Model\Session $_checkoutSession,
array $data = []
) {
$this->_encryptor = $encryptor;
$this->_adyenHelper = $adyenHelper;
$this->_adyenLogger = $adyenLogger;
$this->_recurringType = $recurringType;
$this->_appState = $context->getAppState();
$this->_checkoutSession = $_checkoutSession;
// initialize client
$apiKey = $this->_adyenHelper->getApiKey();
$client = new \Adyen\Client();
$client->setApplicationName("Magento 2 plugin");
$client->setXApiKey($apiKey);
//Set configurable option in M2
$posTimeout = $this->_adyenHelper->getAdyenPosCloudConfigData('pos_timeout');
if (!empty($posTimeout)) {
$client->setTimeout($posTimeout);
}
if ($this->_adyenHelper->isDemoMode()) {
$client->setEnvironment(\Adyen\Environment::TEST);
} else {
$client->setEnvironment(\Adyen\Environment::LIVE);
}
// assign magento log
$client->setLogger($adyenLogger);
$this->_client = $client;
}
/**
* Trigger sync call on terminal
* @return mixed
*/
public function initiate()
{
$quote = $this->_checkoutSession->getQuote();
$payment = $quote->getPayment();
$payment->setMethod(AdyenPosCloudConfigProvider::CODE);
$reference = $quote->reserveOrderId()->getReservedOrderId();
$service = new \Adyen\Service\PosPayment($this->_client);
$transactionType = \Adyen\TransactionType::NORMAL;
$poiId = $this->_adyenHelper->getPoiId();
$serviceID = date("dHis");
$timeStamper = date("Y-m-d") . "T" . date("H:i:s+00:00");
$json = '{
"SaleToPOIRequest": {
"MessageHeader": {
"MessageType": "Request",
"MessageClass": "Service",
"MessageCategory": "Payment",
"SaleID": "Magento2Cloud",
"POIID": "' . $poiId . '",
"ProtocolVersion": "3.0",
"ServiceID": "' . $serviceID . '"
},
"PaymentRequest": {
"SaleData": {
"SaleTransactionID": {
"TransactionID": "' . $reference . '",
"TimeStamp": "' . $timeStamper . '"
},
"TokenRequestedType": "Customer"
},
"PaymentTransaction": {
"AmountsReq": {
"Currency": "' . $quote->getCurrency()->getQuoteCurrencyCode() . '",
"RequestedAmount": ' . $quote->getGrandTotal() . '
}
},
"PaymentData": {
"PaymentType": "' . $transactionType . '"
}
}
}
}
';
$params = json_decode($json, true); //Create associative array for passing along
$quote->getPayment()->getMethodInstance()->getInfoInstance()->setAdditionalInformation('serviceID',
$serviceID);
try {
$response = $service->runTenderSync($params);
} catch (\Adyen\AdyenException $e) {
//Not able to perform a payment
$this->_adyenLogger->addAdyenDebug("adyenexception");
$response['error'] = $e->getMessage();
} catch (\Exception $e) {
//Probably timeout
$quote->getPayment()->getMethodInstance()->getInfoInstance()->setAdditionalInformation('terminalResponse',
null);
$quote->save();
$response['error'] = $e->getMessage();
throw $e;
}
$quote->getPayment()->getMethodInstance()->getInfoInstance()->setAdditionalInformation('terminalResponse',
$response);
$quote->save();
return $response;
}
}
\ No newline at end of file
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment Module
*
* Copyright (c) 2018 Adyen B.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Model;
class AdyenPaymentInformationManagement extends \Magento\Checkout\Model\PaymentInformationManagement
{
/**
* {@inheritDoc}
*/
public function savePaymentInformationAndPlaceOrder(
$cartId,
\Magento\Quote\Api\Data\PaymentInterface $paymentMethod,
\Magento\Quote\Api\Data\AddressInterface $billingAddress = null
) {
$this->savePaymentInformation($cartId, $paymentMethod, $billingAddress);
try {
$orderId = $this->cartManagement->placeOrder($cartId);
} catch (\Exception $e) {
throw $e;
}
return $orderId;
}
}
\ No newline at end of file
......@@ -167,6 +167,7 @@
<can_refund>1</can_refund>
<can_void>1</can_void>
<can_cancel>1</can_cancel>
<pos_timeout>30</pos_timeout>
<group>adyen</group>
</adyen_pos_cloud>
<adyen_pay_by_mail>
......
......@@ -915,4 +915,6 @@
<preference for="Adyen\Payment\Api\GuestAdyenPaymentMethodManagementInterface" type="Adyen\Payment\Model\GuestAdyenPaymentMethodManagement" />
<preference for="Adyen\Payment\Api\AdyenPaymentMethodManagementInterface" type="Adyen\Payment\Model\AdyenPaymentMethodManagement" />
<preference for="Adyen\Payment\Api\AdyenRequestMerchantSessionInterface" type="Adyen\Payment\Model\AdyenRequestMerchantSession" />
<preference for="Adyen\Payment\Api\AdyenInitiateTerminalApiInterface" type="Adyen\Payment\Model\AdyenInitiateTerminalApi" />
<preference for="Magento\Checkout\Api\PaymentInformationManagementInterface" type="Adyen\Payment\Model\AdyenPaymentInformationManagement" />
</config>
\ No newline at end of file
......@@ -50,4 +50,11 @@
<resource ref="anonymous"/>
</resources>
</route>
<route url="/V1/adyen/initiate" method="POST">
<service class="Adyen\Payment\Api\AdyenInitiateTerminalApiInterface" method="initiate"/>
<resources>
<resource ref="anonymous"/>
</resources>
</route>
</routes>
\ No newline at end of file
......@@ -25,11 +25,22 @@
define(
[
'ko',
'jquery',
'Magento_Checkout/js/view/payment/default',
'Adyen_Payment/js/action/set-payment-method',
'Magento_Checkout/js/model/payment/additional-validators'
'Magento_Checkout/js/model/payment/additional-validators',
'Magento_Checkout/js/action/place-order',
'Magento_Checkout/js/model/quote',
'Magento_CheckoutAgreements/js/model/agreements-assigner',
'Magento_Customer/js/model/customer',
'Magento_Checkout/js/model/url-builder',
'mage/storage',
'Magento_Checkout/js/model/full-screen-loader',
'Magento_Checkout/js/model/error-processor',
'Magento_Ui/js/model/messages',
'Magento_Checkout/js/action/redirect-on-success'
],
function (ko, Component, setPaymentMethodAction, additionalValidators) {
function (ko, $, Component, setPaymentMethodAction, additionalValidators, placeOrderAction, quote, agreementsAssigner, customer, urlBuilder, storage, fullScreenLoader, errorProcessor, Messages, redirectOnSuccessAction) {
'use strict';
return Component.extend({
......@@ -38,7 +49,50 @@ define(
template: 'Adyen_Payment/payment/pos-cloud-form'
},
showLogo: function() {
initiate: function () {
var self = this,
serviceUrl,
paymentData = quote.paymentMethod();
// use core code to assign the agreement
agreementsAssigner(paymentData);
serviceUrl = urlBuilder.createUrl('/adyen/initiate', {});
fullScreenLoader.startLoader();
return storage.post(
serviceUrl
).always(function(){
self.placeOrderPos()});
return false;
},
posComplete: function () {
this.afterPlaceOrder();
if (this.redirectAfterPlaceOrder) {
redirectOnSuccessAction.execute();
}
},
placeOrderPos: function () {
var self = this;
return $.when(
placeOrderAction(self.getData(), new Messages())
).fail(
function (response) {
if (response.responseText.indexOf("In Progress") > -1) {
window.setTimeout(function(){
self.placeOrderPos()},5000);
return;
}
errorProcessor.process(response);
fullScreenLoader.stopLoader();
self.isPlaceOrderActionAllowed(true);
}
).done(
function () {
self.posComplete();
}
)
},
showLogo: function () {
return window.checkoutConfig.payment.adyen.showLogo;
},
validate: function () {
......
......@@ -60,7 +60,7 @@
<button class="action primary checkout"
type="submit"
data-bind="
click: placeOrder,
click: initiate,
attr: {title: $t('Place Order')},
enable: (getCode() == isChecked()),
css: {disabled: !isPlaceOrderActionAllowed()}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment