diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardControlsResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardControlsResponse.cs
index 16f33946..edd4381a 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardControlsResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardControlsResponse.cs
@@ -1,24 +1,68 @@
using Checkout.Issuing.Common;
+using System;
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// A control that was set on the card when the virtual card was created, discriminated on control_type.
+ ///
public abstract class AbstractCardControlsResponse
{
+ ///
+ /// The control's type.
+ /// A velocity_limit determines how much can be spent over a given period of time. An mcc_limit determines
+ /// the types of businesses from which transactions can be processed. A mid_limit specifies the merchants
+ /// from whom transactions can be processed.
+ /// [Required]
+ ///
public IssuingControlType? ControlType { get; set; }
+ ///
+ /// The control's unique identifier.
+ /// [Required]
+ /// ^ctr_[a-z0-9]{26}$
+ /// 30 characters
+ ///
public string Id { get; set; }
+ ///
+ /// The ID of the card the control applies to.
+ /// Not returned by the API in this response; still deserialized if present, otherwise null.
+ /// [Optional]
+ ///
+ [Obsolete("Not returned by the API in this response. Will be removed in the next major version.")]
public string TargetId { get; set; }
+ ///
+ /// A description for the control.
+ /// [Required]
+ /// max 256 characters
+ ///
public string Description { get; set; }
+ ///
+ /// The date and time the control was created.
+ /// Not returned by the API in this response; still deserialized if present, otherwise null.
+ /// [Optional]
+ ///
+ [Obsolete("Not returned by the API in this response. Will be removed in the next major version.")]
public string CreatedDate { get; set; }
+ ///
+ /// The date and time the control was last modified.
+ /// Not returned by the API in this response; still deserialized if present, otherwise null.
+ /// [Optional]
+ ///
+ [Obsolete("Not returned by the API in this response. Will be removed in the next major version.")]
public string LastModifiedDate { get; set; }
+ ///
+ /// Initializes the control with its discriminator value.
+ ///
+ /// The control's type.
protected AbstractCardControlsResponse(IssuingControlType controlType)
{
ControlType = controlType;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardCreateResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardCreateResponse.cs
index d46359d6..121ead0d 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardCreateResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/AbstractCardCreateResponse.cs
@@ -4,6 +4,10 @@
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// The response returned when a card is created, discriminated on type (virtual or physical).
+ /// _links (CardLinks: self, credentials, revoke, controls and more) is exposed through Links.
+ ///
public abstract class AbstractCardCreateResponse : Resource
{
///
@@ -16,7 +20,7 @@ public abstract class AbstractCardCreateResponse : Resource
/// The card's unique identifier.
/// [Required]
/// ^crd_[a-z0-9]{26}$
- /// min 30 characters, max 30 characters
+ /// 30 characters
///
public string Id { get; set; }
@@ -24,7 +28,7 @@ public abstract class AbstractCardCreateResponse : Resource
/// The client's unique identifier.
/// [Required]
/// ^cli_[a-z0-9]{26}$
- /// min 30 characters, max 30 characters
+ /// 30 characters
///
public string ClientId { get; set; }
@@ -32,7 +36,7 @@ public abstract class AbstractCardCreateResponse : Resource
/// The entity's unique identifier.
/// [Required]
/// ^ent_[a-z0-9]{26}$
- /// min 30 characters, max 30 characters
+ /// 30 characters
///
public string EntityId { get; set; }
@@ -48,62 +52,75 @@ public abstract class AbstractCardCreateResponse : Resource
/// The last four digits of the card number, also known as the PAN.
/// [Required]
/// ^[0-9]{4}$
+ /// 4 characters
///
public string LastFour { get; set; }
///
/// The card's expiration month.
/// [Required]
- /// >= 1
- /// <= 12
+ /// Format: int32
+ /// min 1, max 12
///
public int? ExpiryMonth { get; set; }
///
/// The card's expiration year.
/// [Required]
- /// min 4 characters, max 4 characters
+ /// Format: int32
+ /// 4 digits
///
public int? ExpiryYear { get; set; }
///
/// The issuing currency, as a three-letter ISO currency code.
/// [Required]
+ /// Format: ISO4217
/// ^[a-zA-Z]{3}$
- /// min 3 characters, max 3 characters
+ /// 3 characters
///
public Currency? BillingCurrency { get; set; }
///
/// The issuing country, as a two-letter ISO country code.
/// [Required]
- /// min 2 characters, max 2 characters
+ /// 2 characters
///
public CountryCode? IssuingCountry { get; set; }
///
- /// The date and time the card was created, in UTC.
- /// [Optional]
+ /// The card scheme.
+ /// [Required]
+ ///
+ public IssuingScheme? Scheme { get; set; }
+
+ ///
+ /// The date and time the card was created.
+ /// [Required]
/// Format: date-time (RFC 3339)
///
public DateTime? CreatedDate { get; set; }
///
/// The card's status, which determines whether it can approve incoming authorizations.
+ /// inactive: the default state, authorizations are declined until the card is activated.
+ /// active: authorization requests can be approved.
+ /// suspended: authorization requests are declined; the card can be reactivated.
+ /// revoked: authorization requests are permanently declined; the card cannot be reactivated.
/// [Required]
- /// Enum: "active" "inactive" "revoked" "suspended"
///
public CardStatus? Status { get; set; }
///
/// Your reference.
/// [Optional]
- /// <= 256 characters
+ /// max 256 characters
///
public string Reference { get; set; }
///
/// The card will be revoked at midnight UTC on the date specified.
+ /// Not listed in add-card-response, but sent on add-card-request and returned by get-card-response.
/// [Optional]
/// Format: yyyy-MM-dd
///
@@ -112,14 +129,19 @@ public abstract class AbstractCardCreateResponse : Resource
///
/// The date and time the card was last activated. If the card has never been activated, this field
/// returns null.
- /// [Optional, read-only]
+ /// [Optional]
/// Format: date-time (RFC 3339)
+ /// Nullable, read-only
///
public DateTime? LastActivatedOn { get; set; }
+ ///
+ /// Initializes the card response with its discriminator value.
+ ///
+ /// The card type.
protected AbstractCardCreateResponse(IssuingCardType? type)
{
Type = type;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/CardControlsResponseConverter.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/CardControlsResponseConverter.cs
index bbf1bec7..8a5734a4 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/CardControlsResponseConverter.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/CardControlsResponseConverter.cs
@@ -68,7 +68,9 @@ private static AbstractCardControlsResponse Create(string type)
private static string GetSourceType(JToken jObject)
{
- return jObject.SelectToken(CheckoutUtils.Type)?.Value()?.ToLowerInvariant();
+ // The spec discriminates card controls on control_type; type is kept as a fallback.
+ var token = jObject.SelectToken(CheckoutUtils.ControlType) ?? jObject.SelectToken(CheckoutUtils.Type);
+ return token?.Value()?.ToLowerInvariant();
}
}
}
\ No newline at end of file
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/Credentials.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/Credentials.cs
index 0979dd5b..2def7673 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/Credentials.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/Credentials.cs
@@ -1,9 +1,25 @@
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// The virtual card's credentials, returned when they were requested on creation.
+ ///
public class Credentials
{
+ ///
+ /// The unencrypted card number.
+ /// [Optional]
+ /// Format: pan
+ /// ^[0-9]{16}$
+ /// 16 characters
+ ///
public string Number { get; set; }
-
+
+ ///
+ /// The card's verification code, also known as the CVV or CVC2.
+ /// [Optional]
+ /// ^[0-9]{3}$
+ /// 3 characters
+ ///
public string Cvc2 { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/MccCardControlsResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/MccCardControlsResponse.cs
index ef5a76d7..394b8419 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/MccCardControlsResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/MccCardControlsResponse.cs
@@ -2,12 +2,23 @@
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// A card control of type mcc_limit set when the virtual card was created.
+ ///
public class MccCardControlsResponse : AbstractCardControlsResponse
{
+ ///
+ /// Initializes an mcc_limit control.
+ ///
public MccCardControlsResponse() : base(IssuingControlType.MccLimit)
{
}
+ ///
+ /// The merchant category code (MCC) rule, which determines the types of businesses transactions can be
+ /// processed from.
+ /// [Required]
+ ///
public MccLimit MccLimit { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/MidCardControlsResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/MidCardControlsResponse.cs
index 5d5ca910..61948814 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/MidCardControlsResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/MidCardControlsResponse.cs
@@ -1,13 +1,39 @@
using Checkout.Issuing.Common;
+using Newtonsoft.Json;
+using System;
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// A card control of type mid_limit set when the virtual card was created.
+ ///
public class MidCardControlsResponse : AbstractCardControlsResponse
{
+ ///
+ /// Initializes a mid_limit control.
+ ///
public MidCardControlsResponse() : base(IssuingControlType.MidLimit)
{
}
- public MidLimit VelocityLimit { get; set; }
+ ///
+ /// The merchant identification (MID) code rule, which determines the merchants from whom transactions can
+ /// be processed.
+ /// [Required]
+ ///
+ public MidLimit MidLimit { get; set; }
+
+ ///
+ /// Former name of . It was mapped to velocity_limit, so it was never populated.
+ /// It now reads and writes and is not serialized.
+ /// [Optional]
+ ///
+ [Obsolete("Use MidLimit instead. Will be removed in the next major version.")]
+ [JsonIgnore]
+ public MidLimit VelocityLimit
+ {
+ get => MidLimit;
+ set => MidLimit = value;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/PhysicalCardCreateResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/PhysicalCardCreateResponse.cs
index 9c4de4ac..0636b04e 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/PhysicalCardCreateResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/PhysicalCardCreateResponse.cs
@@ -2,10 +2,16 @@
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// The response returned when a physical card is created (add-physical-card-response).
+ ///
public class PhysicalCardCreateResponse : AbstractCardCreateResponse
{
+ ///
+ /// Initializes a physical card response.
+ ///
public PhysicalCardCreateResponse() : base(IssuingCardType.Physical)
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/VelocityCardControlsResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/VelocityCardControlsResponse.cs
index fab70e90..2624aed3 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/VelocityCardControlsResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/VelocityCardControlsResponse.cs
@@ -2,12 +2,22 @@
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// A card control of type velocity_limit set when the virtual card was created.
+ ///
public class VelocityCardControlsResponse : AbstractCardControlsResponse
{
+ ///
+ /// Initializes a velocity_limit control.
+ ///
public VelocityCardControlsResponse() : base(IssuingControlType.VelocityLimit)
{
}
+ ///
+ /// The velocity limit, which determines how much a target card can spend over a given timeframe.
+ /// [Required]
+ ///
public VelocityLimit VelocityLimit { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Cards/Responses/Create/VirtualCardCreateResponse.cs b/src/CheckoutSdk/Issuing/Cards/Responses/Create/VirtualCardCreateResponse.cs
index dd46aaad..80cec968 100644
--- a/src/CheckoutSdk/Issuing/Cards/Responses/Create/VirtualCardCreateResponse.cs
+++ b/src/CheckoutSdk/Issuing/Cards/Responses/Create/VirtualCardCreateResponse.cs
@@ -3,14 +3,28 @@
namespace Checkout.Issuing.Cards.Responses.Create
{
+ ///
+ /// The response returned when a virtual card is created (add-virtual-card-response).
+ ///
public class VirtualCardCreateResponse : AbstractCardCreateResponse
{
+ ///
+ /// Initializes a virtual card response.
+ ///
public VirtualCardCreateResponse() : base(IssuingCardType.Virtual)
{
}
-
+
+ ///
+ /// The card's credentials.
+ /// [Optional]
+ ///
public Credentials Credentials { get; set; }
+ ///
+ /// The controls that were set on the card.
+ /// [Optional]
+ ///
public IList Controls { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Common/MccLimit.cs b/src/CheckoutSdk/Issuing/Common/MccLimit.cs
index 461065e8..822f9621 100644
--- a/src/CheckoutSdk/Issuing/Common/MccLimit.cs
+++ b/src/CheckoutSdk/Issuing/Common/MccLimit.cs
@@ -2,10 +2,22 @@
namespace Checkout.Issuing.Common
{
+ ///
+ /// The merchant category code (MCC) rule, which determines the types of businesses transactions can be
+ /// processed from.
+ ///
public class MccLimit
{
+ ///
+ /// Sets whether to allow or block the list of MCCs supplied.
+ /// [Required]
+ ///
public LimitControlType? Type { get; set; }
+ ///
+ /// The list of MCCs to allow or block transactions from, as 4-digit ISO 18245 codes.
+ /// [Required]
+ ///
public IList MccList { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Common/MidLimit.cs b/src/CheckoutSdk/Issuing/Common/MidLimit.cs
index b2308147..b96d5038 100644
--- a/src/CheckoutSdk/Issuing/Common/MidLimit.cs
+++ b/src/CheckoutSdk/Issuing/Common/MidLimit.cs
@@ -2,10 +2,24 @@
namespace Checkout.Issuing.Common
{
+ ///
+ /// The merchant identification (MID) code rule, which determines the merchants from whom transactions can be
+ /// processed.
+ ///
public class MidLimit
{
+ ///
+ /// Sets whether to allow or block the list of MIDs supplied.
+ /// [Required]
+ ///
public LimitControlType? Type { get; set; }
+ ///
+ /// The list of merchant identification (MID) codes to allow or block transactions from.
+ /// [Required]
+ /// Each item: ^[A-Za-z0-9{}\[\] ,+\-=.();'\/&@*]{1,15}$
+ /// Each item: min 1 characters, max 15 characters
+ ///
public IList MidList { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Common/VelocityLimit.cs b/src/CheckoutSdk/Issuing/Common/VelocityLimit.cs
index 8ead32be..90f6fb7d 100644
--- a/src/CheckoutSdk/Issuing/Common/VelocityLimit.cs
+++ b/src/CheckoutSdk/Issuing/Common/VelocityLimit.cs
@@ -4,16 +4,26 @@ namespace Checkout.Issuing.Common
{
///
/// The velocity limit, which determines how much a target card can spend over a given timeframe.
- ///
+ ///
public class VelocityLimit
{
///
/// The amount the target can spend, in minor units.
- /// >= 0
/// [Required]
+ /// Format: int64
+ /// min 0
///
public long? AmountLimit { get; set; }
+ ///
+ /// The remaining amount that can be spent, in minor units.
+ /// Returned on get and update control responses only; leave it null on requests (null values are not sent).
+ /// [Optional]
+ /// Format: int64
+ /// min 0
+ ///
+ public long? AmountRemaining { get; set; }
+
///
/// The period of time over which the specified amount_limit can be spent.
/// [Required]
@@ -26,7 +36,7 @@ public class VelocityLimit
/// [Optional]
///
public IList MccList { get; set; }
-
+
///
/// The list of merchant identification (MID) codes to allow or block transactions from.
/// You can provide either mcc_list or mid_list, but not both.
@@ -34,4 +44,4 @@ public class VelocityLimit
///
public IList MidList { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Common/VelocityWindow.cs b/src/CheckoutSdk/Issuing/Common/VelocityWindow.cs
index b0792598..e20d2428 100644
--- a/src/CheckoutSdk/Issuing/Common/VelocityWindow.cs
+++ b/src/CheckoutSdk/Issuing/Common/VelocityWindow.cs
@@ -1,7 +1,14 @@
namespace Checkout.Issuing.Common
{
+ ///
+ /// The period of time over which the specified amount_limit can be spent.
+ ///
public class VelocityWindow
{
+ ///
+ /// The velocity window's unit of time.
+ /// [Required]
+ ///
public VelocityWindowType? Type { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Controls/Responses/Create/AbstractCardControlResponse.cs b/src/CheckoutSdk/Issuing/Controls/Responses/Create/AbstractCardControlResponse.cs
index 1739fdf7..a409bbe2 100644
--- a/src/CheckoutSdk/Issuing/Controls/Responses/Create/AbstractCardControlResponse.cs
+++ b/src/CheckoutSdk/Issuing/Controls/Responses/Create/AbstractCardControlResponse.cs
@@ -1,26 +1,77 @@
+using Checkout.Common;
using Checkout.Issuing.Common;
namespace Checkout.Issuing.Controls.Responses.Create
{
- public abstract class AbstractCardControlResponse : HttpMetadata
+ ///
+ /// A card control returned by the create, get, list and update control operations,
+ /// discriminated on control_type.
+ /// Links (_links, ControlLinks with the self link to the control's details) are returned by the create
+ /// operation only; get, list and update leave Links empty.
+ ///
+ public abstract class AbstractCardControlResponse : Resource
{
+ ///
+ /// The control's type.
+ /// A velocity_limit determines how much can be spent over a given period of time. An mcc_limit determines
+ /// the types of businesses from which transactions can be processed. A mid_limit specifies the merchants
+ /// from whom transactions can be processed.
+ /// [Required]
+ ///
public IssuingControlType? ControlType { get; set; }
+ ///
+ /// The control's unique identifier.
+ /// [Required]
+ /// ^ctr_[a-z0-9]{26}$
+ /// 30 characters
+ ///
public string Id { get; set; }
+ ///
+ /// The ID of the card or control profile.
+ /// [Required]
+ /// ^(crd|cpr)_[a-z0-9]{26}$
+ /// 30 characters
+ ///
public string TargetId { get; set; }
-
+
+ ///
+ /// Indicates whether you can change this control.
+ /// false: an immutable control applied by Checkout.com that you cannot change.
+ /// true: you applied this control and can change it.
+ /// [Required]
+ ///
public bool? IsEditable { get; set; }
-
+
+ ///
+ /// The date and time the control was created.
+ /// [Required]
+ /// Format: UTC date-time
+ ///
public string CreatedDate { get; set; }
+ ///
+ /// The date and time the control was last modified.
+ /// [Required]
+ /// Format: UTC date-time
+ ///
public string LastModifiedDate { get; set; }
-
+
+ ///
+ /// The description of the control.
+ /// [Optional]
+ /// max 256 characters
+ ///
public string Description { get; set; }
+ ///
+ /// Initializes the control with its discriminator value.
+ ///
+ /// The control's type.
protected AbstractCardControlResponse(IssuingControlType controlType)
{
ControlType = controlType;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Controls/Responses/Create/MccCardControlResponse.cs b/src/CheckoutSdk/Issuing/Controls/Responses/Create/MccCardControlResponse.cs
index fee5e763..5d0de242 100644
--- a/src/CheckoutSdk/Issuing/Controls/Responses/Create/MccCardControlResponse.cs
+++ b/src/CheckoutSdk/Issuing/Controls/Responses/Create/MccCardControlResponse.cs
@@ -2,12 +2,23 @@
namespace Checkout.Issuing.Controls.Responses.Create
{
+ ///
+ /// A card control of type mcc_limit.
+ ///
public class MccCardControlResponse : AbstractCardControlResponse
{
+ ///
+ /// Initializes an mcc_limit control.
+ ///
public MccCardControlResponse() : base(IssuingControlType.MccLimit)
{
}
+ ///
+ /// The merchant category code (MCC) rule, which determines the types of businesses transactions can be
+ /// processed from.
+ /// [Optional]
+ ///
public MccLimit MccLimit { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Controls/Responses/Create/MidCardControlResponse.cs b/src/CheckoutSdk/Issuing/Controls/Responses/Create/MidCardControlResponse.cs
index 10c90f38..2202a69b 100644
--- a/src/CheckoutSdk/Issuing/Controls/Responses/Create/MidCardControlResponse.cs
+++ b/src/CheckoutSdk/Issuing/Controls/Responses/Create/MidCardControlResponse.cs
@@ -1,13 +1,39 @@
using Checkout.Issuing.Common;
+using Newtonsoft.Json;
+using System;
namespace Checkout.Issuing.Controls.Responses.Create
{
+ ///
+ /// A card control of type mid_limit.
+ ///
public class MidCardControlResponse : AbstractCardControlResponse
{
+ ///
+ /// Initializes a mid_limit control.
+ ///
public MidCardControlResponse() : base(IssuingControlType.MidLimit)
{
}
- public MidLimit VelocityLimit { get; set; }
+ ///
+ /// The merchant identification (MID) code rule, which determines the merchants from whom transactions can
+ /// be processed.
+ /// [Optional]
+ ///
+ public MidLimit MidLimit { get; set; }
+
+ ///
+ /// Former name of . It was mapped to velocity_limit, so it was never populated.
+ /// It now reads and writes and is not serialized.
+ /// [Optional]
+ ///
+ [Obsolete("Use MidLimit instead. Will be removed in the next major version.")]
+ [JsonIgnore]
+ public MidLimit VelocityLimit
+ {
+ get => MidLimit;
+ set => MidLimit = value;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/CheckoutSdk/Issuing/Controls/Responses/Create/VelocityCardControlResponse.cs b/src/CheckoutSdk/Issuing/Controls/Responses/Create/VelocityCardControlResponse.cs
index 827aa2be..dd57570c 100644
--- a/src/CheckoutSdk/Issuing/Controls/Responses/Create/VelocityCardControlResponse.cs
+++ b/src/CheckoutSdk/Issuing/Controls/Responses/Create/VelocityCardControlResponse.cs
@@ -2,12 +2,23 @@
namespace Checkout.Issuing.Controls.Responses.Create
{
+ ///
+ /// A card control of type velocity_limit.
+ ///
public class VelocityCardControlResponse : AbstractCardControlResponse
{
+ ///
+ /// Initializes a velocity_limit control.
+ ///
public VelocityCardControlResponse() : base(IssuingControlType.VelocityLimit)
{
}
+ ///
+ /// The velocity limit, which determines how much a target card can spend over a given timeframe.
+ /// On get and update responses it also carries amount_remaining.
+ /// [Optional]
+ ///
public VelocityLimit VelocityLimit { get; set; }
}
-}
\ No newline at end of file
+}
diff --git a/test/CheckoutSdkTest/Issuing/Cards/CardCreateResponseSerializationTest.cs b/test/CheckoutSdkTest/Issuing/Cards/CardCreateResponseSerializationTest.cs
new file mode 100644
index 00000000..79a94f21
--- /dev/null
+++ b/test/CheckoutSdkTest/Issuing/Cards/CardCreateResponseSerializationTest.cs
@@ -0,0 +1,241 @@
+using Checkout.Common;
+using Checkout.Issuing.Cards.Responses.Create;
+using Checkout.Issuing.Common;
+using Newtonsoft.Json.Linq;
+using Shouldly;
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Checkout.Issuing.Cards
+{
+ ///
+ /// Schema validation tests for the card create response (add-card-response) in Checkout.Issuing.Cards.
+ ///
+ public class CardCreateResponseSerializationTest
+ {
+ private static readonly JsonSerializer Serializer = new JsonSerializer();
+
+ // ------------------------------------------------------------------------
+ // AbstractCardControlsResponse subtypes
+ // Covers add-virtual-card-response-control-response (id, description,
+ // control_type) and its velocity, mcc and mid subtypes.
+ // ------------------------------------------------------------------------
+
+ [Fact]
+ public void ShouldDeserializeControlsForVirtualCardCreateResponse()
+ {
+ const string json = @"{
+ ""id"": ""crd_fa6psq42dcdd6fdn5gifcq1491"",
+ ""type"": ""virtual"",
+ ""last_four"": ""1234"",
+ ""controls"": [
+ { ""id"": ""ctr_s6tfwc6jpieexgcet2pljss6tu"", ""description"": ""velocity"", ""control_type"": ""velocity_limit"",
+ ""velocity_limit"": { ""amount_limit"": 5000, ""velocity_window"": { ""type"": ""weekly"" }, ""mcc_list"": [""4121""] } },
+ { ""id"": ""ctr_s6tfwc6jpieexgcet2pljss6tu"", ""description"": ""mcc"", ""control_type"": ""mcc_limit"",
+ ""mcc_limit"": { ""type"": ""allow"", ""mcc_list"": [5932, 5411] } },
+ { ""id"": ""ctr_s6tfwc6jpieexgcet2pljss6tu"", ""description"": ""Allow the card to be used only in AZ Pizza"", ""control_type"": ""mid_limit"",
+ ""mid_limit"": { ""type"": ""allow"", ""mid_list"": [593278, 541114] } }
+ ]
+ }";
+
+ var response = (AbstractCardCreateResponse)Serializer.Deserialize(json, typeof(AbstractCardCreateResponse));
+
+ var card = response.ShouldBeOfType();
+ card.Controls.Count.ShouldBe(3);
+
+ var velocity = card.Controls[0].ShouldBeOfType();
+ velocity.Id.ShouldBe("ctr_s6tfwc6jpieexgcet2pljss6tu");
+ velocity.Description.ShouldBe("velocity");
+ velocity.ControlType.ShouldBe(IssuingControlType.VelocityLimit);
+ velocity.VelocityLimit.AmountLimit.ShouldBe(5000);
+ velocity.VelocityLimit.VelocityWindow.Type.ShouldBe(VelocityWindowType.Weekly);
+ velocity.VelocityLimit.MccList.ShouldBe(new List { "4121" });
+
+ var mcc = card.Controls[1].ShouldBeOfType();
+ mcc.ControlType.ShouldBe(IssuingControlType.MccLimit);
+ mcc.MccLimit.Type.ShouldBe(LimitControlType.Allow);
+ mcc.MccLimit.MccList.ShouldBe(new List { "5932", "5411" });
+
+ var mid = card.Controls[2].ShouldBeOfType();
+ mid.ControlType.ShouldBe(IssuingControlType.MidLimit);
+ mid.Description.ShouldBe("Allow the card to be used only in AZ Pizza");
+ mid.MidLimit.ShouldNotBeNull();
+ mid.MidLimit.Type.ShouldBe(LimitControlType.Allow);
+ mid.MidLimit.MidList.ShouldBe(new List { "593278", "541114" });
+#pragma warning disable CS0618
+ mid.VelocityLimit.ShouldBeSameAs(mid.MidLimit);
+#pragma warning restore CS0618
+ }
+
+ [Fact]
+ public void ShouldForwardDeprecatedVelocityLimitToMidLimitAndSerializeOnceForMidCardControlsResponse()
+ {
+ var limit = new MidLimit { Type = LimitControlType.Block, MidList = new List { "593278" } };
+#pragma warning disable CS0618
+ var control = new MidCardControlsResponse { Id = "ctr_s6tfwc6jpieexgcet2pljss6tu", Description = "mid", VelocityLimit = limit };
+ control.MidLimit.ShouldBeSameAs(limit);
+#pragma warning restore CS0618
+
+ var json = JObject.Parse(Serializer.Serialize(control));
+
+ json.ContainsKey("mid_limit").ShouldBeTrue();
+ json.ContainsKey("velocity_limit").ShouldBeFalse();
+ json["control_type"].Value().ShouldBe("mid_limit");
+ }
+
+ // ------------------------------------------------------------------------
+ // VirtualCardCreateResponse / PhysicalCardCreateResponse
+ // Covers every property of add-virtual-card-response and
+ // add-physical-card-response, including _links and scheme.
+ // ------------------------------------------------------------------------
+
+ private const string SwaggerBody = @"{
+ ""id"": ""crd_fa6psq242dcd6fdn5gifcq1491"",
+ ""client_id"": ""cli_vkuhvk4vjn2edkps7dfsq6emqm"",
+ ""entity_id"": ""ent_fa6psq242dcd6fdn5gifcq1491"",
+ ""last_four"": 1234,
+ ""expiry_month"": 5,
+ ""expiry_year"": 2025,
+ ""billing_currency"": ""USD"",
+ ""issuing_country"": ""US"",
+ ""status"": ""active"",
+ ""type"": ""TYPE"",
+ ""scheme"": ""mastercard"",
+ ""created_date"": ""2019-09-10T10:11:12.0000000+00:00""
+ }";
+
+ [Theory]
+ [InlineData("virtual", typeof(VirtualCardCreateResponse))]
+ [InlineData("physical", typeof(PhysicalCardCreateResponse))]
+ public void ShouldDeserializeSwaggerExampleForCardCreateResponse(string type, Type expected)
+ {
+ var response = (AbstractCardCreateResponse)Serializer.Deserialize(
+ SwaggerBody.Replace("TYPE", type), typeof(AbstractCardCreateResponse));
+
+ response.ShouldBeOfType(expected);
+ response.Id.ShouldBe("crd_fa6psq242dcd6fdn5gifcq1491");
+ response.ClientId.ShouldBe("cli_vkuhvk4vjn2edkps7dfsq6emqm");
+ response.EntityId.ShouldBe("ent_fa6psq242dcd6fdn5gifcq1491");
+ response.LastFour.ShouldBe("1234");
+ response.ExpiryMonth.ShouldBe(5);
+ response.ExpiryYear.ShouldBe(2025);
+ response.BillingCurrency.ShouldBe(Currency.USD);
+ response.IssuingCountry.ShouldBe(CountryCode.US);
+ response.Status.ShouldBe(CardStatus.Active);
+ response.Scheme.ShouldBe(IssuingScheme.Mastercard);
+ response.CreatedDate.ShouldNotBeNull();
+ }
+
+ [Fact]
+ public void ShouldRoundTripAllPropertiesForVirtualCardCreateResponse()
+ {
+ var original = new VirtualCardCreateResponse
+ {
+ Credentials = new Credentials { Number = "4242424242424242", Cvc2 = "604" },
+ Controls = new List
+ {
+ new MidCardControlsResponse
+ {
+ Id = "ctr_s6tfwc6jpieexgcet2pljss6tu", Description = "mid",
+ MidLimit = new MidLimit { Type = LimitControlType.Allow, MidList = new List { "593278" } }
+ }
+ }
+ };
+ PopulateCard(original);
+
+ var result = (AbstractCardCreateResponse)Serializer.Deserialize(
+ Serializer.Serialize(original), typeof(AbstractCardCreateResponse));
+
+ var card = result.ShouldBeOfType();
+ AssertCard(card);
+ card.Credentials.Number.ShouldBe("4242424242424242");
+ card.Credentials.Cvc2.ShouldBe("604");
+ card.Controls.Count.ShouldBe(1);
+ card.Controls[0].ShouldBeOfType().MidLimit.MidList.ShouldBe(new List { "593278" });
+ }
+
+ [Fact]
+ public void ShouldRoundTripAllPropertiesForPhysicalCardCreateResponse()
+ {
+ var original = new PhysicalCardCreateResponse();
+ PopulateCard(original);
+
+ var result = (AbstractCardCreateResponse)Serializer.Deserialize(
+ Serializer.Serialize(original), typeof(AbstractCardCreateResponse));
+
+ AssertCard(result.ShouldBeOfType());
+ }
+
+ [Fact]
+ public void ShouldLeaveDeprecatedFieldsNullForSpecBodyOfCardControlsResponse()
+ {
+ const string json = @"{ ""id"": ""ctr_s6tfwc6jpieexgcet2pljss6tu"", ""description"": ""mcc"", ""control_type"": ""mcc_limit"",
+ ""mcc_limit"": { ""type"": ""block"", ""mcc_list"": [""5411""] } }";
+
+ var control = (AbstractCardControlsResponse)Serializer.Deserialize(json, typeof(AbstractCardControlsResponse));
+
+ control.ShouldBeOfType();
+#pragma warning disable CS0618
+ control.TargetId.ShouldBeNull();
+ control.CreatedDate.ShouldBeNull();
+ control.LastModifiedDate.ShouldBeNull();
+#pragma warning restore CS0618
+ }
+
+ [Fact]
+ public void ShouldStillDeserializeDeprecatedFieldsWhenPresentOnCardControlsResponse()
+ {
+ const string json = @"{ ""id"": ""ctr_s6tfwc6jpieexgcet2pljss6tu"", ""control_type"": ""mcc_limit"",
+ ""target_id"": ""crd_fa6psq42dcdd6fdn5gifcq1491"", ""created_date"": ""a"", ""last_modified_date"": ""b"" }";
+
+ var control = (AbstractCardControlsResponse)Serializer.Deserialize(json, typeof(AbstractCardControlsResponse));
+
+#pragma warning disable CS0618
+ control.TargetId.ShouldBe("crd_fa6psq42dcdd6fdn5gifcq1491");
+ control.CreatedDate.ShouldBe("a");
+ control.LastModifiedDate.ShouldBe("b");
+#pragma warning restore CS0618
+ }
+
+ private static void PopulateCard(AbstractCardCreateResponse card)
+ {
+ card.Id = "crd_fa6psq242dcd6fdn5gifcq1491";
+ card.ClientId = "cli_vkuhvk4vjn2edkps7dfsq6emqm";
+ card.EntityId = "ent_fa6psq242dcd6fdn5gifcq1491";
+ card.DisplayName = "JOHN KENNEDY";
+ card.LastFour = "1234";
+ card.ExpiryMonth = 5;
+ card.ExpiryYear = 2025;
+ card.BillingCurrency = Currency.USD;
+ card.IssuingCountry = CountryCode.US;
+ card.Scheme = IssuingScheme.Visa;
+ card.CreatedDate = new DateTime(2019, 9, 10, 10, 11, 12, DateTimeKind.Utc);
+ card.Status = CardStatus.Suspended;
+ card.Reference = "X-123456-N11";
+ card.ScheduledRevocationDate = "2027-03-12";
+ card.LastActivatedOn = new DateTime(2019, 9, 11, 10, 11, 12, DateTimeKind.Utc);
+ card.Links = new Dictionary { { "self", new Link { Href = "https://api.checkout.com/issuing/cards/crd_fa6psq242dcd6fdn5gifcq1491" } } };
+ }
+
+ private static void AssertCard(AbstractCardCreateResponse card)
+ {
+ card.Id.ShouldBe("crd_fa6psq242dcd6fdn5gifcq1491");
+ card.ClientId.ShouldBe("cli_vkuhvk4vjn2edkps7dfsq6emqm");
+ card.EntityId.ShouldBe("ent_fa6psq242dcd6fdn5gifcq1491");
+ card.DisplayName.ShouldBe("JOHN KENNEDY");
+ card.LastFour.ShouldBe("1234");
+ card.ExpiryMonth.ShouldBe(5);
+ card.ExpiryYear.ShouldBe(2025);
+ card.BillingCurrency.ShouldBe(Currency.USD);
+ card.IssuingCountry.ShouldBe(CountryCode.US);
+ card.Scheme.ShouldBe(IssuingScheme.Visa);
+ card.CreatedDate.ShouldBe(new DateTime(2019, 9, 10, 10, 11, 12, DateTimeKind.Utc));
+ card.Status.ShouldBe(CardStatus.Suspended);
+ card.Reference.ShouldBe("X-123456-N11");
+ card.ScheduledRevocationDate.ShouldBe("2027-03-12");
+ card.LastActivatedOn.ShouldBe(new DateTime(2019, 9, 11, 10, 11, 12, DateTimeKind.Utc));
+ card.GetSelfLink().Href.ShouldBe("https://api.checkout.com/issuing/cards/crd_fa6psq242dcd6fdn5gifcq1491");
+ }
+ }
+}
diff --git a/test/CheckoutSdkTest/Issuing/Controls/ControlsSerializationTest.cs b/test/CheckoutSdkTest/Issuing/Controls/ControlsSerializationTest.cs
new file mode 100644
index 00000000..a54357e8
--- /dev/null
+++ b/test/CheckoutSdkTest/Issuing/Controls/ControlsSerializationTest.cs
@@ -0,0 +1,292 @@
+using Checkout.Issuing.Common;
+using Checkout.Issuing.Controls.Requests.Update;
+using Checkout.Issuing.Controls.Responses.Create;
+using Checkout.Issuing.Controls.Responses.Query;
+using Newtonsoft.Json.Linq;
+using Shouldly;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Checkout.Issuing.Controls
+{
+ ///
+ /// Schema validation tests for Checkout.Issuing.Controls.
+ /// Grouped by domain; each section below covers one subject.
+ ///
+ public class ControlsSerializationTest
+ {
+ private static readonly JsonSerializer Serializer = new JsonSerializer();
+
+ private const string ControlId = "ctr_gp7vkmxayztufjz6top5bjcdra";
+ private const string TargetId = "crd_fa6psq42dcdd6fdn5gifcq1491";
+ private const string Date = "2023-03-12T18:20:12.0000000+00:00";
+
+ // ------------------------------------------------------------------------
+ // VelocityCardControlResponse
+ // Covers the base control fields plus description and velocity_limit
+ // (VelocityLimitWithRemainingAmount) against update-control-velocity-limit-response.
+ // ------------------------------------------------------------------------
+
+ [Fact]
+ public void ShouldDeserializeSwaggerExampleForVelocityCardControlResponse()
+ {
+ const string json = @"{
+ ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"",
+ ""target_id"": ""crd_fa6psq42dcdd6fdn5gifcq1491"",
+ ""description"": ""Maximum spend of 500 per week for restaurants"",
+ ""control_type"": ""velocity_limit"",
+ ""is_editable"": true,
+ ""created_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""last_modified_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""velocity_limit"": {
+ ""amount_remaining"": 45000,
+ ""amount_limit"": 50000,
+ ""velocity_window"": { ""type"": ""weekly"" },
+ ""mcc_list"": [4121, 4582]
+ }
+ }";
+
+ var response = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var velocity = response.ShouldBeOfType();
+ AssertBase(velocity, IssuingControlType.VelocityLimit, "Maximum spend of 500 per week for restaurants");
+ velocity.VelocityLimit.AmountRemaining.ShouldBe(45000);
+ velocity.VelocityLimit.AmountLimit.ShouldBe(50000);
+ velocity.VelocityLimit.VelocityWindow.Type.ShouldBe(VelocityWindowType.Weekly);
+ velocity.VelocityLimit.MccList.ShouldBe(new List { "4121", "4582" });
+ velocity.VelocityLimit.MidList.ShouldBeNull();
+ }
+
+ [Fact]
+ public void ShouldRoundTripAllPropertiesForVelocityCardControlResponse()
+ {
+ var original = new VelocityCardControlResponse
+ {
+ VelocityLimit = new VelocityLimit
+ {
+ AmountLimit = 50000,
+ AmountRemaining = 45000,
+ VelocityWindow = new VelocityWindow { Type = VelocityWindowType.AllTime },
+ MccList = new List { "4121" },
+ MidList = new List { "593278" }
+ }
+ };
+ PopulateBase(original, "velocity");
+
+ var json = Serializer.Serialize(original);
+ var result = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var velocity = result.ShouldBeOfType();
+ AssertBase(velocity, IssuingControlType.VelocityLimit, "velocity");
+ velocity.VelocityLimit.AmountLimit.ShouldBe(50000);
+ velocity.VelocityLimit.AmountRemaining.ShouldBe(45000);
+ velocity.VelocityLimit.VelocityWindow.Type.ShouldBe(VelocityWindowType.AllTime);
+ velocity.VelocityLimit.MccList.ShouldBe(new List { "4121" });
+ velocity.VelocityLimit.MidList.ShouldBe(new List { "593278" });
+ }
+
+ [Fact]
+ public void ShouldNotSendAmountRemainingOnVelocityCardControlUpdate()
+ {
+ var request = new VelocityCardControlUpdate
+ {
+ Description = "velocity",
+ VelocityLimit = new VelocityLimit
+ {
+ AmountLimit = 100,
+ VelocityWindow = new VelocityWindow { Type = VelocityWindowType.Daily }
+ }
+ };
+
+ var json = JObject.Parse(Serializer.Serialize(request));
+
+ ((JObject)json["velocity_limit"]).ContainsKey("amount_remaining").ShouldBeFalse();
+ }
+
+ // ------------------------------------------------------------------------
+ // MccCardControlResponse
+ // Covers the base control fields plus description and mcc_limit against
+ // update-control-mcc-limit-response.
+ // ------------------------------------------------------------------------
+
+ [Fact]
+ public void ShouldDeserializeSwaggerExampleForMccCardControlResponse()
+ {
+ const string json = @"{
+ ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"",
+ ""target_id"": ""crd_fa6psq42dcdd6fdn5gifcq1491"",
+ ""description"": ""Allow the card to be used only in restaurants and supermarkets"",
+ ""control_type"": ""mcc_limit"",
+ ""is_editable"": true,
+ ""created_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""last_modified_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""mcc_limit"": { ""type"": ""allow"", ""mcc_list"": [5932, 5411] }
+ }";
+
+ var response = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var mcc = response.ShouldBeOfType();
+ AssertBase(mcc, IssuingControlType.MccLimit, "Allow the card to be used only in restaurants and supermarkets");
+ mcc.MccLimit.Type.ShouldBe(LimitControlType.Allow);
+ mcc.MccLimit.MccList.ShouldBe(new List { "5932", "5411" });
+ }
+
+ [Fact]
+ public void ShouldRoundTripAllPropertiesForMccCardControlResponse()
+ {
+ var original = new MccCardControlResponse
+ {
+ MccLimit = new MccLimit { Type = LimitControlType.Block, MccList = new List { "5932" } }
+ };
+ PopulateBase(original, "mcc");
+
+ var json = Serializer.Serialize(original);
+ var result = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var mcc = result.ShouldBeOfType();
+ AssertBase(mcc, IssuingControlType.MccLimit, "mcc");
+ mcc.MccLimit.Type.ShouldBe(LimitControlType.Block);
+ mcc.MccLimit.MccList.ShouldBe(new List { "5932" });
+ }
+
+ // ------------------------------------------------------------------------
+ // MidCardControlResponse
+ // Covers the base control fields plus description and mid_limit against
+ // update-control-mid-limit-response, and the deprecated VelocityLimit alias.
+ // ------------------------------------------------------------------------
+
+ [Fact]
+ public void ShouldDeserializeSwaggerExampleForMidCardControlResponse()
+ {
+ const string json = @"{
+ ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"",
+ ""target_id"": ""crd_fa6psq42dcdd6fdn5gifcq1491"",
+ ""description"": ""Allow the card to be used only in AZ Pizza"",
+ ""control_type"": ""mid_limit"",
+ ""is_editable"": true,
+ ""created_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""last_modified_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""mid_limit"": { ""type"": ""allow"", ""mid_list"": [593278, 541114] }
+ }";
+
+ var response = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var mid = response.ShouldBeOfType();
+ AssertBase(mid, IssuingControlType.MidLimit, "Allow the card to be used only in AZ Pizza");
+ mid.MidLimit.ShouldNotBeNull();
+ mid.MidLimit.Type.ShouldBe(LimitControlType.Allow);
+ mid.MidLimit.MidList.ShouldBe(new List { "593278", "541114" });
+#pragma warning disable CS0618
+ mid.VelocityLimit.ShouldBeSameAs(mid.MidLimit);
+#pragma warning restore CS0618
+ }
+
+ [Fact]
+ public void ShouldRoundTripAllPropertiesForMidCardControlResponse()
+ {
+ var original = new MidCardControlResponse
+ {
+ MidLimit = new MidLimit { Type = LimitControlType.Block, MidList = new List { "AZ-PIZZA" } }
+ };
+ PopulateBase(original, "mid");
+
+ var json = Serializer.Serialize(original);
+ var result = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var mid = result.ShouldBeOfType();
+ AssertBase(mid, IssuingControlType.MidLimit, "mid");
+ mid.MidLimit.Type.ShouldBe(LimitControlType.Block);
+ mid.MidLimit.MidList.ShouldBe(new List { "AZ-PIZZA" });
+ }
+
+ [Fact]
+ public void ShouldForwardDeprecatedVelocityLimitToMidLimitAndSerializeOnceForMidCardControlResponse()
+ {
+ var limit = new MidLimit { Type = LimitControlType.Allow, MidList = new List { "593278" } };
+#pragma warning disable CS0618
+ var response = new MidCardControlResponse { VelocityLimit = limit };
+ response.MidLimit.ShouldBeSameAs(limit);
+ response.VelocityLimit.ShouldBeSameAs(response.MidLimit);
+#pragma warning restore CS0618
+
+ var json = JObject.Parse(Serializer.Serialize(response));
+
+ json.ContainsKey("mid_limit").ShouldBeTrue();
+ json.ContainsKey("velocity_limit").ShouldBeFalse();
+ }
+
+ // ------------------------------------------------------------------------
+ // Create control response (add-control-response)
+ // The create operation also returns _links (ControlLinks).
+ // ------------------------------------------------------------------------
+
+ [Fact]
+ public void ShouldDeserializeLinksForCreateCardControlResponse()
+ {
+ const string json = @"{
+ ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"",
+ ""target_id"": ""crd_fa6psq42dcdd6fdn5gifcq1491"",
+ ""description"": ""Allow the card to be used only in AZ Pizza"",
+ ""control_type"": ""mid_limit"",
+ ""is_editable"": true,
+ ""created_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""last_modified_date"": ""2023-03-12T18:20:12.0000000+00:00"",
+ ""mid_limit"": { ""type"": ""allow"", ""mid_list"": [""593278""] },
+ ""_links"": { ""self"": { ""href"": ""https://api.checkout.com/issuing/controls/ctr_gp7vkmxayztufjz6top5bjcdra"" } }
+ }";
+
+ var response = (AbstractCardControlResponse)Serializer.Deserialize(json, typeof(AbstractCardControlResponse));
+
+ var mid = response.ShouldBeOfType();
+ AssertBase(mid, IssuingControlType.MidLimit, "Allow the card to be used only in AZ Pizza");
+ mid.GetSelfLink().ShouldNotBeNull();
+ mid.GetSelfLink().Href.ShouldBe("https://api.checkout.com/issuing/controls/ctr_gp7vkmxayztufjz6top5bjcdra");
+ }
+
+ // ------------------------------------------------------------------------
+ // CardControlsQueryResponse
+ // The list operation returns the same discriminated subtypes.
+ // ------------------------------------------------------------------------
+
+ [Fact]
+ public void ShouldDeserializeEverySubtypeForCardControlsQueryResponse()
+ {
+ const string json = @"{ ""controls"": [
+ { ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"", ""control_type"": ""velocity_limit"",
+ ""velocity_limit"": { ""amount_remaining"": 1, ""amount_limit"": 2, ""velocity_window"": { ""type"": ""daily"" } } },
+ { ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"", ""control_type"": ""mcc_limit"",
+ ""mcc_limit"": { ""type"": ""block"", ""mcc_list"": [""5411""] } },
+ { ""id"": ""ctr_gp7vkmxayztufjz6top5bjcdra"", ""control_type"": ""mid_limit"",
+ ""mid_limit"": { ""type"": ""block"", ""mid_list"": [""593278""] } }
+ ] }";
+
+ var response = (CardControlsQueryResponse)Serializer.Deserialize(json, typeof(CardControlsQueryResponse));
+
+ response.Controls.Count.ShouldBe(3);
+ response.Controls[0].ShouldBeOfType().VelocityLimit.AmountRemaining.ShouldBe(1);
+ response.Controls[1].ShouldBeOfType().MccLimit.MccList.ShouldBe(new List { "5411" });
+ response.Controls[2].ShouldBeOfType().MidLimit.MidList.ShouldBe(new List { "593278" });
+ }
+
+ private static void PopulateBase(AbstractCardControlResponse response, string description)
+ {
+ response.Id = ControlId;
+ response.TargetId = TargetId;
+ response.IsEditable = true;
+ response.CreatedDate = Date;
+ response.LastModifiedDate = Date;
+ response.Description = description;
+ }
+
+ private static void AssertBase(AbstractCardControlResponse response, IssuingControlType type, string description)
+ {
+ response.ControlType.ShouldBe(type);
+ response.Id.ShouldBe(ControlId);
+ response.TargetId.ShouldBe(TargetId);
+ response.IsEditable.ShouldBe(true);
+ response.CreatedDate.ShouldNotBeNullOrEmpty();
+ response.LastModifiedDate.ShouldNotBeNullOrEmpty();
+ response.Description.ShouldBe(description);
+ }
+ }
+}