Ecommerce Activity Tracking
Reteno ecommerce events describe the customer journey through a product catalog, cart, and order flow. Send every supported event through the instance API:
await Reteno().logEcommerceEvent(event);The SDK timestamps the event itself; only an order payload carries its own date. The returned Future completes after the event is dispatched to the native SDK, not after the backend ingests it.
The Dart models use double, but iOS narrows every monetary value to a 32-bit Float while Android keeps Double. Use ordinary currency-scale values.
Supported Events
| Event class | Use |
|---|---|
RetenoEcommerceProductViewed | Product card viewed |
RetenoEcommerceProductCategoryViewed | Product-category/listing page viewed |
RetenoEcommerceProductAddedToWishlist | Product added to a wishlist |
RetenoEcommerceCartUpdated | Cart contents changed |
RetenoEcommerceOrderCreated | Order created |
RetenoEcommerceOrderUpdated | Existing order updated |
RetenoEcommerceOrderDelivered | Order marked delivered |
RetenoEcommerceOrderCancelled | Order marked cancelled |
RetenoEcommerceSearchRequest | Catalog search performed |
Product Viewed
final product = RetenoEcommerceProduct(
productId: 'PRODUCT_ID',
price: 20.0,
inStock: true,
attributes: {
'size': ['23', '42'],
'color': ['white', 'orange'],
},
);
await Reteno().logEcommerceEvent(
RetenoEcommerceProductViewed(
product: product,
currency: 'UAH',
),
);product is required; currency is optional and falls back to the organization default.
RetenoEcommerceProduct
| Property | Type | Description |
|---|---|---|
productId | String | Unique identifier of the product in your catalog. |
price | double | Current price including taxes and discounts. |
inStock | bool | true if the product is available for purchase. |
attributes | Map<String, List<String>>? | Required constructor argument; pass null or an attribute map such as { 'size': ['M'] }. |
Product Category Viewed
final category = RetenoEcommerceCategory(
productCategoryId: 'CATEGORY_ID',
attributes: {
'gender': ['kids'],
},
);
await Reteno().logEcommerceEvent(
RetenoEcommerceProductCategoryViewed(category: category),
);| Property | Type | Description |
|---|---|---|
productCategoryId | String | Category identifier (slug, code, or UUID). |
attributes | Map<String, List<String>>? | Optional category metadata. |
Product Added to Wishlist
await Reteno().logEcommerceEvent(
RetenoEcommerceProductAddedToWishlist(
product: product,
currency: 'EUR',
),
);This event uses the same RetenoEcommerceProduct model as Product Viewed.
Cart Updated
final cartProducts = [
const RetenoEcommerceProductInCart(
productId: 'uut',
price: 20.0,
quantity: 1,
),
const RetenoEcommerceProductInCart(
productId: 'lk',
price: 100.0,
quantity: 3,
discount: 10.0,
name: 'Example product',
category: 'CATEGORY_ID',
attributes: {
'color': ['orange'],
},
),
];
await Reteno().logEcommerceEvent(
RetenoEcommerceCartUpdated(
cartId: 'CART_ID',
products: cartProducts,
currency: 'UAH',
),
);cartId and products are required; currency is optional.
RetenoEcommerceProductInCart
| Property | Type | Description |
|---|---|---|
productId | String | Product identifier. |
price | double | Unit price before discount. |
quantity | int | Number of units; keep it within the signed 32-bit range. |
discount | double? | Discount per unit (absolute, optional). |
name | String? | Product name (optional). |
category | String? | Product category name or ID (optional). |
attributes | Map<String, List<String>>? | Extra attributes (optional). |
iOS forwards only productId, price, and quantity; the optional fields are dropped. Android forwards all of them.
Order Created or Updated
RetenoEcommerceOrderCreated and RetenoEcommerceOrderUpdated use the same payload. For a cross-platform date, send UTC ISO 8601 without fractional seconds:
final orderDate = DateTime.now()
.toUtc()
.toIso8601String()
.replaceFirst(RegExp(r'\.\d+Z$'), 'Z');
final order = RetenoEcommerceOrder(
externalOrderId: 'ORDER_ID',
totalCost: 300.0,
status: RetenoEcommerceOrderStatus.initialized,
date: orderDate,
cartId: 'CART_ID',
);
await Reteno().logEcommerceEvent(
RetenoEcommerceOrderCreated(
order: order,
currency: 'UAH',
),
);order is required; currency is optional.
RetenoEcommerceOrder
| Property | Type | Description |
|---|---|---|
externalOrderId | String | Order ID from your commerce backend. |
totalCost | double | Grand total charged to the customer. |
status | RetenoEcommerceOrderStatus | initialized, inProgress, delivered, or cancelled, sent as INITIALIZED, IN_PROGRESS, DELIVERED, and CANCELLED. |
date | String (ISO 8601) | Date and time the order was placed. |
cartId | String? | Cart that generated the order (optional). |
email, phone | String? | Customer contacts (optional). |
firstName, lastName | String? | Customer name (optional). |
shipping, discount, taxes | double? | Order price components (optional). |
restoreUrl, statusDescription | String? | Restore link and status details (optional). |
storeId, source | String? | Store and source identifiers (optional). |
deliveryMethod, paymentMethod, deliveryAddress | String? | Delivery and payment details (optional). |
items | List<RetenoEcommerceItem>? | Order line items (optional). |
attributes | Map<String, List<String>>? | Additional order attributes (optional). |
The model has no externalCustomerId property.
RetenoEcommerceItem requires externalItemId, name, category, url (String), and quantity, cost (double). It also accepts optional imageUrl and description.
iOS substitutes the current native time when date contains fractional seconds; Android also accepts offset and local ISO 8601 input. The format in the example above works on both platforms.
Order attributes are encoded differently per platform: Android joins each list into one comma-separated string, iOS sends {"values": [...]}. Do not use them for cross-platform segmentation.
Order Delivered or Cancelled
Both events take the existing external order ID:
await Reteno().logEcommerceEvent(
RetenoEcommerceOrderDelivered(externalOrderId: 'ORDER_ID'),
);
await Reteno().logEcommerceEvent(
RetenoEcommerceOrderCancelled(externalOrderId: 'ORDER_ID'),
);Search Request
query is required and isFound is nullable. Pass an explicit boolean: Android converts null to false, iOS preserves null.
await Reteno().logEcommerceEvent(
RetenoEcommerceSearchRequest(
query: 'iphone',
isFound: true,
),
);Custom Events
If the use case does not match a predefined ecommerce event, use the generic event API:
await Reteno().logEvent(
event: RetenoCustomEvent(
eventTypeKey: 'custom_event_type',
dateOccurred: DateTime.now(),
parameters: [
RetenoCustomEventParameter('parameter_name', 'value'),
],
),
);See Tracking User Behaviour for custom-event semantics and the iOS timestamp limitation.
Currency Codes
Use the supported ISO 4217 codes USD, EUR, or UAH. If currency is null, the organization's default currency is used.
