commerce-8.x-2.8/modules/payment/src/Form/PaymentAddForm.php
modules/payment/src/Form/PaymentAddForm.php
<?php
namespace Drupal\commerce_payment\Form;
use Drupal\commerce\EntityHelper;
use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\OffsitePaymentGatewayInterface;
use Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface;
use Drupal\Component\Utility\Html;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Provides the payment add form.
*/
class PaymentAddForm extends FormBase implements ContainerInjectionInterface {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The current order.
*
* @var \Drupal\commerce_order\Entity\OrderInterface
*/
protected $order;
/**
* Constructs a new PaymentAddForm instance.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, RouteMatchInterface $route_match) {
$this->entityTypeManager = $entity_type_manager;
$this->order = $route_match->getParameter('commerce_order');
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager'),
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'commerce_payment_add_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
// Prepare the form for ajax.
$form['#wrapper_id'] = Html::getUniqueId('payment-add-form-wrapper');
$form['#prefix'] = '<div id="' . $form['#wrapper_id'] . '">';
$form['#suffix'] = '</div>';
$form['#tree'] = TRUE;
$step = $form_state->get('step');
$step = $step ?: 'payment_gateway';
$form_state->set('step', $step);
if ($step == 'payment_gateway') {
$form = $this->buildPaymentGatewayForm($form, $form_state);
}
elseif ($step == 'payment') {
$form = $this->buildPaymentForm($form, $form_state);
}
return $form;
}
/**
* Builds the form for selecting a payment gateway.
*
* @param array $form
* The parent form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the complete form.
*
* @return array
* The built form.
*/
protected function buildPaymentGatewayForm(array $form, FormStateInterface $form_state) {
// @todo
// Support adding payments to anonymous orders, by adding support for
// creating payment methods directly on this form.
if (!$this->order->getCustomerId()) {
throw new AccessDeniedHttpException();
}
/** @var \Drupal\commerce_payment\PaymentGatewayStorageInterface $payment_gateway_storage */
$payment_gateway_storage = $this->entityTypeManager->getStorage('commerce_payment_gateway');
$payment_gateways = $payment_gateway_storage->loadMultipleForOrder($this->order);
// Allow on-site and manual payment gateways.
$payment_gateways = array_filter($payment_gateways, function ($payment_gateway) {
/** @var \Drupal\commerce_payment\Entity\PaymentGateway $payment_gateway */
return !($payment_gateway->getPlugin() instanceof OffsitePaymentGatewayInterface);
});
// @todo Move this check to the access handler.
if (count($payment_gateways) < 1) {
throw new AccessDeniedHttpException();
}
$user_input = $form_state->getUserInput();
$first_payment_gateway = reset($payment_gateways);
$selected_payment_gateway_id = $first_payment_gateway->id();
if (isset($user_input['payment_gateway'])) {
$selected_payment_gateway_id = $user_input['payment_gateway'];
}
$selected_payment_gateway = $payment_gateways[$selected_payment_gateway_id];
$form['payment_gateway'] = [
'#type' => 'radios',
'#title' => $this->t('Payment gateway'),
'#options' => EntityHelper::extractLabels($payment_gateways),
'#default_value' => $selected_payment_gateway_id,
'#required' => TRUE,
'#ajax' => [
'callback' => [get_class($this), 'ajaxRefresh'],
'wrapper' => $form['#wrapper_id'],
],
];
if ($selected_payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) {
/** @var \Drupal\commerce_payment\PaymentMethodStorageInterface $payment_method_storage */
$payment_method_storage = $this->entityTypeManager->getStorage('commerce_payment_method');
$billing_countries = $this->order->getStore()->getBillingCountries();
$payment_methods = $payment_method_storage->loadReusable($this->order->getCustomer(), $selected_payment_gateway, $billing_countries);
if (!empty($payment_methods)) {
$selected_payment_method = reset($payment_methods);
$payment_method_options = [];
foreach ($payment_methods as $id => $payment_method) {
$payment_method_options[$id] = $payment_method->label();
if ($payment_method->isDefault()) {
$selected_payment_method = $payment_method;
}
}
$form['payment_method'] = [
'#type' => 'radios',
'#title' => $this->t('Payment method'),
'#options' => $payment_method_options,
'#default_value' => $selected_payment_method->id(),
'#required' => TRUE,
'#after_build' => [
[get_class($this), 'clearValue'],
],
];
}
else {
$form['payment_method'] = [
'#type' => 'markup',
'#markup' => $this->t('There are no reusable payment methods available for the selected gateway.'),
];
// Don't allow the form to be submitted.
return $form;
}
}
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Continue'),
'#button_type' => 'primary',
];
return $form;
}
/**
* Clears the payment method value when the payment gateway changes.
*
* Changing the payment gateway results in a new set of payment methods,
* causing the submitted value to trigger an "Illegal choice" error, cause
* it's no longer allowed. Clearing the value causes the element to fallback
* to the default value, avoiding the error.
*/
public static function clearValue(array $element, FormStateInterface $form_state) {
$value = $element['#value'];
if (!isset($element['#options'][$value])) {
$element['#value'] = NULL;
$user_input = &$form_state->getUserInput();
unset($user_input['payment_method']);
}
return $element;
}
/**
* Builds the form for adding a payment.
*
* @param array $form
* The parent form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the complete form.
*
* @return array
* The built form.
*/
protected function buildPaymentForm(array $form, FormStateInterface $form_state) {
$values = [
'payment_gateway' => $form_state->getValue('payment_gateway'),
'order_id' => $this->order->id(),
];
if ($payment_method = $form_state->getValue('payment_method')) {
$values['payment_method'] = $payment_method;
}
$payment_storage = $this->entityTypeManager->getStorage('commerce_payment');
$payment = $payment_storage->create($values);
$form['payment'] = [
'#type' => 'commerce_payment_gateway_form',
'#operation' => 'add-payment',
'#default_value' => $payment,
];
$form['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Add payment'),
'#button_type' => 'primary',
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
$step = $form_state->get('step');
if ($step == 'payment_gateway') {
$form_state->set('step', 'payment');
$form_state->setRebuild(TRUE);
}
elseif ($step == 'payment') {
/** @var \Drupal\commerce_payment\Entity\PaymentInterface $payment */
$payment = $form_state->getValue('payment');
$this->messenger()->addMessage($this->t('Payment saved.'));
$form_state->setRedirect('entity.commerce_payment.collection', ['commerce_order' => $payment->getOrderId()]);
}
}
/**
* Ajax callback.
*/
public static function ajaxRefresh(array $form, FormStateInterface $form_state) {
return $form;
}
}
