Skip to content

Commit 3cfba67

Browse files
feat(debit_routing): Implement frontend changes (#259)
1 parent 3627893 commit 3cfba67

116 files changed

Lines changed: 7880 additions & 2390 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,24 @@ Decision Engine is a **high-performance payment gateway router** built in Rust t
106106
# Clone and run
107107
git clone https://github.com/juspay/decision-engine.git
108108
cd decision-engine
109-
docker compose up -d
109+
docker compose --profile postgres-ghcr up -d
110110

111111
# That's it! API ready at http://localhost:8080
112112
```
113113

114+
For API + dashboard + docs:
115+
116+
```bash
117+
docker compose --profile dashboard-postgres-ghcr up -d
118+
```
119+
120+
Open:
121+
122+
- API: `http://localhost:8080`
123+
- Dashboard: `http://localhost:8081/dashboard/`
124+
- Docs: `http://localhost:8081/introduction`
125+
- API examples: `http://localhost:8081/api-refs/api-ref`
126+
114127
### 🦀 From Source
115128

116129
```bash
@@ -145,7 +158,8 @@ curl http://localhost:8080/health
145158
| [Local Setup Guide](docs/local-setup.md) | Canonical guide for CLI, Docker, Compose profiles, and Helm |
146159
| [MySQL Setup Guide](docs/setup-guide-mysql.md) | MySQL-specific walkthrough |
147160
| [PostgreSQL Setup Guide](docs/setup-guide-postgres.md) | PostgreSQL-specific walkthrough |
148-
| [API Reference](docs/api-reference1.md) | Complete REST API documentation |
161+
| [API Reference](docs/api-reference.md) | OpenAPI-backed REST API documentation |
162+
| [API Examples](docs/api-refs/api-ref.mdx) | Curl examples for every route family and routing flavour |
149163
| [Configuration Guide](docs/configuration.md) | All config options explained |
150164
| [Deep Dive Blog](https://juspay.io/blog/juspay-orchestrator-and-merchant-controlled-routing-engine) | How routing logic works |
151165

cypress/e2e/api/merchant-crud.cy.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,36 @@ describe('Merchant CRUD API', () => {
3636
expect(status).to.not.eq(200)
3737
})
3838
})
39+
40+
it('gets and updates the debit routing feature flag', () => {
41+
const missingMerchantId = factory.merchantId('merchant_missing_debit')
42+
43+
cy.createMerchantAccount(testMerchantId)
44+
45+
cy.getDebitRoutingFlag(testMerchantId).then(({ response }) => {
46+
expect(response.merchant_id).to.eq(testMerchantId)
47+
expect(response.debit_routing_enabled).to.eq(false)
48+
})
49+
50+
cy.updateDebitRoutingFlag(testMerchantId, true).then(({ response }) => {
51+
expect(response.merchant_id).to.eq(testMerchantId)
52+
expect(response.debit_routing_enabled).to.eq(true)
53+
})
54+
55+
cy.getDebitRoutingFlag(testMerchantId).then(({ response }) => {
56+
expect(response.debit_routing_enabled).to.eq(true)
57+
})
58+
59+
cy.updateDebitRoutingFlag(testMerchantId, false).then(({ response }) => {
60+
expect(response.debit_routing_enabled).to.eq(false)
61+
})
62+
63+
cy.getDebitRoutingFlag(testMerchantId).then(({ response }) => {
64+
expect(response.debit_routing_enabled).to.eq(false)
65+
})
66+
67+
cy.getDebitRoutingFlag(missingMerchantId, { failOnStatusCode: false }).then(({ status }) => {
68+
expect(status).to.eq(404)
69+
})
70+
})
3971
})

cypress/e2e/ui/analytics-page.cy.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ describe('Analytics UI', () => {
7878
.parents('div.space-y-6')
7979
.first()
8080
.within(() => {
81-
cy.contains('button', /^Transactions$/).should('be.visible')
82-
cy.contains('button', /^Rule-Based$/).should('be.visible')
81+
cy.contains('button', /^Auth-rate based$/).should('be.visible')
82+
cy.contains('button', /^Rule based \/ Volume based$/).should('be.visible')
8383
})
8484

8585
cy.intercept('GET', '**/analytics/overview*', (req) => {
@@ -91,7 +91,7 @@ describe('Analytics UI', () => {
9191

9292
cy.get('select').first().select('Last 1 week')
9393
cy.contains('button', 'Refresh').click()
94-
cy.contains(/Refreshing transaction analytics for/i).should('be.visible')
94+
cy.contains(/Refreshing auth-rate based analytics for/i).should('be.visible')
9595
cy.wait('@overviewRefresh')
9696
cy.wait('@routingRefresh')
9797

@@ -102,10 +102,10 @@ describe('Analytics UI', () => {
102102
.parents('div.space-y-6')
103103
.first()
104104
.within(() => {
105-
cy.contains('button', /^Rule-Based$/).scrollIntoView().click({ force: true })
105+
cy.contains('button', /^Rule based \/ Volume based$/).scrollIntoView().click({ force: true })
106106
})
107107
cy.contains(
108-
'Preview-only activity for rule-based routing, separate from transaction decisions and score updates.',
108+
'Routing activity for rule-based and volume-based flows, separate from auth-rate score updates.',
109109
).should('exist')
110110
})
111111
})

cypress/e2e/ui/auth-page.cy.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,58 @@ describe('Auth UI', () => {
2323
win.localStorage.removeItem('merchant-store')
2424
})
2525

26-
cy.contains('h1', 'Decision Engine Console').should('be.visible')
2726
cy.contains('h2', 'Route, inspect, and iterate from one control surface.').should('be.visible')
27+
cy.contains('Welcome back').should('be.visible')
2828
cy.contains('button', 'Enter workspace').should('be.visible')
2929
cy.visitWithSession('/', merchantId)
3030
cy.contains(email, { timeout: 20000 }).should('be.visible')
3131
cy.contains(merchantId).should('be.visible')
3232
})
33+
34+
it('keeps the sign-up tab active across refresh', () => {
35+
cy.visitAppPath('/login', {
36+
onBeforeLoad(win) {
37+
win.localStorage.removeItem('auth-store')
38+
win.localStorage.removeItem('merchant-store')
39+
},
40+
})
41+
42+
cy.contains('button', 'Sign up').click()
43+
cy.location('pathname').should('include', '/signup')
44+
cy.contains('Create account').should('be.visible')
45+
46+
cy.reload()
47+
48+
cy.location('pathname').should('include', '/signup')
49+
cy.contains('Create account').should('be.visible')
50+
cy.contains('button', 'Create account').should('be.visible')
51+
})
52+
53+
it('switches duplicate sign-up attempts to sign-in with email preserved', () => {
54+
const duplicateEmail = `duplicate-${merchantId}@example.com`
55+
56+
cy.intercept('POST', '**/decision-engine-api/auth/signup', {
57+
statusCode: 409,
58+
body: { message: 'Email already registered' },
59+
}).as('duplicateSignup')
60+
61+
cy.visitAppPath('/login', {
62+
onBeforeLoad(win) {
63+
win.localStorage.removeItem('auth-store')
64+
win.localStorage.removeItem('merchant-store')
65+
},
66+
})
67+
68+
cy.contains('button', 'Sign up').click()
69+
cy.get('input[type="email"]').clear().type(duplicateEmail)
70+
cy.get('input[placeholder="e.g. Acme Corp"]').clear().type('Venom')
71+
cy.get('input[placeholder="Enter your password"]').clear().type('ValidPass1!')
72+
cy.contains('button', 'Create account').click()
73+
74+
cy.wait('@duplicateSignup')
75+
cy.contains('Welcome back').should('be.visible')
76+
cy.contains('Account already exists. Sign in with this email.').should('be.visible')
77+
cy.get('input[type="email"]').should('have.value', duplicateEmail)
78+
cy.get('input[placeholder="Enter your password"]').should('be.focused')
79+
})
3380
})
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const factory = require('../../support/test-data-factory')
2+
3+
describe('Debit Routing UI', () => {
4+
let merchantId
5+
6+
beforeEach(() => {
7+
cy.waitForService()
8+
cy.viewport(1600, 1200)
9+
merchantId = factory.merchantId('debit_ui')
10+
cy.ensureMerchantAccount(merchantId)
11+
})
12+
13+
afterEach(() => {
14+
cy.cleanupTestData(merchantId)
15+
})
16+
17+
it('toggles merchant debit routing access without exposing unsupported config editing', () => {
18+
cy.visitWithMerchant('/routing/debit', merchantId)
19+
20+
cy.contains('h1', 'Network / Debit Routing').should('be.visible')
21+
cy.contains('Debit Routing Runtime Access').should('be.visible')
22+
cy.contains('Save Config').should('not.exist')
23+
cy.contains('Merchant Category Code (MCC)').should('not.exist')
24+
25+
cy.contains('button', 'Enable Debit Routing').click()
26+
cy.contains('Debit routing enabled for this merchant.').should('be.visible')
27+
cy.getDebitRoutingFlag(merchantId).then(({ response }) => {
28+
expect(response.debit_routing_enabled).to.eq(true)
29+
})
30+
31+
cy.contains('button', 'Disable Debit Routing').click()
32+
cy.contains('Debit routing disabled for this merchant.').should('be.visible')
33+
cy.getDebitRoutingFlag(merchantId).then(({ response }) => {
34+
expect(response.debit_routing_enabled).to.eq(false)
35+
})
36+
})
37+
})

cypress/e2e/ui/decision-explorer.cy.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,36 @@ describe('Decision Explorer UI', () => {
4949
})
5050
.then(() => cy.cleanupTestData(merchantId))
5151
})
52+
53+
it('renders the debit routing surface with backend-valid defaults', () => {
54+
const merchantId = factory.merchantId('decision_explorer_debit')
55+
cy.ensureMerchantAccount(merchantId)
56+
.then(() => cy.visitWithSession('/decisions', merchantId))
57+
.then(() => {
58+
cy.contains('button', 'Debit Routing').click()
59+
cy.contains('Debit Routing Parameters').should('be.visible')
60+
cy.contains('Debit routing is disabled for this merchant.').should('be.visible')
61+
cy.contains('button', 'Enable Debit Routing').should('be.visible')
62+
cy.get('input[value="merchant_category_code_0001"]').should('be.visible')
63+
cy.get('input[value="VISA, NYCE, PULSE, STAR"]').should('be.visible')
64+
cy.contains('button', 'Run Debit Routing').should('be.disabled')
65+
})
66+
.then(() => cy.cleanupTestData(merchantId))
67+
})
68+
69+
it('runs debit routing through decide-gateway when enabled', () => {
70+
const merchantId = factory.merchantId('decision_explorer_debit_run')
71+
cy.ensureMerchantAccount(merchantId)
72+
.then(() => cy.updateDebitRoutingFlag(merchantId, true))
73+
.then(() => cy.visitWithSession('/decisions', merchantId))
74+
.then(() => {
75+
cy.contains('button', 'Debit Routing').click()
76+
cy.contains('Debit routing is enabled for this merchant.').should('be.visible')
77+
cy.contains('button', 'Run Debit Routing').should('not.be.disabled').click()
78+
cy.contains('Debit Routing Result', { timeout: 20000 }).should('be.visible')
79+
cy.contains('Ranked Debit Networks').should('be.visible')
80+
cy.contains('td', 'VISA').should('be.visible')
81+
})
82+
.then(() => cy.cleanupTestData(merchantId))
83+
})
5284
})

cypress/support/commands.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ Cypress.Commands.add('getMerchantAccount', (merchantId, options = {}) => {
259259
)
260260
})
261261

262+
Cypress.Commands.add('getDebitRoutingFlag', (merchantId, options = {}) => {
263+
return requestApi('GET', `/merchant-account/${merchantId}/debit-routing`, options).then((response) =>
264+
cy.wrap({ merchantId, response: response.body, status: response.status }),
265+
)
266+
})
267+
268+
Cypress.Commands.add('updateDebitRoutingFlag', (merchantId, enabled, options = {}) => {
269+
return requestApi('POST', `/merchant-account/${merchantId}/debit-routing`, {
270+
...options,
271+
body: { enabled },
272+
}).then((response) => cy.wrap({ merchantId, response: response.body, status: response.status }))
273+
})
274+
262275
Cypress.Commands.add('deleteMerchantAccount', (merchantId, options = {}) => {
263276
return requestApi('DELETE', `/merchant-account/${merchantId}`, options).then((response) =>
264277
cy.wrap({ merchantId, response: response.body, status: response.status }),

docs/api-reference.md

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,77 @@
11
# API Overview
22

3-
The canonical API contract for the docs site is `docs/openapi.json`.
4-
5-
Use this page to find the right endpoint family. Use the OpenAPI-backed endpoint pages for schema details.
3+
The canonical OpenAPI contract for the docs site is `docs/openapi.json`. Use this page for schema-oriented navigation. Use [API Examples](/api-refs/api-ref) for curl-first flows, valid payloads, and route variants.
64

75
## Endpoint Families
86

97
### Health
108

11-
- [Health Check](api-reference/endpoint/healthCheck)
9+
- [Health Check](/api-reference/endpoint/healthCheck)
10+
- [Health Ready](/api-reference/endpoint/healthReady)
11+
- [Health Diagnostics](/api-reference/endpoint/healthDiagnostics)
1212

13-
### Gateway Decision
13+
### Auth And Onboarding
1414

15-
- [Decide Gateway](api-reference/endpoint/decideGateway)
15+
- [Signup](/api-reference/endpoint/signup)
16+
- [Login](/api-reference/endpoint/login)
17+
- [Logout](/api-reference/endpoint/logout)
18+
- [Current User](/api-reference/endpoint/me)
19+
- [List User Merchants](/api-reference/endpoint/listUserMerchants)
20+
- [Switch Merchant](/api-reference/endpoint/switchMerchant)
21+
- [Onboard Merchant](/api-reference/endpoint/onboardMerchant)
1622

17-
### Score Feedback
23+
### API Keys
1824

19-
- [Update Gateway Score](api-reference/endpoint/updateGatewayScore)
25+
- [Create API Key](/api-reference/endpoint/createApiKey)
26+
- [List API Keys](/api-reference/endpoint/listApiKeys)
27+
- [Revoke API Key](/api-reference/endpoint/revokeApiKey)
2028

2129
### Merchant Account
2230

23-
- [Create Merchant](api-reference/endpoint/createMerchant)
24-
- [Get Merchant](api-reference/endpoint/getMerchant)
25-
- [Delete Merchant](api-reference/endpoint/deleteMerchant)
31+
- [Create Merchant](/api-reference/endpoint/createMerchant)
32+
- [Get Merchant](/api-reference/endpoint/getMerchant)
33+
- [Delete Merchant](/api-reference/endpoint/deleteMerchant)
34+
- [Get Merchant Debit Routing](/api-reference/endpoint/getMerchantDebitRouting)
35+
- [Update Merchant Debit Routing](/api-reference/endpoint/updateMerchantDebitRouting)
36+
37+
### Gateway Decision
38+
39+
- [Decide Gateway](/api-reference/endpoint/decideGateway)
40+
- [Legacy Decision Gateway](/api-reference/endpoint/legacyDecisionGateway)
41+
- [Update Gateway Score](/api-reference/endpoint/updateGatewayScore)
42+
- [Legacy Update Score](/api-reference/endpoint/legacyUpdateScore)
2643

2744
### Routing Rules
2845

29-
- [Create Routing Rule](api-reference/endpoint/createRoutingRule)
30-
- [Activate Routing Rule](api-reference/endpoint/activateRoutingRule)
31-
- [List Routing Rules](api-reference/endpoint/listRoutingRules)
32-
- [Get Active Routing Rule](api-reference/endpoint/getActiveRoutingRule)
33-
- [Evaluate Routing Rule](api-reference/endpoint/evaluateRoutingRule)
46+
- [Create Routing Rule](/api-reference/endpoint/createRoutingRule)
47+
- [Activate Routing Rule](/api-reference/endpoint/activateRoutingRule)
48+
- [List Routing Rules](/api-reference/endpoint/listRoutingRules)
49+
- [Get Active Routing Rule](/api-reference/endpoint/getActiveRoutingRule)
50+
- [Evaluate Routing Rule](/api-reference/endpoint/evaluateRoutingRule)
51+
- [Hybrid Routing](/api-reference/endpoint/hybridRouting)
3452

3553
### Rule Configuration
3654

37-
- [Create Rule Config](api-reference/endpoint/createRuleConfig)
38-
- [Get Rule Config](api-reference/endpoint/getRuleConfig)
39-
- [Update Rule Config](api-reference/endpoint/updateRuleConfig)
40-
- [Delete Rule Config](api-reference/endpoint/deleteRuleConfig)
55+
- [Create Rule Config](/api-reference/endpoint/createRuleConfig)
56+
- [Get Rule Config](/api-reference/endpoint/getRuleConfig)
57+
- [Update Rule Config](/api-reference/endpoint/updateRuleConfig)
58+
- [Delete Rule Config](/api-reference/endpoint/deleteRuleConfig)
59+
60+
### Config
61+
62+
- [Get Routing Config](/api-reference/endpoint/getRoutingConfig)
63+
- [Configure SR Dimensions](/api-reference/endpoint/configSrDimension)
64+
65+
### Analytics
66+
67+
- [Overview](/api-reference/endpoint/analyticsOverview)
68+
- [Gateway Scores](/api-reference/endpoint/analyticsGatewayScores)
69+
- [Decisions](/api-reference/endpoint/analyticsDecisions)
70+
- [Routing Stats](/api-reference/endpoint/analyticsRoutingStats)
71+
- [Log Summaries](/api-reference/endpoint/analyticsLogSummaries)
72+
- [Payment Audit](/api-reference/endpoint/analyticsPaymentAudit)
73+
- [Preview Trace](/api-reference/endpoint/analyticsPreviewTrace)
4174

4275
## Curl Examples
4376

44-
For local smoke-test examples, use [Curl API References](api-refs/api-ref).
77+
For local and sandbox smoke-test examples, use [API Examples](/api-refs/api-ref).
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: "Analytics Decisions"
3+
openapi: "GET /analytics/decisions"
4+
---
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: "Analytics Gateway Scores"
3+
openapi: "GET /analytics/gateway-scores"
4+
---

0 commit comments

Comments
 (0)