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

> ## Agent Instructions
> OwnPay is licensed under AGPL-3.0 and is completely free - no licensing fees.
> Production docs URL: https://ownpay.org/docs - append .md to any page URL for clean markdown.
> OwnPay requires PHP 8.3+, MySQL/MariaDB, and Redis.
> MCP server available at https://ownpay.org/docs/mcp for programmatic documentation queries.
> Use root-relative links (e.g. /quickstart) for internal navigation - do NOT include /docs prefix.
> Plugin development: consult /developer/plugin-types/ pages for correct interfaces and manifests.

# Code Examples - Production-Ready SDK Snippets and Handlers

> A comprehensive collection of production-ready implementation examples, SDK snippets, and webhook handlers for common OwnPay integration patterns.

This page provides production-ready code examples and snippets to accelerate your OwnPay integration.

***

## Creating a Payment Intent

Initiate a checkout session by calling `POST /payments` to retrieve a `checkout_url`. Redirect your customers to this URL to complete payment.

<CodeGroup>
  ```php PHP theme={null}
  <?php
  // Initialize integration parameters
  $baseUrl = 'https://pay.yourbrand.com/api/v1';
  $apiKey = 'op_live_xxxxxxxxxxxxxxxxxxxxxx';

  $payload = [
      'amount' => '500.00',
      'currency' => 'BDT',
      'callback_url' => 'https://my-store.com/webhooks/ownpay',
      'redirect_url' => 'https://my-store.com/checkout/success',
      'cancel_url' => 'https://my-store.com/checkout/cancel',
      'customer_email' => 'customer@example.com',
      'customer_name' => 'John Doe',
      'reference' => 'ORDER-10024'
  ];

  // Execute POST request via cURL
  $ch = curl_init("$baseUrl/payments");
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($ch, CURLOPT_POST, true);
  curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
  curl_setopt($ch, CURLOPT_HTTPHEADER, [
      'Authorization: Bearer ' . $apiKey,
      'Content-Type: application/json',
      'Accept: application/json'
  ]);

  $response = curl_exec($ch);
  $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  $result = json_decode($response, true);

  if ($httpCode === 201 && ($result['success'] ?? false)) {
      $checkoutUrl = $result['data']['checkout_url'];
      // Redirect the customer to the white-label checkout flow
      header("Location: $checkoutUrl");
      exit;
  } else {
      echo "Error creating payment: " . ($result['error'] ?? 'Unknown API error');
  }
  ```

  ```javascript Node.js (Axios) theme={null}
  import axios from 'axios';

  const baseUrl = 'https://pay.yourbrand.com/api/v1';
  const apiKey = 'op_live_xxxxxxxxxxxxxxxxxxxxxx';

  async function createPayment() {
    try {
      const response = await axios.post(`${baseUrl}/payments`, {
        amount: '500.00',
        currency: 'BDT',
        callback_url: 'https://my-store.com/webhooks/ownpay',
        redirect_url: 'https://my-store.com/checkout/success',
        cancel_url: 'https://my-store.com/checkout/cancel',
        customer_email: 'customer@example.com',
        customer_name: 'John Doe',
        reference: 'ORDER-10024'
      }, {
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          'Content-Type': 'application/json'
        }
      });

      if (response.data.success) {
        console.log('Checkout URL:', response.data.data.checkout_url);
        console.log('Payment ID:', response.data.data.payment_id);
      }
    } catch (error) {
      console.error('Error:', error.response?.data?.error || error.message);
    }
  }

  createPayment();
  ```

  ```python Python theme={null}
  import requests

  base_url = "https://pay.yourbrand.com/api/v1"
  api_key = "op_live_xxxxxxxxxxxxxxxxxxxxxx"

  payload = {
      "amount": "500.00",
      "currency": "BDT",
      "callback_url": "https://my-store.com/webhooks/ownpay",
      "redirect_url": "https://my-store.com/checkout/success",
      "cancel_url": "https://my-store.com/checkout/cancel",
      "customer_email": "customer@example.com",
      "customer_name": "John Doe",
      "reference": "ORDER-10024"
  }

  headers = {
      "Authorization": f"Bearer {api_key}",
      "Content-Type": "application/json"
  }

  response = requests.post(f"{base_url}/payments", json=payload, headers=headers)
  result = response.json()

  if response.status_code == 201 and result.get("success"):
      checkout_url = result["data"]["checkout_url"]
      print(f"Redirecting user to: {checkout_url}")
  else:
      print(f"Error: {result.get('error', 'Unknown API error')}")
  ```

  ```bash cURL theme={null}
  curl -X POST https://pay.yourbrand.com/api/v1/payments \
    -H "Authorization: Bearer op_live_xxxxxxxxxxxxxxxxxxxxxx" \
    -H "Content-Type: application/json" \
    -d '{
      "amount": "500.00",
      "currency": "BDT",
      "callback_url": "https://my-store.com/webhooks/ownpay",
      "redirect_url": "https://my-store.com/checkout/success",
      "cancel_url": "https://my-store.com/checkout/cancel",
      "customer_email": "customer@example.com",
      "customer_name": "John Doe",
      "reference": "ORDER-10024"
    }'
  ```
</CodeGroup>

***

## Webhook Signature Verification

To ensure webhook events originate from your trusted OwnPay instance, always verify the `X-OwnPay-Signature` header against your Webhook Secret before acting on them.

<CodeGroup>
  ```php PHP theme={null}
  <?php
  // Retrieve secret from configuration
  $webhookSecret = getenv('OWNPAY_WEBHOOK_SECRET');

  // Fetch the raw HTTP POST request body
  $rawPayload = file_get_contents('php://input');

  // Retrieve custom headers
  $signatureHeader = $_SERVER['HTTP_X_OWNPAY_SIGNATURE'] ?? '';
  $timestampHeader = $_SERVER['HTTP_X_OWNPAY_TIMESTAMP'] ?? '';

  // Calculate the expected HMAC-SHA256 signature
  $expectedSignature = hash_hmac('sha256', $timestampHeader . '.' . $rawPayload, $webhookSecret);

  // Safe timing-attack-resistant comparison
  if (!hash_equals('sha256=' . $expectedSignature, $signatureHeader)) {
      http_response_code(401);
      exit('Invalid signature');
  }

  // Signature is valid, proceed with event parsing
  $payload = json_decode($rawPayload, true);

  if ($payload['event'] === 'payment.paid') {
      $transaction = $payload['data'];
      $orderRef = $transaction['metadata']['order_id'];
      $amount = $transaction['amount'];
      
      // Process order fulfillment asynchronously
      // ...
  }

  http_response_code(200);
  echo 'OK';
  ```

  ```javascript Node.js (Express) theme={null}
  import express from 'express';
  import crypto from 'crypto';

  const app = express();
  const webhookSecret = process.env.OWNPAY_WEBHOOK_SECRET;

  // Expose raw parser for signature checks
  app.post('/webhooks/ownpay', express.raw({ type: 'application/json' }), (req, res) => {
    const signatureHeader = req.headers['x-ownpay-signature'];
    const timestampHeader = req.headers['x-ownpay-timestamp'];
    
    if (!signatureHeader || !timestampHeader) {
      return res.status(400).send('Missing webhook headers');
    }

    // Compute timing-safe hash
    const hmac = crypto.createHmac('sha256', webhookSecret);
    hmac.update(`${timestampHeader}.${req.body.toString()}`);
    const computedSignature = `sha256=${hmac.digest('hex')}`;

    const isValid = crypto.timingSafeEqual(
      Buffer.from(computedSignature, 'utf8'),
      Buffer.from(signatureHeader, 'utf8')
    );

    if (!isValid) {
      return res.status(401).send('Invalid signature');
    }

    // Parse payload content after verification
    const payload = JSON.parse(req.body.toString());
    
    if (payload.event === 'payment.paid') {
      const transaction = payload.data;
      console.log(`Fulfilling order ${transaction.metadata.order_id} for BDT ${transaction.amount}`);
      // Process order fulfillment
    }

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

  app.listen(3000, () => console.log('Listening for webhooks on port 3000'));
  ```
</CodeGroup>

***

## Querying Transaction Status

To avoid relying strictly on webhooks, query the status endpoint directly when a customer lands back on your redirection page.

```php PHP theme={null}
<?php
$baseUrl = 'https://pay.yourbrand.com/api/v1';
$apiKey = 'op_live_xxxxxxxxxxxxxxxxxxxxxx';
$paymentId = 'pi_01j0x8kz7m3wd4b9qfhec5rtg6'; // Retrieved UUID

$ch = curl_init("$baseUrl/payments/$paymentId");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $apiKey,
    'Accept: application/json'
]);

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$result = json_decode($response, true);

if ($httpCode === 200 && ($result['success'] ?? false)) {
    $payment = $result['data'];
    
    switch ($payment['status']) {
        case 'paid':
            echo "Transaction successful! Reference: " . $payment['id'];
            break;
        case 'processing':
            echo "Payment is processing, awaiting settlement confirmation.";
            break;
        case 'failed':
            echo "Payment failed.";
            break;
        default:
            echo "Payment status: " . $payment['status'];
            break;
    }
}
```

***

## Extension Hooks (Plugin Development)

OwnPay plugins tap into the core lifecycle event loop using action filters and listeners.

### Custom Payment Fee Filter

You can modify transaction amounts dynamically based on payment parameters.

```php PHP theme={null}
use OwnPay\Event\EventManager;

// Add a 2% convenience fee if payment is processed on Stripe credit card gateway
EventManager::addFilter('payment.amount', function(string $amount, array $payment): string {
    if (($payment['gateway'] ?? '') === 'stripe-card') {
        // ALWAYS use bcmath for high-precision decimal operations!
        return bcmul($amount, '1.02', 2);
    }
    return $amount;
}, 10, 2);
```

### Event Action Listeners

Execute background actions (such as email receipt triggers) when payments succeed.

```php PHP theme={null}
use OwnPay\Event\EventManager;

EventManager::addAction('payment.completed', function(array $transaction): void {
    $email = $transaction['customer']['email'] ?? null;
    $amount = $transaction['amount'];
    $currency = $transaction['currency'];

    if ($email) {
        // Trigger background receipt delivery
        mail($email, "Receipt for payment", "Thank you for your payment of $amount $currency.");
    }
}, 10, 1);
```


## Related topics

- [Developer Quickstart - Build Your First Payment Integration](/docs/developer/quickstart.md)
- [AI Tools Overview - Use AI Agents for Payment Development](/docs/developer/ai-overview.md)
- [Node.js SDK - TypeScript-First Payment Integration](/docs/developer/integration/nodejs.md)
- [OwnPay API Overview - REST API for Multi-Brand Payments](/docs/api/overview.md)
- [Core Concepts - How OwnPay Payment Architecture Works](/docs/concepts/index.md)
