Skip to content

Commit

Permalink
Merge pull request #13 from dlmelendez/rel/2.2
Browse files Browse the repository at this point in the history
v2.2
  • Loading branch information
dlmelendez authored Apr 30, 2023
2 parents d92bf9f + b20f7fb commit 4f771fa
Show file tree
Hide file tree
Showing 9 changed files with 26 additions and 126 deletions.
1 change: 0 additions & 1 deletion PayPalCheckoutSdk/JsonSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ namespace PayPalCheckoutSdk
//[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.VerificationStatus))]
[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.VerifyWebhookSignature), TypeInfoPropertyName = "Webhooks_VerifyWebhookSignature")]
//[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.VerifyWebhookSignatureRequest))]
[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.VerifyWebhookSignatureResponse), TypeInfoPropertyName = "Webhooks_VerifyWebhookSignatureResponse")]
[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.Webhook), TypeInfoPropertyName = "Webhooks_Webhook")]
//[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.WebhookCreateRequest))]
//[JsonSerializable(typeof(PayPalCheckoutSdk.Webhooks.WebhookDeleteRequest))]
Expand Down
2 changes: 1 addition & 1 deletion PayPalCheckoutSdk/PayPalCheckoutSdk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<LangVersion>11.0</LangVersion>
<Version>2.1.0</Version>
<Version>2.2.0</Version>
<Nullable>enable</Nullable>
<Owners>David Melendez</Owners>
<RepositoryType>git</RepositoryType>
Expand Down
16 changes: 11 additions & 5 deletions PayPalCheckoutSdk/Webhooks/VerifyWebhookEvent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public static class VerifyWebhookEvent
{
private const string WithRSAToken = "withRSA";

private static string? _publicLocalCertificateThumbprint;

public static async Task<bool> ValidateReceivedEventAsync(VerifyWebhookSignature verifySignature)
{
if (string.IsNullOrWhiteSpace(verifySignature.TransmissionTime))
Expand Down Expand Up @@ -75,11 +77,15 @@ public static async Task<bool> ValidateReceivedEventAsync(VerifyWebhookSignature
X509Certificate2Collection remoteCertificateCollection = new X509Certificate2Collection();
remoteCertificateCollection.Import(certificateBytes);

using var publicCertStream = typeof(Event).Assembly.GetManifestResourceStream("PayPalCheckoutSdk.Webhooks.DigiCertSHA2ExtendedValidationServerCA.crt");
byte[] resourceBytes = new byte[publicCertStream!.Length];
_ = await publicCertStream.ReadAsync(resourceBytes, 0, resourceBytes.Length);
if (string.IsNullOrWhiteSpace(_publicLocalCertificateThumbprint))
{
using var publicCertStream = typeof(Event).Assembly.GetManifestResourceStream("PayPalCheckoutSdk.Webhooks.DigiCertSHA2ExtendedValidationServerCA.crt");
byte[] resourceBytes = new byte[publicCertStream!.Length];
_ = await publicCertStream.ReadAsync(resourceBytes);

X509Certificate2 publicLocalCertificate = new X509Certificate2(resourceBytes);
X509Certificate2 publicLocalCertificate = new X509Certificate2(resourceBytes);
_publicLocalCertificateThumbprint = publicLocalCertificate.Thumbprint;
}
// Create and configure the X509Chain object
using X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
Expand All @@ -93,7 +99,7 @@ public static async Task<bool> ValidateReceivedEventAsync(VerifyWebhookSignature
}

// Validate the certificate chain.
bool validateChain = chain.ChainElements.Any(a => a.Certificate.Thumbprint == publicLocalCertificate.Thumbprint);
bool validateChain = chain.ChainElements.Any(a => a.Certificate.Thumbprint == (_publicLocalCertificateThumbprint?? string.Empty));
if (!validateChain)
{
throw new Exception($"Invalid remote certificate, public key not found in chain {verifySignature.CertUrl}");
Expand Down
2 changes: 1 addition & 1 deletion PayPalCheckoutSdk/Webhooks/VerifyWebhookSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ public class VerifyWebhookSignature

/// <summary>
/// A webhook event notification.
/// Please use the WebhooEventRequestBody property instead with <see cref="VerifyWebhookEvent.ValidateReceivedEventAsync"/>
/// </summary>
[Obsolete("Please use the WebhooEventRequestBody property instead with VerifyWebhookEvent.ValidateReceivedEventAsync")]
[DataMember(Name = "webhook_event", EmitDefaultValue = false)]
[JsonPropertyName("webhook_event")]
public Event? WebhookEvent { get; set; }
Expand Down
58 changes: 0 additions & 58 deletions PayPalCheckoutSdk/Webhooks/VerifyWebhookSignatureRequest.cs

This file was deleted.

31 changes: 0 additions & 31 deletions PayPalCheckoutSdk/Webhooks/VerifyWebhookSignatureResponse.cs

This file was deleted.

2 changes: 1 addition & 1 deletion Samples/Samples.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<PackageVersion>2.1.0</PackageVersion>
<PackageVersion>2.2.0</PackageVersion>
</PropertyGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Test/Test.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFrameworks>net6.0;net7.0</TargetFrameworks>
<PackageVersion>2.1.0</PackageVersion>
<PackageVersion>2.2.0</PackageVersion>
<IsPackable>false</IsPackable>
<UserSecretsId>4dbe0d9e-0d79-4e71-b5dd-44b90dbe3ee9</UserSecretsId>
</PropertyGroup>
Expand Down
38 changes: 11 additions & 27 deletions Test/Webhooks/EventTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Threading.Tasks;
using PayPalCheckoutSdk.Orders;
Expand Down Expand Up @@ -65,7 +66,7 @@ public async Task TestSimulateEvent()
var simulateResult = simulateResponse.Result<Event>();
Assert.Equal(HttpStatusCode.Accepted, simulateResponse.StatusCode);

Capture? capture = simulateResult.GetResource<Capture>();
Capture capture = simulateResult.GetResource<Capture>();
Assert.NotNull(capture);
Assert.False(string.IsNullOrWhiteSpace(capture.Id));
}
Expand Down Expand Up @@ -138,34 +139,10 @@ public async Task TestResendNegative()
Assert.Equal(HttpStatusCode.NoContent, deleteResponse.StatusCode);
}

}

[Fact]
public async Task TestVerifySignatureEventNegative()
{
VerifyWebhookSignatureRequest simulateRequest = new VerifyWebhookSignatureRequest();

var verifySignature = new VerifyWebhookSignature()
{
CertUrl = "https://api.sandbox.paypal.com/v1/notifications/certs/CERT-360caa42-fca2a594-2d7ab011",
AuthAlgo = "SHA256withRSA",
TransmissionId = "d0a19f40-e46b-11ed-af6d-5d1995803275",
TransmissionSig = "Tg9131sVOAPVYn5XjQsR8C/tcOWuPkc//VkifmPX7TZD24\u002BkuRLIZ\u002BzfbMxkeuS0er1EzLHw4MRG83xkYoEGLe9QWD4nfvg/HIAvXDNgEZCG2BrPIwPaiFrA9G0SX22\u002BvpOiy4\u002BbWNrKAFZdt/gobEairdaqVe1unsCxCJQT6czTFiOBBAn85yDCSIhctk6RbEkprxjwTrgLDf1Cq41AgxZ72RwVuZlJHbMQF5Dl/cRQ9pU38I0HOq0DRXiiaJwrp7UJXkLRdu3ge4ivN3Th1Wq8D\u002BL/0xYrub9lFB0TKI2a7XBKlaua9aT7XrtuwZeI1cNz/jr0luz7K6JYdiZSlQ==",
TransmissionTime = "2023-04-26T19:52:03Z",
WebhookId = "3RY87287BU229431J",
WebhookEvent = new Event() { Id = Guid.NewGuid().ToString(), EventType = "PAYMENT.CAPTURE.COMPLETED" }
};
simulateRequest.RequestBody(verifySignature);

var createResponse = await TestHarness.client().Execute(simulateRequest);
var createResult = createResponse.Result<VerifyWebhookSignatureResponse>();
Assert.Equal(HttpStatusCode.OK, createResponse.StatusCode);

Assert.False(createResult.ValidSignature);
}
}

[Fact]
public async Task TestVerifySignatureEventCert()
public async Task TestVerifySignatureEventCertNegative()
{
var verifySignature = new VerifyWebhookSignature()
{
Expand All @@ -178,7 +155,14 @@ public async Task TestVerifySignatureEventCert()
WebhookEventRequestBody = "{\r\n \"create_time\": \"2023-04-26T19:51:57.324Z\",\r\n \"event_type\": \"BILLING.SUBSCRIPTION.ACTIVATED\",\r\n \"event_version\": \"1.0\",\r\n \"id\": \"WH-2GA799794C526164B-4HC631101W386110B\",\r\n \"links\": [\r\n {\r\n \"encType\": null,\r\n \"href\": \"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2GA799794C526164B-4HC631101W386110B\",\r\n \"mediaType\": null,\r\n \"method\": \"GET\",\r\n \"rel\": \"self\",\r\n \"title\": null\r\n },\r\n {\r\n \"encType\": null,\r\n \"href\": \"https://api.sandbox.paypal.com/v1/notifications/webhooks-events/WH-2GA799794C526164B-4HC631101W386110B/resend\",\r\n \"mediaType\": null,\r\n \"method\": \"POST\",\r\n \"rel\": \"resend\",\r\n \"title\": null\r\n }\r\n ],\r\n \"resource\": {\r\n \"quantity\": \"1\",\r\n \"subscriber\": {\r\n \"email_address\": \"[email protected]\",\r\n \"payer_id\": \"P2DZ2LMXC5SY2\",\r\n \"name\": {\r\n \"given_name\": \"test\",\r\n \"surname\": \"buyer\"\r\n },\r\n \"shipping_address\": {\r\n \"address\": {\r\n \"address_line_1\": \"1 Main St\",\r\n \"admin_area_2\": \"San Jose\",\r\n \"admin_area_1\": \"CA\",\r\n \"postal_code\": \"95131\",\r\n \"country_code\": \"US\"\r\n }\r\n }\r\n },\r\n \"create_time\": \"2023-04-26T19:51:53Z\",\r\n \"plan_overridden\": false,\r\n \"shipping_amount\": {\r\n \"currency_code\": \"USD\",\r\n \"value\": \"0.0\"\r\n },\r\n \"start_time\": \"2023-04-26T19:51:38Z\",\r\n \"update_time\": \"2023-04-26T19:51:53Z\",\r\n \"billing_info\": {\r\n \"outstanding_balance\": {\r\n \"currency_code\": \"USD\",\r\n \"value\": \"0.0\"\r\n },\r\n \"cycle_executions\": [\r\n {\r\n \"tenure_type\": \"TRIAL\",\r\n \"sequence\": 1,\r\n \"cycles_completed\": 1,\r\n \"cycles_remaining\": 0,\r\n \"current_pricing_scheme_version\": 1,\r\n \"total_cycles\": 1\r\n },\r\n {\r\n \"tenure_type\": \"REGULAR\",\r\n \"sequence\": 2,\r\n \"cycles_completed\": 0,\r\n \"cycles_remaining\": 0,\r\n \"current_pricing_scheme_version\": 1,\r\n \"total_cycles\": 0\r\n }\r\n ],\r\n \"next_billing_time\": \"2023-05-10T10:00:00Z\",\r\n \"failed_payments_count\": 0\r\n },\r\n \"links\": [\r\n {\r\n \"href\": \"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-B9YUUVSYXFX3/cancel\",\r\n \"rel\": \"cancel\",\r\n \"method\": \"POST\",\r\n \"encType\": \"application/json\"\r\n },\r\n {\r\n \"href\": \"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-B9YUUVSYXFX3\",\r\n \"rel\": \"edit\",\r\n \"method\": \"PATCH\",\r\n \"encType\": \"application/json\"\r\n },\r\n {\r\n \"href\": \"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-B9YUUVSYXFX3\",\r\n \"rel\": \"self\",\r\n \"method\": \"GET\",\r\n \"encType\": \"application/json\"\r\n },\r\n {\r\n \"href\": \"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-B9YUUVSYXFX3/suspend\",\r\n \"rel\": \"suspend\",\r\n \"method\": \"POST\",\r\n \"encType\": \"application/json\"\r\n },\r\n {\r\n \"href\": \"https://api.sandbox.paypal.com/v1/billing/subscriptions/I-B9YUUVSYXFX3/capture\",\r\n \"rel\": \"capture\",\r\n \"method\": \"POST\",\r\n \"encType\": \"application/json\"\r\n }\r\n ],\r\n \"id\": \"I-B9YUUVSYXFX3\",\r\n \"plan_id\": \"P-11V96330AW144370PMBGAOEA\",\r\n \"status\": \"ACTIVE\",\r\n \"status_update_time\": \"2023-04-26T19:51:53Z\"\r\n },\r\n \"resource_type\": \"subscription\",\r\n \"resource_version\": \"2.0\",\r\n \"summary\": \"Subscription activated\"\r\n }",
};

Stopwatch stopwatch = Stopwatch.StartNew();
bool verified = await VerifyWebhookEvent.ValidateReceivedEventAsync(verifySignature);
stopwatch.Stop();
_output.WriteLine($"1st ValidateReceivedEventAsync {stopwatch.Elapsed.TotalMilliseconds} ms");
stopwatch.Restart();
verified = await VerifyWebhookEvent.ValidateReceivedEventAsync(verifySignature);
stopwatch.Stop();
_output.WriteLine($"2nd ValidateReceivedEventAsync {stopwatch.Elapsed.TotalMilliseconds} ms");
//
// Unfortunately, we it is too difficult to setup a positive test.
// We are mainly checking for any exceptions during validation
Expand Down

0 comments on commit 4f771fa

Please sign in to comment.