
Ab Testing Pricing
- 56 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Run controlled price A/B experiments to find the revenue-maximizing price point while tracking conversion and margin, using platform-specific tools or custom bucketing.
About
A skill for setting up statistically rigorous price A/B tests across ecommerce platforms while managing the trust and data risks of price experiments. A developer uses it to validate a price change before rolling it out site-wide.
- Per-platform price-test approaches (Intelligems, Shopify Functions)
- Emphasis on session stickiness, reversibility, and margin tracking
Ab Testing Pricing by the numbers
- 56 all-time installs (skills.sh)
- Ranked #580 of 1,106 Finance & Trading skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill ab-testing-pricingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 56 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Run controlled price A/B experiments to find the revenue-maximizing price point while tracking conversion and margin, using platform-specific tools or custom bucketing.
Files
A/B Testing Pricing
Overview
Price A/B testing lets you run controlled experiments — showing one price to a segment of visitors and a different price to another — before committing to a permanent change. This is one of the highest-leverage optimizations available, but also one of the riskiest: a poorly executed test destroys customer trust and produces misleading data. Most platforms do not have native price A/B testing, so this requires either a dedicated app or a custom implementation. This skill walks you through setting up price experiments correctly on each major platform.
When to Use This Skill
- When you need data-driven evidence for a price change before rolling it out site-wide
- When testing price sensitivity across different customer segments or product categories
- When evaluating the revenue impact of a new pricing model (e.g., switching from $49.99 to $45.00)
- When running multiple concurrent price experiments on different products without interference
- When regulatory or ethical requirements demand that price tests are documented, time-limited, and reversible
Core Instructions
Step 1: Determine the merchant's platform and choose the right approach
| Platform | Recommended Tool | Why |
|---|---|---|
| Shopify | Intelligems or Neat A/B Testing app | Native Shopify apps that handle price variant assignment, session stickiness, and statistical reporting without custom code |
| Shopify Plus | Intelligems + Shopify Functions | Shopify Functions allow server-side price overrides — the most reliable approach on Plus |
| WooCommerce | Nelio A/B Testing plugin or Split Hero | WordPress-native experiment plugins with WooCommerce price testing support |
| BigCommerce | Intelligems (supports BigCommerce) or Google Optimize alternatives | BigCommerce's Price Lists API can set variant-specific prices per customer segment |
| Custom / Headless | Build with your own session bucketing + platform pricing API | Full control but requires custom statistical tracking |
Step 2: Design the experiment
Before setting up any tool, define these parameters:
1. Hypothesis — "Reducing the price from $49.99 to $44.99 will increase revenue per visitor by more than 5%" 2. Primary metric — Revenue per visitor (RPV), not just conversion rate. A lower price converts better but may earn less per order 3. Traffic split — 50/50 is standard; never run more than 2 variants simultaneously on the same product 4. Minimum duration — At least 7 days to capture weekday/weekend variation; ideally 14 days 5. Minimum sample size — Calculate using a significance calculator (use abtestguide.com). Aim for at least 100 conversions per variant before declaring a winner 6. Exclusion rules — Existing customers who paid the old price should not be shown the new price mid-experiment
Step 3: Platform-specific setup
---
Shopify
Option A: Intelligems (recommended for most merchants)
Intelligems is the leading Shopify app for price testing. It handles session stickiness, statistical significance, and integrates with Shopify's checkout natively.
1. Install Intelligems from the Shopify App Store 2. Go to Intelligems → Tests → New Price Test 3. Select the product(s) to test 4. Set your variant prices (e.g., $49.99 vs $44.99) 5. Set traffic split (50/50 recommended) 6. Set a start date and estimated end date based on your sample size calculator 7. Intelligems assigns visitors to variants via a persistent cookie and reports RPV, conversion rate, and statistical significance in their dashboard
Key settings to configure:
- Sticky bucketing — ensure the same visitor always sees the same price (enabled by default)
- Returning customer exclusion — under Advanced Settings, exclude customers who have previously purchased the product at the original price
- Minimum detectable effect — set to the minimum revenue change that would justify the price change (e.g., 5%)
Option B: Neat A/B Testing
A lighter-weight alternative if you only need simple price split tests: 1. Install Neat A/B Testing from the App Store 2. Create a test targeting a specific product or collection 3. Set the price variant and traffic split 4. Monitor results in the Neat dashboard
Option C: Shopify Plus — Shopify Functions (advanced)
For Plus merchants who need full control: 1. Create a Shopify Function (Discount Function type) 2. The function receives cart context and customer ID 3. Use deterministic hashing on the customer/session ID to assign to a variant 4. Apply a fixed-amount or percentage discount equal to the price difference 5. Track conversions via Shopify's order webhook + a custom analytics store
---
WooCommerce
Option A: Nelio A/B Testing plugin
1. Install Nelio A/B Testing from the WordPress plugin directory 2. Go to Nelio A/B Testing → Add New Experiment → WooCommerce Product Experiment 3. Select the product and set the alternative price 4. Configure the conversion goal (purchase of that product) 5. The plugin tracks sessions and reports conversion rates with statistical significance
Option B: Manual with Google Optimize (sunsetted) replacements
Since Google Optimize was discontinued in 2023, consider:
- VWO (Visual Website Optimizer) — supports WooCommerce price testing via JavaScript
- AB Tasty — enterprise-grade with WooCommerce native integration
- Convert.com — WooCommerce-compatible with server-side testing support
Important WooCommerce note: Client-side price changes (JavaScript-based) are cosmetic only — they do not change the actual checkout price. Always ensure your A/B testing tool changes the price at the WooCommerce product level or through the woocommerce_get_price filter, not just the displayed number.
---
BigCommerce
1. Use BigCommerce's Price Lists feature (available on Plus and above):
- Go to Products → Price Lists → Add Price List
- Create a price list for your "treatment" price
- Assign the price list to a specific customer group
2. Split customers into control/treatment groups by assigning them to the customer group 3. Track conversions using BigCommerce's built-in analytics or Google Analytics with custom dimensions
Alternatively, use Intelligems for BigCommerce which manages the visitor bucketing automatically.
---
Custom / Headless
For headless storefronts, you need to implement session bucketing, price resolution, and conversion tracking:
import crypto from 'crypto';
// Deterministic variant assignment by session ID
function assignVariant(
experimentId: string,
sessionId: string,
variants: { id: string; weight: number }[] // weights must sum to 100
): string {
const hash = parseInt(
crypto.createHash('sha256')
.update(`${experimentId}:${sessionId}`)
.digest('hex')
.slice(0, 8),
16
) % 100;
let cumulative = 0;
for (const variant of variants) {
cumulative += variant.weight;
if (hash < cumulative) return variant.id;
}
return variants[variants.length - 1].id;
}
// Usage: get the price for a visitor
const variantId = assignVariant('exp_price_widget_pro', sessionId, [
{ id: 'control', weight: 50 }, // $49.99
{ id: 'treatment', weight: 50 }, // $44.99
]);
const prices = { control: 4999, treatment: 4499 }; // cents
const priceForVisitor = prices[variantId];Track conversions and calculate statistical significance:
// Two-proportion z-test for statistical significance
function zTest(
controlConversions: number,
controlVisitors: number,
treatmentConversions: number,
treatmentVisitors: number
): { pValue: number; significant: boolean } {
const p1 = controlConversions / controlVisitors;
const p2 = treatmentConversions / treatmentVisitors;
const pPooled = (controlConversions + treatmentConversions) / (controlVisitors + treatmentVisitors);
const se = Math.sqrt(pPooled * (1 - pPooled) * (1 / controlVisitors + 1 / treatmentVisitors));
if (se === 0) return { pValue: 1, significant: false };
const z = Math.abs((p2 - p1) / se);
// Approximate p-value (two-tailed)
const pValue = 2 * (1 - normalCDF(z));
return { pValue, significant: pValue < 0.05 };
}Use your platform's pricing API (Shopify Admin API, BigCommerce Catalog API) to push price updates once a winner is declared.
Step 4: Monitor and conclude the experiment
Track these metrics during the experiment:
| Metric | Where to find it |
|---|---|
| Revenue per visitor (RPV) | Intelligems dashboard; or calculate from your orders data |
| Conversion rate by variant | Your A/B tool's analytics |
| Statistical significance (p-value) | Your A/B tool; aim for p < 0.05 |
| Sample size per variant | Ensure you hit your pre-calculated minimum before concluding |
Declare a winner only when:
- You have reached the pre-calculated minimum sample size (not before)
- The p-value is below 0.05
- The experiment has run for at least 7 days
After declaring a winner, update the product price permanently and end the experiment.
Best Practices
- Use Revenue Per Visitor (RPV), not just conversion rate — a lower price may convert better but generate less revenue; RPV captures the full picture
- Require sticky bucketing — once a session is assigned to a variant, always show the same price; inconsistent prices destroy trust and distort results
- Pre-register your minimum sample size — decide before the experiment starts how many conversions you need; stopping as soon as p < 0.05 (peeking) inflates false positive rates
- Exclude existing customers — showing an existing customer a price lower than what they paid triggers complaints and churn
- Run only one experiment per product at a time — overlapping experiments on the same product create interaction effects that make results uninterpretable
- Pause experiments during promotions — site-wide sales or marketing campaigns confound price experiment results
- Log the variant assignment on every order — store the variant ID in the order metadata so you can later audit revenue attribution
Common Pitfalls
| Problem | Solution |
|---|---|
| Visitor sees different price on refresh | Ensure your A/B tool uses persistent cookies or server-side session storage for bucketing |
| Peeking — stopping at first p < 0.05 | Pre-register a minimum conversion count (e.g., 200 per variant) and only check significance after reaching it |
| Price change only affects the product page, not checkout | Ensure the test tool changes the actual price, not just a displayed number — verify in the cart and checkout |
| Bots inflate visitor counts and skew results | Filter bot traffic before recording impressions; most A/B tools have bot filtering built in |
| Experiment runs during a flash sale | Pause all price experiments during site-wide promotions |
Related Skills
- @dynamic-pricing
- @price-rules-engine
- @coupon-management
- @discount-engine
- @demand-forecasting
{
"context": "Tests whether the agent uses atomic SQL increment updates for conversion events, correctly separates add_to_cart from purchase tracking, stores revenue in cents, and persists the experiment variant ID in each order record for later reconciliation.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Atomic add_to_cart increment",
"max_score": 10,
"description": "trackExperimentEvent uses a SQL UPDATE with 'add_to_carts = add_to_carts + 1' for the add_to_cart event — not a read-modify-write pattern"
},
{
"name": "Atomic purchase increment",
"max_score": 10,
"description": "trackExperimentEvent uses a SQL UPDATE with 'purchases = purchases + 1' for the purchase event — not a read-modify-write pattern"
},
{
"name": "Revenue atomic increment",
"max_score": 10,
"description": "trackExperimentEvent updates revenue using 'revenue = revenue + ?' in the same UPDATE statement as the purchase count"
},
{
"name": "Revenue in cents",
"max_score": 8,
"description": "The revenueCents parameter or revenue amounts are treated as integer cents throughout — no conversion to/from dollars inside the tracking function"
},
{
"name": "Separate event handling",
"max_score": 8,
"description": "add_to_cart and purchase are handled as distinct branches (e.g., if/else or switch) — purchase does NOT also update add_to_carts"
},
{
"name": "Variant ID in order",
"max_score": 12,
"description": "recordOrder or checkoutIntegration persists the variantId into the order record (not just logged or discarded)"
},
{
"name": "Experiment ID in order",
"max_score": 8,
"description": "recordOrder or checkoutIntegration also persists the experimentId alongside variantId in the order record"
},
{
"name": "Null variant handled",
"max_score": 8,
"description": "The integration handles the case where variantId is null (session not in any experiment) without error — orders still complete normally"
},
{
"name": "Impression before event",
"max_score": 8,
"description": "checkoutIntegration shows that the impression is recorded when the price is first shown (via getExperimentPrice), before any add_to_cart or purchase event"
},
{
"name": "Integration notes completeness",
"max_score": 10,
"description": "INTEGRATION_NOTES.md mentions storing variant ID in orders AND using atomic increments (not read-modify-write) as key requirements"
},
{
"name": "No double counting",
"max_score": 8,
"description": "The checkout flow does not call trackExperimentEvent for purchase AND also increment add_to_carts again at checkout time — each event type is tracked exactly once"
}
]
}
Price Experiment Event Tracking Integration
Problem/Feature Description
A retail engineering team has built a basic price experimentation system but hasn't yet wired up the analytics side. Currently, the system assigns shoppers to price variants but there is no record of what happens next — no tracking of which shoppers add products to their cart, no recording of completed purchases, and no way to reconcile the revenue back to the experiment that influenced the sale.
The head of data has raised a specific concern: they want to be able to run post-hoc revenue analysis months later, which means every order record must carry enough metadata to identify which experiment and variant influenced that sale. The team also needs to understand shopping funnel behaviour at the variant level, not just final conversion.
Output Specification
Produce the following files:
1. eventTracking.ts — A TypeScript module exporting:
- A function to record a conversion event (add to cart or purchase) for a given experiment variant, including any relevant revenue amount.
- A function to persist a completed order in the database along with any relevant experiment context needed for later attribution.
2. checkoutIntegration.ts — A TypeScript module showing how to integrate the event tracking into a typical checkout flow. It should demonstrate the full sequence from showing a price, through add-to-cart, to completing a purchase.
3. INTEGRATION_NOTES.md — A brief document (under 300 words) summarising the rules the team must follow when integrating the experiment tracking into the checkout flow. Focus on data integrity and correctness concerns.
Assume a db object is available with methods including db.raw, db.orders.insert, and the methods from the experiment system.
{
"context": "Tests whether the agent implements the correct three-table schema with proper constraints, stores monetary values in cents, uses deterministic SHA-256 hashing with prefixed keys for assignment, and implements sticky bucketing to ensure session consistency.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Three tables present",
"max_score": 8,
"description": "Schema defines exactly three tables: one for experiments, one for variants, one for assignments"
},
{
"name": "Status constraint",
"max_score": 8,
"description": "Experiments table has a CHECK constraint limiting status to the lifecycle values ('draft', 'running', 'paused', 'concluded')"
},
{
"name": "Prices in cents",
"max_score": 8,
"description": "Variant price column is stored as INTEGER (not DECIMAL or FLOAT), and revenue column is BIGINT — both representing cents"
},
{
"name": "Assignments composite PK",
"max_score": 8,
"description": "Assignments table uses a composite PRIMARY KEY on (session_id, experiment_id), not a separate surrogate key"
},
{
"name": "Traffic split constraint",
"max_score": 6,
"description": "Schema or code comments indicate that variant traffic_split values must sum to 100 for an experiment"
},
{
"name": "SHA-256 for traffic hash",
"max_score": 10,
"description": "assignVariant uses crypto.createHash('sha256') to compute the traffic bucket hash (not Math.random or other methods)"
},
{
"name": "Prefixed hash keys",
"max_score": 10,
"description": "Uses distinct prefixed strings for traffic hash ('traffic:...') and variant selection hash ('variant:...') to produce independent random values"
},
{
"name": "Sticky bucketing check",
"max_score": 10,
"description": "assignVariant checks for an existing assignment record before computing any hash, and returns the previously assigned variant if found"
},
{
"name": "Assignment persisted",
"max_score": 8,
"description": "After selecting a new variant, the assignment is inserted into the assignments table before returning"
},
{
"name": "Running status guard",
"max_score": 8,
"description": "assignVariant returns null (no assignment) if the experiment status is not 'running'"
},
{
"name": "Traffic pct guard",
"max_score": 8,
"description": "Sessions whose traffic hash falls at or above traffic_pct are excluded from the experiment (return null)"
},
{
"name": "Impression tracking",
"max_score": 8,
"description": "getExperimentPrice increments the impression counter atomically using a SQL UPDATE with += 1, not a read-modify-write"
}
]
}
Price Experiment Infrastructure Setup
Problem/Feature Description
A mid-size e-commerce company is launching a new initiative to run controlled price tests before committing to any pricing changes across their catalog. The engineering lead has asked you to build the foundational layer: the database schema and the core TypeScript function that decides which price variant a shopper sees.
The system must handle tens of thousands of concurrent sessions reliably. A shopper who loads a product page twice in the same session should always see the same price — price inconsistency within a session breaks trust and skews data. The schema must also support partial traffic rollouts (not all shoppers need to see the experiment), a clean lifecycle for experiments, and the ability to run independent experiments on different products.
Output Specification
Produce the following files:
1. schema.sql — the complete SQL DDL to create all necessary tables for the experiment system 2. assignVariant.ts — a TypeScript function assignVariant(experimentId: string, sessionId: string) that returns the assigned variant (or null if the session is not in the experiment). Include any helper types needed. 3. getExperimentPrice.ts — a TypeScript function getExperimentPrice(productId: string, sessionId: string, defaultPrice: number) that looks up any active experiment for the product and returns the price to show along with tracking metadata.
Assume a db object is available with methods: db.priceExperiments.findById, db.priceExperiments.findOne, db.priceExperimentVariants.findByExperiment, db.priceExperimentAssignments.findOne, db.priceExperimentAssignments.insert, db.priceExperimentVariants.findById, db.raw.
{
"context": "Tests whether the agent implements the two-proportion z-test correctly using a polynomial normal CDF approximation, applies the correct significance threshold and minimum sample size requirements, and reports revenue-per-impression alongside conversion rate.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Two-proportion z-test",
"max_score": 12,
"description": "calculateSignificance uses a two-proportion z-test: computes pooled proportion, standard error, and z-score from control and treatment conversion rates"
},
{
"name": "Polynomial normalCDF",
"max_score": 10,
"description": "normalCDF is implemented via a polynomial approximation (Horner's method or explicit coefficients) — NOT using an external statistics library or a simple lookup table"
},
{
"name": "Two-tailed p-value",
"max_score": 8,
"description": "p-value is computed as 2 * (1 - normalCDF(|zScore|)), making it a two-tailed test"
},
{
"name": "Significance threshold",
"max_score": 8,
"description": "The 'significant' flag is set to true when pValue < 0.05 (not 0.1 or any other threshold)"
},
{
"name": "Zero-impressions guard",
"max_score": 6,
"description": "calculateSignificance handles the edge case of zero impressions in either group by returning { zScore: 0, pValue: 1, significant: false }"
},
{
"name": "Revenue per impression",
"max_score": 10,
"description": "getExperimentResults computes and returns a revenue-per-impression (RPV) metric for each variant (revenue / impressions), not just conversion rate"
},
{
"name": "Conversion rate metric",
"max_score": 8,
"description": "getExperimentResults computes and returns a conversion rate (purchases / impressions) for each variant"
},
{
"name": "Minimum conversions gate",
"max_score": 10,
"description": "isReadyToConclude returns false if either variant has fewer than 100 purchases, regardless of p-value"
},
{
"name": "Peeking prevention",
"max_score": 10,
"description": "isReadyToConclude requires BOTH statistical significance (p < 0.05) AND minimum sample size — it does NOT allow concluding on significance alone"
},
{
"name": "Control baseline comparison",
"max_score": 8,
"description": "getExperimentResults identifies the 'control' variant and only applies statistical comparison to treatment variants (not control vs control)"
},
{
"name": "Test cases present",
"max_score": 10,
"description": "analysis.test.ts contains at least one scenario expected to be significant and at least one expected to be not significant"
}
]
}
Price Experiment Results Analyzer
Problem/Feature Description
A growth engineering team has been running a series of price tests over the past quarter and needs a standalone TypeScript module to analyze the accumulated data. The results dashboard currently just shows raw counts but the team needs statistically rigorous analysis so they can tell whether one price variant is genuinely outperforming another, rather than just looking better by chance.
The analytics lead has stressed that the module must enforce proper statistical discipline — the team has been burned before by acting on early results that later reversed. There is also a concern that a variant with a higher conversion rate might not actually be the better business outcome, so the analysis needs to surface more than just conversion numbers.
Output Specification
Produce the following files:
1. analysis.ts — A TypeScript module exporting:
- A function
calculateSignificance(controlConversions, controlImpressions, treatmentConversions, treatmentImpressions)that returns a statistical test result with at leastzScore,pValue, andsignificantfields. - A function
getExperimentResults(experimentId: string)that retrieves variant data and returns enriched results per variant. - A function
isReadyToConclude(variant: object, pValue: number)that returns true only when the experiment meets minimum requirements to be acted upon. - A helper function implementing an approximation for the standard normal CDF (do not use an external statistics library).
2. analysis.test.ts — A set of TypeScript test cases (plain assertions or any test framework) that demonstrate the significance calculation working correctly on some sample data. Include at least one case that should be significant and one that should not.
Assume a db object is available with db.priceExperimentVariants.findByExperiment(experimentId).
{
"name": "finsi/ab-testing-pricing",
"version": "0.1.0",
"summary": "Price experimentation frameworks with statistical significance and revenue tracking",
"skills": {
"ab-testing-pricing": {
"path": "SKILL.md"
}
}
}