Skip to content

Conversation

@aguxez
Copy link
Contributor

@aguxez aguxez commented Jan 22, 2026


Open with Devin

Summary by CodeRabbit

  • New Features
    • Added Pax travel insurance support for users upgrading from Platinum to Signature card tier, automatically provisioned upon card upgrade completion.

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

@changeset-bot
Copy link

changeset-bot bot commented Jan 22, 2026

🦋 Changeset detected

Latest commit: 1be9a29

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

@gemini-code-assist
Copy link

Summary of Changes

Hello @aguxez, 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 enhances the server's card issuance API by integrating with PAX for users who are upgrading from a Platinum card to a Signature card. The system now identifies these upgrade scenarios, retrieves necessary user information from Persona, and then registers the user with PAX for associated benefits, all while ensuring that the card issuance flow remains resilient to external service failures.

Highlights

  • PAX Integration for Platinum to Signature Upgrades: Implemented logic to automatically enroll users in PAX when they upgrade their card from Platinum to Signature. This involves checking for a previously deleted Platinum card associated with the user's credential.
  • Persona Data Retrieval: A new function, addCapitaForPlatinumUpgrade, was introduced to fetch user KYC (Know Your Customer) data (such as name, birthdate, document ID, email, and phone) from the Persona API. This data is then used to register the user with PAX for services like travel insurance.
  • Robust Error Handling: The integration includes comprehensive error handling for calls to both Persona and PAX APIs. If these external API calls fail or if Persona account data is missing, the core card issuance process will still succeed, and errors are captured via Sentry.
  • Comprehensive Testing: Extensive unit tests have been added to cover various scenarios, including successful upgrades, new card issuances (no upgrade), and error conditions from the PAX and Persona APIs, ensuring the feature's reliability.
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.

@coderabbitai
Copy link

coderabbitai bot commented Jan 22, 2026

Warning

Rate limit exceeded

@aguxez has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 27 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

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

The PR adds PAX integration for platinum-to-signature card upgrades. When a user upgrades their card, the system detects the transition, retrieves persona data, derives an internal ID, and invokes PAX's addCapita function to provision travel insurance. Tests and mocks are updated accordingly.

Changes

Cohort / File(s) Summary
Release Metadata
.changeset/wise-comics-know.md
Documents patch release for @exactly/server with PAX feature for platinum signature upgrades.
Core Implementation
server/api/card.ts
Detects platinum-to-signature upgrades via deleted platinum card queries; introduces addCapitaForPlatinumUpgrade() function to fetch persona data, extract identity attributes, derive internal ID, and call PAX's addCapita with "travel insurance" product; invokes upgrade handler in both GET and POST card flows; adds Sentry error capture.
Test Infrastructure
server/test/api/card.test.ts, server/test/mocks/pax.ts
Extends PAX mock with typed signatures for addCapita and removeCapita; adds comprehensive test cases for platinum upgrade flow, non-upgrade scenarios, API error handling, and missing persona account edge cases; mocks persona.getAccount and Pax.addCapita; includes database verification for deleted platinum cards.

Sequence Diagram

sequenceDiagram
    participant Client
    participant CardAPI as Card API
    participant DB as Database
    participant Persona as Persona Service
    participant PAX as PAX Service
    participant Sentry

    Client->>CardAPI: POST/GET card request
    CardAPI->>DB: Query DELETED platinum cards
    DB-->>CardAPI: Check if upgrade from platinum
    alt Is Upgrade
        CardAPI->>Persona: getAccount(credentialId)
        Persona-->>CardAPI: Account with persona data
        CardAPI->>CardAPI: Extract identity attributes & derive internalId
        CardAPI->>PAX: addCapita(internalId, product: "travel insurance")
        alt PAX Success
            PAX-->>CardAPI: Confirmation
        else PAX Error
            PAX-->>CardAPI: Error
            CardAPI->>Sentry: Capture error with context
        end
    end
    CardAPI->>DB: Issue signature card
    CardAPI-->>Client: Card response
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related PRs

  • ✨ server: integrate sardine #594 — Both modify server/api/card.ts to trigger external service calls post-issuance; PR #594 adds Sardine.customer integration while this PR adds PAX addCapita for platinum upgrades.

Suggested Reviewers

  • cruzdanilo
  • nfmelendez
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding Pax integration for platinum-to-signature card upgrades, matching the changeset and code modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pax-card-upgrade

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link

@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

The pull request introduces functionality to track platinum to signature card upgrades by integrating with the PAX system. The changes include adding logic to detect such upgrades, calling an external API (addCapita) with user data, and comprehensive test cases covering various scenarios, including successful integration, no-upgrade cases, and error handling for external API calls. The addition of vi.clearAllMocks() in the test suite's afterEach hook is a good practice for ensuring test isolation and reliability.

Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional flags.

Open in Devin Review

Copy link

@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

🤖 Fix all issues with AI agents
In `@server/api/card.ts`:
- Around line 383-385: The call to addCapitaForPlatinumUpgrade(credentialId,
account) is intended as fire-and-forget but currently returns a floating
promise; prefix the call with the void operator to make the intent explicit and
satisfy linters (i.e., change the invocation inside the isUpgradeFromPlatinum
block to void addCapitaForPlatinumUpgrade(credentialId, account)). Ensure you do
not await it and keep any surrounding logic unchanged so card creation remains
non-blocking.

In `@server/test/mocks/pax.ts`:
- Around line 3-4: The imports in server/test/mocks/pax.ts are out of order;
move external-library imports before relative imports so "valibot" (InferInput)
is imported before the local "../../utils/pax" (CapitaRequest), ensuring import
order follows eslint-plugin-import conventions (external libs first, then
relative paths) and updating any surrounding import groups similarly.

Comment on lines +383 to +385
if (isUpgradeFromPlatinum) {
addCapitaForPlatinumUpgrade(credentialId, account);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider using void operator to make fire-and-forget intent explicit.

The fire-and-forget pattern is appropriate here to avoid blocking card creation. However, prepending void makes the intent explicit and suppresses potential linter warnings about floating promises.

♻️ Suggested change
          if (isUpgradeFromPlatinum) {
-           addCapitaForPlatinumUpgrade(credentialId, account);
+           void addCapitaForPlatinumUpgrade(credentialId, account);
          }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (isUpgradeFromPlatinum) {
addCapitaForPlatinumUpgrade(credentialId, account);
}
if (isUpgradeFromPlatinum) {
void addCapitaForPlatinumUpgrade(credentialId, account);
}
🤖 Prompt for AI Agents
In `@server/api/card.ts` around lines 383 - 385, The call to
addCapitaForPlatinumUpgrade(credentialId, account) is intended as
fire-and-forget but currently returns a floating promise; prefix the call with
the void operator to make the intent explicit and satisfy linters (i.e., change
the invocation inside the isUpgradeFromPlatinum block to void
addCapitaForPlatinumUpgrade(credentialId, account)). Ensure you do not await it
and keep any surrounding logic unchanged so card creation remains non-blocking.

Comment on lines +3 to +4
import type { CapitaRequest } from "../../utils/pax";
import type { InferInput } from "valibot";
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Import order should follow eslint-plugin-import conventions.

External library imports should come before relative path imports. As per coding guidelines, the order should be: react, external libraries, then relative paths.

♻️ Suggested fix
-import type { CapitaRequest } from "../../utils/pax";
-import type { InferInput } from "valibot";
+import type { InferInput } from "valibot";
+
+import type { CapitaRequest } from "../../utils/pax";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import type { CapitaRequest } from "../../utils/pax";
import type { InferInput } from "valibot";
import type { InferInput } from "valibot";
import type { CapitaRequest } from "../../utils/pax";
🤖 Prompt for AI Agents
In `@server/test/mocks/pax.ts` around lines 3 - 4, The imports in
server/test/mocks/pax.ts are out of order; move external-library imports before
relative imports so "valibot" (InferInput) is imported before the local
"../../utils/pax" (CapitaRequest), ensuring import order follows
eslint-plugin-import conventions (external libs first, then relative paths) and
updating any surrounding import groups similarly.

@sentry
Copy link

sentry bot commented Jan 22, 2026

Codecov Report

❌ Patch coverage is 33.33333% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.67%. Comparing base (907ade0) to head (1be9a29).

Files with missing lines Patch % Lines
server/api/card.ts 33.33% 10 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #670      +/-   ##
==========================================
- Coverage   63.66%   59.67%   -4.00%     
==========================================
  Files         169      169              
  Lines        5882     5309     -573     
  Branches     1753     1502     -251     
==========================================
- Hits         3745     3168     -577     
- Misses       1964     1976      +12     
+ Partials      173      165       -8     
Flag Coverage Δ
e2e 59.67% <33.33%> (-3.07%) ⬇️

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

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

Comment on lines +341 to +350
// check if this is an upgrade from platinum to signature
const deletedPlatinumCard = await database.query.cards.findFirst({
where: and(
eq(cards.credentialId, credentialId),
eq(cards.status, "DELETED"),
eq(cards.productId, PLATINUM_PRODUCT_ID),
),
columns: { id: true },
});
const isUpgradeFromPlatinum = !!deletedPlatinumCard;

Choose a reason for hiding this comment

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

🔴 Pax enrollment missed for platinum users upgrading via migration path

Users whose platinum card gets deleted during the migration process will not be enrolled in pax travel insurance, even though they are effectively upgrading from platinum to signature.

Click to expand

Root Cause

The isUpgradeFromPlatinum check at lines 342-350 queries for cards with status: "DELETED" and productId: PLATINUM_PRODUCT_ID before the migration loop at lines 354-369 runs. The migration loop marks cards as DELETED if they don't exist in Panda.

Scenario

  1. User has an ACTIVE platinum card that no longer exists in Panda
  2. deletedPlatinumCard query runs and finds nothing (card is still ACTIVE)
  3. isUpgradeFromPlatinum is set to false
  4. Migration loop runs, marks the platinum card as DELETED
  5. New signature card is created
  6. Pax enrollment is skipped because isUpgradeFromPlatinum is false

Actual vs Expected

  • Actual: User upgrading from platinum via migration doesn't get pax travel insurance
  • Expected: User should be enrolled in pax since they had a platinum card that was deleted as part of upgrading to signature

Impact

Users who had platinum cards that were migrated/deleted during the upgrade process miss out on the travel insurance benefit they should receive.

Recommendation: Move the isUpgradeFromPlatinum check to after the migration loop, or track which cards get deleted during migration and check if any of them were platinum cards. For example:

let isUpgradeFromPlatinum = !!deletedPlatinumCard;
for (const card of credential.cards) {
  // ... existing logic ...
  if (/* card deleted */ && card.productId === PLATINUM_PRODUCT_ID) {
    isUpgradeFromPlatinum = true;
  }
}

Note: This also requires adding productId to the cards query at line 334.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +342 to +351
const deletedPlatinumCard = await database.query.cards.findFirst({
where: and(
eq(cards.credentialId, credentialId),
eq(cards.status, "DELETED"),
eq(cards.productId, PLATINUM_PRODUCT_ID),
),
columns: { id: true },
});
const isUpgradeFromPlatinum = !!deletedPlatinumCard;

Copy link

Choose a reason for hiding this comment

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

Bug: A user can be enrolled in Pax multiple times by repeatedly creating and deleting a signature card, as the upgrade check doesn't track previous enrollments.
Severity: MEDIUM

Suggested Fix

To prevent duplicate enrollments, introduce a mechanism to track whether a user has already received the Pax upgrade benefit. This could be a new flag in the user's profile or a separate table that logs Pax enrollments. Before calling addCapitaForPlatinumUpgrade, check this flag to ensure the user hasn't been enrolled before.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: server/api/card.ts#L342-L351

Potential issue: The logic for Pax enrollment during a card upgrade can be triggered
multiple times for the same user. A user with a previously deleted platinum card who
creates a signature card gets enrolled in Pax. If they then delete this signature card
and create another one, the system will enroll them in Pax again. This happens because
the upgrade check only looks for a deleted platinum card and doesn't verify if the user
has already been enrolled. The check preventing multiple active cards (`cardCount > 0`)
doesn't account for deleted signature cards, allowing the re-creation flow. This can
lead to duplicate travel insurance policies and potential billing issues.

Did we get this right? 👍 / 👎 to inform future reviews.

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.

2 participants