| name | Cryptocurrency Payment |
| description | Enabling direct peer-to-peer cryptocurrency transactions without intermediaries, including direct wallet payments, payment processors, transaction monitoring, and blockchain confirmation. |
Cryptocurrency Payment
Current Level: Advanced
Domain: Blockchain / Payments
Overview
Cryptocurrency payments enable direct peer-to-peer transactions without intermediaries. This guide covers direct wallet payments, payment processors, and transaction monitoring for accepting crypto payments in applications.
Crypto Payment Concepts
Customer → Payment Request → Wallet → Blockchain → Confirmation → Order Complete
Payment Methods:
- Direct Wallet - Customer sends crypto directly
- Payment Processor - Third-party handles payments
- Smart Contract - Automated payment handling
Direct Wallet Payments
import { ethers } from 'ethers';
export class CryptoPaymentService {
constructor(
private provider: ethers.providers.Provider,
private merchantAddress: string
) {}
async createPaymentRequest(
amount: string,
currency: 'ETH' | 'USDC' | 'DAI'
): Promise<PaymentRequest> {
const paymentId = this.generatePaymentId();
const expiresAt = Date.now() + 15 * 60 * 1000;
return {
paymentId,
merchantAddress: this.merchantAddress,
amount,
currency,
expiresAt,
status: 'pending'
};
}
async monitorPayment(
paymentId: string,
expectedAmount: string
): Promise<PaymentStatus> {
const filter = {
address: this.merchantAddress,
topics: []
};
return new Promise((resolve) => {
this.provider.on(filter, async (log) => {
const tx = await this.provider.getTransaction(log.transactionHash);
if (tx.to === this.merchantAddress) {
const receivedAmount = ethers.utils.formatEther(tx.value);
if (receivedAmount === expectedAmount) {
resolve({
paymentId,
status: 'confirmed',
transactionHash: tx.hash,
amount: receivedAmount
});
}
}
});
});
}
async verifyPayment(
transactionHash: string,
expectedAmount: string
): Promise<boolean> {
const receipt = await this.provider.getTransactionReceipt(transactionHash);
if (!receipt || receipt.status !== 1) {
return false;
}
const tx = await this.provider.getTransaction(transactionHash);
const receivedAmount = ethers.utils.formatEther(tx.value);
return (
tx.to === this.merchantAddress &&
receivedAmount === expectedAmount &&
receipt.confirmations >= 3
);
}
private generatePaymentId(): string {
return `pay_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
}
interface PaymentRequest {
paymentId: string;
merchantAddress: string;
amount: string;
currency: string;
expiresAt: number;
status: 'pending' | 'confirmed' | 'expired';
}
interface PaymentStatus {
paymentId: string;
status: string;
transactionHash: string;
amount: string;
}
Payment Processors
Coinbase Commerce
import axios from 'axios';
export class CoinbaseCommerceService {
private apiKey = process.env.COINBASE_COMMERCE_API_KEY!;
private baseUrl = 'https://api.commerce.coinbase.com';
async createCharge(data: CreateChargeDto): Promise<Charge> {
const response = await axios.post(
`${this.baseUrl}/charges`,
{
name: data.name,
description: data.description,
pricing_type: 'fixed_price',
local_price: {
amount: data.amount,
currency: data.currency
},
metadata: data.metadata
},
{
headers: {
'X-CC-Api-Key': this.apiKey,
'X-CC-Version': '2018-03-22'
}
}
);
return response.data.data;
}
async getCharge(chargeId: ): <> {
response = axios.(
,
{
: {
: .,
:
}
}
);
response..;
}
(): <[]> {
response = axios.(
,
{
: {
: .,
:
}
}
);
response..;
}
(: , : ): {
crypto = ();
webhookSecret = process..!;
expectedSignature = crypto
.(, webhookSecret)
.(payload)
.();
signature === expectedSignature;
}
}
{
: ;
: ;
: ;
: ;
?: <, >;
}
{
: ;
: ;
: ;
: ;
: ;
: {
: { : ; : };
?: { : ; : };
?: { : ; : };
};
: [];
: [];
}
{
: ;
: ;
: ;
: {
: { : ; : };
: { : ; : };
};
}
{
: ;
: ;
}
NOWPayments
export class NOWPaymentsService {
private apiKey = process.env.NOWPAYMENTS_API_KEY!;
private baseUrl = 'https://api.nowpayments.io/v1';
async createPayment(data: CreatePaymentDto): Promise<Payment> {
const response = await axios.post(
`${this.baseUrl}/payment`,
{
price_amount: data.amount,
price_currency: data.currency,
pay_currency: data.payCurrency,
order_id: data.orderId,
order_description: data.description,
ipn_callback_url: data.callbackUrl
},
{
headers: {
'x-api-key': this.apiKey
}
}
);
return response.data;
}
async getPaymentStatus(paymentId: string): Promise<PaymentStatus> {
const response = await axios.get(
,
{
: {
: .
}
}
);
response.;
}
(): <[]> {
response = axios.(
,
{
: {
: .
}
}
);
response..;
}
(
: ,
: ,
:
): <> {
response = axios.(
,
{
: {
amount,
: fromCurrency,
: toCurrency
},
: {
: .
}
}
);
response.;
}
}
{
: ;
: ;
: ;
: ;
: ;
: ;
}
{
: ;
: ;
: ;
}
Transaction Monitoring
export class TransactionMonitorService {
private provider: ethers.providers.Provider;
constructor(provider: ethers.providers.Provider) {
this.provider = provider;
}
async waitForConfirmations(
txHash: string,
confirmations: number = 3
): Promise<ethers.providers.TransactionReceipt> {
const receipt = await this.provider.waitForTransaction(txHash, confirmations);
return receipt;
}
async getTransactionStatus(txHash: string): Promise<TransactionStatus> {
const [tx, receipt] = await Promise.all([
this.provider.getTransaction(txHash),
this.provider.getTransactionReceipt(txHash)
]);
if (!receipt) {
return {
: ,
:
};
}
currentBlock = ..();
confirmations = currentBlock - receipt. + ;
{
: receipt. === ? : ,
confirmations,
: receipt.,
: receipt..()
};
}
(
: ,
:
): {
filter = {
address,
: []
};
= () => {
tx = ..(log.);
(tx. === address) {
(tx);
}
};
..(filter, listener);
{
..(filter, listener);
};
}
}
{
: | | ;
: ;
?: ;
?: ;
}
Payment Confirmation
export class PaymentConfirmationService {
async confirmPayment(
transactionHash: string,
expectedAmount: string,
expectedRecipient: string
): Promise<ConfirmationResult> {
const provider = new ethers.providers.InfuraProvider('mainnet');
const receipt = await provider.waitForTransaction(transactionHash, 3);
if (receipt.status !== 1) {
return {
confirmed: false,
reason: 'Transaction failed'
};
}
const tx = await provider.getTransaction(transactionHash);
if (tx.to?.toLowerCase() !== expectedRecipient.toLowerCase()) {
return {
confirmed: false,
reason: 'Incorrect recipient'
};
}
const receivedAmount = ethers.utils.formatEther(tx.);
(receivedAmount !== expectedAmount) {
{
: ,
:
};
}
{
: ,
transactionHash,
: receivedAmount,
: receipt.
};
}
}
{
: ;
?: ;
?: ;
?: ;
?: ;
}
Multi-Currency Support
export class MultiCurrencyService {
private supportedCurrencies = ['ETH', 'BTC', 'USDC', 'USDT', 'DAI'];
async getExchangeRate(from: string, to: string): Promise<number> {
const response = await fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${from}&vs_currencies=${to}`
);
const data = await response.json();
return data[from.toLowerCase()][to.toLowerCase()];
}
async convertAmount(
amount: number,
from: string,
to: string
): Promise<number> {
const rate = await this.getExchangeRate(from, to);
return amount * rate;
}
async getPriceInCrypto(
: ,
:
): <> {
cryptoAmount = .(usdAmount, , cryptocurrency);
cryptoAmount.();
}
}
Webhook Handling
import type { NextApiRequest, NextApiResponse } from 'next';
import crypto from 'crypto';
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
const signature = req.headers['x-cc-webhook-signature'] as string;
const payload = JSON.stringify(req.body);
if (!verifySignature(signature, payload)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = req.body;
switch (event.) {
:
(event.);
;
:
(event.);
;
:
(event.);
;
}
res.({ : });
}
(): {
webhookSecret = process..!;
expectedSignature = crypto
.(, webhookSecret)
.(payload)
.();
signature === expectedSignature;
}
(): <> {
db..({
: { : data.. },
: {
: ,
: data.[].
}
});
emailService.(data..);
}
(): <> {
db..({
: { : data.. },
: { : }
});
}
(): <> {
db..({
: { : data.. },
: { : }
});
}
Quick Start
Payment Request
interface PaymentRequest {
orderId: string
amount: number
currency: 'ETH' | 'BTC' | 'USDT'
recipientAddress: string
}
async function createPaymentRequest(request: PaymentRequest) {
const payment = await db.payments.create({
data: {
orderId: request.orderId,
amount: request.amount,
currency: request.currency,
recipientAddress: request.recipientAddress,
status: 'pending',
expiresAt: addMinutes(new Date(), 15)
}
})
return {
address: request.recipientAddress,
amount: request.amount,
currency: request.currency,
qrCode: generateQRCode(`${request.currency}:${request.recipientAddress}?amount=`)
}
}
Transaction Monitoring
async function monitorTransaction(txHash: string) {
const provider = new ethers.providers.JsonRpcProvider(RPC_URL)
const receipt = await provider.waitForTransaction(txHash, 3)
if (receipt.status === 1) {
await updatePaymentStatus(txHash, 'confirmed')
} else {
await updatePaymentStatus(txHash, 'failed')
}
}
Production Checklist
Anti-patterns
❌ Don't: No Confirmations
const receipt = await provider.waitForTransaction(txHash, 1)
await completeOrder(orderId)
const receipt = await provider.waitForTransaction(txHash, 3)
await completeOrder(orderId)
❌ Don't: Store Private Keys
const wallet = new ethers.Wallet('0x...private-key...')
Integration Points
- Wallet Connection (
35-blockchain-web3/wallet-connection/) - User wallets
- Smart Contracts (
35-blockchain-web3/smart-contracts/) - Contract payments
- Payment Gateways (
30-ecommerce/payment-gateways/) - Payment patterns
Further Reading
Best Practices
- Confirmations - Wait for multiple confirmations
- Amount Verification - Verify exact payment amount
- Address Verification - Verify recipient address
- Webhook Security - Verify webhook signatures
- Timeout Handling - Set payment expiration times
- Multi-Currency - Support multiple cryptocurrencies
- Price Updates - Update crypto prices regularly
- Refunds - Implement refund mechanism
- Testing - Test on testnets first
- Compliance - Follow local regulations
Resources