Skip to content
Code-Schnipsel Gruppen Projekte
AbstractBasePayment.php 17,3 KiB
Newer Older
Patrick Müller's avatar
Patrick Müller committed
<?php

namespace QUI\ERP\Payments\Stripe;

use QUI;
Patrick Müller's avatar
Patrick Müller committed
use QUI\ERP\Accounting\Payments\Gateway\Gateway;
use QUI\ERP\Accounting\Payments\Transactions\Factory as TransactionFactory;
Patrick Müller's avatar
Patrick Müller committed
use QUI\ERP\Accounting\Payments\Transactions\Transaction;
use QUI\ERP\Order\AbstractOrder;
use QUI\ERP\Order\Handler as OrderHandler;
Patrick Müller's avatar
Patrick Müller committed
use Stripe\Exception\ApiErrorException;
use Stripe\PaymentIntent as StripePaymentIntent;
Patrick Müller's avatar
Patrick Müller committed
use Stripe\Refund as StripeRefund;
Patrick Müller's avatar
Patrick Müller committed

/**
Patrick Müller's avatar
Patrick Müller committed
 * Class BasePayment
Patrick Müller's avatar
Patrick Müller committed
 *
Patrick Müller's avatar
Patrick Müller committed
 * Main Payment class for all Stripe payment methods
Patrick Müller's avatar
Patrick Müller committed
 */
abstract class AbstractBasePayment extends QUI\ERP\Accounting\Payments\Api\AbstractPayment
Patrick Müller's avatar
Patrick Müller committed
{
    /**
Patrick Müller's avatar
Patrick Müller committed
     * Stripe API Order attributes
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    const ATTR_STRIPE_PAYMENT_INTENT_ID = 'stripe-PaymentIntentId';
Patrick Müller's avatar
Patrick Müller committed
    const ATTR_STRIPE_PAYMENT_METHOD_ID = 'stripe-PaymentMethodId';
    const ATTR_STRIPE_REFUND_ID = 'stripe-RefundId';
    const ATTR_STRIPE_ORDER_SUCCESSFUL = 'stripe-OrderSuccessful';
    const ATTR_STRIPE_BILLING_PLAN_ID = 'stripe-BillingPlanId';
    const ATTR_STRIPE_SUBSCRIPTION_ID = 'stripe-SubscriptionId';
    const ATTR_STRIPE_INVOICE_ID = 'stripe-InvoiceId';

    /**
     * Stripe User attributes
     */
    const USER_ATTR_STRIPE_CUSTOMER_ID = 'quiqqer.payment.stripe.customer_id';
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Error codes
     */
    const ERROR_GENERAL = 'error_general';
    const ERROR_REFUND = 'error_refund';
    const ERROR_REFUND_STATUS = 'error_refund_status';
Patrick Müller's avatar
Patrick Müller committed
    const ERROR_REFUND_MISSING_PAYMENT_INTENT_ID = 'error_missing_payment_intent_id';

Patrick Müller's avatar
Patrick Müller committed
    /**
Patrick Müller's avatar
Patrick Müller committed
     * BasePayment constructor.
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    public function __construct()
Patrick Müller's avatar
Patrick Müller committed
    {
        Provider::setupApi();
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Get title for frontend
     *
     * @return string
     */
    abstract public function getFrontendTitle();

    /**
     * Get description for frontend
     *
     * @return string
     */
    abstract public function getFrontendDescription();

    /**
     * Get title for the Payment step (OrderProcess)
     *
     * @return string
     */
    abstract public function getPaymentStepTitle();

    /**
     * Get description step for the Payment step (OrderProcess)
     *
     * @return string
     */
    abstract public function getPaymentStepInfo();

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Get type string of Stripe PaymentMethod
     *
     * @return string
     */
    abstract public function getPaymentMethodType();

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Is the payment process successful?
     * 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) {
Patrick Müller's avatar
Patrick Müller committed
            QUI\System\Log::writeException($Exception);
Patrick Müller's avatar
Patrick Müller committed
            return false;
        }

Patrick Müller's avatar
Patrick Müller committed
        return $Order->getPaymentDataEntry(self::ATTR_STRIPE_ORDER_SUCCESSFUL);
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Is the payment a gateway payment?
     *
     * @return bool
     */
    public function isGateway()
    {
        return true;
    }

    /**
     * @return bool
     */
    public function refundSupport()
    {
        return true;
    }

    /**
     * Execute a refund
     *
     * @param QUI\ERP\Accounting\Payments\Transactions\Transaction $Transaction
     * @param int|float $amount
     * @param string $message
     * @param false|string $hash - if a new hash will be used
     * @throws QUI\ERP\Accounting\Payments\Transactions\RefundException
     */
    public function refund(
        Transaction $Transaction,
        $amount,
        $message = '',
        $hash = false
    ) {
        try {
            if ($hash === false) {
                $hash = $Transaction->getHash();
            }

            $this->refundPayment($Transaction, $hash, $amount, $message);
Patrick Müller's avatar
Patrick Müller committed
        } catch (StripeException $Exception) {
Patrick Müller's avatar
Patrick Müller committed
            QUI\System\Log::writeDebugException($Exception);

            throw new QUI\ERP\Accounting\Payments\Transactions\RefundException([
Patrick Müller's avatar
Patrick Müller committed
                'quiqqer/payment-stripe',
Patrick Müller's avatar
Patrick Müller committed
                'exception.BasePayment.refund_error_stripe',
                [
                    'stripeError' => $Exception->getMessage()
                ]
Patrick Müller's avatar
Patrick Müller committed
            ]);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);

            throw new QUI\ERP\Accounting\Payments\Transactions\RefundException([
Patrick Müller's avatar
Patrick Müller committed
                'quiqqer/payment-stripe',
Patrick Müller's avatar
Patrick Müller committed
                'exception.BasePayment.refund_error'
Patrick Müller's avatar
Patrick Müller committed
            ]);
        }
    }

    /**
     * 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
     *
     * @throws QUI\Exception
     */
    public function getGatewayDisplay(AbstractOrder $Order, $Step = null)
    {
Patrick Müller's avatar
Patrick Müller committed
        $type = $this->getPaymentMethodType();

        $Control = new PaymentDisplay([
            'paymentMethod' => $type,
            'infoText' => $this->getPaymentStepInfo()
Patrick Müller's avatar
Patrick Müller committed
        ]);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $Control->setAttribute('Order', $Order);

        $stepTitle = $this->getPaymentStepTitle();
        $Step->setTitle($stepTitle);
Patrick Müller's avatar
Patrick Müller committed

        $Engine = QUI::getTemplateManager()->getEngine();

        $Engine->assign([
            'stepTitle' => $stepTitle
        ]);

        $Step->setContent($Engine->fetch(dirname(__FILE__) . '/PaymentDisplay.Header.html'));
Patrick Müller's avatar
Patrick Müller committed

        return $Control->create();
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Create a Stripe PaymentIntent for an order
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param AbstractOrder $Order
Patrick Müller's avatar
Patrick Müller committed
     * @param string $paymentMethodId - Stripe PaymentMethod ID
     * @return StripePaymentIntent
Patrick Müller's avatar
Patrick Müller committed
     *
Patrick Müller's avatar
Patrick Müller committed
     * @throws ApiErrorException
Patrick Müller's avatar
Patrick Müller committed
     * @throws QUI\Exception
     */
Patrick Müller's avatar
Patrick Müller committed
    public function createPaymentIntent(AbstractOrder $Order, $paymentMethodId)
Patrick Müller's avatar
Patrick Müller committed
    {
Patrick Müller's avatar
Patrick Müller committed
        if (!empty($Order->getPaymentDataEntry(self::ATTR_STRIPE_PAYMENT_INTENT_ID))) {
Patrick Müller's avatar
Patrick Müller committed
            $PaymentIntent = $this->getPaymentIntentByOrder($Order);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            // Re-set PaymentMethod
            if (empty($PaymentIntent->payment_method)) {
                $PaymentIntent = StripePaymentIntent::update($PaymentIntent->id, [
                    'payment_method' => $paymentMethodId
                ]);
            }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            return $PaymentIntent;
        }

        $PaymentIntent = $this->createPaymentIntentForOrder($Order, $paymentMethodId);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $Order->setPaymentData(self::ATTR_STRIPE_PAYMENT_INTENT_ID, $PaymentIntent->id);
        $this->addOrderHistoryEntry($Order, 'PaymentIntent ' . $PaymentIntent->id . ' created.');
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        return $PaymentIntent;
    }

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Return attributes for creating a PaymentIntent
     *
     * @param AbstractOrder $Order
     * @param string $paymentMethodId - Stripe PaymentMethod ID
     * @return StripePaymentIntent
     */
    abstract protected function createPaymentIntentForOrder(AbstractOrder $Order, $paymentMethodId);

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Confirm a Stripe PaymentIntent
     *
     * @param AbstractOrder $Order
     * @param StripePaymentIntent $PaymentIntent
     * @return array - confirmation data
     *
     * @throws ApiErrorException
     */
    public function confirmPaymentIntent(AbstractOrder $Order, StripePaymentIntent $PaymentIntent)
    {
        if (empty($PaymentIntent->payment_method)) {
Patrick Müller's avatar
Patrick Müller committed
            $this->addOrderHistoryEntry($Order, 'Payment method validation fail. Re-fetch payment method from user.');
Patrick Müller's avatar
Patrick Müller committed
            return ['status' => 'retry_payment_method'];
        }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if ($Order->getPaymentDataEntry(self::ATTR_STRIPE_ORDER_SUCCESSFUL)) {
            return ['status' => 'success'];
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        $this->addOrderHistoryEntry($Order, 'Confirming PaymentIntent.');
Patrick Müller's avatar
Patrick Müller committed

        if ($PaymentIntent->status === $PaymentIntent::STATUS_REQUIRES_CONFIRMATION) {
            $PaymentIntent = $PaymentIntent->confirm();
        }

Patrick Müller's avatar
Patrick Müller committed
        $confirmData = [];
Patrick Müller's avatar
Patrick Müller committed

        if (
            $PaymentIntent->status === $PaymentIntent::STATUS_REQUIRES_ACTION
            && $PaymentIntent->next_action->type === 'use_stripe_sdk'
        ) {
            $confirmData['status'] = 'action_required';
Patrick Müller's avatar
Patrick Müller committed
            $confirmData['clientSecret'] = $PaymentIntent->client_secret;
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            $this->addOrderHistoryEntry($Order, 'Additional user action required for PaymentIntent confirmation.');
        } elseif ($PaymentIntent->status === $PaymentIntent::STATUS_SUCCEEDED) {
            $confirmData['status'] = 'success';
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            $this->addOrderHistoryEntry($Order, 'PaymentIntent successully confirmed.');
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
            try {
                $Order->setSuccessfulStatus();
Patrick Müller's avatar
Patrick Müller committed
                $Order->setPaymentData(self::ATTR_STRIPE_ORDER_SUCCESSFUL, true);

                $this->saveOrder($Order);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
                $Transaction = Gateway::getInstance()->purchase(
                    $PaymentIntent->amount_received / 100,
                    QUI\ERP\Currency\Handler::getCurrency(mb_strtoupper($PaymentIntent->currency)),
Patrick Müller's avatar
Patrick Müller committed
                    $Order,
                    $this
                );
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
                $Transaction->setData(
                    self::ATTR_STRIPE_PAYMENT_INTENT_ID,
                    $Order->getPaymentDataEntry(self::ATTR_STRIPE_PAYMENT_INTENT_ID)
                );
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
                $Transaction->updateData();
Patrick Müller's avatar
Patrick Müller committed
            } catch (\Exception $Exception) {
                QUI\System\Log::writeException($Exception);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
                $this->addOrderHistoryEntry(
                    $Order,
                    'Error while trying to set order as successful: ' . $Exception->getMessage()
Patrick Müller's avatar
Patrick Müller committed
                );
Patrick Müller's avatar
Patrick Müller committed
            }
Patrick Müller's avatar
Patrick Müller committed
        } else {
            $confirmData['status'] = 'error';
            $this->addOrderHistoryEntry($Order, 'Confirmation error.');
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        return $confirmData;
    }
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
    /**
     * Get Stripe PaymentIntent by order
     *
     * @param AbstractOrder $Order
     * @return false|StripePaymentIntent - PaymentIntent or false if not found
     *
     * @throws ApiErrorException
     * @throws StripeException
     */
    public function getPaymentIntentByOrder(AbstractOrder $Order)
    {
        $paymentIntentId = $Order->getPaymentDataEntry(self::ATTR_STRIPE_PAYMENT_INTENT_ID);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if (empty($paymentIntentId)) {
            throw new StripeException([
                'quiqqer/payment-stripe',
                'exception.BasePayment.getPaymentIntentByOrder.payment_intent_id_missing',
                [
                    'orderHash' => $Order->getHash()
                ]
            ]);
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        return StripePaymentIntent::retrieve($paymentIntentId);
Patrick Müller's avatar
Patrick Müller committed
    }

    /**
     * Refund partial or full payment of an Order
     *
     * @param QUI\ERP\Accounting\Payments\Transactions\Transaction $Transaction
     * @param string $refundHash - Hash of the refund Transaction
Patrick Müller's avatar
Patrick Müller committed
     * @param float $amount - The amount to be refunded
Patrick Müller's avatar
Patrick Müller committed
     * @param string $reason (optional) - The reason for the refund [default: none; max. 255 characters]
     * @return void
     *
Patrick Müller's avatar
Patrick Müller committed
     * @throws StripeException
Patrick Müller's avatar
Patrick Müller committed
     * @throws QUI\Exception
     */
    public function refundPayment(Transaction $Transaction, $refundHash, $amount, $reason = '')
    {
        $Process = new QUI\ERP\Process($Transaction->getGlobalProcessId());
        $Process->addHistory('Stripe :: Start refund for transaction #' . $Transaction->getTxId());
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        if (!$Transaction->getData(self::ATTR_STRIPE_PAYMENT_INTENT_ID)) {
            $Process->addHistory(
                'PayPal :: Transaction cannot be refunded because it is not yet captured / completed.'
            );
Patrick Müller's avatar
Patrick Müller committed

            $this->throwStripeException(
                self::ERROR_REFUND_MISSING_PAYMENT_INTENT_ID,
                [
                    'globalProcessingId' => $Transaction->getGlobalProcessId()
                ]
            );

Patrick Müller's avatar
Patrick Müller committed
            return;
        }

Patrick Müller's avatar
Patrick Müller committed
        $paymentIntentId = $Transaction->getData(self::ATTR_STRIPE_PAYMENT_INTENT_ID);
        $Currency = $Transaction->getCurrency();
Patrick Müller's avatar
Patrick Müller committed

        // Create a refund transaction (QUIQQER)
Patrick Müller's avatar
Patrick Müller committed
        $RefundTransaction = TransactionFactory::createPaymentRefundTransaction(
            $amount,
Patrick Müller's avatar
Patrick Müller committed
            $Currency,
Patrick Müller's avatar
Patrick Müller committed
            $refundHash,
            $Transaction->getPayment()->getName(),
            [
                'isRefund' => 1,
                'message' => $reason
Patrick Müller's avatar
Patrick Müller committed
            ],
            null,
            false,
            $Transaction->getGlobalProcessId()
        );

        $RefundTransaction->pending();

Patrick Müller's avatar
Patrick Müller committed
        // Create refund (Stripe)
Patrick Müller's avatar
Patrick Müller committed
        try {
Patrick Müller's avatar
Patrick Müller committed
            $PaymentIntent = StripePaymentIntent::retrieve($paymentIntentId);
            $AmountValue = new QUI\ERP\Accounting\CalculationValue($amount, $Transaction->getCurrency(), 2);
            $refundAmount = $AmountValue->get() * 100; // convert to smallest currency unit
Patrick Müller's avatar
Patrick Müller committed

            $Refund = StripeRefund::create([
                'charge' => $PaymentIntent->charges->data[0]->id,
                'amount' => $refundAmount,
Patrick Müller's avatar
Patrick Müller committed
//                'reason'   => StripeRefund::REASON_REQUESTED_BY_CUSTOMER,
                'metadata' => [
                    'refundTxId' => $RefundTransaction->getTxId()
                ]
            ]);
        } catch (\Exception $Exception) {
            QUI\System\Log::writeException($Exception);
Patrick Müller's avatar
Patrick Müller committed

            $RefundTransaction->error();

Patrick Müller's avatar
Patrick Müller committed
            $Process->addHistory(
                QUI::getLocale()->get(
                    'quiqqer/payment-stripe',
                    'history.refund.error_api',
                    [
                        'amount' => $amount,
Patrick Müller's avatar
Patrick Müller committed
                        'currency' => $Currency->getCode(),
                        'txId' => $Transaction->getTxId()
Patrick Müller's avatar
Patrick Müller committed
                    ]
                )
            );

            return;
Patrick Müller's avatar
Patrick Müller committed
        }

Patrick Müller's avatar
Patrick Müller committed
        switch ($Refund->status) {
            case StripeRefund::STATUS_SUCCEEDED:
                $RefundTransaction->complete();
                $RefundTransaction->setData(self::ATTR_STRIPE_REFUND_ID, $Refund->id);
Patrick Müller's avatar
Patrick Müller committed
                $RefundTransaction->updateData();

                $Process->addHistory(
                    QUI::getLocale()->get(
Patrick Müller's avatar
Patrick Müller committed
                        'quiqqer/payment-stripe',
Patrick Müller's avatar
Patrick Müller committed
                        'history.refund.success',
Patrick Müller's avatar
Patrick Müller committed
                        [
Patrick Müller's avatar
Patrick Müller committed
                            'refundId' => $Refund->id,
                            'amount' => $amount,
Patrick Müller's avatar
Patrick Müller committed
                            'currency' => $Currency->getCode(),
                            'txId' => $Transaction->getTxId()
Patrick Müller's avatar
Patrick Müller committed
                        ]
                    )
                );

                QUI::getEvents()->fireEvent('transactionSuccessfullyRefunded', [
                    $RefundTransaction,
                    $this
                ]);
                break;

Patrick Müller's avatar
Patrick Müller committed
            case StripeRefund::STATUS_PENDING:
                $RefundTransaction->pending();
                $RefundTransaction->setData(self::ATTR_STRIPE_REFUND_ID, $Refund->id);
                $RefundTransaction->updateData();

                $Process->addHistory(
                    QUI::getLocale()->get(
                        'quiqqer/payment-stripe',
                        'history.refund.pending',
                        [
                            'refundId' => $Refund->id,
                            'amount' => $amount,
Patrick Müller's avatar
Patrick Müller committed
                            'currency' => $Currency->getCode(),
                            'txId' => $Transaction->getTxId()
Patrick Müller's avatar
Patrick Müller committed
                        ]
                    )
                );
                break;

Patrick Müller's avatar
Patrick Müller committed
            // FAILURE
            default:
Patrick Müller's avatar
Patrick Müller committed
                $RefundTransaction->error();

                $stripeFailureReason = '';

                if (!empty($Refund->failure_reason)) {
                    $stripeFailureReason = $Refund->failure_reason;
                }

Patrick Müller's avatar
Patrick Müller committed
                $Process->addHistory(
Patrick Müller's avatar
Patrick Müller committed
                    QUI::getLocale()->get(
                        'quiqqer/payment-stripe',
                        'history.refund.error_status',
                        [
                            'refundId' => $Refund->id,
                            'refundStatus' => $Refund->status,
Patrick Müller's avatar
Patrick Müller committed
                            'failureReason' => $stripeFailureReason,
                            'amount' => $amount,
                            'currency' => $Currency->getCode(),
                            'txId' => $Transaction->getTxId()
Patrick Müller's avatar
Patrick Müller committed
                        ]
                    )
Patrick Müller's avatar
Patrick Müller committed
                );

Patrick Müller's avatar
Patrick Müller committed
                $this->throwStripeException(self::ERROR_REFUND_STATUS, ['refundStatus' => $Refund->status]);
Patrick Müller's avatar
Patrick Müller committed
     * Add history entry for current Order
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param AbstractOrder $Order
Patrick Müller's avatar
Patrick Müller committed
     * @param string $msg
Patrick Müller's avatar
Patrick Müller committed
     * @return void
     */
Patrick Müller's avatar
Patrick Müller committed
    protected function addOrderHistoryEntry(AbstractOrder $Order, $msg)
Patrick Müller's avatar
Patrick Müller committed
    {
        $Order->addHistory('Stripe :: ' . $msg);
Patrick Müller's avatar
Patrick Müller committed
        $this->saveOrder($Order);
    }

    /**
Patrick Müller's avatar
Patrick Müller committed
     * Throw StripeException for specific PayPal API Error
Patrick Müller's avatar
Patrick Müller committed
     *
     * @param string $errorCode (optional) - default: general error message
     * @param array $exceptionAttributes (optional) - Additional Exception attributes that may be relevant for the Frontend
     * @return string
     *
Patrick Müller's avatar
Patrick Müller committed
     * @throws StripeException
Patrick Müller's avatar
Patrick Müller committed
     */
Patrick Müller's avatar
Patrick Müller committed
    protected function throwStripeException($errorCode = self::ERROR_GENERAL, $exceptionAttributes = [])
Patrick Müller's avatar
Patrick Müller committed
    {
        $L = $this->getLocale();
        $lg = 'quiqqer/payment-stripe';
        $msg = $L->get($lg, 'exception.BasePayment.' . $errorCode);
Patrick Müller's avatar
Patrick Müller committed

Patrick Müller's avatar
Patrick Müller committed
        $Exception = new StripeException($msg);
Patrick Müller's avatar
Patrick Müller committed
        $Exception->setAttributes($exceptionAttributes);

        throw $Exception;
    }

    /**
     * Save Order with SystemUser
     *
     * @param AbstractOrder $Order
     * @return void
     */
    protected function saveOrder(AbstractOrder $Order)
    {
        $Order->update(QUI::getUsers()->getSystemUser());
    }
}