This guide walks through migrating from the deprecated Coinbase Commerce API to the modern Payment Links API.
Status: Frontend migration COMPLETE - ready for integration testing Target completion: March 31, 2026 (Commerce API shutdown deadline) Risk level: Medium (payment processing critical)
MIGRATION COMPLETED: The frontend has been updated to use the Coinbase Business Payment Link API via the backend admin endpoints. The old Commerce API charge route has been removed. See backend PR #225 in Morpheus-Marketplace-API for the corresponding backend changes.
Architecture change: The frontend no longer calls Coinbase APIs directly. Instead, it calls the backend admin endpoint (
POST /admin/payment-links) which handles JWT authentication with Coinbase and auto-injectsuser_idmetadata.Key frontend changes:
src/app/api/coinbase/charge/route.ts→ REMOVEDsrc/app/api/coinbase/payment-link/route.ts→ NEW (calls backend admin API)- Webhook handler updated for
X-Hook0-Signatureandpayment_link.payment.*events- UI updated: USDC on Base instead of multi-currency, faster confirmation messaging
- Environment: Uses
ADMIN_API_SECRET+COINBASE_PAYMENT_LINK_WEBHOOK_SECRET
- Frontend validation - No more anonymous payments
- Balance refresh - Page reloads after successful payment to show new balance
- Webhook robustness - Better error handling, idempotency, logging
- Security - Proper signature verification enforced
- Backend Payment Links API integration in morpheus-marketplace-api
- Frontend charge creation using new API format
- Modern Coinbase integration following 2024 best practices
Note: Payment Links API migration requires changes in BOTH repositories:
- morpheus-marketplace-api (backend) - webhook handler updates
- Morpheus-Marketplace-APP (frontend) - charge creation updates
Files changed in Morpheus-Marketplace-APP:
- ✅
src/components/billing/FundingSection.tsx - ✅
src/app/api/coinbase/charge/route.ts
Files that need fixing in morpheus-marketplace-api:
⚠️ Backend webhook handler (see BACKEND_WEBHOOK_FIXES.md)
Deploy these changes immediately to fix the current issues:
- Missing user_id errors
- Balance not updating
- Better webhook reliability
Testing checklist:
# 1. Verify no anonymous payments possible
- [ ] Try to pay without logging in → Should show "Login required"
- [ ] Try with userId=null → Should get 401 error
# 2. Test payment flow
- [ ] Log in as test user
- [ ] Click "Pay with Crypto"
- [ ] Enter $5 amount
- [ ] Complete payment on Coinbase
- [ ] Verify webhook received (check backend logs)
- [ ] Verify balance updates after 2 seconds
- [ ] Check database: credits should be added
# 3. Test idempotency
- [ ] Manually trigger same webhook twice
- [ ] Verify credits only added onceRollback plan:
git revert HEAD~1 # If issues occur- Log into Coinbase Developer Portal
- Click "Create API key"
- Configure the API key:
- Nickname: "Morpheus Marketplace Payment Links"
- API restrictions: Enable View permission
- This grants access to Payment Links API (create, update, manage)
- IP allowlist: (Optional but recommended) Add your server IPs
- Signature algorithm: Select ECDSA (recommended)
- Click "Create API key"
- Save these securely (shown only once):
- API Key Name (e.g.,
organizations/{org_id}/apiKeys/{key_id}) - Private Key (multi-line EC private key)
- API Key Name (e.g.,
Permission details:
- View permission includes Payment Links API access
- You do NOT need "Trade" or "Transfer" permissions for payment links
- See Coinbase API Key docs
Important: Payment Links API uses JWT Bearer tokens generated from CDP API keys, not simple API key strings.
Add to .env.local (development):
# Old API (keep for now during transition)
COINBASE_COMMERCE_API_KEY=existing_key
COINBASE_COMMERCE_WEBHOOK_SECRET=existing_secret
# New Payment Links API (JWT-based authentication)
COINBASE_CDP_KEY_NAME=organizations/{org_id}/apiKeys/{key_id}
COINBASE_CDP_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\nYOUR_PRIVATE_KEY\n-----END EC PRIVATE KEY-----\n"
COINBASE_WEBHOOK_SECRET=your_new_webhook_secret_here
# Backend API (existing)
ADMIN_API_SECRET=your_admin_secret
NEXT_PUBLIC_API_BASE_URL=https://api.mor.orgAdd to backend environment variables:
# In morpheus-marketplace-api
COINBASE_CDP_KEY_NAME=organizations/{org_id}/apiKeys/{key_id}
COINBASE_CDP_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----\n"
COINBASE_WEBHOOK_SECRET=your_webhook_secretNote: You'll need to generate JWT tokens on-the-fly for each API request. See Coinbase JWT docs for implementation.
-
In Coinbase Developer Portal:
- Go to Webhooks settings
- Add new webhook endpoint (or update existing):
- URL:
https://api.mor.org/api/v1/webhooks/coinbase - Events: Select all
payment_link.payment.*events (new API) - Note: Same endpoint handles both old and new API formats!
- URL:
- Save webhook secret to environment variables
-
Test webhook:
# Test in dev environment first
curl -X POST https://api.dev.mor.org/api/v1/webhooks/coinbase \
-H "Content-Type: application/json" \
-H "X-Hook0-Signature: test" \
-d '{"event_type":"payment_link.payment.success","data":{"metadata":{"user_id":"test-uuid"}}}'
# Production
curl -X POST https://api.mor.org/api/v1/webhooks/coinbase \
-H "Content-Type: application/json" \
-H "X-Hook0-Signature: test" \
-d '{"event_type":"payment_link.payment.success","data":{"metadata":{"user_id":"test-uuid"}}}'Important: The backend webhook handler already auto-detects API version based on signature header:
X-CC-Webhook-Signature→ Legacy Commerce APIX-Hook0-Signature→ Payment Links API
Create test payment link:
// In browser console or API testing tool
fetch('/api/coinbase/payment-link', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: '5.00',
currency: 'USD',
userId: 'YOUR_COGNITO_USER_ID',
description: 'Test payment'
})
})
.then(r => r.json())
.then(data => console.log(data))Expected response:
{
"success": true,
"payment_link": {
"id": "link_abc123",
"hosted_url": "https://pay.coinbase.com/...",
"expires_at": "2026-02-12T12:00:00Z",
"metadata": {
"user_id": "..."
}
}
}Option A: Gradual Migration (Recommended)
Add feature flag to switch between old and new API:
- Add environment variable:
COINBASE_USE_PAYMENT_LINKS=false # Start with old API- Update FundingSection.tsx:
const openCoinbaseCheckout = async (amount: string) => {
const usePaymentLinks = process.env.NEXT_PUBLIC_COINBASE_USE_PAYMENT_LINKS === 'true';
const endpoint = usePaymentLinks
? '/api/coinbase/payment-link'
: '/api/coinbase/charge';
const response = await fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ amount, currency: 'USD', userId })
});
const data = await response.json();
// Handle both response formats
const hostedUrl = data.payment_link?.hosted_url || data.charge?.hosted_url;
if (hostedUrl) {
window.open(hostedUrl, '_blank');
}
};- Rollout schedule:
Week 1: Deploy with flag=false (old API, with fixes)
Week 2: Enable for 10% of users (flag=true for test accounts)
Week 3: Enable for 50% of users
Week 4: Enable for 100% of users
Week 5: Remove old API code
Option B: Immediate Switch (Higher Risk)
- Replace old endpoints with new ones
- Update webhook URL in Coinbase dashboard
- Deploy and monitor closely
- Rollback plan ready
After successful migration:
- Remove old frontend code:
# In Morpheus-Marketplace-APP repo
rm src/app/api/coinbase/charge/route.ts # Replace with payment-link version- Update backend:
# In morpheus-marketplace-api repo
# Remove legacy Commerce API support from webhook handler
# Keep only Payment Links API logic- Update references:
- Update FundingSection to only use new endpoint
- Remove feature flags
- Update documentation
- Remove old environment variables:
# Delete from Vercel/production
COINBASE_COMMERCE_API_KEY
COINBASE_COMMERCE_WEBHOOK_SECRET- Disable old webhook:
- In Coinbase dashboard, disable old webhook endpoint
- Keep for 30 days in case of rollback needed
If issues occur after migration:
# 1. Switch feature flag
vercel env add COINBASE_USE_PAYMENT_LINKS false
# 2. Redeploy
vercel --prod
# 3. Verify old API working
curl https://app.mor.org/api/coinbase/charge# 1. Revert git commits
git revert HEAD~3 # Revert last 3 commits
# 2. Redeploy
git push origin main
# 3. Update webhook URL in Coinbase dashboard
# Back to: /api/webhooks/coinbase-
Payment success rate
- Old API baseline: [Record current %]
- New API target: ≥ baseline
- Alert if: < 95%
-
Webhook processing time
- Old API baseline: [Record current avg]
- New API target: < 2 seconds
- Alert if: > 5 seconds
-
Failed webhooks
- Target: 0 failures
- Alert if: > 5 failures in 1 hour
-
Balance update latency
- Target: < 10 seconds from payment to balance update
- Alert if: > 30 seconds
Check for webhook failures:
# In your logging system (e.g., Vercel logs)
"[Payment Link Webhook] Error" OR "[Coinbase Webhook] Error"Check for missing user_id:
"Missing or invalid user_id"Check for duplicate processing:
"already processed"- All environment variables set
- Webhook endpoint configured in Coinbase dashboard
- Test payment link creation succeeds
- Test webhook signature verification works
- Code reviewed by 2+ team members
- Rollback plan documented and tested
- Make test payment ($1) and verify:
- Payment link opens
- Payment completes on Coinbase
- Webhook received and processed
- Credits added to account
- Balance updates in UI
- Transaction appears in history
- Monitor logs for 1 hour
- Check error rates
- Verify no duplicate credits
- 10 concurrent payments
- 100 payments in 1 minute
- Verify all processed correctly
- No duplicate credits
- No missed webhooks
Cause: Missing COINBASE_API_KEY or COINBASE_API_SECRET
Fix: Add environment variables and redeploy
Cause: Wrong COINBASE_WEBHOOK_SECRET or signature verification logic error
Fix: Verify secret matches Coinbase dashboard, check signature algorithm
Cause: Backend API call failing
Fix: Check ADMIN_API_SECRET and NEXT_PUBLIC_API_BASE_URL are correct
Cause: Webhook processed but UI not refreshing
Fix: Already fixed in Phase 1 patches - deploy those first
Cause: Idempotency check not working
Fix: Already implemented - processedPayments Set tracks processed payment IDs
- Bug report:
COINBASE_PAYMENT_ISSUES.md - Architecture:
ARCHITECTURE.md - Backend fixes:
BACKEND_WEBHOOK_FIXES.md - Frontend code:
src/app/api/coinbase/charge/route.ts - Backend webhook:
morpheus-marketplace-apirepo (Python)
- Developer support: https://developer.coinbase.com/support
- Status page: https://status.coinbase.com/
| Phase | Duration | Effort |
|---|---|---|
| Phase 1 (Immediate fixes) | 1 day | Low |
| Phase 2 (Setup new API) | 2-3 days | Medium |
| Phase 3 (Feature flag rollout) | 2-4 weeks | Low |
| Phase 4 (Cleanup) | 1 day | Low |
| Total | 3-5 weeks | Medium |
Migration is complete when:
- ✅ 0 "missing user_id" errors in last 7 days
- ✅ 100% of payments using Payment Links API
- ✅ Balance updates within 5 seconds of payment
- ✅ Payment success rate ≥ 99%
- ✅ Old API code removed
- ✅ Old webhooks disabled
- ✅ Documentation updated
- ✅ Team trained on new system
- Engineering lead approval
- Product manager approval
- Security team approval
- QA sign-off
- Production deployment approval
Last updated: February 12, 2026
Document owner: [Your name]
Status: Ready for Phase 1 deployment