I've created a coupon code for a 5% discount on all total products and this works fine. We created an additional coupon for a promotion we wanted to offer for 10%. The settings are EXACTLY the same as the 5% coupon. On the checkout page, the calculations are done correctly. So it appears as if everything works up to this point. But, when the customers get charged by cybersource. It's billing for the total WITHOUT the discount. It only happens on any additional coupons because the first coupon we've created (the 5%) charges the correct amount. Not sure how to troubleshoot this but I suspect it's a settings somewhere because the 5% coupon works as it should.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

wodenx’s picture

I don't use cybersource so it will be difficult for me to troubleshoot this - but if everything looks right on the checkout page, I suspect this is some sort of incompatablity with that gateway -- maybe CS only allows a single discount line item? Are there any error messages from cybersource in watchdog? Does cybersource give any kind of itemized report for the order?

wodenx’s picture

Status: Active » Postponed (maintainer needs more info)
Digital Fire’s picture

Status: Postponed (maintainer needs more info) » Active

It does work with the gateway because again, the first coupon works as it should.

Let me clear this up in case there is a misunderstanding. I create an additional coupon for another campaign and the calculations work on the checkout page but the wrong (undiscounted) amount is getting sent to cyber source.

I'm NOT trying to get 2 coupons to work together. These are 2 separate coupons for 2 different discounts. Just the only coupon that actually works is the first coupon i've ever created. The second coupon has all the same settings and does the calculations but doesn't send the correct amount to the payment gateway.

Digital Fire’s picture

Let me know of what I can do to trouble shoot this problem to help you with the fix.

Digital Fire’s picture

After running multiple tests, We figured out that there is a specific amount that causes the payment gateway to receive the undiscounted price w/ the correct tax. If the discount is more than 8%, It breaks. I will run more tests today to see if the amount of the coupon that breaks it instead of the number of coupons created.

Digital Fire’s picture

Title: First Coupon works, Any created afterwards don't » Coupon calculates but after 9% or more, doesn't sent correct amount to payment gateway (Cybersource)
Digital Fire’s picture

Priority: Normal » Critical

Relevant Modules & Versions:
Cart 7.x-3.2
Discount Coupons 7.x-2.1-alpha7
Token 7.x-1.4
Entity API 7.x-1.0-rc3
Views 7.x-3.5

Modules Enabled:
- ALL of UC core.
- Catalog, Payment, Product Attributes, Reports, Shipping, Shipping quotes, Tax Reports, Taxes
- Product Kit, Stock
- Flat Rate
- Credit Card, Cyber Source, Discount Coupons

Payment Gateway: Cyber Source
Payment Method: SOAP Toolkit API - Everything here is configured correctly here.

Shipping: Flat Rate (We add $30 for next day air)

Coupon Settings:
- Coupon is Active is checked.
- Discount amount [we are using percentage based discounts. If the discount is BELOW 9%. It works as it should. If its 9% or HIGHER, it sends the undiscounted amount to the payment gateway.] The calculations on the checkout page are ALWAYS correct. No matter the amount of the coupon percentile.
- Apply Discount to: We have tried with Order Subtotal, and Total of matching products. Both cause the same results.
- Redemption Amounts Restrictions are both blank.

This link mentions a similar issue except it is with paypal.

We have also tried with a FRESH & completely bare Drupal 7, UC 7.x-3.2, & Discounted Coupons 7.x-2.1-alpha7. It still did not work.

Digital Fire’s picture

The problem lies between these lines;

    // Add the order total information.
    $purchaseTotals = new stdClass();
    $purchaseTotals->currency = $currency;

    // Specify the total to charge if it's less than the order total.
    if ($amount < $order->order_total) {
      $purchaseTotals->grandTotalAmount = $amount;
    }
    $request->purchaseTotals = $purchaseTotals;

    // Separately add products and line item into the request items object if
    // we're charging the full order total.
    if (round($amount, 2) == round($order->order_total, 2)) {
      $request->item = array();
      $counter = 0;

      // Add the products to the item array.
      foreach ($order->products as $product) {
        $obj = $request->item[] = new stdClass();

        $obj->productName = $product->title;
        $obj->unitPrice = $product->price;
        $obj->quantity = $product->qty;
        $obj->productSKU = $product->model;
        $obj->productCode = 'default';
        $obj->id = $counter;

        $counter++;
      }

      // Add the line items to the item array.
      $discount_amount = 0;
      foreach ((array) $order->line_items as $line_item) {
        // Handle negative line items.
        if ($line_item['amount'] < 0) {
          $discount_amount += -$line_item['amount'];
        }
        // Skip subtotal line items.
        elseif (strpos($line_item['type'], 'subtotal') === FALSE) {
          $obj = $request->item[] = new stdClass();

          $obj->productName = $line_item['title'];
          $obj->unitPrice = $line_item['amount'];
          $obj->quantity = 1;
          $obj->productSKU = $line_item['type'] . '_' . $line_item['line_item_id'];
          $obj->id = $counter;

          $counter++;
        }
      }
    }

    // Add the total order discount into the request.
    if ($discount_amount != 0) {
      $request->purchaseTotals->discountAmount = $discount_amount;
    }

Here is a successful order w/ a discount of 8%:

    [purchaseTotals] => stdClass Object
        (
            [currency] => usd
            [grandTotalAmount] => 321.58
            [discountAmount] => 23.92
        )

    [item] => Array
        (
            [0] => stdClass Object
                (
                    [productName] => Signature Pendant - 14K Yellow Gold
                    [unitPrice] => 299.00000
                    [quantity] => 1
                    [productSKU] => USI2541YR5J
                    [productCode] => default
                    [id] => 0
                )

            [1] => stdClass Object
                (
                    [productName] => Next Day Air
                    [unitPrice] => 30.00000
                    [quantity] => 1
                    [productSKU] => shipping_158
                    [id] => 1
                )

            [2] => stdClass Object
                (
                    [productName] => FL TAX
                    [unitPrice] => 16.50480
                    [quantity] => 1
                    [productSKU] => tax_160
                    [id] => 2
                )

        )

Here is what a failure looks like with a 10% discount. The threshold to break it is 9% thou;

    [purchaseTotals] => stdClass Object
        (
            [currency] => usd
            [discountAmount] => 29.9
        )

    [item] => Array
        (
            [0] => stdClass Object
                (
                    [productName] => Signature Pendant - 14K Yellow Gold
                    [unitPrice] => 299.00000
                    [quantity] => 1
                    [productSKU] => USI2541YR5J
                    [productCode] => default
                    [id] => 0
                )

            [1] => stdClass Object
                (
                    [productName] => Next Day Air
                    [unitPrice] => 30.00000
                    [quantity] => 1
                    [productSKU] => shipping_155
                    [id] => 1
                )

            [2] => stdClass Object
                (
                    [productName] => FL TAX
                    [unitPrice] => 16.14600
                    [quantity] => 1
                    [productSKU] => tax_157
                    [id] => 2
                )

        )

The difference is that "grandTotalAmount" is getting populated on 8% or less of a discount. Anything after that and "grandTotalAmount" doesn't get generated. Not sure but is this correct?

    // Add the total order discount into the request.
    if ($discount_amount != 0) {
      $request->purchaseTotals->discountAmount = $discount_amount;
    }

Almost seems like something is missing inside the if statement.

Digital Fire’s picture

Assigned: Unassigned » Digital Fire
Priority: Critical » Normal
Status: Active » Needs review

Fixed it by changing the code to the following;

// Specify the total to charge if it's less than the order total.
    if ($amount <= $order->order_total || $amount >= $order->order_total) {
      $purchaseTotals->grandTotalAmount = $amount;
    }
    $request->purchaseTotals = $purchaseTotals;
wodenx’s picture

Project: Ubercart Discount Coupons » Ubercart
Version: 7.x-2.1-alpha7 » 7.x-3.x-dev
Component: uc_coupon_purchase » Payment

This seems like a problem with uc_cybersource. Moving to the UC queue.

TR’s picture

Status: Needs review » Postponed (maintainer needs more info)

If it's a problem with uc_cybersource, then it should work with a different payment method. Can you please try it with a different payment method and verify that it's only uc_cybersource that causes the problem?

Also, if ($amount <= $order->order_total || $amount >= $order->order_total) is always true, so that's not a proper fix ... Please make any proposed changes in the form of a patch so the automated tests can look at it and reviewers can easily try out the changes.

longwave’s picture

According to the documentation:

You may send this field instead of or in addition to the item-level information discussed in
the previous section. If you send purchaseTotals_grandTotalAmount, CyberSource uses
your value and does not use the item-level information to calculate the transaction’s
grand total.

So, it sounds like it is safe to remove the test around grandTotalAmount and always send it. Commerce CyberSource and some other code I found also always sends grandTotalAmount.

Digital Fire’s picture

Yea I had a feeling my fix was dirty but this was on a production site that needed an immediate fix. It fixed the problem so I left that as is. I don't know how to submit a proper patch unfortunately but with the information longwave gave. I can make an attempt to create a clean fix.

longwave’s picture

Status: Postponed (maintainer needs more info) » Needs review
FileSize
817 bytes

If you can test this patch both with and without discounts, and it works successfully, it can be committed.

Digital Fire’s picture

Indeed it worked.

DanZ’s picture

Status: Needs review » Reviewed & tested by the community
longwave’s picture

Status: Reviewed & tested by the community » Fixed

Committed to both branches.

Status: Fixed » Closed (fixed)

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