diff --git a/Library/Adjustment.cs b/Library/Adjustment.cs index 04ddaf75..d8cb8355 100644 --- a/Library/Adjustment.cs +++ b/Library/Adjustment.cs @@ -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; } @@ -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": diff --git a/Library/Invoice.cs b/Library/Invoice.cs index 96ce2fe5..508cd1fd 100644 --- a/Library/Invoice.cs +++ b/Library/Invoice.cs @@ -458,7 +458,29 @@ public Invoice Refund(IEnumerable 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; + } + + /// + /// Allows you to refund a percentage from an invoice. + /// + /// The percentage as an integer to refund from the invoice. + /// The options for the refund invoice. + /// new Invoice object + 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", diff --git a/Library/OpenAmountRefund.cs b/Library/OpenAmountRefund.cs index 64dffce7..461922df 100644 --- a/Library/OpenAmountRefund.cs +++ b/Library/OpenAmountRefund.cs @@ -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; } @@ -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) diff --git a/Library/Refund.cs b/Library/Refund.cs index 04a04c71..d66268ec 100644 --- a/Library/Refund.cs +++ b/Library/Refund.cs @@ -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).")] @@ -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; } @@ -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()); diff --git a/Test/InvoiceTest.cs b/Test/InvoiceTest.cs index 93fdb52a..117ac501 100644 --- a/Test/InvoiceTest.cs +++ b/Test/InvoiceTest.cs @@ -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() { @@ -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() {