Right now if payments are configured, checkout will always require a payment.

We should be able to configure (per order type) how to handle free orders (total = 0):

* Do not collect any information (nothing)
* Collect billing information (still need to capture CRM related items)
* Collect billing and payment information (free trial subscription order use case)

Comments

bojanz created an issue. See original summary.

chishah92’s picture

Status: Active » Needs review
StatusFileSize
new817 bytes

This Patch is temporary fix i feel, as it is not configurable, but just playing with the payment information pane according to order total. But , this patch will resolve the issue.

dom.’s picture

Hi !
This seems a must have since coupons are now in Commerce 2.x and you may happen to have a coupon leading to a free backet (usage of gift card for instance).

The issue I see trying your patch is the following: it completely skips the PaymentInformation pane, thus you also skip the billing information panel. It seems thought that you would still ask for people information, while not actually sending them thought the payment process. The patch should be thus a little more clever to let you fill billing information while not requiring you to choose a payment method and not sending you to payment gateway.

Ohh god: my english is so bad on this one, I hope someone can understand what I mean !

dom.’s picture

StatusFileSize
new1.6 KB

Following #3 I would rather suggest this patch for review.

grimreaper’s picture

Hello,

I have tested both patchs, but it does not solved my problem.

To avoid a fatal error from Paypal when sending a free order. I created a manual paiement method for free order and only allow Paypal method when the order is not free.

The problem I have is that after entering a coupon, the list of paiement methods is not refreshed. It is only refreshed if (refreshing the page and) I select a paiement method and so get a form error for selecting an unavailable method.

I currently not have time to investigate.

sumanthkumarc’s picture

i tried in the way of #4 as follows and it looks to work fine.

   // This pane is not necessary if the total price is zero or below.
    $free_order = $this->order->getTotalPrice()->isZero();
    return $payment_info_pane->isVisible() && $payment_info_pane->getStepId() != '_disabled' && !$free_order;

This satisifies below:
a) Collect just the billing information - we do have payment information pane to collect details.
b) Collect a payment method, but don't charge it - we collect it and not sending it to gateway, but need to check what happens for onsite gateway, currently i only tried on offsite gateways.

sagesolutions’s picture

I tried #4, but it doesn't quite solve my problem.

I'm using stripe as my payment gateway.

If I apply a coupon which results in a $0.00 order, it should hide / remove the credit fields. The user shouldn't be required to enter in credit info on a free order. However, I would like to collect the user's information.

I'm on the latest dev branches of both stripe and commerce.

megachriz’s picture

Closed #2912306: Missing billing information pane as a duplicate. In there the suggestion is that Commerce Payment should not remove the billing information pane:

/**
 * Implements hook_commerce_checkout_pane_info_alter().
 */
function commerce_payment_commerce_checkout_pane_info_alter(&$definitions) {
  // The payment_information pane replaces the billing_information one.
  unset($definitions['billing_information']);
}
sorabh.v6’s picture

Assigned: Unassigned » sorabh.v6
sorabh.v6’s picture

Assigned: sorabh.v6 » Unassigned

I can confirm that the patch in #4 is working correctly for me. After applying the patch I tried with authorize.net payment gateway. It did not created any payment against the order. Just saved the payment information.

Thanks All

sagesolutions’s picture

I agree that #4 does allow free orders, however it would be better if the payment gateway was hidden when the order gets set (or reduced to) zero.

Should we open a new ticket hiding payment gateway info when order total is zero, or keep it in this one?

sorabh.v6’s picture

I agree with @sagesolutions. It would be good if payment gateway is hidden on free orders.

mglaman’s picture

I had discussed this with Bojan. We need a checkout pane setting which says:

For free orders:

* Do not collect any information (nothing)
* Collect billing information (still need to capture CRM related items)
* Collect billing and payment information (free trial subscription order use case)

chrisrockwell’s picture

Status: Needs review » Needs work
StatusFileSize
new3.87 KB

Per @mglaman's comment this should be Needs work, I think.

I'm attaching a WIP that adds settings to the PaymentInformation pane. Right now it only works if admin opts to collect none, what I know still needs to be done:
- conditionally hide credit card information only
- Decide if changing the step button label is appropriate
- Tests
- How to deal with #3 in Matt's comment: Collect billing and payment information. I'm unsure how to deal with that and I _think_ it reaches farther than this panes. i.e. If a payment method is configured to authorize and capture, do we need to override those for authorize only? In the case of authorize.net, I know we can't auth & capture a $0 payment. Need some direction here.

chrisrockwell’s picture

Another scenario: coupon in sidebar on order_information page - if you apply a coupon that makes the total $0, it doesn't refresh the page so the payment information pane is still visible.

nikathone’s picture

Issue tags: +Needs tests
StatusFileSize
new6.38 KB
new3.45 KB

Added support for collecting billing info only and added need test coverage tag. We still have to deal with scenario in #15.

nikathone’s picture

StatusFileSize
new7.09 KB
new1.28 KB

Updated commerce_payment.schema.yml to include commerce_checkout.commerce_checkout_pane.payment_information

adanielyan’s picture

With patch #17 and collect_none, every time I add a product to cart it skips the payment step as expected, but no order is created. I only see the Complete message

Complete

Your order number is . 
You can view your order on your account page when logged in.

Shouldn't an order be created as usual? In fact when I navigate to admin/commerce/orders/XX, where XX is the [last order id] + 1, I can see the order, but it is not displayed in admin/commerce/orders list. Besides every time I add another free product to cart or checkout an order the XX order is overwritten.

UPDATE: OK, after a little more exploration I found that an order is in fact being created but not placed. When I click on Place Order button the order appears in the list. I think the free orders should be placed by default.

nikathone’s picture

@adanielyan can you please provide more details about the version of commerce you are using and your setup? Cause when I did test #14 patch it place the order and most of my update on #17 didn't change any of that.

adanielyan’s picture

StatusFileSize
new229.77 KB

I have Commerce 8.x-2.1 installed, not dev. However the patch was applied without a problem. I didn't try #14, only #17.

Here is my order type config. The workflow is Fulfillment.

Order type

I have Contact information disabled. Can it be an issue?

nikathone’s picture

Hmmmm! If I am not mistaken with the fulfillment workflow you kind have to do things manually. Can you please try with the default workflow and see if it will work?
Thanks

adanielyan’s picture

Same thing with Default workflow. Order is created as Draft. I can't find where in Commerce module's code the status of order is changed to "completed" to understand why that line of code is not executed. Do you by chance know where it is?

adanielyan’s picture

What is strange is that even though the order seems to be created as Draft (with id 42):

order

it is not listed in orders list:

orders

adanielyan’s picture

StatusFileSize
new10.15 KB

In commerce_order table in database the order_number, checkout_step, and placed for order 42 are all NULL:

Only local images are allowed.

adanielyan’s picture

mglaman’s picture

Assigned: Unassigned » mglaman

Reviewing this.

mglaman’s picture

Issue summary: View changes
mglaman’s picture

Not sure what happened in #18 through #25. I don't know how this patch could have caused that, but proves needs for tests.

Hacking away at https://github.com/drupalcommerce/commerce/pull/847, will post updated patch here.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new6.96 KB

Rerolled with two changes.

  1. +++ b/modules/payment/config/schema/commerce_payment.schema.yml
    @@ -51,6 +51,13 @@ commerce_payment_gateway_configuration:
    +    capture:
    

    This is free_orders

  2. +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
    @@ -23,6 +23,68 @@ class PaymentInformation extends CheckoutPaneBase {
    +      'free_orders' => ''
    ...
    +      case 'collect_billing':
    ...
    +      case 'collect_all':
    ...
    +      case 'collect_none':
    

    Provided default value, moved values to const

now for tests.

Status: Needs review » Needs work

The last submitted patch, 29: 2856583-29.patch, failed testing. View results

mglaman’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
StatusFileSize
new14.55 KB

This has three tests for the three options.

Concerns for possible follow up, so we can open the floodgates around free orders. We can try to tackle them here, but I have a feeling there will be various issues which crop up, and getting the base functionality in is the best option.

    // @todo this is weird if a free order, "Pay" but no payment.
    $this->submitForm([], 'Pay and complete purchase');

The step label is really odd when the order is free.

In \Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentProcess::isVisible

return $payment_info_pane->isVisible() && $payment_info_pane->getStepId() != '_disabled' && !$order_total->isZero();

The `payment_information` pane is not visible when COLLECT_NONE. But it is when COLLECT_BILLING, so we need the isZero check. The problem here is that isZero actually kills COLLECT_ALL and the payment method generation. So, currently, COLLECT_ALL is a broken option. Do we

  • Fix COLLECT_ALL, that PaymentProcess runs if isZero and creates a payment method if SupportsStoredPaymentMethodsInterface then exits
  • Remove that option so we only have COLLECT_NONE and COLLECT_BILLING leaving COLLECT_ALL in a follow up due to substansive refactoring required
mglaman’s picture

Status: Needs review » Needs work
+++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
@@ -340,6 +439,13 @@ class PaymentInformation extends CheckoutPaneBase {
+    if ($this->order->getTotalPrice()->isZero() && $this->configuration['free_orders'] == self::COLLECT_BILLING) {

`collectBillingProfile` method which returns this boolean.

This is on pause for a moment. Discussing with bojanz as it always has /some/ impact in #2871483: Add checkout settings for payment method behavior for Payment Information pane settings.

mglaman’s picture

We have identified that this should focus down to hiding the payment method form if the order total is zero. The only reason you would collect a payment method on a free order is for later use. This requires "always save" option as described in #2871483: Add checkout settings for payment method behavior.

bojanz’s picture

Basically, we'll always collect billing info for now, attack "no billing info" for both free and non-free orders at a later point.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new7.43 KB

Here is a simplified patch.

  • Remove settings. Free orders do not collect payment method informationg, only billing
  • Added collectBillingProfileOnly method to support later "forced storing of payment methods, even free orders"

We can follow up with a "Don't collect anything at all" if it is required. But going this route simplified things.

sorabh.v6’s picture

  1. +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
    @@ -382,4 +400,17 @@ class PaymentInformation extends CheckoutPaneBase {
    +   *  Returns TRUE if only the billing profile should be collected.
    

    Return comment indentation must 3 spaces but found 2 (Nitpick).

  2. +++ b/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php
    @@ -3,9 +3,11 @@
    +use Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentInformation;
    

    Unused use statement.

jonnyeom’s picture

Just fixing the Nitpicks.

mglaman’s picture

Assigned: mglaman » bojanz
Status: Needs review » Reviewed & tested by the community

nikathone tested this and gave it a thumbs up. Marking RTBC and assigning to bojanz for feedback.

The last submitted patch, 4: allow_free_order--2856583-4.patch, failed testing. View results

bojanz’s picture

Status: Reviewed & tested by the community » Needs work
+        $summary = [
+          'label' => [
+            '#markup' => $this->t('Billing information'),
+          ],
+          'profile' => $profile_view,
+        ];

We should have a mechanism for changing the summary fieldset title.
We can then change both the pane title and the summary fieldset title to "Billing information" when that's the only thing that's being displayed. Showing "Payment information" doesn't make sense when it's just billing information.

-    if ($payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) {
+    if (!$collect_billing_only && $payment_gateway->getPlugin() instanceof SupportsStoredPaymentMethodsInterface) {

The code feels hard to follow now. I'd perform the "is free order" check on the very top of the method, returning just the billing information form in that case. We'll need to move the billing information form code to a helper for that to work.

+  /**
+   * Determines if only the billing profile should be collected.
+   *
+   * @return bool
+   *   Returns TRUE if only the billing profile should be collected.
+   */
+  protected function collectBillingProfileOnly() {
+    // This will be enhanced to consider the payment method storage strategy
+    // that will be determined in #2871483. Such as collecting payment methods
+    // for free orders for later use in recurring instances.
+    return $this->order->getTotalPrice()->isZero();
+  }

Let's not use a helper until we need it. Hardcoding $this->order->getTotalPrice()->isZero() is cleaner for the time being, since free orders will land in a different release from the payment method settings.

   public function isVisible() {
     // This pane can't be used without the PaymentInformation pane.
     $payment_info_pane = $this->checkoutFlow->getPane('payment_information');
-    return $payment_info_pane->isVisible() && $payment_info_pane->getStepId() != '_disabled';
+    // Disable if order total is 0.
+    $order_total = $this->order->getTotalPrice();
+    return $payment_info_pane->isVisible() && $payment_info_pane->getStepId() != '_disabled' && !$order_total->isZero();

This is hard to follow, let's split it into two separate checks.

bojanz’s picture

Status: Needs work » Needs review
StatusFileSize
new11.82 KB

Still need to resolve Matt's "Pay and complete order" @todo.

Status: Needs review » Needs work

The last submitted patch, 41: 2856583-41-free-orders.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

  • bojanz committed 445f106 on 8.x-2.x authored by mglaman
    Issue #2856583 by mglaman, nikathone, jonnyeom, bojanz, chrisrockwell,...
bojanz’s picture

Status: Needs work » Fixed

I went with "Complete checkout" for the button label. Not completely convinced, open to suggestions in a followup.
We also discussed "Finish your order" and "Complete order", but this felt better for now.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

freelylw’s picture

has this done in the latest 2.13 version ? I still don't see how to complete a free order without going through the payment.

Ivo.Radulovski’s picture

same here, trying to provide 100% discount codes, but free orders seem not to work. Any feedback?

kerasai’s picture

I had to extend the PaymentInfo pane, where I copied the entire buildPaneForm and deleted the conditional at the top where it checks for paid/zero-balance orders.

chandeepkhosa’s picture

While this is a little different from the original issue, from the past few comments I believe this may help some of you.
For my use case a client wants to add a coupon code which changes the order to free (zero cost).

I did this by

  1. enabling commerce_promotion (a part of commerce core)
  2. creating a promotion
  3. in Offer Type, selecting 'Percentage off the order subtotal'
  4. changing the value to 100
joelpittet’s picture

We might need to do what @kerasai and extend PaymentInformation because we can get a free order with the promotion but trouble when the product is price 0 to begin with.