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
gross_amount
Fix: Verify the payer sent the exact gross_amount 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
