Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions PR_DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# PR: Implement funnel, ML forecasting, A/B testing, customer health (#852 #853 #854 #855)

**Branch:** `feat/issues-852-853-854-855`
**Base:** `main`
**Commits:**
- `bfc5bb0` feat: add funnel conversion tracking, ML forecasting, A/B testing, customer health (#852 #853 #854 #855)
- `05ac260` docs: add integration docs for funnel, ML forecast, AB testing, health

Closes #852
Closes #853
Closes #854
Closes #855

## Summary
This PR implements all four requested features:

### #853 Add funnel conversion tracking
- Service `backend/src/services/funnel-tracking.ts` with `FunnelTrackingService`
- Custom funnel definitions, step ordering, conversion window, per-step stats (entered, conversionRate, stepConversionRate, dropOff, avg/median/p95 time to next)
- User journey tracking with completion and conversion time
- Routes `backend/src/routes/funnel-tracking.ts` mounted at `/api/v1/funnels`
- Tests `backend/src/services/__tests__/funnel-tracking.test.ts` (9 cases)
- Docs `backend/docs/FUNNEL_TRACKING.md`

### #854 Implement revenue forecasting with ML
- Service `backend/src/services/ml-forecast.ts` with `MLForecastService`
- Ensemble: linear regression, polynomial (degree 2 via 3x3 solve), exponential smoothing, Holt-Winters seasonal, moving average
- Cross-validated model selection (80/20 holdout, RMSE), confidence intervals (1.96*RMSE), seasonality detection via autocorrelation, trend analysis
- Routes `backend/src/routes/ml-forecast.ts` mounted at `/api/v1/forecast/ml`
- Tests `backend/src/services/__tests__/ml-forecast.test.ts`
- Docs `backend/docs/ML_FORECAST.md`

### #852 Build A/B testing framework
- Service `backend/src/services/ab-testing.ts` with `ABTestingService`
- Deterministic MD5 bucket assignment, weighted variants, traffic allocation, lifecycle (draft/running/paused/completed/archived)
- Statistical: two-proportion z-test, Wilson interval, Bayesian prob, sample size calculator
- Routes `backend/src/routes/ab-testing.ts` mounted at `/api/v1/ab-tests`
- Tests `backend/src/services/__tests__/ab-testing.test.ts`
- Docs `backend/docs/AB_TESTING.md`

### #855 Build customer health score system
- Service `backend/src/services/customer-health.ts` with `CustomerHealthService`
- Composite 0-100 score: payment_health 30% + frequency 20% + recency 20% + engagement 15% + support 15%, with penalties for churn signals
- Levels champion/healthy/at_risk/critical, trend, riskReasons, recommendations, history, distribution, at-risk listing
- Routes `backend/src/routes/customer-health.ts` mounted at `/api/v1/customer-health`
- Tests `backend/src/services/__tests__/customer-health.test.ts`
- Docs `backend/docs/CUSTOMER_HEALTH.md`

## API Registration
Updated `backend/src/index.ts` to mount all new routers and fix missing `allowancesRouter` import.

## How to create PR (when GitHub auth available)
```bash
git push -u origin feat/issues-852-853-854-855
gh pr create --title "feat: add funnel conversion tracking, ML forecasting, A/B testing, customer health (#852 #853 #854 #855)" \
--body "Closes #852, Closes #853, Closes #854, Closes #855" \
--base main --head feat/issues-852-853-854-855
```
Or fork first:
```bash
gh repo fork Smartdevs17/agenticpay --clone=false
git remote add fork https://github.com/<your-user>/agenticpay.git
git push -u fork feat/issues-852-853-854-855
gh pr create --repo Smartdevs17/agenticpay --head <your-user>:feat/issues-852-853-854-855
```

## Testing
Tests are vitest-based, run with:
```bash
cd backend && npm test src/services/__tests__/funnel-tracking.test.ts src/services/__tests__/ml-forecast.test.ts src/services/__tests__/ab-testing.test.ts src/services/__tests__/customer-health.test.ts
```

All services expose `resetForTests()` for isolation.

## Verification
- `git log --oneline bfc5bb0..HEAD`
- `git diff main..HEAD --stat`
53 changes: 53 additions & 0 deletions backend/docs/AB_TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# A/B Testing Framework — Issue #852

## Overview
Generic experiment framework with deterministic assignment, statistical significance and lifecycle.

## Service
`backend/src/services/ab-testing.ts` — `ABTestingService`

### Experiment
```ts
{
id, name, description, hypothesis,
variants: [{ key, name, weight, isControl, payload }],
primaryMetric: 'conversion',
trafficAllocation: 100,
status: 'draft'|'running'|'paused'|'completed'|'archived'
}
```

### API
| Method | Path | Description |
|--------|------|-------------|
| POST | /api/v1/ab-tests | Create |
| GET | /api/v1/ab-tests?status= | List |
| GET | /api/v1/ab-tests/:id | Get |
| PATCH | /api/v1/ab-tests/:id | Update (draft only) |
| DELETE | /api/v1/ab-tests/:id | Delete |
| POST | /api/v1/ab-tests/:id/start, /pause, /complete, /archive | Lifecycle |
| POST | /api/v1/ab-tests/:id/assign | `{ subjectId }` |
| GET | /api/v1/ab-tests/:id/assign/:subjectId | Assign |
| POST | /api/v1/ab-tests/:id/exposure | Mark exposed |
| POST | /api/v1/ab-tests/:id/track | `{ subjectId, metric?, value? }` |
| GET | /api/v1/ab-tests/:id/results | Results with pValue, CI, winner |
| GET | /api/v1/ab-tests/:id/bayesian | Bayesian prob beats control |
| POST | /api/v1/ab-tests/utils/sample-size | `{ baselineRate, mde }` |

### Stats
- Two-proportion z-test (pooled)
- Wilson score interval
- Bayesian Beta-Binomial (normal approx)
- Sample size calculator

### Example Results
```json
{
"winner": "treatment",
"variants": [
{ "key": "control", "participants": 100, "conversionRate": 0.12, "confidenceInterval": [0.06, 0.18] },
{ "key": "treatment", "participants": 98, "conversionRate": 0.22, "lift": 0.1, "pValue": 0.03, "isSignificant": true, "isWinner": true }
],
"recommendation": "Variant treatment is winner..."
}
```
50 changes: 50 additions & 0 deletions backend/docs/CUSTOMER_HEALTH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Customer Health Score — Issue #855

## Overview
Composite 0-100 health based on payment, engagement, churn, support signals.

## Service
`backend/src/services/customer-health.ts` — `CustomerHealthService`

### Factors (weighted sum)
- `payment_health` 30% — success rate, failure penalty
- `frequency` 20% — payments per 90d
- `recency` 20% — days since last success
- `engagement` 15% — logins/api_calls per 30d, inactivity penalty
- `support` 15% — tickets & cancellations

Extra penalties: 2+ fails in 7d (-15), cancellations (-20), refunds (-10)

Levels: champion >=85, healthy >=65, at_risk >=40, critical <40

Trend: improving/declining if change >5 vs previous score

### API
| Method | Path | Description |
|--------|------|-------------|
| POST | /api/v1/customer-health/events | `{ customerId, type, amount?, timestamp? }` |
| POST | /api/v1/customer-health/events/bulk | `{ events: [...] }` |
| GET | /api/v1/customer-health/distribution | Aggregate distribution |
| GET | /api/v1/customer-health/at-risk?threshold=40 | At-risk list |
| GET | /api/v1/customer-health/export | CSV |
| GET | /api/v1/customer-health/:customerId | Health score |
| GET | /api/v1/customer-health/:customerId/history | History |
| GET | /api/v1/customer-health/:customerId/trend?days=30 | Trend |

### Activity Types
`payment_success`, `payment_failed`, `payment_refunded`, `login`, `api_call`, `support_ticket_opened`, `support_ticket_resolved`, `subscription_cancelled`, `subscription_renewed`, `inactivity`

### Example
```json
{
"customerId": "cust_123",
"score": 78,
"level": "healthy",
"factors": [
{ "name": "payment_health", "score": 90, "weight": 0.3, "details": "9/10 success" }
],
"trend": "stable",
"riskReasons": [],
"recommendations": ["Maintain engagement"]
}
```
49 changes: 49 additions & 0 deletions backend/docs/FUNNEL_TRACKING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Funnel Conversion Tracking — Issue #853

## Overview
Custom funnel definitions with step ordering, conversion window, per-step stats and user journey tracking.

## Service
`backend/src/services/funnel-tracking.ts` — `FunnelTrackingService`

### FunnelDefinition
```ts
{
id: string,
name: string,
steps: [{ id, name, order }],
conversionWindowMs: number // default 7d
}
```

### API

| Method | Path | Description |
|--------|------|-------------|
| POST | /api/v1/funnels | Create funnel |
| GET | /api/v1/funnels | List funnels |
| GET | /api/v1/funnels/:funnelId | Get funnel |
| PATCH | /api/v1/funnels/:funnelId | Update funnel |
| DELETE | /api/v1/funnels/:funnelId | Delete funnel |
| POST | /api/v1/funnels/:funnelId/track | Track event `{ userId, stepId, timestamp? }` |
| GET | /api/v1/funnels/:funnelId/stats?since=&until=&conversionWindowMs= | Stats with conversion rates |
| GET | /api/v1/funnels/:funnelId/journey/:userId | User journey |
| GET | /api/v1/funnels/:funnelId/export | CSV export |

### Stats Example
```json
{
"totalUsers": 100,
"totalConverted": 30,
"overallConversionRate": 0.3,
"steps": [
{ "stepId": "visit", "entered": 100, "conversionRate": 1, "stepConversionRate": 1 },
{ "stepId": "checkout", "entered": 60, "conversionRate": 0.6, "stepConversionRate": 0.6, "dropOffRate": 0.4 },
{ "stepId": "purchase", "entered": 30, "conversionRate": 0.3, "stepConversionRate": 0.5 }
],
"avgTotalConversionTimeMs": 45000
}
```

## Testing
`backend/src/services/__tests__/funnel-tracking.test.ts`
44 changes: 44 additions & 0 deletions backend/docs/ML_FORECAST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ML Revenue Forecasting — Issue #854

## Overview
Ensemble ML forecasting with cross-validated model selection.

## Service
`backend/src/services/ml-forecast.ts` — `MLForecastService`

### Models
- Linear regression
- Polynomial degree 2
- Exponential smoothing (alpha 0.3)
- Holt-Winters seasonal (period 7)
- Moving average (window 7)
- Ensemble blend 85% best + 15% MA

### Inference
1. Split historical 80/20 holdout
2. Train each model on train, predict holdout, compute MAE/RMSE/MAPE/R2/Bias
3. Select best by RMSE
4. Retrain best on full data, forecast horizon with confidence interval (1.96*RMSE)
5. Detect seasonality via autocorrelation, determine trend slope

### API
| Method | Path | Description |
|--------|------|-------------|
| POST | /api/v1/forecast/ml/predict | `{ historical: [{timestamp,value}], horizon }` or auto from analytics |
| GET | /api/v1/forecast/ml/predict?horizon=&since=&granularity= | Forecast from analytics time series |
| POST | /api/v1/forecast/ml/evaluate | Evaluate models |
| POST | /api/v1/forecast/ml/features | Feature engineering |

### Example
```json
{
"historical": [{ "timestamp": "2026-01-01", "value": 1200 }],
"forecast": [{ "timestamp": "2026-02-01", "predicted": 1350, "lowerBound": 1100, "upperBound": 1600, "model": "holt_winters" }],
"bestModel": "holt_winters",
"confidence": "high",
"trend": "up",
"seasonalityDetected": true,
"seasonalityPeriod": 7,
"summary": { "next7Days": 9450, "next30Days": 40500, "next90Days": 121500 }
}
```
62 changes: 62 additions & 0 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,33 @@ import { auditRouter } from './routes/audit.js';
import { hedgingRouter } from './routes/hedging.js';
import { complianceRouter } from './routes/compliance.js';
import { gdprRouter } from './routes/gdpr.js';
import dataExportRouter from './routes/dataExport.js';
import securityRouter from './routes/security.js';
import commentsRouter from './routes/comments.js';
import collaborationRouter from './routes/collaboration.js';
import { paymentStrategiesRouter } from './routes/payment-strategies.js';
import { registerDefaultPaymentProviders } from './services/payments/bootstrap.js';
import { compressionMiddleware } from './middleware/compression.js';
import { streamingExportRouter } from './routes/streaming-export.js';
import { poolMonitorRouter } from './routes/pool-monitor.js';
import { legacyRouter } from './routes/legacy.js';
import { splitsRouter } from './routes/splits.js';
import { refundsRouter } from './routes/refunds.js';
import { databaseRouter } from './routes/database.js';
import { archiveRouter } from './routes/archive.js';
import { searchRouter } from './routes/search.js';
import { zapierRouter } from './routes/zapier.js';
import { intercomRouter } from './routes/intercom.js';
import { allowancesRouter } from './routes/allowances.js';
import { getPrismaReplicaClient } from './db/PrismaReplicaClient.js';
import { cohortAnalyticsRouter } from './routes/cohort-analytics.js';
import { churnPredictionRouter } from './routes/churn-prediction.js';
import { slackRouter } from './routes/slack.js';
import { githubIntegrationRouter } from './routes/github-integration.js';
import { funnelTrackingRouter } from './routes/funnel-tracking.js';
import { mlForecastRouter } from './routes/ml-forecast.js';
import { abTestingRouter } from './routes/ab-testing.js';
import { customerHealthRouter } from './routes/customer-health.js';
import { kybRouter } from './routes/kyb.js';
import { kycRouter } from './routes/kyc.js';
import { batchRouter } from './routes/batch.js';
Expand Down Expand Up @@ -270,6 +297,41 @@ apiV1Router.use('/portfolio', portfolioRouter);
apiV1Router.use('/backup', backupRouter);
apiV1Router.use('/ip-allowlist', ipAllowlistRouter);
apiV1Router.use('/push', pushRouter);
// Stripe card payments
apiV1Router.use('/stripe', stripeRouter);
// Automated tax reporting, export, and calendar — Issues #690–#693
apiV1Router.use('/tax-reporting', taxReportingRouter);
// Cross-chain wallet abstraction & unified balance aggregation — Issue #711
apiV1Router.use('/wallet', walletRouter);
// GDPR data subject rights: erasure, portability, consent, retention — Issue #713
apiV1Router.use('/gdpr', gdprRouter);
// GDPR-aware data export jobs & scheduling — Issue #713
apiV1Router.use('/data-export', dataExportRouter);
// Automated security scanning findings & remediation tracking — Issue #712
apiV1Router.use('/security', securityRouter);
// Project collaboration: threaded comments, reactions, activity feed — Issue #714
apiV1Router.use('/comments', commentsRouter);
// Real-time collaboration: presence, field locks, edit history — Issue #714
apiV1Router.use('/collaboration', collaborationRouter);
// Multi-chain payment processing via the PaymentProvider strategy pattern — Issue #726
apiV1Router.use('/payment-strategies', paymentStrategiesRouter);
// Large dataset streaming exports
apiV1Router.use('/exports', streamingExportRouter);
// Performance and pool monitoring
apiV1Router.use('/monitoring', poolMonitorRouter);
apiV1Router.use('/database', databaseRouter);
// Soft delete archival sweep + restore — Issue #884
apiV1Router.use('/archive', archiveRouter);
// Full-text search — Issue #885
apiV1Router.use('/search', searchRouter);
apiV1Router.use('/analytics/cohorts', cohortAnalyticsRouter);
apiV1Router.use('/analytics/churn', churnPredictionRouter);
apiV1Router.use('/integrations/slack', slackRouter);
apiV1Router.use('/integrations/github', githubIntegrationRouter);
apiV1Router.use('/funnels', funnelTrackingRouter);
apiV1Router.use('/forecast/ml', mlForecastRouter);
apiV1Router.use('/ab-tests', abTestingRouter);
apiV1Router.use('/customer-health', customerHealthRouter);
apiV1Router.use('/nfc', nfcRouter);
apiV1Router.use('/cache', cacheRouter);
apiV1Router.use('/circuit-breaker', circuitBreakerRouter);
Expand Down
Loading
Loading