Skip to content

Fix/invoice wallet select v2#487

Merged
ralyodio merged 3 commits into
profullstack:masterfrom
russo2100:fix/invoice-wallet-select-v2
Jun 18, 2026
Merged

Fix/invoice wallet select v2#487
ralyodio merged 3 commits into
profullstack:masterfrom
russo2100:fix/invoice-wallet-select-v2

Conversation

@russo2100

Copy link
Copy Markdown
Contributor
```mermaid
graph LR
    A[gig.payment_coin] -->|e.g. USDC| B(workerWallets)
    B --> C{Fuzzy Prefix Match}
    C -->|w.currency.startsWith| D[usdc_sol ✅ Correct Wallet Selected]
    C -->|w.currency === gigCoin| E[USDC ✅ Correct Wallet Selected]
    C -->|No match| F[Fallback to index 0 ⚠️]

🧪 Testing

• The frontend compiles successfully without Cannot find name 'isWorker' errors.
• Submitting an invoice as a poster properly queries and assigns the worker's correct cryptocurrency receiving wallet.

… 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 18, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes invoice submission for poster users by skipping wallet selection in the frontend and adding server-side wallet auto-selection in the backend, where the worker's wallets are matched against the gig's payment_coin using an exact match or a prefix match on the base coin symbol (e.g. "usdc_" for "USDC").

  • InvoiceButton.tsx: isWorker is now threaded into InvoiceForm; the wallet picker UI and wallet-selection guard are hidden/skipped for poster users, and the submit button's disabled condition is relaxed to isWorker && !hasWallets.
  • route.ts: Wallet selection/validation is moved after workerWallets is fetched; when payment_currency/merchant_wallet_address are absent the backend auto-selects the best matching wallet (or falls back to workerWallets[0]), allowing posters to create invoices without needing to know the worker's wallet details.

Confidence Score: 5/5

Safe to merge; the poster invoice flow works correctly for the common case and the worker path is unchanged.

The core logic change is straightforward: the poster path now omits wallet fields from the request and the backend auto-selects from the worker's wallets using a base-symbol prefix match. The fallback to workerWallets[0] is an acknowledged edge case flagged in the PR diagram. The only finding is an unreachable else branch that is dead code with no runtime impact.

No files require special attention; the dead-code else branch in route.ts is harmless but could be cleaned up.

Important Files Changed

Filename Overview
src/app/api/gigs/[id]/invoice/route.ts Adds server-side wallet auto-selection when poster submits invoice without wallet info; fallback to workerWallets[0] is acknowledged. The else branch inside the new auto-selection block is unreachable dead code.
src/components/gigs/InvoiceButton.tsx Conditionally hides wallet selection UI and skips wallet validation for non-worker (poster) users; passes isWorker prop down to InvoiceForm; adjusts submit button disabled state accordingly.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant P as Poster / Worker
    participant FE as InvoiceButton.tsx
    participant API as POST /api/gigs/[id]/invoice
    participant CP as CoinPay

    P->>FE: Click "Send Invoice"
    alt isWorker
        FE->>FE: Require wallet selection (selectedWalletCurrency + selectedWalletAddress)
    else isPoster
        FE->>FE: Skip wallet selection (both undefined)
    end
    FE->>API: "POST { payment_currency?, merchant_wallet_address? }"

    API->>CP: getConnectedCoinpayAccessToken(workerId)
    CP-->>API: workerCoinpayToken
    API->>CP: getCoinpayGlobalWalletTokens(token)
    CP-->>API: workerWallets[]

    alt payment_currency and address provided (Worker path)
        API->>API: "selectedCurrency = preferredCoinToPaymentCurrency(payment_currency)"
    else missing (Poster path)
        API->>API: "baseCoin = gig.payment_coin.toLowerCase()"
        API->>API: "gigCoin = preferredCoinToPaymentCurrency(gig.payment_coin)"
        API->>API: "preferred = workerWallets.find(exact or prefix match) || workerWallets[0]"
        API->>API: "selectedCurrency = preferred.currency"
        API->>API: "selectedAddress = preferred.address"
    end

    API->>API: findCoinpayGlobalWallet(workerWallets, selectedCurrency, selectedAddress)
    API->>CP: Create CoinPay invoice
    CP-->>API: invoice data
    API-->>FE: "200 { invoice_id, pay_url, ... }"
    FE-->>P: Show invoice / pay URL
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant P as Poster / Worker
    participant FE as InvoiceButton.tsx
    participant API as POST /api/gigs/[id]/invoice
    participant CP as CoinPay

    P->>FE: Click "Send Invoice"
    alt isWorker
        FE->>FE: Require wallet selection (selectedWalletCurrency + selectedWalletAddress)
    else isPoster
        FE->>FE: Skip wallet selection (both undefined)
    end
    FE->>API: "POST { payment_currency?, merchant_wallet_address? }"

    API->>CP: getConnectedCoinpayAccessToken(workerId)
    CP-->>API: workerCoinpayToken
    API->>CP: getCoinpayGlobalWalletTokens(token)
    CP-->>API: workerWallets[]

    alt payment_currency and address provided (Worker path)
        API->>API: "selectedCurrency = preferredCoinToPaymentCurrency(payment_currency)"
    else missing (Poster path)
        API->>API: "baseCoin = gig.payment_coin.toLowerCase()"
        API->>API: "gigCoin = preferredCoinToPaymentCurrency(gig.payment_coin)"
        API->>API: "preferred = workerWallets.find(exact or prefix match) || workerWallets[0]"
        API->>API: "selectedCurrency = preferred.currency"
        API->>API: "selectedAddress = preferred.address"
    end

    API->>API: findCoinpayGlobalWallet(workerWallets, selectedCurrency, selectedAddress)
    API->>CP: Create CoinPay invoice
    CP-->>API: invoice data
    API-->>FE: "200 { invoice_id, pay_url, ... }"
    FE-->>P: Show invoice / pay URL
Loading

Reviews (2): Last reviewed commit: "fix(invoice): correct fuzzy match to use..." | Re-trigger Greptile

Comment thread src/app/api/gigs/[id]/invoice/route.ts Outdated
Comment on lines +353 to +361
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.

P2 Unreachable error branch

The else block that returns a 400 "Select a CoinPay receiving wallet" error can never execute. preferred is assigned as workerWallets.find(...) || workerWallets[0], and the earlier guard at line 332 already short-circuits with a 409 when workerWallets is empty, so workerWallets[0] is always a valid object at this point. The if (preferred) check and its else branch are therefore dead code and can be removed.

@ralyodio ralyodio merged commit 7aaceb7 into profullstack:master Jun 18, 2026
6 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.

2 participants