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

Support for new line item refund work #844

Merged
merged 3 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Library/Adjustment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public enum RevenueSchedule : short
public int UnitAmountInCents { get; set; }
public int Quantity { get; set; }
public decimal? QuantityDecimal { get; set; }
public int? RefundAmountInCents { get; set; }
public int? RefundPercentage { get; set; }
public Refund.RefundType RefundType { get; set; }
public int? QuantityRemaining { get; set; }
public decimal? QuantityDecimalRemaining { get; set; }
public int DiscountInCents { get; protected set; }
Expand Down Expand Up @@ -235,7 +238,9 @@ internal override void ReadXml(XmlTextReader reader)
break;

case "quantity_decimal":
QuantityDecimal = reader.ReadElementContentAsDecimal();
var quantityDecimal = reader.ReadElementContentAsString();
if (!quantityDecimal.IsNullOrEmpty())
QuantityDecimal = reader.ReadElementContentAsDecimal();
break;

case "quantity_remaining":
Expand Down
24 changes: 23 additions & 1 deletion Library/Invoice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,29 @@ public Invoice Refund(IEnumerable<Adjustment> adjustments, bool prorate = false,
public Invoice RefundAmount(int amountInCents, RefundOptions options)
{
var refundInvoice = new Invoice();
var refund = new OpenAmountRefund(amountInCents, options);
var refund = new OpenAmountRefund("amount_in_cents", amountInCents, options);

var statusCode = Client.Instance.PerformRequest(Client.HttpRequestMethod.Post,
memberUrl() + "/refund",
refund.WriteXml,
refundInvoice.ReadXml);

if (HttpStatusCode.Created == statusCode || HttpStatusCode.OK == statusCode)
return refundInvoice;
else
return null;
}

/// <summary>
/// Allows you to refund a percentage from an invoice.
/// </summary>
/// <param name="percentage">The percentage as an integer to refund from the invoice.</param>
/// <param name="options">The options for the refund invoice.</param>
/// <returns>new Invoice object</returns>
public Invoice RefundPercentage(int percentage, RefundOptions options)
{
var refundInvoice = new Invoice();
var refund = new OpenAmountRefund("percentage", percentage, options);

var statusCode = Client.Instance.PerformRequest(Client.HttpRequestMethod.Post,
memberUrl() + "/refund",
Expand Down
12 changes: 7 additions & 5 deletions Library/OpenAmountRefund.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@ namespace Recurly
{
class OpenAmountRefund : RecurlyEntity
{
public int AmountInCents { get; protected set; }
public int OpenAmount { get; protected set; }
public string RefundType { get; protected set; }
private Invoice.RefundOptions RefundOptions;

[Obsolete("This constructor is deprecated, please use OpenAmountRefund(int, Invoice.RefundOptions).")]
internal OpenAmountRefund(int amountInCents, Invoice.RefundMethod method = Invoice.RefundMethod.CreditFirst)
{
AmountInCents = amountInCents;
OpenAmount = amountInCents;
RefundOptions = new Invoice.RefundOptions()
{
Method = method
};
}

internal OpenAmountRefund(int amountInCents, Invoice.RefundOptions options)
internal OpenAmountRefund(string refundType, int amount, Invoice.RefundOptions options)
{
AmountInCents = amountInCents;
RefundType = refundType;
OpenAmount = amount;
RefundOptions = options;
}

Expand All @@ -32,7 +34,7 @@ internal override void ReadXml(XmlTextReader reader)
internal override void WriteXml(XmlTextWriter writer)
{
writer.WriteStartElement("invoice");
writer.WriteElementString("amount_in_cents", AmountInCents.AsString());
writer.WriteElementString(RefundType, OpenAmount.AsString());
writer.WriteElementString("refund_method", RefundOptions.Method.ToString().EnumNameToTransportCase());

if (RefundOptions.ExternalRefund.HasValue)
Expand Down
28 changes: 25 additions & 3 deletions Library/Refund.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,19 @@ namespace Recurly
{
public class Refund : RecurlyEntity
{
public enum RefundType
{
Quantity,
Percentage,
AmountInCents
}

public bool Prorate { get; protected set; }
public int Quantity { get; protected set; }
public decimal? QuantityDecimal { get; protected set; }
public int? AmountInCents { get; protected set; }
public int? Percentage { get; protected set; }
public RefundType Type { get; protected set; }
public string Uuid { get; protected set; }

[Obsolete("This constructor is deprecated, please use Refund(Adjustment).")]
Expand All @@ -23,6 +33,9 @@ internal Refund(Adjustment adjustment)
Prorate = adjustment.Prorate.HasValue ? adjustment.Prorate.Value : false;
Quantity = adjustment.Quantity;
QuantityDecimal = adjustment.QuantityDecimal;
AmountInCents = adjustment.RefundAmountInCents;
Percentage = adjustment.RefundPercentage;
Type = adjustment.RefundType;
Uuid = adjustment.Uuid;
}

Expand All @@ -36,10 +49,19 @@ internal override void WriteXml(XmlTextWriter writer)
writer.WriteStartElement("adjustment");

writer.WriteElementString("uuid", Uuid);
writer.WriteElementString("quantity", Quantity.AsString());

if (QuantityDecimal.HasValue)
writer.WriteElementString("quantity_decimal", QuantityDecimal.Value.ToString());
if (Type == RefundType.Quantity)
{
writer.WriteElementString("quantity", Quantity.AsString());
if (QuantityDecimal.HasValue)
writer.WriteElementString("quantity_decimal", QuantityDecimal.Value.ToString());
}

if (Type == RefundType.Percentage)
writer.WriteElementString("percentage", Percentage.Value.ToString());

if (Type == RefundType.AmountInCents)
writer.WriteElementString("amount_in_cents", AmountInCents.Value.ToString());

writer.WriteElementString("prorate", Prorate.AsString());

Expand Down
101 changes: 101 additions & 0 deletions Test/InvoiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,35 @@ public void RefundSingle()
account.Close();
}

[RecurlyFact(TestEnvironment.Type.Integration)]
public void RefundPercentage()
{
var account = CreateNewAccountWithBillingInfo();

var adjustment = account.NewAdjustment("USD", 1000, "Test Charge");

adjustment.Create();

var collection = account.InvoicePendingCharges();
var invoice = collection.ChargeInvoice;

invoice.MarkSuccessful();

invoice.State.Should().Be(Invoice.InvoiceState.Paid);

Assert.Equal(1, invoice.Adjustments.Count);

// refund
System.Threading.Thread.Sleep(1000); // Sleep hack to avoid simultaneous_request errors
adjustment.RefundType = Refund.RefundType.Percentage;
adjustment.RefundPercentage = 10;
var refundInvoice = invoice.Refund(adjustment, new Invoice.RefundOptions());
Assert.NotEqual(invoice.Uuid, refundInvoice.Uuid);
Assert.Equal(-100, refundInvoice.SubtotalInCents);
Assert.Equal(1, refundInvoice.Adjustments.Count);
account.Close();
}

[RecurlyFact(TestEnvironment.Type.Integration)]
public void UpdateInvoice()
{
Expand Down Expand Up @@ -444,6 +473,78 @@ public void RefundLineItems()
account.Close();
}

[RecurlyFact(TestEnvironment.Type.Integration)]
public void RefundLineItemsPercentage()
{
var account = CreateNewAccountWithBillingInfo();
var adjustment = account.NewAdjustment("USD", 4000, "Test Charge 1");
adjustment.Create();
adjustment = account.NewAdjustment("USD", 4999, "Test Charge 2");
adjustment.Create();
var collection = account.InvoicePendingCharges();
var invoice = collection.ChargeInvoice;
invoice.MarkSuccessful();


invoice.State.Should().Be(Invoice.InvoiceState.Paid);
Assert.Equal(2, invoice.Adjustments.Count);

var refundOptions = new Invoice.RefundOptions()
{
ExternalRefund = true,
Description = "External Refund Description",
CreditCustomerNotes = "Credit Customer Notes",
PaymentMethod = "credit_card",
Method = Invoice.RefundMethod.AllTransaction
};

adjustment = invoice.Adjustments[0];
adjustment.RefundType = Refund.RefundType.Percentage;
adjustment.RefundPercentage = 10;
System.Threading.Thread.Sleep(1000); // Sleep hack to avoid simultaneous_request errors
var refundInvoice = invoice.Refund(adjustment, refundOptions);
Assert.NotEqual(invoice.Uuid, refundInvoice.Uuid);
Assert.Equal(1, refundInvoice.Adjustments.Count);

account.Close();
}

[RecurlyFact(TestEnvironment.Type.Integration)]
public void RefundLineItemsAmountInCents()
{
var account = CreateNewAccountWithBillingInfo();
var adjustment = account.NewAdjustment("USD", 4000, "Test Charge 1");
adjustment.Create();
adjustment = account.NewAdjustment("USD", 4999, "Test Charge 2");
adjustment.Create();
var collection = account.InvoicePendingCharges();
var invoice = collection.ChargeInvoice;
invoice.MarkSuccessful();


invoice.State.Should().Be(Invoice.InvoiceState.Paid);
Assert.Equal(2, invoice.Adjustments.Count);

var refundOptions = new Invoice.RefundOptions()
{
ExternalRefund = true,
Description = "External Refund Description",
CreditCustomerNotes = "Credit Customer Notes",
PaymentMethod = "credit_card",
Method = Invoice.RefundMethod.AllTransaction
};

adjustment = invoice.Adjustments[0];
adjustment.RefundType = Refund.RefundType.AmountInCents;
adjustment.RefundAmountInCents = 10;
System.Threading.Thread.Sleep(1000); // Sleep hack to avoid simultaneous_request errors
var refundInvoice = invoice.Refund(adjustment, refundOptions);
Assert.NotEqual(invoice.Uuid, refundInvoice.Uuid);
Assert.Equal(1, refundInvoice.Adjustments.Count);

account.Close();
}

[RecurlyFact(TestEnvironment.Type.Integration)]
public void EnterOfflinePayment()
{
Expand Down
Loading