Dynamic Shipping Providers¶
Dynamic shipping providers fetch live rates from carrier APIs at checkout. Merchello ships with built-in FedEx and UPS providers, and you can add custom providers for other carriers.
How Dynamic Providers Differ from Flat Rate¶
| Aspect | Flat Rate | Dynamic |
|---|---|---|
| Rates | Pre-configured in database | Fetched live from carrier API |
| Package info | Not used | Weight, dimensions sent to carrier |
| API keys | Not needed | Required (carrier credentials) |
| Cost accuracy | Fixed | Real-time based on actual shipment |
| Speed | Instant (DB lookup) | Network call to carrier API |
Dynamic providers set UsesLiveRates = true in their metadata and must not rely on fixed-cost entries in the database.
Invariant (CLAUDE.md): If you want a carrier-named option at a fixed cost (e.g., "FedEx Ground $8.99"), create it as a flat-rate
ShippingOption(ProviderKey = "flat-rate"). Assigning a dynamic provider key to a fixed-cost entry will cause the option to be hidden or rendered as "Calculated at checkout".
Built-in dynamic providers:
FedExShippingProvider(fedex)UpsShippingProvider(ups)
Built-in Providers¶
FedEx¶
Provider Key: fedex
Real-time FedEx shipping rates via the FedEx REST API.
Configuration Fields¶
| Field | Description |
|---|---|
apiKey |
FedEx API Key (Client ID) |
secretKey |
FedEx Secret Key (Client Secret) |
accountNumber |
FedEx Account Number |
environment |
sandbox or production |
childKey |
(Optional) Child Key for CSP/parent-child OAuth |
childSecret |
(Optional) Child Secret for CSP/parent-child OAuth |
Supported Service Types¶
| Service Code | Description |
|---|---|
FEDEX_GROUND |
FedEx Ground |
FEDEX_EXPRESS_SAVER |
FedEx Express Saver |
FEDEX_2_DAY |
FedEx 2Day |
FEDEX_2_DAY_AM |
FedEx 2Day AM |
PRIORITY_OVERNIGHT |
FedEx Priority Overnight |
STANDARD_OVERNIGHT |
FedEx Standard Overnight |
FIRST_OVERNIGHT |
FedEx First Overnight |
INTERNATIONAL_ECONOMY |
FedEx International Economy |
INTERNATIONAL_PRIORITY |
FedEx International Priority |
FEDEX_FREIGHT_ECONOMY |
FedEx Freight Economy |
Testing¶
For sandbox testing, use your own API Key and Secret Key from the FedEx Developer Portal, FedEx's test account number 740561073, and real addresses (sandbox returns simulated rates). Do not use your production account number in sandbox mode.
UPS¶
Provider Key: ups
Real-time UPS shipping rates via the UPS REST API.
Configuration Fields¶
| Field | Description |
|---|---|
clientId |
UPS Client ID |
clientSecret |
UPS Client Secret |
accountNumber |
UPS Shipper Number |
environment |
sandbox or production |
Supported Service Types¶
| Service Code | Description |
|---|---|
03 |
UPS Ground |
02 |
UPS 2nd Day Air |
13 |
UPS Next Day Air Saver |
01 |
UPS Next Day Air |
14 |
UPS Next Day Air Early |
59 |
UPS 2nd Day Air AM |
12 |
UPS 3 Day Select |
65 |
UPS Saver (International) |
07 |
UPS Worldwide Express |
08 |
UPS Worldwide Expedited |
11 |
UPS Standard (International) |
54 |
UPS Worldwide Express Plus |
Testing¶
Your Client ID and Secret work for both sandbox and production. Sandbox returns simulated rates; production rates reflect your negotiated pricing.
Rate Fetching at Checkout¶
When the customer's shipping address is known, IShippingQuoteService fetches rates from all applicable providers:
var quotes = await shippingQuoteService.GetQuotesForWarehouseAsync(
new GetWarehouseQuotesParameters
{
WarehouseId = warehouseId,
Items = shippableItems,
DestinationAddress = shippingAddress,
CurrencyCode = currencyCode
},
cancellationToken);
The quotes are combined with flat-rate options and presented to the customer as a unified list.
Quoted Rate Caching¶
When a customer selects a dynamic shipping option at checkout, the quoted rate is stored in the checkout session. This ensures the customer is charged the rate they saw, even if carrier rates change between selection and payment.
The QuotedCosts dictionary in the shipping selection request preserves these rates:
Selection Key Contract¶
Shipping selection keys follow a stable contract that is parsed into order fields (ShippingProviderKey, ShippingServiceCode, ShippingServiceName):
- Flat-rate:
so:{guid} - Dynamic:
dyn:{provider}:{serviceCode}
Fallback Behavior¶
If a dynamic provider fails to return rates (API timeout, invalid credentials, etc.), the system:
- Logs a warning
- Marks the option with
IsFallbackRate = trueand aFallbackReason - Falls back to flat-rate options for that group
- The customer can still complete checkout using flat-rate shipping
The checkout UI can display a message when fallback rates are used (check HasFallbackRates on the shipping group DTO).
Currency Conversion¶
Carrier APIs may return rates in a different currency than your store currency. The system:
- Detects the currency mismatch between the carrier response and store currency
- Looks up the exchange rate from
IExchangeRateCache - Converts the rate to the store currency
- Applies any further display currency conversion for the customer
Creating a Custom Dynamic Provider¶
To add support for another carrier, implement IShippingProvider (or extend ShippingProviderBase for sensible defaults):
public class MyCarrierProvider : ShippingProviderBase
{
public override ShippingProviderMetadata Metadata => new()
{
Key = "mycarrier",
DisplayName = "My Carrier",
SupportsRealTimeRates = true,
RequiresFullAddress = true
};
public override async Task<ShippingRateQuote?> GetRatesAsync(
ShippingQuoteRequest request,
CancellationToken ct)
{
// Call your carrier's API
// Return a ShippingRateQuote with service levels
}
}
Your provider is automatically discovered by ExtensionManager and available for configuration in the backoffice.
Blocking Dynamic Options per Product¶
Set ProductRoot.AllowExternalCarrierShipping = false to prevent dynamic carrier options from appearing for specific products. Those products will only show flat-rate shipping options at checkout.
Invariant (CLAUDE.md):
AllowExternalCarrierShipping = falsemust suppress dynamic options for the entire shipping group containing that product. The default order grouping strategy enforces this -- custom strategies must honour it too.
IShippingProvider Reference¶
Required Members¶
| Member | Purpose |
|---|---|
Metadata |
Provider name, key, capabilities |
IsAvailableFor(request) |
Quick check if provider can service the request |
GetRatesAsync(request) |
Fetch all available rates |
Configuration Members¶
| Member | Purpose |
|---|---|
GetConfigurationFieldsAsync() |
Global config fields (API keys, etc.) |
GetMethodConfigFieldsAsync() |
Per-method config fields |
GetSupportedServiceTypesAsync() |
List all service types |
ConfigureAsync(config) |
Apply saved configuration |
Extended Rate Methods¶
| Method | Purpose |
|---|---|
GetRatesForServicesAsync(request, services, options) |
Fetch rates for specific service types |
GetRatesForAllServicesAsync(request, warehouseConfig) |
Fetch all rates with warehouse config applied (markup, exclusions) |
GetAvailableServicesAsync(origin, destination) |
Discover available services for a route |
Delivery Date Methods¶
| Method | Purpose |
|---|---|
GetAvailableDeliveryDatesAsync(request, service) |
Get selectable delivery dates |
CalculateDeliveryDateSurchargeAsync(request, service, date) |
Calculate surcharge for a specific date |
ValidateDeliveryDateAsync(request, service, date) |
Validate a delivery date is still available |
See the full interface at IShippingProvider.cs.
Related Topics¶
- Shipping Overview
- Flat Rate Shipping
- Package Configuration -- weight/dimensions used by carrier APIs
- Creating Shipping Providers