diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 00000000..828793bb --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,18 @@ + + + + + + + True + True + Resources.resx + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 00000000..1fe41e41 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,34 @@ + + + + + + true + true + Tingle Software Limited + + + + + + true + + + true + + + true + snupkg + + MIT + + + + $(GITVERSION_NUGETVERSION) + + + + + + + diff --git a/src/Falu/Core/BasicListOptions.cs b/src/Falu/Core/BasicListOptions.cs index 84e971ae..37498f8d 100644 --- a/src/Falu/Core/BasicListOptions.cs +++ b/src/Falu/Core/BasicListOptions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.Serialization; namespace Falu.Core { @@ -58,19 +57,7 @@ internal virtual IDictionary PopulateQueryValues(IDictionary d.ToString("o"); internal static string ConvertInt32(int i) => i.ToString(); internal static string ConvertInt64(long i) => i.ToString(); - internal static string ConvertEnum(T e) where T : Enum - { - // Give priority to EnumMemberAttribute - var memInfo = typeof(T).GetMember(e.ToString()); - var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false) - .OfType() - .FirstOrDefault(); - - return attr?.Value ?? e.ToString().ToLowerInvariant(); - } - internal static string ConvertEnumList(IList list) where T : Enum - { - return string.Join(",", list.Select(l => ConvertEnum(l))); - } + internal static string ConvertEnum(T e) where T : Enum => e.GetEnumMemberAttrValueOrDefault(); + internal static string ConvertEnumList(IList list) where T : Enum => string.Join(",", list.Select(l => ConvertEnum(l))); } } diff --git a/src/Falu/Core/IHasTags.cs b/src/Falu/Core/IHasTags.cs index 3f1a501a..5a2c8b4b 100644 --- a/src/Falu/Core/IHasTags.cs +++ b/src/Falu/Core/IHasTags.cs @@ -10,6 +10,7 @@ public interface IHasTags /// /// Set of values that you can attach to an object. This can be useful for searching. /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/Evaluations/EvaluationPatchModel.cs b/src/Falu/Evaluations/EvaluationPatchModel.cs index b7bb4bdc..47036ff1 100644 --- a/src/Falu/Evaluations/EvaluationPatchModel.cs +++ b/src/Falu/Evaluations/EvaluationPatchModel.cs @@ -15,6 +15,7 @@ public class EvaluationPatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/Evaluations/EvaluationsService.cs b/src/Falu/Evaluations/EvaluationsService.cs index 86462aa4..29053910 100644 --- a/src/Falu/Evaluations/EvaluationsService.cs +++ b/src/Falu/Evaluations/EvaluationsService.cs @@ -84,36 +84,54 @@ public virtual async Task> CreateAsync(EvaluationCr CancellationToken cancellationToken = default) { if (evaluation is null) throw new ArgumentNullException(nameof(evaluation)); + if (evaluation.Scope is null) throw new InvalidOperationException($"{nameof(evaluation.Scope)} cannot be null."); + if (evaluation.Provider is null) throw new InvalidOperationException($"{nameof(evaluation.Provider)} cannot be null."); + if (string.IsNullOrWhiteSpace(evaluation.Name)) + { + throw new InvalidOperationException($"{nameof(evaluation.Name)} cannot be null or whitespace."); + } var content = new MultipartFormDataContent { // populate fields of the model as key value pairs - { new StringContent(evaluation.Currency), nameof(evaluation.Currency) }, - { new StringContent(evaluation.Scope.ToString()), nameof(evaluation.Scope) }, - { new StringContent(evaluation.Provider.ToString()), nameof(evaluation.Provider) }, - { new StringContent(evaluation.Name), nameof(evaluation.Name) }, - { new StringContent(evaluation.Phone), nameof(evaluation.Phone) }, - { new StringContent(evaluation.Password), nameof(evaluation.Password) }, + { new StringContent(evaluation.Currency), "currency" }, + { new StringContent(evaluation.Scope?.GetEnumMemberAttrValueOrDefault()), "scope" }, + { new StringContent(evaluation.Provider?.GetEnumMemberAttrValueOrDefault()), "provider" }, + { new StringContent(evaluation.Name), "name" }, // populate the file stream - { new StreamContent(evaluation.Content), "File", evaluation.FileName }, + { new StreamContent(evaluation.Content), "file", evaluation.FileName }, }; + // Add phone if provided + if (!string.IsNullOrWhiteSpace(evaluation.Phone)) + { + content.Add(new StringContent(evaluation.Phone), "phone"); + } + + // Add password if provided + if (!string.IsNullOrWhiteSpace(evaluation.Password)) + { + content.Add(new StringContent(evaluation.Password), "password"); + } + // Add description if provided if (!string.IsNullOrWhiteSpace(evaluation.Description)) { - content.Add(new StringContent(evaluation.Description), nameof(evaluation.Description)); + content.Add(new StringContent(evaluation.Description), "description"); } // Add tags if provided +#pragma warning disable CS0618 // Type or member is obsolete var tags = evaluation.Tags; if (tags != null) { for (var i = 0; i < tags.Count; i++) { - content.Add(new StringContent(tags[i]), $"{nameof(evaluation.Tags)}[{i}]"); + content.Add(new StringContent(tags[i]), $"tags[{i}]"); } } +#pragma warning restore CS0618 // Type or member is obsolete // Add metadata if provided var metadata = evaluation.Metadata?.ToList(); @@ -121,8 +139,8 @@ public virtual async Task> CreateAsync(EvaluationCr { for (var i = 0; i < metadata.Count; i++) { - content.Add(new StringContent(metadata[i].Key), $"{nameof(evaluation.Metadata)}[{i}].Key"); - content.Add(new StringContent(metadata[i].Value), $"{nameof(evaluation.Metadata)}[{i}].Value"); + content.Add(new StringContent(metadata[i].Key), $"metadata[{i}].Key"); + content.Add(new StringContent(metadata[i].Value), $"metadata[{i}].Value"); } } diff --git a/src/Falu/Events/WebhookEvent.cs b/src/Falu/Events/WebhookEvent.cs index 766b39ad..5e30009e 100644 --- a/src/Falu/Events/WebhookEvent.cs +++ b/src/Falu/Events/WebhookEvent.cs @@ -16,7 +16,7 @@ public class WebhookEvent : IHasId, IHasCreated, IHasMetadata, IHasWork public DateTimeOffset Created { get; set; } /// - /// Type of event (e.g. payment.updated, balance.updated, etc.). + /// Type of event (e.g. payment.updated, payment_balances.updated, etc.). /// Possible values are available in . /// public string? Type { get; set; } diff --git a/src/Falu/Events/WebhookEventData.cs b/src/Falu/Events/WebhookEventData.cs index d9451432..18b41db0 100644 --- a/src/Falu/Events/WebhookEventData.cs +++ b/src/Falu/Events/WebhookEventData.cs @@ -7,7 +7,7 @@ public class WebhookEventData { /// /// Object containing the API resource relevant to the event. - /// For example, a balance.updated event will have a full balance object. + /// For example, a payment_balances.updated event will have a full balance object. /// public TObject? Object { get; set; } diff --git a/src/Falu/Extensions/EnumExtensions.cs b/src/Falu/Extensions/EnumExtensions.cs new file mode 100644 index 00000000..47ff406e --- /dev/null +++ b/src/Falu/Extensions/EnumExtensions.cs @@ -0,0 +1,18 @@ +using System.Linq; +using System.Runtime.Serialization; + +namespace System.Collections.Generic +{ + internal static class EnumExtensions + { + public static string GetEnumMemberAttrValueOrDefault(this T enumVal) where T : Enum + { + var memInfo = typeof(T).GetMember(enumVal.ToString()); + var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false) + .OfType() + .FirstOrDefault(); + + return attr?.Value ?? enumVal.ToString().ToLowerInvariant(); + } + } +} diff --git a/src/Falu/Falu.csproj b/src/Falu/Falu.csproj index e6451b3e..63e194b6 100644 --- a/src/Falu/Falu.csproj +++ b/src/Falu/Falu.csproj @@ -2,12 +2,7 @@ netstandard2.1 - true - true - The official client library for Falu. (https://falu.io) - Tingle Software Limited - Tingle Software Limited @@ -19,26 +14,4 @@ - - - true - - - true - - - true - snupkg - - MIT - - - - $(GITVERSION_NUGETVERSION) - - - - - - diff --git a/src/Falu/MessageStreams/MessageStreamPatchModel.cs b/src/Falu/MessageStreams/MessageStreamPatchModel.cs index 02c1acaf..4b90b22f 100644 --- a/src/Falu/MessageStreams/MessageStreamPatchModel.cs +++ b/src/Falu/MessageStreams/MessageStreamPatchModel.cs @@ -20,6 +20,7 @@ public class MessageStreamPatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/MessageStrings.cs b/src/Falu/MessageStrings.cs new file mode 100644 index 00000000..fce183f8 --- /dev/null +++ b/src/Falu/MessageStrings.cs @@ -0,0 +1,7 @@ +namespace Falu +{ + internal class MessageStrings + { + public const string TagsDeprecated = "Tags have been deprecated and scheduled to be removed in a future API update. Migrate to using Metadata."; + } +} diff --git a/src/Falu/MessageTemplates/MessageTemplatePatchModel.cs b/src/Falu/MessageTemplates/MessageTemplatePatchModel.cs index 1588b6ad..4b51fcd1 100644 --- a/src/Falu/MessageTemplates/MessageTemplatePatchModel.cs +++ b/src/Falu/MessageTemplates/MessageTemplatePatchModel.cs @@ -28,6 +28,7 @@ public class MessageTemplatePatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/Messages/MessagePatchModel.cs b/src/Falu/Messages/MessagePatchModel.cs index 620e9ded..958429b9 100644 --- a/src/Falu/Messages/MessagePatchModel.cs +++ b/src/Falu/Messages/MessagePatchModel.cs @@ -12,6 +12,7 @@ public class MessagePatchModel : IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/PaymentAuthorizations/PaymentAuthorization.cs b/src/Falu/PaymentAuthorizations/PaymentAuthorization.cs index defab835..095f504b 100644 --- a/src/Falu/PaymentAuthorizations/PaymentAuthorization.cs +++ b/src/Falu/PaymentAuthorizations/PaymentAuthorization.cs @@ -48,6 +48,11 @@ public class PaymentAuthorization : PaymentAuthorizationPatchModel, IHasId, IHas /// public PaymentMpesaDetails? Mpesa { get; set; } + /// + /// Identifier of the payment created after the authorization is approved and closed. + /// + public string? PaymentId { get; set; } + /// public string? WorkspaceId { get; set; } diff --git a/src/Falu/PaymentAuthorizations/PaymentAuthorizationPatchModel.cs b/src/Falu/PaymentAuthorizations/PaymentAuthorizationPatchModel.cs index b7d9f158..e51b2bf9 100644 --- a/src/Falu/PaymentAuthorizations/PaymentAuthorizationPatchModel.cs +++ b/src/Falu/PaymentAuthorizations/PaymentAuthorizationPatchModel.cs @@ -12,6 +12,7 @@ public class PaymentAuthorizationPatchModel : IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/PaymentReversals/PaymentReversalFailureReason.cs b/src/Falu/PaymentReversals/PaymentReversalFailureReason.cs index 7cb3ea83..a0011ffe 100644 --- a/src/Falu/PaymentReversals/PaymentReversalFailureReason.cs +++ b/src/Falu/PaymentReversals/PaymentReversalFailureReason.cs @@ -18,6 +18,10 @@ public enum PaymentReversalFailureReason [EnumMember(Value = "authentication_error")] AuthenticationError, + /// + [EnumMember(Value = "amount_out_of_bound")] + AmountOutOfBound, + /// Timeout, diff --git a/src/Falu/PaymentReversals/PaymentReversalPatchModel.cs b/src/Falu/PaymentReversals/PaymentReversalPatchModel.cs index 75196761..b502c5ba 100644 --- a/src/Falu/PaymentReversals/PaymentReversalPatchModel.cs +++ b/src/Falu/PaymentReversals/PaymentReversalPatchModel.cs @@ -15,6 +15,7 @@ public class PaymentReversalPatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/Payments/Payment.cs b/src/Falu/Payments/Payment.cs index 7d378d16..0ff326f0 100644 --- a/src/Falu/Payments/Payment.cs +++ b/src/Falu/Payments/Payment.cs @@ -35,6 +35,11 @@ public class Payment : PaymentPatchModel, IHasId, IHasCurrency, IHasCreated, IHa /// public DateTimeOffset? Succeeded { get; set; } + /// + /// Identifier of the authorization, if the payment passed through a flow requiring authorization. + /// + public string? AuthorizationId { get; set; } + /// /// The type of the Payment. /// An additional property is populated on the Payment with a name matching this value. diff --git a/src/Falu/Payments/PaymentFailureReason.cs b/src/Falu/Payments/PaymentFailureReason.cs index d2596021..03cb3b40 100644 --- a/src/Falu/Payments/PaymentFailureReason.cs +++ b/src/Falu/Payments/PaymentFailureReason.cs @@ -18,6 +18,10 @@ public enum PaymentFailureReason [EnumMember(Value = "authentication_error")] AuthenticationError, + /// + [EnumMember(Value = "amount_out_of_bound")] + AmountOutOfBound, + /// Timeout, diff --git a/src/Falu/Payments/PaymentPatchModel.cs b/src/Falu/Payments/PaymentPatchModel.cs index bb69b688..34363cab 100644 --- a/src/Falu/Payments/PaymentPatchModel.cs +++ b/src/Falu/Payments/PaymentPatchModel.cs @@ -15,6 +15,7 @@ public class PaymentPatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/TransferReversals/TransferReversalFailureReason.cs b/src/Falu/TransferReversals/TransferReversalFailureReason.cs index 9ac75ebb..6194826d 100644 --- a/src/Falu/TransferReversals/TransferReversalFailureReason.cs +++ b/src/Falu/TransferReversals/TransferReversalFailureReason.cs @@ -18,6 +18,10 @@ public enum TransferReversalFailureReason [EnumMember(Value = "authentication_error")] AuthenticationError, + /// + [EnumMember(Value = "amount_out_of_bound")] + AmountOutOfBound, + /// Timeout, diff --git a/src/Falu/TransferReversals/TransferReversalPatchModel.cs b/src/Falu/TransferReversals/TransferReversalPatchModel.cs index 643fb063..5aad3412 100644 --- a/src/Falu/TransferReversals/TransferReversalPatchModel.cs +++ b/src/Falu/TransferReversals/TransferReversalPatchModel.cs @@ -15,6 +15,7 @@ public class TransferReversalPatchModel : IHasDescription, IHasMetadata, IHasTag public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/Transfers/TransferFailureReason.cs b/src/Falu/Transfers/TransferFailureReason.cs index f3e548e9..7bd0dc3f 100644 --- a/src/Falu/Transfers/TransferFailureReason.cs +++ b/src/Falu/Transfers/TransferFailureReason.cs @@ -18,6 +18,10 @@ public enum TransferFailureReason [EnumMember(Value = "authentication_error")] AuthenticationError, + /// + [EnumMember(Value = "amount_out_of_bound")] + AmountOutOfBound, + /// Timeout, diff --git a/src/Falu/Transfers/TransferPatchModel.cs b/src/Falu/Transfers/TransferPatchModel.cs index db0ee20d..a768e6ba 100644 --- a/src/Falu/Transfers/TransferPatchModel.cs +++ b/src/Falu/Transfers/TransferPatchModel.cs @@ -15,6 +15,7 @@ public class TransferPatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } } diff --git a/src/Falu/Webhooks/EventTypes.cs b/src/Falu/Webhooks/EventTypes.cs index 979e8a77..2d373cb4 100644 --- a/src/Falu/Webhooks/EventTypes.cs +++ b/src/Falu/Webhooks/EventTypes.cs @@ -25,9 +25,9 @@ public static class EventTypes #region Payments /// - /// Occurs whenever the balance has been updated. + /// Occurs whenever a payment balances are updated. /// - public const string BalanceUpdated = "balance.updated"; + public const string PaymentBalancesUpdated = "payment_balances.updated"; /// /// Occurs whenever a payment is updated. diff --git a/src/Falu/Webhooks/WebhookEndpointPatchModel.cs b/src/Falu/Webhooks/WebhookEndpointPatchModel.cs index 5a51b562..e493f6a5 100644 --- a/src/Falu/Webhooks/WebhookEndpointPatchModel.cs +++ b/src/Falu/Webhooks/WebhookEndpointPatchModel.cs @@ -36,6 +36,7 @@ public class WebhookEndpointPatchModel : IHasDescription, IHasMetadata, IHasTags public Dictionary? Metadata { get; set; } /// + [System.Obsolete(MessageStrings.TagsDeprecated)] public List? Tags { get; set; } } }