diff --git a/ShopifySharp.Tests/Playlists/Charges.playlist b/ShopifySharp.Tests/Playlists/Charges.playlist index fff5b925..c9bf1796 100644 --- a/ShopifySharp.Tests/Playlists/Charges.playlist +++ b/ShopifySharp.Tests/Playlists/Charges.playlist @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/ShopifySharp.Tests/Playlists/RecurringCharges.playlist b/ShopifySharp.Tests/Playlists/RecurringCharges.playlist new file mode 100644 index 00000000..783ea866 --- /dev/null +++ b/ShopifySharp.Tests/Playlists/RecurringCharges.playlist @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ShopifySharp.Tests/ShopifyChargeService Tests/When_activating_a_charge.cs b/ShopifySharp.Tests/ShopifyChargeService Tests/When_activating_a_charge.cs new file mode 100644 index 00000000..bf2fd80f --- /dev/null +++ b/ShopifySharp.Tests/ShopifyChargeService Tests/When_activating_a_charge.cs @@ -0,0 +1,50 @@ +using Machine.Specifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp.Tests.ShopifyChargeService_Tests +{ + [Subject(typeof(ShopifyChargeService))] + class When_activating_a_charge + { + Establish context = () => + { + // NOTE: Creating a charge will fail if the access token used is for a private app. + // Only real apps can use the Shopify billing API. + + Service = new ShopifyChargeService(Utils.BillingMyShopifyUrl, Utils.BillingAccessToken); + Charge = Service.CreateAsync(new ShopifyCharge() + { + Name = "Lorem Ipsum Single Charge", + Price = 123.45, + Test = true, + ReturnUrl = "http://localhost:5445/shopify/chargeresult/?attemptedChargeId=" + Guid.NewGuid().ToString() + }).Await().AsTask.Result; + }; + + Because of = () => + { + Service.ActivateAsync(Charge.Id.Value).Await(); + Charge = Service.GetAsync(Charge.Id.Value).Await().AsTask.Result; + }; + + It should_activate_a_charge = () => + { + // NOTE: This test will require you to set a break point after creating the charge but before activating it, + // grab the confirmation url and manually accept it, then continue the test. + Charge.Status.ShouldEqual(Enums.ShopifyChargeStatus.Active); + }; + + Cleanup after = () => + { + //Charges cannot be deleted. + }; + + static ShopifyChargeService Service; + + static ShopifyCharge Charge; + } +} diff --git a/ShopifySharp.Tests/ShopifyChargeService Tests/When_creating_a_charge.cs b/ShopifySharp.Tests/ShopifyChargeService Tests/When_creating_a_charge.cs new file mode 100644 index 00000000..8a524cbc --- /dev/null +++ b/ShopifySharp.Tests/ShopifyChargeService Tests/When_creating_a_charge.cs @@ -0,0 +1,48 @@ +using Machine.Specifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp.Tests.ShopifyChargeService_Tests +{ + [Subject(typeof(ShopifyChargeService))] + class When_creating_a_charge + { + Establish context = () => + { + Service = new ShopifyChargeService(Utils.BillingMyShopifyUrl, Utils.BillingAccessToken); + Charge = new ShopifyCharge() + { + Name = "Lorem Ipsum Single Charge", + Price = 123.45, + Test = true + }; + }; + + Because of = () => + { + // NOTE: Creating a charge will fail if the access token used is for a private app. + // Only real apps can use the Shopify billing API. + + Charge = Service.CreateAsync(Charge).Await().AsTask.Result; + }; + + It should_create_a_charge = () => + { + Charge.ConfirmationUrl.ShouldNotBeNull(); + Charge.Price.ShouldEqual(123.45); + Charge.Test.ShouldBeTrue(); + }; + + Cleanup after = () => + { + //Charges cannot be deleted. + }; + + static ShopifyChargeService Service; + + static ShopifyCharge Charge; + } +} diff --git a/ShopifySharp.Tests/ShopifyChargeService Tests/When_listing_charges.cs b/ShopifySharp.Tests/ShopifyChargeService Tests/When_listing_charges.cs new file mode 100644 index 00000000..c3d37da4 --- /dev/null +++ b/ShopifySharp.Tests/ShopifyChargeService Tests/When_listing_charges.cs @@ -0,0 +1,49 @@ +using Machine.Specifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp.Tests.ShopifyChargeService_Tests +{ + [Subject(typeof(ShopifyChargeService))] + class When_listing_charges + { + Establish context = () => + { + // NOTE: Creating a charge will fail if the access token used is for a private app. + // Only real apps can use the Shopify billing API. + + Service = new ShopifyChargeService(Utils.BillingMyShopifyUrl, Utils.BillingAccessToken); + ChargeId = Service.CreateAsync(new ShopifyCharge() + { + Name = "Lorem Ipsum Single Charge", + Price = 123.45, + Test = true, + }).Await().AsTask.Result.Id.Value; + }; + + Because of = () => + { + Charges = Service.ListAsync().Await().AsTask.Result; + }; + + It should_retrieve_a_list_of_charges = () => + { + Charges.ShouldNotBeNull(); + Charges.Count().ShouldBeGreaterThanOrEqualTo(1); + }; + + Cleanup after = () => + { + //Charges cannot be deleted. + }; + + static ShopifyChargeService Service; + + static IEnumerable Charges; + + static long ChargeId; + } +} diff --git a/ShopifySharp.Tests/ShopifyChargeService Tests/When_retrieving_a_charge.cs b/ShopifySharp.Tests/ShopifyChargeService Tests/When_retrieving_a_charge.cs new file mode 100644 index 00000000..3c8feb18 --- /dev/null +++ b/ShopifySharp.Tests/ShopifyChargeService Tests/When_retrieving_a_charge.cs @@ -0,0 +1,48 @@ +using Machine.Specifications; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp.Tests.ShopifyChargeService_Tests +{ + [Subject(typeof(ShopifyChargeService))] + class When_retrieving_a_charge + { + Establish context = () => + { + // NOTE: Creating a charge will fail if the access token used is for a private app. + // Only real apps can use the Shopify billing API. + + Service = new ShopifyChargeService(Utils.BillingMyShopifyUrl, Utils.BillingAccessToken); + ChargeId = Service.CreateAsync(new ShopifyCharge() + { + Name = "Lorem Ipsum Single Charge", + Price = 123.45, + Test = true, + }).Await().AsTask.Result.Id.Value; + }; + + Because of = () => + { + Charge = Service.GetAsync(ChargeId).Await().AsTask.Result; + }; + + It should_retrieve_a_charge = () => + { + Charge.ShouldNotBeNull(); + }; + + Cleanup after = () => + { + //Charges cannot be deleted. + }; + + static ShopifyChargeService Service; + + static ShopifyCharge Charge; + + static long ChargeId; + } +} diff --git a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_activating_a_charge.cs b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_activating_a_recurring_charge.cs similarity index 88% rename from ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_activating_a_charge.cs rename to ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_activating_a_recurring_charge.cs index 6e5ab791..80b0eeae 100644 --- a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_activating_a_charge.cs +++ b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_activating_a_recurring_charge.cs @@ -8,7 +8,7 @@ namespace ShopifySharp.Tests.ShopifyRecurringChargeService_Tests { [Subject(typeof(ShopifyRecurringChargeService))] - class When_activating_a_charge + class When_activating_a_recurring_charge { Establish context = () => { @@ -22,6 +22,7 @@ class When_activating_a_charge Price = 123.45, Test = true, TrialDays = 21, + ReturnUrl = "http://localhost:5445/shopify/chargeresult/?attemptedChargeId=" + Guid.NewGuid().ToString() }).Await().AsTask.Result; }; @@ -31,7 +32,7 @@ class When_activating_a_charge Charge = Service.GetAsync(Charge.Id.Value).Await().AsTask.Result; }; - It should_activate_a_charge = () => + It should_activate_a_recurring_charge = () => { // NOTE: This test will require you to set a break point after creating the charge but before activating it, // grab the confirmation url and manually accept it, then continue the test. diff --git a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_creating_a_charge.cs b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_creating_a_recurring_charge.cs similarity index 93% rename from ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_creating_a_charge.cs rename to ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_creating_a_recurring_charge.cs index d6d19f91..78509683 100644 --- a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_creating_a_charge.cs +++ b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_creating_a_recurring_charge.cs @@ -8,7 +8,7 @@ namespace ShopifySharp.Tests.ShopifyRecurringChargeService_Tests { [Subject(typeof(ShopifyRecurringChargeService))] - class When_creating_a_charge + class When_creating_a_recurring_charge { Establish context = () => { @@ -30,7 +30,7 @@ class When_creating_a_charge Charge = Service.CreateAsync(Charge).Await().AsTask.Result; }; - It should_create_a_charge = () => + It should_create_a_recurring_charge = () => { Charge.ConfirmationUrl.ShouldNotBeNull(); Charge.Price.ShouldEqual(123.45); diff --git a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_deleting_a_charge.cs b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_deleting_a_recurring_charge.cs similarity index 94% rename from ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_deleting_a_charge.cs rename to ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_deleting_a_recurring_charge.cs index 98ed8169..4c6699f5 100644 --- a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_deleting_a_charge.cs +++ b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_deleting_a_recurring_charge.cs @@ -8,7 +8,7 @@ namespace ShopifySharp.Tests.ShopifyRecurringChargeService_Tests { [Subject(typeof(ShopifyRecurringChargeService))] - class When_deleting_a_charge + class When_deleting_a_recurring_charge { Establish context = () => { @@ -37,7 +37,7 @@ class When_deleting_a_charge } }; - It should_delete_a_charge = () => + It should_delete_a_recurring_charge = () => { // A charge cannot be deleted unless it has been activated. This test will fail unless you manually // accept the charge, then activate it, then let the test delete it. diff --git a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_listing_charges.cs b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_listing_recurring_charges.cs similarity index 93% rename from ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_listing_charges.cs rename to ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_listing_recurring_charges.cs index b94ec99f..aa6a0b1f 100644 --- a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_listing_charges.cs +++ b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_listing_recurring_charges.cs @@ -8,7 +8,7 @@ namespace ShopifySharp.Tests.ShopifyRecurringChargeService_Tests { [Subject(typeof(ShopifyRecurringChargeService))] - class When_listing_charges + class When_listing_recurring_charges { Establish context = () => { @@ -29,7 +29,7 @@ class When_listing_charges Charges = Service.ListAsync().Await().AsTask.Result; }; - It should_retrieve_a_list_of_charges = () => + It should_retrieve_a_list_of_recurring_charges = () => { Charges.ShouldNotBeNull(); Charges.Count().ShouldBeGreaterThanOrEqualTo(1); diff --git a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_retrieving_a_charge.cs b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_retrieving_a_recurring_charge.cs similarity index 92% rename from ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_retrieving_a_charge.cs rename to ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_retrieving_a_recurring_charge.cs index f796f8be..476569b5 100644 --- a/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_retrieving_a_charge.cs +++ b/ShopifySharp.Tests/ShopifyRecurringChargeService Tests/When_retrieving_a_recurring_charge.cs @@ -8,7 +8,7 @@ namespace ShopifySharp.Tests.ShopifyRecurringChargeService_Tests { [Subject(typeof(ShopifyRecurringChargeService))] - class When_retrieving_a_charge + class When_retrieving_a_recurring_charge { Establish context = () => { @@ -29,7 +29,7 @@ class When_retrieving_a_charge Charge = Service.GetAsync(ChargeId).Await().AsTask.Result; }; - It should_retrieve_a_charge = () => + It should_retrieve_a_recurring_charge = () => { Charge.ShouldNotBeNull(); }; diff --git a/ShopifySharp.Tests/ShopifySharp.Tests.csproj b/ShopifySharp.Tests/ShopifySharp.Tests.csproj index ad8ac987..95d306df 100644 --- a/ShopifySharp.Tests/ShopifySharp.Tests.csproj +++ b/ShopifySharp.Tests/ShopifySharp.Tests.csproj @@ -76,11 +76,15 @@ - - - - - + + + + + + + + + diff --git a/ShopifySharp/Converters/ShopifyChargeConverter.cs b/ShopifySharp/Converters/ShopifyChargeConverter.cs new file mode 100644 index 00000000..2920aff7 --- /dev/null +++ b/ShopifySharp/Converters/ShopifyChargeConverter.cs @@ -0,0 +1,44 @@ +using Newtonsoft.Json.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using ShopifySharp.Enums; + +namespace ShopifySharp.Converters +{ + /// + /// A custom enum converter for enums which sets the default value + /// to when the value is null or does not exist. + /// + public class ShopifyChargeConverter : StringEnumConverter + { + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + ShopifyChargeStatus parsed; + + if (!Enum.TryParse(reader.Value?.ToString() ?? "", true, out parsed)) + { + return ShopifyChargeStatus.Unknown; + } + + return parsed; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null || + !Enum.IsDefined(typeof(ShopifyChargeStatus), value) || + (Enum.IsDefined(typeof(ShopifyChargeStatus), value) && ((ShopifyChargeStatus)value) == ShopifyChargeStatus.Unknown)) + { + writer.WriteNull(); + } + else + { + base.WriteJson(writer, value, serializer); + } + } + } +} diff --git a/ShopifySharp/Converters/ShopifyRecurringChargeConverter.cs b/ShopifySharp/Converters/ShopifyRecurringChargeConverter.cs index 899f6bda..b4d531a3 100644 --- a/ShopifySharp/Converters/ShopifyRecurringChargeConverter.cs +++ b/ShopifySharp/Converters/ShopifyRecurringChargeConverter.cs @@ -9,6 +9,8 @@ namespace ShopifySharp.Converters { + // TODO: Merge this converter with ShopifyChargeConverter in v2.0 + /// /// A custom enum converter for enums which sets the default value /// to when the value is null or does not exist. @@ -17,10 +19,14 @@ public class ShopifyRecurringChargeConverter : StringEnumConverter { public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - if (string.IsNullOrEmpty(reader.Value?.ToString())) + ShopifyRecurringChargeStatus parsed; + + if (!Enum.TryParse(reader.Value?.ToString() ?? "", true, out parsed)) + { return ShopifyRecurringChargeStatus.Unknown; + } - return base.ReadJson(reader, objectType, existingValue, serializer); + return parsed; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) diff --git a/ShopifySharp/Entities/ShopifyCharge.cs b/ShopifySharp/Entities/ShopifyCharge.cs new file mode 100644 index 00000000..805bf598 --- /dev/null +++ b/ShopifySharp/Entities/ShopifyCharge.cs @@ -0,0 +1,67 @@ +using Newtonsoft.Json; +using ShopifySharp.Converters; +using ShopifySharp.Enums; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp +{ + /// + /// An object representing an application charge. + /// + public class ShopifyCharge : ShopifyObject + { + /// + /// The URL that the customer should be sent to, to accept or decline the application charge. + /// + [JsonProperty("confirmation_url")] + public string ConfirmationUrl { get; set; } + + /// + /// The date and time when the application charge was created. + /// + [JsonProperty("created_at")] + public DateTime CreatedAt { get; set; } + + /// + /// The name of the application charge. + /// + [JsonProperty("name")] + public string Name { get; set; } + + /// + /// The price of the application charge. + /// + /// Shopify returns this as a string, but JSON.net should be able to convert it to a double. + [JsonProperty("price")] + public double Price { get; set; } + + /// + /// The URL the customer is sent to once they accept/decline a charge. + /// + [JsonProperty("return_url")] + public string ReturnUrl { get; set; } + + /// + /// The status of the charged. + /// + [JsonProperty("status"), JsonConverter(typeof(ShopifyChargeConverter))] + public ShopifyChargeStatus Status { get; set; } = ShopifyChargeStatus.Pending; + + /// + /// States whether or not the application charge is a test transaction. + /// + /// Valid values are 'true' or null. Needs a special converter to convert null to false and vice-versa. + [JsonProperty("test"), JsonConverter(typeof(FalseToNullConverter))] + public bool Test { get; set; } + + /// + /// The date and time when the recurring application charge was last updated. + /// + [JsonProperty("updated_at")] + public DateTime UpdatedAt { get; set; } + } +} diff --git a/ShopifySharp/Enums/ShopifyChargeStatus.cs b/ShopifySharp/Enums/ShopifyChargeStatus.cs new file mode 100644 index 00000000..99404e12 --- /dev/null +++ b/ShopifySharp/Enums/ShopifyChargeStatus.cs @@ -0,0 +1,49 @@ +using Newtonsoft.Json; +using ShopifySharp.Converters; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp.Enums +{ + /// + /// An enum that indicates the status of a object. + /// + [JsonConverter(typeof(ShopifyChargeConverter))] + public enum ShopifyChargeStatus + { + /// + /// The charge is pending and has not been accepted or declined by the user. + /// + [EnumMember(Value = "pending")] + Pending, + + /// + /// The charge has been accepted by the user and can be activated. + /// + [EnumMember(Value = "accepted")] + Accepted, + + /// + /// The charge has been accepted and activated. + /// + [EnumMember(Value = "active")] + Active, + + /// + /// The charge has been declined by the user and cannot be activated. + /// + [EnumMember(Value = "declined")] + Declined, + + /// + /// The status of the charge is unknown. This is an invalid value. If possible, submit a + /// pull request to https://github.com/nozzlegear/shopifysharp with the correct value. + /// + [EnumMember(Value = "")] + Unknown + } +} diff --git a/ShopifySharp/Enums/ShopifyRecurringChargeStatus.cs b/ShopifySharp/Enums/ShopifyRecurringChargeStatus.cs index 7a4d9cc6..aa18c5e6 100644 --- a/ShopifySharp/Enums/ShopifyRecurringChargeStatus.cs +++ b/ShopifySharp/Enums/ShopifyRecurringChargeStatus.cs @@ -9,6 +9,8 @@ namespace ShopifySharp.Enums { + // TODO: Merge this enum with ShopifyChargeStatus in v2.0 + /// /// An enum that indicates the status of a object. /// @@ -33,12 +35,24 @@ public enum ShopifyRecurringChargeStatus [EnumMember(Value = "active")] Active, + /// + /// The charge has been cancelled. + /// + [EnumMember(Value ="cancelled")] + Cancelled, + /// /// The charge has been declined by the user and cannot be activated. /// [EnumMember(Value = "declined")] Declined, + /// + /// The charge has expired. + /// + [EnumMember(Value = "expired")] + Expired, + /// /// The status of the charge is unknown. This is an invalid value. If possible, submit a /// pull request to https://github.com/nozzlegear/shopifysharp with the correct value. diff --git a/ShopifySharp/Properties/AssemblyInfo.cs b/ShopifySharp/Properties/AssemblyInfo.cs index 56e6d100..d7c880a5 100644 --- a/ShopifySharp/Properties/AssemblyInfo.cs +++ b/ShopifySharp/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.0.0")] +[assembly: AssemblyVersion("1.6.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ShopifySharp/Services/Charge/ShopifyChargeService.cs b/ShopifySharp/Services/Charge/ShopifyChargeService.cs new file mode 100644 index 00000000..fa977c5f --- /dev/null +++ b/ShopifySharp/Services/Charge/ShopifyChargeService.cs @@ -0,0 +1,98 @@ +using Humanizer; +using RestSharp; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShopifySharp +{ + /// + /// A service for manipulating Shopify's ApplicationCharge API. + /// + public class ShopifyChargeService : ShopifyService + { + #region Constructor + + /// + /// Creates a new instance of . + /// + /// The shop's *.myshopify.com URL. + /// An API access token for the shop. + public ShopifyChargeService(string myShopifyUrl, string shopAccessToken) : base(myShopifyUrl, shopAccessToken) { } + + #endregion + + #region Public, non-static Charge methods + + /// + /// Creates a . + /// + /// The to create. + /// The new . + public async Task CreateAsync(ShopifyCharge charge) + { + IRestRequest req = RequestEngine.CreateRequest("application_charges.json", Method.POST, "application_charge"); + + req.AddJsonBody(new { application_charge = charge }); + + return await RequestEngine.ExecuteRequestAsync(_RestClient, req); + } + + /// + /// Retrieves the with the given id. + /// + /// The id of the charge to retrieve. + /// A comma-separated list of fields to return. + /// The . + public async Task GetAsync(long id, string fields = null) + { + IRestRequest req = RequestEngine.CreateRequest("application_charges/{0}.json".FormatWith(id), Method.GET, "application_charge"); + + if (string.IsNullOrEmpty(fields) == false) + { + req.AddParameter("fields", fields); + } + + return await RequestEngine.ExecuteRequestAsync(_RestClient, req); + } + + /// + /// Retrieves a list of all past and present objects. + /// + /// Restricts results to any charge after the given id. + /// A comma-separated list of fields to return. + /// The list of objects. + public async Task> ListAsync(long? sinceId = null, string fields = null) + { + IRestRequest req = RequestEngine.CreateRequest("application_charges.json", Method.GET, "application_charges"); + + if (string.IsNullOrEmpty(fields) == false) + { + req.AddParameter("fields", fields); + } + + if (sinceId.HasValue) + { + req.AddParameter("since_id", sinceId); + } + + return await RequestEngine.ExecuteRequestAsync>(_RestClient, req); + } + + /// + /// Activates a that the shop owner has accepted. + /// + /// The id of the charge to activate. + public async Task ActivateAsync(long id) + { + IRestRequest req = RequestEngine.CreateRequest("application_charges/{0}/activate.json".FormatWith(id), Method.POST); + + await RequestEngine.ExecuteRequestAsync(_RestClient, req); + } + + #endregion + } +} diff --git a/ShopifySharp/Services/RecurringCharge/ShopifyRecurringChargeService.cs b/ShopifySharp/Services/RecurringCharge/ShopifyRecurringChargeService.cs index 8479fce2..1c007c5f 100644 --- a/ShopifySharp/Services/RecurringCharge/ShopifyRecurringChargeService.cs +++ b/ShopifySharp/Services/RecurringCharge/ShopifyRecurringChargeService.cs @@ -25,7 +25,7 @@ public ShopifyRecurringChargeService(string myShopifyUrl, string shopAccessToken #endregion - #region Public, non-static RecurringCharge methods + #region Public, non-static methods /// /// Creates a . @@ -105,9 +105,5 @@ public async Task DeleteAsync(long id) } #endregion - - #region Public, non-static SingleCharge methods - - #endregion } } diff --git a/ShopifySharp/ShopifySharp.csproj b/ShopifySharp/ShopifySharp.csproj index 927b6a33..c8f95a82 100644 --- a/ShopifySharp/ShopifySharp.csproj +++ b/ShopifySharp/ShopifySharp.csproj @@ -57,8 +57,10 @@ + + @@ -76,6 +78,7 @@ + @@ -107,6 +110,7 @@ + diff --git a/ShopifySharp/ShopifySharp.nuspec b/ShopifySharp/ShopifySharp.nuspec index f08c6b91..a9e11a11 100644 --- a/ShopifySharp/ShopifySharp.nuspec +++ b/ShopifySharp/ShopifySharp.nuspec @@ -11,6 +11,12 @@ false ShopifySharp is a .NET library that enables you to authenticate and make API calls to Shopify. + 1.6.0 + ===== + - New feature: ShopifyChargeService. Create, retrieve, list and activate a one-time application charge. + - Added "expired" and "cancelled" to ShopifyRecurringChargeStatus enum. + - Bugfix: ShopifyRecurringChargeConverter did not properly convert unknown enums to ShopifyRecurringChargeStatus.Unknown. Caused exceptions when receiving charges with previously undocumented "expired" and "cancelled" statuses. + 1.5.0 ===== - Added a ShopifyRecurringChargeStatus enum to the ShopifyRecurringCharge object. diff --git a/readme.md b/readme.md index bc9b0100..ce833136 100644 --- a/readme.md +++ b/readme.md @@ -192,7 +192,7 @@ await service.ActivateAsync(chargeId); ### Deleting a charge -Charges cannot be deleted unless they've been activated. Shopify automatically deletes pending charges after 48 hours pass without activation. +Charges cannot be deleted unless they've been activated. Shopify automatically deletes pending charges after 48 hours pass without activation. ```c# var service = new ShopifyRecurringChargeService(myShopifyUrl, shopAccessToken); @@ -200,6 +200,50 @@ var service = new ShopifyRecurringChargeService(myShopifyUrl, shopAccessToken); await service.DeleteAsync(chargeId); ``` +## One-time application charges + +Just like with the above recurring charges, the Shopify billing API lets you create a one-time application charge on the shop owner's account. One-time charges cannot be deleted. + +### Create a one-time charge + +```c# +var service = new ShopifyChargeService(myShopifyUrl, shopAccessToken); +var charge = new ShopifyCharge() +{ + Name = "Lorem Ipsum Charge", + Price = 12.34, + Test = true, //Marks this charge as a test, meaning it won't charge the shop owner. +} + +charge = await service.CreateAsync(charge); +``` + +### Retrieve a one-time charge + +```c# +var service = new ShopifyChargeService(myShopifyUrl, shopAccessToken); + +var charge = await service.GetAsync(chargeId); +``` + +### Listing one-time charges + +```c# +var service = new ShopifyChargeService(myShopifyUrl, shopAccessToken); + +IEnumerable charges = await service.ListAsync(); +``` + +### Activating a charge + +Just like recurring charges, creating a one-time charge does not actually charge the shop owner. You need to send them to the charge's `ConfirmationUrl`, have them accept the charge, then activate it. + +```c# +var service = new ShopifyChargeService(myShopifyUrl, shopAccessToken); + +await service.ActivateAsync(chargeId); +``` + ## Shops ### Retrieving shop information