Skip to content
Ken edited this page Dec 13, 2025 · 2 revisions

PayZephyr Documentation

Welcome to the PayZephyr documentation! This guide will help you get started and make the most of the package.

👋 New to PayZephyr? Start with our Getting Started Guide - a complete beginner-friendly tutorial!


📚 Table of Contents

Getting Started

  1. Getting Started GuideStart here! - Complete beginner tutorial
  2. Installation & Quick Start
  3. Configuration Guide
  4. Basic Usage Examples

Core Documentation

  1. Architecture Overview - System design and components
  2. API Reference - Complete API documentation
  3. Payment Providers - Detailed provider information
  4. Webhook Integration - Complete webhook guide

Advanced Topics

  1. Transaction Logging
  2. Error Handling
  3. Testing Your Integration

Development

  1. Contributing Guide for BeginnersNew to open source? - Step-by-step contribution tutorial
  2. Contributing Guidelines - Detailed technical guide
  3. Changelog
  4. API Reference - Complete API documentation

🚀 Quick Links

By Use Case

I want to...

By Provider


Configuration

Environment Setup

# Core Settings
PAYMENTS_DEFAULT_PROVIDER=paystack
PAYMENTS_FALLBACK_PROVIDER=stripe
PAYMENTS_LOGGING_ENABLED=true

# Paystack Configuration
PAYSTACK_SECRET_KEY=sk_test_xxxxxxxxxxxxx
PAYSTACK_PUBLIC_KEY=pk_test_xxxxxxxxxxxxx
PAYSTACK_ENABLED=true

# Stripe Configuration
STRIPE_SECRET_KEY=sk_test_xxxxxxxxxxxxx
STRIPE_PUBLIC_KEY=pk_test_xxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx
STRIPE_ENABLED=true

# See providers.md for complete configuration options

Configuration File

The main configuration is in config/payments.php:

return [
    'default' => env('PAYMENTS_DEFAULT_PROVIDER', 'paystack'),
    'fallback' => env('PAYMENTS_FALLBACK_PROVIDER', 'stripe'),
    
    'providers' => [
        'paystack' => [/* ... */],
        'stripe' => [/* ... */],
        // ... more providers
    ],
    
    'webhook' => [
        'verify_signature' => true, // ALWAYS true in production
        'path' => '/payments/webhook',
    ],
    
    'logging' => [
        'enabled' => true,
        'table' => 'payment_transactions',
    ],
];

📖 See providers.md for detailed provider configuration


Basic Usage

1. Simple Payment

use KenDeNigerian\PayZephyr\Facades\Payment;

// Builder methods can be chained in any order
// redirect() must be called last to execute
return Payment::amount(10000)
    ->email('customer@example.com')
    ->callback(route('payment.callback'))
    ->redirect();

2. With Metadata

// All builder methods are chainable in any order
return Payment::amount(50000)
    ->currency('NGN')
    ->email('customer@example.com')
    ->reference('ORDER_' . time())
    ->idempotency(Str::uuid()->toString()) // Prevent double billing
    ->metadata([
        'order_id' => 12345,
        'customer_id' => auth()->id(),
    ])
    ->description('Premium Plan Subscription')
    ->redirect(); // Must be called last

3. Multiple Providers

// Try Paystack, fallback to Stripe
// with() or using() can be called anywhere in the chain
return Payment::amount(10000)
    ->email('customer@example.com')
    ->with(['paystack', 'stripe']) // or ->using(['paystack', 'stripe'])
    ->redirect(); // Must be called last

4. Verify Payment

public function callback(Request $request)
{
    $reference = $request->input('reference');
    
    try {
        // verify() is a standalone method, NOT chainable
        // It searches all providers if no provider is specified
        $verification = Payment::verify($reference);
        
        // Or specify a provider explicitly
        // $verification = Payment::verify($reference, 'paystack');
        
        if ($verification->isSuccessful()) {
            // Update your database
            Order::where('reference', $reference)
                ->update(['status' => 'paid']);
            
            return view('payment.success');
        }
        
        return view('payment.failed');
        
    } catch (\Exception $e) {
        logger()->error('Verification failed', [
            'reference' => $reference,
            'error' => $e->getMessage(),
        ]);
        
        return view('payment.error');
    }
}

5. Using Helper Function

// The payment() helper works exactly like the Payment facade
// All builder methods are chainable in any order
return payment()
    ->amount(10000)
    ->email('customer@example.com')
    ->redirect(); // Must be called last

📖 See Architecture Guide for advanced patterns


Transaction Logging

All payments are automatically logged to the database when logging is enabled.

Query Transactions

use KenDeNigerian\PayZephyr\Models\PaymentTransaction;

// Get all successful payments
$successful = PaymentTransaction::successful()->get();

// Get failed payments
$failed = PaymentTransaction::failed()->get();

// Get pending payments
$pending = PaymentTransaction::pending()->get();

// Get by email
$userTransactions = PaymentTransaction::where('email', 'user@example.com')
    ->orderBy('created_at', 'desc')
    ->get();

// Get by reference
$transaction = PaymentTransaction::where('reference', 'ORDER_123')
    ->first();

Check Transaction Status

$transaction = PaymentTransaction::where('reference', $reference)->first();

if ($transaction->isSuccessful()) {
    // Process successful payment
}

if ($transaction->isFailed()) {
    // Handle failed payment
}

if ($transaction->isPending()) {
    // Payment still processing
}

Transaction Model Properties

$transaction->id            // Auto-increment ID
$transaction->reference     // Payment reference
$transaction->provider      // Provider name (paystack, stripe, etc.)
$transaction->status        // Status (success, failed, pending)
$transaction->amount        // Amount (decimal)
$transaction->currency      // Currency code (NGN, USD, etc.)
$transaction->email         // Customer email
$transaction->channel       // Payment channel (card, bank, etc.)
$transaction->metadata      // Custom metadata (array)
$transaction->customer      // Customer info (array)
$transaction->paid_at       // Payment timestamp
$transaction->created_at    // Created timestamp
$transaction->updated_at    // Updated timestamp

Error Handling

Exception Hierarchy

Exception
└── PaymentException (base)
    ├── DriverNotFoundException
    ├── InvalidConfigurationException
    ├── ChargeException
    ├── VerificationException
    ├── WebhookException
    └── ProviderException

Catching Specific Exceptions

use KenDeNigerian\PayZephyr\Exceptions\{
    ChargeException,
    VerificationException,
    ProviderException
};

try {
    $response = Payment::amount(10000)
        ->email('customer@example.com')
        ->charge();
        
} catch (ChargeException $e) {
    // Handle charge failure
    logger()->error('Charge failed', [
        'error' => $e->getMessage(),
        'context' => $e->getContext(),
    ]);
    
} catch (ProviderException $e) {
    // All providers failed
    return back()->with('error', 'All payment providers are unavailable');
    
} catch (PaymentException $e) {
    // General payment error
    return back()->with('error', 'Payment processing failed');
}

Exception Context

try {
    Payment::verify($reference);
} catch (ProviderException $e) {
    // Get detailed error context
    $context = $e->getContext();
    
    // $context['exceptions'] contains errors from all providers
    foreach ($context['exceptions'] as $provider => $error) {
        logger()->error("Provider $provider failed: $error");
    }
}

Testing

Running Tests

# Run all tests
composer test

# Run with coverage
composer test-coverage

# Run specific test file
vendor/bin/pest tests/Unit/PaystackDriverTest.php

# Static analysis
composer analyse

# Format code
composer format

Writing Tests

use KenDeNigerian\PayZephyr\Facades\Payment;

test('payment charge works', function () {
    // Builder methods can be chained in any order
    $response = Payment::amount(10000)
        ->email('test@example.com')
        ->with('paystack') // or ->using('paystack')
        ->charge(); // Must be called last

    expect($response->reference)->toBeString()
        ->and($response->status)->toBe('pending');
});

test('payment verification works', function () {
    // verify() is standalone, not chainable
    $verification = Payment::verify('ref_123');
    
    expect($verification->isSuccessful())->toBeBool();
});

Mocking in Tests

use KenDeNigerian\PayZephyr\DataObjects\ChargeResponseDTO;

Payment::shouldReceive('charge')
    ->once()
    ->andReturn(new ChargeResponseDTO(
        reference: 'TEST_REF',
        authorizationUrl: 'https://checkout.test.com',
        accessCode: 'access_123',
        status: 'pending',
    ));

API Reference

Fluent API Methods

Builder Methods (Chainable - Can be called in any order)

Payment::amount(float $amount)           // Set payment amount
Payment::currency(string $currency)      // Set currency (default: NGN)
Payment::email(string $email)           // Set customer email (required)
Payment::reference(string $reference)   // Set custom reference
Payment::callback(string $url)          // Set callback URL
Payment::metadata(array $metadata)      // Set custom metadata
Payment::idempotency(string $key)       // Set unique idempotency key
Payment::description(string $description) // Set payment description
Payment::customer(array $customer)      // Set customer information
Payment::channels(array $channels)      // Set payment channels
Payment::with(string|array $providers)  // Set provider(s) for this transaction
Payment::using(string|array $providers)  // Alias for with()

Note: Builder methods can be chained in any order. They return the Payment instance for method chaining.

Action Methods (Must be called last)

Payment::charge()                        // Returns ChargeResponseDTO (no redirect)
Payment::redirect()                      // Redirects user to payment page

Note: charge() and redirect() must be called last in the chain to execute the payment. They compile all the builder data and process the transaction.

Verification Method (Standalone - NOT chainable)

Payment::verify(string $reference, ?string $provider = null)  // Returns VerificationResponseDTO

Note: verify() is a standalone method that cannot be chained. It searches all enabled providers if no provider is specified, or verifies with the specified provider.

Response Objects

ChargeResponse

$response->reference          // string - Payment reference
$response->authorizationUrl   // string - URL to redirect user
$response->accessCode         // string - Access code
$response->status             // string - Payment status (pending, success, etc.)
$response->metadata           // array - Custom metadata
$response->provider           // string - Provider name

// Methods
$response->isSuccessful()     // bool
$response->isPending()        // bool

VerificationResponse

$verification->reference      // string - Payment reference
$verification->status         // string - Payment status
$verification->amount         // float - Amount paid
$verification->currency       // string - Currency code
$verification->paidAt         // ?string - Payment timestamp
$verification->channel        // ?string - Payment channel
$verification->cardType       // ?string - Card type (if applicable)
$verification->bank           // ?string - Bank name (if applicable)
$verification->customer       // ?array - Customer information
$verification->metadata       // array - Custom metadata
$verification->provider       // string - Provider name

// Methods
$verification->isSuccessful() // bool - Payment succeeded
$verification->isFailed()     // bool - Payment failed
$verification->isPending()    // bool - Payment pending

Troubleshooting

Common Issues

1. Webhook Not Received

Symptoms: Webhook endpoint not called by provider

Solutions:

  • Ensure URL is accessible publicly (use ngrok for local testing)
  • Verify HTTPS is enabled (most providers require it)
  • Check provider dashboard for webhook delivery status
  • Verify webhook URL is correctly configured
  • Check server firewall settings

2. Signature Validation Fails

Symptoms: Webhook returns 403 Forbidden

Solutions:

  • Verify webhook secret is correct in .env
  • Ensure PAYMENTS_WEBHOOK_VERIFY_SIGNATURE=true
  • Check provider documentation for correct header name
  • Confirm raw body is being used (not parsed JSON)

3. Provider Not Found

Symptoms: DriverNotFoundException

Solutions:

  • Verify provider is enabled in config
  • Check provider name spelling
  • Ensure credentials are set in .env
  • Run php artisan config:clear

4. Amount Mismatch

Symptoms: Wrong amount charged

Solutions:

  • Ensure amount is in major units (100.00, not 10000)
  • Check currency decimal places
  • Verify getAmountInMinorUnits() is used correctly

5. Transaction Not Logged

Symptoms: No records in payment_transactions table

Solutions:

  • Run migrations: php artisan migrate
  • Verify PAYMENTS_LOGGING_ENABLED=true
  • Check database connection
  • Review application logs for errors

Debug Mode

Enable detailed logging:

// config/logging.php
'channels' => [
    'payments' => [
        'driver' => 'single',
        'path' => storage_path('logs/payments.log'),
        'level' => 'debug',
    ],
],

Best Practices

1. Security

  • ✅ Always enable webhook signature verification in production
  • ✅ Use HTTPS for all webhook URLs
  • ✅ Rotate API keys periodically
  • ✅ Never commit credentials to version control
  • ✅ Use environment variables for all sensitive data

2. Error Handling

  • ✅ Always wrap payment operations in try-catch blocks
  • ✅ Log errors with context for debugging
  • ✅ Show user-friendly error messages
  • ✅ Implement retry logic for transient failures
  • ✅ Monitor failed payments

3. Testing

  • ✅ Test with sandbox/test credentials first
  • ✅ Test all payment flows (success, failure, timeout)
  • ✅ Test webhook handling
  • ✅ Test with different currencies
  • ✅ Test fallback mechanisms

4. Performance

  • ✅ Enable health check caching
  • ✅ Use queue workers for webhook processing
  • ✅ Implement rate limiting
  • ✅ Monitor provider response times
  • ✅ Cache provider availability status

5. Monitoring

  • ✅ Set up alerts for failed payments
  • ✅ Monitor webhook delivery success rate
  • ✅ Track provider uptime
  • ✅ Review transaction logs regularly
  • ✅ Set up exception monitoring (Sentry, Bugsnag)

Support & Resources

Getting Help

Provider Documentation


Next Steps

  1. Install the package
  2. Configure your providers
  3. Implement basic payment flow
  4. Set up webhooks
  5. Test your integration
  6. ✅ Deploy to production

Happy Coding! 🚀