Open Closed

Cannot Use ExtraProperties with Interface Type in PaymentRequestProductCreateDto Across Microservices #9511


User avatar
0
socsricardo created

We are building a microservice-based solution using abp.io and the ABP Payment module. In our payment workflow, we use PaymentRequestProductCreateDto, which has an ExtraProperties property of type Dictionary<string, IPaymentRequestProductExtraParameterConfiguration>. We need to pass custom gateway configuration data (e.g., ZendaPaymentRequestProductExtraParameterConfiguration) through this property.

    public class ZendaPaymentRequestProductExtraParameterConfiguration : IPaymentRequestProductExtraParameterConfiguration
    {
        public string RegisterNo { get; set; }
    }

However, when sending this DTO between services (e.g., via HTTP), we encounter the following error during deserialization

Deserialization of interface or abstract types is not supported. Type 'Volo.Payment.IPaymentRequestProductExtraParameterConfiguration'. Path: $.products[0].extraProperties.Zenda | LineNumber: 0 | BytePositionInLine: 165.

Expected behaviour: We would like to be able to pass custom configuration objects in ExtraProperties across microservice boundaries without serialization issues, or have guidance on the recommended ABP approach for this scenario.

Question: What is the recommended way to use ExtraProperties for gateway-specific configuration in a microservice environment, given the interface-based dictionary and serialization limitations?


5 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share full error logs and exceptions?

    Also what is the code of ZendaPaymentRequestProductExtraParameterConfiguration class.

    liming.ma@volosoft.com

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Can you add a new JsonConverter to your caller project?

    services.Configure<AbpSystemTextJsonSerializerOptions>(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new PaymentRequestProductExtraParameterConfigurationJsonConverter());
    });
    
    using System;
    using System.Text.Json;
    using System.Text.Json.Serialization;
    using Volo.Payment;
    
    public class PaymentRequestProductExtraParameterConfigurationJsonConverter : JsonConverter<IPaymentRequestProductExtraParameterConfiguration>
    {
        private const string TypeDiscriminator = "$type";
    
        public override IPaymentRequestProductExtraParameterConfiguration Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
        {
            using var jsonDoc = JsonDocument.ParseValue(ref reader);
            var root = jsonDoc.RootElement;
    
            if (!root.TryGetProperty(TypeDiscriminator, out var typeProp))
            {
                throw new JsonException($"Missing discriminator field '{TypeDiscriminator}'.");
            }
    
            var typeName = typeProp.GetString();
            if (typeName.IsNullOrWhiteSpace())
            {
                throw new JsonException($"Missing type name in discriminator field '{TypeDiscriminator}'.");
            }
    
            var actualType = Type.GetType(typeName);
            if (actualType == null)
            {
                throw new JsonException($"Unable to find type: {typeName}");
            }
    
            var json = root.GetRawText();
            return (IPaymentRequestProductExtraParameterConfiguration)JsonSerializer.Deserialize(json, actualType, options);
        }
    
        public override void Write(Utf8JsonWriter writer, IPaymentRequestProductExtraParameterConfiguration value, JsonSerializerOptions options)
        {
            var type = value.GetType();
            var json = JsonSerializer.SerializeToElement(value, type, options);
    
            writer.WriteStartObject();
            writer.WriteString(TypeDiscriminator, type.AssemblyQualifiedName);
            foreach (var prop in json.EnumerateObject())
            {
                prop.WriteTo(writer);
            }
            writer.WriteEndObject();
        }
    }
    
  • User Avatar
    0
    socsricardo created

    This did not resolve my issue, I am still experiencing the same error. I added the new JsonConverter to my web project.

    The API is still throwing the following exception: Deserialization of interface or abstract types is not supported. Type 'Volo.Payment.IPaymentRequestProductExtraParameterConfiguration'. Path: $.products[0].extraProperties.ZendaPaymentRequestProductExtraParameterConfiguration | LineNumber: 0 | BytePositionInLine: 213

    StackTrace:

    at System.Text.Json.ThrowHelper.ThrowNotSupportedException(ReadStack& state, Utf8JsonReader& reader, Exception innerException)
       at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DeserializeNoConstructor(JsonTypeInfo typeInfo, Utf8JsonReader& reader, ReadStack& state)
       at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
       at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
       at System.Text.Json.Serialization.JsonDictionaryConverter`3.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TDictionary& value)
       at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
       at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
       at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
       at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
       at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
       at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
       at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
       at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
       at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
       at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)
       at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, T& value)
       at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
       at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
       at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
       at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
       at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
       at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
       at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
    --- End of stack trace from previous location ---
       at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
       at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
    

    I also add the same JsonConverter to the API project where the exception is being thrown.

  • User Avatar
    0
    socsricardo created

    Have managed to get this working.

    In the API project this registration would not trigger the read. This is still the registration on the web project.

    services.Configure<AbpSystemTextJsonSerializerOptions>(options => c
    {
        options.JsonSerializerOptions.Converters.Add(new PaymentRequestProductExtraParameterConfigurationJsonConverter());
    });
    

    Adding the JsonConverter in the API project against the JsonOptions instead of AbpSystemTextJsonSerializerOptions resolved the issue and the read now trigger.

    Configure<JsonOptions>(options =>
    {
        options.JsonSerializerOptions.Converters.Add(new PaymentRequestProductExtraParameterConfigurationJsonConverter());
    });
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Great 👍

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on July 09, 2025, 06:20