diff --git a/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs b/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs index 00ef14a379..4de3063b98 100644 --- a/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs +++ b/src/Stripe.net/Infrastructure/Public/LiveApiRequestor.cs @@ -6,6 +6,7 @@ namespace Stripe using System.Linq; using System.Net; using System.Net.Http; + using System.Net.Http.Headers; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -225,6 +226,18 @@ private static StripeException BuildV2StripeException(StripeResponse response) } } + private static void MaybeEmitStripeNotice(HttpResponseHeaders headers) + { + if (headers != null && headers.Contains("Stripe-Notice")) + { + var notice = headers.GetValues("Stripe-Notice").FirstOrDefault(); + if (!string.IsNullOrEmpty(notice)) + { + Console.Error.WriteLine(notice); + } + } + } + // Note: BaseOptions options really means query params here private StripeRequest MakeStripeRequest( BaseAddress baseAddress, @@ -274,6 +287,8 @@ private T ProcessResponse(StripeResponse response, ApiMode apiMode) throw BuildStripeException(response); } + MaybeEmitStripeNotice(response.Headers); + T obj; try { @@ -362,6 +377,8 @@ public override async Task RawRequestAsync( throw BuildStripeException(response); } + MaybeEmitStripeNotice(response.Headers); + return response; } } diff --git a/src/StripeTests/Infrastructure/Public/LiveApiRequestorTest.cs b/src/StripeTests/Infrastructure/Public/LiveApiRequestorTest.cs index 97e4d72963..4a1d881bb0 100644 --- a/src/StripeTests/Infrastructure/Public/LiveApiRequestorTest.cs +++ b/src/StripeTests/Infrastructure/Public/LiveApiRequestorTest.cs @@ -6,6 +6,7 @@ namespace StripeTests 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; @@ -711,6 +712,137 @@ public async Task StripeAccountHeaderSet() Assert.Equal("acct_2345", lastRequest.StripeHeaders["Stripe-Account"]); } + [Fact] + public async Task RequestAsync_WritesToStderr_WhenStripeNoticeHeaderPresent() + { + var headers = BuildHeaders("Stripe-Notice", "test notice message"); + var response = new StripeResponse(HttpStatusCode.OK, headers, "{\"id\": \"ch_123\"}"); + this.httpClient.Response = response; + + var stderr = new StringWriter(); + Console.SetError(stderr); + try + { + await this.apiRequestor.RequestAsync( + BaseAddress.Api, + HttpMethod.Post, + "/v1/charges", + this.options, + this.requestOptions); + + Assert.Contains("test notice message", stderr.ToString()); + } + finally + { + Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }); + } + } + + [Fact] + public async Task RequestAsync_NoStderrWrite_WhenStripeNoticeHeaderAbsent() + { + var response = new StripeResponse(HttpStatusCode.OK, null, "{\"id\": \"ch_123\"}"); + this.httpClient.Response = response; + + var stderr = new StringWriter(); + Console.SetError(stderr); + try + { + await this.apiRequestor.RequestAsync( + BaseAddress.Api, + HttpMethod.Post, + "/v1/charges", + this.options, + this.requestOptions); + + Assert.Empty(stderr.ToString()); + } + finally + { + Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }); + } + } + + [Fact] + public async Task RequestAsync_NoStderrWrite_WhenStripeNoticeHeaderEmpty() + { + var headers = BuildHeaders("Stripe-Notice", string.Empty); + var response = new StripeResponse(HttpStatusCode.OK, headers, "{\"id\": \"ch_123\"}"); + this.httpClient.Response = response; + + var stderr = new StringWriter(); + Console.SetError(stderr); + try + { + await this.apiRequestor.RequestAsync( + BaseAddress.Api, + HttpMethod.Post, + "/v1/charges", + this.options, + this.requestOptions); + + Assert.Empty(stderr.ToString()); + } + finally + { + Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }); + } + } + + [Fact] + public async Task RawRequestAsync_WritesToStderr_WhenStripeNoticeHeaderPresent() + { + var headers = BuildHeaders("Stripe-Notice", "raw notice message"); + var response = new StripeResponse(HttpStatusCode.OK, headers, "{\"id\": \"ch_123\"}"); + this.httpClient.Response = response; + + var stderr = new StringWriter(); + Console.SetError(stderr); + try + { + await this.apiRequestor.RawRequestAsync( + HttpMethod.Post, + "/v1/charges", + "foo=bar"); + + Assert.Contains("raw notice message", stderr.ToString()); + } + finally + { + Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }); + } + } + + [Fact] + public async Task RawRequestAsync_NoStderrWrite_WhenStripeNoticeHeaderAbsent() + { + var response = new StripeResponse(HttpStatusCode.OK, null, "{\"id\": \"ch_123\"}"); + this.httpClient.Response = response; + + var stderr = new StringWriter(); + Console.SetError(stderr); + try + { + await this.apiRequestor.RawRequestAsync( + HttpMethod.Post, + "/v1/charges", + "foo=bar"); + + Assert.Empty(stderr.ToString()); + } + finally + { + Console.SetError(new StreamWriter(Console.OpenStandardError()) { AutoFlush = true }); + } + } + + private static HttpResponseHeaders BuildHeaders(string name, string value) + { + var message = new HttpResponseMessage(); + message.Headers.Add(name, value); + return message.Headers; + } + private class Foo : StripeEntity { [JsonProperty("bar")]