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