Àpèjúwe
NettoProfit is a WooCommerce profit intelligence plugin that shows the real bottom-line behind every sale.
FREE Features
Pixel Health Checker (FREE — no account needed)
Know immediately whether your Meta Pixel, Google Tag, TikTok Pixel, Pinterest Tag and Snapchat Pixel are firing correctly on every key WooCommerce page.
- Per-page event coverage matrix (Homepage, Shop, Product, Cart, Checkout)
- 0–100 health score with A–F grade
- Meta Event Match Quality (EMQ) field analysis — see exactly which fields are missing and how much they hurt attribution
- Google Consent Mode v2 compliance check (critical for EU/EEA traffic)
- Weekly automated scan with low-score email alerts
- One-click “Copy Report” to clipboard
PRO Features
- Real net profit per order — after COGS, shipping, payment fees, ad spend and custom expenses
- Campaign Ad Set Ad hierarchy with 6-period comparison
- Google Ads (including Performance Max), Meta Ads (including Advantage+), TikTok Ads (including Smart+) sync
- Product break-even analysis: break-even price, ad cost headroom, break-even CPA
- P&L waterfall report
- Multi-period ROAS, POAS, and CPA trending
- CSV export
- Profit-threshold alerts with email + admin notice delivery
PRO+AI Features
- Claude Sonnet AI Advisor — natural language profit questions answered instantly
- AI anomaly explainer — why did revenue drop? (with 3 actionable steps)
- AI ad copy generator — 3 variants per brief, one click to copy
- AI narrative report — weekly plain-English P&L summary
- Monthly token budget tracker with dev-mode cap
Technical Notes
- Requires WooCommerce High-Performance Order Storage (HPOS) — enabled by default in WC 8.2+
- WooCommerce Cart and Checkout Blocks compatible
- PSR-4 autoloaded PHP 8.3 codebase — no global function soup
- All DB queries use
$wpdb->prepare()— OWASP SQL injection prevention - AES-256-CBC encryption for all stored API keys
- No external CDN dependencies on the critical path
External Services
This plugin connects to several third-party services for the optional PRO and PRO+AI features. The free Pixel Health Checker does not send any data outside your own server.
NettoProfit License & AI Proxy
Used by the PRO and PRO+AI tiers to validate license keys and to relay AI requests to AnthropicÌtumọ̀ Yorùbá: ’s Claude API on the userÌtumọ̀ Yorùbá: ’s behalf.
- Service provider: NettoProfit. Terms of service: https://nettoprofit.com/terms/ — Privacy policy: https://nettoprofit.com/privacy-policy/.
- What is sent: license key (during validation), aggregated profit/loss numbers and the userÌtumọ̀ Yorùbá: ’s natural-language question (during AI Advisor calls). No customer personally identifiable information (PII) is sent.
- When it is sent: license validation runs once on key entry and at most once every 12 hours; AI calls run only when the site administrator clicks an AI feature.
- Endpoints: two private POST-only endpoints on the NettoProfit API host — one for license validation and one for the AI proxy. Both require a valid license key in the request body and respond 401 to unauthenticated requests; they are not browseable URLs.
Anthropic Claude API (proxied)
The PRO+AI tier sends aggregated P&L data and the administratorÌtumọ̀ Yorùbá: ’s prompt to AnthropicÌtumọ̀ Yorùbá: ’s Claude API through the NettoProfit proxy described above. No direct connection to Anthropic is made from the userÌtumọ̀ Yorùbá: ’s WordPress site.
- Service provider: Anthropic PBC. Terms of service: https://www.anthropic.com/legal/consumer-terms — Privacy policy: https://www.anthropic.com/legal/privacy.
- What is sent: aggregated metrics (revenue, COGS, ad spend, profit) and the prompt text — no order numbers, customer names, emails, or addresses.
- When it is sent: only when an administrator triggers an AI Advisor, Anomaly Explainer, Ad Copy Generator, or Report Narrator action.
Google Ads API
Used by the PRO tier (only when the administrator enters Google Ads API credentials) to import campaign, ad group, and ad spend data.
- Service provider: Google LLC. Terms of service: https://cloud.google.com/terms/ — Privacy policy: https://policies.google.com/privacy.
- What is sent: the credentials configured by the administrator (developer token, OAuth refresh token, customer ID) and standard Google Ads API queries.
- When it is sent: on a scheduled hourly sync and on manual “Sync now” clicks.
Meta Marketing API
Used by the PRO tier (only when the administrator enters Meta Ads credentials) to import ad spend.
- Service provider: Meta Platforms, Inc. Terms of service: https://www.facebook.com/legal/terms — Privacy policy: https://www.facebook.com/privacy/policy/.
- What is sent: the credentials configured by the administrator (App ID, App Secret, access token, ad account ID) and standard Meta Marketing API queries.
- When it is sent: on a scheduled hourly sync and on manual “Sync now” clicks.
TikTok Marketing API
Used by the PRO tier (only when the administrator enters TikTok Ads credentials) to import ad spend.
- Service provider: TikTok Pte. Ltd. Terms of service: https://ads.tiktok.com/i18n/official/policy/terms — Privacy policy: https://ads.tiktok.com/i18n/official/policy/privacy.
- What is sent: the credentials configured by the administrator (App ID, App Secret, access token) and standard TikTok Marketing API queries.
- When it is sent: on a scheduled hourly sync and on manual “Sync now” clicks.
Source Code
The bundled assets/js/chart.umd.min.js is the official minified UMD build of Chart.js v4.5.1 (MIT license). The complete human-readable source is available at https://github.com/chartjs/Chart.js (tag v4.5.1); the matching unminified build is dist/chart.umd.js in the chart.js@4.5.1 npm package. All other JavaScript and CSS in this plugin ships unminified.
Àwọn àwòrán ìbòjú





Ìgbéwọlẹ̀
- Upload the
nettoprofitfolder to/wp-content/plugins/. - Activate the plugin through the Plugins menu in WordPress.
- Navigate to NettoProfit Pixel Health for the FREE pixel scan (no configuration needed).
- For PRO features, go to NettoProfit Settings and enter your ad platform API credentials.
FAQ
-
Is the Pixel Health Checker really free?
-
Yes. It requires no account, no license key, and no API credentials. Install, click “Run Scan”, and see your health score immediately.
-
Does it work without WooCommerce?
-
No. NettoProfit is purpose-built for WooCommerce stores and requires WooCommerce 6.7+.
-
Does it require HPOS?
-
Yes. HPOS (Custom Order Tables) is required. It is enabled by default in WooCommerce 8.2 and later. The plugin will display a notice if HPOS is disabled.
-
What data is sent to external services?
-
The Pixel Health Checker fetches your own storeÌtumọ̀ Yorùbá: ’s public pages (via wp_remote_get) — no data leaves your server. The AI Advisor (PRO+AI) sends aggregated P&L numbers to AnthropicÌtumọ̀ Yorùbá: ’s Claude API — never customer PII.
-
Which ad platforms are supported?
-
Google Ads (including Performance Max campaigns), Meta Ads (including Advantage+ Shopping), TikTok Ads (including Smart+ campaigns), and manual UTM-tracked sources. Microsoft/Bing Ads available as a manual provider via UTM parameters.
Àwọn àgbéyẹ̀wò
Kò sí àwọn àgbéyẹ̀wò fún plugin yìí.
Àwọn Olùkópa & Olùgbéejáde
“NettoProfit” jẹ́ ètò ìṣàmúlò orísun ṣíṣí sílẹ̀. Àwọn ènìyàn wọ̀nyí ti ṣe ìkópa sí plugin yìí.
Àwọn OlùkópaTúmọ̀ “NettoProfit” sí èdè rẹ.
Ṣe o nífẹ̀ẹ́ sí ìdàgbàsókè?
Ṣàwárí koodu, ṣàyẹ̀wò ibi ìpamọ́ SVN, tàbí ṣe àgbékalẹ̀ sí àkọsílẹ̀ ìdàgbàsókè nípasẹ̀ RSS.
Àkọsílẹ̀ àwọn àyípadà
1.13.21
- Pixel Checker accuracy: the scan is now clearly labeled as a static scan of rendered page HTML; expected-event definitions list only page-load events (interaction events like AddToCart clicks or Purchase cannot be observed statically and are no longer reported as “missing”).
- Pixel Checker: TikTok PageView is now detected from the standard
ttq.page()base-snippet call — conventional TikTok installs were previously reported as missing PageView on every page. - Pixel Checker: Meta Advanced Matching is now detected only from real user-data arguments (
fbq('init', id, {...})third argument orfbq('set', 'userData', ...)) instead of being credited whenever the pixel exists. - Pixel Checker: pages that fail to fetch (network error, non-200, maintenance mode) are reported as “not scanned” with the actual error instead of being counted as missing pixels/events; fetch timeout lowered to 8s and the scan request extends the PHP time limit.
- Pixel Checker: pixel/tag consistency now inspects every ID on every fetched page (multiple GA4 + Ads IDs on one page are no longer flagged), and pages that were fetched but contain no pixel are listed explicitly.
- Pixel Checker: Google Consent Mode v2 parameter detection is scoped to the
gtag('consent', 'default', ...)call itself, so unrelated scripts can no longer produce a false “all parameters present”. - Pixel Checker: server-side GTM detection requires a URL context instead of matching the substring
sgtm.anywhere in the HTML; Meta CAPI / TikTok Events API status wording now says “credentials configured” (a static scan cannot verify a live connection). - Pixel Checker: scan history rows are now persisted with real per-platform health scores, missing events, EMQ estimate, CAPI and Consent Mode status;
GET /pixel-check/historyreturns the persisted history instead of rebuilding the current snapshot. - Pixel Checker UI: removed a dead legacy script that targeted non-existent markup; the scan button script now surfaces server-side errors instead of reloading the page on failure.
- WordPress.org build: fixed the Freemius bootstrap strip in the build script (a CRLF-only marker silently failed on LF sources, so 1.13.20 shipped the inert bootstrap); the build now fails loudly if Freemius code,
vendor/, orcomposer.jsonwould ship. - WordPress.org build: the license-refresh cron is no longer registered or scheduled (all capabilities are unlocked in this build, so license validation would be a purposeless remote call); any previously scheduled event is unscheduled on upgrade.
- WordPress.org build: replaced the Composer vendor tree with a minimal in-plugin PSR-4 autoloader — the ZIP no longer ships Composer metadata advertising packages that are not bundled.
- readme: documented the bundled Chart.js version and where its human-readable source lives (see “Source Code” section).
1.13.20
- Reviewer feedback (2026-05-15): readme “External Services” section no longer prints the literal NettoProfit API endpoint URLs. The wp.org reviewerÌtumọ̀ Yorùbá: ’s automated URL checker was treating the license-validation endpoint (which intentionally responds 401 to unauthenticated GETs) as a broken Terms/Privacy URL. The endpoints are now described in prose (POST-only, license-key-protected, not browseable) without exposing a non-200 URL to the scanner.
- Reviewer feedback (2026-05-15): for every external service block, moved the
Service provider / Terms of service / Privacy policyline to the top of the bullet list so the canonical Terms/Privacy URL is the first URL the scanner encounters in each section. NettoProfitÌtumọ̀ Yorùbá: ’s own Terms/Privacy now resolve tohttps://nettoprofit.com/terms/andhttps://nettoprofit.com/privacy-policy/.
1.13.19
- Plugin Check: removed unused
src/Database/Repository.phpentirely. The class had zero call sites anywhere in the plugin (nonew Repository, noRepository::, no static usage in tests or anywhere else); WPCS 3.x sniffer in PCP firesWordPress.DB.PreparedSQL.NotPreparedon$wpdb->get_*($variable)regardless of how the variable was prepared because it does not trace assignments through the$this->wpdbproperty pattern. Deleting the dead class is the cleanest fix. - Plugin Check: removed the matching
NettoProfit\Database\Repositoryentries fromvendor/composer/autoload_classmap.phpandvendor/composer/autoload_static.php.
1.13.18
- Plugin Check: refactored
Database\Repositoryso every$wpdb->prepare()result is assigned to a$sqlvariable before being passed to$wpdb->get_row/get_var/get_results. PCP runs PHPCS with--ignore-annotationsso file-levelphpcs:disablewas being bypassed; this is the structural pattern the sniffer recognises. - Plugin Check: removed unused
Repository::scalar()andRepository::rawQuery()helpers (no callers across the codebase) — eliminates twoUnescapedDBParameterwarnings on raw$wpdb->get_var($sql)/$wpdb->query($preparedSql)calls. - Plugin Check: wrapped
$_FILES['csv_file']['tmp_name']read inProductCostPage::ajaxImportCsvwithsanitize_text_field(wp_unslash(...))so the sniffer no longer flags it as unsanitized input (the subsequentis_uploaded_file()+realpath()guards already made it safe in practice). - Readme: shortened
== Upgrade Notice ==entries to fit the 300-character wp.org limit.
1.13.17
- Reviewer feedback (2026-05-13): set
Contributors: swabicso the listed contributor matches the plugin ownerÌtumọ̀ Yorùbá: ’s WordPress.org account. - Reviewer feedback: documented the private license-validation endpoint as POST-only requiring a license key (HTTP 401 without one is expected behavior, not a broken URL).
- Reviewer feedback: dropped
set_time_limit(300)fromsrc/API/RestProEndpoints.php::syncOrders— host PHP limits must not be forced globally. - Reviewer feedback: added explicit nonce + capability guards inside
SettingsPage::saveLicense()(in addition to the parenthandleSavenonce) andProductSalesPage::getDateRange()(capability check + Y-m-d format validation forfrom/to). - Reviewer feedback: deeper sanitization of
$_FILES['csv_file']inProductCostPage::ajaxImportCsv(per-field unslash/sanitize +realpath()for tmp_name) and of$_POST['new_rule']inSettingsPage::saveAlerts(every array key unslashed/sanitized individually before the whitelist check). - Reviewer feedback: escaped
$sortIcon($col['key'])output intemplates/admin/product-sales.phpviawp_kses()with an explicit<span class>allowlist. - Reviewer feedback (prefix “too short”): renamed every
np_/NP_/pp_/pp-short prefix (41 elements) tonettop_/NETTOP_/nettop-so all reserved identifiers are over 4 characters. Covers PHP functions and constants, WP-cron hooks (nettop_every_6h,nettop_license_refresh,nettop_ai_cleanup,nettop_weekly_pixel_scan), AJAX actions (nettop_sync_license,nettop_save_product_cost,nettop_import_csv,nettop_export_csv,nettop_inline_edit), transients (nettop_license_status,nettop_grace_mode,nettop_ai_*,nettop_pixel_scan_result,nettop_alert_notices), DB tables ({prefix}nettop_ad_spend,{prefix}nettop_order_profits,{prefix}nettop_expenses, etc.), post/order meta (_nettop_cost), JS/CSS asset handles (nettop-charts,nettop-admin,nettop-ai-advisor,nettop-hierarchy-table,nettop-pixel-checker,nettop-utm-capture), andwp_localize_scriptvariable names (nettopDashboardSync,nettopDashboardCharts,nettopAdmin,nettopAI,nettopHierarchy,nettopPixelCheckerSync,nettopPixelCheckerLegacy, etc.). - Reviewer feedback (unsafe SQL): rewrote
HierarchyAnalyzer::fetchCampaigns/fetchAdsets/fetchAds/fetchFlatEntities/fetchTrendMap/fetchSparklineMapandRestProEndpoints::getTimeSeriesto use%iidentifier placeholders for$table/$idCol(WP 6.2+) and split the optional$providerclause into two fully-static prepared queries — no more variables interpolated intowpdb::prepare()first argument. - Reviewer feedback (unsafe SQL): rewrote
Database\Repository::upsertto use the native$wpdb->replace()API, and refactoredfindBy/all/paginateso every SQL identifier flows through%iand every value flows through%s/%f/%dplaceholders; dropped the bespokesanitizeColumn()helper that the previous build leaned on. - Reviewer feedback (unsafe SQL):
uninstall.phpcleanup queries (DROP TABLE, transient/option/meta DELETE) all routed through$wpdb->prepare()with%itable placeholders and%sLIKE patterns. - Build script: updated
build-wporg.ps1for the newNETTOP_*constants andnettop_*function names so the injectedNETTOP_WPORG_BUILDmarker and thenettop_pro_gate/nettop_ai_gateno-op stubs land on the renamed symbols.
1.13.16
- Plugin Check: collapsed the two adjacent
phpcs:disable/phpcs:enableblocks in uninstall.php into a single block from the foreach drop-tables loop down to the last cleanup query, and reverted the cleanup block to the parent ruleWordPress.DB.DirectDatabaseQuery. Subrule-level disables (...DirectQuery,...NoCaching) are silently ignored by the current Plugin Check release; parent-rule disables propagate to children. Clears the final 10 warnings.
1.13.15
- Plugin Check: replaced the five
phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,...NoCachingper-line annotations in uninstall.php with aphpcs:disable/phpcs:enableblock around the full cleanup section. Single-linephpcs:ignoredoes not propagate to multi-line$wpdb->query()calls in the current Plugin Check release; block-form does. (Subrule sniff names did not actually take effect — see 1.13.16.)
1.13.14
- Plugin Check: expanded the five bare
phpcs:ignore WordPress.DB.DirectDatabaseQueryannotations in uninstall.php to explicitDirectQuery,NoCachingsubrules — Plugin Check propagates parent-rule disables but not parent-rule ignores, so the per-line suppressions were not catching the child sniffs. - Encoding fix: repaired triple-encoded em-dash mojibake in the main plugin file header (Description line and 9 inline NOSONAR/Freemius comments). The Description shown in the WordPress admin Plugins screen now reads correctly.
- Restored the en-dash mojibake entry in the breadcrumb fixEncoding() lookup table that was overwritten by the encoding sweep.
1.13.13
- Plugin Check: build script now injects a
phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFounddirective together with the NETTOP_WPORG_BUILD constant so the injected define() lands inside the suppression scope. - Plugin Check: rewrote bare
// phpcs:enabledirectives in uninstall.php, templates/admin/ad-detail.php, and src/Database/Repository.php to enumerate the rules being re-enabled. Barephpcs:enablereactivates every rule, including file-levelPrefixAllGlobals.NonPrefixedVariableFounddisables, so the variables in scope after the directive were getting flagged again.
1.13.12
- Plugin Check final pass: silenced the last 24 warnings — file-level phpcs:disable for NP_* constants in nettoprofit.php and uninstall.php scope-local variables; restructured multi-line $wpdb->query() phpcs:ignore into phpcs:disable/enable blocks so the suppression actually covers the SQL line.
- Build script: post-strip line-ending normalization step (LF + UTF-8 no BOM) on every text file in the staged plugin folder. Fixes the four files where PowerShell -replace was leaking CRLF back in after License/PDF/Freemius strip operations.
1.13.11
- Plugin Check sweep: file-level phpcs:disable annotations and inline escaping/sanitization improvements across templates, REST endpoints, COGS importer, dashboard widget, and uninstall to silence ~380 Plugin Check warnings without behavioural changes.
1.13.10
- Fixed alert rules save: input is no longer flattened with sanitize_text_field() before iteration, so toggle states persist correctly.
- Removed sslverify => false from the pixel scanner HTTP call (Plugin Check warning).
- Build script neutralizes the include_once for the deleted locked-feature.php template in CampaignPage.php.
- Aligned PHP version mention in readme description with the header (PHP 8.3).
1.13.9
- Build hardening (round 2): WordPress.org ZIP now also removes the unused locked-feature.php template, strips the PDF REST endpoint and PDFExporter class, drops the “PDF export” line from the Description, and converts the orphan elseif left behind by the License tab strip into a valid if so the Settings template parses cleanly.
1.13.8
- Build hardening: WordPress.org ZIP now strips the License tab + Buy Pro CTA from Settings, the PDF download button + dompdf composer hint from Export, and the Pro+AI upgrade dead branch from the Dashboard.
- Build script ignores stray zero-byte root files (PowerShell redirection artifacts).
1.13.7
- WordPress.org build hardening: Freemius bootstrap, PRO upgrade overlays, and pixel-checker / dashboard upgrade CTAs are stripped from the directory ZIP at build time so reviewers see a fully-functional plugin with no commercial calls or trialware UI.
- Slimmed the WordPress.org ZIP by ~9 MB by excluding
vendor/dompdf,vendor/phenx,vendor/sabberworm, andvendor/masterminds(PDF export gracefully degrades when the library is absent). - Removed bundled
.potranslation files — only the canonicalnettoprofit.potis shipped (translations are managed via translate.wordpress.org). - Stripped UTF-8 BOM from all CSS and JS assets.
uninstall.phpnow also clearsnp_*andnettoprofit_*transients (single + multisite) andnp_*cron events.- Fixed corrupted phpcs:disable rationale comments in 21 source files.
1.13.6
- Hardened all admin and AJAX input handling: every
$_GET,$_POST, and$_FILESvalue is now unslashed and sanitized before use. - Documented WordPress.org-recommended
phpcs:disablerationale on real-time analytics queries (table identifiers come from$wpdb->prefix; user values are placeholder-bound; caching would yield stale data). - Removed the manual
load_plugin_textdomain()call (WP 4.6+ auto-loads translations from thelanguages/folder). - Trimmed
Tags:to the WordPress.org-permitted maximum of five. - Internal: cleaned diagnostic
error_log()sites (gated byWP_DEBUG) and silenced sniff false-positives with auditable inline comments.
1.13.5
- WordPress.org build: introduced NETTOP_WPORG_BUILD constant injected by build-wporg.ps1 — unlocks every PRO and PRO+AI capability in the directory distribution to comply with Guideline 5 (no trialware / locked features).
- Build script: exclude
.wordpress-org/,screenshot-*.png,banner-*.png,icon-*.pngso directory assets are never shipped inside the plugin ZIP.
1.2.1
- Fix: DashboardDataProvider — corrected column names (cogs, refunds, margin_percent) and table name (nettop_expenses)
- Fix: getDailyExpenseAvg() now computes from amount+frequency instead of non-existent daily_equiv column
- Alerts: add ad-level kill/pause/frequency rules (POAS < 0.3 kill, < 0.5 pause, frequency > 4.0)
- DB: add idx_created index on nettop_alert_log.created_at (migration 1.2.1)
- i18n: regenerated nettoprofit.pot (267 strings)
- QA: test_checklist.php 136/136 PASS, 0 PHP warnings, EXPLAIN 9/9 PASS
1.2.0
- PHP 8.3 — typed class constants, readonly properties
- Google Ads: Performance Max (PERFORMANCE_MAX badge, campaign-level only, no ad detail drilldown)
- Meta Ads: Advantage+ Shopping detection (buying_type), catalog_segment_value, automated_targeting flag
- TikTok Ads: Smart+ campaign detection (SMART_PLUS campaign_type)
- Microsoft/Bing Ads: hsa_cam/hsa_grp/hsa_ad UTM capture via manual provider
- WC Blocks checkout UTM capture (woocommerce_store_api_checkout_update_order_meta fallback)
- HPOS full audit — confirmed all order meta uses $order->get_meta() / update_meta_data()
- Google Consent Mode v2: all 4 params checked, CRITICAL issue raised if missing
- WooCommerce 10.x compatibility declared
1.0.0
- Initial release
- FREE: Pixel Health Checker for Meta, Google, TikTok, Pinterest, Snapchat
- FREE: Meta EMQ field scoring and improvement tips
- FREE: Google Consent Mode v2 compliance check
- FREE: WooCommerce event coverage matrix with weekly auto-scan
- PRO: Real net profit per order with full expense waterfall
- PRO: Campaign Ad Set Ad hierarchy for Google, Meta, TikTok
- PRO: Product break-even analysis column
- PRO: Multi-period P&L comparison (6 periods)
- PRO: CSV and PDF export
- PRO: Profit-threshold alerts with email delivery
- PRO+AI: Claude Sonnet integration — Advisor, Copy Generator, Anomaly Explainer
1.13.4
- Plugin Check: fixed remaining 19 PreparedSQL.NotPrepared errors in Repository.php by switching multi-line query annotations from phpcs:ignore (single line) to phpcs:disable / phpcs:enable blocks
1.13.3
- Plugin Check: fixed all 58 ERROR-level issues (escape output, prepared SQL ignore comments, exception escaping, file system operation annotations, gmdate, missing translators comment)
- Synced “Requires at least” between header (6.5) and readme
- Bundled Chart.js locally instead of loading from cdn.jsdelivr.net (WP.org enqueued resource policy)
1.13.2
- Build script: switched from Compress-Archive to System.IO.Compression.ZipFile to produce ZIPs with forward-slash entries (fixes “Plugin file does not exist” on Linux/WordPress activation)
- Excluded releasesWP/ folder from the WP.org build
- Plugin URI updated to https://nettoprofit.com/
1.13.0
- WordPress.org compliance: replaced self::TEXT_DOMAIN constants with literal ‘nettoprofit’ in all i18n calls
- Removed UTF-8 BOM from CampaignPage.php
- Replaced heredoc syntax with concatenated strings in AlertNotifier, PDFExporter, EmailDigest
- Synced “Tested up to” header (6.9) between plugin file and readme
- Plugin URI / Author URI differentiated
- Added composer.json at plugin root
- Pre-translated tab labels and period options (no dynamic i18n strings in templates)
1.6.2
- Capability-based tier system: Lite / Pro / Pro+AI with PlanCapabilities + FeatureGate
- LicenseValidator: fly.io validation endpoint with 72h grace mode and AES-256-CBC key storage
- REST API: all 22 PRO and AI routes migrated to registerCapabilityRoute() capability gates
- Campaign limit: backend-enforced via DB transaction (SELECT FOR UPDATE) per tier
- AI endpoints: 7 routes gated behind ai_access capability (PRO+AI only)
- Lite tier: CSV export restricted to campaign-level; adset/ad views locked in UI
- Export: PDF export via bundled dompdf/dompdf v2.0.8
- Alerts: profit-threshold alert engine with ad/adset/campaign kill and pause rules
- HPOS: full WooCommerce Custom Order Tables + Cart/Checkout Blocks compatibility
- i18n: de_DE, es_ES, fr_FR, sr_RS translations included
1.5.0
- FeatureGate: centralized capability enforcement for REST, admin, and service classes
- PlanCapabilities: deterministic capability map — no HTTP calls, no DB reads
- nettop_can() global helper for template and service-layer capability checks
- Encryption: AES-256-CBC for all stored API keys and license key
- Grace mode: 72-hour fallback when fly.io validation endpoint is unreachable
1.4.0
- LicenseValidator: custom fly.io /license/validate backend
- Tier model: free / lite / pro / pro_ai with sanitized tier propagation
- Campaign meta: per-campaign break-even ROAS/POAS/CPA overrides with upsert
- Historical backfill: Action Scheduler for async order profit recalculation
1.3.0
- Multi-period analyzer: 6-period comparison (today/7d/30d/90d/12m/custom)
- Product analyzer: break-even price, ad cost headroom, break-even CPA columns
- Alert engine: AlertEngine + AlertNotifier with email delivery via wp_mail
- Email digest: weekly P&L summary with optional AI narrative paragraph
