Skip to content
This repository was archived by the owner on Feb 19, 2019. It is now read-only.
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ GoCardlessSdk.Tests/test-results
GoCardlessSdk.userprefs

*.nupkg
*.ncrunch*
22 changes: 21 additions & 1 deletion GoCardlessSdk.Tests/Api/GetMerchantTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.IO;
using GoCardlessSdk.Api;
using GoCardlessSdk.Helpers;
using Newtonsoft.Json;
using NUnit.Framework;

namespace GoCardlessSdk.Tests.Api
Expand Down Expand Up @@ -34,6 +37,23 @@ public void CanFetchAndDeserializeCorrectly()
DeepAssertHelper.AssertDeepEquality(expected, new ApiClient("asdf").GetMerchant("WOQRUJU9OH2HH1"));
}


private MerchantResponse ParseMerchantJson() {
const string JSON_DATA = "{ \"id\": \"MERCHANT01\", \"name\": \"Test Merchant\", \"description\": \"Test merchant for reproducing serialization bug in GoCardless SDK\", \"created_at\": \"2014-12-19T12:46:36Z\", \"first_name\": \"Test\", \"last_name\": \"Merchant\", \"email\": \"help@gocardless.com\", \"uri\": \"https://gocardless.com/api/v1/merchants/MERCHANT01\", \"balance\": \"0.0\", \"pending_balance\": \"0.0\", \"next_payout_date\": null, \"next_payout_amount\": null, \"hide_variable_amount\": false, \"sub_resource_uris\": { \"users\": \"https://gocardless.com/api/v1/merchants/MERCHANT01/users\", \"bills\": \"https://gocardless.com/api/v1/merchants/MERCHANT01/bills\", \"pre_authorizations\": \"https://gocardless.com/api/v1/merchants/MERCHANT01/pre_authorizations\", \"subscriptions\": \"https://gocardless.com/api/v1/merchants/MERCHANT01/subscriptions\", \"payouts\": \"https://gocardless.com/api/v1/merchants/MERCHANT01/payouts\" } }";
var serializer = new JsonSerializer { ContractResolver = new UnderscoreToCamelCasePropertyResolver() };
var reader = new JsonTextReader(new StringReader(JSON_DATA));
return(serializer.Deserialize<MerchantResponse>(reader));
}

[Test]
public void CanDeserializeMerchantResponseWithNullPayoutAmount() {
var response = ParseMerchantJson();
Assert.IsNull(response.NextPayoutAmount);
}

[Test]
public void CanDeserializeMerchantResponseWithNullPayoutDate() {
var response = ParseMerchantJson();
Assert.IsNull(response.NextPayoutDate);
}
}
}
199 changes: 107 additions & 92 deletions GoCardlessSdk/Api/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,170 +9,185 @@
using RestSharp.Serializers;
using System.Globalization;

namespace GoCardlessSdk.Api
{
public class ApiClient
{
namespace GoCardlessSdk.Api {
public class HttpRequestEventArgs : EventArgs {
public Uri RequestUri { get; set; }
public string Method { get; set; }
}
public class ApiClient {
public event EventHandler<HttpRequestEventArgs> HttpRequestSending;

public static readonly string ApiPath = "/api/v1";
public static string ApiUrl
{
public static string ApiUrl {
get { return GoCardless.BaseUrl + ApiPath; }
}

private readonly string _accessToken;

public ApiClient(string accessToken)
{
public ApiClient(string accessToken) {
_accessToken = accessToken;
}

public MerchantResponse GetMerchant(string id)
{
public MerchantResponse GetMerchant(string id) {
var restRequest = GetRestRequest("merchants/" + id, Method.GET);
var merchant = Execute<MerchantResponse>(restRequest);
merchant.ApiClient = this;
return merchant;
}

public BillResponse GetBill(string id)
{
public BillResponse GetBill(string id) {
var restRequest = GetRestRequest("bills/" + id, Method.GET);
return Execute<BillResponse>(restRequest);
}

public PayoutResponse GetPayout(string id)
{
public PayoutResponse GetPayout(string id) {
var restRequest = GetRestRequest("payouts/" + id, Method.GET);
return Execute<PayoutResponse>(restRequest);
}

public IEnumerable<BillResponse> GetMerchantBills(string merchantId, string sourceId = null, string subscriptionId = null, string preAuthorizationId = null, string userId = null, DateTimeOffset? before = null, DateTimeOffset? after = null, bool? paid = null)
{
var options = new { source_id = sourceId, subscription_id = subscriptionId, pre_authorization_id = preAuthorizationId, user_id = userId, before, after, paid };
var restRequest = GetRestRequest("merchants/" + merchantId + "/bills", Method.GET, options);
return Execute<List<BillResponse>>(restRequest).AsReadOnly();
const int per_page = 100;

public IEnumerable<BillResponse> GetMerchantBills(string merchantId, string sourceId = null, string subscriptionId = null, string preAuthorizationId = null, string userId = null, DateTimeOffset? before = null, DateTimeOffset? after = null, bool? paid = null) {
var page = 0;
while (true) {
var options = new { source_id = sourceId, subscription_id = subscriptionId, pre_authorization_id = preAuthorizationId, user_id = userId, before, after, paid, per_page, page };
var restRequest = GetRestRequest("merchants/" + merchantId + "/bills", Method.GET, options);
var list = Execute<List<BillResponse>>(restRequest).AsReadOnly();
if (!list.Any()) yield break;
foreach (var item in list) yield return (item);
page++;
}

}

public IEnumerable<PreAuthorizationResponse> GetMerchantPreAuthorizations(string merchantId, string userId = null, DateTimeOffset? before = null, DateTimeOffset? after = null)
{
var options = new {user_id = userId, before, after};
var restRequest = GetRestRequest("merchants/" + merchantId + "/pre_authorizations", Method.GET, options);
return Execute<List<PreAuthorizationResponse>>(restRequest).AsReadOnly();

public IEnumerable<PreAuthorizationResponse> GetMerchantPreAuthorizations(string merchantId, string userId = null, DateTimeOffset? before = null, DateTimeOffset? after = null) {
var page = 0;
while (true) {
var options = new { user_id = userId, before, after, per_page, page };
var restRequest = GetRestRequest("merchants/" + merchantId + "/pre_authorizations", Method.GET, options);
var list = Execute<List<PreAuthorizationResponse>>(restRequest).AsReadOnly();
if (!list.Any()) yield break;
foreach (var item in list) yield return (item);
page++;
}
}
public IEnumerable<SubscriptionResponse> GetMerchantSubscriptions(string merchantId, string userId = null, DateTimeOffset? before = null, DateTimeOffset? after = null)
{
var options = new { user_id = userId, before, after };
var restRequest = GetRestRequest("merchants/" + merchantId + "/subscriptions", Method.GET, options);
return Execute<List<SubscriptionResponse>>(restRequest).AsReadOnly();
public IEnumerable<SubscriptionResponse> GetMerchantSubscriptions(string merchantId, string userId = null, DateTimeOffset? before = null, DateTimeOffset? after = null) {
var page = 0;
while (true) {
var options = new { user_id = userId, before, after, per_page, page };
var restRequest = GetRestRequest("merchants/" + merchantId + "/subscriptions", Method.GET, options);
var list = Execute<List<SubscriptionResponse>>(restRequest).AsReadOnly();
if (!list.Any()) yield break;
foreach (var item in list) yield return item;
page++;
}
}
public IEnumerable<UserResponse> GetMerchantUsers(string merchantId)
{
var restRequest = GetRestRequest("merchants/" + merchantId + "/users", Method.GET);
return Execute<List<UserResponse>>(restRequest).AsReadOnly();

public IEnumerable<UserResponse> GetMerchantUsers(string merchantId) {
var page = 0;
while (true) {
var options = new { per_page, page };
var restRequest = GetRestRequest("merchants/" + merchantId + "/users", Method.GET, options);
var list = Execute<List<UserResponse>>(restRequest).AsReadOnly();
if (!list.Any()) yield break;
foreach (var item in list) yield return (item);
page++;
}
}

public IEnumerable<PayoutResponse> GetMerchantPayouts(string merchantId)
{
var restRequest = GetRestRequest("merchants/" + merchantId + "/payouts", Method.GET);
return Execute<List<PayoutResponse>>(restRequest).AsReadOnly();
public IEnumerable<PayoutResponse> GetMerchantPayouts(string merchantId) {
var page = 0;
while (true) {
var options = new { per_page, page };
var restRequest = GetRestRequest("merchants/" + merchantId + "/payouts", Method.GET, options);
var list = Execute<List<PayoutResponse>>(restRequest).AsReadOnly();
if (!list.Any()) yield break;
foreach (var item in list) yield return item;
page++;
}
}

public SubscriptionResponse GetSubscription(string id)
{
public SubscriptionResponse GetSubscription(string id) {
var restRequest = GetRestRequest("subscriptions/" + id, Method.GET);
return Execute<SubscriptionResponse>(restRequest);
}
public SubscriptionResponse CancelSubscription(string id)
{
public SubscriptionResponse CancelSubscription(string id) {
var restRequest = GetRestRequest("subscriptions/" + id + "/cancel", Method.PUT);
return Execute<SubscriptionResponse>(restRequest);
}
public PreAuthorizationResponse GetPreAuthorization(string id)
{
public PreAuthorizationResponse GetPreAuthorization(string id) {
var restRequest = GetRestRequest("pre_authorizations/" + id, Method.GET);
return Execute<PreAuthorizationResponse>(restRequest);
}
public PreAuthorizationResponse CancelPreAuthorization(string id)
{
public PreAuthorizationResponse CancelPreAuthorization(string id) {
var restRequest = GetRestRequest("pre_authorizations/" + id + "/cancel", Method.PUT);
return Execute<PreAuthorizationResponse>(restRequest);
}

public BillResponse RetryBill(string id)
{
public BillResponse RetryBill(string id) {
var restRequest = GetRestRequest(string.Format("bills/{0}/retry", id), Method.POST);
return Execute<BillResponse>(restRequest, HttpStatusCode.Created);
}

private static RestRequest GetRestRequest(string resource, Method method, object options = null)
{
var request = new RestRequest(resource, method)
{
RequestFormat = DataFormat.Json,
JsonSerializer = new NewtonsoftJsonSerializer()
};
if (options != null)
{
foreach (var arg in ToHash(options).Where(arg => arg.Value != null))
{
private static RestRequest GetRestRequest(string resource, Method method, object options = null) {
var request = new RestRequest(resource, method) {
RequestFormat = DataFormat.Json,
JsonSerializer = new NewtonsoftJsonSerializer()
};
if (options != null) {
foreach (var arg in ToHash(options).Where(arg => arg.Value != null)) {
var value = arg.Value is DateTimeOffset ? ((DateTimeOffset)arg.Value).ToString("r") : arg.Value.ToString();
request.AddParameter(arg.Key, value, ParameterType.GetOrPost);
}
}
return request;
}

private static Dictionary<string, object> ToHash(object obj)
{
private static Dictionary<string, object> ToHash(object obj) {
return obj.GetType().GetProperties().ToDictionary(p => p.Name, p => p.GetValue(obj, null));
}

public T Execute<T>(RestRequest request, HttpStatusCode expected = HttpStatusCode.OK) where T : new()
{
var client = new RestClient
{
BaseUrl = ApiUrl,
UserAgent = GoCardless.UserAgent
};
var serializer = new Newtonsoft.Json.JsonSerializer
{
public virtual T Execute<T>(RestRequest request, HttpStatusCode expected = HttpStatusCode.OK) where T : new() {
var client = new RestClient {
BaseUrl = ApiUrl,
UserAgent = GoCardless.UserAgent
};
var serializer = new Newtonsoft.Json.JsonSerializer {
ContractResolver = new UnderscoreToCamelCasePropertyResolver(),
};
client.AddHandler("application/json", new NewtonsoftJsonDeserializer(serializer));
request.AddHeader("Authorization", "bearer " + _accessToken); // used on every request
var handle = HttpRequestSending;
if (handle != null) {
var args = new HttpRequestEventArgs() { Method = request.Method.ToString(), RequestUri = client.BuildUri(request) };
handle(this, args);
}
var response = client.Execute<T>(request);
if (response.StatusCode != expected)
{
var ex = new ApiException("Expected response " + (int) expected + " " + expected + " but received " +
(int) response.StatusCode + " " + response.StatusCode +
if (response.StatusCode != expected) {
var ex = new ApiException("Expected response " + (int)expected + " " + expected + " but received " +
(int)response.StatusCode + " " + response.StatusCode +
". See Content for more details");
try
{
try {
ex.Content = JObject.Parse(response.Content);
}
catch (Exception)
{
} catch (Exception) {
}
ex.RawContent = response.Content;
throw ex;
}
return response.Data;
}

public BillResponse PostBill(decimal amount, string preAuthorizationId, string name = null, string description = null, DateTime? chargeCustomerAt=null)
{

public BillResponse PostBill(decimal amount, string preAuthorizationId, string name = null, string description = null, DateTime? chargeCustomerAt = null) {

var restRequest = GetRestRequest("bills", Method.POST);
restRequest.AddBody(new
{
bill = new
{
amount = amount.ToString(CultureInfo.InvariantCulture),
pre_authorization_id = preAuthorizationId,
charge_customer_at = (chargeCustomerAt.HasValue) ? string.Format("{0:yyyy-MM-dd}", chargeCustomerAt) : null,
name,
description
}
restRequest.AddBody(new {
bill = new {
amount = amount.ToString(CultureInfo.InvariantCulture),
pre_authorization_id = preAuthorizationId,
charge_customer_at = (chargeCustomerAt.HasValue) ? string.Format("{0:yyyy-MM-dd}", chargeCustomerAt) : null,
name,
description
}
});
return Execute<BillResponse>(restRequest, HttpStatusCode.Created);
}
Expand Down
1 change: 1 addition & 0 deletions GoCardlessSdk/Api/BillResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class BillResponse
public string SourceType { get; set; } // "subscription",
public string SourceId { get; set; } // "YH1VEVQHYVB1UT",
public string Uri { get; set; } // "https://gocardless.com/api/v1/bills/VZUG2SC3PRT5EM"
public string PayoutId { get; set; }

public DateTimeOffset? ChargeCustomerAt { get; set; }
}
Expand Down
4 changes: 2 additions & 2 deletions GoCardlessSdk/Api/MerchantResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public MerchantResponse()
public string Uri { get; set; }
public decimal Balance { get; set; }
public decimal PendingBalance { get; set; }
public DateTimeOffset NextPayoutDate { get; set; }
public decimal NextPayoutAmount { get; set; }
public DateTimeOffset? NextPayoutDate { get; set; }
public decimal? NextPayoutAmount { get; set; }
public bool HideVariableAmount { get; set; }
public SubResourceUrisResponse SubResourceUris { get; set; }

Expand Down