Skip to content

fix(invoice): auto-select worker's coinpay wallet when poster creates invoice#482

Closed
russo2100 wants to merge 1 commit into
profullstack:masterfrom
russo2100:fix-invoice-wallet-selection
Closed

fix(invoice): auto-select worker's coinpay wallet when poster creates invoice#482
russo2100 wants to merge 1 commit into
profullstack:masterfrom
russo2100:fix-invoice-wallet-selection

Conversation

@russo2100

Copy link
Copy Markdown

Fixes #478.

When the poster initiates an invoice on behalf of the worker, the frontend no longer forces the poster to select a CoinPay receiving wallet (which they wouldn't have access to). The backend API now automatically falls back to selecting the worker's CoinPay wallet matching the gig's payment coin.

… invoice

Fixes profullstack#478. When the poster initiates an invoice on behalf of the worker, the frontend no longer forces the poster to select a CoinPay receiving wallet (which they wouldn't have access to). The backend API now automatically falls back to selecting the worker's CoinPay wallet matching the gig's payment coin.
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown

Greptile Summary

This PR removes the poster-side wallet selector requirement and shifts auto-selection of the worker's CoinPay wallet to the backend when the poster initiates an invoice. The frontend skips wallet loading/validation entirely for posters, and the API now picks the matching wallet from the worker's global wallet list when none is supplied.

  • Frontend: InvoiceForm conditionally renders the wallet-selector section behind an isWorker flag — but isWorker is never added to the component's props interface or passed at its two call sites, producing a TypeScript compile error that blocks deployment.
  • Backend: Auto-selection prefers the worker's wallet for the gig's payment coin via strict currency === equality, then falls back to the first wallet; this may silently miss rail-suffixed currency keys (e.g. usdc_sol for a USDC gig), causing the invoice to be created with the wrong coin.

Confidence Score: 2/5

Not safe to merge — the frontend change introduces a compile-time error that blocks deployment.

The InvoiceForm component references isWorker in two places but the variable is absent from the component props type and is never passed at either call site. TypeScript will reject this at compile time. Additionally, the backend coin-matching uses strict equality that can silently fall back to the wrong worker wallet.

src/components/gigs/InvoiceButton.tsx — the InvoiceForm props interface and both call sites need isWorker added before this builds.

Important Files Changed

Filename Overview
src/components/gigs/InvoiceButton.tsx Guards the wallet-selector UI and submit-button disable logic behind isWorker, but never adds isWorker to InvoiceForm's props or passes it at the call sites — a TypeScript compile error that blocks deployment.
src/app/api/gigs/[id]/invoice/route.ts Wallet validation moved after worker wallets are fetched; auto-selection logic prefers the gig's coin but uses strict equality that may miss rail-suffixed currency keys (e.g. usdc_sol), silently falling back to the first wallet.

Sequence Diagram

sequenceDiagram
    participant Poster as Poster (Browser)
    participant FE as InvoiceButton.tsx
    participant API as POST /api/gigs/[id]/invoice
    participant CoinPay as CoinPay API

    Poster->>FE: "Opens invoice form (isWorker=false)"
    Note over FE: Wallet selector hidden (isWorker guard)<br/>payment_currency & merchant_wallet_address = undefined

    Poster->>FE: Submits invoice
    FE->>API: "POST {items, amount, currency}"

    API->>API: Validate schema (both fields optional)
    API->>CoinPay: getConnectedCoinpayAccessToken(workerId)
    CoinPay-->>API: worker access token
    API->>CoinPay: getCoinpayGlobalWalletTokens(token)
    CoinPay-->>API: workerWallets[]

    Note over API: payment_currency missing, auto-select<br/>1. find wallet where currency === gigCoin<br/>2. fallback: workerWallets[0]

    API->>API: findCoinpayGlobalWallet(selectedCurrency, selectedAddress)
    API->>API: Insert gig_invoice record
    API-->>FE: 201 invoice created
    FE-->>Poster: Invoice displayed
Loading

Reviews (1): Last reviewed commit: "fix(invoice): auto-select worker's coinp..." | Re-trigger Greptile

Comment on lines 854 to +925
@@ -906,14 +914,15 @@ function InvoiceForm({
)}
</div>
)}
</div>
</div>
)}

{error && <p className="text-sm text-destructive">{error}</p>}

<div className="flex gap-2">
<Button
onClick={onSubmit}
disabled={isCreating || total <= 0 || overCap || !hasWallets}
disabled={isCreating || total <= 0 || overCap || (isWorker && !hasWallets)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P0 isWorker is not a prop of InvoiceForm — compile error

isWorker is used at two points inside InvoiceForm (the wallet section conditional and the submit-button disabled expression), but it is never declared in the component's props interface and is never passed at either of the two call sites. TypeScript will emit Cannot find name 'isWorker' and refuse to compile, blocking deployment. Even if the build somehow succeeded, isWorker would be undefined (falsy), causing the wallet-selector section to be invisible to workers and the "no wallets" guard on the submit button to never fire.

isWorker needs to be added to InvoiceForm's props type and destructuring, then passed as a prop from every <InvoiceForm ... /> call site in InvoiceButton.

Comment on lines +349 to +362
if (!selectedCurrency || !selectedAddress) {
const gigCoin = preferredCoinToPaymentCurrency(gig.payment_coin || null);
const preferred = workerWallets.find((w) => w.currency === gigCoin) || workerWallets[0];

if (preferred) {
selectedCurrency = preferred.currency;
selectedAddress = preferred.address;
} else {
return NextResponse.json(
{ error: "Select a CoinPay receiving wallet before sending the invoice" },
{ status: 400 }
);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Auto-selected wallet may mismatch the gig's payment coin

When payment_currency / merchant_wallet_address are absent (poster-initiated flow), the backend looks up the worker's wallet with w.currency === gigCoin using strict equality. The frontend walletMatchesCoin helper uses a broader rule: it also accepts currencies whose key starts with ${want}_ (e.g. "usdc_sol" matches gig coin "USDC"). If preferredCoinToPaymentCurrency maps "USDC" to a canonical key that differs from the wallet's .currency field, the .find() silently fails and the code falls back to workerWallets[0], which could be a completely different coin. The poster then creates a CoinPay invoice denominated in the wrong currency without any warning.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@ralyodio ralyodio closed this Jun 14, 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.

Bug: 'Select a CoinPay receiving wallet' error blocks invoice creation even when wallet exists

2 participants