diff --git a/Library/ExternalInvoice.cs b/Library/ExternalInvoice.cs index ed28a1b4..cd5d80e7 100644 --- a/Library/ExternalInvoice.cs +++ b/Library/ExternalInvoice.cs @@ -34,6 +34,15 @@ public ExternalSubscription ExternalSubscription internal set { _externalSubscription = value; } } + private string _externalPaymentPhaseUuid; + public string ExternalPaymentPhaseUuid => _externalPaymentPhaseUuid; + + private ExternalPaymentPhase _externalPaymentPhase; + public ExternalPaymentPhase ExternalPaymentPhase + { + get { return _externalPaymentPhase ?? (_externalPaymentPhase = ExternalPaymentPhases.Get(_externalPaymentPhaseUuid)); } + internal set { _externalPaymentPhase = value; } + } public string ExternalId { get; private set; } public ExternalInvoiceState State { get; private set; } public decimal Total { get; private set; } @@ -86,6 +95,12 @@ internal override void ReadXml(XmlTextReader reader) _externalSubscriptionUuid = Uri.UnescapeDataString(href.Substring(href.LastIndexOf("/") + 1)); break; + case "external_payment_phase": + href = reader.GetAttribute("href"); + if (null != href) + _externalPaymentPhaseUuid = Uri.UnescapeDataString(href.Substring(href.LastIndexOf("/") + 1)); + break; + case "external_id": ExternalId = reader.ReadElementContentAsString(); break; diff --git a/Library/ExternalPaymentPhase.cs b/Library/ExternalPaymentPhase.cs new file mode 100644 index 00000000..2b55ec2c --- /dev/null +++ b/Library/ExternalPaymentPhase.cs @@ -0,0 +1,154 @@ +using System; +using System.Net; +using System.Xml; + +namespace Recurly +{ + public class ExternalPaymentPhase : RecurlyEntity + { + public DateTime StartedAt { get; private set; } + public DateTime EndsAt { get; private set; } + public int StartingBillingPeriodIndex { get; private set; } + public int EndingBillingPeriodIndex { get; private set; } + public string OfferType { get; private set; } + public string OfferName { get; private set; } + public int PeriodCount { get; private set; } + public string PeriodLength { get; private set; } + public decimal Amount { get; private set; } + public string Currency { get; private set; } + public DateTime CreatedAt { get; private set; } + public DateTime UpdatedAt { get; private set; } + + internal const string UrlPrefix = "/external_payment_phases/"; + + internal ExternalPaymentPhase() + { + } + internal ExternalPaymentPhase(XmlTextReader reader) + { + ReadXml(reader); + } + #region Read XML documents + + internal override void ReadXml(XmlTextReader reader) + { + while (reader.Read()) + { + DateTime dateVal; + + if (reader.Name == "external_payment_phase" && reader.NodeType == XmlNodeType.EndElement) + break; + + if (reader.NodeType != XmlNodeType.Element) continue; + + switch (reader.Name) + { + case "started_at": + if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal)) + StartedAt = dateVal; ; + break; + + case "ends_at": + if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal)) + EndsAt = dateVal; ; + break; + + case "starting_billing_period_index": + StartingBillingPeriodIndex = reader.ReadElementContentAsInt(); + break; + + case "ending_billing_period_index": + EndingBillingPeriodIndex = reader.ReadElementContentAsInt(); + break; + + case "offer_type": + OfferType = reader.ReadElementContentAsString(); + break; + + case "offer_name": + OfferName = reader.ReadElementContentAsString(); + break; + + case "period_count": + PeriodCount = reader.ReadElementContentAsInt(); + break; + + case "period_length": + PeriodLength = reader.ReadElementContentAsString(); + break; + + case "amount": + Amount = reader.ReadElementContentAsDecimal(); + break; + + case "currency": + Currency = reader.ReadElementContentAsString(); + break; + + case "updated_at": + if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal)) + UpdatedAt = dateVal; ; + break; + + case "created_at": + if (DateTime.TryParse(reader.ReadElementContentAsString(), out dateVal)) + CreatedAt = dateVal; ; + break; + + } + } + } + + internal override void WriteXml(XmlTextWriter writer) + { + throw new NotImplementedException(); + } + + #endregion + + #region Object Overrides + + public override string ToString() + { + return "Recurly External Payment Phase: " + StartedAt + " " + EndsAt + " " + StartingBillingPeriodIndex + " " + EndingBillingPeriodIndex + " " + OfferType + " " + OfferName + " " + PeriodCount + " " + PeriodLength + " " + Amount + " " + Currency + " " + CreatedAt + " " + UpdatedAt; + } + + #endregion + } + + public sealed class ExternalPaymentPhases + { + /// + /// Returns a list of recurly external_payment_phases + /// + /// + public static RecurlyList List() + { + return List(null); + } + /// + /// Returns a list of recurly external_payment_phases + /// + /// FilterCriteria used to apply server side sorting and filtering + /// + public static RecurlyList List(FilterCriteria filter) + { + filter = filter ?? FilterCriteria.Instance; + var parameters = filter.ToNamedValueCollection(); + return new ExternalPaymentPhaseList(ExternalPaymentPhase.UrlPrefix + "?" + parameters.ToString()); + } + public static ExternalPaymentPhase Get(string uuid) + { + if (string.IsNullOrWhiteSpace(uuid)) + { + return null; + } + var externalPaymentPhase = new ExternalPaymentPhase(); + var statusCode = Client.Instance.PerformRequest(Client.HttpRequestMethod.Get, + ExternalPaymentPhase.UrlPrefix + Uri.EscapeDataString(uuid), + externalPaymentPhase.ReadXml); + + return statusCode == HttpStatusCode.NotFound ? null : externalPaymentPhase; + } + } +} diff --git a/Library/ExternalSubscription.cs b/Library/ExternalSubscription.cs index 661b81e3..1e1082bb 100644 --- a/Library/ExternalSubscription.cs +++ b/Library/ExternalSubscription.cs @@ -21,6 +21,12 @@ public Account Account internal set { _account = value; } } + private List _externalInvoices; + public List ExternalInvoices + { + get { return _externalInvoices ?? (_externalInvoices = new List()); } + set { _externalInvoices = value; } + } public string ExternalId { get; set; } public string AppIdentifier { get; private set; } public string State { get; set; } @@ -204,5 +210,33 @@ public static RecurlyList GetExternalInvoices(string uuid) { return new ExternalInvoiceList(ExternalSubscription.UrlPrefix + Uri.EscapeDataString(uuid) + "/external_invoices/"); } + + /// + /// Returns a list of external_payment_phases for this external subscription + /// + /// + public static RecurlyList GetExternalPaymentPhases(string uuid) + { + return new ExternalPaymentPhaseList(ExternalSubscription.UrlPrefix + Uri.EscapeDataString(uuid) + ExternalPaymentPhase.UrlPrefix); + } + + /// + /// Returns an external payment phase for external subscription + /// + /// + public static ExternalPaymentPhase GetExternalPaymentPhase(string uuid, string externalPaymentPhaseUuid) + { + if (string.IsNullOrWhiteSpace(uuid) || string.IsNullOrWhiteSpace(externalPaymentPhaseUuid)) + { + return null; + } + var externalPaymentPhase = new ExternalPaymentPhase(); + var statusCode = Client.Instance.PerformRequest(Client.HttpRequestMethod.Get, + ExternalSubscription.UrlPrefix + Uri.EscapeDataString(uuid) + ExternalPaymentPhase.UrlPrefix + Uri.EscapeDataString(externalPaymentPhaseUuid), + externalPaymentPhase.ReadXml); + + return statusCode == HttpStatusCode.NotFound ? null : externalPaymentPhase; + } + } } diff --git a/Library/List/ExternalInvoiceList.cs b/Library/List/ExternalInvoiceList.cs index c8998658..ff59b92f 100644 --- a/Library/List/ExternalInvoiceList.cs +++ b/Library/List/ExternalInvoiceList.cs @@ -8,6 +8,9 @@ internal ExternalInvoiceList(string baseUrl) : base(Client.HttpRequestMethod.Get { } + internal ExternalInvoiceList() + { + } public override RecurlyList Start { get { return HasStartPage() ? new ExternalInvoiceList(StartUrl) : RecurlyList.Empty(); } diff --git a/Library/List/ExternalPaymentPhaseList.cs b/Library/List/ExternalPaymentPhaseList.cs new file mode 100644 index 00000000..7a652532 --- /dev/null +++ b/Library/List/ExternalPaymentPhaseList.cs @@ -0,0 +1,43 @@ +using System.Xml; + +namespace Recurly +{ + public class ExternalPaymentPhaseList : RecurlyList + { + internal ExternalPaymentPhaseList(string baseUrl) : base(Client.HttpRequestMethod.Get, baseUrl) + { + } + + internal ExternalPaymentPhaseList() + { + } + public override RecurlyList Start + { + get { return HasStartPage() ? new ExternalPaymentPhaseList(StartUrl) : RecurlyList.Empty(); } + } + + public override RecurlyList Next + { + get { return HasNextPage() ? new ExternalPaymentPhaseList(NextUrl) : RecurlyList.Empty(); } + } + + public override RecurlyList Prev + { + get { return HasPrevPage() ? new ExternalPaymentPhaseList(PrevUrl) : RecurlyList.Empty(); } + } + + internal override void ReadXml(XmlTextReader reader) + { + while (reader.Read()) + { + if (reader.Name == "external_payment_phases" && reader.NodeType == XmlNodeType.EndElement) + break; + + if (reader.NodeType == XmlNodeType.Element && reader.Name == "external_payment_phase") + { + Add(new ExternalPaymentPhase(reader)); + } + } + } + } +} \ No newline at end of file diff --git a/Test/ExternalInvoiceTest.cs b/Test/ExternalInvoiceTest.cs index e0fca1dd..583fb004 100644 --- a/Test/ExternalInvoiceTest.cs +++ b/Test/ExternalInvoiceTest.cs @@ -1,7 +1,7 @@ using System; -using System.Collections.Generic; +using System.Xml; using FluentAssertions; -using Xunit; +using Recurly.Test.Fixtures; namespace Recurly.Test { @@ -10,12 +10,16 @@ public class ExternalInvoiceTest : BaseTest [RecurlyFact(TestEnvironment.Type.Integration)] public void LookupExternalInvoice() { - var uuid = "sl7984v66da8"; - var externalInvoice = ExternalInvoices.Get(uuid); - externalInvoice.ExternalId.Should().Be("external_id"); + var xmlFixture = FixtureImporter.Get(FixtureType.ExternalInvoices, "show-200").Xml; + XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture)); + var externalInvoice = new ExternalInvoice(); + externalInvoice.ReadXml(reader); + externalInvoice.ExternalId.Should().Be("2000000458276005"); externalInvoice.State.Should().Be(ExternalInvoice.ExternalInvoiceState.Paid); - externalInvoice.Total.Should().Be(123); + externalInvoice.Total.Should().Be(new decimal(10.45)); externalInvoice.Currency.Should().Be("USD"); + externalInvoice.ExternalSubscriptionUuid.Should().Be("tsfnx2vn5wh6"); + externalInvoice.ExternalPaymentPhaseUuid.Should().Be("twqswp627ri3"); externalInvoice.PurchasedAt.Should().NotBe(default(DateTime)); externalInvoice.CreatedAt.Should().NotBe(default(DateTime)); externalInvoice.UpdatedAt.Should().NotBe(default(DateTime)); diff --git a/Test/ExternalPaymentPhaseTest.cs b/Test/ExternalPaymentPhaseTest.cs new file mode 100644 index 00000000..4091d766 --- /dev/null +++ b/Test/ExternalPaymentPhaseTest.cs @@ -0,0 +1,31 @@ +using System; +using System.Xml; +using FluentAssertions; +using Recurly.Test.Fixtures; + +namespace Recurly.Test +{ + public class ExternalPaymentPhaseTest : BaseTest + { + [RecurlyFact(TestEnvironment.Type.Integration)] + public void LookupExternalPaymentPhase() + { + var externalPaymentPhase = new ExternalPaymentPhase(); + var xmlFixture = FixtureImporter.Get(FixtureType.ExternalPaymentPhases, "show-200").Xml; + XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture)); + externalPaymentPhase.ReadXml(reader); + externalPaymentPhase.StartedAt.Should().Be(new DateTime(2023, 11, 15, 0, 0, 0, DateTimeKind.Utc)); + externalPaymentPhase.EndsAt.Should().Be(new DateTime(2023, 11, 17, 21, 27, 10, DateTimeKind.Utc)); + externalPaymentPhase.StartingBillingPeriodIndex.Should().Be(1); + externalPaymentPhase.EndingBillingPeriodIndex.Should().Be(2); + externalPaymentPhase.OfferType.Should().Be("FREE_TRIAL"); + externalPaymentPhase.OfferName.Should().Be("introductory"); + externalPaymentPhase.PeriodCount.Should().Be(2); + externalPaymentPhase.PeriodLength.Should().Be("TWO WEEKS"); + externalPaymentPhase.Amount.Should().Be(new decimal(1.99)); + externalPaymentPhase.Currency.Should().Be("USD"); + externalPaymentPhase.CreatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc)); + externalPaymentPhase.UpdatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc)); + } + } +} diff --git a/Test/Fixtures/FixtureImporter.cs b/Test/Fixtures/FixtureImporter.cs index bb4a64ae..b01b30db 100644 --- a/Test/Fixtures/FixtureImporter.cs +++ b/Test/Fixtures/FixtureImporter.cs @@ -8,6 +8,15 @@ namespace Recurly.Test.Fixtures { public class FixtureImporter { + public static string GetFixtureTypeDescription(FixtureType type) + { + DescriptionAttribute[] attributes = (DescriptionAttribute[])type + .GetType() + .GetField(type.ToString()) + .GetCustomAttributes(typeof(DescriptionAttribute), false); + + return attributes.Length > 0 ? attributes[0].Description : string.Empty; + } public static FixtureResponse Get(FixtureType type, string name) { if (name.IsNullOrEmpty()) @@ -16,7 +25,7 @@ public static FixtureResponse Get(FixtureType type, string name) if (!Enum.IsDefined(typeof(FixtureType), type)) throw new InvalidEnumArgumentException("type", (int)type, typeof(FixtureType)); - var fixturePath = string.Format("fixtures/{0}/{1}.xml", type, name); + var fixturePath = string.Format("Fixtures/{0}/{1}.xml", GetFixtureTypeDescription(type), name); if (!File.Exists(fixturePath)) throw new FileNotFoundException("Could not locate the fixture response file!", fixturePath); @@ -66,6 +75,10 @@ private static KeyValuePair ParseHeader(string line) public enum FixtureType { - Accounts + Accounts, + [Description("external_payment_phases")] + ExternalPaymentPhases, + [Description("external_invoices")] + ExternalInvoices, } } diff --git a/Test/Fixtures/external_invoices/index-200.xml b/Test/Fixtures/external_invoices/index-200.xml new file mode 100644 index 00000000..36376e10 --- /dev/null +++ b/Test/Fixtures/external_invoices/index-200.xml @@ -0,0 +1,37 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 + + + + + + + + 2000000458276005 + paid + USD + 10.45 + + + + + + twqswp9p3krc + random-ass-test-2 + apple_app_store + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + + the best banana in the world + 10.45 + USD + 1 + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + + + 2023-11-14T14:15:22Z + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + + \ No newline at end of file diff --git a/Test/Fixtures/external_invoices/show-200.xml b/Test/Fixtures/external_invoices/show-200.xml new file mode 100644 index 00000000..efb3a11f --- /dev/null +++ b/Test/Fixtures/external_invoices/show-200.xml @@ -0,0 +1,35 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 + + + + + + + 2000000458276005 + paid + USD + 10.45 + + + + + + twqswp9p3krc + random-ass-test-2 + apple_app_store + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + + the best banana in the world + 10.45 + USD + 1 + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + + + 2023-11-14T14:15:22Z + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + \ No newline at end of file diff --git a/Test/Fixtures/external_payment_phases/index-200.xml b/Test/Fixtures/external_payment_phases/index-200.xml new file mode 100644 index 00000000..e523bb94 --- /dev/null +++ b/Test/Fixtures/external_payment_phases/index-200.xml @@ -0,0 +1,21 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 + + + + + twqswp627ri3 + 2023-11-15T00:00:00Z + 2023-11-17T21:27:10Z + 1 + 2 + FREE_TRIAL + introductory + 2 + TWO WEEKS + 1.99 + USD + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + + \ No newline at end of file diff --git a/Test/Fixtures/external_payment_phases/show-200.xml b/Test/Fixtures/external_payment_phases/show-200.xml new file mode 100644 index 00000000..281a08b0 --- /dev/null +++ b/Test/Fixtures/external_payment_phases/show-200.xml @@ -0,0 +1,19 @@ +HTTP/1.1 200 OK +Content-Type: application/xml; charset=utf-8 + + + + twqswp627ri3 + 2023-11-15T00:00:00Z + 2023-11-17T21:27:10Z + 1 + 2 + FREE_TRIAL + introductory + 2 + TWO WEEKS + 1.99 + USD + 2023-11-15T16:16:43Z + 2023-11-15T16:16:43Z + \ No newline at end of file diff --git a/Test/List/ExternalInvoiceListTest.cs b/Test/List/ExternalInvoiceListTest.cs index 18abb8e2..787893cf 100644 --- a/Test/List/ExternalInvoiceListTest.cs +++ b/Test/List/ExternalInvoiceListTest.cs @@ -1,8 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; +using System.Xml; using FluentAssertions; -using Xunit; +using Recurly.Test.Fixtures; namespace Recurly.Test { @@ -11,8 +10,21 @@ public class ExternalInvoiceListTest : BaseTest [RecurlyFact(TestEnvironment.Type.Integration)] public void ListExternalInvoices() { - var externalInvoices = ExternalInvoices.List(); - externalInvoices.Should().NotBeEmpty(); + var xmlFixture = FixtureImporter.Get(FixtureType.ExternalInvoices, "index-200").Xml; + var externalInvoices = new ExternalInvoiceList(); + XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture)); + externalInvoices.ReadXml(reader); + externalInvoices.Should().HaveCount(1); + var externalInvoice = externalInvoices[0]; + externalInvoice.ExternalId.Should().Be("2000000458276005"); + externalInvoice.State.Should().Be(ExternalInvoice.ExternalInvoiceState.Paid); + externalInvoice.Total.Should().Be(new decimal(10.45)); + externalInvoice.Currency.Should().Be("USD"); + externalInvoice.ExternalSubscriptionUuid.Should().Be("tsfnx2vn5wh6"); + externalInvoice.ExternalPaymentPhaseUuid.Should().Be("twqswp627ri3"); + externalInvoice.PurchasedAt.Should().NotBe(default(DateTime)); + externalInvoice.CreatedAt.Should().NotBe(default(DateTime)); + externalInvoice.UpdatedAt.Should().NotBe(default(DateTime)); } } } diff --git a/Test/List/ExternalPaymentPhaseListTest.cs b/Test/List/ExternalPaymentPhaseListTest.cs new file mode 100644 index 00000000..28f7bdab --- /dev/null +++ b/Test/List/ExternalPaymentPhaseListTest.cs @@ -0,0 +1,37 @@ +using System; + +using System.Xml; + +using FluentAssertions; + +using Recurly.Test.Fixtures; + +namespace Recurly.Test +{ + public class ExternalPaymentPhaseListTest : BaseTest + { + [RecurlyFact(TestEnvironment.Type.Integration)] + public void ListExternalPaymentPhase() + { + var xmlFixture = FixtureImporter.Get(FixtureType.ExternalPaymentPhases, "index-200").Xml; + var externalPaymentPhases = new ExternalPaymentPhaseList(); + XmlTextReader reader = new XmlTextReader(new System.IO.StringReader(xmlFixture)); + externalPaymentPhases.ReadXml(reader); + externalPaymentPhases.Should().HaveCount(1); + var externalPaymentPhase = externalPaymentPhases[0]; + externalPaymentPhase.StartedAt.Should().Be(new DateTime(2023, 11, 15, 0, 0, 0, DateTimeKind.Utc)); + externalPaymentPhase.EndsAt.Should().Be(new DateTime(2023, 11, 17, 21, 27, 10, DateTimeKind.Utc)); + externalPaymentPhase.StartingBillingPeriodIndex.Should().Be(1); + externalPaymentPhase.EndingBillingPeriodIndex.Should().Be(2); + externalPaymentPhase.OfferType.Should().Be("FREE_TRIAL"); + externalPaymentPhase.OfferName.Should().Be("introductory"); + externalPaymentPhase.PeriodCount.Should().Be(2); + externalPaymentPhase.PeriodLength.Should().Be("TWO WEEKS"); + externalPaymentPhase.Amount.Should().Be(new decimal(1.99)); + externalPaymentPhase.Currency.Should().Be("USD"); + externalPaymentPhase.CreatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc)); + externalPaymentPhase.UpdatedAt.Should().Be(new DateTime(2023, 11, 15, 16, 16, 43, DateTimeKind.Utc)); + + } + } +} diff --git a/Test/Recurly.Test.csproj b/Test/Recurly.Test.csproj index 63f953e5..4fbb7ec8 100644 --- a/Test/Recurly.Test.csproj +++ b/Test/Recurly.Test.csproj @@ -206,6 +206,18 @@ Always + + Always + + + Always + + + Always + + + Always +