/** * * @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; }
/** * 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); }
/** * 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]); } } }
public function getPaymentMethods() { return GatewayInfo::getSupportedGateways(); }
public function getRequiredFields(Order $order) { return GatewayInfo::requiredFields(Checkout::get($order)->getSelectedPaymentMethod()); }