smartshopping-sdk
Version:
Coupon autoapply SDK
439 lines (307 loc) • 11.3 kB
Markdown
# 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>