For a scenario like this one would need a custom model binder. The framework allows for such flexibility.
Using the walkthrough provided here
Custom model binder sample
and adapting it to this question.
The following sample uses the ModelBinder attribute on the SalesRecord model:
[ModelBinder(BinderType = typeof(SalesRecordBinder))]
[JsonConverter(typeof(JsonPathConverter))]
public class SalesRecord {
    [JsonProperty("user.name.first")]
    public string FirstName {get; set;}
    [JsonProperty("user.name.last")]
    public string LastName {get; set;}
    [JsonProperty("payment.type")]
    public string PaymentType {get; set;}
}
In the preceding code, the ModelBinder attribute specifies the type of IModelBinder that should be used to bind SalesRecord action parameters.
The SalesRecordBinder is used to bind an SalesRecord parameter by trying to parse the posted content using a custom JSON converter to simplify the deseiralization.
class JsonPathConverter : JsonConverter {
    public override object ReadJson(JsonReader reader, Type objectType, 
                                    object existingValue, JsonSerializer serializer) {
        JObject jo = JObject.Load(reader);
        object targetObj = Activator.CreateInstance(objectType);
        foreach (PropertyInfo prop in objectType.GetProperties()
                                                .Where(p => p.CanRead && p.CanWrite)) {
            JsonPropertyAttribute att = prop.GetCustomAttributes(true)
                                            .OfType<JsonPropertyAttribute>()
                                            .FirstOrDefault();
            string jsonPath = (att != null ? att.PropertyName : prop.Name);
            JToken token = jo.SelectToken(jsonPath);
            if (token != null && token.Type != JTokenType.Null) {
                object value = token.ToObject(prop.PropertyType, serializer);
                prop.SetValue(targetObj, value, null);
            }
        }
        return targetObj;
    }
    public override bool CanConvert(Type objectType) {
        // CanConvert is not called when [JsonConverter] attribute is used
        return false;
    }
    public override bool CanWrite {
        get { return false; }
    }
    public override void WriteJson(JsonWriter writer, object value, 
                                    JsonSerializer serializer) {
        throw new NotImplementedException();
    }
}
Source: Can I specify a path in an attribute to map a property in my class to a child property in my JSON?
public class SalesRecordBinder : IModelBinder {
    public Task BindModelAsync(ModelBindingContext bindingContext) {
        if (bindingContext == null){
            throw new ArgumentNullException(nameof(bindingContext));
        }
        // Try to fetch the value of the argument by name
        var valueProviderResult = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName);
        if (valueProviderResult == ValueProviderResult.None){
            return Task.CompletedTask;
        }
        var json = valueProviderResult.FirstValue;
        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(json)) {
            return Task.CompletedTask;
        }
        //Try to parse the provided value into the desired model
        var model = JsonConvert.DeserializeObject<SalesRecord>(json);
        //Model will be null if unable to desrialize.
        if (model == null) {
            bindingContext.ModelState
                .TryAddModelError(
                    bindingContext.ModelName,
                    "Invalid data"
                );
            return Task.CompletedTask;
        }
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, model);
        //could consider checking model state if so desired.
        //set result state of binding the model
        bindingContext.Result = ModelBindingResult.Success(model);
        return Task.CompletedTask;
    }
}
From there it should be now a simple matter of using the model in the action
[HttpPost("create", Name = "CreateSalesRecord")]
public IActionResult Create([FromBody] SalesRecord record) {
    if(ModelState.IsValid) {
        //...
        return Ok();
    }
    return BadRequest(ModelState);
}
Disclaimer: This has not been tested as yet. There may be issues still to be fixed as it is based on the linked sources provided above.