UNPKG

smartshopping-sdk

Version:

Coupon autoapply SDK

439 lines (307 loc) 11.3 kB
# SmartShopping SDK ## Installation `npm i smartshopping-sdk` `yarn add smartshopping-sdk` ## Integration "storage", "tabs" and "alarms" permissions required To use the `checkAdblockAndCookie` function in Manifest V3 need add "scripting" permission and host_permissions in manifest: ``` "host_permissions": [ "*://*/*" ], ``` For Manifest V2 add "management" and "<all_urls>" permissions ### Background script Background script of extension using **SmartShopping SDK** might look like this: ``` import { bootstrap } from 'smartshopping-sdk'; import { requirePromocodes } from '../utils'; (async () => { const { install, startEngine, setCodes, checkAdblockAndCookie } = bootstrap({ clientID: 'demo', key: 'very secret key', }); install(); chrome.tabs.onUpdated.addListener(async (tabId, changeInfo) => { if (changeInfo.status === 'complete') { startEngine(tabId); } }); chrome.tabs.onReplaced.addListener(async (tabId) => { startEngine(tabId); }); chrome.runtime.onMessage.addListener( async (message, sender, sendResponse) => { const tabId = sender?.tab?.id; if (!tabId) { return; } if (message.type === 'ready_to_CAA') { const codes = await requirePromocodes(tabId); if (codes.length) { setCodes(tabId, codes); } } if (message.type === 'check_adblock_cookie') { checkAdblockAndCookie() .then(({ isAdblockDisabled, isCookieEnabled }) => sendResponse({ isAdblockDisabled, isCookieEnabled })) .catch(err => console.log(err)) } } ); })(); ``` `bootstrap` takes three arguments client ID, secret key (which is used for data decryption) and optional - serverUrl (not used by default). If you dont know your ID and secret key, contact **SmartShopping** tech support It returns four functions: - `install: () => Promise<void>` uploads and stores merchants data; also sets up message passing between background and content parts of **SmartShopping**. - `startEngine: (tabId: number) => Promise<void>` identifies merchant in an active tab, uploads corresponding config and initializes `Engine` in a content script. Takes browser tab ID as an argument. - `setCodes: (tabId: number, codes: string[]) => void` sends to the `Engine` a list of coupons Takes browser tab ID and array of promo codes as an arguments - `checkAdblockAndCookie: () => Promise<{ isAdblockDisabled: boolean; isCookieEnabled: boolean; }>;` function checks if Adblock extension is disabled and checks if cookies are enabled and returns an object with two boolean keys. In the example above `requirePromocodes` is a function defined by host extension, responsible for finding promocodes which will be passed to `setCodes`. ### Content Script In a content script you need to import an `engine` instance: ``` import { engine } from 'smartshopping-sdk'; ``` `engine` receives config object from background script and manages coupon autoapply flow or detect a successful coupon. Coupon autoapply flow stages: 1. `engine.inspect()` analyzing checkout page and collecting information; 2. `engine.apply()` applying promocodes and storing results into internal execution context; 3. `engine.applyBest()` choosing and applying best promocode; 4. `engine.productInspect()` product information extraction; All three stages can be executed consistently via `engine.fullCycle()`. Detect stage - `engine.detect()` detects if a user tried to apply a coupons; The execution of the stages: `detect`, `apply`, `applyBest` can be aborted using the `engine.abort()` method. Since it is up to you to show the modal window with the suggestion to apply coupons, call the `engine.notifyAboutShowModal()` method before it is shown so that we can get these statistics. And if the user closes the modal prompting them to apply codes, call the `engine.notifyAboutCloseModal()` method. When a user tries to apply a third-party coupon, you can show them a modal window notifying them that they are trying to apply a third-party coupon, to collect statistics you can use methods: 1. `notifyAboutShow3dPartyModal` - when opening the modal window; 2. `notifyAboutClose3dPartyModal` - when closing the modal window; 3. `notifyAboutReactivate3dParty` - when reactivating coupon (if you want to offer a user to reactivate a coupon); Config object looks like this: ``` type EngineConfig { version: number; shopId: string; shopName: string; shopUrl: string; checkoutUrl: string; productUrl: string; inspect: Array<Command>; detect: Array<Command>; product: Array<Command>; apply: Array<Command>; applyBest: Array<Command>; selectorsToCheck: Array<string>; } ``` - `version` version of config spec for intrinsic usage - `shopId` merchant's unique ID in **SmartShopping** database - `shopName` merchant's name - `shopUrl` RegEx matching all merchant's URLs - `checkoutUrl` RegEx matching merchant's checkout page - `productUrl` RegEx matching merchant's product page - `inspect`, `product`, `detect`, `apply` and `applyBest` arrays of commands for respective stages of auto apply flow. - `selectorsToCheck` array of selectors which are necessary to proceed. If any of them are invalid, execution stops with an error. `engine` stores info in the following properties: 1. `config: EngineConfig` Config object 2. `progress: EngineProgress` Execution status ``` type EngineProgress = | 'IDLE' | 'INSPECT' | 'INSPECT_END' | 'DETECT' | 'APPLY' | 'APPLY_END' | 'APPLY-BEST' | 'PRODUCT' | 'APPLY-BEST_END' | 'ERROR' | 'CANCEL'; ``` 3. `checkoutState: EngineCheckoutState` Info collected during `inspect` stage `checkoutState.total` cart total before applying any codes ``` type EngineCheckoutState { total: number | null; } ``` 4. `finalCost: EngineFinalCost` This object contains pairs `"promocode": "cart total after applying promocode"` ``` type EngineFinalCost = { [key: string]: number } ``` 5. `promocodes: Array<string>` Array of promocodes for testing 6. `currentCode: string` Promocode currently being processed 7. `detectState: EngineDetectState` Info collected during `detect` stage `detectState.userCode` - promocode entered by the user. If `detectState.userCode === 'UNDEFINED_CODE'` it means that we can't get the value of the code, but we know that it was entered `detectState.isValid` - validity of the code entered by the user. `true` if the code is successfully applied and `false` if the entered code turned out to be erroneous ``` type EngineDetectState { userCode: string | 'UNDEFINED_CODE'; isValid: boolean; } ``` 8. `bestCode: string` Most profitable promocode If none of the codes worked, `bestCode === ''` 9. `checkout: boolean | null` The flag is null if you are on a page for which we don't have a config yet. In other cases, the flag has a boolean value and indicates whether you are on the checkout page 10. `productState: EngineProductState` Info collected during `product` stage `productState.title` Product name `productState.fullPrice` Full Price `productState.discountPrice` Discount Price `productState.sku` SKU `productState.imageUrl` Image URL `productState.variants` Variants ``` type EngineProductState { title: null | string; fullPrice: null | number; discountPrice: null | number; sku: null | string; imageUrl: null | string; imageUrl: null | string; variants: EngineProductVariantGroup[]; } interface EngineProductVariantGroup { label: string; items: EngineProductVariantValue[]; } interface EngineProductVariantValue { label: string; } ``` You can subscribe to those properties' changes via `engine.subscribe()`... ``` const unbinders = engine.subscribe( { config: configListener, checkoutState: checkoutStateListener, finalCost: finalCostListener, promocodes: promocodesListener, progress: progressListener, currentCode: currentCodeListener, detectState: detectStateListener, bestCode: bestCodeListener, checkout: checkoutListener, product: productListener, } ); ``` ...and unsubscribe via `engine.unsubscribe()`, passing return value of `engine.subscribe()` as an argument: ``` engine.unsubscribe(unbinders); ``` Subscribing for properties is optional so you may only choose ones you need and skip the rest: ``` const unbinders = engine.subscribe( { progress: listener1, currentCode: listener2, bestCode: listener3, checkout: listener4, } ); ``` `listener` - functions look like this: ``` listener: (value: %property_type%, state: EngineState) => void; interface EngineState { checkoutState: EngineCheckoutState; productState: EngineProductState; finalCost: EngineFinalCost; progress: EngineProgress; config: EngineConfig; promocodes: Array<string>; detectState: EngineDetectState; bestCode: string; currentCode: string; checkout: boolean | null; } ``` You can see more in our [demo-extension](https://github.com/cupcakedev/smartshopping-demo) # Changelog <details> <summary>All notable changes to this project will be documented in this.</summary> ## [0.8.5] - 2025-06-19 ### Update - Update api url ## [0.8.4] - 2025-05-30 ### Fixed - Fixed duplicated variants data ## [0.8.3] - 2025-05-28 ### Fixed - Added support variants for manifest v3 ## [0.8.2] - 2025-05-27 ### Added - Added support product page variants ## [0.8.1] - 2025-05-16 ### Added - Added support product page parsing ## [0.8.0] - 2025-02-28 ### Added - Added support SmartShopping promocodes ## [0.7.0] - 2023-11-16 ### Changed - Engine initialization ### Added - Added support for user configs - Extended error log - SDK version in server request - Methods for 3d party coupons ## [0.6.11] - 2023-07-10 ### Added - Added Edge supports for adblock check ## [0.6.10] - 2023-07-06 ### Update - Update documentation - Update @types/chrome package ## [0.6.9] - 2023-06-29 ### Added - Export 'AdblockAndCookieOutput' type ## [0.6.8] - 2023-06-23 ### Changed - Now the checkout state is of type `boolean | null`. If we don't support the merchant from the page, we return `null` in the `checkoutListener` ## [0.6.7] - 2023-06-21 ### Fixed - `checkAdblockAndCookie` for use with Manifest V3 - Error in console - Update documentation ## [0.6.6] - 2023-06-15 ### Fixed - Error in console ## [0.6.5] - 2023-06-15 ### Added - Export `checkAdblockAndCookie` function ## [0.6.4] - 2023-05-29 ### Added - Export `BootstrapOutput` type ## [0.6.3] - 2023-05-26 ### Fixed - Optimizing the size of the data requested from the server ## [0.6.2] - 2023-03-03 ### Changed - Change method name from `sendСodes` to `setCodes` ## [0.6.1] - 2023-02-27 ### Added - README.md file with Changelog ## [0.6.0] - 2023-02-16 ### Added - `sendCodes` method ### Changed - Remove `codes` param from `startEngine` method ## [0.5.21] - 2023-01-24 ### Fixed - Cache work </details>