Customize Commerce Connect
Why customize Commerce Connect
Section titled “Why customize Commerce Connect”The pre-built connectors handle standard product sync and transaction routing. Your business likely has requirements that go beyond the standard — custom product attributes, complex pricing logic, multi-currency handling, or proprietary API formats. Custom connectors and event handlers let you adapt Commerce Connect to your specific integration needs without forking the framework.
What you will do
Section titled “What you will do”- Build a custom connector for a non-standard commerce platform
- Add custom data transformations for product sync
- Handle Commerce Connect events for real-time processing
- Extend the transaction proxy with custom logic
- Implement error handling and retry strategies
Build a custom connector
Section titled “Build a custom connector”If your commerce platform does not have a pre-built connector, implement the ICommerceConnector interface.
public class CustomPlatformConnector
: ICommerceConnector
{
private readonly HttpClient _httpClient;
private readonly ConnectorOptions _options;
public CustomPlatformConnector(
HttpClient httpClient,
IOptions<ConnectorOptions> options)
{
_httpClient = httpClient;
_options = options.Value;
}
public async Task<IEnumerable<ExternalProduct>>
GetProducts(SyncContext context)
{
var response = await _httpClient.GetAsync(
$"{_options.ApiUrl}/products" +
$"?modified_after={context.LastSyncUtc:O}");
response.EnsureSuccessStatusCode();
var data = await response.Content
.ReadFromJsonAsync<ProductResponse>();
return data.Products.Select(p =>
new ExternalProduct
{
ExternalId = p.Id.ToString(),
Name = p.Title,
Description = p.BodyHtml,
Slug = p.Handle,
Categories = p.Collections,
Variants = p.Variants.Select(v =>
MapVariant(v)).ToList(),
Images = p.Images.Select(i =>
new ProductImage {
Url = i.Src, Alt = i.Alt
}).ToList()
});
}
public async Task<CartResult> AddToCart(
string variantId, int quantity,
string sessionId)
{
var payload = new { variant_id = variantId,
quantity, session_id = sessionId };
var response = await _httpClient.PostAsJsonAsync(
$"{_options.ApiUrl}/cart/add", payload);
var result = await response.Content
.ReadFromJsonAsync<CartResponse>();
return new CartResult
{
Success = response.IsSuccessStatusCode,
CartId = result.CartId,
ItemCount = result.TotalItems
};
}
} Register your custom connector in the service collection.
services.AddCommerceConnect(options =>
{
options.Platform = CommercePlatform.Custom;
})
.AddConnector<CustomPlatformConnector>(); Add custom data transformations
Section titled “Add custom data transformations”Override the default data mapping when your commerce platform uses non-standard data structures.
public class CustomProductTransformer
: IProductTransformer
{
public CmsProduct Transform(
ExternalProduct source)
{
var product = new CmsProduct
{
Name = source.Name,
Code = source.ExternalId,
UrlSlug = source.Slug,
Description = SanitizeHtml(
source.Description),
Brand = source.Metadata
.GetValueOrDefault("vendor", ""),
};
// Custom logic: generate search-friendly tags
product.SearchTags = GenerateSearchTags(
source.Name,
source.Description,
source.Categories);
// Custom logic: map multi-currency pricing
product.Prices = source.Variants
.SelectMany(v => v.Prices
.Select(p => new ProductPrice
{
VariantCode = v.Sku,
Amount = p.Amount,
Currency = p.CurrencyCode,
CustomerGroup = p.PriceList
}))
.ToList();
return product;
}
private string[] GenerateSearchTags(
string name, string desc,
IEnumerable<string> categories)
{
var words = $"{name} {desc}"
.Split(' ', StringSplitOptions
.RemoveEmptyEntries)
.Where(w => w.Length > 3)
.Distinct(StringComparer.OrdinalIgnoreCase)
.Take(20);
return categories.Concat(words).ToArray();
}
} Handle Commerce Connect events
Section titled “Handle Commerce Connect events”Subscribe to events that fire during sync and transaction operations. Use events for logging, notifications, cache invalidation, or triggering downstream processes.
public class CommerceConnectEventHandlers
{
public static void Register(
ICommerceConnectEvents events)
{
// After product sync completes
events.OnProductSynced += async (product) =>
{
// Invalidate CDN cache for updated pages
await CdnPurge.PurgeProduct(
product.UrlSlug);
// Send to ODP for customer analytics
await OdpTracker.TrackProductUpdate(
product.Code, product.Name,
product.Categories);
};
// After category structure changes
events.OnCategorySynced += async (category) =>
{
await NavigationCache.Invalidate();
};
// When a sync error occurs
events.OnSyncError += (error) =>
{
Logger.Error(
"Sync failed for {ProductId}: {Error}",
error.ExternalId, error.Message);
if (error.IsTransient)
error.ScheduleRetry(
TimeSpan.FromMinutes(5));
};
// After order is placed through proxy
events.OnOrderPlaced += async (order) =>
{
await OdpTracker.TrackPurchase(
order.CustomerId,
order.OrderTotal,
order.LineItems);
};
}
} Extend the transaction proxy
Section titled “Extend the transaction proxy”Add custom logic to cart and checkout operations before they reach your commerce platform.
public class CustomTransactionProxy
: ITransactionProxy
{
private readonly ICommerceConnector _connector;
private readonly IExperimentationService _experiments;
public async Task<CartResult> AddToCart(
AddToCartRequest request)
{
// Run experimentation variation assignment
var variation = await _experiments
.GetVariation("checkout-flow-test",
request.SessionId);
// Apply variation-specific logic
if (variation == "express-checkout")
{
request.Metadata["checkout_type"] = "express";
}
// Track the add-to-cart event in ODP
await OdpTracker.Track("add_to_cart", new
{
product_id = request.VariantId,
quantity = request.Quantity,
session_id = request.SessionId
});
return await _connector.AddToCart(
request.VariantId,
request.Quantity,
request.SessionId);
}
} Implement error handling and retries
Section titled “Implement error handling and retries”Build resilient sync and transaction handling with retry logic and circuit breakers.
services.AddHttpClient<CustomPlatformConnector>()
.AddPolicyHandler(Policy
.Handle<HttpRequestException>()
.OrResult<HttpResponseMessage>(
r => r.StatusCode == HttpStatusCode
.TooManyRequests)
.WaitAndRetryAsync(3,
attempt => TimeSpan.FromSeconds(
Math.Pow(2, attempt)),
onRetry: (outcome, delay, attempt, ctx) =>
{
Logger.Warning(
"Retry {Attempt} after {Delay}s",
attempt, delay.TotalSeconds);
}))
.AddPolicyHandler(Policy
.Handle<HttpRequestException>()
.CircuitBreakerAsync(5,
TimeSpan.FromMinutes(1))); Common customization patterns
Section titled “Common customization patterns”| Pattern | When to use | Implementation |
|---|---|---|
| Multi-source catalog | Products come from multiple systems | Register multiple connectors with source priority |
| Price override | Optimizely controls some prices | Implement IPriceOverrideProvider |
| Inventory aggregation | Stock from multiple warehouses | Custom IInventoryAggregator |
| Custom checkout fields | B2B-specific checkout data | Extend the transaction proxy request model |
| Selective sync | Only sync specific categories | Add SyncFilter to connector options |