Newer
Older
use QUI\ERP\Order\Handler as OrderHandler;
use QUI\ERP\Accounting\Payments\Gateway\Gateway;
/**
* Class Payment
*/
class Payment extends QUI\ERP\Accounting\Payments\Api\AbstractPayment
{
/**
* Amazon API Order attributes
*/
const ATTR_AUTHORIZATION_REFERENCE_IDS = 'amazon-AuthorizationReferenceIds';
const ATTR_AMAZON_AUTHORIZATION_ID = 'amazon-AmazonAuthorizationId';
const ATTR_AMAZON_CAPTURE_ID = 'amazon-AmazonCaptureId';
const ATTR_AMAZON_ORDER_REFERENCE_ID = 'amazon-OrderReferenceId';
const ATTR_CAPTURE_REFERENCE_IDS = 'amazon-CaptureReferenceIds';
/**
* Setting options
*/
const SETTING_ARTICLE_TYPE_MIXED = 'mixed';
const SETTING_ARTICLE_TYPE_PHYSICAL = 'physical';
const SETTING_ARTICLE_TYPE_DIGITAL = 'digital';
/**
* Amazon Pay PHP SDK Client
*
* @var AmazonPayClient
*/
protected $AmazonPayClient = null;
/**
* Current Order that is being processed
*
* @var AbstractOrder
*/
protected $Order = null;
/**
* @return string
*/
public function getTitle()
{
return $this->getLocale()->get('quiqqer/payment-amazon', 'payment.title');
}
/**
* @return string
*/
public function getDescription()
{
return $this->getLocale()->get('quiqqer/payment-amazon', 'payment.description');
* This method returns the payment success type
*
* @param string $hash - Vorgangsnummer - hash number - procedure number
* @return bool
*/
public function isSuccessful($hash)
{
\QUI\System\Log::writeRecursive("check sucess $hash");
try {
$Order = OrderHandler::getInstance()->getOrderByHash($hash);
} catch (\Exception $Exception) {
QUI\System\Log::addError(
'Amazon Pay :: Cannot check if payment process for Order #' . $hash . ' is successful'
. ' -> ' . $Exception->getMessage()
);
return false;
}
$orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
if (empty($orderReferenceId)) {
return false;
}
try {
self::authorizePayment($orderReferenceId, $Order);
} catch (AmazonPayException $Exception) {
return false;
} catch (\Exception $Exception) {
QUI\System\Log::writeException($Exception);
return false;
}
// If payment is authorized everyhting is fine
return true;
/**
* Is the payment a gateway payment?
*
* @return bool
*/
public function isGateway()
{
return true;
}
/**
* @return bool
*/
public function refundSupport()
{
return true;
}
/**
* Execute the request from the payment provider
*
* @param QUI\ERP\Accounting\Payments\Gateway\Gateway $Gateway
* @throws QUI\ERP\Accounting\Payments\Exception
* @throws QUI\ERP\Accounting\Payments\Transactions\Exception
*/
public function executeGatewayPayment(QUI\ERP\Accounting\Payments\Gateway\Gateway $Gateway)
{
$AmazonPay = $this->getAmazonPayClient();
$Order = $Gateway->getOrder();
$Order->addHistory('Amazon Pay :: Check if payment from Amazon was successful');
$amazonCaptureId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_CAPTURE_ID);
$Response = $AmazonPay->getCaptureDetails(array(
'amazon_capture_id' => $amazonCaptureId
));
$response = $this->getResponseData($Response);
// check the amount that has already been captured
$calculations = $Order->getArticles()->getCalculations();
$targetSum = $calculations['sum'];
$currencyCode = $Order->getCurrency()->getCode();
$captureData = $response['GetCaptureDetailsResult']['CaptureDetails'];
$actualSum = $captureData['CaptureAmount']['Amount'];
if ($actualSum < $targetSum) {
$Order->addHistory(
'Amazon Pay :: The amount that was captured from Amazon was less than the'
. ' total sum of the order. Total sum: ' . $targetSum . ' ' . $currencyCode
. ' | Actual sum captured by Amazon: ' . $actualSum . ' ' . $currencyCode
);
return;
}
// book payment in QUIQQER ERP
$Gateway->purchase(
$actualSum,
new QUI\ERP\Currency\Currency($currencyCode),
$Order,
$this
);
}
/**
* Execute a refund
*
* @param QUI\ERP\Accounting\Payments\Transactions\Transaction $Transaction
*/
public function refund(QUI\ERP\Accounting\Payments\Transactions\Transaction $Transaction)
{
// @todo
}
/**
* If the Payment method is a payment gateway, it can return a gateway display
*
* @param AbstractOrder $Order
* @param QUI\ERP\Order\Controls\OrderProcess\Processing $Step
* @return string
*/
public function getGatewayDisplay(AbstractOrder $Order, $Step = null)
{
$Control = new PaymentDisplay();
$Control->setAttribute('Order', $Order);
$Control->setAttribute('Payment', $this);
$Step->setTitle(
QUI::getLocale()->get(
'quiqqer/payment-amazon',
'payment.step.title'
)
);
$Engine = QUI::getTemplateManager()->getEngine();
$Step->setContent($Engine->fetch(dirname(__FILE__) . '/PaymentDisplay.Header.html'));
*
* @param string $orderReferenceId
* @param AbstractOrder $Order
*
* @throws AmazonPayException
*/
public function authorizePayment($orderReferenceId, AbstractOrder $Order)
$AmazonPay = $this->getAmazonPayClient();
$this->Order = $Order;
$Order->addHistory('Amazon Pay :: Authorize payment');
// $Order->setPaymentData(self::ATTR_AMAZON_AUTHORIZATION_ID, null);
$calculations = $Order->getArticles()->getCalculations();
$amazonAuthorizationId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_AUTHORIZATION_ID);
if (!empty($amazonAuthorizationId)) {
$Order->addHistory('Amazon Pay :: Authorization already exist');
// check if an Authorization already exists and is in state OPEN
$Response = $AmazonPay->getAuthorizationDetails(array(
'amazon_authorization_id' => $amazonAuthorizationId
));
$response = $this->getResponseData($Response);
$this->checkAuthorizationStatus($response['GetAuthorizationDetailsResult']['AuthorizationDetails']);
return;
}
$Order->addHistory('Amazon Pay :: Authorization does not exist. Requesting new Authorization.');
$authorizationReferenceId = $this->getNewAuthorizationReferenceId($Order);
$Response = $AmazonPay->charge(array(
'amazon_reference_id' => $orderReferenceId,
'charge_amount' => $calculations['sum'],
'currency_code' => $Order->getCurrency()->getCode(),
'authorization_reference_id' => $authorizationReferenceId,
'charge_order_id' => $Order->getId(),
'transaction_timeout' => 0 // get authorization status synchronously
if (isset($response['SetOrderReferenceDetailsResult']['OrderReferenceDetails']['Constraints']['Constraint']['ConstraintID'])) {
'Amazon Pay :: An error occurred while requesting the Authorization: "'
. $response['SetOrderReferenceDetailsResult']['OrderReferenceDetails']['Constraints']['Constraint']['ConstraintID'] . '""'
);
$this->throwAmazonPayException(
$response['SetOrderReferenceDetailsResult']['OrderReferenceDetails']['Constraints']['Constraint']['ConstraintID']
);
}
// save reference ids in $Order
$authorizationDetails = $response['AuthorizeResult']['AuthorizationDetails'];
$amazonAuthorizationId = $authorizationDetails['AmazonAuthorizationId'];
$this->addAuthorizationReferenceIdToOrder($authorizationReferenceId);
$Order->setPaymentData(self::ATTR_AMAZON_AUTHORIZATION_ID, $amazonAuthorizationId);
$Order->setPaymentData(self::ATTR_AMAZON_ORDER_REFERENCE_ID, $orderReferenceId);
$Order->update(QUI::getUsers()->getSystemUser());
$this->checkAuthorizationStatus($authorizationDetails);
$Order->addHistory('Amazon Pay :: Authorization request successful');
* Capture the actual payment for an Order
*
* @param AbstractOrder $Order
* @return void
* @throws AmazonPayException
*/
public function capturePayment(AbstractOrder $Order)
{
$this->Order = $Order;
$AmazonPay = $this->getAmazonPayClient();
$orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
if (empty($orderReferenceId)) {
$Order->addHistory(
'Amazon Pay :: Capture failed because the Order has no AmazonOrderReferenceId'
);
throw new AmazonPayException(array(
'quiqqer/payment-amazon',
'exception.Payment.capture.not_authorized',
array(
'orderHash' => $Order->getHash()
)
));
}
try {
$this->authorizePayment($orderReferenceId, $Order);
} catch (AmazonPayException $Exception) {
$Order->addHistory(
'Amazon Pay :: Capture failed because the Order has no Authorization'
);
throw new AmazonPayException(array(
'quiqqer/payment-amazon',
'exception.Payment.capture.not_authorized',
array(
'orderHash' => $Order->getHash()
)
));
} catch (\Exception $Exception) {
$Order->addHistory(
'Amazon Pay :: Capture failed because of an error: ' . $Exception->getMessage()
);
QUI\System\Log::writeException($Exception);
return;
}
$calculations = $Order->getArticles()->getCalculations();
$sum = $calculations['sum'];
// check if $sum was already fully captured
$amazonCaptureId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_CAPTURE_ID);
if (!empty($amazonCaptureId)) {
$Order->addHistory('Amazon Pay :: Capture already exists');
$Response = $AmazonPay->getCaptureDetails(array(
'amazon_capture_id' => $amazonCaptureId
));
\QUI\System\Log::writeRecursive($this->getResponseData($Response));
return;
}
$captureReferenceId = $this->getNewCaptureReferenceId($Order);
$Response = $AmazonPay->capture(array(
'amazon_authorization_id' => $Order->getPaymentDataEntry(self::ATTR_AMAZON_AUTHORIZATION_ID),
'capture_amount' => $sum,
'currency_code' => $Order->getCurrency()->getCode(),
'capture_reference_id' => $captureReferenceId
));
$response = $this->getResponseData($Response);
$captureDetails = $response['CaptureResult']['CaptureDetails'];
$amazonCaptureId = $captureDetails['AmazonCaptureId'];
$this->addCaptureReferenceIdToOrder($amazonCaptureId);
$Order->setPaymentData(self::ATTR_AMAZON_CAPTURE_ID, $amazonCaptureId);
$Order->update(QUI::getUsers()->getSystemUser());
$this->checkCaptureStatus($captureDetails);
$Order->addHistory(
'Amazon Pay :: Capture successful -> ' . $calculations['sum'] . ' ' . $Order->getCurrency()->getCode()
);
}
/**
* Set the Amazon Pay OrderReference to status CLOSED
*
* @return void
*/
protected function closeOrderReference()
{
$AmazonPay = $this->getAmazonPayClient();
$orderReferenceId = $this->Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
}
/**
* Check AuthorizationDetails of an Amazon Pay Authorization
*
* If "State" is "Open" everything is fine and the payment can be captured
*
* @param array $authorizationDetails
* @return void
* @throws AmazonPayException
*/
public function checkAuthorizationStatus($authorizationDetails)
{
$this->Order->addHistory('Amazon Pay :: Checking Authorization status');
$status = $authorizationDetails['AuthorizationStatus'];
$state = $status['State'];
switch ($state) {
case 'Open':
$this->Order->addHistory(
'Amazon Pay :: Authorization is OPEN an can be used for charging'
);
return; // everything is fine
break;
default:
$this->Order->addHistory(
'Amazon Pay :: Authorization cannot be used because it is in state "' . $state . '".'
. ' ReasonCode: "' . $status['ReasonCode'] . '"'
);
$this->throwAmazonPayException($status['ReasonCode']);
break;
}
}
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/**
* Check CaptureDetails of an Amazon Pay Authorization
*
* If "State" is "Completed" everything is fine and the payment is completed
*
* @param array $captureDetails
* @return void
* @throws AmazonPayException
*/
public function checkCaptureStatus($captureDetails)
{
$this->Order->addHistory('Amazon Pay :: Checking Capture status');
$status = $captureDetails['CaptureStatus'];
$state = $status['State'];
switch ($state) {
case 'Completed':
$this->Order->addHistory(
'Amazon Pay :: Capture is COMPLETED'
);
return; // everything is fine
break;
default:
$this->Order->addHistory(
'Amazon Pay :: Capture operation failed with state "' . $state . '".'
. ' ReasonCode: "' . $status['ReasonCode'] . '"'
);
$this->throwAmazonPayException($status['ReasonCode']);
break;
}
}
/**
* Check if the Amazon Pay API response is OK
*
* @param ResponseInterface $Response - Amazon Pay API Response
* @return array
* @throws AmazonPayException
*/
protected function getResponseData(ResponseInterface $Response)
{
$response = $Response->toArray();
if (!empty($response['Error']['Code'])) {
$this->throwAmazonPayException($response['Error']['Code']);
}
return $response;
}
/**
* Throw AmazonPayException for specific Amazon API Error
*
* @param string $errorCode
* @return string
*
* @throws AmazonPayException
*/
protected function throwAmazonPayException($errorCode)
{
$L = $this->getLocale();
$lg = 'quiqqer/payment-amazon';
$msg = $L->get($lg, 'payment.error_msg.general_error');
$reRenderWallet = false;
switch ($errorCode) {
case 'InvalidPaymentMethod':
case 'TransactionTimedOut':
case 'AmazonRejected':
$msg = $L->get($lg, 'payment.error_msg.' . $errorCode);
break;
default:
}
$Exception = new AmazonPayException($msg);
$Exception->setAttribute('reRenderWallet', $reRenderWallet);
throw $Exception;
}
/**
* Generate a unique, random Authorization Reference ID to identify
* authorization transactions for an order
*
* @param AbstractOrder $Order
* @return string
*/
protected function getNewAuthorizationReferenceId(AbstractOrder $Order)
{
return mb_substr('a_' . $Order->getId() . '_' . uniqid(), 0, 32);
*
* @param string $authorizationReferenceId
* @return void
*/
protected function addAuthorizationReferenceIdToOrder($authorizationReferenceId)
$authorizationReferenceIds = $this->Order->getPaymentDataEntry(self::ATTR_AUTHORIZATION_REFERENCE_IDS);
if (empty($authorizationReferenceIds)) {
$authorizationReferenceIds = array();
}
$authorizationReferenceIds[] = $authorizationReferenceId;
$this->Order->setPaymentData(self::ATTR_AUTHORIZATION_REFERENCE_IDS, $authorizationReferenceIds);
$this->Order->update(QUI::getUsers()->getSystemUser());
}
/**
* Generate a unique, random CaptureReferenceId to identify
* captures for an order
*
* @param AbstractOrder $Order
* @return string
*/
protected function getNewCaptureReferenceId(AbstractOrder $Order)
return mb_substr('c_' . $Order->getId() . '_' . uniqid(), 0, 32);
*
* @param string $captureReferenceId
* @return void
*/
protected function addCaptureReferenceIdToOrder($captureReferenceId)
$captureReferenceIds = $this->Order->getPaymentDataEntry(self::ATTR_CAPTURE_REFERENCE_IDS);
if (empty($captureReferenceIds)) {
$captureReferenceIds = array();
}
$captureReferenceIds[] = $captureReferenceId;
$this->Order->setPaymentData(self::ATTR_CAPTURE_REFERENCE_IDS, $captureReferenceIds);
$this->Order->update(QUI::getUsers()->getSystemUser());
}
/**
* Get Amazon Pay Client for current payment process
*
* @return AmazonPayClient
*/
protected function getAmazonPayClient()
{
if (!is_null($this->AmazonPayClient)) {
return $this->AmazonPayClient;
}
$this->AmazonPayClient = new AmazonPayClient(array(
'merchant_id' => Provider::getApiSetting('merchant_id'),
'access_key' => Provider::getApiSetting('access_key'),
'secret_key' => Provider::getApiSetting('secret_key'),
'client_id' => Provider::getApiSetting('client_id'),
'sandbox' => boolval(Provider::getApiSetting('sandbox')),
'region' => Provider::getApiSetting('region')
));
return $this->AmazonPayClient;
}