> ## Documentation Index
> Fetch the complete documentation index at: https://payer.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Node.js Integration Guide

> Complete guide to integrating Card2Crypto with Node.js and Express

## Prerequisites

* Node.js 14+ installed
* npm or yarn package manager
* Card2Crypto account with API key

## Quick Start

### 1. Install Dependencies

```bash theme={null}
npm install express node-fetch dotenv
```

### 2. Set Up Environment Variables

Create a `.env` file in your project root:

```bash theme={null}
# .env
CARD2CRYPTO_API_KEY=c2c_live_your_api_key_here
CARD2CRYPTO_WEBHOOK_SECRET=your_webhook_secret_here
PORT=3000
```

Add to `.gitignore`:

```bash theme={null}
.env
node_modules/
```

### 3. Create Basic Server

```javascript theme={null}
// server.js
require('dotenv').config();
const express = require('express');
const fetch = require('node-fetch');

const app = express();
app.use(express.json());

const API_KEY = process.env.CARD2CRYPTO_API_KEY;
const WEBHOOK_SECRET = process.env.CARD2CRYPTO_WEBHOOK_SECRET;

// Routes will go here

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
```

## Creating Payments

### Basic Payment Creation

```javascript theme={null}
// server.js

app.post('/create-payment', async (req, res) => {
  try {
    const { amount, email, name, orderId } = req.body;

    // Create payment via Card2Crypto API
    const response = await fetch('https://card2crypto.cc/api/v1/payments', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        amount: amount, // Amount in cents
        currency: 'usd',
        return_url: `${req.protocol}://${req.get('host')}/payment-complete`,
        customer_email: email,
        customer_name: name,
        metadata: {
          order_id: orderId
        }
      })
    });

    const payment = await response.json();

    if (payment.success) {
      // Redirect customer to checkout
      res.redirect(payment.data.checkout_url);
    } else {
      res.status(400).json({ error: payment.error });
    }

  } catch (error) {
    console.error('Payment creation failed:', error);
    res.status(500).json({ error: 'Failed to create payment' });
  }
});
```

### Reusable Payment Helper

```javascript theme={null}
// lib/card2crypto.js

const fetch = require('node-fetch');

class Card2Crypto {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://card2crypto.cc/api/v1';
  }

  async createPayment({ amount, currency = 'usd', returnUrl, customerEmail, customerName, metadata }) {
    const response = await fetch(`${this.baseUrl}/payments`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        amount,
        currency,
        return_url: returnUrl,
        customer_email: customerEmail,
        customer_name: customerName,
        metadata
      })
    });

    const data = await response.json();

    if (!data.success) {
      throw new Error(data.error || 'Payment creation failed');
    }

    return data.data;
  }

  async getPayment(paymentId) {
    const response = await fetch(`${this.baseUrl}/payments/${paymentId}`, {
      headers: {
        'Authorization': `Bearer ${this.apiKey}`
      }
    });

    const data = await response.json();

    if (!data.success) {
      throw new Error(data.error || 'Payment retrieval failed');
    }

    return data.data;
  }
}

module.exports = Card2Crypto;
```

Usage:

```javascript theme={null}
// server.js
const Card2Crypto = require('./lib/card2crypto');

const c2c = new Card2Crypto(process.env.CARD2CRYPTO_API_KEY);

app.post('/create-payment', async (req, res) => {
  try {
    const payment = await c2c.createPayment({
      amount: req.body.amount,
      returnUrl: `${req.protocol}://${req.get('host')}/payment-complete`,
      customerEmail: req.body.email,
      customerName: req.body.name,
      metadata: {
        order_id: req.body.orderId
      }
    });

    res.redirect(payment.checkout_url);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
```

## Handling Return URL

When customer completes payment, they're redirected to your return URL:

```javascript theme={null}
// server.js

app.get('/payment-complete', async (req, res) => {
  const paymentId = req.query.payment_id;

  if (!paymentId) {
    return res.status(400).send('Missing payment ID');
  }

  try {
    // Retrieve payment to verify status
    const payment = await c2c.getPayment(paymentId);

    if (payment.status === 'completed') {
      // Payment successful
      res.send(`
        <html>
          <head><title>Payment Successful</title></head>
          <body>
            <h1>Thank you for your payment!</h1>
            <p>Payment ID: ${payment.id}</p>
            <p>Amount: $${(payment.amount / 100).toFixed(2)}</p>
          </body>
        </html>
      `);
    } else if (payment.status === 'pending') {
      res.send('Payment is still processing...');
    } else {
      res.send('Payment failed. Please try again.');
    }

  } catch (error) {
    console.error('Error retrieving payment:', error);
    res.status(500).send('Error verifying payment');
  }
});
```

## Webhook Integration

Webhooks provide real-time notifications when payment status changes.

### Basic Webhook Handler

```javascript theme={null}
// server.js
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(JSON.stringify(payload))
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

app.post('/webhooks/card2crypto', (req, res) => {
  const signature = req.headers['x-card2crypto-signature'];

  // Verify webhook signature
  if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) {
    console.error('Invalid webhook signature');
    return res.status(401).send('Invalid signature');
  }

  // Return 200 immediately
  res.status(200).send('OK');

  // Process webhook asynchronously
  processWebhook(req.body).catch(error => {
    console.error('Error processing webhook:', error);
  });
});

async function processWebhook(event) {
  console.log('Webhook received:', event.event);

  switch (event.event) {
    case 'payment.completed':
      await handlePaymentCompleted(event.payment);
      break;

    case 'payment.failed':
      await handlePaymentFailed(event.payment);
      break;

    case 'payment.refunded':
      await handlePaymentRefunded(event.payment);
      break;

    default:
      console.log('Unknown event type:', event.event);
  }
}

async function handlePaymentCompleted(payment) {
  const orderId = payment.metadata.order_id;

  console.log(`Payment completed for order ${orderId}`);

  // Update your database
  // await db.orders.update({
  //   where: { id: orderId },
  //   data: { status: 'paid', payment_id: payment.id }
  // });

  // Grant access to product/service
  // await grantAccess(payment.customer_email);

  // Send confirmation email
  // await sendEmail(payment.customer_email, 'Payment confirmed!');
}

async function handlePaymentFailed(payment) {
  const orderId = payment.metadata.order_id;
  console.log(`Payment failed for order ${orderId}`);

  // Update order status
  // await db.orders.update({
  //   where: { id: orderId },
  //   data: { status: 'payment_failed' }
  // });
}

async function handlePaymentRefunded(payment) {
  const orderId = payment.metadata.order_id;
  console.log(`Payment refunded for order ${orderId}`);

  // Revoke access
  // await revokeAccess(payment.customer_email);

  // Update order status
  // await db.orders.update({
  //   where: { id: orderId },
  //   data: { status: 'refunded' }
  // });
}
```

## Complete Example with Database

Here's a complete example using Prisma ORM:

### Install Prisma

```bash theme={null}
npm install @prisma/client
npm install -D prisma
npx prisma init
```

### Database Schema

```prisma theme={null}
// prisma/schema.prisma

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model Order {
  id           String   @id @default(uuid())
  userId       String
  productName  String
  amount       Int      // Amount in cents
  status       String   @default("pending")
  paymentId    String?  @unique
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  @@index([userId])
  @@index([paymentId])
}
```

### Full Integration

```javascript theme={null}
// server.js
require('dotenv').config();
const express = require('express');
const { PrismaClient } = require('@prisma/client');
const Card2Crypto = require('./lib/card2crypto');
const crypto = require('crypto');

const app = express();
app.use(express.json());

const prisma = new PrismaClient();
const c2c = new Card2Crypto(process.env.CARD2CRYPTO_API_KEY);

// Create order and initiate payment
app.post('/orders', async (req, res) => {
  try {
    const { userId, productName, amount } = req.body;

    // Create order in database
    const order = await prisma.order.create({
      data: {
        userId,
        productName,
        amount,
        status: 'pending'
      }
    });

    // Create payment
    const payment = await c2c.createPayment({
      amount: amount,
      returnUrl: `${req.protocol}://${req.get('host')}/orders/${order.id}/complete`,
      metadata: {
        order_id: order.id,
        user_id: userId
      }
    });

    // Update order with payment ID
    await prisma.order.update({
      where: { id: order.id },
      data: { paymentId: payment.id }
    });

    res.json({
      order_id: order.id,
      checkout_url: payment.checkout_url
    });

  } catch (error) {
    console.error('Error creating order:', error);
    res.status(500).json({ error: 'Failed to create order' });
  }
});

// Payment completion page
app.get('/orders/:id/complete', async (req, res) => {
  try {
    const order = await prisma.order.findUnique({
      where: { id: req.params.id }
    });

    if (!order) {
      return res.status(404).send('Order not found');
    }

    const payment = await c2c.getPayment(order.paymentId);

    if (payment.status === 'completed') {
      res.send(`
        <h1>Order Complete!</h1>
        <p>Order ID: ${order.id}</p>
        <p>Amount: $${(order.amount / 100).toFixed(2)}</p>
        <p>Status: Paid</p>
      `);
    } else {
      res.send('Payment is still processing...');
    }

  } catch (error) {
    console.error('Error:', error);
    res.status(500).send('Error retrieving order');
  }
});

// Webhook handler
app.post('/webhooks/card2crypto', async (req, res) => {
  const signature = req.headers['x-card2crypto-signature'];

  // Verify signature
  const expectedSignature = crypto
    .createHmac('sha256', process.env.CARD2CRYPTO_WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature))) {
    return res.status(401).send('Invalid signature');
  }

  res.status(200).send('OK');

  // Process webhook
  const { event, payment } = req.body;

  if (event === 'payment.completed') {
    await prisma.order.update({
      where: { id: payment.metadata.order_id },
      data: { status: 'paid' }
    });

    console.log(`Order ${payment.metadata.order_id} marked as paid`);
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});
```

## Error Handling

### Robust Error Handling

```javascript theme={null}
class Card2CryptoError extends Error {
  constructor(message, statusCode, response) {
    super(message);
    this.name = 'Card2CryptoError';
    this.statusCode = statusCode;
    this.response = response;
  }
}

async function createPayment(data) {
  try {
    const response = await fetch('https://card2crypto.cc/api/v1/payments', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    });

    const result = await response.json();

    if (!response.ok) {
      throw new Card2CryptoError(
        result.error || 'Payment creation failed',
        response.status,
        result
      );
    }

    return result.data;

  } catch (error) {
    if (error instanceof Card2CryptoError) {
      throw error;
    }
    throw new Card2CryptoError('Network error', 500, null);
  }
}

// Usage
app.post('/create-payment', async (req, res) => {
  try {
    const payment = await createPayment({
      amount: req.body.amount,
      currency: 'usd',
      return_url: 'https://yoursite.com/success'
    });

    res.json(payment);

  } catch (error) {
    if (error instanceof Card2CryptoError) {
      console.error(`Card2Crypto API error (${error.statusCode}):`, error.message);
      res.status(error.statusCode).json({ error: error.message });
    } else {
      console.error('Unexpected error:', error);
      res.status(500).json({ error: 'Internal server error' });
    }
  }
});
```

## Testing

### Local Testing with ngrok

```bash theme={null}
# Install ngrok
npm install -g ngrok

# Start your server
node server.js

# In another terminal, start ngrok
ngrok http 3000

# Use the ngrok URL in your webhook settings
# https://abc123.ngrok.io/webhooks/card2crypto
```

### Unit Tests

```javascript theme={null}
// tests/card2crypto.test.js
const Card2Crypto = require('../lib/card2crypto');

describe('Card2Crypto', () => {
  const c2c = new Card2Crypto(process.env.CARD2CRYPTO_API_KEY);

  test('creates payment', async () => {
    const payment = await c2c.createPayment({
      amount: 10000,
      returnUrl: 'https://test.com/success',
      metadata: { test: true }
    });

    expect(payment.id).toBeDefined();
    expect(payment.amount).toBe(10000);
    expect(payment.status).toBe('pending');
  });

  test('retrieves payment', async () => {
    const created = await c2c.createPayment({
      amount: 5000,
      returnUrl: 'https://test.com/success'
    });

    const retrieved = await c2c.getPayment(created.id);

    expect(retrieved.id).toBe(created.id);
    expect(retrieved.amount).toBe(5000);
  });
});
```

## Best Practices

1. **Never expose API keys in frontend code**
2. **Always verify webhook signatures**
3. **Return 200 OK immediately in webhook handlers**
4. **Process webhooks asynchronously**
5. **Implement idempotency for critical operations**
6. **Log all payment-related events**
7. **Use environment variables for secrets**
8. **Validate all user input**
9. **Handle all payment statuses**
10. **Test with real payments before going live**

## Next Steps

<CardGroup cols={2}>
  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Complete API documentation
  </Card>

  <Card title="Webhook Security" icon="shield-check" href="/webhooks/security">
    Learn about webhook verification
  </Card>

  <Card title="PHP Guide" icon="php" href="/guides/php">
    Integration guide for PHP
  </Card>

  <Card title="Testing Guide" icon="flask" href="/resources/testing">
    How to test your integration
  </Card>
</CardGroup>
