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

# PHP SDK

> Official PHP SDK for PayBridgeNP. Requires PHP 7.4+.

## Installation

```bash theme={null}
composer require paybridge-np/sdk
```

## Initialization

```php theme={null}
use PayBridgeNP\PayBridgeNP;

$paybridgenp = new PayBridgeNP([
    'apiKey'     => getenv('PAYBRIDGENP_API_KEY'), // sk_test_... or sk_live_...
    'baseUrl'    => 'https://api.paybridgenp.com', // optional
    'timeout'    => 30,                            // optional, seconds. default: 30
    'maxRetries' => 2,                             // optional. default: 2
]);
```

***

## `$paybridgenp->checkout`

### `checkout->create(array $params)`

Creates a checkout session.

```php theme={null}
$session = $paybridgenp->checkout->create([
    'amount'    => 10000,                              // required - paisa (NPR × 100)
    'returnUrl' => 'https://yoursite.com/success',    // required
    'cancelUrl' => 'https://yoursite.com/cart',       // optional
    'provider'  => 'khalti',                          // optional - omit to let customer pick
    'currency'  => 'NPR',                             // optional, default: "NPR"
    'metadata'  => ['orderId' => 'ORD-001'],          // optional
]);

echo $session['checkout_url'];  // redirect customer here
echo $session['id'];            // cs_...
echo $session['expires_at'];    // ISO timestamp
```

**Parameters:**

| Name        | Type     | Required | Description                                    |
| ----------- | -------- | -------- | ---------------------------------------------- |
| `amount`    | `int`    | Yes      | In paisa. NPR 100.00 = `10000`                 |
| `returnUrl` | `string` | Yes      | Redirect URL after payment                     |
| `cancelUrl` | `string` | No       | Redirect URL on cancellation                   |
| `provider`  | `string` | No       | `"esewa"`, `"khalti"`, `"fonepay"`             |
| `currency`  | `string` | No       | Default: `"NPR"`                               |
| `metadata`  | `array`  | No       | Passed through to webhooks and payment records |

### `checkout->expire(string $id)`

Marks a session as expired so it can no longer accept payment. Use this when you mint a fresh session for a logical purchase that already had one outstanding (e.g. a customer requesting a new payment link), so the old URL stops being payable immediately rather than waiting for its 30-minute TTL.

```php theme={null}
$paybridgenp->checkout->expire('cs_6f2jHn2…');
```

Idempotent: calling on an already-terminal session is a no-op that returns the current row state without error.

### `checkout->get(string $id)`

Fetches a checkout session by ID: its current status, amount, customer, and any collected address. Read-only (sessions are created with `checkout->create`).

```php theme={null}
$session = $paybridgenp->checkout->get('cs_6f2jHn2…');

echo $session['status'];        // "pending" | "initiated" | "success" | ...
echo $session['amount'];        // paisa
echo $session['customerName'];  // camelCase on the read shape
```

Note: the read shape uses camelCase keys (`customerName`, `expiresAt`, ...), unlike the snake\_case `checkout->create` response.

### `checkout->list(array $params = [])`

Lists checkout sessions for the project, newest first.

```php theme={null}
$response = $paybridgenp->checkout->list([
    'limit'  => 20,         // optional, 1 to 100 (default 25)
    'offset' => 0,          // optional
    'status' => 'success',  // optional filter
]);

echo $response['meta']['total'];
```

***

## `$paybridgenp->paymentLinks`

Reusable hosted payment pages. Methods need an API key with the `links:read` or `links:write` scope.

### `paymentLinks->create(array $params)`

```php theme={null}
$link = $paybridgenp->paymentLinks->create([
    'title'   => 'Donation',  // required
    'amount'  => 50000,       // paisa. Omit for a customer-entered amount (use minAmount/maxAmount)
    'maxUses' => 100,         // optional
]);

echo $link['url']; // public hosted payment page
```

### `paymentLinks->list(array $params = [])`

```php theme={null}
$response = $paybridgenp->paymentLinks->list([
    'active' => true, // optional filter
    'limit'  => 20,   // optional
]);
```

### `paymentLinks->get(string $id)`

Returns the link plus aggregated view and conversion stats.

```php theme={null}
$link = $paybridgenp->paymentLinks->get('lnk_…');

echo $link['stats']['views'];
echo $link['stats']['conversion_rate'];
```

### `paymentLinks->update(string $id, array $params)`

Updates editable fields. Only the keys you pass are changed.

```php theme={null}
$paybridgenp->paymentLinks->update('lnk_…', ['active' => false]);
```

### `paymentLinks->cancel(string $id)`

Deactivates a link so it can no longer accept payments, while keeping it and its history for your records. The recommended way to retire a link that has already been used.

```php theme={null}
$paybridgenp->paymentLinks->cancel('lnk_…');
```

### `paymentLinks->delete(string $id)`

Permanently deletes a link. Allowed only when the link has never been used. Otherwise it returns `422`; cancel it instead.

```php theme={null}
$paybridgenp->paymentLinks->delete('lnk_…');
```

***

## `$paybridgenp->payments`

### `payments->list(array $params = [])`

```php theme={null}
$response = $paybridgenp->payments->list([
    'limit'  => 20,  // optional, default 20, max 100
    'offset' => 0,   // optional, default 0
]);

echo $response['meta']['total'];

foreach ($response['data'] as $payment) {
    echo $payment['id'] . ' ' . $payment['amount'] . ' ' . $payment['status'];
}
```

### `payments->get(string $id)`

```php theme={null}
$payment = $paybridgenp->payments->get('pay_6f2jHn2...');

echo $payment['status'];       // "success" | "failed"
echo $payment['amount'];       // paisa
echo $payment['provider'];     // "esewa" | "khalti" | ...
echo $payment['provider_ref']; // provider's transaction reference
```

***

## `$paybridgenp->webhooks`

### `webhooks->create(array $params)`

```php theme={null}
$endpoint = $paybridgenp->webhooks->create([
    'url'    => 'https://yoursite.com/webhooks/paybridgenp',
    'events' => ['payment.succeeded', 'payment.failed'], // optional - omit for all
]);

echo $endpoint['signing_secret']; // save this - shown only once
```

### `webhooks->list()`

```php theme={null}
$response = $paybridgenp->webhooks->list();
foreach ($response['data'] as $wh) {
    echo $wh['id'] . ' ' . $wh['url'];
}
```

### `webhooks->delete(string $id)`

```php theme={null}
$paybridgenp->webhooks->delete('wh_...');
```

### `PayBridgeNP::constructEvent(string $body, string $signature, string $secret)` (static)

Verifies a webhook signature and returns the parsed event. Use this in your webhook handler.

```php theme={null}
use PayBridgeNP\PayBridgeNP;
use PayBridgeNP\Exceptions\SignatureVerificationException;

$body      = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAYBRIDGENP_SIGNATURE'] ?? '';

try {
    $event = PayBridgeNP::constructEvent($body, $signature, getenv('PAYBRIDGENP_WEBHOOK_SECRET'));
} catch (SignatureVerificationException $e) {
    http_response_code(400);
    exit('Invalid signature');
}

switch ($event['type']) {
    case 'payment.succeeded':
        // $event['data']['id'], $event['data']['amount'], $event['data']['metadata']
        fulfillOrder($event['data']['metadata']['orderId'] ?? null);
        break;

    case 'payment.failed':
        notifyOrderFailed($event['data']['session_id']);
        break;
}

http_response_code(200);
echo json_encode(['received' => true]);
```

Throws `SignatureVerificationException` if:

* The signature header is missing or malformed
* The HMAC doesn't match
* The timestamp is more than 5 minutes old (replay attack protection)

***

## `$paybridgenp->billing`

The billing methods provide access to plans, customers, subscriptions, and invoices. Billing API access requires the Growth plan or higher.

### Plans

```php theme={null}
// Create a plan
$plan = $paybridgenp->plans->create([
    'name'          => 'Pro Monthly',
    'amount'        => 99900,   // paisa - NPR 999/month
    'intervalUnit'  => 'month',
    'intervalCount' => 1,
    'trialDays'     => 14,      // optional
]);

// List plans
$response = $paybridgenp->plans->list();

// Get a plan
$plan = $paybridgenp->plans->get('plan_...');

// Update a plan
$updated = $paybridgenp->plans->update('plan_...', ['name' => 'Pro Monthly v2']);
```

### Customers

```php theme={null}
// Create a customer
$customer = $paybridgenp->customers->create([
    'name'               => 'Aarav Sharma',
    'email'              => 'aarav@example.com',
    'externalCustomerId' => 'user_123',  // optional
]);

// List customers
$response = $paybridgenp->customers->list();

// Get a customer
$customer = $paybridgenp->customers->get('cus_...');

// Update a customer
$updated = $paybridgenp->customers->update('cus_...', ['email' => 'new@example.com']);

// Delete a customer
$paybridgenp->customers->delete('cus_...');
```

### Subscriptions

```php theme={null}
// Create a subscription
$sub = $paybridgenp->subscriptions->create([
    'customerId' => 'cus_...',
    'planId'     => 'plan_...',
]);

// List subscriptions
$response = $paybridgenp->subscriptions->list();

// Get a subscription
$sub = $paybridgenp->subscriptions->get('sub_...');

// Lifecycle actions
$paybridgenp->subscriptions->pause('sub_...');
$paybridgenp->subscriptions->resume('sub_...');
$paybridgenp->subscriptions->cancel('sub_...');

// Change plan
$paybridgenp->subscriptions->changePlan('sub_...', ['newPlanId' => 'plan_...']);
```

### Invoices

```php theme={null}
// List invoices
$response = $paybridgenp->invoices->list([
    'subscriptionId' => 'sub_...',  // optional
]);

// Get an invoice
$invoice = $paybridgenp->invoices->get('inv_...');

echo $invoice['status'];      // "open" | "paid" | "overdue" | "void" | "uncollectible" | "write_off"
echo $invoice['amount_due'];  // paisa
echo $invoice['due_at'];      // ISO timestamp
echo $invoice['hosted_invoice_url']; // hosted payment link
```

***

## Error handling

All SDK methods throw typed exceptions you can catch and inspect:

```php theme={null}
use PayBridgeNP\Exceptions\AuthenticationException;
use PayBridgeNP\Exceptions\PayBridgeException;

try {
    $paybridgenp->checkout->create(['amount' => 1000, 'returnUrl' => '...']);
} catch (AuthenticationException $e) {
    // Invalid API key
    error_log('Check your PAYBRIDGENP_API_KEY');
} catch (PayBridgeException $e) {
    error_log($e->getMessage());    // human-readable message
    error_log($e->getStatusCode()); // HTTP status
    error_log($e->getCode());       // machine-readable code
}
```

### Exception classes

| Class                            | Status | When                            |
| -------------------------------- | ------ | ------------------------------- |
| `AuthenticationException`        | 401    | Invalid or missing API key      |
| `InvalidRequestException`        | 400    | Bad request parameters          |
| `NotFoundException`              | 404    | Resource not found              |
| `RateLimitException`             | 429    | Too many requests               |
| `PayBridgeException`             | 5xx    | Server error                    |
| `SignatureVerificationException` | -      | Webhook HMAC mismatch or replay |

***

## Framework examples

<AccordionGroup>
  <Accordion title="Laravel">
    ```php theme={null}
    // config/services.php
    'paybridgenp' => [
        'key'    => env('PAYBRIDGENP_API_KEY'),
        'secret' => env('PAYBRIDGENP_WEBHOOK_SECRET'),
    ],
    ```

    ```php theme={null}
    // app/Http/Controllers/CheckoutController.php
    use PayBridgeNP\PayBridgeNP;

    class CheckoutController extends Controller
    {
        public function create(Request $request)
        {
            $paybridgenp = new PayBridgeNP(['apiKey' => config('services.paybridgenp.key')]);

            $session = $paybridgenp->checkout->create([
                'amount'    => $request->amount,
                'returnUrl' => route('checkout.success'),
                'cancelUrl' => route('cart.index'),
                'metadata'  => ['orderId' => $request->order_id],
            ]);

            return redirect($session['checkout_url']);
        }
    }
    ```

    ```php theme={null}
    // app/Http/Controllers/WebhookController.php
    use PayBridgeNP\PayBridgeNP;
    use PayBridgeNP\Exceptions\SignatureVerificationException;

    class WebhookController extends Controller
    {
        public function handle(Request $request)
        {
            try {
                $event = PayBridgeNP::constructEvent(
                    $request->getContent(),
                    $request->header('X-PayBridgeNP-Signature'),
                    config('services.paybridgenp.secret'),
                );
            } catch (SignatureVerificationException $e) {
                return response()->json(['error' => 'Invalid signature'], 400);
            }

            if ($event['type'] === 'payment.succeeded') {
                Order::where('id', $event['data']['metadata']['orderId'] ?? null)
                    ->update(['status' => 'paid']);
            }

            return response()->json(['received' => true]);
        }
    }
    ```
  </Accordion>
</AccordionGroup>
