Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Paypal receives full amount when there are multiple payments on order #190

Open
joeswann opened this issue Nov 21, 2016 · 4 comments
Open

Comments

@joeswann
Copy link

joeswann commented Nov 21, 2016

So I got here via quite a roundabout route. The long and short of it is I wanted paypal to receive a correct total when I had already applied a payment (in this case a gift card).

I am somewhat cheating in my solution, because in order to update the outstanding_balance I need to first process the gift card. I believe store_credit also has the ability to do a partial deduction so this might apply there too?

My current solution is to replace the payment_details function in app/controllers/spree/paypal_controller.rb with one that uses outstanding_balance.

This may not work if store credit amount doesn't process until the order state == complete. In that case maybe it would be better to manually add up uncaptured payment amounts (from methods that aren't the current payment method) and use that instead?

@joeswann joeswann changed the title Paypal receives full amount when outstanding_balance != total Paypal receives full amount when there are multiple payments on order Nov 21, 2016
@joeswann
Copy link
Author

Update: just using outstanding_balance doesn't work because capture doesn't generally fire until after the order is completed.

What I'm going to try instead is to calculate an order total that takes other (valid) payments into account when creating its total.

@joeswann
Copy link
Author

Okay so I have this working, basically when it has other valid payments on the order it calculates the new total using current_order.total - payment_adjustment where payment_adjustment is attached to an item as follows

payment_adjustment = current_order.payments.valid.sum(:amount)

items << {
  Name: Spree.t(:payment_adjustment, :scope => 'paypal'), 
  Quantity: 1,
  Amount: {
    currencyID: current_order.currency,
    value: payment_adjustment * -1
  }
}

@tibomogul
Copy link

You also need to adjust the order.total in the confirm action, so that the payment recorded in spree tallies with that on Paypal

@chozandrias76
Copy link

This is my full file to get paypal_express to work with store credit

Spree::PaypalController.class_eval do
  # rubocop:disable Metrics/AbcSize
  # rubocop:disable Metrics/CyclomaticComplexity
  # rubocop:disable Metrics/MethodLength
  # rubocop:disable Metrics/PerceivedComplexity
  def express
    order = current_order || raise(ActiveRecord::RecordNotFound)
    items = order.line_items.map(&method(:line_item))

    additional_adjustments = order.all_adjustments.additional
    tax_adjustments = additional_adjustments.tax
    shipping_adjustments = additional_adjustments.shipping

    additional_adjustments.eligible.each do |adjustment|
      # Because PayPal doesn't accept $0 items at all. See #10
      # https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECCustomizing
      # "It can be a positive or negative value but not zero."
      next if adjustment.amount.zero?
      next if tax_adjustments.include?(adjustment) || shipping_adjustments.include?(adjustment)

      items << {
        Name: adjustment.label,
        Quantity: 1,
        Amount: {
          currencyID: order.currency,
          value: adjustment.amount
        }
      }
    end

    if current_order.using_store_credit?
      items << {
        Name: "Store Credits",
        Quantity: 1,
        Amount: {
          currencyID: current_order.currency,
          value: current_order.total_applied_store_credit * -1
        }
      }
    end

    pp_request = provider.build_set_express_checkout(express_checkout_request_details(order, items))

    begin
      pp_response = provider.set_express_checkout(pp_request)
      if pp_response.success?
        redirect_to provider.express_checkout_url(pp_response, useraction: 'commit')
      else
        flash[:error] = Spree.t('flash.generic_error', scope: 'paypal',
           reasons: pp_response.errors.map(&:long_message).join(" "))
        redirect_to checkout_state_path(:payment)
      end
    rescue SocketError
      flash[:error] = Spree.t('flash.connection_failed', scope: 'paypal')
      redirect_to checkout_state_path(:payment)
    end
  end

  def confirm
    order = current_order || raise(ActiveRecord::RecordNotFound)
    order.payments.create!(
      source: Spree::PaypalExpressCheckout.create(
        token: params[:token],
          payer_id: params[:PayerID]
      ),
      amount: current_order.using_store_credit? ? order.total - order.total_applied_store_credit : order.total,
      payment_method: payment_method
    )
    order.next
    if order.complete?
      flash.notice = Spree.t(:order_processed_successfully)
      flash[:order_completed] = true
      session[:order_id] = nil
      redirect_to completion_route(order)
    else
      redirect_to checkout_state_path(order.state)
    end
  end
  # rubocop:enable Metrics/AbcSize
  # rubocop:enable Metrics/CyclomaticComplexity
  # rubocop:enable Metrics/MethodLength
  # rubocop:enable Metrics/PerceivedComplexity

  # rubocop:disable Metrics/MethodLength
  def payment_details(items)
    # This retrieves the cost of shipping after promotions are applied
    # For example, if shippng costs $10, and is free with a promotion, shipment_sum is now $10
    shipment_sum = current_order.shipments.map(&:discounted_cost).sum

    # This calculates the item sum based upon what is in the order total, but not for shipping
    # or tax.  This is the easiest way to determine what the items should cost, as that
    # functionality doesn't currently exist in Spree core
    item_sum = current_order.total - shipment_sum - current_order.additional_tax_total -
               current_order.total_applied_store_credit

    # # Applying store credit to deduct from the order total
    # item_sum = item_sum - current_order.payments.where(payment_method_id: 8).sum(&:amount)

    if item_sum.zero?
      # Paypal does not support no items or a zero dollar ItemTotal
      # This results in the order summary being simply "Current purchase"
      {
        OrderTotal: {
          currencyID: current_order.currency,
          value: current_order.total
        }
      }
    else
      {
        OrderTotal: {
          currencyID: current_order.currency,
          value: current_order.total - current_order.total_applied_store_credit
        },
        ItemTotal: {
          currencyID: current_order.currency,
          value: item_sum
        },
        ShippingTotal: {
          currencyID: current_order.currency,
          value: shipment_sum,
        },
        TaxTotal: {
          currencyID: current_order.currency,
          value: current_order.additional_tax_total
        },
        ShipToAddress: address_options,
        PaymentDetailsItem: items,
        ShippingMethod: "Shipping Method Name Goes Here",
        PaymentAction: "Sale"
      }
    end
  end
  # rubocop:enable Metrics/MethodLength
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants