Troubleshooting
Common issues and solutions for PayDirect integration. If you don't find your answer here, check the Error Codes reference.
401 Unauthorized
API returns "Unauthorized" for all requests.
Check:
- Key format is correct:
pd_test_...(sandbox) orpd_live_...(live) - Header format is
Authorization: Bearer pd_test_abc...(notTokenorApiKey) - The key has not been revoked (check Dashboard → Settings → API Keys)
- No extra whitespace or newlines in the key value
Wrong environment key
Payment created but no real transaction occurs.
Cause: Using a pd_test_ key in production. Sandbox keys simulate the full lifecycle instantly without real on-chain activity.
Fix: Switch to a pd_live_ key for real Base mainnet transactions.
400 Bad Request — invalid tokenSymbol
"Invalid token symbol"
Cause: The tokenSymbol must be uppercase: "USDC", "ETH", or "ADAO".
Fix: Use the exact uppercase strings. Common mistakes: "usdc", "Usdc", "DAI".
400 Bad Request — invalid merchantWallet
"Invalid merchant wallet address"
Cause: The merchantWallet must be a valid Ethereum address: 42 characters starting with 0x.
Fix: Validate the address matches /^0x[a-fA-F0-9]{40}$/ before sending.
Payment stuck in "pending"
Payment was created but never moves to "detected".
Possible causes:
- Payer sent funds to the wrong address (not the
receivingAddress) - Payer sent on the wrong network (must be Base mainnet, not Ethereum mainnet)
- Payer sent the wrong token (e.g. USDT instead of USDC)
- Amount doesn't match the expected
grossAmount
Fix: Verify the payer sent the exact grossAmount of the correct token to the receivingAddress on Base. Check the transaction on BaseScan.
Payment expired
Status changed to "expired" before funds arrived.
Cause: The payment window (default: 60 minutes) elapsed without detecting the transfer.
Fix: Create a new payment. Optionally increase expiresInMinutes for payments that need more time.
429 Too Many Requests
Rate limit exceeded.
Limits: Free: 100 req/min, Pro: 1,000 req/min.
Fix: Read the Retry-After header and wait that many seconds before retrying. Implement exponential backoff for sustained bursts.
Webhook signature verification fails
Check:
- You are using the correct signing secret (
whsec_...) from webhook registration - You are verifying against the raw request body string, not a parsed/re-serialized object
- The header name is
X-PayDirect-Signature(case-insensitive in most frameworks) - You are using timing-safe comparison (e.g.
crypto.timingSafeEqual)
Webhooks not being received
Check:
- Your endpoint URL is publicly accessible (not localhost)
- Your endpoint is served over HTTPS with a valid certificate
- You subscribed to the correct events (e.g.
payment.forwarded, notpayment.completed) - Your endpoint returns
200within a reasonable timeout
Tip: Use a tool like webhook.site to test webhook delivery during development.
Wrong network
Funds sent on Ethereum mainnet instead of Base.
PayDirect operates exclusively on Base mainnet (Chain ID: 8453). Funds sent on Ethereum mainnet, Arbitrum, Optimism, or any other chain will not be detected.
Fix: Ensure the payer's wallet is connected to Base. Display the Base network requirement prominently in your payment UI.
Insufficient gas for forwarding
Payment stuck in "confirmed" status.
Cause: The workspace wallet may not have enough ETH for gas to forward the payment. PayDirect auto-funds gas, but in rare cases the gas funder wallet may be depleted.
Fix: Contact support if a payment is stuck in "confirmed" status for more than 15 minutes.
- Check the full Error Codes reference
- Review the API Reference for exact endpoint specifications
- Check your API logs in the Dashboard → Logs section
