Skip to content

✨ server: assess transaction risk#607

Merged
cruzdanilo merged 1 commit intomainfrom
nicolas
Dec 30, 2025
Merged

✨ server: assess transaction risk#607
cruzdanilo merged 1 commit intomainfrom
nicolas

Conversation

@nfmelendez
Copy link
Copy Markdown
Contributor

@nfmelendez nfmelendez commented Dec 30, 2025

closes #604

Summary by CodeRabbit

  • New Features

    • Transaction risk assessment added across authorization, verification, refund and settlement flows with high-risk detection and alarm capture.
  • Improvements

    • Transaction payloads expanded to include merchant country (2-letter), merchant category code, merchant ID, and authorization method.
    • Enhanced tracing, error handling, feedback reporting and monitoring for risk outcomes; risk results surfaced to logs/traces.
  • Tests

    • Added high-risk and timeout test scenarios covering authorization, verification and refund paths.
  • Chore

    • Added changeset metadata for dependency versioning.

✏️ Tip: You can customize this high-level summary in your review settings.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Dec 30, 2025

🦋 Changeset detected

Latest commit: a6c130c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@exactly/server Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Integrates Sardine risk assessments and issuer feedback into spend/refund/verification flows, expands spend transaction schema with merchant metadata, attaches risk results to tracing and captures high-risk events, and adds tests for high-risk and timeout scenarios.

Changes

Cohort / File(s) Summary
Changeset & metadata
\.changeset/wet-games-relate.md
New changeset added with a patch bump for @exactly/server and description ✨ assess transaction risk.
Risk assessment & transaction flow
server/hooks/panda.ts
Adds Sardine risk(...) integration invoked across requested/verification/updated/completed/refund branches; runs assessments alongside trace spans; attaches exa.level/exa.score to traces; captures high/very_high risk as errors; invokes issuing feedback; adjusts credential lookup to include id; refactors early-return and span handling.
Schema / public data shape
server/hooks/panda.ts
Extends BaseTransaction.spend with required merchantCountry (2-letter) and merchantCategoryCode; adds optional merchantId and authorizationMethod; changes declinedReason to optional in created/updated transaction variants.
Tests
server/test/hooks/panda.test.ts
Adds Sardine mock usage and multiple high-risk/timeout tests (authorization, verification, refund); updates test merchantCountry values to "AR"; adds spies asserting feedback calls in negative-amount flows.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as Panda Hook
    participant DB as Database
    participant Risk as Sardine Risk
    participant Feedback as Issuer Feedback

    Client->>Server: Spend / Refund / Verify request
    Server->>DB: Load card / credential / transaction
    DB-->>Server: Card & credential

    rect rgb(225,240,255)
      Note over Server,Risk: Risk assessment phase (new)
      Server->>Risk: assess(transaction, card, credential, metadata)
      Risk-->>Server: { level, score } or error/timeout
      Server->>Server: attach exa.level/exa.score to trace
    end

    alt High or Very High risk
      rect rgb(255,230,230)
        Server->>Server: capture error (high risk)
        Server->>Feedback: send negative feedback (optional)
        Server-->>Client: 200 (handled + alarm)
      end
    else Low/Medium risk
      rect rgb(230,255,235)
        Server->>DB: Create/Update transaction
        DB-->>Server: Persisted transaction
        Server->>Feedback: send issuing feedback (state, ids, amounts)
        Feedback-->>Server: ack
        Server-->>Client: 200 (normal response)
      end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ✨ server: integrate sardine #594: Adds Sardine client, environment config, and test mocks that overlap with the new Sardine risk/feedback integration and tests in this change.

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: integrating transaction risk assessment into the server via Sardine integration.
Linked Issues check ✅ Passed The PR implements dry risk assessment for Sardine across transaction flows with risk hooks, data model updates, and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes directly support transaction risk assessment objectives: schema extensions for risk data, Sardine integration, feedback mechanisms, and test coverage.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch nicolas

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @nfmelendez, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a crucial security enhancement by integrating a transaction risk assessment service into the server's transaction processing flow. The primary goal is to proactively identify and flag potentially fraudulent or high-risk transactions, such as suspicious refunds or authorizations, to improve overall system security and prevent financial losses. The changes involve updating transaction data structures, implementing risk evaluation logic, and providing feedback to the risk assessment system.

Highlights

  • Risk Assessment Integration: Integrated the Sardine risk assessment service to evaluate transaction risk during the requested phase of a transaction.
  • Enhanced Transaction Schema: Updated the BaseTransaction and Transaction schemas to include new fields like merchantCategoryCode, merchantId, authorizationMethod, and made merchantCountry mandatory, providing richer data for risk analysis.
  • High-Risk Transaction Alarms: Implemented logic to capture exceptions for transactions identified as 'high' or 'very_high' risk by the Sardine service, specifically for refunds and authorizations, enhancing security monitoring.
  • Sardine Feedback Mechanism: Added calls to the Sardine feedback service at various stages of a transaction's lifecycle (authorization, settlement, refund, network declined, approved) to provide data for continuous risk model improvement.
  • Test Coverage: Expanded the test suite to include scenarios for high-risk authorizations and refunds, ensuring the new risk assessment logic functions as expected.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Comment thread server/hooks/panda.ts
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new transaction risk assessment feature by integrating with a 'Sardine' service. The changes include importing the risk assessment utility, updating transaction schemas to incorporate new fields like merchantCategoryCode, merchantId, and authorizationMethod, and modifying transaction processing logic in server/hooks/panda.ts. Risk assessments are now performed for 'requested' transactions (including refunds and verifications) and 'completed' transactions, with high-risk outcomes leading to error capturing. Additionally, the PR adds new tests to verify the alarming of high-risk authorizations and refunds. Review comments highlight that missing card records should throw a server-side error (5xx) instead of returning a 404, that transaction reversals should be reported as 'refund' instead of 'settled' to the risk service, and suggest refactoring duplicated risk assessment result handling logic into a helper function.

Comment thread server/hooks/panda.ts
Comment thread server/hooks/panda.ts Outdated
Comment thread server/hooks/panda.ts
Comment thread server/hooks/panda.ts
@sentry
Copy link
Copy Markdown

sentry Bot commented Dec 30, 2025

Codecov Report

❌ Patch coverage is 94.83871% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 49.32%. Comparing base (9621fb8) to head (a6c130c).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
server/hooks/panda.ts 94.83% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #607      +/-   ##
==========================================
+ Coverage   48.41%   49.32%   +0.90%     
==========================================
  Files          41       41              
  Lines        6985     7102     +117     
  Branches      501      526      +25     
==========================================
+ Hits         3382     3503     +121     
+ Misses       3586     3581       -5     
- Partials       17       18       +1     
Flag Coverage Δ
github 49.16% <94.83%> (+0.75%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98d6493 and 0bd236b.

📒 Files selected for processing (3)
  • .changeset/wet-games-relate.md
  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
server/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server.mdc)

server/**/*.ts: Use c.var object to pass strongly-typed data between Hono middleware and route handlers; do not use c.set
All request validation (headers, body, params) must be handled by @hono/valibot-validator middleware; do not perform manual validation inside route handlers
Use Hono's built-in error handling by throwing new HTTPException() for expected errors; unhandled errors will be caught and logged automatically
Enforce Node.js best practices using ESLint plugin:n/recommended configuration
Enforce Drizzle ORM best practices using ESLint plugin:drizzle/all configuration, including requiring where clauses for update and delete operations
Use Drizzle ORM query builder for all database interactions; do not write raw SQL queries unless absolutely unavoidable
All authentication and authorization logic must be implemented in Hono middleware
Do not access process.env directly in application code; load all configuration and secrets once at startup and pass them through dependency injection or context
Avoid long-running, synchronous operations; use async/await correctly and be mindful of CPU-intensive tasks to prevent blocking the event loop

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{js,ts,tsx,jsx,sol}

📄 CodeRabbit inference engine (AGENTS.md)

Follow linter/formatter (eslint, prettier, solhint) strictly with high strictness level. No any type.

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Omit redundant type names in variable declarations - let the type system explain itself

**/*.{ts,tsx}: Use PascalCase for TypeScript types and interfaces
Use valibot for all runtime validation of API inputs, environment variables, and other data; define schemas once and reuse them
Infer TypeScript types from valibot schemas using type User = v.Input<typeof UserSchema> instead of manually defining interfaces

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Omit contextual names - don't repeat class/module names in members
Omit meaningless words like 'data', 'state', 'manager', 'engine', 'value' from variable and function names unless they add disambiguation

**/*.{ts,tsx,js,jsx}: Prefer function declarations for all multi-line functions; use function expressions or arrow functions only for single-line implementations
Prefer const for all variable declarations by default; only use let if the variable's value will be reassigned
Declare each variable on its own line with its own const or let keyword, not multiple declarations on one line
Use camelCase for TypeScript variables and functions
Always use import type { ... } for type imports
Use relative paths for all imports within the project; avoid tsconfig path aliases
Follow eslint-plugin-import order: react, external libraries, then relative paths
Use object and array destructuring to access and use properties
Use object method shorthand syntax when a function is a property of an object
Prefer optional chaining (?.), nullish coalescing (??), object and array spreading (...), and for...of loops over traditional syntax
Do not use abbreviations or cryptic names; write out full words like error, parameters, request instead of err, params, req
Use Number.parseInt() instead of the global parseInt() function when parsing numbers
All classes called with new must use PascalCase
Use Buffer.from(), Buffer.alloc(), or Buffer.allocUnsafe() instead of the deprecated new Buffer()
Use @ts-expect-error instead of @ts-ignore; follow it immediately with a single-line lowercase comment explaining why the error is expected, without separators like - or :
Do not include the type in a variable's name; let the static type system do its job (e.g., use const user: User not const userObject: User)
Do not repeat the name of a class or module within its members; omit contextual names (e.g., use `class User { getProfil...

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

server/**/*.{ts,tsx}: Server API: implement schema-first approach using OpenAPI via hono with validation via valibot middleware
Server database: drizzle schema is source of truth. Migrations required. No direct database access in handlers - use c.var.db

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

For files with a single default export, name the file identically to the export; for files with multiple exports, use camelCase with a strong preference for a single word

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/.changeset/*.md

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

Use a lowercase sentence in the imperative present tense for changeset summaries

Files:

  • .changeset/wet-games-relate.md
🧬 Code graph analysis (1)
server/hooks/panda.ts (1)
server/utils/sardine.ts (2)
  • risk (36-38)
  • feedback (33-35)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Seer Code Review
  • GitHub Check: test
🔇 Additional comments (10)
.changeset/wet-games-relate.md (1)

1-5: LGTM!

The changeset follows the imperative present tense convention for the summary. The emoji prefix categorizes this as a feature addition.

server/hooks/panda.ts (6)

220-220: LGTM!

Credential id is now fetched alongside account to support risk assessment and feedback calls.


252-259: High-risk refunds are alerted but not blocked.

The current implementation captures an exception for monitoring but still returns 200 OK for high-risk refunds. Verify this is the intended behavior—if blocking is desired, you would need to return an error status instead.


280-287: LGTM!

Consistent with the refund path—high-risk verification triggers alerts but does not block the authorization.


289-326: LGTM!

Good use of Promise.all to parallelize the trace call and risk assessment, reducing latency in the authorization flow.


649-666: LGTM!

Feedback hooks correctly map action types to authorization/settlement statuses, with amounts included only for settlements.


75-82: Verify that all Panda webhook sources provide merchantCountry.

The schema change at line 75 from v.nullish(v.string()) to v.string() makes merchantCountry required. Transactions missing this field will fail validation (returning 400). Confirm with the Panda service team that all webhook sources consistently send this field.

server/test/hooks/panda.test.ts (3)

8-8: LGTM!

Sardine mock import is correctly placed with other mock imports at the top of the file.


252-274: LGTM!

Test correctly validates that high-risk authorizations trigger exception capture while still returning a successful response.


276-303: LGTM!

Test correctly validates the high-risk refund flow with a negative amount, verifying exception capture without blocking the transaction.

Comment thread server/hooks/panda.ts
Comment thread server/hooks/panda.ts
Comment thread server/test/hooks/panda.test.ts
@cruzdanilo
Copy link
Copy Markdown
Member

@cursor review

@cursor
Copy link
Copy Markdown

cursor Bot commented Dec 30, 2025

PR Summary

Risk assessment integrated into Panda transaction flows

  • Adds utils/sardine integration to assess risk during transaction.requested (including refunds/zero-amount verifications) and runs it in parallel with debug_traceCall. High/very_high risk triggers error capture and telemetry attributes.
  • Sends Sardine feedback on outcomes: authorization approved/declined and settlement settled/refund across created/updated/completed paths (including negative-amount authorizations).

Schema and validation adjustments

  • Tightens BaseTransaction.spend: merchantCountry required; new merchantCategoryCode, optional merchantId, authorizationMethod; declinedReason optional.

Stability and observability

  • Handles risk timeouts/errors gracefully (non-blocking) and records level/score on spans; keeps mutex behavior and adds more context/metrics.

Tests

  • Adds Sardine mocks and cases for timeouts and high-risk (authorization/verification/refund) plus feedback assertions; updates existing tests accordingly.

Chore

  • Adds changeset for @exactly/server patch release.

Written by Cursor Bugbot for commit b305917. This will update automatically on new commits. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Comment thread server/hooks/panda.ts
Comment thread server/hooks/panda.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
server/test/hooks/panda.test.ts (1)

252-303: Add tests for edge cases as previously discussed.

Per the previous review, additional test coverage is still needed for:

  • Risk assessment timeout (returning { status: "timeout", level: "unknown", score: 0 })
  • very_high risk level behavior
  • Non-timeout errors from risk assessment (returning { status: "error", level: "unknown", score: 0 })
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0bd236b and c2a412f.

📒 Files selected for processing (3)
  • .changeset/wet-games-relate.md
  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
server/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server.mdc)

server/**/*.ts: Use c.var object to pass strongly-typed data between Hono middleware and route handlers; do not use c.set
All request validation (headers, body, params) must be handled by @hono/valibot-validator middleware; do not perform manual validation inside route handlers
Use Hono's built-in error handling by throwing new HTTPException() for expected errors; unhandled errors will be caught and logged automatically
Enforce Node.js best practices using ESLint plugin:n/recommended configuration
Enforce Drizzle ORM best practices using ESLint plugin:drizzle/all configuration, including requiring where clauses for update and delete operations
Use Drizzle ORM query builder for all database interactions; do not write raw SQL queries unless absolutely unavoidable
All authentication and authorization logic must be implemented in Hono middleware
Do not access process.env directly in application code; load all configuration and secrets once at startup and pass them through dependency injection or context
Avoid long-running, synchronous operations; use async/await correctly and be mindful of CPU-intensive tasks to prevent blocking the event loop

Files:

  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
**/*.{js,ts,tsx,jsx,sol}

📄 CodeRabbit inference engine (AGENTS.md)

Follow linter/formatter (eslint, prettier, solhint) strictly with high strictness level. No any type.

Files:

  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Omit redundant type names in variable declarations - let the type system explain itself

**/*.{ts,tsx}: Use PascalCase for TypeScript types and interfaces
Use valibot for all runtime validation of API inputs, environment variables, and other data; define schemas once and reuse them
Infer TypeScript types from valibot schemas using type User = v.Input<typeof UserSchema> instead of manually defining interfaces

Files:

  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Omit contextual names - don't repeat class/module names in members
Omit meaningless words like 'data', 'state', 'manager', 'engine', 'value' from variable and function names unless they add disambiguation

**/*.{ts,tsx,js,jsx}: Prefer function declarations for all multi-line functions; use function expressions or arrow functions only for single-line implementations
Prefer const for all variable declarations by default; only use let if the variable's value will be reassigned
Declare each variable on its own line with its own const or let keyword, not multiple declarations on one line
Use camelCase for TypeScript variables and functions
Always use import type { ... } for type imports
Use relative paths for all imports within the project; avoid tsconfig path aliases
Follow eslint-plugin-import order: react, external libraries, then relative paths
Use object and array destructuring to access and use properties
Use object method shorthand syntax when a function is a property of an object
Prefer optional chaining (?.), nullish coalescing (??), object and array spreading (...), and for...of loops over traditional syntax
Do not use abbreviations or cryptic names; write out full words like error, parameters, request instead of err, params, req
Use Number.parseInt() instead of the global parseInt() function when parsing numbers
All classes called with new must use PascalCase
Use Buffer.from(), Buffer.alloc(), or Buffer.allocUnsafe() instead of the deprecated new Buffer()
Use @ts-expect-error instead of @ts-ignore; follow it immediately with a single-line lowercase comment explaining why the error is expected, without separators like - or :
Do not include the type in a variable's name; let the static type system do its job (e.g., use const user: User not const userObject: User)
Do not repeat the name of a class or module within its members; omit contextual names (e.g., use `class User { getProfil...

Files:

  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

server/**/*.{ts,tsx}: Server API: implement schema-first approach using OpenAPI via hono with validation via valibot middleware
Server database: drizzle schema is source of truth. Migrations required. No direct database access in handlers - use c.var.db

Files:

  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

For files with a single default export, name the file identically to the export; for files with multiple exports, use camelCase with a strong preference for a single word

Files:

  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
**/.changeset/*.md

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

Use a lowercase sentence in the imperative present tense for changeset summaries

Files:

  • .changeset/wet-games-relate.md
🧠 Learnings (2)
📚 Learning: 2025-12-23T19:56:43.671Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/contracts.mdc:0-0
Timestamp: 2025-12-23T19:56:43.671Z
Learning: Applies to contracts/**/*.t.sol : Use fuzz testing (testFuzz_...) extensively for functions with numerical or address inputs to cover a wide range of scenarios

Applied to files:

  • server/test/hooks/panda.test.ts
📚 Learning: 2025-12-23T19:58:16.563Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T19:58:16.563Z
Learning: Applies to contracts/**/*.test.sol : Smart contracts testing: fuzzing required for inputs and gas snapshots mandatory

Applied to files:

  • server/test/hooks/panda.test.ts
🧬 Code graph analysis (1)
server/hooks/panda.ts (1)
server/utils/sardine.ts (2)
  • risk (36-38)
  • feedback (33-35)
🔇 Additional comments (6)
server/hooks/panda.ts (5)

225-252: LGTM - Error handling now returns consistent fallback for all error types.

The assess() function now correctly returns a consistent fallback object { status: "error"|"timeout", level: "unknown", score: 0 } for all error cases, ensuring downstream code receives a predictable structure. This addresses the previous review feedback.


291-328: LGTM - Parallel execution of trace and risk assessment.

Good use of Promise.all to run the trace call and risk assessment concurrently, improving authorization latency. The risk level is properly recorded on the span and high-risk transactions are captured as errors.


651-669: LGTM - Switch statement now handles all action types.

The switch statement correctly handles "created", "updated", and "completed" actions for feedback. The "updated" case was added as noted in the previous review.


480-496: Verify edge case: partial capture sends "settled" feedback.

When payload.body.spend.amount > 0 but less than authorizedAmount (partial capture with refund), this sends a "settled" feedback with the partial amount. Confirm this is the intended behavior for Sardine's feedback API, as the partial refund portion is handled separately via the refund transaction.


539-547: Confirm negative amount handling in "created" action is intentional.

This block sends "authorization approved" feedback for negative amounts (refunds) in the "created" action. This is now reachable after the merge conflict fix. Verify this is the correct feedback type for refund authorizations.

server/test/hooks/panda.test.ts (1)

252-303: LGTM - High-risk scenario tests properly validate error capture.

Both tests correctly:

  • Mock the Sardine risk assessment to return high-risk responses
  • Use unique card IDs for test isolation
  • Verify that captureException is called with the appropriate error message
  • Confirm the response status is 200 (authorization proceeds but logs the risk)

Comment thread .changeset/wet-games-relate.md Outdated
@nfmelendez nfmelendez force-pushed the nicolas branch 4 times, most recently from fa6988c to 0c21650 Compare December 30, 2025 18:43
Comment thread server/hooks/panda.ts
@nfmelendez nfmelendez force-pushed the nicolas branch 2 times, most recently from 95e76aa to b305917 Compare December 30, 2025 18:52
Comment thread server/hooks/panda.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
.changeset/wet-games-relate.md (1)

5-5: Changeset summary still contains emoji.

Based on learnings, the changeset summary should be a lowercase sentence in the imperative present tense without emoji: assess transaction risk.

🔎 Suggested fix
-✨ assess transaction risk
+assess transaction risk
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2a412f and 532d6d3.

📒 Files selected for processing (3)
  • .changeset/wet-games-relate.md
  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
**/.changeset/*.md

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

Use a lowercase sentence in the imperative present tense for changeset summaries

Files:

  • .changeset/wet-games-relate.md
server/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server.mdc)

server/**/*.ts: Use c.var object to pass strongly-typed data between Hono middleware and route handlers; do not use c.set
All request validation (headers, body, params) must be handled by @hono/valibot-validator middleware; do not perform manual validation inside route handlers
Use Hono's built-in error handling by throwing new HTTPException() for expected errors; unhandled errors will be caught and logged automatically
Enforce Node.js best practices using ESLint plugin:n/recommended configuration
Enforce Drizzle ORM best practices using ESLint plugin:drizzle/all configuration, including requiring where clauses for update and delete operations
Use Drizzle ORM query builder for all database interactions; do not write raw SQL queries unless absolutely unavoidable
All authentication and authorization logic must be implemented in Hono middleware
Do not access process.env directly in application code; load all configuration and secrets once at startup and pass them through dependency injection or context
Avoid long-running, synchronous operations; use async/await correctly and be mindful of CPU-intensive tasks to prevent blocking the event loop

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{js,ts,tsx,jsx,sol}

📄 CodeRabbit inference engine (AGENTS.md)

Follow linter/formatter (eslint, prettier, solhint) strictly with high strictness level. No any type.

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Omit redundant type names in variable declarations - let the type system explain itself

**/*.{ts,tsx}: Use PascalCase for TypeScript types and interfaces
Use valibot for all runtime validation of API inputs, environment variables, and other data; define schemas once and reuse them
Infer TypeScript types from valibot schemas using type User = v.Input<typeof UserSchema> instead of manually defining interfaces

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Omit contextual names - don't repeat class/module names in members
Omit meaningless words like 'data', 'state', 'manager', 'engine', 'value' from variable and function names unless they add disambiguation

**/*.{ts,tsx,js,jsx}: Prefer function declarations for all multi-line functions; use function expressions or arrow functions only for single-line implementations
Prefer const for all variable declarations by default; only use let if the variable's value will be reassigned
Declare each variable on its own line with its own const or let keyword, not multiple declarations on one line
Use camelCase for TypeScript variables and functions
Always use import type { ... } for type imports
Use relative paths for all imports within the project; avoid tsconfig path aliases
Follow eslint-plugin-import order: react, external libraries, then relative paths
Use object and array destructuring to access and use properties
Use object method shorthand syntax when a function is a property of an object
Prefer optional chaining (?.), nullish coalescing (??), object and array spreading (...), and for...of loops over traditional syntax
Do not use abbreviations or cryptic names; write out full words like error, parameters, request instead of err, params, req
Use Number.parseInt() instead of the global parseInt() function when parsing numbers
All classes called with new must use PascalCase
Use Buffer.from(), Buffer.alloc(), or Buffer.allocUnsafe() instead of the deprecated new Buffer()
Use @ts-expect-error instead of @ts-ignore; follow it immediately with a single-line lowercase comment explaining why the error is expected, without separators like - or :
Do not include the type in a variable's name; let the static type system do its job (e.g., use const user: User not const userObject: User)
Do not repeat the name of a class or module within its members; omit contextual names (e.g., use `class User { getProfil...

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

server/**/*.{ts,tsx}: Server API: implement schema-first approach using OpenAPI via hono with validation via valibot middleware
Server database: drizzle schema is source of truth. Migrations required. No direct database access in handlers - use c.var.db

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

For files with a single default export, name the file identically to the export; for files with multiple exports, use camelCase with a strong preference for a single word

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
🧠 Learnings (4)
📚 Learning: 2025-12-30T15:03:28.449Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/style.mdc:0-0
Timestamp: 2025-12-30T15:03:28.449Z
Learning: Applies to **/.changeset/*.md : Use a lowercase sentence in the imperative present tense for changeset summaries

Applied to files:

  • .changeset/wet-games-relate.md
📚 Learning: 2025-12-23T19:57:14.465Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/git.mdc:0-0
Timestamp: 2025-12-23T19:57:14.465Z
Learning: Select the most precise gitmoji that best communicates the intent of the change (e.g., ✨ for new feature, 🐛 for bug fix, ♻️ for refactoring, ⚡️ for performance improvement, 📝 for documentation, 🚀 for deployment)

Applied to files:

  • .changeset/wet-games-relate.md
📚 Learning: 2025-12-23T19:56:43.683Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/contracts.mdc:0-0
Timestamp: 2025-12-23T19:56:43.683Z
Learning: Applies to contracts/**/*.t.sol : Use fuzz testing (testFuzz_...) extensively for functions with numerical or address inputs to cover a wide range of scenarios

Applied to files:

  • server/test/hooks/panda.test.ts
📚 Learning: 2025-12-23T19:58:16.574Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T19:58:16.574Z
Learning: Applies to contracts/**/*.test.sol : Smart contracts testing: fuzzing required for inputs and gas snapshots mandatory

Applied to files:

  • server/test/hooks/panda.test.ts
🧬 Code graph analysis (2)
server/test/hooks/panda.test.ts (2)
server/database/schema.ts (1)
  • cards (25-34)
server/utils/sardine.ts (1)
  • feedback (33-35)
server/hooks/panda.ts (1)
server/utils/sardine.ts (2)
  • risk (36-38)
  • feedback (33-35)
🔇 Additional comments (13)
server/test/hooks/panda.test.ts (3)

8-8: LGTM!

Sardine mock and utility imports are correctly added to support the new risk assessment tests.

Also applies to: 53-53


234-270: LGTM!

Good addition of feedback verification for the negative amount flow. The test correctly validates that Sardine feedback is sent with the expected issuing data structure.


1475-1475: LGTM!

The merchantCountry value correctly updated to ISO alpha-2 code format ("AR") to align with the new schema validation requiring exactly 2 characters.

server/hooks/panda.ts (10)

56-56: LGTM!

Clean import of risk and feedback functions from sardine utility.


225-252: Well-structured risk assessment with consistent error handling.

The assess() function now returns a consistent object shape for all error cases, addressing the previous concern about implicit undefined returns. The timeout vs other error distinction is preserved in the status field.


254-261: LGTM!

Risk assessment for refunds (negative amounts) correctly logs high-risk transactions as errors while allowing the authorization to proceed.


282-328: LGTM!

The parallel execution of traceCall and assess via Promise.all is efficient. Risk attributes are properly attached to the active span for observability.


480-496: LGTM!

Feedback correctly differentiates between refund (negative amount) and settled (positive amount) for completed transactions.


527-547: LGTM!

Feedback integration for declined transactions and negative amount authorizations is correctly implemented. The decline reason is properly forwarded to Sardine.


573-582: LGTM!

Good handling of the no-call path with appropriate feedback type based on action (authorization for created/updated, settlement for completed).


651-669: LGTM!

The switch statement now correctly handles "updated" action alongside "created", addressing the previous concern about missing feedback for update-with-collection scenarios.


220-220: LGTM!

Credential fetch correctly expanded to include id for Sardine customer identification.


74-82: Schema changes are intentional for risk assessment; verify with Panda documentation.

merchantCountry and merchantCategoryCode are required because the risk assessment integration depends on these fields (passed as countryCode and mcc). Existing tests consistently show Panda provides both fields, but you should confirm with Panda's API documentation that these fields are guaranteed in all webhook payloads before deploying to production.

Comment thread server/test/hooks/panda.test.ts
Comment thread server/test/hooks/panda.test.ts
Comment thread server/test/hooks/panda.test.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
.changeset/wet-games-relate.md (1)

5-5: Changeset summary still contains emoji.

The previous review already flagged that the summary should be a lowercase imperative present tense sentence (e.g., "assess transaction risk" instead of "✨ assess transaction risk"). You acknowledged you would fix this, but it appears unchanged. Based on learnings, changeset summaries must use lowercase imperative present tense.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 532d6d3 and a6c130c.

📒 Files selected for processing (3)
  • .changeset/wet-games-relate.md
  • server/hooks/panda.ts
  • server/test/hooks/panda.test.ts
🧰 Additional context used
📓 Path-based instructions (7)
server/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/server.mdc)

server/**/*.ts: Use c.var object to pass strongly-typed data between Hono middleware and route handlers; do not use c.set
All request validation (headers, body, params) must be handled by @hono/valibot-validator middleware; do not perform manual validation inside route handlers
Use Hono's built-in error handling by throwing new HTTPException() for expected errors; unhandled errors will be caught and logged automatically
Enforce Node.js best practices using ESLint plugin:n/recommended configuration
Enforce Drizzle ORM best practices using ESLint plugin:drizzle/all configuration, including requiring where clauses for update and delete operations
Use Drizzle ORM query builder for all database interactions; do not write raw SQL queries unless absolutely unavoidable
All authentication and authorization logic must be implemented in Hono middleware
Do not access process.env directly in application code; load all configuration and secrets once at startup and pass them through dependency injection or context
Avoid long-running, synchronous operations; use async/await correctly and be mindful of CPU-intensive tasks to prevent blocking the event loop

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{js,ts,tsx,jsx,sol}

📄 CodeRabbit inference engine (AGENTS.md)

Follow linter/formatter (eslint, prettier, solhint) strictly with high strictness level. No any type.

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Omit redundant type names in variable declarations - let the type system explain itself

**/*.{ts,tsx}: Use PascalCase for TypeScript types and interfaces
Use valibot for all runtime validation of API inputs, environment variables, and other data; define schemas once and reuse them
Infer TypeScript types from valibot schemas using type User = v.Input<typeof UserSchema> instead of manually defining interfaces

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Omit contextual names - don't repeat class/module names in members
Omit meaningless words like 'data', 'state', 'manager', 'engine', 'value' from variable and function names unless they add disambiguation

**/*.{ts,tsx,js,jsx}: Prefer function declarations for all multi-line functions; use function expressions or arrow functions only for single-line implementations
Prefer const for all variable declarations by default; only use let if the variable's value will be reassigned
Declare each variable on its own line with its own const or let keyword, not multiple declarations on one line
Use camelCase for TypeScript variables and functions
Always use import type { ... } for type imports
Use relative paths for all imports within the project; avoid tsconfig path aliases
Follow eslint-plugin-import order: react, external libraries, then relative paths
Use object and array destructuring to access and use properties
Use object method shorthand syntax when a function is a property of an object
Prefer optional chaining (?.), nullish coalescing (??), object and array spreading (...), and for...of loops over traditional syntax
Do not use abbreviations or cryptic names; write out full words like error, parameters, request instead of err, params, req
Use Number.parseInt() instead of the global parseInt() function when parsing numbers
All classes called with new must use PascalCase
Use Buffer.from(), Buffer.alloc(), or Buffer.allocUnsafe() instead of the deprecated new Buffer()
Use @ts-expect-error instead of @ts-ignore; follow it immediately with a single-line lowercase comment explaining why the error is expected, without separators like - or :
Do not include the type in a variable's name; let the static type system do its job (e.g., use const user: User not const userObject: User)
Do not repeat the name of a class or module within its members; omit contextual names (e.g., use `class User { getProfil...

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

server/**/*.{ts,tsx}: Server API: implement schema-first approach using OpenAPI via hono with validation via valibot middleware
Server database: drizzle schema is source of truth. Migrations required. No direct database access in handlers - use c.var.db

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

For files with a single default export, name the file identically to the export; for files with multiple exports, use camelCase with a strong preference for a single word

Files:

  • server/test/hooks/panda.test.ts
  • server/hooks/panda.ts
**/.changeset/*.md

📄 CodeRabbit inference engine (.cursor/rules/style.mdc)

Use a lowercase sentence in the imperative present tense for changeset summaries

Files:

  • .changeset/wet-games-relate.md
🧠 Learnings (4)
📚 Learning: 2025-12-23T19:56:43.683Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/contracts.mdc:0-0
Timestamp: 2025-12-23T19:56:43.683Z
Learning: Applies to contracts/**/*.t.sol : Use fuzz testing (testFuzz_...) extensively for functions with numerical or address inputs to cover a wide range of scenarios

Applied to files:

  • server/test/hooks/panda.test.ts
📚 Learning: 2025-12-23T19:58:16.574Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-23T19:58:16.574Z
Learning: Applies to contracts/**/*.test.sol : Smart contracts testing: fuzzing required for inputs and gas snapshots mandatory

Applied to files:

  • server/test/hooks/panda.test.ts
📚 Learning: 2025-12-30T15:03:28.449Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/style.mdc:0-0
Timestamp: 2025-12-30T15:03:28.449Z
Learning: Applies to **/.changeset/*.md : Use a lowercase sentence in the imperative present tense for changeset summaries

Applied to files:

  • .changeset/wet-games-relate.md
📚 Learning: 2025-12-23T19:57:14.465Z
Learnt from: CR
Repo: exactly/exa PR: 0
File: .cursor/rules/git.mdc:0-0
Timestamp: 2025-12-23T19:57:14.465Z
Learning: Select the most precise gitmoji that best communicates the intent of the change (e.g., ✨ for new feature, 🐛 for bug fix, ♻️ for refactoring, ⚡️ for performance improvement, 📝 for documentation, 🚀 for deployment)

Applied to files:

  • .changeset/wet-games-relate.md
🧬 Code graph analysis (1)
server/hooks/panda.ts (1)
server/utils/sardine.ts (2)
  • risk (36-38)
  • feedback (33-35)
🔇 Additional comments (16)
server/hooks/panda.ts (12)

56-56: LGTM!

Clean import of risk assessment and feedback utilities from the sardine module.


97-97: Verify Panda doesn't send null for declinedReason.

Changed from v.nullable to v.optional. If Panda sends null instead of omitting the field, validation will fail. Ensure this matches the actual webhook payload format.


225-252: LGTM! Consistent error handling for risk assessment.

The assess() function now returns a consistent fallback object for all error cases, addressing the previous review concern. The distinction between "timeout" and "error" status is useful for observability.


254-261: LGTM! Risk assessment for refunds with appropriate alerting.

High-risk refunds are flagged via captureException but allowed to proceed, which is appropriate for refund flows where blocking could negatively impact customer experience while still maintaining observability.


282-288: LGTM!

Risk assessment for verification path follows the same pattern as other flows.


291-328: LGTM! Good use of parallel execution.

Running traceCall and assess() in parallel via Promise.all reduces latency without sacrificing the risk assessment data. Risk results are properly attached to tracing spans for observability.


480-496: LGTM! Proper settlement feedback for refund flows.

Correctly distinguishes between refunds (negative amount → status: "refund") and partial captures (positive amount → status: "settled"). Fire-and-forget with error capture is appropriate for non-blocking feedback.


527-538: LGTM!

Declined authorization feedback correctly reports network_declined status with the decline reason for Sardine's analysis.


539-548: LGTM!

Negative amount authorization feedback is correctly placed and reachable after the earlier refactoring.


573-583: LGTM!

Feedback correctly differentiates between authorization (created/updated) and settlement (completed) when no collection call is needed.


651-669: LGTM! Complete feedback coverage for all action types.

The switch statement now correctly handles created, updated, and completed actions, addressing the previous review concern about missing the updated case.


74-82: Verify that Panda webhooks always provide merchantCountry and merchantCategoryCode.

The schema now enforces both fields as required (merchantCountry must be 2 characters, merchantCategoryCode must be present), with no defensive fallback if either is missing. All tests provide these fields, but if actual Panda payloads sometimes omit them, validation will fail and reject the transaction. Confirm with Panda that these fields are guaranteed in all webhook events.

server/test/hooks/panda.test.ts (4)

8-8: LGTM!

Sardine mock imported correctly for test isolation.


187-205: LGTM! Timeout error mock correctly sets error.name.

The mock now properly sets error.name = "TimeoutError" to trigger the timeout branch in the error handler, addressing the previous review feedback.


236-272: LGTM! Good feedback verification for negative amounts.

The test properly verifies that the feedback function is called with the expected parameters for negative amount (refund) authorizations.


1477-1477: LGTM!

Test fixtures correctly updated to use 2-letter ISO country code ("AR") matching the new schema validation.

Comment thread server/test/hooks/panda.test.ts
@cruzdanilo cruzdanilo merged commit a6c130c into main Dec 30, 2025
5 checks passed
@cruzdanilo cruzdanilo deleted the nicolas branch December 30, 2025 20:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

server: enable dry risk assessment for sardine

2 participants