Skip to content

Commit 76f3198

Browse files
Merge pull request #653 from nofrixion/bugfix/MOOV-4853-webhook-secret
Added WebhookUpdate model
2 parents 374c10e + 0eebbcb commit 76f3198

File tree

1 file changed

+164
-0
lines changed

1 file changed

+164
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//-----------------------------------------------------------------------------
2+
// Filename: WebhookUpdate.cs
3+
//
4+
// Description: Model for updating a merchant webhook.
5+
//
6+
// Author(s):
7+
// Donal O'Connor ([email protected])
8+
//
9+
// History:
10+
// 19 09 2025 Donal O'Connor Created, Harcourt St, Dublin, Ireland.
11+
//
12+
// License:
13+
// MIT.
14+
//-----------------------------------------------------------------------------
15+
16+
using NoFrixion.MoneyMoov.Attributes;
17+
using NoFrixion.MoneyMoov.Enums;
18+
using System.ComponentModel.DataAnnotations;
19+
20+
namespace NoFrixion.MoneyMoov.Models;
21+
22+
public class WebhookUpdate : IValidatableObject
23+
{
24+
public const int SECRET_MAX_LENGTH = 32;
25+
26+
public Guid ID { get; set; }
27+
28+
[Required]
29+
public Guid MerchantID { get; set; }
30+
31+
[Obsolete("This field has been deprecated. Please use ResourceTypes instead.")]
32+
public WebhookResourceTypesEnum Type
33+
{
34+
get =>
35+
ResourceTypes.Any() ? ResourceTypes.ToFlagEnum() : WebhookResourceTypesEnum.None;
36+
37+
init
38+
{
39+
if (value == WebhookResourceTypesEnum.None)
40+
{
41+
ResourceTypes.Clear();
42+
}
43+
else
44+
{
45+
ResourceTypes = value.ToList();
46+
}
47+
}
48+
}
49+
50+
/// <summary>
51+
/// The resource types that the webhook should be generated for.
52+
/// </summary>
53+
public List<WebhookResourceTypesEnum> ResourceTypes { get; set; } = new List<WebhookResourceTypesEnum>();
54+
55+
/// <summary>
56+
/// The destination URL for the webhook.
57+
/// Required for webhook notifications.
58+
/// </summary>
59+
public string? DestinationUrl { get; set; }
60+
61+
public bool Retry { get; set; } = true;
62+
63+
/// <summary>
64+
/// The secret key required to authenticate webhook notifications.
65+
/// Required for webhook notifications.
66+
/// </summary>
67+
public string? Secret { get; set; }
68+
69+
public bool IsActive { get; set; } = true;
70+
71+
/// <summary>
72+
/// The recipient email address(es) for notifications. Multiple addresses can be separated by a comma, semicolon, or space.
73+
/// Reruired for email notifications.
74+
/// </summary>
75+
[EmailAddressMultiple(ErrorMessage = "One or more of the email addresses are invalid. Addresses can be separated by a comma, semi-colon or space.")]
76+
public string? EmailAddress { get; set; }
77+
78+
/// <summary>
79+
/// The email address to which notifications about failed webhook deliveries will be sent.
80+
/// </summary>
81+
[EmailAddressMultiple(ErrorMessage = "One or more of the email addresses are invalid. Addresses can be separated by a comma, semi-colon or space.")]
82+
public string? FailedNotificationEmailAddress { get; set; }
83+
84+
/// <summary>
85+
/// Determines the delivery method for the notification. The default is Webhook.
86+
/// </summary>
87+
/// <remarks>
88+
/// This property dictates which other fields are required:
89+
/// <list type="bullet">
90+
/// <item>
91+
/// <term><b>Email</b></term>
92+
/// <description>Sends a notification to a specified email inbox. Requires the <c>EmailAddress</c> property to be set.</description>
93+
/// </item>
94+
/// <item>
95+
/// <term><b>Webhook</b></term>
96+
/// <description>Sends a notification via an HTTP POST request to a specified endpoint. Requires the <c>DestinationUrl</c> and <c>Secret</c> properties to be set.</description>
97+
/// </item>
98+
/// </list>
99+
/// </remarks>
100+
[Required]
101+
public NotificationMethodTypesEnum NotificationMethod { get; set; } = NotificationMethodTypesEnum.Webhook;
102+
103+
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
104+
{
105+
if (Secret?.Length > SECRET_MAX_LENGTH)
106+
{
107+
yield return new ValidationResult($"The Secret string was too long. The Secret maximum length is {SECRET_MAX_LENGTH} characters.");
108+
}
109+
110+
if (ResourceTypes == null || ResourceTypes.Count() == 0)
111+
{
112+
yield return new ValidationResult("Cannot create a webhook with an empty resource type list. Please specify at least one resource type.");
113+
}
114+
115+
if (ResourceTypes?.Contains(WebhookResourceTypesEnum.None) == true)
116+
{
117+
yield return new ValidationResult("Cannot create a webhook with a resource type of none.");
118+
}
119+
120+
if (NotificationMethod is NotificationMethodTypesEnum.None)
121+
{
122+
yield return new ValidationResult("Cannot create a webhook with a notification method type of none.");
123+
}
124+
125+
if (NotificationMethod is NotificationMethodTypesEnum.Email
126+
&& string.IsNullOrWhiteSpace(EmailAddress))
127+
{
128+
yield return new ValidationResult("Email address is required for email notification method.");
129+
}
130+
131+
if (NotificationMethod is NotificationMethodTypesEnum.Webhook && string.IsNullOrWhiteSpace(DestinationUrl))
132+
{
133+
yield return new ValidationResult("Destination URL is required for webhook notification method.");
134+
}
135+
}
136+
137+
public Dictionary<string, string> ToDictionary()
138+
{
139+
var dict = new Dictionary<string, string>
140+
{
141+
{ nameof(ID), ID.ToString() },
142+
{ nameof(MerchantID), MerchantID.ToString() },
143+
{ nameof(DestinationUrl), DestinationUrl ?? string.Empty },
144+
{ nameof(Retry), Retry.ToString() },
145+
{ nameof(Secret), Secret ?? string.Empty },
146+
{ nameof(IsActive), IsActive.ToString() },
147+
{ nameof(EmailAddress), EmailAddress ?? string.Empty },
148+
{ nameof(FailedNotificationEmailAddress), FailedNotificationEmailAddress ?? string.Empty },
149+
{ nameof(NotificationMethod), NotificationMethod.ToString() }
150+
};
151+
152+
if (ResourceTypes?.Count() > 0)
153+
{
154+
int resourceTypeNumber = 0;
155+
foreach (var resourceType in ResourceTypes)
156+
{
157+
dict.Add($"{nameof(ResourceTypes)}[{resourceTypeNumber}]", resourceType.ToString());
158+
resourceTypeNumber++;
159+
}
160+
}
161+
162+
return dict;
163+
}
164+
}

0 commit comments

Comments
 (0)