Skip to content

Commit

Permalink
Added support for refunding invoices and line items
Browse files Browse the repository at this point in the history
  • Loading branch information
andcoca committed Jul 26, 2024
1 parent b67b794 commit 7fcf1fc
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 4 deletions.
33 changes: 32 additions & 1 deletion lib/recurly/invoice.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,25 @@ def refund_amount(amount_in_cents = nil, refund_method = 'credit_first', options
)
end

# Refunds the invoice for a specific percentage.
#
# @return [Invoice, false] Invoice if successful, false if the invoice isn't
# refundable.
# @raise [Error] If the refund fails.
# @param percentage [Integer, nil] The percentage to refund from the invoice.
# @param refund_method ["credit_first", "transaction_first", "all_transaction", "all_credit"] The method used to refund.
# @param external_refund [true, false] Designates that the refund transactions created are manual.
# @param credit_customer_notes [String] Adds notes to refund credit invoice.
# @param payment_method [String] Creates the manual transactions with this payment method. Allowed if *external_refund* is true.
# @param description [String] Sets this value as the *transaction_note* on the manual transactions created. Allowed if *external_refund* is true.
# @param refunded_at [DateTime] Sets this value as the *collected_at* on the manual transactions created. Allowed if *external_refund* is true.
def refund_percentage(percentage = nil, refund_method = 'credit_first', options = {})
return false unless link? :refund
self.class.from_response(
follow_link :refund, :body => refund_percentage_to_xml(percentage, refund_method, options)
)
end

def xml_keys
super - ['currency']
end
Expand Down Expand Up @@ -285,6 +304,16 @@ def refund_amount_to_xml(amount_in_cents = nil, refund_method = nil, options = {
builder.to_s
end

def refund_percentage_to_xml(percentage = nil, refund_method = nil, options = {})
builder = XML.new("<invoice/>")
builder.add_element 'refund_method', refund_method
builder.add_element 'percentage', percentage
options.each do |k, v|
builder.add_element k.to_s, v
end
builder.to_s
end

def refund_line_items_to_xml(line_items = nil, refund_method = nil, options = {})
builder = XML.new("<invoice/>")
builder.add_element 'refund_method', refund_method
Expand All @@ -296,8 +325,10 @@ def refund_line_items_to_xml(line_items = nil, refund_method = nil, options = {}
line_items.each do |line_item|
adj_node = node.add_element 'adjustment'
adj_node.add_element 'uuid', line_item[:adjustment].uuid
adj_node.add_element 'quantity', line_item[:quantity]
adj_node.add_element 'quantity', line_item[:quantity] if line_item.key?(:quantity)
adj_node.add_element('quantity_decimal', line_item[:quantity_decimal]) if line_item.key?(:quantity_decimal)
adj_node.add_element 'percentage', line_item[:percentage] if line_item.key?(:percentage)
adj_node.add_element 'amount_in_cents', line_item[:amount_in_cents] if line_item.key?(:amount_in_cents)
adj_node.add_element 'prorate', line_item[:prorate]
end
builder.to_s
Expand Down
89 changes: 86 additions & 3 deletions spec/recurly/invoice_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,22 @@
adjustment.quantity_remaining.must_equal 1
end
end

it "creates a refund invoice for the line items refunded with amount in cents" do
line_items = @invoice.line_items.values.map do |adjustment|
{ adjustment: adjustment, amount_in_cents: 100, prorate: false }
end
refund_invoice = @invoice.refund line_items
refund_invoice.must_be_instance_of Invoice
end

it "creates a refund invoice for the line items refunded with percentage" do
line_items = @invoice.line_items.values.map do |adjustment|
{ adjustment: adjustment, percentage: 77, prorate: false }
end
refund_invoice = @invoice.refund line_items
refund_invoice.must_be_instance_of Invoice
end
end

describe "#refund_to_xml" do
Expand All @@ -178,6 +194,40 @@
'<invoice><refund_method>credit_first</refund_method><credit_customer_notes>Credit Notes</credit_customer_notes><external_refund>true</external_refund><payment_method>check</payment_method><description>Check no. 12345678</description><refunded_at>2018-12-01T00:00:00+00:00</refunded_at><amount_in_cents>17500</amount_in_cents><line_items><adjustment><uuid>charge1</uuid><quantity>1</quantity><prorate>false</prorate></adjustment></line_items></invoice>'
)
end

it "must serialize line_items with amount_in_cents" do
line_items = @invoice.line_items.values.map do |adjustment|
{ adjustment: adjustment, amount_in_cents: 100, prorate: false }
end
options = {
credit_customer_notes: 'Credit Notes',
external_refund: true,
payment_method: 'check',
description: 'Check no. 12345678',
refunded_at: DateTime.new(2018, 12, 1, 0, 0, 0),
amount_in_cents: 17_500
}
@invoice.send(:refund_line_items_to_xml, line_items, 'credit_first', options).must_equal(
'<invoice><refund_method>credit_first</refund_method><credit_customer_notes>Credit Notes</credit_customer_notes><external_refund>true</external_refund><payment_method>check</payment_method><description>Check no. 12345678</description><refunded_at>2018-12-01T00:00:00+00:00</refunded_at><amount_in_cents>17500</amount_in_cents><line_items><adjustment><uuid>charge1</uuid><amount_in_cents>100</amount_in_cents><prorate>false</prorate></adjustment></line_items></invoice>'
)
end

it "must serialize line_items with percentage" do
line_items = @invoice.line_items.values.map do |adjustment|
{ adjustment: adjustment, percentage: 77, prorate: false }
end
options = {
credit_customer_notes: 'Credit Notes',
external_refund: true,
payment_method: 'check',
description: 'Check no. 12345678',
refunded_at: DateTime.new(2018, 12, 1, 0, 0, 0),
amount_in_cents: 17_500
}
@invoice.send(:refund_line_items_to_xml, line_items, 'credit_first', options).must_equal(
'<invoice><refund_method>credit_first</refund_method><credit_customer_notes>Credit Notes</credit_customer_notes><external_refund>true</external_refund><payment_method>check</payment_method><description>Check no. 12345678</description><refunded_at>2018-12-01T00:00:00+00:00</refunded_at><amount_in_cents>17500</amount_in_cents><line_items><adjustment><uuid>charge1</uuid><percentage>77</percentage><prorate>false</prorate></adjustment></line_items></invoice>'
)
end
end
end

Expand All @@ -189,16 +239,16 @@
@invoice = Invoice.find 'refundable-invoice'
end

describe "#refund" do
it "creates a refund invoice for the line items refunded" do
describe "#refund_amount" do
it "creates a refund invoice for the line items refunded using amount" do
refund_invoice = @invoice.refund_amount 1000
refund_invoice.must_be_instance_of Invoice
refund_invoice.original_invoices.must_be_instance_of Recurly::Resource::Pager
refund_invoice.amount_remaining_in_cents.must_equal 100
end
end

describe "#refund_to_xml" do
describe "#refund_amount_to_xml" do
it "must serialize amount_in_cents" do
options = {
credit_customer_notes: 'Credit Notes',
Expand All @@ -215,6 +265,39 @@
end
end

describe "percentage refund" do
before do
stub_api_request :get, 'invoices/refundable-invoice', 'invoices/show-200-refundable'
stub_api_request :post, 'invoices/refundable-invoice/refund', 'invoices/refund_amount-201'

@invoice = Invoice.find 'refundable-invoice'
end

describe "#refund_percentage" do
it "creates a refund invoice for the line items refunded using percentage" do
refund_invoice = @invoice.refund_percentage 50
refund_invoice.must_be_instance_of Invoice
refund_invoice.original_invoices.must_be_instance_of Recurly::Resource::Pager
refund_invoice.amount_remaining_in_cents.must_equal 100
end
end

describe "#refund_percentage_to_xml" do
it "must serialize percentage" do
options = {
credit_customer_notes: 'Credit Notes',
external_refund: true,
payment_method: 'check',
description: 'Check no. 12345678',
refunded_at: DateTime.new(2018, 12, 1, 0, 0, 0)
}
@invoice.send(:refund_percentage_to_xml, 50, 'credit_first', options).must_equal(
'<invoice><refund_method>credit_first</refund_method><percentage>50</percentage><credit_customer_notes>Credit Notes</credit_customer_notes><external_refund>true</external_refund><payment_method>check</payment_method><description>Check no. 12345678</description><refunded_at>2018-12-01T00:00:00+00:00</refunded_at></invoice>'
)
end
end
end

describe "failed_refund" do
before do
stub_api_request :get, 'invoices/refundable-invoice', 'invoices/show-200-refundable'
Expand Down

0 comments on commit 7fcf1fc

Please sign in to comment.