Open joeswann opened 7 years ago
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.
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
}
}
You also need to adjust the order.total in the confirm action, so that the payment recorded in spree tallies with that on Paypal
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
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 usesoutstanding_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?