/**
  *
  * @param \GridField $gridField
  * @param \DataObject $record
  * @param string $columnName
  * @return string|null - the HTML for the column
  */
 public function getColumnContent($gridField, $record, $columnName)
 {
     if (!$record instanceof \Payment) {
         return null;
     }
     if (!$record->canRefund()) {
         return null;
     }
     \Requirements::css('omnipay-ui/css/omnipay-ui-cms.css');
     \Requirements::javascript('omnipay-ui/javascript/omnipay-ui-cms.js');
     \Requirements::add_i18n_javascript('omnipay-ui/javascript/lang');
     $infoText = '';
     switch (GatewayInfo::refundMode($record->Gateway)) {
         case GatewayInfo::MULTIPLE:
             $infoText = 'MultiRefundInfo';
             break;
         case GatewayInfo::PARTIAL:
             $infoText = 'SingleRefundInfo';
             break;
         case GatewayInfo::FULL:
             $infoText = 'FullRefundInfo';
             break;
     }
     /** @var \Money $money */
     $money = $record->dbObject('Money');
     /** @var \GridField_FormAction $field */
     $field = \GridField_FormAction::create($gridField, 'RefundPayment' . $record->ID, false, 'refundpayment', array('RecordID' => $record->ID))->addExtraClass('gridfield-button-refund payment-dialog-button')->setAttribute('title', _t('GridFieldRefundAction.Title', 'Refund Payment'))->setAttribute('data-icon', 'button-refund')->setAttribute('data-dialog', json_encode(array('maxAmount' => $money->Nice(), 'maxAmountNum' => $money->getAmount(), 'hasAmountField' => $record->canRefund(null, true), 'infoTextKey' => $infoText, 'buttonTextKey' => 'RefundAmount')))->setDescription(_t('GridFieldRefundAction.Description', 'Refund a captured payment'));
     return $field->Field();
 }
 public function paymentmethod()
 {
     $gateways = GatewayInfo::getSupportedGateways();
     if (count($gateways) == 1) {
         return $this->owner->redirect($this->NextStepLink());
     }
     return array('OrderForm' => $this->PaymentMethodForm());
 }
 /**
  * Send an email with a copy of the order for payment
  *
  * Accessing the extension point is needed to dynamically set send_confirmation to true.
  * Normally, an unpaid order will not generate an email.
  * @see onPlaceOrder is an extension within the placeOrder function within the OrderProcessor class
  */
 public function onPlaceOrder()
 {
     $gateway = Checkout::get($this->owner)->getSelectedPaymentMethod();
     if (OrderProcessor::config()->bank_deposit_send_confirmation && GatewayInfo::isManual($gateway) && $this->owner->Status == "Unpaid") {
         OrderProcessor::config()->send_confirmation = true;
     } else {
         OrderProcessor::config()->send_confirmation = false;
     }
 }
 public function checkoutSubmit($data, $form)
 {
     // form validation has passed by this point, so we can save data
     $this->config->setData($form->getData());
     $order = $this->config->getOrder();
     $gateway = Checkout::get($order)->getSelectedPaymentMethod(false);
     if (GatewayInfo::isOffsite($gateway) || GatewayInfo::isManual($gateway) || $this->config->getComponentByType('OnsitePaymentCheckoutComponent')) {
         return $this->submitpayment($data, $form);
     }
     return $this->controller->redirect($this->controller->Link('payment'));
 }
 /**
  * Void/cancel a payment
  *
  * If the transaction-reference of the payment to capture is known, pass it via $data as
  * `transactionReference` parameter. Otherwise the service will try to look up the reference
  * from previous payment messages.
  *
  * If there's no transaction-reference to be found, this method will raise an exception.
  *
  * @inheritdoc
  * @throws MissingParameterException if no transaction reference can be found from messages or parameters
  */
 public function initiate($data = array())
 {
     if (!$this->payment->canVoid()) {
         throw new InvalidConfigurationException('Voiding of this payment not allowed.');
     }
     if (!$this->payment->isInDB()) {
         $this->payment->write();
     }
     $reference = null;
     // If the gateway isn't manual, we need a transaction reference to void a payment
     if (!GatewayInfo::isManual($this->payment->Gateway)) {
         if (!empty($data['transactionReference'])) {
             $reference = $data['transactionReference'];
         } elseif (!empty($data['receipt'])) {
             // legacy code?
             $reference = $data['receipt'];
         } else {
             $reference = $this->payment->TransactionReference;
         }
         if (empty($reference)) {
             throw new MissingParameterException('transactionReference not found and is not set as parameter');
         }
     }
     $gateway = $this->oGateway();
     if (!$gateway->supportsVoid()) {
         throw new InvalidConfigurationException(sprintf('The gateway "%s" doesn\'t support void', $this->payment->Gateway));
     }
     $gatewayData = array_merge($data, array('transactionReference' => $reference, 'notifyUrl' => $this->getEndpointUrl('notify')));
     $this->extend('onBeforeVoid', $gatewayData);
     $request = $this->oGateway()->void($gatewayData);
     $this->extend('onAfterVoid', $request);
     $message = $this->createMessage($this->requestMessageType, $request);
     $message->write();
     try {
         $response = $this->response = $request->send();
     } catch (OmnipayException $e) {
         $this->createMessage($this->errorMessageType, $e);
         return $this->generateServiceResponse(ServiceResponse::SERVICE_ERROR);
     }
     Helper::safeExtend($this, 'onAfterSendVoid', $request, $response);
     $serviceResponse = $this->wrapOmnipayResponse($response);
     if ($serviceResponse->isAwaitingNotification()) {
         $this->payment->Status = $this->pendingState;
         $this->payment->write();
     } else {
         if ($serviceResponse->isError()) {
             $this->createMessage($this->errorMessageType, $response);
         } else {
             $this->markCompleted($this->endState, $serviceResponse, $response);
         }
     }
     return $serviceResponse;
 }
示例#6
0
 /**
  * Get the total captured or authorized amount, excluding Manual payments.
  * @return float
  */
 public function TotalPaidOrAuthorized()
 {
     $paid = 0;
     if ($payments = $this->owner->Payments()) {
         foreach ($payments as $payment) {
             // Captured and authorized payments (which aren't manual) should count towards the total
             if ($payment->Status == 'Captured' || $payment->Status == 'Authorized' && !GatewayInfo::isManual($payment->Gateway)) {
                 $paid += $payment->Amount;
             }
         }
     }
     return $paid;
 }
 public function validateData(Order $order, array $data)
 {
     $result = ValidationResult::create();
     if (!isset($data['PaymentMethod'])) {
         $result->error(_t('PaymentCheckoutComponent.NoPaymentMethod', "Payment method not provided"), "PaymentMethod");
         throw new ValidationException($result);
     }
     $methods = GatewayInfo::getSupportedGateways();
     if (!isset($methods[$data['PaymentMethod']])) {
         $result->error(_t('PaymentCheckoutComponent.UnsupportedGateway', "Gateway not supported"), "PaymentMethod");
         throw new ValidationException($result);
     }
 }
 /**
  * Get all available payment types
  */
 private function PaymentTypes()
 {
     $factory = new \Omnipay\Common\GatewayFactory();
     // since the omnipay gateway factory only returns gateways from the composer.json extra data,
     // we should merge it with user-defined gateways from Payment.allowed_gateways
     $gateways = array_unique(array_merge($factory->find(), array_keys(GatewayInfo::getSupportedGateways(false))));
     $supportedGateways = array();
     array_walk($gateways, function ($name, $index) use(&$supportedGateways, &$factory) {
         try {
             $instance = $factory->create($name);
             $supportedGateways[$name] = $instance;
         } catch (\Exception $e) {
         }
     });
     return $supportedGateways;
 }
 public function __construct(Order $order)
 {
     parent::__construct($order);
     $this->addComponent(CustomerDetailsCheckoutComponent::create());
     $this->addComponent(ShippingAddressCheckoutComponent::create());
     $this->addComponent(BillingAddressCheckoutComponent::create());
     if (Checkout::member_creation_enabled() && !Member::currentUserID()) {
         $this->addComponent(MembershipCheckoutComponent::create());
     }
     if (count(GatewayInfo::getSupportedGateways()) > 1) {
         $this->addComponent(PaymentCheckoutComponent::create());
     }
     $this->addComponent(NotesCheckoutComponent::create());
     $this->addComponent(TermsCheckoutComponent::create());
 }
 /**
  * Return money to the previously charged credit card.
  *
  * If the transaction-reference of the payment to refund is known, pass it via $data as
  * `transactionReference` parameter. Otherwise the service will look up the previous reference
  * from the payment itself.
  * If there's no transaction-reference to be found, this method will raise an exception.
  *
  * You can issue partial refunds (if the gateway supports it) by passing an `amount` parameter in the $data
  * array. If the amount given is not a number, or if it exceeds the total amount of the payment, an exception
  * will be raised.
  *
  * @inheritdoc
  * @throws MissingParameterException if no transaction reference can be found from messages or parameters
  * @throws InvalidParameterException if the amount parameter was invalid
  */
 public function initiate($data = array())
 {
     if (!$this->payment->canRefund()) {
         throw new InvalidConfigurationException('Refunding of this payment not allowed.');
     }
     if (!$this->payment->isInDB()) {
         $this->payment->write();
     }
     $reference = null;
     // If the gateway isn't manual, we need a transaction reference to refund a payment
     if (!GatewayInfo::isManual($this->payment->Gateway)) {
         if (!empty($data['transactionReference'])) {
             $reference = $data['transactionReference'];
         } elseif (!empty($data['receipt'])) {
             // legacy code?
             $reference = $data['receipt'];
         } else {
             $reference = $this->payment->TransactionReference;
         }
         if (empty($reference)) {
             throw new MissingParameterException('transactionReference not found and is not set as parameter');
         }
     }
     $gateway = $this->oGateway();
     if (!$gateway->supportsRefund()) {
         throw new InvalidConfigurationException(sprintf('The gateway "%s" doesn\'t support refunds', $this->payment->Gateway));
     }
     $amount = $this->payment->MoneyAmount;
     $isPartial = false;
     if (!empty($data['amount'])) {
         $amount = $data['amount'];
         if (!is_numeric($amount)) {
             throw new InvalidParameterException('The "amount" parameter has to be numeric.');
         }
         if (!($amount > 0)) {
             throw new InvalidParameterException('The "amount" parameter has to be positive.');
         }
         $compare = PaymentMath::compare($this->payment->MoneyAmount, $amount);
         if ($compare === -1) {
             throw new InvalidParameterException('The "amount" to refund cannot exceed the captured amount.');
         }
         $isPartial = $compare === 1;
     }
     if ($isPartial && !$this->payment->canRefund(null, true)) {
         throw new InvalidParameterException('This payment cannot be partially refunded (unsupported by gateway).');
     }
     $gatewayData = array_merge($data, array('amount' => (double) $amount, 'currency' => $this->payment->MoneyCurrency, 'transactionReference' => $reference, 'notifyUrl' => $this->getEndpointUrl('notify')));
     $this->extend('onBeforeRefund', $gatewayData);
     $request = $this->oGateway()->refund($gatewayData);
     $this->extend('onAfterRefund', $request);
     $message = $this->createMessage($this->requestMessageType, $request);
     $message->write();
     try {
         $response = $this->response = $request->send();
     } catch (OmnipayException $e) {
         $this->createMessage($this->errorMessageType, $e);
         return $this->generateServiceResponse(ServiceResponse::SERVICE_ERROR);
     }
     Helper::safeExtend($this, 'onAfterSendRefund', $request, $response);
     $serviceResponse = $this->wrapOmnipayResponse($response);
     if ($serviceResponse->isAwaitingNotification()) {
         if ($isPartial) {
             $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
         }
         $this->payment->Status = $this->pendingState;
         $this->payment->write();
     } else {
         if ($serviceResponse->isError()) {
             $this->createMessage($this->errorMessageType, $response);
         } else {
             if ($isPartial) {
                 $this->createPartialPayment(PaymentMath::multiply($amount, '-1'), $this->pendingState);
             }
             $this->markCompleted($this->endState, $serviceResponse, $response);
         }
     }
     return $serviceResponse;
 }
 /**
  * Create a new payment for an order
  */
 public function createPayment($gateway)
 {
     if (!GatewayInfo::isSupported($gateway)) {
         $this->error(_t("PaymentProcessor.InvalidGateway", "`{gateway}` isn't a valid payment gateway.", 'gateway is the name of the payment gateway', array('gateway' => $gateway)));
         return false;
     }
     if (!$this->order->canPay(Member::currentUser())) {
         $this->error(_t("PaymentProcessor.CantPay", "Order can't be paid for."));
         return false;
     }
     $payment = Payment::create()->init($gateway, $this->order->TotalOutstanding(true), ShopConfig::get_base_currency());
     $this->order->Payments()->add($payment);
     return $payment;
 }
 protected function markCompleted($endStatus, ServiceResponse $serviceResponse, $gatewayMessage)
 {
     // Get partial payments
     $partials = $this->payment->getPartialPayments()->filter('Status', $this->pendingState);
     if ($partials->count() > 0) {
         $i = 0;
         $total = $originalTotal = $this->payment->MoneyAmount;
         foreach ($partials as $payment) {
             // only the first, eg. most recent payment should be considered valid. All others should be set to void
             if ($i === 0) {
                 $total = PaymentMath::add($total, $payment->MoneyAmount);
                 // deal with partial capture
                 if ($payment->MoneyAmount < 0) {
                     $payment->Status = 'Created';
                     $payment->setAmount(PaymentMath::multiply($payment->MoneyAmount, '-1'));
                     $payment->Status = 'Captured';
                 } else {
                     // void excess amounts
                     $payment->Status = 'Void';
                 }
             } else {
                 $payment->Status = 'Void';
             }
             $payment->write();
             $i++;
         }
         // Ugly hack to set the money amount
         $this->payment->Status = 'Created';
         $this->payment->setAmount($total);
         // If not everything was captured (partial),
         // the payment should be refunded or still Authorized (in case multiple captures are possible)
         if ($total > 0 && $total < $originalTotal) {
             $endStatus = GatewayInfo::captureMode($this->payment->Gateway) === GatewayInfo::MULTIPLE ? 'Authorized' : 'Refunded';
         }
     }
     parent::markCompleted($endStatus, $serviceResponse, $gatewayMessage);
     if ($endStatus === 'Captured') {
         $this->createMessage('CapturedResponse', $gatewayMessage);
     } else {
         $this->createMessage('PartiallyCapturedResponse', $gatewayMessage);
     }
     Helper::safeExtend($this->payment, 'onCaptured', $serviceResponse);
 }
 public function php($data)
 {
     // Check if we should do a payment
     if (Form::current_action() == 'dopayment' && !empty($data['PaymentMethod'])) {
         $gateway = $data['PaymentMethod'];
         // If the gateway isn't manual and not offsite, Check for credit-card fields!
         if (!GatewayInfo::isManual($gateway) && !GatewayInfo::isOffsite($gateway)) {
             // Merge the required fields and the Credit-Card fields that are required for the gateway
             $this->required = array_merge($this->required, array_intersect(array('type', 'name', 'number', 'startMonth', 'startYear', 'expiryMonth', 'expiryYear', 'cvv', 'issueNumber'), GatewayInfo::requiredFields($gateway)));
         }
     }
     return parent::php($data);
 }
 /**
  * Create a payment service. This will either return an AuthorizeService or PurchaseService, depending on
  * the gateway config.
  * @param \Payment $payment
  * @return AuthorizeService|PurchaseService
  * @throws InvalidConfigurationException
  */
 protected function createPaymentService(\Payment $payment)
 {
     return $this->getService($payment, GatewayInfo::shouldUseAuthorize($payment->Gateway) ? ServiceFactory::INTENT_AUTHORIZE : ServiceFactory::INTENT_PURCHASE);
 }
 /**
  * Get a service response from the given Omnipay response
  * @param AbstractResponse $omnipayResponse
  * @param bool $isNotification whether or not this response is a response to a notification
  * @return ServiceResponse
  */
 protected function wrapOmnipayResponse(AbstractResponse $omnipayResponse, $isNotification = false)
 {
     if ($isNotification) {
         $flags = ServiceResponse::SERVICE_NOTIFICATION;
         if (!$omnipayResponse->isSuccessful()) {
             $flags |= ServiceResponse::SERVICE_ERROR;
         }
         return $this->generateServiceResponse($flags, $omnipayResponse);
     }
     $isAsync = GatewayInfo::shouldUseAsyncNotifications($this->payment->Gateway);
     $flags = $isAsync ? ServiceResponse::SERVICE_PENDING : 0;
     if (!$omnipayResponse->isSuccessful() && !$omnipayResponse->isRedirect() && !$isAsync) {
         $flags |= ServiceResponse::SERVICE_ERROR;
     }
     return $this->generateServiceResponse($flags, $omnipayResponse);
 }
示例#16
0
 /**
  * Whether or not this payment can be refunded
  * @param Member|int $member the member/memberID to check permissions on
  * @param boolean $partial check if payment can be partially refunded. Defaults to false
  * @return bool
  */
 public function canRefund($member = null, $partial = false)
 {
     if (!($this->Status == 'Captured' && ($partial ? GatewayInfo::allowPartialRefund($this->Gateway) : GatewayInfo::allowRefund($this->Gateway)))) {
         return false;
     }
     if ($this->isInDB()) {
         // Check if there are partial refunds and deny further refunds if multiple refunds aren't enabled
         $hasPartials = $this->getPartialPayments()->filter('Status', array('Refunded', 'PendingRefund'))->count() > 0;
         if ($hasPartials && GatewayInfo::refundMode($this->Gateway) !== GatewayInfo::MULTIPLE) {
             return false;
         }
     }
     $extended = $this->extendedCan('canRefund', $member);
     if ($extended !== null) {
         return $extended;
     }
     return Permission::check('REFUND_PAYMENTS', 'any', $member);
 }
 public function getPaymentMethodFields()
 {
     //TODO: only get one field if there is no option
     return OptionsetField::create('PaymentMethod', _t('CheckoutField.PaymentType', "Payment Type"), GatewayInfo::getSupportedGateways(), array_keys(GatewayInfo::getSupportedGateways()));
 }
 /**
  * Clear all fields that are not required by the gateway. Does nothing if gateway is null
  * @param $fields
  * @param array $defaults
  */
 protected function cullForGateway(&$fields, $defaults = array())
 {
     if (!$this->gateway) {
         return;
     }
     $selected = array_merge($defaults, GatewayInfo::requiredFields($this->gateway));
     foreach ($fields as $name => $field) {
         if (!in_array($name, $selected)) {
             unset($fields[$name]);
         }
     }
 }
示例#19
0
 public function getPaymentMethods()
 {
     return GatewayInfo::getSupportedGateways();
 }
 public function getRequiredFields(Order $order)
 {
     return GatewayInfo::requiredFields(Checkout::get($order)->getSelectedPaymentMethod());
 }