feat(payment): add Chainrails cross-chain payment module#51
feat(payment): add Chainrails cross-chain payment module#51GoodyBoy301 wants to merge 42 commits intokeep-starknet-strange:mainfrom
Conversation
- Added @chainrails/sdk as a dependency for payment processing. - Introduced Payment module for handling cross-chain, multi-token payments. - Implemented methods for quote discovery, intent management, and session handling. - Updated SDK configuration to include payment settings. - Created tests for Payment module functionalities and SDK integration.
… token and session-based flows
…te flappy bird example
- implement /session-token route to create StarkNet payment sessions - integrate CHAINRAILS_API_KEY environment variable for payment feature - log payment feature status on server start for clarity
feat(payment-server): add payment server with session token endpoint
…tion - Implemented Save Me modal UI with payment processing via StarkSDK - Added event handling for Save Me button, cancel, and close actions - Integrated Save Me modal with game state and revive logic - Updated main.js to handle Save Me flow, countdown, and resurrection - Enhanced CSS with countdown styling and overlay - Modified index.html to include Save Me assets and modal structure - Updated package-lock.json with new dependencies for StarkSDK and Chainrails - Added new asset 'saveme.png' for Save Me button - Extended starknet.ts to support Save Me payment interactions
feat(payment-web): add infinite flappy bird example with TypeScript and Vite setup
- Simplify modal input types and platform checks - Remove deprecated session URL modal logic - Integrate React Native modal opening method - Update tests for platform-specific behavior
…payment implementation
fix(eslint): exclude unused example from config test(payment): add missing assertion for session token in payment test
…odal - Introduce project structure with layout, page, and icons - Add API route for creating payment sessions using Chainrails SDK - Implement client-side payment modal integration - Configure Tailwind CSS and global styles for the demo - Include example icons for UI enhancement - Add TypeScript configurations for project setup
- Change project name from 'Chainrails Next.js Demo' to 'Starkzap Next.js Demo' - Replace Chainrails SDK usage with Starkzap SDK in API route - Update frontend to initialize Starkzap SDK and handle payment session and modal - Adjust configuration references from 'src/app/page.tsx' - Modify package dependencies and scripts accordingly - Improve clarity on destinationChain and token defaults in API - Remove obsolete dependencies and comments for cleaner implementation
|
Thank you for this. Would you have a live demo of the flappy bird example with payment you have deployed? |
Yes! You can try the Flappy Bird payment demo here: https://starkzap-flappy-bird.vercel.app/ |
|
yeah that's what he shared, I believe. The improvement shows up when one fails in the game. Rather than start again, you can simply pay with Chainrails to continue, does that make sense? A deployed demo can be found here: https://starkzap-flappy-bird.vercel.app/ |
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new Chainrails-based Payments feature (types, Payment class, modal manager), exposes it via StarkZap SDK and config, includes docs, tests, examples (Next.js demo, Flappy Bird revive flow, server endpoint), and small tooling/ignore updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client App
participant SDK as StarkZap SDK
participant Payment as Payment Manager
participant Modal as Modal Manager
participant Chainrails as Chainrails API
Client->>SDK: payment()
SDK->>Payment: instantiate(config)
Client->>Payment: createSession(input)
Payment->>Chainrails: POST /session (auth)
Chainrails-->>Payment: { sessionToken, amount }
Payment-->>Client: PaymentSessionOutput
Client->>Payment: modal({sessionToken,...})
Payment->>Modal: modal(input)
Modal->>Modal: create element, bind callbacks, append & open
Client->>Modal: handle.pay()
Modal->>Chainrails: web modal interactions
alt success
Chainrails-->>Modal: success
Modal-->>Client: Promise<true>
else cancel/fail
Modal-->>Client: Promise<false>
end
Modal->>Modal: cleanup DOM
sequenceDiagram
participant Client as Client App
participant Payment as Payment Manager
participant Chainrails as Chainrails API
Client->>Payment: getSessionQuotes(token)
Payment->>Chainrails: GET /quotes?session=token
Chainrails-->>Payment: Quote[]
Client->>Payment: getBestQuote(input)
Payment->>Chainrails: GET /quotes/best
Chainrails-->>Payment: best Quote
Client->>Payment: createSessionIntent(input)
Payment->>Chainrails: POST /intents
Chainrails-->>Payment: PaymentIntent
Client->>Payment: triggerSessionProcessing(addr)
Payment->>Chainrails: POST /intents/process
Chainrails-->>Payment: { status, txHash }
Payment-->>Client: TriggerProcessingOutput
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 16
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
examples/flappy-bird/public/js/main.js (1)
546-553:⚠️ Potential issue | 🟡 Minor
if (canUseSaveMe)is gating unrelated medal animation.Because there’s no block, medal animation only runs when Save Me is available.
Proposed fix
soundSwoosh.stop(); soundSwoosh.play(); $("#actions").transition({ y: '0px', opacity: 1}, 600, 'ease'); - if (canUseSaveMe) - //also animate in the MEDAL! WOO! if(wonmedal) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flappy-bird/public/js/main.js` around lines 546 - 553, The medal animation is accidentally controlled by the preceding if (canUseSaveMe) because there are no braces; fix by properly scoping the save-me conditional (wrap the intended save-me statements in braces or terminate it so it’s a single statement) so the wonmedal block (the wonmedal check and the $("#medal").css / $("#medal").transition calls) executes independently; locate the if (canUseSaveMe), the wonmedal condition, and the $("#medal") calls and adjust braces/statement boundaries accordingly.
🧹 Nitpick comments (11)
examples/chainrails-nextjs-example/src/icons/NGFlag.tsx (2)
8-10: Avoid conflicting accessibility semantics on the SVG.Line 8 hides the icon from assistive tech, while Line 9 assigns it an exposed image role. Keep one intent: decorative (
aria-hiddenonly) or informative (role="img"+ accessible label).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/icons/NGFlag.tsx` around lines 8 - 10, The SVG in the NGFlag component currently has conflicting accessibility attributes (aria-hidden="true" and role="img"); decide whether the flag is decorative or informative and make them consistent: for decorative icons keep aria-hidden="true" and remove role="img"; for informative images remove aria-hidden, keep role="img" and add an accessible label (aria-label or title) to the SVG or expose a descriptive text prop on NGFlag so screen readers get a name. Locate the SVG element inside the NGFlag component and apply the corresponding change.
5-6: Normalize intrinsic SVG size to match rendered size.
width="800px"/height="800px"(Lines 5-6) is oversized relative to Line 11 (h-4 w-4). Consider removing intrinsic size or setting it to24for consistency and maintainability.Also applies to: 11-11
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/icons/NGFlag.tsx` around lines 5 - 6, The SVG in the NGFlag component uses oversized intrinsic width/height ("width" and "height" attributes set to "800px") that conflict with its rendered classes (className "h-4 w-4"); update the SVG by removing the explicit width/height attributes or set them to 24 (e.g., width="24" height="24") so the intrinsic size matches the rendered size, keeping the existing viewBox and paths intact; locate the SVG element in the NGFlag component and adjust the width/height attributes accordingly to normalize sizing.examples/chainrails-nextjs-example/src/app/globals.css (1)
27-35: Minor Stylelint warnings.Static analysis flagged a couple of minor style issues: empty lines before declarations and
optimizeLegibilityvsoptimizelegibility(CSS values are case-insensitive, but Stylelint prefers lowercase). These are cosmetic and don't affect functionality.✨ Optional fix to satisfy Stylelint
:root { font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; - color-scheme: light; color: `#213547`; background-color: `#ffffff`; - font-synthesis: none; - text-rendering: optimizeLegibility; + text-rendering: optimizelegibility; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/app/globals.css` around lines 27 - 35, Remove the stray empty line before the declarations in the CSS rule that contains color-scheme, color, background-color, etc., and change the value "optimizeLegibility" in the text-rendering declaration to lowercase "optimizelegibility" to satisfy Stylelint's casing and blank-line rules; update the block containing the properties (color-scheme, color, background-color, font-synthesis, text-rendering, -webkit-font-smoothing, -moz-osx-font-smoothing) so properties are consecutive with no extra blank line and the text-rendering value is lowercase.examples/chainrails-nextjs-example/package.json (1)
11-19: Consider moving build-time dependencies to devDependencies.
@tailwindcss/postcssandpostcssare build-time tools and are typically placed indevDependenciesrather thandependencies. This doesn't affect functionality but aligns with npm conventions.Suggested change
"dependencies": { - "@tailwindcss/postcss": "^4.1.18", "next": "^15.1.0", - "postcss": "^8.5.6", "react": "^19.2.1", "react-dom": "^19.2.1", "starkzap": "file:../..", "tailwindcss": "^4.1.18" }, "devDependencies": { + "@tailwindcss/postcss": "^4.1.18", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", + "postcss": "^8.5.6", "typescript": "^5" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/package.json` around lines 11 - 19, Move the build-time packages "@tailwindcss/postcss" and "postcss" out of the "dependencies" section in package.json and add them to "devDependencies" with the same version strings (e.g., "^4.1.18" and "^8.5.6") so they are installed only for development/build. Update the package.json entries for "dependencies" and "devDependencies" accordingly and run your package manager to refresh the lockfile so the change is reflected in the project metadata.examples/server/server.ts (2)
214-221: Hardcoded recipient address in demo endpoint.The recipient address and amount are hardcoded, which is fine for demonstration purposes. Consider adding a comment noting these should be parameterized in production use.
Add clarifying comment
try { + // Demo values - parameterize recipient and amount in production const session = await starkzap.payment().createSession({ amount: "0.1", recipient: "0x0075597a61229d143Ffba493C9f8A8057ecCeeA7BFDDBFD8Aaf79AC8935205c0", destinationChain: PaymentChains.STARKNET, token: PaymentTokenSymbols.USDC, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/server/server.ts` around lines 214 - 221, The demo call to starkzap.payment().createSession hardcodes amount and recipient (amount: "0.1", recipient: "0x007559...5205c0", destinationChain: PaymentChains.STARKNET, token: PaymentTokenSymbols.USDC); update the code by adding a clear inline comment above this createSession call stating these values are hardcoded for demo only and must be parameterized/validated in production (e.g., accept from request body, validate address and amount) so reviewers know to replace createSession's hardcoded recipient and amount in real deployments.
206-228: Inconsistent endpoint path and missing authentication.Two observations:
- The
/session-tokenendpoint doesn't follow the/api/prefix convention used by other endpoints in this server.- Unlike wallet endpoints, this endpoint has no authentication middleware. While acceptable for a demo, this should be documented or protected in production.
Suggested path consistency fix
// Payment session endpoint (requires CHAINRAILS_API_KEY) -app.get("/session-token", async (_, res) => { +app.get("/api/session-token", async (_, res) => {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/server/server.ts` around lines 206 - 228, Rename the route path from "/session-token" to "/api/session-token" for consistency and add the same authentication middleware used by the wallet routes to the app.get handler (e.g., app.get("/api/session-token", authMiddleware, async (...) => { ... })); if you intentionally want this endpoint to be public for the demo, instead add an explicit comment above the handler stating it is demo-only and must be protected in production and/or require CHAINRAILS_API_KEY, referencing the starkzap.payment().createSession call and PaymentChains.STARKNET / PaymentTokenSymbols.USDC usage so reviewers know which endpoint to secure.mintlify-docs/build/consumer-app-sdk/payment.mdx (2)
504-507: Avoid nestedsetTimeoutwith async callback in example code.The
setTimeoutwith an async callback can lead to unhandled promise rejections and is a common anti-pattern. Consider using a more robust polling pattern or event-based approach in the example.Improved example with proper async handling
if (success) { console.log("Payment successful!"); - // Check status after a short delay - setTimeout(async () => { - const status = await checkPaymentStatus(session.sessionToken); - console.log("Intent status:", status); - }, 5000); + // Poll for status updates + await new Promise(resolve => setTimeout(resolve, 5000)); + const status = await checkPaymentStatus(session.sessionToken); + console.log("Intent status:", status); } else {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mintlify-docs/build/consumer-app-sdk/payment.mdx` around lines 504 - 507, The example currently uses setTimeout(async () => { const status = await checkPaymentStatus(session.sessionToken); ... }, 5000) which can produce unhandled promise rejections; replace this pattern with a dedicated async polling function (e.g., pollPaymentStatus) that contains a try/catch around await checkPaymentStatus(session.sessionToken), an explicit delay/sleep between attempts, and a termination condition to stop polling when a terminal status is reached; alternatively, call an async IIFE from setTimeout and handle errors with .catch or try/catch instead of passing an async callback directly to setTimeout.
526-535: Add proper type narrowing for error handling.The error handling example accesses
error.messagewithout type checking, which could fail at runtime iferroris not anErrorinstance.Improved error handling example
} catch (error) { - if (error.message.includes("API key")) { + const message = error instanceof Error ? error.message : String(error); + if (message.includes("API key")) { console.log("Check your Chainrails API key"); - } else if (error.message.includes("network")) { + } else if (message.includes("network")) { console.log("Network error, try again"); } else { - console.log("Unknown error:", error.message); + console.log("Unknown error:", message); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@mintlify-docs/build/consumer-app-sdk/payment.mdx` around lines 526 - 535, The catch block currently assumes the caught value is an Error and directly accesses error.message; update the error handling to properly narrow the type by checking `error instanceof Error` (or using `if (typeof error === "object" && error !== null && "message" in error)`) before reading `error.message`, and fall back to a safe string (e.g., String(error)) for logging; update the branches that inspect `error.message` (the existing checks for "API key" and "network" and the final "Unknown error" log) to use the narrowed message variable so runtime exceptions from non-Error throws are avoided.examples/chainrails-nextjs-example/src/app/api/create-session/route.ts (1)
11-11: Empty string fallback for API key may cause silent failures.Using
|| ""whenCHAINRAILS_API_KEYis undefined will pass an empty string to the SDK, which may fail later with a confusing error. Consider failing fast with a clear message.🛡️ Proposed fix for explicit API key validation
+ const apiKey = process.env.CHAINRAILS_API_KEY; + if (!apiKey) { + console.error("CHAINRAILS_API_KEY environment variable is not set"); + return NextResponse.json( + { error: "Payment service not configured" }, + { status: 503 } + ); + } + const sdk = new StarkZap({ network: "mainnet", payment: { - apiKey: process.env.CHAINRAILS_API_KEY || "", + apiKey, environment: "production", }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/app/api/create-session/route.ts` at line 11, The code currently falls back to an empty string for apiKey (apiKey: process.env.CHAINRAILS_API_KEY || ""), which can cause silent failures; instead read process.env.CHAINRAILS_API_KEY into a variable, validate it at startup or at the top of the create-session route handler (e.g., in the module that builds the SDK config), and throw or return a clear error if it's missing/empty (e.g., "Missing CHAINRAILS_API_KEY environment variable"); then pass the validated variable (not an empty string) into the SDK config.examples/chainrails-nextjs-example/src/app/page.tsx (1)
12-15: SDK instantiated on every render.
StarkZapis created inside the component body, causing a new instance on each render. Move initialization outside the component or wrap inuseMemo.♻️ Proposed fix using useMemo
export default function Home() { const [loading, setLoading] = useState(false); - // Initialize SDK and payment modal - const sdk = new StarkZap({ - network: "mainnet", - }); - const payment = sdk.payment(); + + const payment = useMemo(() => { + const sdk = new StarkZap({ network: "mainnet" }); + return sdk.payment(); + }, []);Also add the import:
-import { useState } from "react"; +import { useState, useMemo } from "react";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/app/page.tsx` around lines 12 - 15, The component currently instantiates a new StarkZap on every render (see StarkZap and sdk.payment()), so move the SDK creation out of the render path: either create the StarkZap instance at module scope (top-level) or memoize it inside the component with React's useMemo and import useMemo from 'react'; then call sdk.payment() from that stable instance. Ensure you update the import list to include useMemo if you choose the memo approach and reference the same StarkZap variable used to derive payment().tests/payment.test.ts (1)
1-503: Consider adding error/failure test cases.The test suite thoroughly covers happy paths but lacks tests for error scenarios (e.g., API failures, invalid inputs, network errors). Adding a few negative test cases would improve resilience verification.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/payment.test.ts` around lines 1 - 503, Add negative test cases to cover API failures and invalid inputs by mocking crapi methods to reject or return error payloads and asserting Payment methods and StarkZap.payment respond correctly; for example, add tests that spyOn crapi.auth.getSessionToken to reject and assert payment.createSession throws or returns a handled error, spyOn crapi.quotes.getAll/getBestAcrossBridges to reject and assert payment.getAllQuotes/getBestQuote propagate or handle errors, spyOn crapi.intents.create/getById to reject and assert payment.createIntent/getIntent handle failures, test getSupportedChains when crapi.chains.getSupported rejects, and a StarkZap.payment negative case for browser vs non-browser by stubbing globals and asserting thrown vs not thrown; locate these tests near existing describes for createSession, getAllQuotes, createIntent, getIntent, getSupportedChains, and the "StarkZap.payment()" describe and add expectations for thrown errors or error return values.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.husky/pre-commit:
- Around line 1-7: Add a bash shebang as the very first line so the hook always
runs under a defined shell (use /usr/bin/env to locate bash) before the existing
export NVM_DIR line, and ensure the .husky/pre-commit hook file is executable so
the subsequent export NVM_DIR and npm run typecheck / lint / prettier:check
commands run reliably.
In `@examples/chainrails-nextjs-example/README.md`:
- Around line 70-74: Update the README entry for "Starkzap SDK Reference" to
remove or correct the broken URL; locate the line containing the "Starkzap SDK
Reference" link in README.md and either delete that bullet or replace
"https://docs.starkzap.com" with the correct, accessible URL (or a note that the
documentation is unavailable), leaving the "Chainrails SDK Reference" and
"Chainrails API Reference" links unchanged.
In `@examples/chainrails-nextjs-example/src/app/api/create-session/route.ts`:
- Line 6: Validate the incoming JSON fields before calling createSession: check
that amount is present and a positive number (or numeric string convertible to a
number) and that recipient is present and a non-empty string (optionally
validate address format), and return a 400/Bad Request response if validation
fails; update the handler that destructures { destinationChain, token, amount,
recipient } to perform these checks and only call createSession when valid,
referencing the request handler in route.ts and the createSession invocation.
In `@examples/chainrails-nextjs-example/src/app/page.tsx`:
- Around line 174-182: The radio input with id "credit-card" is using a
controlled "checked" prop without any state or onChange handler; replace the
static checked attribute with defaultChecked for a static demo or wire it to
state (create a state variable and onChange handler for the "payment-method"
group) so React doesn't emit uncontrolled-to-controlled warnings—look for the
input element with id="credit-card" and name="payment-method" in the JSX and
either change checked to defaultChecked or implement state management in the
surrounding component (and update the className/props accordingly).
- Around line 125-127: The JSX uses a deprecated selected attribute on the
<option value="Ikeja">; remove the selected attribute and set the initial
selection on the parent <select> instead (use defaultValue="Ikeja" for
uncontrolled selects or manage value/onChange for a controlled select). Locate
the <option value="Ikeja" selected> instance and update its parent <select> to
use defaultValue="Ikeja" (or implement value/onChange) and delete the selected
attribute from the option.
- Around line 19-40: Wrap the fetch + session extraction and the
payment.modal(...).pay() call in a try/catch/finally: check response.ok after
fetch and throw or handle the error if not OK, validate that data.sessionToken
exists before calling payment.modal({ sessionToken: data.sessionToken, ...
}).pay(), and move setLoading(false) into finally so it always runs; reference
the fetch call to `${process.env.NEXT_PUBLIC_API_URL}/api/create-session`, the
extracted data.sessionToken, and the payment.modal(...).pay() invocation when
applying the changes.
In `@examples/chainrails-nextjs-example/src/icons/Tel.tsx`:
- Around line 12-18: The TelIcon component currently renders the check-circle
glyph (same path as CheckIcon); update TelIcon to use a telephone/phone SVG path
instead (or if the checkmark was intentional, rename the component/file to
CheckIcon). Locate the TelIcon component in the file and replace the <path> that
currently contains d="M8.5 11.5 11 14l4-4m6 2a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
with an appropriate phone/telephone path (or rename the component and its
exports to CheckIcon to match the existing path).
In `@examples/flappy-bird/index.html`:
- Around line 80-83: Replace the interactive <div> elements with semantic
<button> elements for the "save-me" and "replay" controls (keep the ids
"save-me" and "replay") so they are keyboard and assistive-tech accessible;
ensure each button has type="button" and an appropriate accessible name (use the
existing img alt or add an aria-label or visually hidden text) and preserve the
inner <img> markup and any event handlers/CSS selectors that reference those
ids.
In `@examples/flappy-bird/main.ts`:
- Around line 42-45: The payload.amount coming from response.json() is untyped;
before using it (e.g., when constructing modal input) coerce and validate it to
the expected string form: check payload.amount exists, convert it to a string
(e.g., String(payload.amount)) or use a safe default (e.g., '' or '0') if
null/undefined, and if you expect a numeric string optionally validate with
Number.isFinite(Number(payload.amount)) then format accordingly; apply this same
coercion/validation wherever payload.amount is read (the occurrences around the
sessionToken/amount assignment and the later block at lines 58-62) so the modal
always receives a properly typed string.
- Around line 31-40: The fetch in openSaveMeCheckout currently has no
timeout/abort; wrap the fetch call with an AbortController, pass
controller.signal to fetch, start a setTimeout that calls controller.abort()
after a reasonable timeout (e.g., 8–15s), clear the timeout after a successful
response, and catch the AbortError to throw or return a user-friendly timeout
error so the UI can unlock; apply the same pattern to the other checkout fetches
mentioned (the blocks around lines 90–96 and 121–123) to ensure all checkout
requests can be cancelled and cleaned up.
In `@examples/flappy-bird/public/css/main.css`:
- Line 145: There are stray blank lines immediately before CSS declarations that
trigger stylelint's declaration-empty-line-before rule; remove the empty line(s)
directly above the affected declarations (e.g., the -webkit-animation and
corresponding animation declarations) so that each property appears immediately
after the previous line within its rule, then re-run stylelint to verify the
errors are resolved.
In `@examples/flappy-bird/public/js/main.js`:
- Around line 370-375: The spacebar handler incorrectly blocks replay when a
"Save Me" option is available; change the early-return condition in the
ScoreScreen branch so it only returns when saveMeInProgress is true (not when
canUseSaveMe is true) so pressing space still triggers $("#replay").click() even
if canUseSaveMe is true; locate the block around currentstate ==
states.ScoreScreen and update the conditional that currently checks
saveMeInProgress || canUseSaveMe to only check saveMeInProgress.
In `@src/payment/modal.ts`:
- Around line 56-71: The closeWith path currently calls cleanup() before
settle(), allowing re-entrant callbacks to run without the settled guard; change
the order so the settled guard is set and resolve(value) is invoked before
performing cleanup. In practice, update closeWith (or adjust settle) so it calls
settle(value) first (which sets settled=true and resolve), then calls cleanup(),
keeping the existing settle, cleanup, and settled semantics intact.
- Around line 14-15: The modal code is forcing amount to the string "0" when
omitted (e.g., the object property amount: input.amount || "0"), which breaks
session-scoped payments; update the construction in src/payment/modal.ts to stop
defaulting to "0" — either assign amount: input.amount ?? undefined or only
include the amount property when input.amount is present (remove the || "0"
fallback) in both places where amount is set (the object near amount:
input.amount || "0" and the similar block around lines 77-80).
In `@src/sdk.ts`:
- Around line 515-525: The browser fallback in payment() silently constructs new
Payment({ apiKey: "" }) when this.config.payment is missing, causing confusing
runtime errors; update payment() (and its JSDoc for StarkZap if you prefer docs)
to instead throw a clear configuration error consistently by removing the
empty-apiKey fallback branch (i.e., stop creating Payment when isWebRuntime() is
true) and throw the same Error when !this.config.payment, referencing the
Payment class, the payment() method, this.config.payment, and isWebRuntime() so
reviewers can locate the change.
In `@tests/payment.test.ts`:
- Around line 304-321: The mocked response for crapi.intents.getAll uses total
as a string; change it to a number so the test matches the real API shape:
update the output object in the "listIntents" test (where MOCK_INTENT and
getAllSpy are defined and payment.listIntents is called) to use total: 1 (remove
the "1" as const) so getAllSpy.mockResolvedValue(output) returns total as a
number.
---
Outside diff comments:
In `@examples/flappy-bird/public/js/main.js`:
- Around line 546-553: The medal animation is accidentally controlled by the
preceding if (canUseSaveMe) because there are no braces; fix by properly scoping
the save-me conditional (wrap the intended save-me statements in braces or
terminate it so it’s a single statement) so the wonmedal block (the wonmedal
check and the $("#medal").css / $("#medal").transition calls) executes
independently; locate the if (canUseSaveMe), the wonmedal condition, and the
$("#medal") calls and adjust braces/statement boundaries accordingly.
---
Nitpick comments:
In `@examples/chainrails-nextjs-example/package.json`:
- Around line 11-19: Move the build-time packages "@tailwindcss/postcss" and
"postcss" out of the "dependencies" section in package.json and add them to
"devDependencies" with the same version strings (e.g., "^4.1.18" and "^8.5.6")
so they are installed only for development/build. Update the package.json
entries for "dependencies" and "devDependencies" accordingly and run your
package manager to refresh the lockfile so the change is reflected in the
project metadata.
In `@examples/chainrails-nextjs-example/src/app/api/create-session/route.ts`:
- Line 11: The code currently falls back to an empty string for apiKey (apiKey:
process.env.CHAINRAILS_API_KEY || ""), which can cause silent failures; instead
read process.env.CHAINRAILS_API_KEY into a variable, validate it at startup or
at the top of the create-session route handler (e.g., in the module that builds
the SDK config), and throw or return a clear error if it's missing/empty (e.g.,
"Missing CHAINRAILS_API_KEY environment variable"); then pass the validated
variable (not an empty string) into the SDK config.
In `@examples/chainrails-nextjs-example/src/app/globals.css`:
- Around line 27-35: Remove the stray empty line before the declarations in the
CSS rule that contains color-scheme, color, background-color, etc., and change
the value "optimizeLegibility" in the text-rendering declaration to lowercase
"optimizelegibility" to satisfy Stylelint's casing and blank-line rules; update
the block containing the properties (color-scheme, color, background-color,
font-synthesis, text-rendering, -webkit-font-smoothing, -moz-osx-font-smoothing)
so properties are consecutive with no extra blank line and the text-rendering
value is lowercase.
In `@examples/chainrails-nextjs-example/src/app/page.tsx`:
- Around line 12-15: The component currently instantiates a new StarkZap on
every render (see StarkZap and sdk.payment()), so move the SDK creation out of
the render path: either create the StarkZap instance at module scope (top-level)
or memoize it inside the component with React's useMemo and import useMemo from
'react'; then call sdk.payment() from that stable instance. Ensure you update
the import list to include useMemo if you choose the memo approach and reference
the same StarkZap variable used to derive payment().
In `@examples/chainrails-nextjs-example/src/icons/NGFlag.tsx`:
- Around line 8-10: The SVG in the NGFlag component currently has conflicting
accessibility attributes (aria-hidden="true" and role="img"); decide whether the
flag is decorative or informative and make them consistent: for decorative icons
keep aria-hidden="true" and remove role="img"; for informative images remove
aria-hidden, keep role="img" and add an accessible label (aria-label or title)
to the SVG or expose a descriptive text prop on NGFlag so screen readers get a
name. Locate the SVG element inside the NGFlag component and apply the
corresponding change.
- Around line 5-6: The SVG in the NGFlag component uses oversized intrinsic
width/height ("width" and "height" attributes set to "800px") that conflict with
its rendered classes (className "h-4 w-4"); update the SVG by removing the
explicit width/height attributes or set them to 24 (e.g., width="24"
height="24") so the intrinsic size matches the rendered size, keeping the
existing viewBox and paths intact; locate the SVG element in the NGFlag
component and adjust the width/height attributes accordingly to normalize
sizing.
In `@examples/server/server.ts`:
- Around line 214-221: The demo call to starkzap.payment().createSession
hardcodes amount and recipient (amount: "0.1", recipient: "0x007559...5205c0",
destinationChain: PaymentChains.STARKNET, token: PaymentTokenSymbols.USDC);
update the code by adding a clear inline comment above this createSession call
stating these values are hardcoded for demo only and must be
parameterized/validated in production (e.g., accept from request body, validate
address and amount) so reviewers know to replace createSession's hardcoded
recipient and amount in real deployments.
- Around line 206-228: Rename the route path from "/session-token" to
"/api/session-token" for consistency and add the same authentication middleware
used by the wallet routes to the app.get handler (e.g.,
app.get("/api/session-token", authMiddleware, async (...) => { ... })); if you
intentionally want this endpoint to be public for the demo, instead add an
explicit comment above the handler stating it is demo-only and must be protected
in production and/or require CHAINRAILS_API_KEY, referencing the
starkzap.payment().createSession call and PaymentChains.STARKNET /
PaymentTokenSymbols.USDC usage so reviewers know which endpoint to secure.
In `@mintlify-docs/build/consumer-app-sdk/payment.mdx`:
- Around line 504-507: The example currently uses setTimeout(async () => { const
status = await checkPaymentStatus(session.sessionToken); ... }, 5000) which can
produce unhandled promise rejections; replace this pattern with a dedicated
async polling function (e.g., pollPaymentStatus) that contains a try/catch
around await checkPaymentStatus(session.sessionToken), an explicit delay/sleep
between attempts, and a termination condition to stop polling when a terminal
status is reached; alternatively, call an async IIFE from setTimeout and handle
errors with .catch or try/catch instead of passing an async callback directly to
setTimeout.
- Around line 526-535: The catch block currently assumes the caught value is an
Error and directly accesses error.message; update the error handling to properly
narrow the type by checking `error instanceof Error` (or using `if (typeof error
=== "object" && error !== null && "message" in error)`) before reading
`error.message`, and fall back to a safe string (e.g., String(error)) for
logging; update the branches that inspect `error.message` (the existing checks
for "API key" and "network" and the final "Unknown error" log) to use the
narrowed message variable so runtime exceptions from non-Error throws are
avoided.
In `@tests/payment.test.ts`:
- Around line 1-503: Add negative test cases to cover API failures and invalid
inputs by mocking crapi methods to reject or return error payloads and asserting
Payment methods and StarkZap.payment respond correctly; for example, add tests
that spyOn crapi.auth.getSessionToken to reject and assert payment.createSession
throws or returns a handled error, spyOn
crapi.quotes.getAll/getBestAcrossBridges to reject and assert
payment.getAllQuotes/getBestQuote propagate or handle errors, spyOn
crapi.intents.create/getById to reject and assert payment.createIntent/getIntent
handle failures, test getSupportedChains when crapi.chains.getSupported rejects,
and a StarkZap.payment negative case for browser vs non-browser by stubbing
globals and asserting thrown vs not thrown; locate these tests near existing
describes for createSession, getAllQuotes, createIntent, getIntent,
getSupportedChains, and the "StarkZap.payment()" describe and add expectations
for thrown errors or error return values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 18d149db-6fe4-4c0a-815f-c084c476676f
⛔ Files ignored due to path filters (3)
examples/chainrails-nextjs-example/package-lock.jsonis excluded by!**/package-lock.jsonexamples/flappy-bird/public/assets/saveme.pngis excluded by!**/*.pngpackage-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (41)
.husky/pre-commit.prettierignoreREADME.mdeslint.config.mjsexamples/chainrails-nextjs-example/.env.exampleexamples/chainrails-nextjs-example/.gitignoreexamples/chainrails-nextjs-example/README.mdexamples/chainrails-nextjs-example/next.config.tsexamples/chainrails-nextjs-example/package.jsonexamples/chainrails-nextjs-example/postcss.config.mjsexamples/chainrails-nextjs-example/src/app/api/create-session/route.tsexamples/chainrails-nextjs-example/src/app/globals.cssexamples/chainrails-nextjs-example/src/app/layout.tsxexamples/chainrails-nextjs-example/src/app/page.tsxexamples/chainrails-nextjs-example/src/icons/ArrowDown.tsxexamples/chainrails-nextjs-example/src/icons/Check.tsxexamples/chainrails-nextjs-example/src/icons/NGFlag.tsxexamples/chainrails-nextjs-example/src/icons/Tel.tsxexamples/chainrails-nextjs-example/tsconfig.jsonexamples/chainrails-nextjs-example/tsconfig.node.jsonexamples/flappy-bird/README.mdexamples/flappy-bird/index.htmlexamples/flappy-bird/main.tsexamples/flappy-bird/public/css/main.cssexamples/flappy-bird/public/js/main.jsexamples/server/.env.exampleexamples/server/package.jsonexamples/server/server.tsmintlify-docs/build/consumer-app-sdk/configuration.mdxmintlify-docs/build/consumer-app-sdk/payment.mdxmintlify-docs/mint.jsonpackage.jsonsrc/index.tssrc/payment/index.tssrc/payment/modal.tssrc/payment/payment.tssrc/payment/types.tssrc/sdk.tssrc/types/config.tssrc/types/payment-modules.d.tstests/payment.test.ts
| amount: input.amount || "0", | ||
| }; |
There was a problem hiding this comment.
Do not force modal amount to "0" when omitted.
For session-scoped payments, amount can be intentionally absent. Defaulting to "0" risks incorrect display/processing.
Proposed fix
const handle: PaymentModalHandle = {
platform,
sessionToken: input.sessionToken,
pay: () => this.pay({ ...input, platform }),
- amount: input.amount || "0",
+ ...(input.amount !== undefined ? { amount: input.amount } : {}),
};
@@
modal.setProps?.({
sessionToken: input.sessionToken,
- amount: input.amount || "0",
+ ...(input.amount !== undefined ? { amount: input.amount } : {}),
isOpen: true,
isPending: false,
onCancel: () => closeWith(false),
onSuccess: () => closeWith(true),
});Also applies to: 77-80
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/payment/modal.ts` around lines 14 - 15, The modal code is forcing amount
to the string "0" when omitted (e.g., the object property amount: input.amount
|| "0"), which breaks session-scoped payments; update the construction in
src/payment/modal.ts to stop defaulting to "0" — either assign amount:
input.amount ?? undefined or only include the amount property when input.amount
is present (remove the || "0" fallback) in both places where amount is set (the
object near amount: input.amount || "0" and the similar block around lines
77-80).
| payment(): Payment { | ||
| if (!this.config.payment) { | ||
| if (isWebRuntime()) { | ||
| return new Payment({ apiKey: "" }); | ||
| } | ||
| throw new Error( | ||
| "Payment is not configured. Provide a `payment` config with an `apiKey` when creating StarkZap." | ||
| ); | ||
| } | ||
| return new Payment(this.config.payment); | ||
| } |
There was a problem hiding this comment.
Browser fallback with empty apiKey may cause confusing runtime errors.
The JSDoc states "Requires payment.apiKey to be set" but the implementation silently creates a Payment instance with an empty apiKey when running in a browser without configuration. This can lead to confusing Chainrails API errors at call time instead of a clear configuration error upfront.
Consider either:
- Throwing consistently in all runtimes when
paymentconfig is missing, or - Documenting the browser fallback behavior explicitly in the JSDoc
Option 1: Consistent error throwing
payment(): Payment {
if (!this.config.payment) {
- if (isWebRuntime()) {
- return new Payment({ apiKey: "" });
- }
throw new Error(
"Payment is not configured. Provide a `payment` config with an `apiKey` when creating StarkZap."
);
}
return new Payment(this.config.payment);
}📝 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.
| payment(): Payment { | |
| if (!this.config.payment) { | |
| if (isWebRuntime()) { | |
| return new Payment({ apiKey: "" }); | |
| } | |
| throw new Error( | |
| "Payment is not configured. Provide a `payment` config with an `apiKey` when creating StarkZap." | |
| ); | |
| } | |
| return new Payment(this.config.payment); | |
| } | |
| payment(): Payment { | |
| if (!this.config.payment) { | |
| throw new Error( | |
| "Payment is not configured. Provide a `payment` config with an `apiKey` when creating StarkZap." | |
| ); | |
| } | |
| return new Payment(this.config.payment); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/sdk.ts` around lines 515 - 525, The browser fallback in payment()
silently constructs new Payment({ apiKey: "" }) when this.config.payment is
missing, causing confusing runtime errors; update payment() (and its JSDoc for
StarkZap if you prefer docs) to instead throw a clear configuration error
consistently by removing the empty-apiKey fallback branch (i.e., stop creating
Payment when isWebRuntime() is true) and throw the same Error when
!this.config.payment, referencing the Payment class, the payment() method,
this.config.payment, and isWebRuntime() so reviewers can locate the change.
| describe("listIntents", () => { | ||
| it("lists with pagination", async () => { | ||
| const output = { | ||
| intents: [MOCK_INTENT], | ||
| total: "1" as const, | ||
| limit: 10, | ||
| offset: 0, | ||
| }; | ||
| const getAllSpy = vi | ||
| .spyOn(crapi.intents, "getAll") | ||
| .mockResolvedValue(output as never); | ||
|
|
||
| const result = await payment.listIntents({ limit: 10, offset: 0 }); | ||
|
|
||
| expect(getAllSpy).toHaveBeenCalledWith({ limit: 10, offset: 0 }); | ||
| expect(result.intents).toHaveLength(1); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find relevant TypeScript files that might contain type definitions
fd -e ts -e tsx | head -20Repository: keep-starknet-strange/starkzap
Length of output: 950
🏁 Script executed:
# Search for listIntents method definition and related types
rg "listIntents" -A 5 -B 2Repository: keep-starknet-strange/starkzap
Length of output: 1815
🏁 Script executed:
# Search for interface definitions related to intents or pagination
rg "interface.*Intent" -A 10Repository: keep-starknet-strange/starkzap
Length of output: 1653
🏁 Script executed:
# Search for the ListPaymentIntentsOutput type definition
rg "ListPaymentIntentsOutput" -A 10 -B 2Repository: keep-starknet-strange/starkzap
Length of output: 2012
🏁 Script executed:
# Also search for ListPaymentIntentsInput to understand the interface
rg "ListPaymentIntentsInput" -A 10 -B 2Repository: keep-starknet-strange/starkzap
Length of output: 2018
🏁 Script executed:
# Check the payment types file
fd payment -type f | grep -E "\.(ts|d\.ts)$" | grep -v test | grep -v node_modulesRepository: keep-starknet-strange/starkzap
Length of output: 244
🏁 Script executed:
# Search for AsyncResult type definition
rg "type AsyncResult" -A 5Repository: keep-starknet-strange/starkzap
Length of output: 340
🏁 Script executed:
# Search for crapi object and intents.getAll definition
rg "crapi.*intents" -B 5 -A 10 | head -100Repository: keep-starknet-strange/starkzap
Length of output: 5198
🏁 Script executed:
# Look for where crapi is imported or defined
rg "crapi" src/payment/ -lRepository: keep-starknet-strange/starkzap
Length of output: 117
🏁 Script executed:
# Search for crapi import in payment files
rg "import.*crapi" src/payment/Repository: keep-starknet-strange/starkzap
Length of output: 211
🏁 Script executed:
# Also check if crapi is defined in a types file or config
rg "crapi" --type ts -B 2 -A 2 | grep -E "(export|declare|from|import)" | head -30Repository: keep-starknet-strange/starkzap
Length of output: 2300
🏁 Script executed:
# Search for node_modules chainrails SDK type definitions
find node_modules/@chainrails -name "*.d.ts" 2>/dev/null | head -20Repository: keep-starknet-strange/starkzap
Length of output: 56
🏁 Script executed:
# Look for package.json to see what version is being used
rg "chainrails" package.jsonRepository: keep-starknet-strange/starkzap
Length of output: 143
🏁 Script executed:
# Search for any documentation or comments about the intent list response
rg "intents.*getAll" -A 20 -B 5Repository: keep-starknet-strange/starkzap
Length of output: 4213
🏁 Script executed:
# Search for pagination or total field definitions in the codebase
rg "total.*:" -B 2 -A 2 | grep -E "(interface|type|total)" | head -40Repository: keep-starknet-strange/starkzap
Length of output: 2707
🌐 Web query:
Chainrails SDK @chainrails/sdk intents.getAll response type total field
💡 Result:
crapi.intents.getAll(...) returns a paginated list object where the total field is a number (the total count of intents matching the query across all pages, not just the length of the current data/items array). This isn’t explicitly documented in the public SDK/API reference pages, but the SDK ships “full TypeScript type definitions,” so the authoritative answer in your installed version is to inspect the exported response type in node_modules/@chainrails/sdk (or via your IDE hover). [1]
Source(s):
[1] Chainrails SDK Reference – Introduction (mentions full TypeScript type definitions) https://docs.chainrails.io/sdk-reference/introduction
🏁 Script executed:
# Search for other pagination examples in the codebase
rg "limit.*offset" -B 5 -A 5 | head -100Repository: keep-starknet-strange/starkzap
Length of output: 729
🏁 Script executed:
# Check if there are any type hints or JSDoc comments about intents response
rg "@chainrails" -A 20 -B 5 | head -150Repository: keep-starknet-strange/starkzap
Length of output: 6946
Fix mock total field type: should be a number, not a string.
The crapi.intents.getAll() API returns total as a number representing the total count of intents across all pages. Change total: "1" as const to total: 1.
const output = {
intents: [MOCK_INTENT],
- total: "1" as const,
+ total: 1,
limit: 10,
offset: 0,
};🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/payment.test.ts` around lines 304 - 321, The mocked response for
crapi.intents.getAll uses total as a string; change it to a number so the test
matches the real API shape: update the output object in the "listIntents" test
(where MOCK_INTENT and getAllSpy are defined and payment.listIntents is called)
to use total: 1 (remove the "1" as const) so getAllSpy.mockResolvedValue(output)
returns total as a number.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…route.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
- wrap fetch call in try-catch block for robustness - check response status before parsing JSON - validate presence of sessionToken in response data - log errors to console for debugging
…rove UI - Introduce paymentMethod state to manage selected payment option - Add defaultValue to city select dropdown for better UX - Synchronize radio button checked states with paymentMethod - Update Tel icon SVG with a new path for better appearance
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
examples/chainrails-nextjs-example/README.md (2)
32-45: Add language specifier to project structure code block.The fenced code block displaying the directory structure is missing a language identifier, which is flagged by markdownlint.
📝 Suggested improvement
-``` +```plaintext src/ ├── app/ │ ├── api/🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/README.md` around lines 32 - 45, Update the fenced code block in README.md that displays the project directory tree (the block that starts with ``` and contains "src/" and its subfolders) to include a language specifier such as plaintext (i.e., change the opening fence from ``` to ```plaintext) so markdownlint recognizes it as a formatted code block.
17-20: Add language specifier to environment variables code block.The fenced code block is missing a language identifier. Adding one improves rendering and follows markdown best practices.
📝 Suggested improvement
-``` +```bash NEXT_PUBLIC_API_URL=http://localhost:3000 NEXT_PUBLIC_CHAINRAILS_API_KEY=your_api_key_hereAdditionally, consider adding a note about where to obtain the Chainrails API key (e.g., link to Chainrails dashboard or registration page) to help new users get started more easily. </details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In
@examples/chainrails-nextjs-example/README.mdaround lines 17 - 20, Update
the fenced environment variables block to include a language specifier (e.g.,
"bash") so the snippet with NEXT_PUBLIC_API_URL and
NEXT_PUBLIC_CHAINRAILS_API_KEY renders correctly, and add a short note after the
block indicating where to obtain the Chainrails API key (for example, a link or
reference to the Chainrails dashboard/registration page) so users know how to
get the value for NEXT_PUBLIC_CHAINRAILS_API_KEY.</details> </blockquote></details> </blockquote></details> <details> <summary>🤖 Prompt for all review comments with AI agents</summary>Verify each finding against the current code and only fix it if needed.
Inline comments:
In@examples/chainrails-nextjs-example/src/app/page.tsx:
- Around line 149-153: The label's htmlFor ("phone-input-3") does not match the
input's id ("phone-input"), breaking accessibility; update one so they match
(either change the label htmlFor to "phone-input" or rename the input id to
"phone-input-3") in the component where the Phone Number label and input are
defined (ensure the unique attributes htmlFor on the and id on the
corresponding are identical).- Around line 18-59: The pay() function unconditionally opens the Chainrails
crypto modal; update it to read the selected payment method state (e.g.,
selectedPaymentMethod or paymentMethod) and branch: only call
payment.modal(...).pay() when the selected method is the crypto option
(PaymentChains.STARKNET / token flow), otherwise route to the appropriate
handlers for "Payment on delivery" or "Paypal" (e.g., call your COD handler or
redirect to PayPal) and skip the crypto modal; modify pay() and any invocation
sites that assume crypto so the modal is only used for the crypto payment path.- Around line 21-23: The fetch URL is built directly from NEXT_PUBLIC_API_URL
which can be undefined; update the fetch in page.tsx (the call that posts to
"/api/create-session") to guard the env var and fall back to a same-origin path:
compute a base URL from NEXT_PUBLIC_API_URL and if it's falsy use the
same-origin relative path so the final URL is either
${NEXT_PUBLIC_API_URL}/api/create-sessionor/api/create-session, then pass
that URL into the existing fetch call.- Around line 251-264: The PayPal radio input with id "paypal" is missing its
value binding and state handlers and the label's htmlFor is incorrectly
"paypal-2"; update the input to have value="paypal" and wire it to the same
state as the other radios (use the paymentMethod state for checked and the
setPaymentMethod onChange handler used by the other payment inputs), and change
the label's htmlFor from "paypal-2" to "paypal" so the label correctly targets
the input.
Nitpick comments:
In@examples/chainrails-nextjs-example/README.md:
- Around line 32-45: Update the fenced code block in README.md that displays the
project directory tree (the block that starts withand contains "src/" and its subfolders) to include a language specifier such as plaintext (i.e., change the opening fence fromto ```plaintext) so markdownlint recognizes it as a
formatted code block.- Around line 17-20: Update the fenced environment variables block to include a
language specifier (e.g., "bash") so the snippet with NEXT_PUBLIC_API_URL and
NEXT_PUBLIC_CHAINRAILS_API_KEY renders correctly, and add a short note after the
block indicating where to obtain the Chainrails API key (for example, a link or
reference to the Chainrails dashboard/registration page) so users know how to
get the value for NEXT_PUBLIC_CHAINRAILS_API_KEY.</details> --- <details> <summary>ℹ️ Review info</summary> <details> <summary>⚙️ Run configuration</summary> **Configuration used**: defaults **Review profile**: CHILL **Plan**: Pro **Run ID**: `e10debd5-82e0-48f8-8306-7d4e99de1c9e` </details> <details> <summary>📥 Commits</summary> Reviewing files that changed from the base of the PR and between bcba91a6ac6d1571a140d60b1466e99f6438247d and 0d722265cdffea8bbbcc344f06f0684a2e5bfc81. </details> <details> <summary>📒 Files selected for processing (5)</summary> * `examples/chainrails-nextjs-example/README.md` * `examples/chainrails-nextjs-example/src/app/api/create-session/route.ts` * `examples/chainrails-nextjs-example/src/app/page.tsx` * `examples/chainrails-nextjs-example/src/icons/Tel.tsx` * `examples/flappy-bird/public/js/main.js` </details> <details> <summary>🚧 Files skipped from review as they are similar to previous changes (2)</summary> * examples/chainrails-nextjs-example/src/icons/Tel.tsx * examples/chainrails-nextjs-example/src/app/api/create-session/route.ts </details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
| async function pay() { | ||
| setLoading(true); | ||
| try { | ||
| const response = await fetch( | ||
| `${process.env.NEXT_PUBLIC_API_URL}/api/create-session`, | ||
| { | ||
| method: "POST", | ||
| headers: { "Content-Type": "application/json" }, | ||
| body: JSON.stringify({ | ||
| destinationChain: PaymentChains.STARKNET, | ||
| token: PaymentTokenSymbols.USDC, | ||
| recipient: | ||
| "0x0075597a61229d143Ffba493C9f8A8057ecCeeA7BFDDBFD8Aaf79AC8935205c0", | ||
| amount: "0.99", | ||
| }), | ||
| } | ||
| ); | ||
|
|
||
| if (!response.ok) { | ||
| throw new Error(`HTTP error! status: ${response.status}`); | ||
| } | ||
|
|
||
| const data = await response.json(); | ||
|
|
||
| if (!data.sessionToken) { | ||
| throw new Error("Session token not found in response"); | ||
| } | ||
|
|
||
| const paid = await payment | ||
| .modal({ | ||
| sessionToken: data.sessionToken, | ||
| amount: "0.99", | ||
| }) | ||
| .pay(); | ||
|
|
||
| console.log(paid ? "Payment Successful" : "Payment Failed"); | ||
| } catch (error) { | ||
| console.error("Payment failed:", error); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| } |
There was a problem hiding this comment.
Selected payment method is ignored during submission.
Line 309 always triggers pay(), but inside pay() (Line 46) you always execute the Chainrails modal. If the user selects “Payment on delivery” or “Paypal”, the flow still runs crypto checkout.
♻️ Proposed fix
async function pay() {
+ if (paymentMethod !== "credit-card") {
+ console.error(`Payment method "${paymentMethod}" is not implemented yet.`);
+ return;
+ }
+
setLoading(true);
try {Also applies to: 309-315
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/chainrails-nextjs-example/src/app/page.tsx` around lines 18 - 59,
The pay() function unconditionally opens the Chainrails crypto modal; update it
to read the selected payment method state (e.g., selectedPaymentMethod or
paymentMethod) and branch: only call payment.modal(...).pay() when the selected
method is the crypto option (PaymentChains.STARKNET / token flow), otherwise
route to the appropriate handlers for "Payment on delivery" or "Paypal" (e.g.,
call your COD handler or redirect to PayPal) and skip the crypto modal; modify
pay() and any invocation sites that assume crypto so the modal is only used for
the crypto payment path.
| const response = await fetch( | ||
| `${process.env.NEXT_PUBLIC_API_URL}/api/create-session`, | ||
| { |
There was a problem hiding this comment.
Guard NEXT_PUBLIC_API_URL to avoid malformed fetch URLs.
At Line 22, if NEXT_PUBLIC_API_URL is unset, the request becomes undefined/api/create-session and fails at runtime. Use a same-origin fallback.
♻️ Proposed fix
- const response = await fetch(
- `${process.env.NEXT_PUBLIC_API_URL}/api/create-session`,
+ const apiBase = (process.env.NEXT_PUBLIC_API_URL ?? "").replace(/\/$/, "");
+ const response = await fetch(
+ `${apiBase}/api/create-session`,
{📝 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.
| const response = await fetch( | |
| `${process.env.NEXT_PUBLIC_API_URL}/api/create-session`, | |
| { | |
| const apiBase = (process.env.NEXT_PUBLIC_API_URL ?? "").replace(/\/$/, ""); | |
| const response = await fetch( | |
| `${apiBase}/api/create-session`, | |
| { |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/chainrails-nextjs-example/src/app/page.tsx` around lines 21 - 23,
The fetch URL is built directly from NEXT_PUBLIC_API_URL which can be undefined;
update the fetch in page.tsx (the call that posts to "/api/create-session") to
guard the env var and fall back to a same-origin path: compute a base URL from
NEXT_PUBLIC_API_URL and if it's falsy use the same-origin relative path so the
final URL is either `${NEXT_PUBLIC_API_URL}/api/create-session` or
`/api/create-session`, then pass that URL into the existing fetch call.
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@mintlify-docs/build/consumer-app-sdk/payment.mdx`:
- Around line 477-479: The polling call is using session.sessionToken (a session
token) instead of the payment intent identifier, causing
getIntent/checkPaymentStatus to fail; update the call in the polling block to
pass the real intent ID (for example session.intentId or session.intent.id
depending on the session shape) into checkPaymentStatus/getIntent so the
functions receive the actual intent identifier rather than session.sessionToken,
and verify the session object property name in the code that creates the session
before changing the call.
- Around line 222-230: The example for getQuotesFromAllBridges includes an
unsupported field excludeBridges; remove that key from the input object passed
to getQuotesFromAllBridges so the call matches the documented SDK input type.
Locate the call to payment.getQuotesFromAllBridges in the snippet and delete the
excludeBridges: "CCTP" line, keeping sourceChain, destinationChain, tokenIn,
tokenOut, amount, and recipient as shown.
- Line 225: The example currently embeds a real-looking ERC20 address in the
tokenIn field which triggers secret scans; update the docs sample to use a
sanitized placeholder (e.g. a clearly fake address or constant placeholder
string) instead of the real-looking literal—modify the tokenIn value in the
example where the tokenIn property is defined so it reads as a placeholder token
address (e.g. "0x000...000" or "YOUR_TOKEN_ADDRESS") to avoid false positives
from static analysis.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 817da1d0-dda9-4e99-a4d9-a3fac738d40b
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (6)
mintlify-docs/build/consumer-app-sdk/configuration.mdxmintlify-docs/build/consumer-app-sdk/integrations/chainrails.mdxmintlify-docs/build/consumer-app-sdk/overview.mdxmintlify-docs/build/consumer-app-sdk/payment.mdxmintlify-docs/docs.jsonmintlify-docs/mint.json
✅ Files skipped from review due to trivial changes (1)
- mintlify-docs/build/consumer-app-sdk/integrations/chainrails.mdx
| const quotes = await payment.getQuotesFromAllBridges({ | ||
| sourceChain: "ETHEREUM", | ||
| destinationChain: "STARKNET", | ||
| tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC | ||
| tokenOut: "0x053c91253...", // USDC on Starknet | ||
| amount: "1000", | ||
| recipient: "0xrecipient...", | ||
| excludeBridges: "CCTP", // Optional: exclude specific bridges | ||
| }); |
There was a problem hiding this comment.
Remove unsupported excludeBridges from getQuotesFromAllBridges example.
Line 229 uses excludeBridges, but the documented SDK input type for this method does not include that field. This example will mislead users into a type/runtime mismatch.
Suggested doc fix
const quotes = await payment.getQuotesFromAllBridges({
sourceChain: "ETHEREUM",
destinationChain: "STARKNET",
tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
tokenOut: "0x053c91253...", // USDC on Starknet
amount: "1000",
recipient: "0xrecipient...",
- excludeBridges: "CCTP", // Optional: exclude specific bridges
});📝 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.
| const quotes = await payment.getQuotesFromAllBridges({ | |
| sourceChain: "ETHEREUM", | |
| destinationChain: "STARKNET", | |
| tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC | |
| tokenOut: "0x053c91253...", // USDC on Starknet | |
| amount: "1000", | |
| recipient: "0xrecipient...", | |
| excludeBridges: "CCTP", // Optional: exclude specific bridges | |
| }); | |
| const quotes = await payment.getQuotesFromAllBridges({ | |
| sourceChain: "ETHEREUM", | |
| destinationChain: "STARKNET", | |
| tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC | |
| tokenOut: "0x053c91253...", // USDC on Starknet | |
| amount: "1000", | |
| recipient: "0xrecipient...", | |
| }); |
🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 225-225: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mintlify-docs/build/consumer-app-sdk/payment.mdx` around lines 222 - 230, The
example for getQuotesFromAllBridges includes an unsupported field
excludeBridges; remove that key from the input object passed to
getQuotesFromAllBridges so the call matches the documented SDK input type.
Locate the call to payment.getQuotesFromAllBridges in the snippet and delete the
excludeBridges: "CCTP" line, keeping sourceChain, destinationChain, tokenIn,
tokenOut, amount, and recipient as shown.
| const quotes = await payment.getQuotesFromAllBridges({ | ||
| sourceChain: "ETHEREUM", | ||
| destinationChain: "STARKNET", | ||
| tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC |
There was a problem hiding this comment.
Use a placeholder token address to avoid secret-scanner false positives.
Line 225 currently uses a full literal address and is already being flagged by static analysis. Prefer a sanitized placeholder in docs examples.
Suggested doc fix
- tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
+ tokenIn: "0xUSDC_ON_ETHEREUM...", // USDC📝 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.
| tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC | |
| tokenIn: "0xUSDC_ON_ETHEREUM...", // USDC |
🧰 Tools
🪛 Gitleaks (8.30.0)
[high] 225-225: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mintlify-docs/build/consumer-app-sdk/payment.mdx` at line 225, The example
currently embeds a real-looking ERC20 address in the tokenIn field which
triggers secret scans; update the docs sample to use a sanitized placeholder
(e.g. a clearly fake address or constant placeholder string) instead of the
real-looking literal—modify the tokenIn value in the example where the tokenIn
property is defined so it reads as a placeholder token address (e.g.
"0x000...000" or "YOUR_TOKEN_ADDRESS") to avoid false positives from static
analysis.
| setTimeout(async () => { | ||
| const status = await checkPaymentStatus(session.sessionToken); | ||
| console.log("Intent status:", status); |
There was a problem hiding this comment.
Fix status polling to use an actual intent ID, not sessionToken.
Line 478 passes session.sessionToken into getIntent(...). A session token is not an intent identifier, so this flow is incorrect and will fail for consumers who copy/paste it.
Suggested doc fix
- const status = await checkPaymentStatus(session.sessionToken);
+ // Use a real payment intent ID returned by your intent creation flow.
+ const status = await checkPaymentStatus("<intent_id>");
console.log("Intent status:", status);📝 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.
| setTimeout(async () => { | |
| const status = await checkPaymentStatus(session.sessionToken); | |
| console.log("Intent status:", status); | |
| setTimeout(async () => { | |
| // Use a real payment intent ID returned by your intent creation flow. | |
| const status = await checkPaymentStatus("<intent_id>"); | |
| console.log("Intent status:", status); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@mintlify-docs/build/consumer-app-sdk/payment.mdx` around lines 477 - 479, The
polling call is using session.sessionToken (a session token) instead of the
payment intent identifier, causing getIntent/checkPaymentStatus to fail; update
the call in the polling block to pass the real intent ID (for example
session.intentId or session.intent.id depending on the session shape) into
checkPaymentStatus/getIntent so the functions receive the actual intent
identifier rather than session.sessionToken, and verify the session object
property name in the code that creates the session before changing the call.
- update htmlFor attribute to match input id for accessibility - set radio button value to 'paypal' and manage checked state with React - add onChange handler to update paymentMethod state refactor(modal): move cleanup call to closeWith function - ensure cleanup is called when closing modal to prevent leaks fix(sdk): update comment regarding API key requirement - clarify that payment.apiKey is needed in SDK config unless on browser - improve documentation for cross-environment usage
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
examples/chainrails-nextjs-example/src/app/page.tsx (2)
18-59:⚠️ Potential issue | 🟠 MajorHonor the selected payment method before invoking Chainrails.
pay()currently routes every submission through the crypto modal, even when the user selected “Payment on delivery” or “Paypal”.♻️ Proposed fix
async function pay() { + if (paymentMethod !== "credit-card") { + // TODO: Replace with real handlers for each payment path. + console.error(`Payment method "${paymentMethod}" is not implemented yet.`); + return; + } + setLoading(true); try { const response = await fetch(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/app/page.tsx` around lines 18 - 59, The pay function currently always opens Chainrails crypto modal; modify pay() to honor the user's selected payment method (e.g., a state variable like selectedPaymentMethod) by branching before calling payment.modal: if the method is a crypto option (match against PaymentChains/PaymentTokenSymbols or a crypto enum value) then invoke payment.modal({ sessionToken: ..., amount }).pay(), otherwise call the appropriate handler for other methods (e.g., trigger PayPal flow or mark order for Payment on Delivery) and only setLoading/unsetLoading around the chosen flow; ensure you still validate the API response (data.sessionToken) only when needed for the crypto path.
21-23:⚠️ Potential issue | 🟠 MajorAdd a safe fallback when
NEXT_PUBLIC_API_URLis missing.If the env var is unset, the request URL becomes invalid and session creation fails at runtime.
♻️ Proposed fix
- const response = await fetch( - `${process.env.NEXT_PUBLIC_API_URL}/api/create-session`, + const apiBase = (process.env.NEXT_PUBLIC_API_URL ?? "").replace(/\/$/, ""); + const sessionUrl = apiBase ? `${apiBase}/api/create-session` : "/api/create-session"; + const response = await fetch( + sessionUrl, {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/chainrails-nextjs-example/src/app/page.tsx` around lines 21 - 23, The fetch call that builds the request URL using process.env.NEXT_PUBLIC_API_URL can produce an invalid URL if the env var is missing; update the code around the fetch in page.tsx to use a safe fallback (e.g., window.location.origin or a default base URL) when NEXT_PUBLIC_API_URL is undefined or empty, or explicitly throw a clear error before calling fetch; ensure you reference the same endpoint ("/api/create-session") and the fetch invocation so callers still reach the correct route even when the env var is not set.src/payment/modal.ts (2)
56-60:⚠️ Potential issue | 🟠 MajorMake
closeWithidempotent before running cleanup.Line 70 executes
cleanup()even after already settled; concurrentonCancel/onSuccesspaths can trigger repeated cleanup and re-entrant close behavior.Proposed fix
- const settle = (value: boolean): void => { - if (settled) return; + const settle = (value: boolean): boolean => { + if (settled) return false; settled = true; resolve(value); + return true; }; @@ const closeWith = (value: boolean): void => { - settle(value); + if (!settle(value)) return; cleanup(); };Also applies to: 68-71
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/payment/modal.ts` around lines 56 - 60, The closeWith/settle flow is not idempotent: ensure closeWith checks the shared `settled` flag and returns immediately if already settled before calling `cleanup()` or performing any closing logic; keep the existing `settle` implementation (which sets `settled = true` then `resolve(value)`) but invoke it only after the early-return guard in `closeWith`, and apply the same idempotency guard to the other duplicate close path (the lines corresponding to the second close/settle block) so concurrent onCancel/onSuccess handlers cannot run cleanup or re-enter close logic more than once.
14-15:⚠️ Potential issue | 🟠 MajorDo not coerce missing
amountto"0"in modal handle/props.Line 14 and Line 79 still force
"0"whenamountis intentionally omitted. That breaks optional/session-scoped flows.Proposed fix
const handle: PaymentModalHandle = { platform, sessionToken: input.sessionToken, pay: () => this.pay({ ...input, platform }), - amount: input.amount || "0", + ...(input.amount !== undefined ? { amount: input.amount } : {}), }; @@ modal.setProps?.({ sessionToken: input.sessionToken, - amount: input.amount || "0", + ...(input.amount !== undefined ? { amount: input.amount } : {}), isOpen: true, isPending: false, onCancel: () => closeWith(false), onSuccess: () => closeWith(true), });Also applies to: 77-80
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/payment/modal.ts` around lines 14 - 15, The code is forcing a default string "0" for the amount field (amount: input.amount || "0"), which breaks optional/session-scoped flows; change the assignment to preserve an omitted value (e.g., amount: input.amount or amount: input.amount ?? undefined) and remove the hardcoded "0" default in both occurrences (the object being built around amount and the modal props/handle population at lines ~77-80). Also ensure the related type/interface (e.g., PaymentModalProps / the payload type) marks amount as optional so callers can legitimately omit it without runtime coercion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/payment/modal.ts`:
- Around line 65-66: The cleanup function reference being cleared must be the
same as the one set: create a named constant (e.g., const cleanup = () =>
closeWith(false)) and use that same variable when calling the setter and when
calling this.clearActiveModalCleanup(cleanup) so identity matches and
activeModalCleanup can be cleared; update the code around the
activeModalCleanup/setter usage (where () => closeWith(false) is currently
inlined) to use the shared cleanup reference instead.
---
Duplicate comments:
In `@examples/chainrails-nextjs-example/src/app/page.tsx`:
- Around line 18-59: The pay function currently always opens Chainrails crypto
modal; modify pay() to honor the user's selected payment method (e.g., a state
variable like selectedPaymentMethod) by branching before calling payment.modal:
if the method is a crypto option (match against
PaymentChains/PaymentTokenSymbols or a crypto enum value) then invoke
payment.modal({ sessionToken: ..., amount }).pay(), otherwise call the
appropriate handler for other methods (e.g., trigger PayPal flow or mark order
for Payment on Delivery) and only setLoading/unsetLoading around the chosen
flow; ensure you still validate the API response (data.sessionToken) only when
needed for the crypto path.
- Around line 21-23: The fetch call that builds the request URL using
process.env.NEXT_PUBLIC_API_URL can produce an invalid URL if the env var is
missing; update the code around the fetch in page.tsx to use a safe fallback
(e.g., window.location.origin or a default base URL) when NEXT_PUBLIC_API_URL is
undefined or empty, or explicitly throw a clear error before calling fetch;
ensure you reference the same endpoint ("/api/create-session") and the fetch
invocation so callers still reach the correct route even when the env var is not
set.
In `@src/payment/modal.ts`:
- Around line 56-60: The closeWith/settle flow is not idempotent: ensure
closeWith checks the shared `settled` flag and returns immediately if already
settled before calling `cleanup()` or performing any closing logic; keep the
existing `settle` implementation (which sets `settled = true` then
`resolve(value)`) but invoke it only after the early-return guard in
`closeWith`, and apply the same idempotency guard to the other duplicate close
path (the lines corresponding to the second close/settle block) so concurrent
onCancel/onSuccess handlers cannot run cleanup or re-enter close logic more than
once.
- Around line 14-15: The code is forcing a default string "0" for the amount
field (amount: input.amount || "0"), which breaks optional/session-scoped flows;
change the assignment to preserve an omitted value (e.g., amount: input.amount
or amount: input.amount ?? undefined) and remove the hardcoded "0" default in
both occurrences (the object being built around amount and the modal
props/handle population at lines ~77-80). Also ensure the related type/interface
(e.g., PaymentModalProps / the payload type) marks amount as optional so callers
can legitimately omit it without runtime coercion.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ff763716-13a4-4cf0-81f9-f050100d8aa3
📒 Files selected for processing (3)
examples/chainrails-nextjs-example/src/app/page.tsxsrc/payment/modal.tssrc/sdk.ts
| this.clearActiveModalCleanup(cleanup); | ||
| }; |
There was a problem hiding this comment.
Use the same function reference for set/clear active cleanup.
Line 73 stores () => closeWith(false), but Line 65 clears with cleanup; identity never matches at Line 104, so activeModalCleanup can stay stale.
Proposed fix
+ let activeCleanup!: () => void;
+
const cleanup = (): void => {
modal.close();
modal.remove();
- this.clearActiveModalCleanup(cleanup);
+ this.clearActiveModalCleanup(activeCleanup);
};
@@
- this.setActiveModalCleanup(() => closeWith(false));
+ activeCleanup = () => closeWith(false);
+ this.setActiveModalCleanup(activeCleanup);Also applies to: 73-73, 103-105
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/payment/modal.ts` around lines 65 - 66, The cleanup function reference
being cleared must be the same as the one set: create a named constant (e.g.,
const cleanup = () => closeWith(false)) and use that same variable when calling
the setter and when calling this.clearActiveModalCleanup(cleanup) so identity
matches and activeModalCleanup can be cleared; update the code around the
activeModalCleanup/setter usage (where () => closeWith(false) is currently
inlined) to use the shared cleanup reference instead.
…nd modal integration - Introduce server-side session creation with API key protection - Add client-side modal opening instructions with code snippets - Include advanced intent creation example for control - Emphasize security best practices to prevent API key exposure - Update related resource links for comprehensive documentation
Summary
This PR introduces the Payment module powered by ChainRails, enabling cross-chain, multi-token payment acceptance for StarkZap applications.
Features Added
Technical Details
@chainrails/sdk(v0.3.3) and@chainrails/vanilla(v0.0.6)paymentconfig option toSDKConfigwithapiKeyand optionalenvironmentUsage Example
Get Quotes and Create Intent
Payment Modal (Web)
eg. A variant of flappy-bird where the rich live forever
https://github.com/user-attachments/assets/83ea0649-e7aa-4790-9d2a-50a8401ea525
Tests
Added comprehensive test suite with 24 test cases covering:
Breaking Changes
None - Payment module is optional and only activated when payment config is provided.
Related Dependencies
@chainrails/sdk: ^0.3.3
@chainrails/vanilla: ^0.0.6
Summary by CodeRabbit
New Features
Examples
Documentation
Tests
Chores