Payment system migration
Here is a simplified version, thanks to this answer.

public interface IMainPaymentProvider : IPaymentProvider
{
}

public interface IPaymentProvider : IPaymentService
{
    string Name { get; }
}

public interface IPaymentGateway : IPaymentService
{
}

public interface IPaymentService
{
    Task&lt;BillingId&gt; RegisterAsync(CreditCard card);
    Task ChargeAsync(BillingId id, decimal amount);
}


Where:

[TypeConverter(typeof(BillingIdTypeConverter))]
[JsonConverter(typeof(BillingIdJsonConverter))]
public class BillingId
{
    public static BillingId Parse(string text) =&gt;
        IsNullOrWhiteSpace(text) ? null :
        text.Split(new[] { ':' }, StringSplitOptions.RemoveEmptyEntries) is string[] p &amp;&amp; p.Length == 2
            ? new BillingId(p[0], p[1]) 
            : throw new FormatException();

    public BillingId(string provider, string value) =&gt; 
        (Provider, Value) = (provider, value);

    public string Provider { get; }
    public string Value { get; }

    public override string ToString() =&gt; 
        $"{Provider}:{Value}";
}


So payment gateway looks like:

[Service]
public class PaymentGateway : IPaymentGateway
{
    public PaymentGateway(IMainPaymentProvider mainProvider, params IPaymentProvider[] providers) =&gt; 
        (MainProvider, Providers) = 
        (mainProvider, providers.ToDictionary(p =&gt; p.Name));

    IPaymentProvider MainProvider { get; }
    IReadOnlyDictionary&lt;string, IPaymentProvider&gt; Providers { get; }

    public async Task&lt;BillingId&gt; RegisterAsync(CreditCard card) =&gt;
        await MainProvider.RegisterAsync(card);

    public async Task ChargeAsync(BillingId id, decimal amount) =&gt;
        await Providers[id.Provider].ChargeAsync(id, amount); 
}


And a sample provider could be:

public interface IStripeProvider : IMainPaymentProvider
{
}

[Service]
public class StripeProvider : IStripeProvider
{
    StripeClient Client { get; } = new StripeClient();

    public string Name =&gt; "Stripe";

    public async Task&lt;BillingId&gt; RegisterAsync(CreditCard card) =&gt;
        new BillingId(Name, await Client.RegisterAsync(card));

    public async Task ChargeAsync(BillingId id, decimal amount) =&gt;
        await Client.Charge(id.Value, amount);
}