Skip to content

feat(dashboard): add litkey payment entrypoint#395

Merged
GTC6244 merged 4 commits into
mainfrom
feat/dashboard-litkey-entrypoint
May 29, 2026
Merged

feat(dashboard): add litkey payment entrypoint#395
GTC6244 merged 4 commits into
mainfrom
feat/dashboard-litkey-entrypoint

Conversation

@clawdbot-glitch003
Copy link
Copy Markdown
Collaborator

Summary

  • Add a prominent Pay with LITKEY for 25% off option inside the dashboard Add Funds modal
  • Route users to https://payments.litprotocol.com/payWithLitkey?wallet=… with the account funding wallet prefilled
  • Keep the existing Stripe card flow in the same modal under an or pay with credit card divider
  • Resolve ChainSecured accounts from the connected admin wallet and API-mode accounts from the account master wallet; fail closed instead of falling back to an arbitrary wallet
  • Update the lit-payments plan to mark the dashboard entrypoint as the current phase

Config note

For a 25% LITKEY buyer discount, payments should run with:

LITKEY_DISCOUNT_BASIS_POINTS=2500

Test Plan

  • node --check billing.js
  • node --check app.js
  • git diff --check
  • cargo +1.91 fmt --all -- --check
  • cargo +1.91 test --locked --all-targets -q
  • cargo +1.91 clippy --locked --all-features -- -D warnings

Review notes

  • Ran an adversarial review pass; fixed the important findings by avoiding stale cached wallets, ignoring usage-key overrides for funding, and failing closed unless the API-mode account master wallet is found.

@clawdbot-glitch003 clawdbot-glitch003 requested a review from a team May 26, 2026 23:24
return item && (item.wallet_address || item.address || item.name || '');
}

function pickAccountFundingWallet(wallets) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@GTC6244 is this the right way to pick their master wallet?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@glitch003 - looks like you want the billingWalletAddress, not the master wallet, in this case ? I'm going to Claude trace through to see.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A bit wordy, but here's Claude's confirmation of the flow:

Answer to the question

Yes — the question is appropriate, and the answer is to use the billing wallet. I validated the full flow and fixed the code.

Why it matters (the data flow)

The pay-with-LITKEY page is given a wallet via ?wallet=…, and the payments backend maps it directly to a Stripe customer with no resolution step:

  • preview_customer / find_summary_by_wallet → find_by_wallet does metadata['wallet_address'] == (lit-billing-core/src/customer.rs:46).
  • The on-chain listener credits the same way (chain.rs:695).
    So the ?wallet= value must be the billing wallet — the address the Stripe customer is keyed on. The authoritative source is the contract’s getBillingWalletAddress(apiKeyHash), which is set at account creation and preserved across convert-to-ChainSecured and admin-wallet rotation (CPL-313/CPL-324). That’s exactly what the server uses internally (accounts::get_billing_wallet_address).

The bug the question caught

The old resolveLitkeyPaymentWallet returned getChainSecuredWallet() for ChainSecured sessions — the current admin/owner wallet. After a conversion or ownership transfer, that wallet diverges from the billing wallet, so the payment page would find no customer and credits would be stranded. The API-key branch picked the “Account Master Wallet” — coincidentally correct (managed accounts never rotate, so getBillingWalletAddress falls back to the admin wallet = AMW), but the wrong concept.

Changes

  1. lit-static/account_config_view_abi.js — added the getBillingWalletAddress view ABI entry.
  2. lit-static/core_sdk.js — added getBillingWalletAddress({ apiKey }), reading the on-chain view in sovereign mode (mirrors the server). No signer/popup needed — it uses the read provider.
  3. lit-static/dapps/dashboard/billing.js — resolveLitkeyPaymentWallet now:
  • Sovereign/ChainSecured: reads the billing wallet on-chain via client.getBillingWalletAddress(...) instead of the connected wallet.
  • API-key mode: keeps the AMW path (provably == billing wallet for managed accounts) with a comment explaining the invariant.
  • Error messages now say “billing wallet”.
  1. plans/lit-payments-app.md — wording updated to “billing wallet”.
    All three JS files pass node --check. Note: no changes to Rust/Rocket routes, so the k6 client-check gate isn’t triggered.

One thing worth flagging: the API-key branch relies on the invariant that managed accounts never rotate their admin wallet. If a converted (ChainSecured) account is ever accessed via a still-active raw API key, that branch would return the AMW, which may differ from the billing wallet — but in practice such accounts authenticate via wallet (sovereign), which is now handled correctly.

glitch003 and others added 3 commits May 26, 2026 17:05
Use window.open with _blank/noopener so the dashboard isn't navigated
away from when a user starts a LITKEY payment, matching the documented
user flow.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… wallet

The pay-with-LITKEY page maps its ?wallet= param straight to a Stripe
customer via metadata.wallet_address with no resolution, so the prefilled
address must be the account's billing wallet. The billing wallet is set at
account creation and preserved across convert-to-ChainSecured and admin-wallet
rotation (CPL-313/CPL-324), so the connected ChainSecured wallet can diverge
from it — sending the login wallet would strand credits on a customer that
doesn't exist.

Read the authoritative billing wallet on-chain via getBillingWalletAddress in
sovereign mode (mirrors accounts::get_billing_wallet_address). API-key mode
keeps the Account Master Wallet path, which provably equals the billing wallet
for managed accounts (getBillingWalletAddress falls back to the admin wallet).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@GTC6244 GTC6244 merged commit 734e9b0 into main May 29, 2026
14 checks passed
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.

3 participants