PayGate

PHP SDK

Official PHP SDK for PayGate.

PHP SDK

The official PHP SDK for PayGate provides a convenient way to access the PayGate API from PHP applications.

Requirements: PHP 8.0 or later. Composer for installation.

Installation

composer require paygate/paygate-php

Quick Start

<?php
require 'vendor/autoload.php';

$paygate = new \PayGate\PayGate('sk_test_...');

// Create a payment
$payment = $paygate->payments->create([
    'amount' => 5000,
    'currency' => 'GHS',
    'payment_method' => 'mobile_money',
    'provider' => 'mtn',
    'phone' => '0241234567'
]);

echo $payment->id;

Configuration

API Key

<?php
// Using environment variable (recommended)
$paygate = new \PayGate\PayGate(getenv('PAYGATE_SECRET_KEY'));

// Or pass directly
$paygate = new \PayGate\PayGate('sk_test_...');

Options

<?php
$paygate = new \PayGate\PayGate('sk_test_...', [
    // API version (defaults to latest)
    'api_version' => '2024-01-01',

    // Request timeout in seconds (default: 30)
    'timeout' => 60,

    // Maximum retries for failed requests (default: 3)
    'max_retries' => 5,

    // Custom base URL (for testing)
    'base_url' => 'https://api.sandbox.paygate.com.gh'
]);

Payments

Create a Payment

$payment = $paygate->payments->create([
    'amount' => 5000,
    'currency' => 'GHS',
    'payment_method' => 'mobile_money',
    'provider' => 'mtn',
    'phone' => '0241234567',
    'description' => 'Order #1234',
    'metadata' => [
        'order_id' => '1234',
        'customer_email' => 'customer@example.com'
    ]
]);

Retrieve a Payment

$payment = $paygate->payments->retrieve('pay_abc123');

List Payments

// Get a page of payments
$payments = $paygate->payments->list([
    'limit' => 10,
    'status' => 'succeeded'
]);

foreach ($payments->data as $payment) {
    echo $payment->id . "\n";
}

// Check for more pages
if ($payments->has_more) {
    $nextPage = $paygate->payments->list([
        'limit' => 10,
        'starting_after' => $payments->data[count($payments->data) - 1]->id
    ]);
}

Cancel a Payment

$payment = $paygate->payments->cancel('pay_abc123');

Customers

Create a Customer

$customer = $paygate->customers->create([
    'email' => 'customer@example.com',
    'name' => 'John Doe',
    'phone' => '0241234567',
    'metadata' => ['user_id' => '123']
]);

Update a Customer

$customer = $paygate->customers->update('cus_abc123', [
    'name' => 'Jane Doe'
]);

Delete a Customer

$paygate->customers->delete('cus_abc123');

Subscriptions

Create a Subscription

$subscription = $paygate->subscriptions->create([
    'customer' => 'cus_abc123',
    'plan' => 'plan_xyz789',
    'payment_method' => [
        'type' => 'mobile_money',
        'phone' => '0241234567',
        'provider' => 'mtn'
    ]
]);

Cancel a Subscription

// Cancel at period end
$paygate->subscriptions->update('sub_abc123', [
    'cancel_at_period_end' => true
]);

// Cancel immediately
$paygate->subscriptions->cancel('sub_abc123');

Webhooks

Verify Webhook Signature

<?php
require 'vendor/autoload.php';

$endpointSecret = 'whsec_...';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PAYGATE_SIGNATURE'];

try {
    $event = \PayGate\Webhook::constructEvent(
        $payload,
        $signature,
        $endpointSecret
    );
} catch (\PayGate\Exception\SignatureVerificationException $e) {
    http_response_code(400);
    echo 'Invalid signature';
    exit();
} catch (\Exception $e) {
    http_response_code(400);
    echo 'Error: ' . $e->getMessage();
    exit();
}

// Handle the event
switch ($event->type) {
    case 'payment.succeeded':
        $payment = $event->data->object;
        error_log("Payment {$payment->id} succeeded!");
        // Fulfill the order
        break;

    case 'payment.failed':
        $payment = $event->data->object;
        error_log("Payment {$payment->id} failed");
        // Notify customer
        break;

    case 'subscription.created':
        $subscription = $event->data->object;
        // Handle new subscription
        break;

    default:
        error_log("Unhandled event type: {$event->type}");
}

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

Laravel Example

<?php
// routes/web.php
Route::post('/webhooks/paygate', [WebhookController::class, 'handle']);

// app/Http/Controllers/WebhookController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use PayGate\Webhook;
use PayGate\Exception\SignatureVerificationException;

class WebhookController extends Controller
{
    public function handle(Request $request)
    {
        $payload = $request->getContent();
        $signature = $request->header('X-PayGate-Signature');
        $secret = config('services.paygate.webhook_secret');

        try {
            $event = Webhook::constructEvent($payload, $signature, $secret);
        } catch (SignatureVerificationException $e) {
            return response('Invalid signature', 400);
        }

        match ($event->type) {
            'payment.succeeded' => $this->handlePaymentSuccess($event->data->object),
            'payment.failed' => $this->handlePaymentFailure($event->data->object),
            'subscription.created' => $this->handleSubscriptionCreated($event->data->object),
            default => null,
        };

        return response()->json(['received' => true]);
    }

    private function handlePaymentSuccess($payment)
    {
        // Fulfill the order
    }

    private function handlePaymentFailure($payment)
    {
        // Notify customer
    }

    private function handleSubscriptionCreated($subscription)
    {
        // Set up subscription
    }
}

Error Handling

<?php
use PayGate\PayGate;
use PayGate\Exception\PayGateException;
use PayGate\Exception\AuthenticationException;
use PayGate\Exception\InvalidRequestException;
use PayGate\Exception\PaymentException;
use PayGate\Exception\RateLimitException;
use PayGate\Exception\ApiException;

$paygate = new PayGate('sk_test_...');

try {
    $payment = $paygate->payments->create([
        'amount' => 5000,
        'currency' => 'GHS',
        'payment_method' => 'mobile_money',
        'provider' => 'mtn',
        'phone' => '0241234567'
    ]);

} catch (AuthenticationException $e) {
    // Invalid API key
    echo 'Check your API key';

} catch (InvalidRequestException $e) {
    // Invalid parameters
    echo 'Invalid parameter: ' . $e->getParam();
    echo 'Message: ' . $e->getMessage();

} catch (PaymentException $e) {
    // Payment-specific error
    switch ($e->getErrorCode()) {
        case 'insufficient_funds':
            echo 'Please top up your account';
            break;
        case 'phone_unreachable':
            echo 'Please check your phone';
            break;
        default:
            echo $e->getMessage();
    }

} catch (RateLimitException $e) {
    // Too many requests
    sleep(60);
    // Retry

} catch (ApiException $e) {
    // Server error
    error_log('PayGate server error: ' . $e->getMessage());

} catch (PayGateException $e) {
    // Generic error
    echo 'Error: ' . $e->getMessage();
}

Error Types

ExceptionDescription
AuthenticationExceptionInvalid API key
InvalidRequestExceptionInvalid parameters
PaymentExceptionPayment-specific error
RateLimitExceptionToo many requests
ApiExceptionServer error

Idempotency

Use idempotency keys to safely retry requests:

$payment = $paygate->payments->create([
    'amount' => 5000,
    'currency' => 'GHS',
    'payment_method' => 'mobile_money',
    'provider' => 'mtn',
    'phone' => '0241234567'
], [
    'idempotency_key' => 'order_1234_payment'
]);

Pagination Helper

// Iterator for all payments
$iterator = $paygate->payments->all(['status' => 'succeeded']);

foreach ($iterator as $payment) {
    echo $payment->id . "\n";
}

// Or collect all into an array (be careful with memory)
$allPayments = iterator_to_array($iterator);

PSR-18 HTTP Client

The SDK supports any PSR-18 compatible HTTP client:

<?php
use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\Psr7\HttpFactory;

$httpClient = new GuzzleClient();
$requestFactory = new HttpFactory();

$paygate = new \PayGate\PayGate('sk_test_...', [
    'http_client' => $httpClient,
    'request_factory' => $requestFactory
]);

Invoices

Create an Invoice

$invoice = $paygate->invoices->create([
    'customer' => 'cus_abc123',
    'items' => [
        ['description' => 'Consulting Services', 'amount' => 50000],
        ['description' => 'Development Work', 'amount' => 150000]
    ],
    'due_date' => '2024-02-15',
    'metadata' => ['project' => 'Website Redesign']
]);

Send an Invoice

// Send invoice via email/SMS
$paygate->invoices->send('inv_abc123');

Pay an Invoice

$invoice = $paygate->invoices->pay('inv_abc123', [
    'payment_method' => [
        'type' => 'mobile_money',
        'phone' => '0241234567',
        'provider' => 'mtn'
    ]
]);

List Invoices

$invoices = $paygate->invoices->list(['status' => 'open', 'limit' => 10]);
foreach ($invoices->data as $invoice) {
    echo "{$invoice->id}: " . ($invoice->amount_due / 100) . " {$invoice->currency}\n";
}

Coupons

Create a Coupon

// Percentage discount
$coupon = $paygate->coupons->create([
    'code' => 'SAVE20',
    'type' => 'percent',
    'percent_off' => 20,
    'duration' => 'repeating',
    'duration_in_months' => 3,
    'max_redemptions' => 100
]);

// Fixed amount discount
$coupon = $paygate->coupons->create([
    'code' => 'FLAT50',
    'type' => 'amount',
    'amount_off' => 5000, // 50 GHS in pesewas
    'currency' => 'GHS',
    'duration' => 'once'
]);

Validate a Coupon

try {
    $validation = $paygate->coupons->validate('SAVE20');
    if ($validation->valid) {
        echo "Discount: {$validation->percent_off}%\n";
    }
} catch (\PayGate\Exception\InvalidRequestException $e) {
    echo "Coupon is invalid or expired\n";
}

List Coupons

$coupons = $paygate->coupons->list(['limit' => 10]);
foreach ($coupons->data as $coupon) {
    echo "{$coupon->code}: {$coupon->times_redeemed}/{$coupon->max_redemptions}\n";
}

QR Codes

Create a Static QR Code

// Reusable payment QR code
$qrCode = $paygate->qrCodes->create([
    'type' => 'static',
    'name' => 'Store Counter QR',
    'description' => 'Scan to pay at checkout'
]);
echo "QR Image URL: {$qrCode->image_url}\n";

Create a Dynamic QR Code

// QR code for specific payment
$qrCode = $paygate->qrCodes->create([
    'type' => 'dynamic',
    'amount' => 5000,
    'currency' => 'GHS',
    'description' => 'Order #1234',
    'expires_in' => 3600 // 1 hour
]);
echo "QR Image URL: {$qrCode->image_url}\n";

Retrieve QR Code Status

$qrCode = $paygate->qrCodes->retrieve('qr_abc123');
echo "Status: {$qrCode->status}\n";
echo "Times Used: {$qrCode->times_used}\n";

Reports

Generate a Report

$report = $paygate->reports->create([
    'type' => 'payments_summary',
    'parameters' => [
        'start_date' => '2024-01-01',
        'end_date' => '2024-01-31',
        'group_by' => 'day'
    ],
    'format' => 'csv'
]);
echo "Report ID: {$report->id}\n";

Retrieve Report Status

do {
    $report = $paygate->reports->retrieve('report_abc123');
    if ($report->status === 'pending') {
        sleep(5);
    }
} while ($report->status === 'pending');

if ($report->status === 'ready') {
    echo "Download URL: {$report->download_url}\n";
}

Available Report Types

$reportTypes = [
    'payments_summary',
    'payouts_summary',
    'settlements',
    'disputes',
    'customers',
    'subscriptions',
    'revenue'
];

Events

List Events

// Get recent events
$events = $paygate->events->list(['limit' => 20]);
foreach ($events->data as $event) {
    echo "{$event->type}: {$event->id}\n";
}

// Filter by type
$paymentEvents = $paygate->events->list([
    'type' => 'payment.succeeded',
    'limit' => 10
]);

Retrieve an Event

$event = $paygate->events->retrieve('evt_abc123');
echo "Type: {$event->type}\n";
print_r($event->data);

Event Types

Common event types include:

  • payment.created
  • payment.succeeded
  • payment.failed
  • payout.created
  • payout.succeeded
  • customer.created
  • subscription.created
  • subscription.cancelled
  • invoice.created
  • invoice.paid
  • dispute.created
  • dispute.closed

Settings

Settlement Schedule

// Get current settlement schedule
$schedule = $paygate->settings->getSettlementSchedule();
echo "Frequency: {$schedule->frequency}\n";
echo "Day of week: {$schedule->day_of_week}\n";

// Update settlement schedule
$schedule = $paygate->settings->updateSettlementSchedule([
    'frequency' => 'weekly',
    'day_of_week' => 'friday'
]);

Transaction Limits

// Get current limits
$limits = $paygate->settings->getTransactionLimits();
echo "Min: {$limits->min_amount}\n";
echo "Max: {$limits->max_amount}\n";
echo "Daily: {$limits->daily_limit}\n";

// Update limits
$limits = $paygate->settings->updateTransactionLimits([
    'min_amount' => 100,        // 1 GHS
    'max_amount' => 10000000,   // 100,000 GHS
    'daily_limit' => 50000000   // 500,000 GHS
]);

Two-Factor Authentication

Setup 2FA

// Generate 2FA secret and QR code
$setup = $paygate->auth->setup2FA();
echo "Secret: {$setup->secret}\n";
echo "QR Code URL: {$setup->qr_code_url}\n";
// Display QR code to user for scanning

Enable 2FA

// Verify and enable 2FA
$result = $paygate->auth->enable2FA([
    'code' => '123456' // Code from authenticator app
]);
if ($result->enabled) {
    echo "2FA enabled successfully\n";
    print_r($result->backup_codes);
}

Verify 2FA Code

// Verify a 2FA code
$result = $paygate->auth->verify2FA(['code' => '123456']);
if ($result->valid) {
    echo "Code is valid\n";
}

Disable 2FA

// Disable 2FA (requires current code)
$paygate->auth->disable2FA(['code' => '123456']);

Regenerate Backup Codes

// Get new backup codes
$result = $paygate->auth->regenerateBackupCodes(['code' => '123456']);
print_r($result->backup_codes);