Newer
Older
/**
* 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';
const ATTR_ORDER_CAPTURED = 'amazon-OrderCaptures';
const ATTR_ORDER_REFERENCE_SET = 'amazon-OrderReferenceSet';
const ATTR_RECONFIRM_ORDER = 'amazon-ReconfirmOrder';
/**
* 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');
/**
* Return the payment icon (the URL path)
* Can be overwritten
*
* @return string
*/
public function getIcon()
{
return URL_OPT_DIR.'quiqqer/payment-amazon/bin/images/Payment.jpg';
}
* This method returns the payment success type
*
* @param string $hash - Vorgangsnummer - hash number - procedure number
* @return bool
*/
public function isSuccessful($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;
}
return $Order->getPaymentDataEntry(self::ATTR_ORDER_AUTHORIZED);
/**
* 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\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
try {
$response = $this->getResponseData($Response);
} catch (AmazonPayException $Exception) {
$Order->addHistory(
'Amazon Pay :: An error occurred while trying to validate the Capture -> ' . $Exception->getMessage()
);
$Order->update(QUI::getUsers()->getSystemUser());
return;
}
$PriceCalculation = $Order->getPriceCalculation();
$targetSum = $PriceCalculation->getSum()->precision(2)->get();
$targetCurrencyCode = $Order->getCurrency()->getCode();
$captureData = $response['GetCaptureDetailsResult']['CaptureDetails'];
$actualSum = $captureData['CaptureAmount']['Amount'];
$actualCurrencyCode = $captureData['CaptureAmount']['CurrencyCode'];
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 . ' ' . $targetCurrencyCode
. ' | Actual sum captured by Amazon: ' . $actualSum . ' ' . $actualCurrencyCode
$Order->update(QUI::getUsers()->getSystemUser());
$Order->addHistory('Amazo Pay :: Finalize Order payment');
$Order->addHistory('Amazo Pay :: Closing OrderReference');
$this->closeOrderReference($Order);
$Order->addHistory('Amazo Pay :: Order successfully paid');
$Order->update(QUI::getUsers()->getSystemUser());
}
/**
* 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)
$Order->addHistory('Amazon Pay :: Authorize payment');
if ($Order->getPaymentDataEntry(self::ATTR_ORDER_AUTHORIZED)) {
$Order->addHistory('Amazon Pay :: Authorization already exist');
return;
}
$AmazonPay = $this->getAmazonPayClient();
$PriceCalculation = $Order->getPriceCalculation();
$reconfirmOrder = $Order->getPaymentDataEntry(self::ATTR_RECONFIRM_ORDER);
// Re-confirm Order after previously declined Authorization because of "InvalidPaymentMethod"
if ($reconfirmOrder) {
$Order->addHistory(
'Amazon Pay :: Re-confirm Order after declined Authorization because of "InvalidPaymentMethod"'
);
$orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
$Response = $AmazonPay->confirmOrderReference(array(
'amazon_order_reference_id' => $orderReferenceId
));
$this->getResponseData($Response); // check response data
$Order->setPaymentData(self::ATTR_RECONFIRM_ORDER, false);
$Order->addHistory('Amazon Pay :: OrderReference re-confirmed');
} elseif (!$Order->getPaymentDataEntry(self::ATTR_ORDER_REFERENCE_SET)) {
$Order->addHistory(
'Amazon Pay :: Setting details of the Order to Amazon Pay API'
);
$Response = $AmazonPay->setOrderReferenceDetails(array(
'amazon_order_reference_id' => $orderReferenceId,
'amount' => $PriceCalculation->getSum()->precision(2)->get(),
'currency_code' => $Order->getCurrency()->getCode(),
'seller_order_id' => $Order->getId()
$response = $this->getResponseData($Response);
$orderReferenceDetails = $response['SetOrderReferenceDetailsResult']['OrderReferenceDetails'];
if (isset($orderReferenceDetails['Constraints']['Constraint']['ConstraintID'])) {
$Order->addHistory(
'Amazon Pay :: An error occurred while setting the details of the Order: "'
. $orderReferenceDetails['Constraints']['Constraint']['ConstraintID'] . '""'
);
$this->throwAmazonPayException(
$orderReferenceDetails['Constraints']['Constraint']['ConstraintID'],
array(
'reRenderWallet' => 1
)
);
}
$AmazonPay->confirmOrderReference(array(
'amazon_order_reference_id' => $orderReferenceId
));
$Order->setPaymentData(self::ATTR_ORDER_REFERENCE_SET, true);
$Order->update(QUI::getUsers()->getSystemUser());
$Order->addHistory('Amazon Pay :: Requesting new Authorization');
$authorizationReferenceId = $this->getNewAuthorizationReferenceId($Order);
$Response = $AmazonPay->authorize(array(
'amazon_order_reference_id' => $orderReferenceId,
'authorization_amount' => $PriceCalculation->getSum()->precision(2)->get(),
'authorization_reference_id' => $authorizationReferenceId,
'transaction_timeout' => 0 // get authorization status synchronously
// save reference ids in $Order
$authorizationDetails = $response['AuthorizeResult']['AuthorizationDetails'];
$amazonAuthorizationId = $authorizationDetails['AmazonAuthorizationId'];
$this->addAuthorizationReferenceIdToOrder($authorizationReferenceId, $Order);
$Order->setPaymentData(self::ATTR_AMAZON_AUTHORIZATION_ID, $amazonAuthorizationId);
$Order->setPaymentData(self::ATTR_AMAZON_ORDER_REFERENCE_ID, $orderReferenceId);
$Order->update(QUI::getUsers()->getSystemUser());
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
// check Authorization
$Order->addHistory('Amazon Pay :: Checking Authorization status');
$status = $authorizationDetails['AuthorizationStatus'];
$state = $status['State'];
switch ($state) {
case 'Open':
// everything is fine
$Order->addHistory(
'Amazon Pay :: Authorization is OPEN an can be used for capturing'
);
$Order->setPaymentData(self::ATTR_ORDER_AUTHORIZED, true);
$Order->update(QUI::getUsers()->getSystemUser());
break;
case 'Declined':
$reason = $status['ReasonCode'];
switch ($reason) {
case 'InvalidPaymentMethod':
$Order->addHistory(
'Amazon Pay :: Authorization was DECLINED. User has to choose another payment method.'
. ' ReasonCode: "' . $reason . '"'
);
$Order->setPaymentData(self::ATTR_RECONFIRM_ORDER, true);
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
$Order->update(QUI::getUsers()->getSystemUser());
$this->throwAmazonPayException($reason, array(
'reRenderWallet' => 1
));
break;
case 'TransactionTimedOut':
$Order->addHistory(
'Amazon Pay :: Authorization was DECLINED. User has to choose another payment method.'
. ' ReasonCode: "' . $reason . '"'
);
$AmazonPay->cancelOrderReference(array(
'amazon_order_reference_id' => $orderReferenceId,
'cancelation_reason' => 'Order #' . $Order->getHash() . ' could not be authorized :: TransactionTimedOut'
));
$Order->setPaymentData(self::ATTR_ORDER_REFERENCE_SET, false);
$Order->update(QUI::getUsers()->getSystemUser());
$this->throwAmazonPayException($reason, array(
'reRenderWallet' => 1,
'orderCancelled' => 1
));
break;
default:
$Order->addHistory(
'Amazon Pay :: Authorization was DECLINED. OrderReference has to be closed. Cannot use Amazon Pay for this Order.'
. ' ReasonCode: "' . $reason . '"'
);
$Response = $AmazonPay->getOrderReferenceDetails(array(
'amazon_order_reference_id' => $orderReferenceId
));
$response = $Response->toArray();
$orderReferenceDetails = $response['GetOrderReferenceDetailsResult']['OrderReferenceDetails'];
$orderReferenceStatus = $orderReferenceDetails['OrderReferenceStatus']['State'];
if ($orderReferenceStatus === 'Open') {
$AmazonPay->cancelOrderReference(array(
'amazon_order_reference_id' => $orderReferenceId,
'cancelation_reason' => 'Order #' . $Order->getHash() . ' could not be authorized'
));
$Order->setPaymentData(self::ATTR_AMAZON_ORDER_REFERENCE_ID, false);
}
$Order->clearPayment();
$Order->update(QUI::getUsers()->getSystemUser());
$this->throwAmazonPayException($reason);
$reason = $status['ReasonCode'];
$Order->addHistory(
'Amazon Pay :: Authorization cannot be used because it is in state "' . $state . '".'
. ' ReasonCode: "' . $reason . '"'
);
* Capture the actual payment for an Order
*
* @param AbstractOrder $Order
* @return void
*/
public function capturePayment(AbstractOrder $Order)
{
if ($Order->getPaymentDataEntry(self::ATTR_ORDER_CAPTURED)) {
$Order->addHistory('Amazon Pay :: Capture is already completed');
return;
}
$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) {
'Amazon Pay :: Capture failed because the Order has no OPEN 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;
}
$PriceCalculation = $Order->getPriceCalculation();
$sum = $PriceCalculation->getSum()->precision(2)->get();
$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,
'seller_capture_note' => $this->getLocale()->get(
'quiqqer/payment-amazon',
'payment.capture.seller_capture_note',
array(
'orderId' => $Order->getId()
)
)
));
$response = $this->getResponseData($Response);
$captureDetails = $response['CaptureResult']['CaptureDetails'];
$amazonCaptureId = $captureDetails['AmazonCaptureId'];
$this->addCaptureReferenceIdToOrder($amazonCaptureId, $Order);
$Order->setPaymentData(self::ATTR_AMAZON_CAPTURE_ID, $amazonCaptureId);
$Order->update(QUI::getUsers()->getSystemUser());
// Check Capture
$Order->addHistory('Amazon Pay :: Checking Capture status');
$status = $captureDetails['CaptureStatus'];
$state = $status['State'];
switch ($state) {
case 'Completed':
$Order->addHistory(
'Amazon Pay :: Capture is COMPLETED -> ' . $sum . ' ' . $Order->getCurrency()->getCode()
);
$Order->setPaymentData(self::ATTR_ORDER_CAPTURED, true);
$Order->update(QUI::getUsers()->getSystemUser());
break;
default:
$reason = $status['ReasonCode'];
$Order->addHistory(
'Amazon Pay :: Capture operation failed with state "' . $state . '".'
. ' ReasonCode: "' . $reason . '"'
);
// @todo Change order status to "problems with Amazon Payment"
$this->throwAmazonPayException($reason);
break;
}
}
/**
* Set the Amazon Pay OrderReference to status CLOSED
*
* @param AbstractOrder $Order
* @param string $reason (optional) - Close reason [default: "Order #hash completed"]
protected function closeOrderReference(AbstractOrder $Order, $reason = null)
$orderReferenceId = $Order->getPaymentDataEntry(self::ATTR_AMAZON_ORDER_REFERENCE_ID);
$AmazonPay->closeOrderReference(array(
'amazon_order_reference_id' => $orderReferenceId,
'closure_reason' => $reason ?: 'Order #' . $Order->getHash() . ' completed'
));
}
/**
* 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 array $exceptionAttributes (optional) - Additional Exception attributes that may be relevant for the Frontend
* @return string
*
* @throws AmazonPayException
*/
protected function throwAmazonPayException($errorCode, $exceptionAttributes = array())
$L = $this->getLocale();
$lg = 'quiqqer/payment-amazon';
$msg = $L->get($lg, 'payment.error_msg.general_error');
$msg = $L->get($lg, 'payment.error_msg.' . $errorCode);
break;
}
$Exception = new AmazonPayException($msg);
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);
protected function addAuthorizationReferenceIdToOrder($authorizationReferenceId, AbstractOrder $Order)
$authorizationReferenceIds = $Order->getPaymentDataEntry(self::ATTR_AUTHORIZATION_REFERENCE_IDS);
if (empty($authorizationReferenceIds)) {
$authorizationReferenceIds = array();
}
$authorizationReferenceIds[] = $authorizationReferenceId;
$Order->setPaymentData(self::ATTR_AUTHORIZATION_REFERENCE_IDS, $authorizationReferenceIds);
$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);
protected function addCaptureReferenceIdToOrder($captureReferenceId, AbstractOrder $Order)
$captureReferenceIds = $Order->getPaymentDataEntry(self::ATTR_CAPTURE_REFERENCE_IDS);
if (empty($captureReferenceIds)) {
$captureReferenceIds = array();
}
$captureReferenceIds[] = $captureReferenceId;
$Order->setPaymentData(self::ATTR_CAPTURE_REFERENCE_IDS, $captureReferenceIds);
$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;
}