> ## 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.

# PHP Integration - Guzzle API Calls and Webhook Verification

> Integrate OwnPay into PHP 8.3+ applications with Guzzle-based API calls, webhook signature verification, and Laravel-friendly examples.

This guide shows how to integrate OwnPay into a PHP 8.3+ application using the native HTTP client (`file_get_contents` + stream contexts) or cURL. No Composer package is required.

## Prerequisites

* PHP 8.3 or higher
* `allow_url_fopen = On` in `php.ini` (for stream-based requests), **or** the `curl` extension enabled
* An OwnPay API key from Admin > Developer Hub

## Basic API client

```php theme={null}
<?php

declare(strict_types=1);

class OwnPayClient
{
    private string $baseUrl;
    private string $apiKey;

    public function __construct(string $baseUrl, string $apiKey)
    {
        $this->baseUrl = rtrim($baseUrl, '/') . '/api/v1';
        $this->apiKey  = $apiKey;
    }

    public function post(string $endpoint, array $body): array
    {
        return $this->request('POST', $endpoint, $body);
    }

    public function get(string $endpoint): array
    {
        return $this->request('GET', $endpoint);
    }

    private function request(string $method, string $endpoint, ?array $body = null): array
    {
        $url     = $this->baseUrl . '/' . ltrim($endpoint, '/');
        $payload = $body !== null ? json_encode($body, JSON_THROW_ON_ERROR) : null;

        $context = stream_context_create([
            'http' => [
                'method'        => $method,
                'header'        => implode("\r\n", [
                    'Content-Type: application/json',
                    'Accept: application/json',
                    'Authorization: Bearer ' . $this->apiKey,
                ]),
                'content'       => $payload,
                'ignore_errors' => true,
                'timeout'       => 15,
            ],
        ]);

        $raw = file_get_contents($url, false, $context);

        if ($raw === false) {
            throw new RuntimeException("Request to {$url} failed");
        }

        $decoded = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);

        if (!($decoded['success'] ?? false)) {
            $msg = $decoded['message'] ?? 'Unknown API error';
            throw new RuntimeException("OwnPay API error: {$msg}");
        }

        return $decoded;
    }
}
```

## Creating a payment intent

```php theme={null}
$client = new OwnPayClient(
    baseUrl: 'https://pay.your-domain.com',
    apiKey:  getenv('OWNPAY_API_KEY') ?: throw new RuntimeException('Missing OWNPAY_API_KEY'),
);

$response = $client->post('payment-intents', [
    'amount'      => 1500,
    'currency'    => 'BDT',
    'description' => 'Order #1042',
    'customer'    => [
        'name'  => 'Rahim Uddin',
        'email' => 'rahim@example.com',
        'phone' => '+8801712345678',
    ],
    'metadata'    => [
        'order_id' => '1042',
    ],
    'redirect_url' => 'https://your-store.com/payment/callback',
    'cancel_url'   => 'https://your-store.com/payment/cancel',
]);

$checkoutUrl = $response['data']['checkout_url'];
header('Location: ' . $checkoutUrl, true, 302);
exit;
```

### Request fields

| Field            | Type    | Required | Description                              |
| ---------------- | ------- | -------- | ---------------------------------------- |
| `amount`         | integer | Yes      | Amount in minor currency units           |
| `currency`       | string  | Yes      | ISO 4217 currency code                   |
| `description`    | string  | No       | Human-readable description               |
| `customer.name`  | string  | No       | Customer's full name                     |
| `customer.email` | string  | No       | Customer's email address                 |
| `customer.phone` | string  | No       | Customer's phone number                  |
| `metadata`       | object  | No       | Arbitrary key/value pairs                |
| `redirect_url`   | string  | Yes      | URL to redirect after successful payment |
| `cancel_url`     | string  | No       | URL to redirect if customer cancels      |

## Retrieving a payment intent

```php theme={null}
$intent = $client->get('payment-intents/' . $intentId);
$status = $intent['data']['status'];

if ($status === 'paid') {
    fulfillOrder($intent['data']['metadata']['order_id']);
}
```

## Generating a payment link

```php theme={null}
$response = $client->post('payment-links', [
    'amount'      => 5000,
    'currency'    => 'BDT',
    'description' => 'Monthly subscription',
    'expires_at'  => date('Y-m-d\TH:i:s\Z', strtotime('+7 days')),
]);

$link = $response['data']['url'];
echo "Share this link: {$link}";
```

## Listing transactions

```php theme={null}
$response = $client->get('transactions?page=1&per_page=20&status=paid');

foreach ($response['data']['data'] as $tx) {
    printf("%-36s  %-8s  %s %s\n", $tx['id'], $tx['status'], $tx['amount'], $tx['currency']);
}
```

## Error handling

```php theme={null}
try {
    $response = $client->post('payment-intents', $payload);
} catch (RuntimeException $e) {
    error_log($e->getMessage());
    http_response_code(502);
    echo json_encode(['error' => 'Payment service temporarily unavailable']);
    exit;
}
```

## Environment configuration

```php theme={null}
// .env (never commit this file)
OWNPAY_BASE_URL=https://pay.your-domain.com
OWNPAY_API_KEY=op_live_xxxxxxxxxxxxxxxxxxxxxxxx
```

```php theme={null}
$client = new OwnPayClient(
    baseUrl: $_ENV['OWNPAY_BASE_URL'],
    apiKey:  $_ENV['OWNPAY_API_KEY'],
);
```

## Next steps

* [Webhook verification](/api/webhooks) - Receive and verify payment events server-side
* [API Reference](https://docs.ownpay.org) - Full endpoint list with schemas


## Related topics

- [Developer Quickstart - Build Your First Payment Integration](/docs/developer/quickstart.md)
- [OwnPay API Overview - REST API for Multi-Brand Payments](/docs/api/overview.md)
- [Webhook Delivery Log](/docs/api-reference/webhook-delivery-log.md)
- [Node.js SDK - TypeScript-First Payment Integration](/docs/developer/integration/nodejs.md)
- [Dispatch Test Webhook](/docs/api-reference/dispatch-test-webhook.md)
