
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
react-next-402-coinbase-payments
Advanced tools
Librería profesional para integrar pagos con protocolo 402 de Coinbase en aplicaciones React y Next.js
A comprehensive, enterprise-grade payment library for React and Next.js applications. Seamlessly integrates Coinbase Commerce API payments and native blockchain transactions through the innovative 402 Protocol. Perfect for e-commerce, SaaS subscriptions, NFT marketplaces, and Web3 applications.
npm install react-next-402-coinbase-payments
# Get your API key from https://commerce.coinbase.com
COINBASE_API_KEY=your-production-api-key
COINBASE_WEBHOOK_SECRET=your-webhook-secret
# RPC endpoints for different networks (optional - uses public RPCs by default)
ETH_RPC_URL=https://mainnet.infura.io/v3/YOUR_PROJECT_ID
POLYGON_RPC_URL=https://polygon-rpc.com
BSC_RPC_URL=https://bsc-dataseed.binance.org
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
AVAX_RPC_URL=https://api.avax.network/ext/bc/C/rpc
Best for: E-commerce, subscriptions, professional applications
Best for: Web3 apps, NFT marketplaces, DeFi platforms
import { PaymentButton, use402Payment } from 'react-next-402-coinbase-payments';
function ProductCheckout({ product, cart }) {
const { initiatePayment } = use402Payment({
coinbaseApiKey: process.env.COINBASE_API_KEY,
onPaymentSuccess: (result) => {
// Update order status in your database
updateOrderStatus(result.chargeId, 'paid');
// Send confirmation email
sendOrderConfirmation(product, cart);
}
});
const handlePayment = () => {
initiatePayment({
amount: cart.total,
currency: 'USD',
description: `Purchase: ${product.name}`,
metadata: {
productId: product.id,
cartId: cart.id,
customerEmail: cart.customer.email
}
});
};
return (
<div className="checkout-container">
<div className="order-summary">
<h3>Order Summary</h3>
{cart.items.map(item => (
<div key={item.id} className="cart-item">
<span>{item.name}</span>
<span>${item.price}</span>
</div>
))}
<div className="total">Total: ${cart.total}</div>
</div>
<PaymentButton
coinbaseApiKey={process.env.COINBASE_API_KEY}
paymentOptions={{
amount: cart.total,
currency: 'USD',
description: `Order #${cart.id}`,
metadata: { cartId: cart.id }
}}
onSuccess={(result) => {
window.location.href = `/order/${result.chargeId}`;
}}
>
Complete Purchase - ${cart.total}
</PaymentButton>
</div>
);
}
function SubscriptionCheckout({ plan }) {
return (
<PaymentButton
coinbaseApiKey={process.env.COINBASE_API_KEY}
paymentOptions={{
amount: plan.price,
currency: 'USD',
description: `${plan.name} Subscription`,
metadata: {
planId: plan.id,
billingCycle: plan.interval,
userId: currentUser.id
}
}}
onSuccess={async (result) => {
// Create subscription in your database
await createSubscription({
userId: currentUser.id,
planId: plan.id,
chargeId: result.chargeId,
status: 'active'
});
// Redirect to success page
router.push('/subscription/success');
}}
>
Subscribe to {plan.name} - ${plan.price}/{plan.interval}
</PaymentButton>
);
}
function NFTPurchase({ nft }) {
const { connectedWallet } = useWallet();
return (
<div className="nft-purchase">
{connectedWallet ? (
// Native crypto payment for NFT
<NativePaymentButton
amount={nft.price}
currency="ETH"
recipientAddress={nft.creatorAddress}
onSuccess={(txHash) => {
// Record NFT transfer
recordNFTTransfer(nft.id, connectedWallet.address, txHash);
}}
>
Buy NFT for {nft.price} ETH
</NativePaymentButton>
) : (
// Coinbase payment as fallback
<PaymentButton
coinbaseApiKey={process.env.COINBASE_API_KEY}
paymentOptions={{
amount: nft.price,
currency: 'USD',
description: `NFT: ${nft.name}`,
metadata: { nftId: nft.id }
}}
>
Buy NFT for ${nft.price}
</PaymentButton>
)}
</div>
);
}
// Use test API keys in development
const config = {
coinbaseApiKey: process.env.NODE_ENV === 'production'
? process.env.COINBASE_PROD_KEY
: process.env.COINBASE_SANDBOX_KEY,
// Use test networks for native payments
networks: process.env.NODE_ENV === 'production' ? {
ethereum: 'https://mainnet.infura.io/v3/YOUR_KEY',
polygon: 'https://polygon-rpc.com',
} : {
ethereum: 'https://sepolia.infura.io/v3/YOUR_KEY',
polygon: 'https://rpc-mumbai.maticvigil.com',
}
};
# .env.local (Development)
COINBASE_API_KEY=your_sandbox_api_key
COINBASE_WEBHOOK_SECRET=your_sandbox_webhook_secret
NODE_ENV=development
# .env.production (Production)
COINBASE_API_KEY=your_production_api_key
COINBASE_WEBHOOK_SECRET=your_production_webhook_secret
NODE_ENV=production
import { useTestPayment } from 'react-next-402-coinbase-payments';
function DevelopmentPaymentTester() {
const { simulatePayment, getAvailableScenarios } = useTestPayment({
coinbaseApiKey: process.env.COINBASE_API_KEY,
testMode: {
enabled: process.env.NODE_ENV !== 'production',
scenarios: [
{ id: 'success', name: 'Successful Payment', result: 'success' },
{ id: 'failed', name: 'Failed Payment', result: 'error' },
{ id: 'pending', name: 'Pending Payment', result: 'pending' }
]
}
});
if (process.env.NODE_ENV === 'production') return null;
return (
<div className="test-mode-banner">
<h4>Test Payment Scenarios</h4>
{getAvailableScenarios().map(scenario => (
<button
key={scenario.id}
onClick={() => simulatePayment(scenario.id)}
>
Test: {scenario.name}
</button>
))}
</div>
);
}
// For better performance and reliability, configure custom RPC endpoints
const rpcConfig = {
ethereum: {
mainnet: 'https://mainnet.infura.io/v3/YOUR_PROJECT_ID',
sepolia: 'https://sepolia.infura.io/v3/YOUR_PROJECT_ID'
},
polygon: {
mainnet: 'https://polygon-mainnet.infura.io/v3/YOUR_PROJECT_ID',
mumbai: 'https://polygon-mumbai.infura.io/v3/YOUR_PROJECT_ID'
},
bsc: {
mainnet: 'https://bsc-dataseed.binance.org',
testnet: 'https://data-seed-prebsc-1-s1.binance.org:8545'
},
solana: {
mainnet: 'https://api.mainnet-beta.solana.com',
devnet: 'https://api.devnet.solana.com'
},
avalanche: {
mainnet: 'https://api.avax.network/ext/bc/C/rpc',
fuji: 'https://api.avax-test.network/ext/bc/C/rpc'
}
};
import { useWallet } from 'react-next-402-coinbase-payments';
function NetworkSwitcher() {
const { connectedWallet, switchNetwork } = useWallet();
const networks = [
{ id: '0x1', name: 'Ethereum Mainnet' },
{ id: '0x89', name: 'Polygon Mainnet' },
{ id: '0x38', name: 'BSC Mainnet' }
];
return (
<select
onChange={(e) => switchNetwork(e.target.value)}
disabled={!connectedWallet}
>
{networks.map(network => (
<option key={network.id} value={network.id}>
{network.name}
</option>
))}
</select>
);
}
interface PaymentButtonProps {
coinbaseApiKey: string; // Required: Your Coinbase API key
paymentOptions: PaymentOptions; // Required: Payment configuration
children: React.ReactNode; // Required: Button content
onSuccess?: (result: PaymentResult) => void;
onError?: (error: string) => void;
className?: string;
disabled?: boolean;
redirectUrl?: string; // Override success redirect
cancelUrl?: string; // Override cancel redirect
}
interface PaymentOptions {
amount: number; // Payment amount
currency: string; // USD, EUR, GBP, BTC, ETH
description?: string; // Payment description
metadata?: Record<string, any>; // Custom metadata
redirectUrl?: string; // Success redirect URL
cancelUrl?: string; // Cancel redirect URL
customerEmail?: string; // Customer email for receipts
customerName?: string; // Customer name
}
const {
connectedWallet, // Current connected wallet info
isConnecting, // Connection loading state
error, // Connection error message
wallets, // Available wallet list
connect, // Connect wallet function
disconnect, // Disconnect wallet function
switchNetwork // Switch blockchain network
} = useWallet();
import { processWebhook, validateWebhookSignature } from 'react-next-402-coinbase-payments';
// API Route: /api/webhooks/coinbase
export default async function handler(req, res) {
if (req.method !== 'POST') return res.status(405).end();
const signature = req.headers['x-cc-webhook-signature'];
// Validate webhook signature
if (!validateWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.COINBASE_WEBHOOK_SECRET
)) {
return res.status(400).json({ error: 'Invalid signature' });
}
// Process webhook events
processWebhook(req.body, {
onChargeConfirmed: async (charge) => {
await updateOrderStatus(charge.id, 'paid');
await sendOrderConfirmation(charge);
},
onChargeFailed: (charge) => {
console.error('Payment failed:', charge.id);
},
onChargePending: (charge) => {
console.log('Payment pending:', charge.id);
}
});
res.status(200).json({ received: true });
}
# Required for Coinbase Commerce
COINBASE_API_KEY=your_production_api_key
COINBASE_WEBHOOK_SECRET=your_production_webhook_secret
# Optional for Native Crypto (uses public RPCs if not set)
ETH_RPC_URL=https://mainnet.infura.io/v3/YOUR_PROJECT_ID
POLYGON_RPC_URL=https://polygon-rpc.com
BSC_RPC_URL=https://bsc-dataseed.binance.org
SOLANA_RPC_URL=https://api.mainnet-beta.solana.com
AVAX_RPC_URL=https://api.avax.network/ext/bc/C/rpc
# Application settings
NODE_ENV=production
NEXT_PUBLIC_APP_URL=https://yourdomain.com
https://yourdomain.com/api/webhooks/coinbasecharge:confirmed, charge:failed, charge:pending// Always validate webhook signatures
import { validateWebhookSignature } from 'react-next-402-coinbase-payments';
if (!validateWebhookSignature(payload, signature, secret)) {
return res.status(400).json({ error: 'Invalid signature' });
}
// Use HTTPS in production
const redirectUrls = {
success: `${process.env.NEXT_PUBLIC_APP_URL}/payment/success`,
cancel: `${process.env.NEXT_PUBLIC_APP_URL}/payment/cancel`
};
// Store sensitive data securely
const apiKey = process.env.COINBASE_API_KEY;
if (!apiKey) throw new Error('Coinbase API key not configured');
# Clear node_modules and reinstall
rm -rf node_modules package-lock.json
npm install
# For Next.js, clear cache
rm -rf .next
npm run build
// Ensure you're using the raw request body
import { validateWebhookSignature } from 'react-next-402-coinbase-payments';
// For Next.js API routes
export const config = {
api: {
bodyParser: false, // Important for webhook signature validation
},
};
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
const rawBody = Buffer.concat(chunks).toString();
const isValid = validateWebhookSignature(
rawBody,
req.headers['x-cc-webhook-signature'],
process.env.COINBASE_WEBHOOK_SECRET
);
// Check if wallet is installed
const { wallets } = useWallet();
const metamaskInstalled = wallets.find(w => w.type === 'metamask')?.installed;
// Handle connection errors
try {
await connect('metamask');
} catch (error) {
if (error.message.includes('User rejected')) {
// Show user-friendly message
alert('Please approve the connection in MetaMask');
}
}
// Add network to wallet if it doesn't exist
const addNetwork = async (chainId: string) => {
try {
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [{
chainId,
chainName: 'Polygon Mainnet',
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
rpcUrls: ['https://polygon-rpc.com'],
blockExplorerUrls: ['https://polygonscan.com']
}]
});
} catch (error) {
console.error('Failed to add network:', error);
}
};
// Import only what you need
import { PaymentButton } from 'react-next-402-coinbase-payments';
// Instead of importing everything
// Use dynamic imports for heavy components
import dynamic from 'next/dynamic';
const PaymentModal = dynamic(() =>
import('react-next-402-coinbase-payments').then(mod => ({ default: mod.PaymentModal }))
);
// Cache payment configurations
const paymentConfig = useMemo(() => ({
coinbaseApiKey: process.env.COINBASE_API_KEY,
redirectUrl: `${baseUrl}/success`,
cancelUrl: `${baseUrl}/cancel`
}), [baseUrl]);
class PaymentErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// Log to your error reporting service
console.error('Payment error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong with the payment system.</div>;
}
return this.props.children;
}
}
We welcome contributions! Please see our Contributing Guide for details.
git clone https://github.com/viainti/react-next-402-coinbase-payments.git
cd react-next-402-coinbase-payments
npm install
npm run demo # Start development demo
npm test
npm run test:e2e # End-to-end tests
MIT License - see LICENSE file for details.
Built with ❤️ by Viainti
## 🚀 Demo
Try the library with our interactive demo!
### Run demo from root directory:
```bash
npm run demo
cd demo
npm install
npm run dev
The demo runs on port 4020 (related to the 402 protocol).
Open http://localhost:4020 in your browser to see:
The easiest way to use the library with automatic method detection:
import { Payment402Button, use402Protocol } from 'react-next-402-coinbase-payments';
function App() {
return (
<Payment402Button
coinbaseApiKey="your-api-key-here"
paymentOptions={{
amount: 25,
currency: 'USD',
description: 'Premium subscription',
redirectUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cancel',
}}
onSuccess={(result) => {
console.log('402 Payment successful:', result);
console.log('Method used:', result.method);
console.log('Protocol:', result.protocol);
}}
onError={(error) => console.error('Payment error:', error)}
showMethodSelector={true}
>
Pay with 402 Protocol
</Payment402Button>
);
}
import { use402Protocol } from 'react-next-402-coinbase-payments';
function PaymentComponent() {
const { initiate402Payment, handle402Response, currentMethod, switchMethod } = use402Protocol({
coinbaseApiKey: 'your-api-key-here',
onPaymentSuccess: (result) => {
console.log('402 Payment completed:', result);
},
onPaymentError: (error) => console.error('Error:', error)
});
const handlePayment = () => {
initiate402Payment({
amount: 10,
currency: 'USD',
description: 'Premium access'
});
};
// Handle 402 responses automatically
const handleApiResponse = async (response) => {
if (response.status === 402) {
await handle402Response(response.url, {
amount: 10,
currency: 'USD',
description: 'Unlock content'
});
}
};
return (
<div>
<button onClick={handlePayment}>
Pay with {currentMethod}
</button>
<button onClick={() => switchMethod('coinbase')}>
Switch to Coinbase
</button>
</div>
);
}
import { use402Payment } from 'react-next-402-coinbase-payments';
function PaymentComponent() {
const { initiatePayment, isLoading } = use402Payment({
coinbaseApiKey: 'your-api-key-here',
onPaymentSuccess: (result) => {
console.log('Payment successful:', result);
},
onPaymentError: (error) => {
console.error('Payment error:', error);
},
});
const handlePayment = () => {
initiatePayment({
amount: 10,
currency: 'USD',
description: 'Premium access',
redirectUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cancel',
});
};
return (
<button onClick={handlePayment} disabled={isLoading}>
{isLoading ? 'Processing...' : 'Pay $10'}
</button>
);
}
import { PaymentButton } from 'react-next-402-coinbase-payments';
function App() {
return (
<PaymentButton
coinbaseApiKey="your-api-key-here"
paymentOptions={{
amount: 25,
currency: 'USD',
description: 'Monthly subscription',
}}
onSuccess={(result) => console.log('Payment completed:', result)}
onError={(error) => console.error('Error:', error)}
>
Subscribe for $25
</PaymentButton>
);
}
import { WalletConnectButton, NativePaymentButton } from 'react-next-402-coinbase-payments';
function CryptoPaymentComponent() {
return (
<div>
<WalletConnectButton />
<NativePaymentButton
recipientAddress="0x..."
amount={0.01}
currency="ETH"
description="NFT Purchase"
onSuccess={(result) => console.log('Crypto payment successful:', result.txHash)}
onError={(error) => console.error('Crypto payment error:', error)}
>
Pay 0.01 ETH
</NativePaymentButton>
</div>
);
}
import { useNativePayment } from 'react-next-402-coinbase-payments';
function CustomCryptoPayment() {
const { sendPayment, isProcessing, estimateCost } = useNativePayment();
const handlePayment = async () => {
const result = await sendPayment({
amount: 0.05,
currency: 'MATIC',
recipientAddress: '0x...',
description: 'Service payment'
});
if (result.success) {
console.log('Payment sent:', result.txHash);
}
};
return (
<button onClick={handlePayment} disabled={isProcessing}>
{isProcessing ? 'Sending...' : 'Send 0.05 MATIC'}
</button>
);
}
import { WalletConnectButton } from 'react-next-402-coinbase-payments';
function WalletSection() {
return (
<WalletConnectButton
onConnect={(wallet) => {
console.log('Connected:', wallet.address);
}}
onDisconnect={() => {
console.log('Disconnected');
}}
showWalletList={true}
/>
);
}
import { useWallet } from 'react-next-402-coinbase-payments';
function CustomWalletConnection() {
const {
connectedWallet,
isConnecting,
error,
getAvailableWallets,
connect,
disconnect,
switchNetwork
} = useWallet();
const handleConnect = async () => {
const availableWallets = getAvailableWallets();
if (availableWallets.length > 0) {
await connect(availableWallets[0].type);
}
};
return (
<div>
{connectedWallet ? (
<div>
<p>Connected: {connectedWallet.address}</p>
<button onClick={disconnect}>Disconnect</button>
</div>
) : (
<button onClick={handleConnect} disabled={isConnecting}>
{isConnecting ? 'Connecting...' : 'Connect Wallet'}
</button>
)}
{error && <p>Error: {error}</p>}
</div>
);
}
| Wallet | Type | Chains | Detection |
|---|---|---|---|
| MetaMask | EIP-1193 | ETH, Polygon, BSC, Avalanche | window.ethereum.isMetaMask |
| Phantom | Solana | Solana | window.solana.isPhantom |
| Solflare | Solana | Solana | window.solflare |
| Trust Wallet | EIP-1193 | ETH, Polygon, BSC | window.ethereum.isTrust |
| Coinbase Wallet | EIP-1193 | ETH, Polygon, BSC | window.ethereum.isCoinbaseWallet |
| Brave Wallet | EIP-1193 | ETH, Polygon, BSC | window.ethereum.isBraveWallet |
| Opera Wallet | EIP-1193 | ETH, Polygon, BSC | window.ethereum.isOpera |
To process Coinbase webhooks, set up an endpoint on your server:
import { processWebhook, validateWebhookSignature } from 'react-next-402-coinbase-payments';
// In your API route (Next.js)
export default async function handler(req, res) {
if (req.method === 'POST') {
const signature = req.headers['x-cc-webhook-signature'];
const payload = JSON.stringify(req.body);
// Validate signature (optional but recommended)
if (!validateWebhookSignature(payload, signature, 'your-webhook-secret')) {
return res.status(400).json({ error: 'Invalid signature' });
}
// Process webhook
processWebhook(req.body, {
onChargeConfirmed: (charge) => {
console.log('Payment confirmed:', charge.id);
// Update database, grant access, etc.
},
onChargeFailed: (charge) => {
console.log('Payment failed:', charge.id);
},
onChargePending: (charge) => {
console.log('Payment pending:', charge.id);
},
});
res.status(200).json({ received: true });
}
}
Main hook for handling payments.
Parameters:
coinbaseApiKey: Your Coinbase Commerce API keyonPaymentSuccess: Callback when payment is successfulonPaymentError: Callback when there's an errorReturns:
initiatePayment: Function to initiate a paymenthandle402Response: Function to handle 402 responsescheckPaymentStatus: Function to check payment statusisLoading: Loading statepaymentResult: Result of the last paymentButton component for payments.
Props:
coinbaseApiKey: Required API keypaymentOptions: Payment optionschildren: Button contentonSuccess: Success callbackonError: Error callbackclassName: CSS classesdisabled: Disable buttoninterface PaymentOptions {
amount: number;
currency: string;
description?: string;
metadata?: Record<string, any>;
redirectUrl?: string;
cancelUrl?: string;
}
interface PaymentResult {
success: boolean;
chargeId?: string;
error?: string;
redirectUrl?: string;
}
This library implements the HTTP 402 (Payment Required) protocol for web payments. When a resource requires payment, the server can return a 402 response with information on how to pay using cryptocurrencies through Coinbase.
Contributions are welcome! Please open an issue or pull request on GitHub.
MIT
FAQs
Librería profesional para integrar pagos con protocolo 402 de Coinbase en aplicaciones React y Next.js
We found that react-next-402-coinbase-payments demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.