Creating a Custom Payment Provider

Build custom payment integrations for Openfront using HTTP endpoints

Learn how to create custom payment providers to integrate any payment gateway with Openfront. This guide covers building HTTP endpoints that Openfront can call to process payments.

Overview

Custom payment providers in Openfront work by implementing HTTP endpoints that match Openfront's payment processing interface. When customers make payments, Openfront calls your endpoints to handle the actual payment processing with your chosen gateway.

How Custom Payment Integrations Work

Instead of built-in integrations like "stripe" or "paypal", custom payment providers use HTTP endpoints:

Built-in Integration:

createPaymentFunction: "stripe"

Custom HTTP Integration:

createPaymentFunction: "https://your-api.com/api/create-payment"

When a payment is processed, Openfront sends a standardized payload to your endpoint and expects a specific response format.

Required HTTP Endpoints

Your custom payment provider must implement these endpoints:

Create Payment - /api/create-payment

Purpose: Openfront uses this endpoint to initialize payment sessions when customers start checkout

HTTP Method: POST

Request Body:

FieldTypeDescription
provider{ name: string; credentials: object }Payment provider configuration and API keys
cart{ id: string; total: number; currency: string; customer?: object }Cart information and customer details
amountnumberPayment amount in cents
currencystringCurrency code (USD, EUR, etc.)

Response Format:

FieldTypeDescription
sessionDataobjectPayment session data for frontend (client secrets, etc.)
paymentIdstringUnique payment identifier from your gateway
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { provider, cart, amount, currency } = req.body;

  try {
    // Initialize your gateway with provider credentials
    const gateway = new YourPaymentGateway({
      apiKey: provider.credentials.apiKey,
      environment: provider.credentials.environment
    });

    // Create payment intent/session
    const paymentIntent = await gateway.createPayment({
      amount: amount,
      currency: currency,
      orderId: cart.id,
      customerEmail: cart.customer?.email,
      metadata: {
        cartId: cart.id,
        source: 'openfront'
      }
    });

    return res.json({
      sessionData: {
        paymentIntentId: paymentIntent.id,
        clientSecret: paymentIntent.clientSecret,
        publishableKey: provider.credentials.publishableKey
      },
      paymentId: paymentIntent.id
    });
  } catch (error) {
    return res.status(400).json({ 
      error: `Payment creation failed: ${error.message}` 
    });
  }
}

Capture Payment - /api/capture-payment

Purpose: Openfront uses this endpoint to capture authorized payments after order fulfillment

HTTP Method: POST

Request Body:

FieldTypeDescription
provider{ name: string; credentials: object }Payment provider configuration
paymentIdstringPayment ID to capture
amountnumberAmount to capture in cents

Response Format:

FieldTypeDescription
successbooleanWhether capture was successful
transactionIdstringTransaction/capture ID from gateway
capturedAmountnumberActual amount captured
fees?numberProcessing fees if available
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { provider, paymentId, amount } = req.body;

  try {
    const gateway = new YourPaymentGateway({
      apiKey: provider.credentials.apiKey,
      environment: provider.credentials.environment
    });

    const capture = await gateway.capturePayment({
      paymentId: paymentId,
      amount: amount
    });

    return res.json({
      success: capture.status === 'captured',
      transactionId: capture.transactionId,
      capturedAmount: capture.amount,
      fees: capture.processingFees
    });
  } catch (error) {
    return res.status(400).json({ 
      error: `Capture failed: ${error.message}` 
    });
  }
}

Refund Payment - /api/refund-payment

Purpose: Openfront uses this endpoint to process refunds for captured payments

HTTP Method: POST

Request Body:

FieldTypeDescription
provider{ name: string; credentials: object }Payment provider configuration
paymentIdstringOriginal payment ID to refund
amountnumberRefund amount in cents
reason?stringRefund reason

Response Format:

FieldTypeDescription
successbooleanWhether refund was successful
refundIdstringRefund ID from gateway
refundedAmountnumberActual amount refunded
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { provider, paymentId, amount, reason } = req.body;

  try {
    const gateway = new YourPaymentGateway({
      apiKey: provider.credentials.apiKey,
      environment: provider.credentials.environment
    });

    const refund = await gateway.refundPayment({
      paymentId: paymentId,
      amount: amount,
      reason: reason || 'requested_by_customer'
    });

    return res.json({
      success: refund.status === 'refunded',
      refundId: refund.id,
      refundedAmount: refund.amount
    });
  } catch (error) {
    return res.status(400).json({ 
      error: `Refund failed: ${error.message}` 
    });
  }
}

Get Payment Status - /api/get-payment-status

Purpose: Openfront uses this endpoint to check current payment status for order updates

HTTP Method: POST

Request Body:

FieldTypeDescription
provider{ name: string; credentials: object }Payment provider configuration
paymentIdstringPayment ID to check

Response Format:

FieldTypeDescription
statusstringPayment status: pending, authorized, captured, failed, cancelled
amountnumberPayment amount
currencystringPayment currency
lastUpdatedstringISO timestamp of last update
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { provider, paymentId } = req.body;

  try {
    const gateway = new YourPaymentGateway({
      apiKey: provider.credentials.apiKey,
      environment: provider.credentials.environment
    });

    const payment = await gateway.getPayment(paymentId);

    // Map gateway status to Openfront status
    const statusMap = {
      'created': 'pending',
      'authorized': 'authorized',
      'captured': 'captured',
      'failed': 'failed',
      'cancelled': 'cancelled'
    };

    return res.json({
      status: statusMap[payment.status] || 'pending',
      amount: payment.amount,
      currency: payment.currency,
      lastUpdated: payment.updatedAt.toISOString()
    });
  } catch (error) {
    return res.status(400).json({ 
      error: `Status check failed: ${error.message}` 
    });
  }
}

Purpose: Openfront uses this endpoint to create dashboard links for payment management

HTTP Method: POST

Request Body:

FieldTypeDescription
provider{ name: string; credentials: object }Payment provider configuration
paymentIdstringPayment ID to create link for

Response Format:

FieldTypeDescription
urlstringURL to payment in gateway dashboard
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { provider, paymentId } = req.body;

  try {
    // Generate link to payment gateway dashboard
    const baseUrl = provider.credentials.environment === 'live'
      ? 'https://dashboard.yourgateway.com'
      : 'https://sandbox-dashboard.yourgateway.com';
    
    const dashboardUrl = `${baseUrl}/payments/${paymentId}`;

    return res.json({
      url: dashboardUrl
    });
  } catch (error) {
    return res.status(400).json({ 
      error: `Link generation failed: ${error.message}` 
    });
  }
}

Handle Webhook - /api/handle-webhook

Purpose: Openfront uses this endpoint to process webhook notifications from your payment gateway

HTTP Method: POST

Request Body:

FieldTypeDescription
provider{ name: string; credentials: object }Payment provider configuration
eventobjectWebhook event payload from gateway
headersobjectHTTP headers from webhook request

Response Format:

FieldTypeDescription
verifiedbooleanWhether webhook signature is valid
eventTypestringType of event (payment.captured, payment.failed, etc.)
paymentIdstringPayment ID from the event
shouldUpdatePaymentbooleanWhether Openfront should update payment status
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const { provider, event, headers } = req.body;

  try {
    // Verify webhook signature
    const gateway = new YourPaymentGateway({
      apiKey: provider.credentials.apiKey,
      webhookSecret: provider.credentials.webhookSecret
    });

    const signature = headers['x-gateway-signature'];
    const isValid = gateway.verifyWebhookSignature(
      JSON.stringify(event),
      signature
    );

    if (!isValid) {
      return res.status(403).json({ 
        verified: false,
        error: 'Invalid webhook signature' 
      });
    }

    // Process webhook event
    const eventType = event.type;
    const paymentId = event.data.payment.id;

    // Determine if Openfront should update payment status
    const updateEvents = [
      'payment.captured',
      'payment.failed', 
      'payment.cancelled',
      'payment.refunded'
    ];

    return res.json({
      verified: true,
      eventType: eventType,
      paymentId: paymentId,
      shouldUpdatePayment: updateEvents.includes(eventType)
    });
  } catch (error) {
    return res.status(400).json({
      verified: false,
      error: error.message
    });
  }
}

Testing Your Custom Payment Provider

Step 1: Set Up Demo Integration

Before connecting to your live payment gateway, set up a demo integration for testing:

  1. Create demo endpoints that return mock responses
  2. Test the complete flow with sample data
  3. Verify webhook handling with test events
  4. Replace URLs with live endpoints once tested

Step 2: Create Payment Provider Platform

In Openfront admin:

  1. Navigate to Settings > Payment Providers
  2. Click "Create Provider Platform"
  3. Configure your platform:
Platform Configuration:
├── Name: "My Custom Gateway"
├── createPaymentFunction: "https://your-api.com/api/create-payment"
├── capturePaymentFunction: "https://your-api.com/api/capture-payment"
├── refundPaymentFunction: "https://your-api.com/api/refund-payment"
├── getPaymentStatusFunction: "https://your-api.com/api/get-payment-status"
├── generatePaymentLinkFunction: "https://your-api.com/api/generate-payment-link"
└── handleWebhookFunction: "https://your-api.com/api/handle-webhook"

Step 3: Create Payment Provider Instance

  1. Click "Create Provider" under your platform
  2. Configure credentials:
{
  "apiKey": "your-gateway-api-key",
  "publishableKey": "your-gateway-publishable-key",
  "webhookSecret": "your-webhook-secret",
  "environment": "sandbox"
}
  1. Assign to regions where this provider should be available
  2. Test payments in your store

Step 4: Production Deployment

Once testing is complete:

  1. Update environment credentials to production
  2. Switch endpoint URLs to production endpoints
  3. Configure webhook URLs in your gateway dashboard
  4. Monitor payments and error rates

Error Handling Best Practices

Endpoint Error Responses

Always return appropriate HTTP status codes:

// Success
return res.status(200).json({ success: true, ... });

// Client error (invalid request)
return res.status(400).json({ error: "Invalid amount" });

// Authentication error
return res.status(401).json({ error: "Invalid API key" });

// Server error
return res.status(500).json({ error: "Gateway unavailable" });

Webhook Security

Always verify webhook signatures to prevent fraud:

const isValid = gateway.verifyWebhookSignature(payload, signature);
if (!isValid) {
  return res.status(403).json({ 
    verified: false,
    error: 'Invalid signature' 
  });
}

Logging and Monitoring

Log important events without exposing sensitive data:

// Good: Log without sensitive data
console.log(`Payment ${paymentId} created for amount ${amount}`);

// Bad: Never log credentials
// console.log(`API Key: ${apiKey}`);

Your custom payment provider is now ready to process payments through Openfront while maintaining security and reliability standards.