spree-contrib / better_spree_paypal_express

A better Spree PayPal Express Extension.
http://guides.spreecommerce.org
BSD 3-Clause "New" or "Revised" License
110 stars 270 forks source link

Solution for taking in consideration Store Credits #208

Open LucasKuhn opened 5 years ago

LucasKuhn commented 5 years ago

You just need to edit the item_sum to consider the applied store credits (current_order.total_applied_store_credit)

https://github.com/spree-contrib/better_spree_paypal_express/blob/962a789738b59f440bf44ea22e64b65cc2572535/app/controllers/spree/paypal_controller.rb#L117

item_sum = current_order.total - shipment_sum - current_order.additional_tax_total - order.total_applied_store_credit

And add and item with the store credit discount after adding items for all other adjustments https://github.com/spree-contrib/better_spree_paypal_express/blob/4f9a187d31d96be8a616cbb5318cc3e2a713f056/app/controllers/spree/paypal_controller.rb#L28

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

Below my full file, besides the changes above everything else is the same. Store Credits working fine with only these small tweaks to express and payment details method

# app/controllers/spree/paypal_controller_decorator.rb

Spree::PaypalController.class_eval do
  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 order.using_store_credit?
      items << {
        Name: "Store Credits",
        Quantity: 1,
        Amount: {
          currencyID: order.currency,
          value: 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 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

    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
end
chozandrias76 commented 5 years ago

Thank you for detailing this solution! I just spent about an hour looking into how to monkey patch a fix in and it is good to see that my solution matches yours!