{"id":2926,"date":"2026-08-22T09:00:00","date_gmt":"2026-08-22T14:00:00","guid":{"rendered":"https:\/\/tolinku.com\/blog\/?p=2926"},"modified":"2026-03-15T00:48:01","modified_gmt":"2026-03-15T05:48:01","slug":"ecommerce-sdk-integration-guide","status":"publish","type":"post","link":"https:\/\/tolinku.com\/blog\/ecommerce-sdk-integration-guide\/","title":{"rendered":"Integrating E-Commerce Event Tracking Across Five Platforms"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">E-commerce event tracking gives you visibility into how users interact with your products: what they view, what they add to cart, what they buy, and what they return. Tolinku&#39;s SDKs support 13 e-commerce event types across five platforms (Web, React Native, iOS, Android, Flutter), with consistent APIs and automatic features like cart ID management, event batching, and background flushing.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This guide covers the event types, when to fire each one, and platform-specific integration code. If you are starting from scratch, begin with the <a href=\"https:\/\/tolinku.com\/docs\/developer\/sdks\/\">SDK documentation<\/a>. For the backend API, see the <a href=\"https:\/\/tolinku.com\/docs\/developer\/api-reference\/ecommerce\/\">e-commerce API reference<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The 13 E-Commerce Event Types<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each event type captures a specific step in the shopping journey. Fire them at the right moments to build a complete picture of your e-commerce funnel.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Event<\/th>\n<th>When to Fire<\/th>\n<th>Required Fields<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td><code>product_viewed<\/code><\/td>\n<td>User views a product detail page<\/td>\n<td>item_id, name, price, currency<\/td>\n<\/tr>\n<tr>\n<td><code>product_list_viewed<\/code><\/td>\n<td>User views a category or search results page<\/td>\n<td>list_name, items[]<\/td>\n<\/tr>\n<tr>\n<td><code>product_clicked<\/code><\/td>\n<td>User taps a product from a list<\/td>\n<td>item_id, name, list_name<\/td>\n<\/tr>\n<tr>\n<td><code>product_added<\/code><\/td>\n<td>User adds an item to their cart<\/td>\n<td>item_id, name, price, currency, quantity<\/td>\n<\/tr>\n<tr>\n<td><code>product_removed<\/code><\/td>\n<td>User removes an item from their cart<\/td>\n<td>item_id, quantity<\/td>\n<\/tr>\n<tr>\n<td><code>cart_viewed<\/code><\/td>\n<td>User views their cart<\/td>\n<td>items[], cart_total, currency<\/td>\n<\/tr>\n<tr>\n<td><code>checkout_started<\/code><\/td>\n<td>User begins the checkout flow<\/td>\n<td>items[], cart_total, currency<\/td>\n<\/tr>\n<tr>\n<td><code>checkout_step_completed<\/code><\/td>\n<td>User completes a checkout step (shipping, payment, etc.)<\/td>\n<td>step_number, step_name<\/td>\n<\/tr>\n<tr>\n<td><code>payment_info_entered<\/code><\/td>\n<td>User enters payment information<\/td>\n<td>payment_method<\/td>\n<\/tr>\n<tr>\n<td><code>purchase<\/code><\/td>\n<td>Order is confirmed<\/td>\n<td>order_id, total, currency, items[]<\/td>\n<\/tr>\n<tr>\n<td><code>refund<\/code><\/td>\n<td>A refund is processed<\/td>\n<td>order_id, total, currency<\/td>\n<\/tr>\n<tr>\n<td><code>coupon_applied<\/code><\/td>\n<td>User applies a discount code<\/td>\n<td>coupon_code, discount_amount, currency<\/td>\n<\/tr>\n<tr>\n<td><code>coupon_removed<\/code><\/td>\n<td>User removes a discount code<\/td>\n<td>coupon_code<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">These event names follow the conventions established by Google&#39;s <a href=\"https:\/\/developers.google.com\/analytics\/devguides\/collection\/ga4\/ecommerce\" rel=\"nofollow noopener\" target=\"_blank\">GA4 e-commerce events<\/a> specification, so they will be familiar to most analytics teams.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Purchase Event in Detail<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The <code>purchase<\/code> event is the most important event in your e-commerce tracking. It closes the funnel and generates revenue data. Here is the complete field specification.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Required Fields<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Type<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>order_id<\/td>\n<td>string<\/td>\n<td>Unique order identifier (must be unique across your system)<\/td>\n<\/tr>\n<tr>\n<td>total<\/td>\n<td>number<\/td>\n<td>Order total in the currency&#39;s smallest unit (e.g., 8999 = $89.99)<\/td>\n<\/tr>\n<tr>\n<td>currency<\/td>\n<td>string<\/td>\n<td>ISO 4217 currency code (e.g., &quot;USD&quot;, &quot;JPY&quot;, &quot;EUR&quot;)<\/td>\n<\/tr>\n<tr>\n<td>items<\/td>\n<td>array<\/td>\n<td>Array of purchased item objects<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Optional Fields<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Type<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>subtotal<\/td>\n<td>number<\/td>\n<td>Order subtotal before tax and shipping<\/td>\n<\/tr>\n<tr>\n<td>tax<\/td>\n<td>number<\/td>\n<td>Tax amount<\/td>\n<\/tr>\n<tr>\n<td>shipping<\/td>\n<td>number<\/td>\n<td>Shipping cost<\/td>\n<\/tr>\n<tr>\n<td>discount<\/td>\n<td>number<\/td>\n<td>Discount amount applied<\/td>\n<\/tr>\n<tr>\n<td>coupon_code<\/td>\n<td>string<\/td>\n<td>Coupon or promo code used<\/td>\n<\/tr>\n<tr>\n<td>payment_method<\/td>\n<td>string<\/td>\n<td>Payment method (e.g., &quot;credit_card&quot;, &quot;apple_pay&quot;)<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Item Object<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Each item in the <code>items<\/code> array contains:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Field<\/th>\n<th>Type<\/th>\n<th>Required<\/th>\n<th>Description<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>item_id<\/td>\n<td>string<\/td>\n<td>Yes<\/td>\n<td>SKU or product ID<\/td>\n<\/tr>\n<tr>\n<td>name<\/td>\n<td>string<\/td>\n<td>Yes<\/td>\n<td>Product name<\/td>\n<\/tr>\n<tr>\n<td>price<\/td>\n<td>number<\/td>\n<td>Yes<\/td>\n<td>Unit price in smallest currency unit<\/td>\n<\/tr>\n<tr>\n<td>quantity<\/td>\n<td>number<\/td>\n<td>Yes<\/td>\n<td>Quantity purchased<\/td>\n<\/tr>\n<tr>\n<td>category<\/td>\n<td>string<\/td>\n<td>No<\/td>\n<td>Product category<\/td>\n<\/tr>\n<tr>\n<td>brand<\/td>\n<td>string<\/td>\n<td>No<\/td>\n<td>Product brand<\/td>\n<\/tr>\n<tr>\n<td>variant<\/td>\n<td>string<\/td>\n<td>No<\/td>\n<td>Product variant (e.g., &quot;Blue \/ Large&quot;)<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">SDK Setup by Platform<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Web (JavaScript\/TypeScript)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Install the SDK:<\/p>\n\n\n\n<pre><code class=\"language-bash\">npm install @tolinku\/web-sdk\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Initialize and track events:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">import { Tolinku } from &#39;@tolinku\/web-sdk&#39;;\n\nconst tolinku = new Tolinku({ apiKey: &#39;tolk_pub_your_key&#39; });\ntolinku.setUserId(&#39;usr_789&#39;);\n\n\/\/ Track a product view\nawait tolinku.ecommerce.viewItem({\n  items: [{ item_id: &#39;SKU-001&#39;, item_name: &#39;Premium Widget&#39;, price: 49.99, item_category: &#39;widgets&#39; }]\n});\n\n\/\/ Track adding to cart\nawait tolinku.ecommerce.addToCart({\n  items: [{ item_id: &#39;SKU-001&#39;, item_name: &#39;Premium Widget&#39;, price: 49.99, quantity: 1 }]\n});\n\n\/\/ Track a purchase\nawait tolinku.ecommerce.purchase({\n  transaction_id: &#39;ORD-2026-1234&#39;,\n  revenue: 54.98,\n  currency: &#39;USD&#39;,\n  tax: 4.99,\n  items: [\n    { item_id: &#39;SKU-001&#39;, item_name: &#39;Premium Widget&#39;, price: 49.99, quantity: 1, item_category: &#39;widgets&#39; }\n  ]\n});\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Events are batched automatically. On page unload, <code>navigator.sendBeacon<\/code> ensures queued events are delivered. Cart IDs are managed via <code>sessionStorage<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">React Native<\/h3>\n\n\n\n<pre><code class=\"language-bash\">npm install @tolinku\/react-native-sdk @react-native-async-storage\/async-storage\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-typescript\">import { Tolinku } from &#39;@tolinku\/react-native-sdk&#39;;\n\nTolinku.init({ apiKey: &#39;tolk_pub_your_key&#39; });\nTolinku.setUserId(&#39;usr_789&#39;);\n\n\/\/ Track a product view\nawait Tolinku.ecommerce.viewItem({\n  items: [{ item_id: &#39;SKU-001&#39;, item_name: &#39;Premium Widget&#39;, price: 49.99 }]\n});\n\n\/\/ Track a purchase\nawait Tolinku.ecommerce.purchase({\n  transaction_id: &#39;ORD-2026-1234&#39;,\n  revenue: 54.98,\n  currency: &#39;USD&#39;,\n  items: [{ item_id: &#39;SKU-001&#39;, item_name: &#39;Premium Widget&#39;, price: 49.99, quantity: 1 }]\n});\n\n\/\/ Force flush\nawait Tolinku.ecommerce.flush();\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The React Native SDK auto-flushes when the app enters the background via <code>AppState<\/code>. Cart IDs are persisted in <code>AsyncStorage<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">iOS (Swift)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add via Swift Package Manager: <code>https:\/\/github.com\/tolinku\/ios-sdk<\/code><\/p>\n\n\n\n<pre><code class=\"language-swift\">import TolinkuSDK\n\ntry Tolinku.configure(apiKey: &quot;tolk_pub_your_key&quot;)\nlet tolinku = try Tolinku.requireShared()\ntolinku.setUserId(&quot;usr_789&quot;)\n\n\/\/ Track a product view\nawait tolinku.ecommerce.viewItem(\n    items: [TolinkuItem(itemId: &quot;SKU-001&quot;, itemName: &quot;Premium Widget&quot;, price: 49.99, itemCategory: &quot;widgets&quot;)]\n)\n\n\/\/ Track a purchase\nawait tolinku.ecommerce.purchase(\n    transactionId: &quot;ORD-2026-1234&quot;,\n    revenue: 54.98,\n    currency: &quot;USD&quot;,\n    tax: 4.99,\n    items: [TolinkuItem(itemId: &quot;SKU-001&quot;, itemName: &quot;Premium Widget&quot;, price: 49.99, quantity: 1)]\n)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Money values use Swift <code>Decimal<\/code> for precision. Cart IDs are persisted in <code>UserDefaults<\/code>. The SDK auto-flushes when the app enters the background. For iOS deep link setup, see Apple&#39;s <a href=\"https:\/\/developer.apple.com\/documentation\/xcode\/supporting-universal-links-in-your-app\" rel=\"nofollow noopener\" target=\"_blank\">Universal Links documentation<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Android (Kotlin)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add via Gradle:<\/p>\n\n\n\n<pre><code class=\"language-kotlin\">implementation(&quot;com.tolinku:sdk:0.1.0&quot;)\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-kotlin\">import com.tolinku.sdk.Tolinku\nimport com.tolinku.sdk.TolinkuItem\n\nTolinku.configure(apiKey = &quot;tolk_pub_your_key&quot;, context = applicationContext)\nTolinku.setUserId(&quot;usr_789&quot;)\n\n\/\/ Track a product view\nTolinku.ecommerce.viewItem(\n    items = listOf(TolinkuItem(itemId = &quot;SKU-001&quot;, itemName = &quot;Premium Widget&quot;, price = BigDecimal(&quot;49.99&quot;)))\n)\n\n\/\/ Track a purchase\nTolinku.ecommerce.purchase(\n    transactionId = &quot;ORD-2026-1234&quot;,\n    revenue = BigDecimal(&quot;54.98&quot;),\n    currency = &quot;USD&quot;,\n    tax = BigDecimal(&quot;4.99&quot;),\n    items = listOf(TolinkuItem(itemId = &quot;SKU-001&quot;, itemName = &quot;Premium Widget&quot;, price = BigDecimal(&quot;49.99&quot;), quantity = 1))\n)\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Money values use <code>BigDecimal<\/code> for precision. Cart IDs are persisted in <code>SharedPreferences<\/code>. The SDK auto-flushes via <code>ActivityLifecycleCallbacks<\/code>. For Android deep link setup, see Google&#39;s <a href=\"https:\/\/developer.android.com\/training\/app-links\" rel=\"nofollow noopener\" target=\"_blank\">App Links documentation<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Flutter (Dart)<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Add to <code>pubspec.yaml<\/code>:<\/p>\n\n\n\n<pre><code class=\"language-yaml\">dependencies:\n  tolinku: ^0.1.0\n<\/code><\/pre>\n\n\n\n<pre><code class=\"language-dart\">import &#39;package:tolinku\/tolinku.dart&#39;;\n\nTolinku.configure(apiKey: &#39;tolk_pub_your_key&#39;);\nTolinku.instance.setUserId(&#39;usr_789&#39;);\n\n\/\/ Track a product view\nawait Tolinku.instance.ecommerce.viewItem(\n  items: [TolinkuItem(itemId: &#39;SKU-001&#39;, itemName: &#39;Premium Widget&#39;, price: 49.99, itemCategory: &#39;widgets&#39;)],\n);\n\n\/\/ Track a purchase\nawait Tolinku.instance.ecommerce.purchase(\n  transactionId: &#39;ORD-2026-1234&#39;,\n  revenue: 54.98,\n  currency: &#39;USD&#39;,\n  tax: 4.99,\n  items: [TolinkuItem(itemId: &#39;SKU-001&#39;, itemName: &#39;Premium Widget&#39;, price: 49.99, quantity: 1)],\n);\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Cart IDs are persisted in <code>SharedPreferences<\/code>. For lifecycle-aware flushing, add <code>Tolinku.instance.ecommerce.flush()<\/code> to your <code>WidgetsBindingObserver.didChangeAppLifecycleState<\/code> handler.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cart ID Lifecycle<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The SDK automatically manages a cart ID to group cart-related events together. Understanding the lifecycle prevents common tracking errors.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How It Works<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Auto-generated on first <code>product_added<\/code><\/strong>: When the user adds their first item, the SDK generates a unique cart ID (e.g., <code>cart_a1b2c3d4<\/code>)<\/li>\n<li><strong>Persisted across sessions<\/strong>: The cart ID is stored locally (localStorage on web, UserDefaults\/SharedPreferences on mobile) so it survives app restarts<\/li>\n<li><strong>Attached to all cart events<\/strong>: Every <code>product_added<\/code>, <code>product_removed<\/code>, <code>cart_viewed<\/code>, <code>checkout_started<\/code>, and <code>purchase<\/code> event includes this cart ID<\/li>\n<li><strong>Cleared after <code>purchase<\/code><\/strong>: When a purchase completes, the cart ID is cleared. The next <code>product_added<\/code> generates a new one<\/li>\n<li><strong>Cleared after timeout<\/strong>: If no cart activity occurs for the configured timeout (default: 30 minutes), the cart is marked as abandoned and the ID is eventually cleared<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Why This Matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The cart ID links together the full sequence of events from first &quot;add to cart&quot; to purchase (or abandonment). Without it, you cannot:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Calculate cart abandonment rates accurately<\/li>\n<li>Track how many items were added\/removed before purchase<\/li>\n<li>Associate a specific cart with a recovery campaign<\/li>\n<li>Measure time-to-purchase from first cart interaction<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Event Batching<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The SDK does not send each event individually. It batches events to reduce network overhead and improve performance.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Batching Rules<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Trigger<\/th>\n<th>Condition<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>Batch size<\/td>\n<td>10 events accumulated<\/td>\n<\/tr>\n<tr>\n<td>Timer<\/td>\n<td>5 seconds since the last flush<\/td>\n<\/tr>\n<tr>\n<td>App lifecycle<\/td>\n<td>App goes to background (mobile) or page unloads (web)<\/td>\n<\/tr>\n<tr>\n<td>Manual flush<\/td>\n<td>Calling <code>tolinku.flush()<\/code><\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Whichever condition is met first triggers the flush. For most apps, the 5-second timer is the primary trigger during active use, and the lifecycle event catches anything remaining when the user leaves.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Why Batching Matters<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Without batching, an active shopping session (view product, add to cart, view another product, add to cart, view cart, start checkout) would generate 6 individual HTTP requests in a few seconds. Batching reduces this to 1 request containing 6 events. This is better for battery life, network usage, and server load.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Background Flushing<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">What happens when the user leaves mid-session? Unsent events need to be delivered before they are lost.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Web: sendBeacon<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">On web, the SDK uses the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Navigator\/sendBeacon\" rel=\"nofollow noopener\" target=\"_blank\">Navigator.sendBeacon() API<\/a> during the <code>visibilitychange<\/code> event. <code>sendBeacon<\/code> is designed for exactly this purpose: it sends data asynchronously without delaying page unload. Unlike <code>fetch<\/code> or <code>XMLHttpRequest<\/code>, <code>sendBeacon<\/code> requests are not cancelled when the page navigates away.<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ The SDK handles this automatically. You do not need to call it manually.\ndocument.addEventListener(&#39;visibilitychange&#39;, () =&gt; {\n  if (document.visibilityState === &#39;hidden&#39;) {\n    tolinku.flush(); \/\/ Uses sendBeacon internally\n  }\n});\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Mobile: AppState Listeners<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">On iOS, the SDK listens for <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiapplication\/1622973-willresignactivenotification\" rel=\"nofollow noopener\" target=\"_blank\">UIApplication.willResignActiveNotification<\/a> to flush events when the app moves to the background. On Android, it uses <a href=\"https:\/\/developer.android.com\/reference\/androidx\/lifecycle\/ProcessLifecycleOwner\" rel=\"nofollow noopener\" target=\"_blank\">ProcessLifecycleOwner<\/a> to detect the same transition.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">React Native and Flutter use their respective platform bridges to achieve the same behavior (<code>AppState<\/code> listener in React Native, <code>WidgetsBindingObserver<\/code> in Flutter).<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Persistence<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If the flush fails (network error, timeout), the SDK persists unsent events locally and retries on the next app launch. Events are not lost unless the user uninstalls the app before the retry succeeds.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Error Recovery and Retry<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Network requests fail. The SDK handles this with an exponential backoff retry strategy:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Attempt<\/th>\n<th>Delay<\/th>\n<th>Max Events Retained<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>1<\/td>\n<td>Immediate<\/td>\n<td>N\/A<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>1 second<\/td>\n<td>1,000<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>5 seconds<\/td>\n<td>1,000<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>30 seconds<\/td>\n<td>1,000<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td>2 minutes<\/td>\n<td>1,000<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After 5 failed attempts, the SDK stops retrying for that batch and moves on. The 1,000-event limit prevents the local queue from consuming excessive storage on the device.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For server-side errors (5xx), the SDK retries. For client-side errors (4xx), it does not retry (the request is malformed and retrying will not help). The exception is 429 (rate limit), which is retried after the <code>Retry-After<\/code> header duration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing Your Integration<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Enable Debug Mode<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">All SDKs support a debug mode that logs events to the console instead of (or in addition to) sending them to the server:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Web\nconst tolinku = new Tolinku({\n  appKey: &#39;tolk_pub_your_key&#39;,\n  debug: true  \/\/ Logs events to console\n});\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. Verify Event Structure<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Common issues to check for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Amounts in smallest unit<\/strong>: $89.99 should be sent as <code>8999<\/code>, not <code>89.99<\/code><\/li>\n<li><strong>Currency as ISO 4217<\/strong>: Use <code>&quot;USD&quot;<\/code>, not <code>&quot;$&quot;<\/code> or <code>&quot;dollars&quot;<\/code><\/li>\n<li><strong>Unique order IDs<\/strong>: Each purchase must have a unique <code>order_id<\/code>. Duplicates are deduplicated on the server.<\/li>\n<li><strong>Items array is populated<\/strong>: A purchase with an empty items array will be accepted but will not generate product-level analytics<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Walk Through the Full Funnel<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Track a complete shopping journey in debug mode and verify each event:<\/p>\n\n\n\n<pre><code>product_list_viewed \u2192 product_clicked \u2192 product_viewed \u2192\nproduct_added \u2192 cart_viewed \u2192 checkout_started \u2192\ncheckout_step_completed (x2-3) \u2192 payment_info_entered \u2192 purchase\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Verify that the cart ID is consistent across all cart-related events and that it clears after the purchase.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Test Edge Cases<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table>\n<thead>\n<tr>\n<th>Scenario<\/th>\n<th>Expected Behavior<\/th>\n<\/tr>\n<\/thead>\n<tbody><tr>\n<td>User adds item, closes app, reopens<\/td>\n<td>Cart ID persists, cart events continue<\/td>\n<\/tr>\n<tr>\n<td>Network disconnected during purchase<\/td>\n<td>Event queued locally, retried on reconnect<\/td>\n<\/tr>\n<tr>\n<td>User adds then removes the same item<\/td>\n<td>Both events tracked, cart state is accurate<\/td>\n<\/tr>\n<tr>\n<td>Duplicate purchase event (same order_id)<\/td>\n<td>Server deduplicates, counted once<\/td>\n<\/tr>\n<tr>\n<td>Zero-quantity product_added<\/td>\n<td>Rejected by SDK validation<\/td>\n<\/tr>\n<\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Common Mistakes<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Missing User ID<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If you do not set a user ID before tracking e-commerce events, the events are tracked as anonymous. This means you cannot:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Build user-level segments<\/li>\n<li>Calculate per-user LTV<\/li>\n<li>Attribute purchases to referral campaigns<\/li>\n<li>Send user-targeted webhooks<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Set the user ID as soon as the user authenticates:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">tolinku.setUserId(&#39;usr_789&#39;);\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Wrong Currency Format<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The SDK accepts standard decimal amounts (49.99), not cents (4999). The platform handles cents conversion internally. Send the amount as your users see it:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Correct: use the display amount\nawait tolinku.ecommerce.purchase({\n  transaction_id: &#39;order_123&#39;,\n  revenue: 89.99, \/\/ $89.99, not 8999\n  currency: &#39;USD&#39;,\n  items: [{ item_id: &#39;SKU-001&#39;, price: 89.99, quantity: 1 }]\n});\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For zero-decimal currencies like JPY, send the whole number:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">\/\/ Correct for JPY\nawait tolinku.ecommerce.purchase({\n  transaction_id: &#39;order_456&#39;,\n  revenue: 9800, \/\/ 9,800 yen\n  currency: &#39;JPY&#39;,\n  items: [{ item_id: &#39;SKU-002&#39;, price: 9800, quantity: 1 }]\n});\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Forgetting to Flush<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">In single-page applications, the <code>visibilitychange<\/code> event handles flushing automatically. But if you are navigating with full page reloads (server-rendered pages), events tracked just before navigation may be lost. Call <code>tolinku.flush()<\/code> before programmatic navigation:<\/p>\n\n\n\n<pre><code class=\"language-javascript\">async function handlePurchase() {\n  await tolinku.ecommerce.purchase({ \/* ... *\/ });\n  await tolinku.ecommerce.flush(); \/\/ Ensure the event is sent\n  window.location.href = &#39;\/order-confirmation&#39;;\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Not Tracking the Full Funnel<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Some teams only track the <code>purchase<\/code> event. This gives you revenue data but no funnel visibility. You cannot calculate:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Product view to purchase conversion rate<\/li>\n<li>Add-to-cart rate<\/li>\n<li>Checkout abandonment rate<\/li>\n<li>Which checkout step loses the most users<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Track at minimum: <code>view_item<\/code>, <code>add_to_cart<\/code>, <code>begin_checkout<\/code>, and <code>purchase<\/code>. The other events add detail but these four give you the core funnel.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">E-commerce event tracking is the foundation for revenue analytics, audience segmentation, webhook automation, and attribution. The 13 event types cover the complete shopping journey, from product discovery through purchase and refund.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The key integration points: set the user ID early (required for attribution), use standard decimal amounts for revenue (the SDK handles cents conversion internally), track the full funnel (not just purchases), and let the SDK handle cart IDs, batching, and background flushing automatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For SDK reference and installation guides, see the <a href=\"https:\/\/tolinku.com\/docs\/developer\/sdks\/\">SDK documentation<\/a>. For the server-side API, see the <a href=\"https:\/\/tolinku.com\/docs\/developer\/api-reference\/ecommerce\/\">e-commerce API reference<\/a>. For what you can do with the data once it is flowing, see the <a href=\"https:\/\/tolinku.com\/features\/ecommerce-analytics\">e-commerce analytics features<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Set up e-commerce event tracking with Tolinku SDKs across Web, React Native, iOS, Android, and Flutter. Code examples for all 13 event types, cart ID lifecycle, batching, and background flushing.<\/p>\n","protected":false},"author":2,"featured_media":2925,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"Integrating E-Commerce Event Tracking Across Five Platforms","rank_math_description":"Set up e-commerce event tracking with Tolinku SDKs across Web, React Native, iOS, Android, and Flutter. Code examples for all 13 event types, cart management, and batching.","rank_math_focus_keyword":"e-commerce SDK integration","rank_math_canonical_url":"","rank_math_facebook_title":"","rank_math_facebook_description":"","rank_math_facebook_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-ecommerce-sdk-integration-guide.png","rank_math_facebook_image_id":"","rank_math_twitter_title":"","rank_math_twitter_description":"","rank_math_twitter_image":"https:\/\/tolinku.com\/blog\/wp-content\/uploads\/2026\/03\/og-ecommerce-sdk-integration-guide.png","footnotes":""},"categories":[15],"tags":[37,156,20,58,669,188,69,186,371],"class_list":["post-2926","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","tag-analytics","tag-cross-platform","tag-deep-linking","tag-e-commerce","tag-event-tracking","tag-integration","tag-mobile-development","tag-sdk","tag-web-development"],"_links":{"self":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/2926","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/comments?post=2926"}],"version-history":[{"count":1,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/2926\/revisions"}],"predecessor-version":[{"id":2927,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/posts\/2926\/revisions\/2927"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media\/2925"}],"wp:attachment":[{"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/media?parent=2926"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/categories?post=2926"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tolinku.com\/blog\/wp-json\/wp\/v2\/tags?post=2926"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}