diff --git a/payment/uc_payment/src/PaymentMethodPluginInterface.php b/payment/uc_payment/src/PaymentMethodPluginInterface.php index c61b1fb..3f55b18 100644 --- a/payment/uc_payment/src/PaymentMethodPluginInterface.php +++ b/payment/uc_payment/src/PaymentMethodPluginInterface.php @@ -137,9 +137,8 @@ interface PaymentMethodPluginInterface extends PluginInspectionInterface, Plugin * @param \Drupal\uc_order\OrderInterface $order * The order that is being submitted. * - * @return string|\Symfony\Component\HttpFoundation\RedirectResponse|NULL + * @return string|NULL * An error message that can be shown to the user if the payment failed, - * a redirect if the user must be redirected to complete the transaction, * or NULL if everything was successful. */ public function orderSubmit(OrderInterface $order); diff --git a/payment/uc_payment/uc_payment.module b/payment/uc_payment/uc_payment.module index b05cbcd..5750d18 100644 --- a/payment/uc_payment/uc_payment.module +++ b/payment/uc_payment/uc_payment.module @@ -76,10 +76,12 @@ function uc_payment_form_uc_cart_checkout_review_form_alter(&$form, FormStateInt $plugin = \Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order); if ($plugin instanceof OffsitePaymentMethodPluginInterface) { - unset($form['actions']['submit']); $offsite_form = new OffsitePaymentMethodForm($plugin); $suffix = \Drupal::formBuilder()->getForm($offsite_form, $order); - $form['#suffix'] = \Drupal::service('renderer')->renderPlain($suffix); + if (!empty($suffix['actions'])) { + unset($form['actions']['submit']); + $form['#suffix'] = \Drupal::service('renderer')->renderPlain($suffix); + } } } @@ -93,12 +95,7 @@ function uc_payment_checkout_review_form_validate($form, FormStateInterface $for // If the plugin failed for any reason, show the message to the user. if ($message = $plugin->orderSubmit($order)) { - if (is_object($message) && $message instanceof \Symfony\Component\HttpFoundation\RedirectResponse) { - $form_state->setResponse($message); - } - else { - $form_state->setErrorByName('', $message); - } + $form_state->setErrorByName('', $message); } } diff --git a/payment/uc_paypal/src/Controller/EcController.php b/payment/uc_paypal/src/Controller/EcController.php index 7b3d12d..f32a7df 100644 --- a/payment/uc_paypal/src/Controller/EcController.php +++ b/payment/uc_paypal/src/Controller/EcController.php @@ -64,34 +64,39 @@ class EcController extends ControllerBase { return $this->redirect('uc_cart.cart'); } + // Get the payer ID from PayPal. $plugin = \Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order); $response = $plugin->sendNvpRequest([ 'METHOD' => 'GetExpressCheckoutDetails', 'TOKEN' => $session->get('TOKEN'), ]); - $session->set('PAYERID', $response['PAYERID']); + // Store delivery address. + $address = $order->getAddress('delivery'); $shipname = $response['SHIPTONAME']; if (strpos($shipname, ' ') > 0) { - $order->delivery_first_name = substr($shipname, 0, strrpos(trim($shipname), ' ')); - $order->delivery_last_name = substr($shipname, strrpos(trim($shipname), ' ') + 1); + $address->first_name = substr($shipname, 0, strrpos(trim($shipname), ' ')); + $address->last_name = substr($shipname, strrpos(trim($shipname), ' ') + 1); } else { - $order->delivery_first_name = $shipname; - $order->delivery_last_name = ''; + $address->first_name = $shipname; + $address->last_name = ''; } - - $order->delivery_street1 = $response['SHIPTOSTREET']; - $order->delivery_street2 = isset($response['SHIPTOSTREET2']) ? $response['SHIPTOSTREET2'] : ''; - $order->delivery_city = $response['SHIPTOCITY']; - $order->delivery_zone = $response['SHIPTOSTATE']; - $order->delivery_postal_code = $response['SHIPTOZIP']; - $order->delivery_country = $response['SHIPTOCOUNTRYCODE']; - - $order->billing_first_name = $response['FIRSTNAME']; - $order->billing_last_name = $response['LASTNAME']; - $order->billing_country = $response['COUNTRYCODE']; + $address->street1 = $response['SHIPTOSTREET']; + $address->street2 = isset($response['SHIPTOSTREET2']) ? $response['SHIPTOSTREET2'] : ''; + $address->city = $response['SHIPTOCITY']; + $address->zone = $response['SHIPTOSTATE']; + $address->postal_code = $response['SHIPTOZIP']; + $address->country = $response['SHIPTOCOUNTRYCODE']; + $order->setAddress('delivery', $address); + + // Store billing details. + $address = $order->getAddress('billing'); + $address->first_name = $response['FIRSTNAME']; + $address->last_name = $response['LASTNAME']; + $address->country = $response['COUNTRYCODE']; + $order->setAddress('billing', $address); $order->setEmail($response['EMAIL']); $order->save(); diff --git a/payment/uc_paypal/src/Form/EcReviewForm.php b/payment/uc_paypal/src/Form/EcReviewForm.php index 3352a62..89672bc 100644 --- a/payment/uc_paypal/src/Form/EcReviewForm.php +++ b/payment/uc_paypal/src/Form/EcReviewForm.php @@ -16,6 +16,11 @@ use Drupal\uc_order\OrderInterface; */ class EcReviewForm extends FormBase { + /** + * The order that is being reviewed. + * + * @var \Drupal\uc_order\OrderInterface + */ protected $order; /** @@ -51,15 +56,6 @@ class EcReviewForm extends FormBase { /** * {@inheritdoc} */ - public function validateForm(array &$form, FormStateInterface $form_state) { - if (!$form_state->isValueEmpty('shippable') && $form_state->isValueEmpty(['quotes', 'quote_option'])) { - $form_state->setErrorByName('shipping', $this->t('You must calculate and select a shipping option.')); - } - } - - /** - * {@inheritdoc} - */ public function submitForm(array &$form, FormStateInterface $form_state) { \Drupal::service('plugin.manager.uc_payment.method') ->createFromOrder($this->order) diff --git a/payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalExpressCheckout.php b/payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalExpressCheckout.php index 1ffc56a..506b79f 100644 --- a/payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalExpressCheckout.php +++ b/payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalExpressCheckout.php @@ -13,6 +13,7 @@ use Drupal\Core\Url; use Drupal\uc_order\Entity\Order; use Drupal\uc_order\OrderInterface; use Drupal\uc_payment\ExpressPaymentMethodPluginInterface; +use Drupal\uc_payment\OffsitePaymentMethodPluginInterface; use GuzzleHttp\Exception\TransferException; /** @@ -23,7 +24,7 @@ use GuzzleHttp\Exception\TransferException; * name = @Translation("PayPal Express Checkout") * ) */ -class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements ExpressPaymentMethodPluginInterface { +class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements ExpressPaymentMethodPluginInterface, OffsitePaymentMethodPluginInterface { /** * The payment method entity ID that is using this plugin. @@ -131,24 +132,30 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp } /** - * {@inheritdoc} + * Redirect to PayPal Express Checkout Mark Flow. + * + * This is used when the user does not use the cart button, but follows the + * normal checkout process and selects Express Checkout as a payment method. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param \Drupal\uc_order\OrderInterface $order + * The order that is being processed. + * + * @return array + * The form structure. */ - public function orderSubmit(OrderInterface $order) { + public function buildRedirectForm(array $form, FormStateInterface $form_state, OrderInterface $order = NULL) { $session = \Drupal::service('session'); if ($session->has('TOKEN') && $session->has('PAYERID')) { - return $this->orderSubmitShortcut($order); - } - else { - return $this->orderSubmitMark($order); + // If the session variables are set, then the user already gave their + // details via Shortcut Flow, so we do not need to redirect them here. + return []; } - } - - /** - * Submits an order via Express Checkout Mark Flow. - */ - protected function orderSubmitMark(OrderInterface $order) { - $session = \Drupal::service('session'); + $address = $order->getAddress('delivery'); $request = array( 'METHOD' => 'SetExpressCheckout', 'RETURNURL' => Url::fromRoute('uc_paypal.ec_complete', [], ['absolute' => TRUE])->toString(), @@ -162,14 +169,14 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp 'ADDROVERRIDE' => 1, 'BUTTONSOURCE' => 'Ubercart_ShoppingCart_EC_US', 'NOTIFYURL' => Url::fromRoute('uc_paypal.ipn', [], ['absolute' => TRUE])->toString(), - 'SHIPTONAME' => substr($order->delivery_first_name->value . ' ' . $order->delivery_last_name->value, 0, 32), - 'SHIPTOSTREET' => substr($order->delivery_street1->value, 0, 100), - 'SHIPTOSTREET2' => substr($order->delivery_street2->value, 0, 100), - 'SHIPTOCITY' => substr($order->delivery_city->value, 0, 40), - 'SHIPTOSTATE' => $order->delivery_zone->value, - 'SHIPTOCOUNTRYCODE' => $order->delivery_country->value, - 'SHIPTOZIP' => substr($order->delivery_postal_code->value, 0, 20), - 'PHONENUM' => substr($order->delivery_phone->value, 0, 20), + 'SHIPTONAME' => substr($address->first_name . ' ' . $address->last_name, 0, 32), + 'SHIPTOSTREET' => substr($address->street1, 0, 100), + 'SHIPTOSTREET2' => substr($address->street2, 0, 100), + 'SHIPTOCITY' => substr($address->city, 0, 40), + 'SHIPTOSTATE' => $address->zone, + 'SHIPTOCOUNTRYCODE' => $address->country, + 'SHIPTOZIP' => substr($address->postal_code, 0, 20), + 'PHONENUM' => substr($address->phone, 0, 20), 'LANDINGPAGE' => $this->configuration['ec_landingpage_style'], ); @@ -189,13 +196,21 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp $sandbox = strpos($this->configuration['wpp_server'], 'sandbox') > 0 ? 'sandbox.' : ''; $url = 'https://www.' . $sandbox . 'paypal.com/cgi-bin/webscr?cmd=_express-checkout&useraction=commit&token=' . $response['TOKEN']; - return new TrustedRedirectResponse($url); + $form['#action'] = $url; + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Submit order'), + ); + + return $form; } /** - * Finalises an order via Express Checkout Shortcut Flow. + * {@inheritdoc} */ - public function orderSubmitShortcut(OrderInterface $order) { + public function orderSubmit(OrderInterface $order) { $session = \Drupal::service('session'); $shipping = 0; @@ -266,8 +281,10 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp return; } - $order = Order::create(['uid' => \Drupal::currentUser()->id()]); - $order->setPaymentMethodId($this->methodId); + $order = Order::create([ + 'uid' => \Drupal::currentUser()->id(), + 'payment_method' => $this->methodId, + ]); $order->products = array(); foreach ($items as $item) { $order->products[] = $item->toOrderProduct(); @@ -308,30 +325,36 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp * Form constructor for the express checkout review form. */ public function getExpressReviewForm(array $form, FormStateInterface $form_state, OrderInterface $order) { + // Required by QuotePane::prepare(). + $form['#tree'] = TRUE; + // @todo: Replace with PayPal shipping callback? + // @todo: Make a simpler way of getting and applying shipping quotes. if ($this->configuration['ec_review_shipping'] && \Drupal::moduleHandler()->moduleExists('uc_quote') && $order->isShippable()) { - uc_checkout_pane_quotes('prepare', $order, NULL); - $order->line_items = $order->getLineItems(); - $order->save(); + /** @var \Drupal\uc_cart\CheckoutPanePluginInterface $pane */ + $pane = \Drupal::service('plugin.manager.uc_cart.checkout_pane')->createInstance('quotes'); + $pane->prepare($order, $form, $form_state); - $result = uc_checkout_pane_quotes('view', $order, NULL); $form['panes']['quotes'] = array( - '#type' => 'fieldset', + '#type' => 'details', '#title' => $this->t('Shipping cost'), + '#open' => TRUE, ); - $form['panes']['quotes'] += $result['contents']; + $form['panes']['quotes'] += $pane->view($order, $form, $form_state); + $form['panes']['quotes']['quotes']['quote_option']['#required'] = TRUE; + unset($form['panes']['quotes']['#description']); unset($form['panes']['quotes']['quote_button']); - - $form['shippable'] = array('#type' => 'value', '#value' => 'true'); } + $address = $order->getAddress('delivery'); + // @todo: Replace with "BUSINESS" from PayPal if ($this->configuration['ec_review_company']) { $form['delivery_company'] = array( '#type' => 'textfield', '#title' => $this->t('Company'), '#description' => $order->isShippable() ? $this->t('Leave blank if shipping to a residence.') : '', - '#default_value' => $order->delivery_company->value, + '#default_value' => $address->company, ); } @@ -340,7 +363,7 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp $form['delivery_phone'] = array( '#type' => 'textfield', '#title' => $this->t('Contact phone number'), - '#default_value' => $order->delivery_phone->value, + '#default_value' => $address->phone, '#size' => 24, ); } @@ -361,34 +384,20 @@ class PayPalExpressCheckout extends PayPalPaymentMethodPluginBase implements Exp * Form constructor for the express checkout review form. */ public function submitExpressReviewForm(array $form, FormStateInterface $form_state, OrderInterface $order) { - if (!$form_state->isValueEmpty('shippable')) { - $quote_option = explode('---', $form_state->getValue(['quotes', 'quote_option'])); - $order->quote['method'] = $quote_option[0]; - $order->quote['accessorials'] = $quote_option[1]; - $method = ShippingQuoteMethod::load($quote_option[0]); - - $label = $method['quote']['accessorials'][$quote_option[1]]; -// $label = $method->label(); - - $quote_option = $form_state->getValue(['quotes', 'quote_option']); - $order->quote['rate'] = $form_state->getValue(['quotes', $quote_option, 'rate']); - - $result = db_query("SELECT line_item_id FROM {uc_order_line_items} WHERE order_id = :id AND type = :type", [':id' => $order->id(), ':type' => 'shipping']); - if ($lid = $result->fetchField()) { - uc_order_update_line_item($lid, $label, $order->quote['rate']); - } - else { - uc_order_line_item_add($order->id(), 'shipping', $label, $order->quote['rate']); - } + if (!empty($form['panes']['quotes']['quotes'])) { + \Drupal::service('plugin.manager.uc_cart.checkout_pane') + ->createInstance('quotes') + ->prepare($order, $form, $form_state); } + $address = $order->getAddress('delivery'); if ($this->configuration['ec_review_company']) { - $order->delivery_company = $form_state->getValue('delivery_company'); + $address->company = $form_state->getValue('delivery_company'); } - if ($this->configuration['ec_review_phone']) { - $order->delivery_phone = $form_state->getValue('delivery_phone'); + $address->phone = $form_state->getValue('delivery_phone'); } + $order->setAddress('delivery', $address); if ($this->configuration['ec_review_comment'] && $form_state->getValue('order_comments')) { db_delete('uc_order_comments')