Skip to content

✨ server: add wallet provisioning endpoint#949

Open
aguxez wants to merge 1 commit intomainfrom
server-provisioning
Open

✨ server: add wallet provisioning endpoint#949
aguxez wants to merge 1 commit intomainfrom
server-provisioning

Conversation

@aguxez
Copy link
Copy Markdown
Contributor

@aguxez aguxez commented Apr 10, 2026

Closes #440


Open with Devin

Summary by CodeRabbit

  • New Features

    • Added a wallet provisioning endpoint allowing authenticated users to retrieve provisioning details (card ID and time-based secret) for an eligible card.
  • Tests

    • Added comprehensive tests for successful provisioning, processor error mappings, deleted/missing card, missing external association, and missing credential scenarios.
  • Chores

    • Added a changeset declaring a patch release documenting the new wallet provisioning endpoint.

This is part 1 of 2 in a stack made with GitButler:

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 10, 2026

🦋 Changeset detected

Latest commit: 2f6644b

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

@aguxez aguxez force-pushed the server-provisioning branch from 7de9bc2 to 1d0aa0f Compare April 10, 2026 09:27
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new authenticated GET /wallet endpoint that loads a credential's ACTIVE|FROZEN card, calls Panda for processor details, returns processorCardId/timeBasedSecret, adds a Panda utility, tests covering success and error mappings, and adds a changeset for a patch release.

Changes

Cohort / File(s) Summary
Wallet Endpoint
server/api/card.ts
New authenticated GET /wallet route: reads credentialId from cookie, loads credential (pandaId + first `ACTIVE
Panda Utility
server/utils/panda.ts
Added exported getProcessorDetails(cardId: string) which GETs /issuing/cards/${cardId}/processorDetails and validates/returns { processorCardId, timeBasedSecret }.
Tests
server/test/api/card.test.ts
Added wallet test suite seeding credentials/cards and asserting: success for active/frozen cards (200 with processor details and stub verification), Panda error mappings (404→404 { code: "no card" }, 5xx→500), and credential/panda/card absence responses (500/403/404 as appropriate).
Release Metadata
.changeset/chilly-suns-dress.md
New changeset declaring a patch release for @exactly/server referencing the wallet provisioning endpoint.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client
    participant API as GET /wallet
    participant DB as Database
    participant Panda as Panda API

    Client->>API: GET /wallet (credentialId cookie)
    API->>DB: Load credential (pandaId, first ACTIVE|FROZEN card)
    alt no credential
        DB-->>API: not found
        API-->>Client: 500 { code: "no credential" }
    else credential missing pandaId
        DB-->>API: credential (no pandaId)
        API-->>Client: 403 { code: "no panda" }
    else no eligible card
        DB-->>API: credential (no eligible card)
        API-->>Client: 404 { code: "no card" }
    else credential + card found
        DB-->>API: credential + cardId
        API->>Panda: GET /issuing/cards/{cardId}/processorDetails
        alt processor details found
            Panda-->>API: { processorCardId, timeBasedSecret }
            API-->>Client: 200 { cardId, cardSecret }
        else processor 404
            Panda-->>API: 404
            API-->>Client: 404 { code: "no card" }
        else other error
            Panda-->>API: error
            API-->>Client: 500 (error)
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • cruzdanilo
  • nfmelendez
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'add wallet provisioning endpoint' accurately and directly describes the main change: a new authenticated GET /wallet endpoint for wallet provisioning. It is specific, clear, and concise without unnecessary details.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch server-provisioning

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.

@aguxez aguxez marked this pull request as draft April 10, 2026 09:27
devin-ai-integration[bot]

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

@sentry
Copy link
Copy Markdown

sentry bot commented Apr 10, 2026

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
627 1 626 1
View the full list of 1 ❄️ flaky test(s)
local::local

Flake rate in main: 100.00% (Passed 0 times, Failed 1 times)

Stack Traces | 349s run time
Element not found: Text matching regex: Spending limit

To view more test analytics, go to the Prevent Tests Dashboard

@aguxez aguxez force-pushed the server-provisioning branch 3 times, most recently from af7c987 to 9495bf1 Compare April 10, 2026 13:10
coderabbitai[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the server-provisioning branch from 9495bf1 to 0975d4d Compare April 10, 2026 13:46
coderabbitai[bot]

This comment was marked as resolved.

@aguxez aguxez marked this pull request as ready for review April 10, 2026 16:12
@aguxez aguxez force-pushed the server-provisioning branch from 0975d4d to 0ed88f7 Compare April 13, 2026 08:27
coderabbitai[bot]

This comment was marked as resolved.

chatgpt-codex-connector[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the server-provisioning branch from 0ed88f7 to ba4376c Compare April 13, 2026 09:21
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.

♻️ Duplicate comments (1)
server/test/api/card.test.ts (1)

867-869: ⚠️ Potential issue | 🟡 Minor

Use Panda in the mocked ServiceError for consistency.

"Rain" works, but using the real provider label keeps test fixtures closer to production behavior.

Suggested fix
-      vi.spyOn(panda, "getProcessorDetails").mockRejectedValueOnce(new ServiceError("Rain", 500, "internal error"));
+      vi.spyOn(panda, "getProcessorDetails").mockRejectedValueOnce(new ServiceError("Panda", 500, "internal error"));

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7e528c82-bb28-42c9-8cd9-b0f075ed5b87

📥 Commits

Reviewing files that changed from the base of the PR and between 0ed88f7 and ba4376c.

📒 Files selected for processing (4)
  • .changeset/chilly-suns-dress.md
  • server/api/card.ts
  • server/test/api/card.test.ts
  • server/utils/panda.ts

sentry[bot]

This comment was marked as resolved.

@aguxez aguxez force-pushed the server-provisioning branch from ba4376c to 2f6644b Compare April 15, 2026 08:39
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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36b39f7e-467b-4910-bcf5-d80b1c4e08e9

📥 Commits

Reviewing files that changed from the base of the PR and between ba4376c and 2f6644b.

📒 Files selected for processing (4)
  • .changeset/chilly-suns-dress.md
  • server/api/card.ts
  • server/test/api/card.test.ts
  • server/utils/panda.ts

Comment thread server/api/card.ts
Comment on lines +632 to +639
try {
const provisioning = await getProcessorDetails(credential.cards[0].id);
return c.json(
{ cardId: provisioning.processorCardId, cardSecret: provisioning.timeBasedSecret } satisfies InferOutput<
typeof WalletResponse
>,
200,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Prevent caching of wallet provisioning secrets.

This success path returns raw provisioning material on a GET, but it does not set Cache-Control: no-store. With cookie auth, browser/private caches can persist cardSecret, which materially weakens the protection around these credentials.

🔒 Proposed fix
       try {
         const provisioning = await getProcessorDetails(credential.cards[0].id);
+        c.header("Cache-Control", "no-store");
+        c.header("Pragma", "no-cache");
         return c.json(
           { cardId: provisioning.processorCardId, cardSecret: provisioning.timeBasedSecret } satisfies InferOutput<
             typeof WalletResponse
           >,
           200,

@aguxez aguxez mentioned this pull request Apr 15, 2026
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: push provisioning

1 participant