diff --git a/src/Stripe.net/Entities/_base/StripeEntity.cs b/src/Stripe.net/Entities/_base/StripeEntity.cs
index 4acfe4ed2d..ab80067180 100644
--- a/src/Stripe.net/Entities/_base/StripeEntity.cs
+++ b/src/Stripe.net/Entities/_base/StripeEntity.cs
@@ -26,7 +26,7 @@ public static IHasObject FromJson(string value)
/// The object to deserialize.
/// The deserialized Stripe object from the JSON string.
public static T FromJson(string value)
- where T : StripeEntity
+ where T : IStripeEntity
{
return JsonConvert.DeserializeObject(value, StripeConfiguration.SerializerSettings);
}
diff --git a/src/Stripe.net/Infrastructure/Client.cs b/src/Stripe.net/Infrastructure/Client.cs
deleted file mode 100644
index 635cb146f6..0000000000
--- a/src/Stripe.net/Infrastructure/Client.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-namespace Stripe.Infrastructure
-{
- using System;
- using System.Collections.Generic;
- using System.Net;
- using System.Net.Http;
- using System.Reflection;
- using Newtonsoft.Json;
-#if NET45
- using Microsoft.Win32;
-#else
- using System.Runtime.InteropServices;
-#endif
-
- internal class Client
- {
- public Client(HttpRequestMessage requestMessage)
- {
- this.RequestMessage = requestMessage;
- }
-
- private HttpRequestMessage RequestMessage { get; set; }
-
- public void ApplyUserAgent()
- {
- this.RequestMessage.Headers.UserAgent.ParseAdd($"Stripe/v1 .NetBindings/{StripeConfiguration.StripeNetVersion}");
- }
-
- public void ApplyClientData()
- {
- this.RequestMessage.Headers.Add("X-Stripe-Client-User-Agent", this.GetClientUserAgentString());
- }
-
-#if NET45
- private static string GetMonoVersion()
- {
- Type monoRuntimeType = typeof(object).Assembly.GetType("Mono.Runtime");
-
- if (monoRuntimeType != null)
- {
- MethodInfo getDisplayNameMethod = monoRuntimeType.GetMethod(
- "GetDisplayName",
- BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding,
- null,
- Type.EmptyTypes,
- null);
-
- if (getDisplayNameMethod != null)
- {
- return (string)getDisplayNameMethod.Invoke(null, null);
- }
- }
-
- return null;
- }
-#endif
-
- private string GetClientUserAgentString()
- {
- var values = new Dictionary
- {
- { "bindings_version", StripeConfiguration.StripeNetVersion },
- { "lang", ".net" },
- { "publisher", "stripe" },
- };
-
-#if NET45
- values.Add("lang_version", ".NET Framework 4.5+");
- values.Add("os_version", Environment.OSVersion.ToString());
-
- string monoVersion = Client.GetMonoVersion();
- if (monoVersion != null)
- {
- values.Add("mono_version", monoVersion);
- }
-#else
- values.Add("lang_version", RuntimeInformation.FrameworkDescription);
- values.Add("os_version", RuntimeInformation.OSDescription);
-#endif
-
- return JsonConvert.SerializeObject(values, Formatting.None);
- }
- }
-}
diff --git a/src/Stripe.net/Infrastructure/Extensions/ServiceExtensions.cs b/src/Stripe.net/Infrastructure/Extensions/ServiceExtensions.cs
index 559c320333..e8c64d8130 100644
--- a/src/Stripe.net/Infrastructure/Extensions/ServiceExtensions.cs
+++ b/src/Stripe.net/Infrastructure/Extensions/ServiceExtensions.cs
@@ -8,28 +8,6 @@ namespace Stripe.Infrastructure.Extensions
internal static class ServiceExtensions
{
- /// Creates the full URL for a request.
- /// Entity type returned by the service.
- /// The service sending the request.
- /// The request parameters.
- /// The base URL for the request.
- /// Whether the request is a list request or not.
- /// The full URL for the request.
- public static string ApplyAllParameters(this Service service, BaseOptions options, string baseUrl, bool isListMethod = false)
- where T : IStripeEntity
- {
- var expansions = service.Expansions(isListMethod);
- if (options != null)
- {
- expansions.AddRange(options.Expand);
- }
-
- return FormEncoder.AppendQueries(
- baseUrl,
- options?.ToQueryString(includeExtraParams: true, includeExpandParams: false),
- FormEncoder.EncodeList(expansions, "expand"));
- }
-
///
/// Returns the list of attributes to expand in requests sent by the service.
///
diff --git a/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs b/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs
index 2926741b6c..20f9cd205d 100644
--- a/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs
+++ b/src/Stripe.net/Infrastructure/FormEncoding/FormEncoder.cs
@@ -4,10 +4,13 @@ namespace Stripe.Infrastructure.FormEncoding
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
+ using System.IO;
using System.Linq;
using System.Net;
+ using System.Net.Http;
using System.Reflection;
using Newtonsoft.Json;
+ using Stripe.Infrastructure.Http;
///
/// This class provides methods to serialize various objects with
@@ -24,6 +27,60 @@ public static string EncodeOptions(BaseOptions options)
return EncodeValue(options, null);
}
+ public static HttpContent EncodeOptionsContent(BaseOptions options)
+ {
+ if (options is MultipartOptions multipartOptions)
+ {
+ return EncodeOptionsMultipart(multipartOptions);
+ }
+
+ return new FormUrlEncodedUTF8Content(options);
+ }
+
+ public static MultipartFormDataContent EncodeOptionsMultipart(MultipartOptions options)
+ {
+ var content = new MultipartFormDataContent();
+
+ foreach (var property in options.GetType().GetRuntimeProperties())
+ {
+ // Skip properties not annotated with `[JsonProperty]`
+ var attribute = property.GetCustomAttribute();
+ if (attribute == null)
+ {
+ continue;
+ }
+
+ var key = attribute.PropertyName;
+ var value = property.GetValue(options);
+
+ if (value is Stream stream)
+ {
+ string fileName = "blob";
+#if NET45 || NETSTANDARD2_0
+ FileStream fileStream = stream as FileStream;
+ if ((fileStream != null) && (!string.IsNullOrEmpty(fileStream.Name)))
+ {
+ fileName = fileStream.Name;
+ }
+#endif
+ var streamContent = new StreamContent(stream);
+
+ content.Add(streamContent, key, fileName);
+ }
+ else
+ {
+ var flatParams = FlattenParamsValue(value, key);
+
+ foreach (var flatParam in flatParams)
+ {
+ content.Add(new StringContent(flatParam.Value), flatParam.Key);
+ }
+ }
+ }
+
+ return content;
+ }
+
/// Creates the HTTP query string for a given dictionary.
/// The dictionary for which to create the query string.
/// The query string.
@@ -60,22 +117,6 @@ public static string JoinQueries(params string[] queries)
return string.Join("&", queries.Where(q => !string.IsNullOrEmpty(q)));
}
- /// Append one or more query strings to a URL.
- /// The base URL.
- /// One or more query strings to be appended to the URL.
- /// The full URL with all the query strings.
- public static string AppendQueries(string url, params string[] queries)
- {
- var fullQuery = JoinQueries(queries);
-
- if (!string.IsNullOrEmpty(fullQuery))
- {
- url += "?" + fullQuery;
- }
-
- return url;
- }
-
/// Creates the HTTP query string for a given value.
/// The value to encode.
///
diff --git a/src/Stripe.net/Infrastructure/Http/FormUrlEncodedUTF8Content.cs b/src/Stripe.net/Infrastructure/Http/FormUrlEncodedUTF8Content.cs
new file mode 100644
index 0000000000..1e9d92b1f5
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/Http/FormUrlEncodedUTF8Content.cs
@@ -0,0 +1,27 @@
+namespace Stripe.Infrastructure.Http
+{
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Text;
+ using Stripe.Infrastructure.Extensions;
+
+ public class FormUrlEncodedUTF8Content : ByteArrayContent
+ {
+ public FormUrlEncodedUTF8Content(BaseOptions options)
+ : base(GetContentByteArray(options))
+ {
+ this.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
+ this.Headers.ContentType.CharSet = "utf-8";
+ }
+
+ private static byte[] GetContentByteArray(BaseOptions options)
+ {
+ if (options == null)
+ {
+ return new byte[0];
+ }
+
+ return Encoding.UTF8.GetBytes(options.ToQueryString());
+ }
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/Http/HttpClient.cs b/src/Stripe.net/Infrastructure/Http/HttpClient.cs
new file mode 100644
index 0000000000..018a78d67b
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/Http/HttpClient.cs
@@ -0,0 +1,25 @@
+namespace Stripe.Infrastructure.Http
+{
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ ///
+ /// Abstract base class for HTTP clients used to make requests to Stripe's API.
+ ///
+ public abstract class HttpClient
+ {
+ /// The last request made by this client.
+ public Request LastRequest { get; protected set; }
+
+ /// The last response received by this client.
+ public Response LastResponse { get; protected set; }
+
+ /// Sends a request to Stripe's API as an asynchronous operation.
+ /// The parameters of the request to send.
+ /// The cancellation token to cancel operation.
+ /// The task object representing the asynchronous operation.
+ public abstract Task MakeRequestAsync(
+ Request request,
+ CancellationToken cancellationToken = default(CancellationToken));
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/Http/Request.cs b/src/Stripe.net/Infrastructure/Http/Request.cs
new file mode 100644
index 0000000000..8b9b494b8f
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/Http/Request.cs
@@ -0,0 +1,119 @@
+namespace Stripe.Infrastructure.Http
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Text;
+ using Stripe.Infrastructure.Extensions;
+ using Stripe.Infrastructure.FormEncoding;
+
+ ///
+ /// Represents a request to Stripe's API.
+ ///
+ public class Request
+ {
+ /// Initializes a new instance of the class.
+ /// The HTTP method.
+ /// The path of the request.
+ /// The parameters of the request.
+ /// The special modifiers of the request.
+ public Request(
+ HttpMethod method,
+ string path,
+ BaseOptions options,
+ RequestOptions requestOptions)
+ {
+ this.Method = method;
+
+ this.Uri = BuildUri(method, path, options, requestOptions);
+
+ this.AuthorizationHeader = new AuthenticationHeaderValue(
+ "Bearer",
+ requestOptions?.ApiKey ?? StripeConfiguration.ApiKey);
+
+ this.StripeHeaders = BuildStripeHeaders(requestOptions);
+
+ this.Content = BuildContent(method, options);
+ }
+
+ /// The HTTP method for the request (GET, POST or DELETE).
+ public HttpMethod Method { get; }
+
+ ///
+ /// The URL for the request. If this is a GET or DELETE request, the URL also includes
+ /// the parameters in the query string.
+ ///
+ public Uri Uri { get; }
+
+ /// The value of the Authorization header with the API key.
+ public AuthenticationHeaderValue AuthorizationHeader { get; }
+
+ ///
+ /// Dictionary containing Stripe custom headers (Stripe-Version,
+ /// Stripe-Account, Idempotency-Key...).
+ ///
+ public Dictionary StripeHeaders { get; }
+
+ ///
+ /// The body of the request. For POST requests, this will be either a
+ /// application/x-www-form-urlencoded or a multipart/form-data encoded
+ /// payload. For non-POST requests, this will be null.
+ ///
+ public HttpContent Content { get; }
+
+ private static Uri BuildUri(
+ HttpMethod method,
+ string path,
+ BaseOptions options,
+ RequestOptions requestOptions)
+ {
+ var b = new StringBuilder();
+
+ b.Append(requestOptions?.BaseUrl ?? StripeConfiguration.ApiBase);
+ b.Append(path);
+
+ if (method != HttpMethod.Post)
+ {
+ var queryString = options?.ToQueryString();
+ if (!string.IsNullOrEmpty(queryString))
+ {
+ b.Append("?");
+ b.Append(queryString);
+ }
+ }
+
+ return new Uri(b.ToString());
+ }
+
+ private static Dictionary BuildStripeHeaders(RequestOptions requestOptions)
+ {
+ var stripeHeaders = new Dictionary
+ {
+ { "Stripe-Version", requestOptions?.StripeVersion ?? StripeConfiguration.ApiVersion },
+ };
+
+ if (!string.IsNullOrEmpty(requestOptions?.StripeConnectAccountId))
+ {
+ stripeHeaders.Add("Stripe-Account", requestOptions.StripeConnectAccountId);
+ }
+
+ if (!string.IsNullOrEmpty(requestOptions?.IdempotencyKey))
+ {
+ stripeHeaders.Add("Idempotency-Key", requestOptions.IdempotencyKey);
+ }
+
+ return stripeHeaders;
+ }
+
+ private static HttpContent BuildContent(HttpMethod method, BaseOptions options)
+ {
+ if (method != HttpMethod.Post)
+ {
+ return null;
+ }
+
+ return FormEncoder.EncodeOptionsContent(options);
+ }
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/Http/Response.cs b/src/Stripe.net/Infrastructure/Http/Response.cs
new file mode 100644
index 0000000000..7b7a82f5dd
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/Http/Response.cs
@@ -0,0 +1,31 @@
+namespace Stripe.Infrastructure.Http
+{
+ using System.Net;
+ using System.Net.Http.Headers;
+
+ ///
+ /// Represents a response from Stripe's API.
+ ///
+ public class Response
+ {
+ /// Initializes a new instance of the class.
+ /// The HTTP status code.
+ /// The HTTP headers of the response.
+ /// The body of the response.
+ public Response(HttpStatusCode statusCode, HttpHeaders headers, string content)
+ {
+ this.StatusCode = statusCode;
+ this.Headers = headers;
+ this.Content = content;
+ }
+
+ /// The HTTP status code of the response.
+ public HttpStatusCode StatusCode { get; }
+
+ /// The HTTP headers of the response.
+ public HttpHeaders Headers { get; }
+
+ /// The body of the response.
+ public string Content { get; }
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/Http/SystemNetHttpClient.cs b/src/Stripe.net/Infrastructure/Http/SystemNetHttpClient.cs
new file mode 100644
index 0000000000..2cc3bc2f02
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/Http/SystemNetHttpClient.cs
@@ -0,0 +1,123 @@
+namespace Stripe.Infrastructure.Http
+{
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Newtonsoft.Json;
+
+ ///
+ /// Standard client to make requests to Stripe's API, using
+ /// to send HTTP requests.
+ ///
+ public class SystemNetHttpClient : HttpClient
+ {
+ private static readonly string UserAgentString
+ = $"Stripe/v1 .NetBindings/{StripeConfiguration.StripeNetVersion}";
+
+ private readonly System.Net.Http.HttpClient httpClient;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The client to use. If null, an HTTP
+ /// client will be created with default parameters.
+ ///
+ public SystemNetHttpClient(System.Net.Http.HttpClient httpClient = null)
+ {
+ this.httpClient = httpClient ?? BuildDefaultSystemNetHttpClient();
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ /// with default parameters.
+ ///
+ /// The new instance of the class.
+ public static System.Net.Http.HttpClient BuildDefaultSystemNetHttpClient()
+ {
+ // We set the User-Agent and X-Stripe-Client-User-Agent headers in each request
+ // message rather than through the client's DefaultRequestHeaders because we
+ // want these headers to be present even when a custom HTTP client is used.
+ return new System.Net.Http.HttpClient()
+ {
+ Timeout = StripeConfiguration.DefaultHttpTimeout,
+ };
+ }
+
+ /// Sends a request to Stripe's API as an asynchronous operation.
+ /// The parameters of the request to send.
+ /// The cancellation token to cancel operation.
+ /// The task object representing the asynchronous operation.
+ public override async Task MakeRequestAsync(
+ Request request,
+ CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var httpRequest = BuildRequestMessage(request);
+
+ this.LastRequest = request;
+ this.LastResponse = null;
+
+ // TODO: telemetry
+ // TODO: request retries
+ var response = await this.httpClient.SendAsync(httpRequest, cancellationToken)
+ .ConfigureAwait(false);
+ var reader = new StreamReader(
+ await response.Content.ReadAsStreamAsync().ConfigureAwait(false));
+ this.LastResponse = new Response(
+ response.StatusCode,
+ response.Headers,
+ await reader.ReadToEndAsync().ConfigureAwait(false));
+
+ return this.LastResponse;
+ }
+
+ private static System.Net.Http.HttpRequestMessage BuildRequestMessage(Request request)
+ {
+ var requestMessage = new System.Net.Http.HttpRequestMessage(request.Method, request.Uri);
+
+ // Standard headers
+ requestMessage.Headers.UserAgent.ParseAdd(UserAgentString);
+ requestMessage.Headers.Authorization = request.AuthorizationHeader;
+
+ // Custom headers
+ requestMessage.Headers.Add(
+ "X-Stripe-Client-User-Agent",
+ BuildStripeClientUserAgentString());
+ foreach (var header in request.StripeHeaders)
+ {
+ requestMessage.Headers.Add(header.Key, header.Value);
+ }
+
+ // Request body
+ if (request.Content != null)
+ {
+ requestMessage.Content = request.Content;
+ }
+
+ return requestMessage;
+ }
+
+ private static string BuildStripeClientUserAgentString()
+ {
+ var values = new Dictionary
+ {
+ { "bindings_version", StripeConfiguration.StripeNetVersion },
+ { "lang", ".net" },
+ { "publisher", "stripe" },
+ { "lang_version", RuntimeInformation.GetLanguageVersion() },
+ { "os_version", RuntimeInformation.GetOSVersion() },
+ };
+
+#if NET45
+ string monoVersion = RuntimeInformation.GetMonoVersion();
+ if (!string.IsNullOrEmpty(monoVersion))
+ {
+ values.Add("mono_version", monoVersion);
+ }
+#endif
+
+ return JsonConvert.SerializeObject(values, Formatting.None);
+ }
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/IStripeClient.cs b/src/Stripe.net/Infrastructure/IStripeClient.cs
new file mode 100644
index 0000000000..e8192ab978
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/IStripeClient.cs
@@ -0,0 +1,25 @@
+namespace Stripe.Infrastructure
+{
+ using System.Net.Http;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ public interface IStripeClient
+ {
+ /// Sends a request to Stripe's API as an asynchronous operation.
+ /// Type of the Stripe entity returned by the API.
+ /// The HTTP method.
+ /// The path of the request.
+ /// The parameters of the request.
+ /// The special modifiers of the request.
+ /// The cancellation token to cancel operation.
+ /// The task object representing the asynchronous operation.
+ Task RequestAsync(
+ HttpMethod method,
+ string path,
+ BaseOptions options,
+ RequestOptions requestOptions,
+ CancellationToken cancellationToken = default(CancellationToken))
+ where T : IStripeEntity;
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/Public/Mapper.cs b/src/Stripe.net/Infrastructure/Public/Mapper.cs
deleted file mode 100644
index 1d94a204d9..0000000000
--- a/src/Stripe.net/Infrastructure/Public/Mapper.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-namespace Stripe
-{
- using System.Reflection;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
-
- public static class Mapper
- {
- // the ResponseJson on a list method is the entire list (as json) returned from stripe.
- // the ObjectJson is so we can store only the json for a single object in the list on that entity for
- // logging and/or debugging
- public static T MapFromJson(string json, string parentToken = null, StripeResponse stripeResponse = null)
- {
- var jsonToParse = string.IsNullOrEmpty(parentToken) ? json : JObject.Parse(json).SelectToken(parentToken).ToString();
-
- var result = JsonConvert.DeserializeObject(jsonToParse, StripeConfiguration.SerializerSettings);
-
- // if necessary, we might need to apply the stripe response to nested properties for StripeList
- ApplyStripeResponse(json, stripeResponse, result);
-
- return result;
- }
-
- public static T MapFromJson(StripeResponse stripeResponse, string parentToken = null)
- {
- return MapFromJson(stripeResponse.ResponseJson, parentToken, stripeResponse);
- }
-
- private static void ApplyStripeResponse(string json, StripeResponse stripeResponse, object obj)
- {
- if (stripeResponse == null)
- {
- return;
- }
-
- foreach (var property in obj.GetType().GetRuntimeProperties())
- {
- if (property.Name == nameof(StripeResponse))
- {
- property.SetValue(obj, stripeResponse);
- }
- }
-
- stripeResponse.ObjectJson = json;
- }
- }
-}
diff --git a/src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs b/src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs
index 9397a4c20d..bbcb9d1cde 100644
--- a/src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs
+++ b/src/Stripe.net/Infrastructure/Public/StripeConfiguration.cs
@@ -2,7 +2,6 @@ namespace Stripe
{
using System;
using System.Collections.Generic;
- using System.Net.Http;
using System.Reflection;
using Newtonsoft.Json;
using Stripe.Infrastructure;
@@ -14,9 +13,11 @@ public static class StripeConfiguration
{
private static string apiKey;
+ private static IStripeClient stripeClient;
+
static StripeConfiguration()
{
- StripeNetVersion = new AssemblyName(typeof(Requestor).GetTypeInfo().Assembly.FullName).Version.ToString(3);
+ StripeNetVersion = new AssemblyName(typeof(StripeConfiguration).GetTypeInfo().Assembly.FullName).Version.ToString(3);
}
/// API version used by Stripe.net.
@@ -71,12 +72,6 @@ public static string ApiKey
/// Gets or sets the base URL for Stripe's Files API.
public static string FilesBase { get; set; } = DefaultFilesBase;
- /// Gets or sets a custom .
- public static HttpMessageHandler HttpMessageHandler { get; set; }
-
- /// Gets or sets the timespan to wait before the request times out.
- public static TimeSpan HttpTimeout { get; set; } = DefaultHttpTimeout;
-
///
/// Gets or sets the settings used for deserializing JSON objects returned by Stripe's API.
/// It is highly recommended you do not change these settings, as doing so can produce
@@ -87,22 +82,41 @@ public static string ApiKey
///
public static JsonSerializerSettings SerializerSettings { get; set; } = DefaultSerializerSettings();
- /// Gets the version of the Stripe.net client library.
- public static string StripeNetVersion { get; }
-
///
- /// Gets or sets the timespan to wait before the request times out.
- /// This property is deprecated and will be removed in a future version, please use the
- /// property instead.
+ /// Gets or sets a custom for sending requests to Stripe's
+ /// API. You can use this to use a custom message handler, set proxy parameters, etc.
///
- // TODO: remove this property in a future major version
- [Obsolete("Use StripeConfiguration.HttpTimeout instead.")]
- public static TimeSpan? HttpTimeSpan
+ ///
+ /// To use a custom message handler:
+ ///
+ /// System.Net.Http.HttpMessageHandler messageHandler = ...;
+ /// var httpClient = new System.Net.HttpClient(messageHandler);
+ /// var stripeClient = new StripeClient(new Stripe.Client.SystemNetHttpClient(httpClient));
+ /// StripeConfiguration.StripeClient = stripeClient;
+ ///
+ ///
+ // TODO fix example with final namespaces
+ public static IStripeClient StripeClient
{
- get { return HttpTimeout; }
- set { HttpTimeout = value ?? DefaultHttpTimeout; }
+ get
+ {
+ if (stripeClient == null)
+ {
+ stripeClient = new StripeClient();
+ }
+
+ return stripeClient;
+ }
+
+ set
+ {
+ stripeClient = value;
+ }
}
+ /// Gets the version of the Stripe.net client library.
+ public static string StripeNetVersion { get; }
+
///
/// Returns a new instance of with
/// the default settings used by Stripe.net.
diff --git a/src/Stripe.net/Infrastructure/Public/StripeResponse.cs b/src/Stripe.net/Infrastructure/Public/StripeResponse.cs
index ca69a43200..05851194cd 100644
--- a/src/Stripe.net/Infrastructure/Public/StripeResponse.cs
+++ b/src/Stripe.net/Infrastructure/Public/StripeResponse.cs
@@ -6,10 +6,17 @@ public class StripeResponse
{
public string ResponseJson { get; set; }
- public string ObjectJson { get; set; }
-
public string RequestId { get; set; }
public DateTime RequestDate { get; set; }
+
+ public override string ToString()
+ {
+ return string.Format(
+ "{0} {{ RequestId={1}, RequestDate={2} }}",
+ this.GetType().FullName,
+ this.RequestId,
+ this.RequestDate.ToString("s"));
+ }
}
}
diff --git a/src/Stripe.net/Infrastructure/Requestor.cs b/src/Stripe.net/Infrastructure/Requestor.cs
deleted file mode 100644
index 3d0156140a..0000000000
--- a/src/Stripe.net/Infrastructure/Requestor.cs
+++ /dev/null
@@ -1,251 +0,0 @@
-namespace Stripe.Infrastructure
-{
- using System;
- using System.Globalization;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
-
- internal static class Requestor
- {
- static Requestor()
- {
- HttpClient =
- StripeConfiguration.HttpMessageHandler != null
- ? new HttpClient(StripeConfiguration.HttpMessageHandler)
- : new HttpClient();
-
- HttpClient.Timeout = StripeConfiguration.HttpTimeout;
- }
-
- internal static HttpClient HttpClient { get; private set; }
-
- public static StripeResponse GetString(string url, RequestOptions requestOptions)
- {
- var wr = GetRequestMessage(url, HttpMethod.Get, requestOptions);
-
- return ExecuteRequest(wr);
- }
-
- public static StripeResponse PostString(string url, RequestOptions requestOptions)
- {
- var wr = GetRequestMessage(url, HttpMethod.Post, requestOptions);
-
- return ExecuteRequest(wr);
- }
-
- public static StripeResponse Delete(string url, RequestOptions requestOptions)
- {
- var wr = GetRequestMessage(url, HttpMethod.Delete, requestOptions);
-
- return ExecuteRequest(wr);
- }
-
- public static StripeResponse PostFile(string url, Stream stream, string purpose, RequestOptions requestOptions)
- {
- var wr = GetRequestMessage(url, HttpMethod.Post, requestOptions);
-
- ApplyMultiPartFileToRequest(wr, stream, purpose);
-
- return ExecuteRequest(wr);
- }
-
- private static StripeResponse ExecuteRequest(HttpRequestMessage requestMessage)
- {
- var response = HttpClient.SendAsync(requestMessage).ConfigureAwait(false).GetAwaiter().GetResult();
- var responseText = response.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
-
- var result = BuildResponseData(response, responseText);
-
- if (response.IsSuccessStatusCode)
- {
- return result;
- }
-
- throw BuildStripeException(result, response.StatusCode, requestMessage.RequestUri.AbsoluteUri, responseText);
- }
-
- public static Task GetStringAsync(string url, RequestOptions requestOptions, CancellationToken cancellationToken = default(CancellationToken))
- {
- var wr = GetRequestMessage(url, HttpMethod.Get, requestOptions);
-
- return ExecuteRequestAsync(wr, cancellationToken);
- }
-
- public static Task PostStringAsync(string url, RequestOptions requestOptions, CancellationToken cancellationToken = default(CancellationToken))
- {
- var wr = GetRequestMessage(url, HttpMethod.Post, requestOptions);
-
- return ExecuteRequestAsync(wr, cancellationToken);
- }
-
- public static Task DeleteAsync(string url, RequestOptions requestOptions, CancellationToken cancellationToken = default(CancellationToken))
- {
- var wr = GetRequestMessage(url, HttpMethod.Delete, requestOptions);
-
- return ExecuteRequestAsync(wr, cancellationToken);
- }
-
- public static Task PostFileAsync(string url, Stream stream, string purpose, RequestOptions requestOptions, CancellationToken cancellationToken = default(CancellationToken))
- {
- var wr = GetRequestMessage(url, HttpMethod.Post, requestOptions);
-
- ApplyMultiPartFileToRequest(wr, stream, purpose);
-
- return ExecuteRequestAsync(wr, cancellationToken);
- }
-
- internal static async Task ExecuteRequestAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default(CancellationToken))
- {
- var response = await HttpClient.SendAsync(requestMessage, cancellationToken).ConfigureAwait(false);
- var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-
- var result = BuildResponseData(response, responseText);
-
- if (response.IsSuccessStatusCode)
- {
- return result;
- }
-
- throw BuildStripeException(result, response.StatusCode, requestMessage.RequestUri.AbsoluteUri, responseText);
- }
-
- internal static HttpRequestMessage GetRequestMessage(string url, HttpMethod method, RequestOptions requestOptions)
- {
- requestOptions.ApiKey = requestOptions.ApiKey ?? StripeConfiguration.ApiKey;
-
-#if NET45
- ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
-#endif
-
- var request = BuildRequest(method, url);
-
- request.Headers.Add(
- "Authorization",
- GetAuthorizationHeaderValue(requestOptions.ApiKey));
-
- if (requestOptions.StripeConnectAccountId != null)
- {
- request.Headers.Add("Stripe-Account", requestOptions.StripeConnectAccountId);
- }
-
- if (requestOptions.IdempotencyKey != null)
- {
- request.Headers.Add("Idempotency-Key", requestOptions.IdempotencyKey);
- }
-
- if (requestOptions.StripeVersion != null)
- {
- request.Headers.Add("Stripe-Version", requestOptions.StripeVersion);
- }
- else
- {
- request.Headers.Add("Stripe-Version", StripeConfiguration.ApiVersion);
- }
-
- var client = new Client(request);
- client.ApplyUserAgent();
- client.ApplyClientData();
-
- return request;
- }
-
- private static HttpRequestMessage BuildRequest(HttpMethod method, string url)
- {
- if (method != HttpMethod.Post)
- {
- return new HttpRequestMessage(method, new Uri(url));
- }
-
- var postData = string.Empty;
- var newUrl = url;
-
- if (!string.IsNullOrEmpty(new Uri(url).Query))
- {
- postData = new Uri(url).Query.Substring(1);
- newUrl = url.Substring(0, url.IndexOf("?", StringComparison.CurrentCultureIgnoreCase));
- }
-
- var request = new HttpRequestMessage(method, new Uri(newUrl))
- {
- Content = new StringContent(postData, Encoding.UTF8, "application/x-www-form-urlencoded")
- };
-
- return request;
- }
-
- private static string GetAuthorizationHeaderValue(string apiKey)
- {
- return $"Bearer {apiKey}";
- }
-
- private static void ApplyMultiPartFileToRequest(HttpRequestMessage requestMessage, Stream stream, string purpose)
- {
- requestMessage.Headers.ExpectContinue = true;
-
- string fileName = "blob";
-
- #if NET45
- // Doing this on .NET Standard would require us to bump the minimum framework version
- // to .NET Standard 1.3, which isn't worth it since the filename is basically ignored
- // by the server.
- FileStream fileStream = stream as FileStream;
- if ((fileStream != null) && (!string.IsNullOrEmpty(fileStream.Name)))
- {
- fileName = fileStream.Name;
- }
- #endif
-
- var fileContent = new StreamContent(stream);
- fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
- {
- Name = "\"file\"",
- FileName = $"\"{fileName}\""
- };
-
- fileContent.Headers.ContentType = new MediaTypeHeaderValue(MimeTypes.GetMimeType(fileName));
-
- var multiPartContent =
- new MultipartFormDataContent($"----------Upload:{DateTime.UtcNow.Ticks :x}")
- {
- { new StringContent(purpose), "\"purpose\"" },
- fileContent
- };
-
- requestMessage.Content = multiPartContent;
- }
-
- private static StripeException BuildStripeException(StripeResponse response, HttpStatusCode statusCode, string requestUri, string responseContent)
- {
- var stripeError = requestUri.Contains("oauth")
- ? Mapper.MapFromJson(responseContent, null, response)
- : Mapper.MapFromJson(responseContent, "error", response);
-
- return new StripeException(statusCode, stripeError, stripeError.Message)
- {
- StripeResponse = response
- };
- }
-
- private static StripeResponse BuildResponseData(HttpResponseMessage response, string responseText)
- {
- var result = new StripeResponse
- {
- RequestId = response.Headers.Contains("Request-Id") ?
- response.Headers.GetValues("Request-Id").First() :
- "n/a",
- RequestDate = response.Headers.Contains("Date") ?
- Convert.ToDateTime(response.Headers.GetValues("Date").First(), CultureInfo.InvariantCulture) :
- default(DateTime),
- ResponseJson = responseText,
- };
-
- return result;
- }
- }
-}
diff --git a/src/Stripe.net/Infrastructure/RuntimeInformation.cs b/src/Stripe.net/Infrastructure/RuntimeInformation.cs
new file mode 100644
index 0000000000..d234dda8f0
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/RuntimeInformation.cs
@@ -0,0 +1,53 @@
+namespace Stripe.Infrastructure
+{
+#if NET45
+ using System;
+ using System.Reflection;
+ using Microsoft.Win32;
+#endif
+
+ internal static class RuntimeInformation
+ {
+ public static string GetLanguageVersion()
+ {
+#if NET45
+ return ".NET Framework 4.5+";
+#else
+ return System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription;
+#endif
+ }
+
+ public static string GetOSVersion()
+ {
+#if NET45
+ return Environment.OSVersion.ToString();
+#else
+ return System.Runtime.InteropServices.RuntimeInformation.OSDescription;
+#endif
+ }
+
+#if NET45
+ public static string GetMonoVersion()
+ {
+ Type monoRuntimeType = typeof(object).Assembly.GetType("Mono.Runtime");
+
+ if (monoRuntimeType != null)
+ {
+ MethodInfo getDisplayNameMethod = monoRuntimeType.GetMethod(
+ "GetDisplayName",
+ BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.DeclaredOnly | BindingFlags.ExactBinding,
+ null,
+ Type.EmptyTypes,
+ null);
+
+ if (getDisplayNameMethod != null)
+ {
+ return (string)getDisplayNameMethod.Invoke(null, null);
+ }
+ }
+
+ return null;
+ }
+#endif
+ }
+}
diff --git a/src/Stripe.net/Infrastructure/StripeClient.cs b/src/Stripe.net/Infrastructure/StripeClient.cs
new file mode 100644
index 0000000000..eae6ad5087
--- /dev/null
+++ b/src/Stripe.net/Infrastructure/StripeClient.cs
@@ -0,0 +1,105 @@
+namespace Stripe.Infrastructure
+{
+ using System;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net;
+ using System.Net.Http;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Newtonsoft.Json.Linq;
+ using Stripe.Infrastructure.Http;
+
+ public class StripeClient : IStripeClient
+ {
+ /// Initializes a new instance of the class.
+ ///
+ /// The client to use. If null,
+ /// an HTTP client will be created with default parameters.
+ ///
+ public StripeClient(Stripe.Infrastructure.Http.HttpClient httpClient = null)
+ {
+ this.HttpClient = httpClient ?? BuildDefaultHttpClient();
+ }
+
+ public Stripe.Infrastructure.Http.HttpClient HttpClient { get; }
+
+ /// Sends a request to Stripe's API as an asynchronous operation.
+ /// Type of the Stripe entity returned by the API.
+ /// The HTTP method.
+ /// The path of the request.
+ /// The parameters of the request.
+ /// The special modifiers of the request.
+ /// The cancellation token to cancel operation.
+ /// The task object representing the asynchronous operation.
+ public async Task RequestAsync(
+ HttpMethod method,
+ string path,
+ BaseOptions options,
+ RequestOptions requestOptions,
+ CancellationToken cancellationToken = default(CancellationToken))
+ where T : IStripeEntity
+ {
+ var request = new Request(method, path, options, requestOptions);
+
+ var response = await this.HttpClient.MakeRequestAsync(request);
+
+ return ProcessResponse(response);
+ }
+
+ private static Stripe.Infrastructure.Http.HttpClient BuildDefaultHttpClient()
+ {
+ return new SystemNetHttpClient();
+ }
+
+ private static T ProcessResponse(Response response)
+ where T : IStripeEntity
+ {
+ var stripeResponse = BuildStripeResponse(response);
+
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
+ throw BuildStripeException(response, stripeResponse);
+ }
+
+ var obj = StripeEntity.FromJson(response.Content);
+ obj.StripeResponse = stripeResponse;
+
+ return obj;
+ }
+
+ private static StripeResponse BuildStripeResponse(Response response)
+ {
+ return new StripeResponse
+ {
+ RequestId = response.Headers.Contains("Request-Id")
+ ? response.Headers.GetValues("Request-Id").First()
+ : "n/a",
+ RequestDate = response.Headers.Contains("Date")
+ ? Convert.ToDateTime(
+ response.Headers.GetValues("Date").First(),
+ CultureInfo.InvariantCulture)
+ : default(DateTime),
+ ResponseJson = response.Content,
+ };
+ }
+
+ private static StripeException BuildStripeException(
+ Response response,
+ StripeResponse stripeResponse)
+ {
+ var stripeError = false // TODO
+ ? StripeError.FromJson(response.Content)
+ : StripeError.FromJson(JObject.Parse(response.Content)["error"].ToString()); // TODO
+ stripeError.StripeResponse = stripeResponse;
+
+ return new StripeException(
+ response.StatusCode,
+ stripeError,
+ stripeError.Message)
+ {
+ StripeResponse = stripeResponse,
+ };
+ }
+ }
+}
diff --git a/src/Stripe.net/Services/Files/FileCreateOptions.cs b/src/Stripe.net/Services/Files/FileCreateOptions.cs
index aa36be5b43..6affcfdb43 100644
--- a/src/Stripe.net/Services/Files/FileCreateOptions.cs
+++ b/src/Stripe.net/Services/Files/FileCreateOptions.cs
@@ -1,10 +1,9 @@
namespace Stripe
{
- using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
- public class FileCreateOptions : BaseOptions
+ public class FileCreateOptions : MultipartOptions
{
///
/// REQUIRED. A file to upload. The file should follow the specifications of RFC 2388
diff --git a/src/Stripe.net/Services/Files/FileService.cs b/src/Stripe.net/Services/Files/FileService.cs
index 20622f7dca..9fb9530212 100644
--- a/src/Stripe.net/Services/Files/FileService.cs
+++ b/src/Stripe.net/Services/Files/FileService.cs
@@ -25,26 +25,15 @@ public FileService(string apiKey)
public virtual File Create(FileCreateOptions options, RequestOptions requestOptions = null)
{
requestOptions = this.SetupRequestOptions(requestOptions);
- requestOptions.BaseUrl = StripeConfiguration.FilesBase;
- return Mapper.MapFromJson(
- Requestor.PostFile(
- StripeConfiguration.FilesBase + this.ClassUrl(),
- options.File,
- options.Purpose,
- requestOptions));
+ requestOptions.BaseUrl = requestOptions.BaseUrl ?? StripeConfiguration.FilesBase;
+ return this.CreateEntity(options, requestOptions);
}
- public virtual async Task CreateAsync(FileCreateOptions options, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken))
+ public virtual Task CreateAsync(FileCreateOptions options, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken))
{
requestOptions = this.SetupRequestOptions(requestOptions);
- requestOptions.BaseUrl = StripeConfiguration.FilesBase;
- return Mapper.MapFromJson(
- await Requestor.PostFileAsync(
- StripeConfiguration.FilesBase + this.ClassUrl(),
- options.File,
- options.Purpose,
- requestOptions,
- cancellationToken).ConfigureAwait(false));
+ requestOptions.BaseUrl = requestOptions.BaseUrl ?? StripeConfiguration.FilesBase;
+ return this.CreateEntityAsync(options, requestOptions, cancellationToken);
}
public virtual File Get(string fileId, RequestOptions requestOptions = null)
diff --git a/src/Stripe.net/Services/OAuth/OAuthTokenService.cs b/src/Stripe.net/Services/OAuth/OAuthTokenService.cs
index 7c00c38c05..e6f83cc31d 100644
--- a/src/Stripe.net/Services/OAuth/OAuthTokenService.cs
+++ b/src/Stripe.net/Services/OAuth/OAuthTokenService.cs
@@ -17,18 +17,18 @@ public OAuthTokenService(string apiKey)
{
}
- public override string BasePath => null;
+ public override string BasePath => "/oauth/token";
public override string BaseUrl => StripeConfiguration.ConnectBase;
public virtual OAuthToken Create(OAuthTokenCreateOptions options, RequestOptions requestOptions = null)
{
- return this.Request(HttpMethod.Post, "/oauth/token", options, requestOptions);
+ return this.CreateEntity(options, requestOptions);
}
public virtual Task CreateAsync(OAuthTokenCreateOptions options, RequestOptions requestOptions = null, CancellationToken cancellationToken = default(CancellationToken))
{
- return this.RequestAsync(HttpMethod.Post, "/oauth/token", options, requestOptions, cancellationToken);
+ return this.CreateEntityAsync(options, requestOptions, cancellationToken);
}
public virtual OAuthDeauthorize Deauthorize(OAuthTokenDeauthorizeOptions options, RequestOptions requestOptions = null)
diff --git a/src/Stripe.net/Services/_base/MultipartOptions.cs b/src/Stripe.net/Services/_base/MultipartOptions.cs
new file mode 100644
index 0000000000..97bd5ac739
--- /dev/null
+++ b/src/Stripe.net/Services/_base/MultipartOptions.cs
@@ -0,0 +1,10 @@
+namespace Stripe
+{
+ ///
+ /// Base class for Stripe options classes for which parameters should be encoded as
+ /// multipart/form-data rather than the usual application/x-www-form-urlencoded.
+ ///
+ public class MultipartOptions : BaseOptions
+ {
+ }
+}
diff --git a/src/Stripe.net/Services/_base/Service.cs b/src/Stripe.net/Services/_base/Service.cs
index 5cd46c5f3a..9da1bbba61 100644
--- a/src/Stripe.net/Services/_base/Service.cs
+++ b/src/Stripe.net/Services/_base/Service.cs
@@ -1,6 +1,7 @@
namespace Stripe
{
using System.Collections.Generic;
+ using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
@@ -208,13 +209,14 @@ protected async Task RequestAsync(
CancellationToken cancellationToken = default(CancellationToken))
where T : IStripeEntity
{
+ options = this.SetupOptions(options, IsStripeList());
requestOptions = this.SetupRequestOptions(requestOptions);
- var url = requestOptions.BaseUrl + path;
- var wr = Requestor.GetRequestMessage(
- this.ApplyAllParameters(options, url, IsStripeList()),
+ return await StripeConfiguration.StripeClient.RequestAsync(
method,
- requestOptions);
- return Mapper.MapFromJson(await Requestor.ExecuteRequestAsync(wr));
+ path,
+ options,
+ requestOptions,
+ cancellationToken);
}
protected IEnumerable ListRequestAutoPaging(
@@ -259,7 +261,7 @@ protected RequestOptions SetupRequestOptions(RequestOptions requestOptions)
requestOptions = new RequestOptions();
}
- if (!string.IsNullOrEmpty(this.ApiKey))
+ if (requestOptions.ApiKey == null && !string.IsNullOrEmpty(this.ApiKey))
{
requestOptions.ApiKey = this.ApiKey;
}
@@ -269,6 +271,21 @@ protected RequestOptions SetupRequestOptions(RequestOptions requestOptions)
return requestOptions;
}
+ protected BaseOptions SetupOptions(BaseOptions options, bool isListMethod)
+ {
+ var expansions = this.Expansions(isListMethod);
+
+ if (!expansions.Any())
+ {
+ return options;
+ }
+
+ options = options ?? new BaseOptions();
+ options.Expand.AddRange(expansions);
+
+ return options;
+ }
+
protected virtual string ClassUrl()
{
return this.BasePath;
diff --git a/src/StripeTests/Infrastructure/ClientTest.cs b/src/StripeTests/Infrastructure/ClientTest.cs
deleted file mode 100644
index 91b9e877b9..0000000000
--- a/src/StripeTests/Infrastructure/ClientTest.cs
+++ /dev/null
@@ -1,35 +0,0 @@
-namespace StripeTests
-{
- using System.Linq;
- using System.Net.Http;
- using Newtonsoft.Json.Linq;
- using Stripe;
- using Stripe.Infrastructure;
- using Xunit;
-
- public class ClientTest : BaseStripeTest
- {
- [Fact]
- public void SetsUserAgent()
- {
- var request = new HttpRequestMessage();
- Assert.NotNull(request);
-
- var client = new Client(request);
- client.ApplyClientData();
- client.ApplyUserAgent();
-
- var expectedUserAgent = $"Stripe/v1 .NetBindings/{StripeConfiguration.StripeNetVersion}";
- Assert.Equal(expectedUserAgent, request.Headers.UserAgent.ToString());
- Assert.NotNull(request.Headers.GetValues("X-Stripe-Client-User-Agent"));
-
- var userAgentJson = JToken.Parse(request.Headers.GetValues("X-Stripe-Client-User-Agent").FirstOrDefault());
- Assert.NotNull(userAgentJson);
- Assert.Equal(StripeConfiguration.StripeNetVersion, userAgentJson["bindings_version"]);
- Assert.Equal(".net", userAgentJson["lang"]);
- Assert.Equal("stripe", userAgentJson["publisher"]);
- Assert.NotNull(userAgentJson["lang_version"]);
- Assert.NotNull(userAgentJson["os_version"]);
- }
- }
-}
diff --git a/src/StripeTests/Infrastructure/Extensions/ServiceExtensionsTest.cs b/src/StripeTests/Infrastructure/Extensions/ServiceExtensionsTest.cs
index 6c9cf71d37..e36c79b66e 100644
--- a/src/StripeTests/Infrastructure/Extensions/ServiceExtensionsTest.cs
+++ b/src/StripeTests/Infrastructure/Extensions/ServiceExtensionsTest.cs
@@ -9,46 +9,31 @@ public class ServiceExtensionsTest : BaseStripeTest
private readonly TestService service = new TestService();
[Fact]
- public void ExpandServiceResource()
+ public void Expansions()
{
this.service.ExpandSimple = true;
this.service.ExpandMultiWordProperty = true;
- var url = this.service.ApplyAllParameters(null, string.Empty, false);
- Assert.Equal("?expand[0]=simple&expand[1]=multi_word_property", url);
+ var expansions = this.service.Expansions(false);
+ Assert.NotNull(expansions);
+ Assert.Collection(
+ expansions,
+ i1 => Assert.Equal("simple", i1),
+ i2 => Assert.Equal("multi_word_property", i2));
}
[Fact]
- public void ExpandServiceList()
+ public void ExpansionsForListMethod()
{
this.service.ExpandSimple = true;
this.service.ExpandMultiWordProperty = true;
- var url = this.service.ApplyAllParameters(null, string.Empty, true);
- Assert.Equal("?expand[0]=data.simple&expand[1]=data.multi_word_property", url);
- }
-
- [Fact]
- public void SetsUrl()
- {
- var url = this.service.ApplyAllParameters(null, "base_url", false);
- Assert.Equal("base_url", url);
- }
-
- [Fact]
- public void ExpandViaServicePropertiesAndOptionsClass()
- {
- this.service.ExpandSimple = true;
- this.service.ExpandMultiWordProperty = true;
-
- var options = new TestOptions();
- options.AddExpand("foo");
- options.AddExpand("bar.baz");
-
- var url = this.service.ApplyAllParameters(options, string.Empty, false);
- Assert.Equal(
- "?expand[0]=simple&expand[1]=multi_word_property&expand[2]=foo&expand[3]=bar.baz",
- url);
+ var expansions = this.service.Expansions(true);
+ Assert.NotNull(expansions);
+ Assert.Collection(
+ expansions,
+ i1 => Assert.Equal("data.simple", i1),
+ i2 => Assert.Equal("data.multi_word_property", i2));
}
}
}
diff --git a/src/StripeTests/Infrastructure/MapperTest.cs b/src/StripeTests/Infrastructure/MapperTest.cs
deleted file mode 100644
index 00f36fc5d5..0000000000
--- a/src/StripeTests/Infrastructure/MapperTest.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-namespace StripeTests
-{
- using Stripe;
- using Xunit;
-
- public class MapperTest : BaseStripeTest
- {
- [Fact]
- public void DeserializeISODates()
- {
- var json = GetResourceAsString("api_fixtures.customer_iso_dates.json");
- var customer = Mapper.MapFromJson(json);
-
- Assert.Equal("2018-01-01T12:34:56-07:00", customer.Description);
- Assert.Equal("2018-02-02T12:34:56+02:00", customer.Metadata["some_iso_date"]);
- Assert.Equal("2018-03-03T12:34:56+08:30", customer.Subscriptions.Data[0].Metadata["another_iso_date"]);
- Assert.Equal("2018-04-04T12:34:56Z", ((Card)customer.DefaultSource).Metadata["yet_another_iso_date"]);
- Assert.Equal("2018-04-04T12:34:56Z", ((Card)customer.Sources.Data[0]).Metadata["yet_another_iso_date"]);
- }
- }
-}
diff --git a/src/StripeTests/Infrastructure/RequestorTest.cs b/src/StripeTests/Infrastructure/RequestorTest.cs
deleted file mode 100644
index 06f6cd520b..0000000000
--- a/src/StripeTests/Infrastructure/RequestorTest.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace StripeTests
-{
- using System.Linq;
- using System.Net.Http;
- using Newtonsoft.Json.Linq;
- using Stripe;
- using Stripe.Infrastructure;
- using Xunit;
-
- public class RequestorTest : BaseStripeTest
- {
- [Fact]
- public void SetsHeaders()
- {
- RequestOptions options = new RequestOptions
- {
- ApiKey = "sk_key",
- StripeConnectAccountId = "acct_123",
- IdempotencyKey = "123",
- };
- var request = Requestor.GetRequestMessage("http://localhost", HttpMethod.Get, options);
- Assert.NotNull(request);
- Assert.Equal($"Bearer {options.ApiKey}", request.Headers.GetValues("Authorization").FirstOrDefault());
- Assert.Equal(options.IdempotencyKey, request.Headers.GetValues("Idempotency-Key").FirstOrDefault());
- Assert.Equal(options.StripeConnectAccountId, request.Headers.GetValues("Stripe-Account").FirstOrDefault());
- Assert.Equal(StripeConfiguration.ApiVersion, request.Headers.GetValues("Stripe-Version").FirstOrDefault());
- }
- }
-}
diff --git a/src/StripeTests/Infrastructure/StripeResponseTest.cs b/src/StripeTests/Infrastructure/StripeResponseTest.cs
index 78f9a250b7..218ed585dc 100644
--- a/src/StripeTests/Infrastructure/StripeResponseTest.cs
+++ b/src/StripeTests/Infrastructure/StripeResponseTest.cs
@@ -20,7 +20,6 @@ public void Initializes()
Assert.NotNull(this.charges.StripeResponse);
Assert.NotNull(this.charges.StripeResponse.RequestId);
Assert.NotNull(this.charges.StripeResponse.ResponseJson);
- Assert.NotNull(this.charges.StripeResponse.ObjectJson);
Assert.True(this.charges.StripeResponse.RequestDate.Year > 0);
}
}
diff --git a/src/StripeTests/MockHttpClientFixture.cs b/src/StripeTests/MockHttpClientFixture.cs
index c251087d11..04dd4e5767 100644
--- a/src/StripeTests/MockHttpClientFixture.cs
+++ b/src/StripeTests/MockHttpClientFixture.cs
@@ -8,10 +8,11 @@ namespace StripeTests
using Moq;
using Moq.Protected;
using Stripe;
+ using Stripe.Infrastructure;
public class MockHttpClientFixture : IDisposable
{
- private readonly HttpMessageHandler origHandler;
+ private readonly IStripeClient origClient;
public MockHttpClientFixture()
{
@@ -19,16 +20,19 @@ public MockHttpClientFixture()
{
CallBase = true
};
+ var httpClient = new System.Net.Http.HttpClient(this.MockHandler.Object);
+ var stripeClient = new StripeClient(
+ new Stripe.Infrastructure.Http.SystemNetHttpClient(httpClient));
- this.origHandler = StripeConfiguration.HttpMessageHandler;
- StripeConfiguration.HttpMessageHandler = this.MockHandler.Object;
+ this.origClient = StripeConfiguration.StripeClient;
+ StripeConfiguration.StripeClient = stripeClient;
}
public Mock MockHandler { get; }
public void Dispose()
{
- StripeConfiguration.HttpMessageHandler = this.origHandler;
+ StripeConfiguration.StripeClient = this.origClient;
}
///