PHP code example of gowelle / flutterwave-php

1. Go to this page and download the library: Download gowelle/flutterwave-php library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

gowelle / flutterwave-php example snippets


use Gowelle\Flutterwave\Facades\Flutterwave;

// Create a direct charge
// NOTE: Card data shown below must be encrypted before sending
// See: https://developer.flutterwave.com/docs/encryption
$charge = Flutterwave::directCharge()->create([
    'amount' => 1000,
    'currency' => 'TZS',
    'reference' => 'ORDER-123',
    'customer' => [
        'email' => '[email protected]',      // Required
        'name' => [
            'first' => 'John',                   // Required
            'last' => 'Doe',                     // Required
        ],
        'phone_number' => '+255123456789',        // Required
    ],
    'payment_method' => [
        'type' => 'card',
        'card' => [
            'nonce' => 'RANDOMLY_GENERATED_12_CHAR_NONCE',
            'encrypted_card_number' => 'BASE64_ENCRYPTED_CARD_NUMBER',
            'encrypted_cvv' => 'BASE64_ENCRYPTED_CVV',
            'encrypted_expiry_month' => 'BASE64_ENCRYPTED_EXPIRY_MONTH',
            'encrypted_expiry_year' => 'BASE64_ENCRYPTED_EXPIRY_YEAR',
        ],
    ],
    'redirect_url' => 'https://example.com/callback',
]);

'client_id' => env('FLUTTERWAVE_CLIENT_ID'),
'client_secret' => env('FLUTTERWAVE_CLIENT_SECRET'),
'secret_hash' => env('FLUTTERWAVE_SECRET_HASH'),

'environment' => env('FLUTTERWAVE_ENVIRONMENT', 'staging'),

'timeout' => env('FLUTTERWAVE_TIMEOUT', 30),           // Request timeout in seconds
'max_retries' => env('FLUTTERWAVE_MAX_RETRIES', 3),    // Maximum retry attempts
'retry_delay' => env('FLUTTERWAVE_RETRY_DELAY', 1000), // Retry delay in milliseconds

'rate_limit' => [
    'enabled' => env('FLUTTERWAVE_RATE_LIMIT_ENABLED', true),
    'max_requests' => env('FLUTTERWAVE_RATE_LIMIT_MAX', 100),
    'per_seconds' => env('FLUTTERWAVE_RATE_LIMIT_WINDOW', 60),
],

'logging' => [
    'enabled' => env('FLUTTERWAVE_LOGGING_ENABLED', true),
    'channel' => env('FLUTTERWAVE_LOG_CHANNEL', 'stack'),
    'level' => env('FLUTTERWAVE_LOG_LEVEL', 'info'),
    'log_requests' => env('FLUTTERWAVE_LOG_REQUESTS', false),
    'log_responses' => env('FLUTTERWAVE_LOG_RESPONSES', false),
],

'webhook' => [
    'verify_signature' => env('FLUTTERWAVE_WEBHOOK_VERIFY', true),
    'route_path' => env('FLUTTERWAVE_WEBHOOK_PATH', 'webhooks/flutterwave'),
    'route_name' => 'flutterwave.webhook',
    'middleware' => ['api'],
],

'default_currency' => env('FLUTTERWAVE_DEFAULT_CURRENCY', 'TZS'),

'charge_sessions' => [
    'enabled' => true,
    'table_name' => 'flutterwave_charge_sessions',
    'cleanup_after_days' => env('FLUTTERWAVE_SESSION_CLEANUP_DAYS', 30),
    'auto_create' => env('FLUTTERWAVE_SESSION_AUTO_CREATE', false),
    'max_polls' => env('FLUTTERWAVE_SESSION_MAX_POLLS', 60),
],

'cache' => [
    'enabled' => env('FLUTTERWAVE_CACHE_ENABLED', true),
    'prefix' => 'flutterwave',
    'ttl' => [
        'access_token' => 3600,      // 1 hour (managed by auth service)
        'banks' => 86400,            // 24 hours
        'mobile_networks' => 86400,  // 24 hours
    ],
],

'models' => [
    'user' => env('FLUTTERWAVE_USER_MODEL', 'App\Models\User'),
    'payment' => env('FLUTTERWAVE_PAYMENT_MODEL', 'App\Domain\Payment\Models\Payment'),
],

use Gowelle\Flutterwave\Facades\Flutterwave;
use Gowelle\Flutterwave\Exceptions\FlutterwaveException;

try {
    // IMPORTANT: Card data must be encrypted before sending
    // Retrieve your encryption key from Flutterwave dashboard > API Settings
    // Use AES-256-GCM encryption with a 12-character nonce
    // See: https://developer.flutterwave.com/docs/encryption

    $charge = Flutterwave::directCharge()->create([
        'amount' => 10000,        // Amount in smallest currency unit (e.g., cents)
        'currency' => 'TZS',       // Currency code
        'reference' => 'ORDER-123', // Your unique reference
        'customer' => [
            'email' => '[email protected]',
            'name' => [
                'first' => 'John',
                'last' => 'Doe',
            ],
            'phone_number' => '+255123456789',
        ],
        'payment_method' => [
            'type' => 'card',
            'card' => [
                'nonce' => 'RANDOMLY_GENERATED_12_CHAR_NONCE',
                'encrypted_card_number' => 'BASE64_ENCRYPTED_CARD_NUMBER',
                'encrypted_cvv' => 'BASE64_ENCRYPTED_CVV',
                'encrypted_expiry_month' => 'BASE64_ENCRYPTED_EXPIRY_MONTH',
                'encrypted_expiry_year' => 'BASE64_ENCRYPTED_EXPIRY_YEAR',
            ],
        ],
        'redirect_url' => 'https://example.com/callback',
        'meta' => [
            'order_id' => '12345',
            'user_id' => '67890',
        ],
    ]);

    // Check charge status
    if ($charge->status->isSuccessful()) {
        // Payment succeeded
    } elseif ($charge->status->

use Gowelle\Flutterwave\Data\DirectCharge\CreateDirectChargeRequest;
use Gowelle\Flutterwave\Facades\Flutterwave;

$request = CreateDirectChargeRequest::make(
    amount: 10000,
    currency: 'NGN',
    reference: 'ORDER-' . uniqid(),
    customer: [
        'email' => '[email protected]',
        'name' => [
            'first' => 'John',
            'last' => 'Doe',
        ],
        'phone_number' => '+2341234567890',
    ],
    paymentMethod: [
        'type' => 'card',
        'card' => [
            'nonce' => 'RANDOMLY_GENERATED_12_CHAR_NONCE',
            'encrypted_card_number' => 'BASE64_ENCRYPTED_CARD_NUMBER',
            'encrypted_cvv' => 'BASE64_ENCRYPTED_CVV',
            'encrypted_expiry_month' => 'BASE64_ENCRYPTED_EXPIRY_MONTH',
            'encrypted_expiry_year' => 'BASE64_ENCRYPTED_EXPIRY_YEAR',
        ],
    ],
    redirectUrl: 'https://example.com/callback',
    meta: ['order_id' => '12345'],
);

$charge = Flutterwave::directCharge()->createFromDto($request);

// Access charge details including new fields
echo $charge->fees;          // Transaction fees
echo $charge->settlementId;  // Settlement ID
$charge->isSettled();        // Check if settled
$charge->isDisputed();       // Check if disputed

use Gowelle\Flutterwave\Data\AuthorizationData;
use Gowelle\Flutterwave\Enums\NextActionType;

// For PIN authorization
$authorization = AuthorizationData::createPin(
    nonce: $nonce,              // Nonce from Flutterwave
    encryptedPin: $encryptedPin // Encrypted PIN
);

// For OTP authorization
$authorization = AuthorizationData::createOtp(
    code: $otpCode // OTP code from customer
);

// For AVS (Address Verification System)
$authorization = AuthorizationData::createAvs([
    'line1' => '123 Main St',
    'city' => 'Dar es Salaam',
    'state' => 'Dar es Salaam',
    'country' => 'TZ',
    'postal_code' => '11101',
]);

// Submit authorization
$updatedCharge = Flutterwave::directCharge()->updateChargeAuthorization(
    chargeId: $charge->id,
    authorizationData: $authorization
);

// Check if charge is now complete
if ($updatedCharge->status->isSuccessful()) {
    // Payment completed successfully
} elseif ($updatedCharge->status->

// PIN + OTP flow
$authorization = AuthorizationData::createPin(
    nonce: $nonce,
    encryptedPin: $encryptedPin,
    scenarioKey: 'scenario:auth_pin'
);

// OTP-only step (after PIN was submitted)
$authorization = AuthorizationData::createOtp(
    code: $otpCode,
    scenarioKey: 'scenario:auth_pin'
);

// AVS flow
$authorization = AuthorizationData::createAvs(
    address: ['line1' => '123 Main St', 'city' => 'Lagos', 'country' => 'NG'],
    scenarioKey: 'scenario:auth_avs'
);

$updatedCharge = Flutterwave::directCharge()->updateChargeAuthorization(
    chargeId: $charge->id,
    authorizationData: $authorization
);

use Gowelle\Flutterwave\Enums\DirectChargeStatus;

$status = Flutterwave::directCharge()->status('charge-id');

if ($status->isSuccessful()) {
    // Payment succeeded
} elseif ($status->isTerminal()) {
    // Payment failed, cancelled, or timed out
} else {
    // Payment is pending or 

use Gowelle\Flutterwave\Facades\Flutterwave;

// Process a payment with callback for trace ID
$payment = Flutterwave::payments()->process([
    'amount' => 1000,
    'currency' => 'TZS',
    'reference' => 'ORDER-123',
    'customer_id' => 'CUST-456',
    'payment_method_id' => 'PM-789',
    'payment_method_type' => 'card',
    'redirect_url' => 'https://example.com/callback',
], function ($traceId) {
    // Callback executed when charge is successfully created
    logger()->info('Charge created', ['trace_id' => $traceId]);
});

// Get payment status
$status = Flutterwave::payments()->status('charge-id');

$methods = Flutterwave::payments()->methods([
    'customer_id' => 'CUST-456',
    'currency' => 'TZS',
]);

// IMPORTANT: Card data must be encrypted before sending
// Retrieve your encryption key from Flutterwave dashboard > API Settings
// Use AES-256-GCM encryption with a 12-character nonce
// See: https://developer.flutterwave.com/docs/encryption

$paymentMethod = Flutterwave::payments()->createMethod([
    'customer_id' => 'CUST-456',
    'type' => 'card',
    'card' => [
        'nonce' => 'RANDOMLY_GENERATED_12_CHAR_NONCE',
        'encrypted_card_number' => 'BASE64_ENCRYPTED_CARD_NUMBER',
        'encrypted_cvv' => 'BASE64_ENCRYPTED_CVV',
        'encrypted_expiry_month' => 'BASE64_ENCRYPTED_EXPIRY_MONTH',
        'encrypted_expiry_year' => 'BASE64_ENCRYPTED_EXPIRY_YEAR',
    ],
]);

$paymentMethod = Flutterwave::payments()->getMethod('payment-method-id');

$customer = Flutterwave::customers()->create([
    'email' => '[email protected]',        // Required
    'name' => [
        'first' => 'John',
        'middle' => 'Michael',             // Optional
        'last' => 'Doe',
    ],
    'phone' => [
        'country_code' => 'TZA',
        'number' => '712345678',
    ],
]);

use Gowelle\Flutterwave\Data\Customer\CreateCustomerRequest;

// Minimal (email only)
$request = new CreateCustomerRequest(email: '[email protected]');

// With name, phone object, and optional address
$request = new CreateCustomerRequest(
    email: '[email protected]',
    firstName: 'John',
    lastName: 'Doe',
    phone: ['country_code' => 'TZA', 'number' => '712345678'],
    middleName: 'Michael',  // optional
    address: [              // optional: line1, line2?, city, state, postal_code, country
        'line1' => '221B Baker Street',
        'city' => 'London',
        'state' => 'England',
        'postal_code' => 'NW1 6XE',
        'country' => 'GB',
    ],
);

$customer = Flutterwave::customers()->createFromDto($request);

use Gowelle\Flutterwave\Data\Customer\UpdateCustomerRequest;

$request = new UpdateCustomerRequest(
    email: '[email protected]',
    firstName: 'John',
    lastName: 'Doe',
    phone: ['country_code' => 'TZA', 'number' => '987654321'],
    address: [ /* optional */ ],
);

$customer = Flutterwave::customers()->updateFromDto('customer-id', $request);

use Gowelle\Flutterwave\Data\Customer\SearchCustomerRequest;

$request = new SearchCustomerRequest(email: '[email protected]');
$customer = Flutterwave::customers()->searchFromDto($request);

$customer = Flutterwave::customers()->get('customer-id');

$customers = Flutterwave::customers()->list([
    'page' => 1,
    'limit' => 20,
]);

use Gowelle\Flutterwave\Facades\Flutterwave;

$order = Flutterwave::orders()->create([
    'amount' => 10000,
    'currency' => 'NGN',
    'reference' => 'ORDER-' . uniqid(),  // 6-42 chars, unique
    'customer_id' => 'cust_abc123',
    'payment_method_id' => 'pm_xyz789',
    'meta' => ['order_type' => 'subscription'],  // optional
    'redirect_url' => 'https://example.com/callback',  // optional
]);

use Gowelle\Flutterwave\Data\Order\CreateOrderRequest;

$request = CreateOrderRequest::make(
    amount: 10000.00,
    currency: 'NGN',
    reference: 'ORDER-' . uniqid(),
    customerId: 'cust_abc123',
    paymentMethodId: 'pm_xyz789',
    meta: ['source' => 'api'],      // optional
    redirectUrl: 'https://example.com/callback',  // optional
);

$order = Flutterwave::orders()->createFromDto($request);

use Gowelle\Flutterwave\Data\Order\CreateOrchestratorOrderRequest;

$request = CreateOrchestratorOrderRequest::make(
    amount: 10000.00,
    currency: 'NGN',
    reference: 'ORDER-' . uniqid(),
    customer: [
        'email' => '[email protected]',
        'name' => [
            'first' => 'John',
            'last' => 'Doe',
        ],
        'phone' => '+2341234567890',
    ],
    paymentMethod: [
        'type' => 'card',
        'card' => [
            'nonce' => 'RANDOM_12_CHAR',
            'encrypted_card_number' => 'ENCRYPTED_DATA',
            'encrypted_cvv' => 'ENCRYPTED_DATA',
            'encrypted_expiry_month' => 'ENCRYPTED_DATA',
            'encrypted_expiry_year' => 'ENCRYPTED_DATA',
        ],
    ],
    meta: ['order_type' => 'subscription'],  // optional
    redirectUrl: 'https://example.com/callback',  // optional
);

$order = Flutterwave::orders()->createWithOrchestrator($request);

$order = Flutterwave::orders()->retrieve('order-id');

use Gowelle\Flutterwave\Data\Order\ListOrdersRequest;
use Gowelle\Flutterwave\Data\Order\OrderStatus;

// List all orders (default pagination)
$orders = Flutterwave::orders()->list();

// List with filters
$request = new ListOrdersRequest(
    status: OrderStatus::Completed,      // Filter by status
    from: new DateTime('2024-01-01'),   // Start date
    to: new DateTime('2024-12-31'),     // End date
    customerId: 'cust_abc123',          // Filter by customer
    paymentMethodId: 'pm_xyz789',       // Filter by payment method
    page: 1,                            // Page number (>=1)
    size: 20,                           // Results per page (10-50)
);

$orders = Flutterwave::orders()->listWithFilters($request);

use Gowelle\Flutterwave\Data\Order\OrderStatus;

OrderStatus::Completed          // Order completed successfully
OrderStatus::Pending            // Order is pending
OrderStatus::Authorized         // Order is authorized, awaiting capture
OrderStatus::PartiallyCompleted // Partially completed
OrderStatus::Voided             // Order was voided
OrderStatus::Failed             // Order failed

use Gowelle\Flutterwave\Data\Order\UpdateOrderRequest;
use Gowelle\Flutterwave\Data\Order\OrderAction;

// Update with metadata only
$request = UpdateOrderRequest::withMeta(['updated' => true]);
$order = Flutterwave::orders()->updateFromDto('order-id', $request);

// Void an order
$request = UpdateOrderRequest::void(['reason' => 'Customer cancelled']);
$order = Flutterwave::orders()->updateFromDto('order-id', $request);

// Capture an authorized order
$request = UpdateOrderRequest::capture();
$order = Flutterwave::orders()->updateFromDto('order-id', $request);

// Void an order directly
$order = Flutterwave::orders()->void('order-id', ['reason' => 'Cancelled']);

// Capture an authorized order directly
$order = Flutterwave::orders()->capture('order-id');

use Gowelle\Flutterwave\Data\Refund\CreateRefundRequest;
use Gowelle\Flutterwave\Enums\RefundReason;

// Create a refund with DTO (type-safe)
$refund = Flutterwave::refunds()->create(
    new CreateRefundRequest(
        amount: 500.00,
        chargeId: 'charge-123',
        reason: RefundReason::REQUESTED_BY_CUSTOMER,
        meta: ['note' => 'Customer requested refund'],  // optional
    )
);

// Check refund status
if ($refund->isSuccessful()) {
    // Refund succeeded
} elseif ($refund->isPending()) {
    // Refund is processing
}

$refund = Flutterwave::refunds()->get('refund-id');

// Access refund properties with type safety
echo $refund->amountRefunded;  // Float
echo $refund->status->value;   // String via enum
echo $refund->status->isSuccessful();  // Boolean

use Gowelle\Flutterwave\Data\Refund\ListRefundsRequest;

// List all refunds (default page=1, size=10)
$refunds = Flutterwave::refunds()->list();

// List with custom pagination
$refunds = Flutterwave::refunds()->list(
    new ListRefundsRequest(page: 2, size: 20)
);

// List refunds within date range
$refunds = Flutterwave::refunds()->list(
    new ListRefundsRequest(
        page: 1,
        size: 50,
        from: now()->subDays(30),
        to: now(),
    )
);

// Access refund data
foreach ($refunds as $refund) {
    echo $refund->id;
    echo $refund->chargeId;
    echo $refund->amountRefunded;
    echo $refund->status->value;
    echo $refund->reason;
}

use Gowelle\Flutterwave\Enums\RefundReason;

RefundReason::DUPLICATE                  // Duplicate charge
RefundReason::FRAUDULENT                 // Fraudulent transaction
RefundReason::REQUESTED_BY_CUSTOMER      // Customer requested
RefundReason::EXPIRED_UNCAPTURED_CHARGE  // Expired uncaptured charge

use Gowelle\Flutterwave\Enums\RefundStatus;

RefundStatus::NEW       // Refund created, not yet processed
RefundStatus::PENDING   // Refund is being processed
RefundStatus::SUCCEEDED // Refund completed successfully
RefundStatus::FAILED    // Refund failed

// Use helper methods for type-safe checks
$refund->status->isSuccessful();  // true if SUCCEEDED
$refund->status->isPending();     // true if NEW or PENDING
$refund->status->isTerminal();    // true if SUCCEEDED or FAILED

use Gowelle\Flutterwave\Data\Transfer\BankTransferRequest;

$transfer = Flutterwave::transfers()->bankTransfer(
    new BankTransferRequest(
        amount: 50000,
        sourceCurrency: 'NGN',
        destinationCurrency: 'NGN',
        accountNumber: '0123456789',
        bankCode: '044',
        reference: 'PAYOUT-' . uniqid(),
        narration: 'Monthly payout',     // optional
    )
);

use Gowelle\Flutterwave\Data\Transfer\MobileMoneyTransferRequest;

$transfer = Flutterwave::transfers()->mobileMoneyTransfer(
    new MobileMoneyTransferRequest(
        amount: 1000,
        sourceCurrency: 'NGN',
        destinationCurrency: 'GHS',
        network: 'MTN',
        phoneNumber: '2339012345678',
        firstName: 'John',
        lastName: 'Doe',
        reference: 'MOMO-' . uniqid(),
    )
);

$transfer = Flutterwave::transfers()->get('transfer-id');

$transfers = Flutterwave::transfers()->list();

use Gowelle\Flutterwave\Data\Transfer\RetryTransferRequest;

$transfer = Flutterwave::transfers()->retry(
    new RetryTransferRequest(
        action: 'retry',
        reference: 'UNIQUE_RETRY_UUID'
    )
);

use Gowelle\Flutterwave\Data\Transfer\CreateRecipientRequest;

// Nigerian (NGN) - simplest form
$recipient = Flutterwave::transfers()->createRecipient(
    CreateRecipientRequest::bankNgn(
        accountNumber: '0123456789',
        bankCode: '044',
    )
);

// Ethiopian (ETB)
CreateRecipientRequest::bankEtb($accountNumber, $bankCode, $firstName, $lastName);

// Kenyan (KES)
CreateRecipientRequest::bankKes($accountNumber, $bankCode, $firstName, $lastName);

// Malawian (MWK)
CreateRecipientRequest::bankMwk($accountNumber, $bankCode, $firstName, $lastName);

// Rwandan (RWF)
CreateRecipientRequest::bankRwf($accountNumber, $bankCode, $firstName, $lastName);

// Sierra Leonean (SLL)
CreateRecipientRequest::bankSll($accountNumber, $bankCode, $firstName, $lastName);

// Ugandan (UGX)
CreateRecipientRequest::bankUgx($accountNumber, $bankCode, $firstName, $lastName);

// Ghanaian (GHS)
CreateRecipientRequest::bankGhs($accountNumber, $bankCode, $branch, $firstName, $lastName);

// Central African (XAF)
CreateRecipientRequest::bankXaf($accountNumber, $bankCode, $branch, $firstName, $lastName);

// West African (XOF)
CreateRecipientRequest::bankXof($accountNumber, $bankCode, $branch, $firstName, $lastName);

// US (USD) bank recipient
$recipient = Flutterwave::transfers()->createRecipient(
    CreateRecipientRequest::bankUsd(
        accountNumber: '1234567890',
        bankCode: '021000021',
        accountType: 'checking', // or 'savings'
        routingNumber: '021000021',
        swiftCode: 'CHASUS33',
        firstName: 'John',
        lastName: 'Doe',
        phone: ['country_code' => '1', 'number' => '2025551234'],
        email: '[email protected]',
        address: [
            'city' => 'New York',
            'country' => 'US',
            'line1' => '123 Main St',
            'postal_code' => '10001',
            'state' => 'NY',
        ],
    )
);

// UK (GBP) bank recipient
CreateRecipientRequest::bankGbp(
    accountNumber: 'GB82WEST12345698765432',
    accountType: 'individual', // or 'corporate'
    bankName: 'HSBC',
    sortCode: '401276',
    firstName: 'John',
    lastName: 'Doe',
    phone: ['country_code' => '44', 'number' => '7911123456'],
    email: '[email protected]',
    address: ['city' => 'London', 'country' => 'GB', 'line1' => '123 High St', 'postal_code' => 'EC1A 1BB'],
);

// European (EUR) bank recipient
CreateRecipientRequest::bankEur(
    accountNumber: 'DE89370400440532013000',
    bankName: 'Deutsche Bank',
    swiftCode: 'DEUTDEFF',
    firstName: 'Hans',
    lastName: 'Mueller',
    phone: ['country_code' => '49', 'number' => '1701234567'],
    email: '[email protected]',
    address: ['city' => 'Berlin', 'country' => 'DE', 'line1' => 'Alexanderplatz 1', 'postal_code' => '10178'],
);

// South African (ZAR) bank recipient
CreateRecipientRequest::bankZar(
    accountNumber: '1234567890',
    bankCode: 'ABSAZAJJ',
    firstName: 'John',
    lastName: 'Doe',
    phone: ['country_code' => '27', 'number' => '823456789'],
    email: '[email protected]',
    address: ['city' => 'Cape Town', 'country' => 'ZA', 'line1' => '123 Long St', 'postal_code' => '8001'],
);

$recipient = Flutterwave::transfers()->createRecipient(
    CreateRecipientRequest::mobileMoney(
        currency: 'TZS',
        network: 'VODACOM',
        phoneNumber: '255123456789',
        firstName: 'John',
        lastName: 'Doe',
    )
);

// For any type not covered by factory methods
$recipient = Flutterwave::transfers()->createRecipient(
    new CreateRecipientRequest(
        type: 'bank_custom',
        bank: ['account_number' => '...', 'code' => '...'],
        name: ['first' => 'John', 'last' => 'Doe'],
    )
);

use Gowelle\Flutterwave\Data\Transfer\CreateSenderRequest;

// Basic generic sender
$sender = Flutterwave::transfers()->createSender(
    CreateSenderRequest::generic(
        firstName: 'John',
        lastName: 'Doe',
    )
);

// Generic sender with full details
$sender = Flutterwave::transfers()->createSender(
    CreateSenderRequest::generic(
        firstName: 'John',
        lastName: 'Doe',
        middleName: 'Michael',
        phone: ['country_code' => '234', 'number' => '8012345678'],
        email: '[email protected]',
        address: [
            'city' => 'Lagos',
            'country' => 'NG',
            'line1' => '123 Main Street',
            'postal_code' => '100001',
            'state' => 'Lagos',
        ],
    )
);

// GBP sender for UK bank transfers
$sender = Flutterwave::transfers()->createSender(
    CreateSenderRequest::bankGbp(
        firstName: 'John',
        lastName: 'Doe',
        phone: ['country_code' => '44', 'number' => '7911123456'],
        email: '[email protected]',
        address: [
            'city' => 'London',
            'country' => 'GB',
            'line1' => '123 High Street',
            'postal_code' => 'EC1A 1BB',
            'state' => 'Greater London',
        ],
    )
);

// EUR sender for European bank transfers
$sender = Flutterwave::transfers()->createSender(
    CreateSenderRequest::bankEur(
        firstName: 'Hans',
        lastName: 'Mueller',
        phone: ['country_code' => '49', 'number' => '1701234567'],
        email: '[email protected]',
        address: [
            'city' => 'Berlin',
            'country' => 'DE',
            'line1' => 'Alexanderplatz 1',
            'postal_code' => '10178',
            'state' => 'Berlin',
        ],
    )
);

Flutterwave::transfers()->deleteSender('sender-id');

use Gowelle\Flutterwave\Data\Transfer\GetRateRequest;

$rate = Flutterwave::transfers()->getRate(
    new GetRateRequest(
        sourceCurrency: 'NGN',
        destinationCurrency: 'GHS',
        amount: 10000,
    )
);

use Gowelle\Flutterwave\Data\Transfer\CreateTransferRequest;

// First, create recipient and sender (see above)
$recipient = Flutterwave::transfers()->createRecipient(...);
$sender = Flutterwave::transfers()->createSender(...);

// Then create the transfer
$transfer = Flutterwave::transfers()->create(
    new CreateTransferRequest(
        amount: 50000,
        sourceCurrency: 'NGN',
        destinationCurrency: 'NGN',
        recipientId: $recipient->id,
        senderId: $sender->id,
        reference: 'PAYOUT-' . uniqid(),
    )
);

$settlement = Flutterwave::settlements()->get('settlement-id');

$settlements = Flutterwave::settlements()->list([
    'page' => 1,
    'limit' => 20,
]);

$banks = Flutterwave::banks()->get('NG'); // Country code (e.g., NG, TZ, KE)

$branches = Flutterwave::banks()->branches('bank-id');

$account = Flutterwave::banks()->resolveAccount(
    bankCode: '044',
    accountNumber: '0123456789',
);

echo $account->accountName;

$account = Flutterwave::banks()->resolveUsdNgAccount(
    bankCode: '044',
    accountNumber: '0690000031',
);

$account = Flutterwave::banks()->resolveGbpCorporateAccount(
    bankCode: '044',
    accountNumber: '0690000031',
    businessName: 'Ajadi & Sons Ltd.',
);

$account = Flutterwave::banks()->resolveGbpIndividualAccount(
    bankCode: '044',
    accountNumber: '0690000031',
    firstName: 'King',
    lastName: 'LeBron',
    middleName: 'Leo', // optional
);

use Gowelle\Flutterwave\Data\Banks\BankAccountResolveRequest;

$ngnRequest = BankAccountResolveRequest::forNgn('044', '0123456789');
$usdNgRequest = BankAccountResolveRequest::forUsdNg('044', '0690000031');
$gbpCorporateRequest = BankAccountResolveRequest::forGbpCorporate('044', '0690000031', 'Ajadi & Sons Ltd.');
$gbpIndividualRequest = BankAccountResolveRequest::forGbpIndividual(
    '044',
    '0690000031',
    'King',
    'LeBron',
    'Leo',
);

$account = Flutterwave::banks()->resolveFromDto($gbpCorporateRequest);

$networks = Flutterwave::mobileNetworks()->list('TZ'); // Country code

foreach ($networks as $network) {
    echo $network->name;
    echo $network->code;
}

use Gowelle\Flutterwave\Facades\Flutterwave;

$account = Flutterwave::wallets()->resolveAccount(
    provider: 'flutterwave',
    identifier: 'wallet_123'
);

// Access resolved account details
echo $account->provider;      // 'flutterwave'
echo $account->identifier;    // 'wallet_123'
echo $account->name;          // Account holder name

use Gowelle\Flutterwave\Facades\Flutterwave;

$statement = Flutterwave::wallets()->getStatement([
    'currency' => 'NGN',           // Required: 3-letter currency code
    'size' => 20,                  // Optional: Page size (10-50, default 10)
    'from' => '2024-01-01T00:00:00Z', // Optional: Start date (ISO 8601)
    'to' => '2024-12-31T23:59:59Z',   // Optional: End date (ISO 8601)
    'next' => 'next_cursor',       // Optional: Next page cursor
    'previous' => 'prev_cursor',   // Optional: Previous page cursor
]);

// Access statement data
echo $statement->cursor->total;        // Total transactions
echo $statement->cursor->limit;       // Page limit
echo $statement->cursor->hasMoreItems; // Whether more items exist
echo $statement->cursor->next;         // Next page cursor
echo $statement->cursor->previous;      // Previous page cursor

// Access transactions
foreach ($statement->transactions as $transaction) {
    echo $transaction['transaction_direction']; // 'credit' or 'debit'
    echo $transaction['amount']['value'];
    echo $transaction['amount']['currency'];
    echo $transaction['balance']['before'];
    echo $transaction['balance']['after'];
}

use Gowelle\Flutterwave\Facades\Flutterwave;

$balance = Flutterwave::wallets()->getBalance('NGN');

echo $balance->currency;           // 'NGN'
echo $balance->availableBalance;   // 1200.09

use Gowelle\Flutterwave\Facades\Flutterwave;

$balances = Flutterwave::wallets()->getBalances();

foreach ($balances as $balance) {
    echo $balance->currency;           // 'NGN', 'USD', etc.
    echo $balance->availableBalance;   // Available balance
}

use Gowelle\Flutterwave\Facades\Flutterwave;
use Gowelle\Flutterwave\Data\VirtualAccount\CreateVirtualAccountRequestDTO;
use Gowelle\Flutterwave\Enums\VirtualAccountCurrency;
use Gowelle\Flutterwave\Enums\VirtualAccountType;

$request = new CreateVirtualAccountRequestDTO(
    reference: 'unique-ref-' . time(),      // 6-42 chars, unique
    customerId: 'cus_123',                  // Existing customer ID
    amount: 0,                              // 0 for static accounts
    currency: VirtualAccountCurrency::NGN,  // NGN, GHS, EGP, KES, MAD, or ZAR
    accountType: VirtualAccountType::STATIC, // STATIC or DYNAMIC
    narration: 'Payment for Order #123',    // Optional
    meta: ['order_id' => '123'],            // Optional metadata
    bankCode: '044',                        // Optional preferred bank code
);

$account = Flutterwave::banks()->createVirtualAccount($request);

// Access account details
echo $account->accountNumber;      // Virtual account number
echo $account->accountBankName;    // Bank name (e.g., "WEMA BANK")
echo $account->reference;          // Your reference
echo $account->status->value;      // 'active' or 'inactive'

$account = Flutterwave::banks()->retrieveVirtualAccount('va_123');

echo $account->accountNumber;
echo $account->accountBankName;
echo $account->status->value;
echo $account->currency->value;

$accounts = Flutterwave::banks()->listVirtualAccounts();

foreach ($accounts as $account) {
    echo $account->accountNumber;
    echo $account->status->value;
    echo $account->currency->value;
}

use Gowelle\Flutterwave\Data\VirtualAccount\ListVirtualAccountsParamsDTO;

$params = new ListVirtualAccountsParamsDTO(
    page: 1,                                // Page number (min: 1)
    size: 20,                               // Page size (10-50)
    from: '2024-01-01T00:00:00Z',          // Start date (ISO 8601)
    to: '2024-12-31T23:59:59Z',            // End date (ISO 8601)
    reference: 'unique-ref-123',            // Filter by reference
);

$accounts = Flutterwave::banks()->listVirtualAccountsWithParams($params);

foreach ($accounts as $account) {
    echo $account->accountNumber;
    echo $account->reference;
}

use Gowelle\Flutterwave\Data\VirtualAccount\UpdateVirtualAccountRequestDTO;
use Gowelle\Flutterwave\Enums\VirtualAccountStatus;

// Deactivate an account
$request = UpdateVirtualAccountRequestDTO::forStatusUpdate(
    VirtualAccountStatus::INACTIVE
);

$updated = Flutterwave::banks()->updateVirtualAccount('va_123', $request);

// Update BVN
$request = UpdateVirtualAccountRequestDTO::forBvnUpdate(
    bvn: '12345678901',
    meta: ['updated_by' => 'admin']  // Optional
);

$updated = Flutterwave::banks()->updateVirtualAccount('va_123', $request);

use Gowelle\Flutterwave\Facades\Flutterwave;
use Gowelle\Flutterwave\Data\VirtualAccount\CreateVirtualAccountRequestDTO;
use Gowelle\Flutterwave\Enums\VirtualAccountCurrency;
use Gowelle\Flutterwave\Enums\VirtualAccountType;

// Create a static account for a customer
$request = new CreateVirtualAccountRequestDTO(
    reference: 'order-' . $order->id,
    customerId: 'cus_' . $customer->id,
    amount: 0,
    currency: VirtualAccountCurrency::NGN,
    accountType: VirtualAccountType::STATIC,
    narration: "Payment for Order #{$order->id}",
    meta: [
        'order_id' => $order->id,
        'customer_id' => $customer->id,
    ],
);

$account = Flutterwave::banks()->createVirtualAccount($request);

// Store the account number for the customer to pay into
$bankAccount = $account->accountNumber;
$bankName = $account->accountBankName;

// Later, retrieve to check status
$current = Flutterwave::banks()->retrieveVirtualAccount($account->id);
if ($current->isActive()) {
    // Account is still active
}

// When no longer needed, deactivate
$api->update($account['data']['id'], [
    'action_type' => 'update_status',
    'status' => 'inactive',
]);

// Retrieve a list of all chargebacks/disputes
$chargebacks = Flutterwave::chargebacks()->list();

// Filter by page, size, and date range
$chargebacks = Flutterwave::chargebacks()->list([
    'page' => 1,
    'size' => 10,
    'from' => '2025-04-21T10:55:16Z',
    'to' => '2025-05-21T10:48:18Z',
]);

use Gowelle\Flutterwave\Data\Chargeback\CreateChargebackRequest;

$chargeback = Flutterwave::chargebacks()->create(
    new CreateChargebackRequest(
        chargeId: 'chg_eahdhfThdHsgaSra',
        amount: 12.34,
        type: 'local',
        expiry: 72,
        stage: 'new',
        status: 'pending',
        comment: 'Customer claims the charge was unauthorized.',
        provider: 'Visa',
        arn: '1243453453434234534443423',
        initiator: 'customer',
        uploadedProof: 'https://example.com/proofs/proof_123.pdf',
    )
);

// Accept a chargeback (agree to refund the customer)
$chargeback = Flutterwave::chargebacks()->accept(
    'chargeback-id',
    'We accept this dispute and will process the reversal.'
);

use Gowelle\Flutterwave\Data\Chargeback\UpdateChargebackRequest;

// Decline a chargeback with evidence
$chargeback = Flutterwave::chargebacks()->decline(
    'chargeback-id',
    UpdateChargebackRequest::decline(
        message: 'Service was securely delivered',
        evidenceUrl: 'https://example.com/delivery/proof.jpg',
        proofData: base64_encode('proof document bytes'),
        provider: 'Visa',
        arn: '1243453453434234534443423',
        dueDatetime: '2025-05-30T23:59:59Z',
    )
);

// Get details for a specific chargeback
$chargeback = Flutterwave::chargebacks()->retrieve('chargeback-id');

// Fetch transaction costs for a 5000 TZS charge
$fees = Flutterwave::fees()->calculate([
    'amount' => 5000,
    'currency' => 'TZS',
    'payment_method' => 'mobile_money',
    'network' => 'M-PESA', // Optional, for mobile money
]);

echo $fees->fee;            // Direct gateway transaction cost
echo $fees->flutterwaveFee; // Direct markup

// app/Livewire/CheckoutPage.php
namespace App\Livewire;

use Livewire\Component;
use Gowelle\Flutterwave\Facades\Flutterwave;
use Gowelle\Flutterwave\Data\AuthorizationData;

class CheckoutPage extends Component
{
    public int $amount = 10000;
    public string $currency = 'TZS';
    
    // Payment flow state
    public string $step = 'form'; // 'form', 'pin', 'otp', 'status'
    public ?string $chargeId = null;
    public ?string $error = null;
    
    protected $listeners = [
        'payment-success' => 'handlePaymentSuccess',
        'payment-error' => 'handlePaymentError',
        'authorization-       // Switch to appropriate input based on authorization type
        match ($data['type']) {
            'etMessage();
            $this->step = 'form';
        }
    }
    
    public function handleOtpSubmitted($otp)
    {
        try {
            $authorization = AuthorizationData::createOtp(code: $otp);
            
            $charge = Flutterwave::directCharge()->updateChargeAuthorization(
                chargeId: $this->chargeId,
                authorizationData: $authorization
            );
            
            $this->processChargeResult($charge);
        } catch (\Exception $e) {
            $this->error = $e->getMessage();
            $this->step = 'form';
        }
    }
    
    public function handleCancelled()
    {
        $this->step = 'form';
        $this->chargeId = null;
    }
    
    protected function processChargeResult($charge)
    {
        if ($charge->status->isSuccessful()) {
            return redirect()->route('payment.success');
        }
        
        if ($charge->status->

// routes/api.php
use App\Http\Controllers\FlutterwaveController;

Route::prefix('flutterwave')->group(function () {
    Route::post('/charges', [FlutterwaveController::class, 'createCharge']);
    Route::post('/charges/{chargeId}/authorize', [FlutterwaveController::class, 'authorize']);
    Route::get('/charges/{chargeId}/status', [FlutterwaveController::class, 'status']);
});

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

use Gowelle\Flutterwave\Facades\Flutterwave;
use Gowelle\Flutterwave\Data\AuthorizationData;
use Illuminate\Http\Request;

class FlutterwaveController extends Controller
{
    public function createCharge(Request $request)
    {
        $validated = $request->validate([
            'amount' => 'ent_method' => '   'pin' => AuthorizationData::createPin(
                nonce: $request->input('nonce'),
                encryptedPin: $request->input('encrypted_pin')
            ),
            'otp' => AuthorizationData::createOtp(
                code: $request->input('code')
            ),
            default => throw new \InvalidArgumentException('Invalid authorization type'),
        };

        $charge = Flutterwave::directCharge()->updateChargeAuthorization(
            chargeId: $chargeId,
            authorizationData: $authorization
        );

        return response()->json(['charge' => $charge]);
    }

    public function status(string $chargeId)
    {
        $status = Flutterwave::directCharge()->status($chargeId);

        return response()->json(['charge' => $status]);
    }
}

'charge_sessions' => [
    'enabled' => true,
    'auto_create' => true, // Automatically create sessions on charge creation
],

use Gowelle\Flutterwave\Facades\Flutterwave;
use Gowelle\Flutterwave\Models\ChargeSession;

$charge = Flutterwave::directCharge()->create([
    'amount' => 1000,
    'currency' => 'TZS',
    'reference' => 'ORDER-123',
    'customer' => [...],
    'payment_method' => [...],
    'user_id' => auth()->id(),        // Required for auto-create
    'payment_id' => $payment->id,     // Required for auto-create
]);

// Session is automatically created and linked
$session = ChargeSession::byRemoteChargeId($charge->id)->first();

use Gowelle\Flutterwave\Models\ChargeSession;

$session = ChargeSession::create([
    'user_id' => auth()->id(),
    'payment_id' => $payment->id,
    'remote_charge_id' => $charge->id,
    'status' => $charge->status->value,
    'next_action_type' => $charge->nextAction->type->value ?? null,
    'next_action_data' => $charge->nextAction->data ?? null,
    'payment_method_type' => 'card',
    'meta' => [
        'order_id' => '12345',
    ],
]);

use Gowelle\Flutterwave\Models\ChargeSession;
use Gowelle\Flutterwave\Enums\DirectChargeStatus;

// Find by remote charge ID
$session = ChargeSession::byRemoteChargeId('charge-id')->first();

// Find pending sessions
$pendingSessions = ChargeSession::pending()->get();

// Find completed sessions
$completedSessions = ChargeSession::completed()->get();

// Find by status
$succeededSessions = ChargeSession::withStatus(DirectChargeStatus::SUCCEEDED)->get();

// Access relationships
$user = $session->user;
$payment = $session->payment;

$session->updateStatus(DirectChargeStatus::SUCCEEDED);
$session->updateNextAction($nextActionData);
$session->setMeta('custom_key', 'custom_value');
$session->save();

protected function schedule(Schedule $schedule)
{
    $schedule->command('flutterwave:cleanup-sessions')->daily();
}

use Gowelle\Flutterwave\Events\FlutterwaveChargeCreated;

Event::listen(FlutterwaveChargeCreated::class, function (FlutterwaveChargeCreated $event) {
    $chargeData = $event->chargeData;
    $requestData = $event->requestData;

    // Create charge session, send notification, etc.
});

use Gowelle\Flutterwave\Events\FlutterwaveChargeUpdated;

Event::listen(FlutterwaveChargeUpdated::class, function (FlutterwaveChargeUpdated $event) {
    $chargeData = $event->chargeData;
    $authorizationData = $event->authorizationData;

    // Update charge session, process completion, etc.
});

use Gowelle\Flutterwave\Events\FlutterwaveTransferCreated;

Event::listen(FlutterwaveTransferCreated::class, function (FlutterwaveTransferCreated $event) {
    $transferData = $event->transferData;

    // Log transfer, update records, send notification, etc.
    logger()->info('Transfer created', [
        'id' => $transferData->id,
        'status' => $transferData->status->value,
        'amount' => $transferData->amount,
    ]);
});

use Gowelle\Flutterwave\Events\FlutterwaveWebhookReceived;

Event::listen(FlutterwaveWebhookReceived::class, function (FlutterwaveWebhookReceived $event) {
    $eventType = $event->getEventType(); // String (backward compatible)
    $eventTypeEnum = $event->getEventTypeEnum(); // WebhookEventType enum (recommended)
    $transactionData = $event->getTransactionData();

    // Using helper methods on the event
    if ($event->isPaymentEvent()) {
        // Handle payment-related webhook
    } elseif ($event->isTransferEvent()) {
        // Handle transfer-related webhook
    }

    // Or using the enum directly
    if ($eventTypeEnum?->isPaymentEvent()) {
        // Handle payment-related webhook
    } elseif ($eventTypeEnum?->isTransferEvent()) {
        // Handle transfer-related webhook
    }

    if ($event->isSuccessful()) {
        // Transaction was successful
    }
});

// app/Listeners/ProcessSuccessfulPayment.php
namespace App\Listeners;

use Gowelle\Flutterwave\Events\FlutterwaveWebhookReceived;

class ProcessSuccessfulPayment
{
    public function handle(FlutterwaveWebhookReceived $event): void
    {
        if (!$event->isPaymentEvent() || !$event->isSuccessful()) {
            return;
        }

        $transactionData = $event->getTransactionData();
        $chargeId = $transactionData['id'] ?? null;

        // Update your payment record, send confirmation email, etc.
    }
}

use App\Listeners\ProcessSuccessfulPayment;
use Gowelle\Flutterwave\Events\FlutterwaveWebhookReceived;

protected $listen = [
    FlutterwaveWebhookReceived::class => [
        ProcessSuccessfulPayment::class,
    ],
];

use Gowelle\Flutterwave\Events\FlutterwaveWebhookReceived;
use Illuminate\Support\Facades\Event;

Event::listen(FlutterwaveWebhookReceived::class, function (FlutterwaveWebhookReceived $event) {
    $payload = $event->payload;
    $eventType = $event->getEventType(); // Returns string
    $data = $event->getTransactionData();

    // Process webhook event based on type
    match ($eventType) {
        'charge.completed' => $this->handleChargeCompleted($data),
        'charge.failed' => $this->handleChargeFailed($data),
        'transfer.completed' => $this->handleTransferCompleted($data),
        default => logger()->info('Unhandled webhook event', ['type' => $eventType]),
    };
});

use Gowelle\Flutterwave\Enums\WebhookEventType;
use Gowelle\Flutterwave\Events\FlutterwaveWebhookReceived;
use Illuminate\Support\Facades\Event;

Event::listen(FlutterwaveWebhookReceived::class, function (FlutterwaveWebhookReceived $event) {
    $eventTypeEnum = $event->getEventTypeEnum(); // Returns WebhookEventType enum
    $data = $event->getTransactionData();

    if ($eventTypeEnum === null) {
        logger()->warning('Unknown webhook event type', ['payload' => $event->payload]);
        return;
    }

    // Use enum helper methods
    if ($eventTypeEnum->isPaymentEvent()) {
        // Handle payment-related webhook
        if ($eventTypeEnum->isSuccessful()) {
            $this->handleSuccessfulPayment($data);
        } else {
            $this->handleFailedPayment($data);
        }
    } elseif ($eventTypeEnum->isTransferEvent()) {
        // Handle transfer-related webhook
        $this->handleTransfer($data);
    }

    // Or use match with enum cases
    match ($eventTypeEnum) {
        WebhookEventType::CHARGE_COMPLETED => $this->handleChargeCompleted($data),
        WebhookEventType::CHARGE_FAILED => $this->handleChargeFailed($data),
        WebhookEventType::CHARGE_SUCCESSFUL => $this->handleChargeSuccessful($data),
        WebhookEventType::PAYMENT_COMPLETED => $this->handlePaymentCompleted($data),
        WebhookEventType::PAYMENT_FAILED => $this->handlePaymentFailed($data),
        WebhookEventType::PAYMENT_SUCCESSFUL => $this->handlePaymentSuccessful($data),
        WebhookEventType::TRANSFER_COMPLETED => $this->handleTransferCompleted($data),
    };
});
bash
composer 
bash
php artisan vendor:publish --tag="flutterwave-config"
bash
php artisan vendor:publish --tag="flutterwave-config"
php artisan vendor:publish --tag="flutterwave-migrations"
bash
php artisan flutterwave:verify
bash
php artisan migrate
bash
# Publish Livewire Blade views (for customization)
php artisan vendor:publish --tag=flutterwave-views

# Publish Vue components (for Vue/Inertia apps)
php artisan vendor:publish --tag=flutterwave-vue
blade
{{-- resources/views/livewire/checkout-page.blade.php --}}
<div class="checkout-container">
    @if ($error)
        <div class="alert alert-danger">{{ $error }}</div>
    @endif

    @if ($step === 'form')
        <livewire:flutterwave-payment-form
            :amount="$amount"
            :currency="$currency"
            :customer="['email' => auth()->user()->email, 'firstName' => auth()->user()->first_name, 'lastName' => auth()->user()->last_name]"
        />
    @elseif ($step === 'pin')
        <livewire:flutterwave-pin-input :charge-id="$chargeId" />
    @elseif ($step === 'otp')
        <livewire:flutterwave-otp-input :charge-id="$chargeId" />
    @elseif ($step === 'status')
        <livewire:flutterwave-payment-status :charge-id="$chargeId" :auto-poll="true" />
    @endif
</div>
bash
php artisan vendor:publish --tag=flutterwave-vue
bash
php artisan vendor:publish --tag="flutterwave-translations"
vue
<script setup>
const customLabels = {
    pay: 'Lipa Sasa',
    processing: 'Inachakata...'
};
</script>

<template>
    <PaymentForm :labels="customLabels" />
</template>
bash
php artisan vendor:publish --tag="flutterwave-migrations"
php artisan migrate
bash
php artisan flutterwave:cleanup-sessions
php
use Gowelle\Flutterwave\Exceptions\FlutterwaveException;
use Gowelle\Flutterwave\Facades\Flutterwave;

try {
    $payment = Flutterwave::payments()->process($data);
} catch (FlutterwaveException $e) {
    // Get user-friendly message
    $userMessage = $e->getUserFriendlyMessage();

    // Check error type
    if ($e->isValidationError()) {
        // Handle validation error (400)
        logger()->warning('Validation error', ['message' => $userMessage]);
    } elseif ($e->isAuthenticationError()) {
        // Handle authentication error (401)
        logger()->error('Authentication failed', ['message' => $userMessage]);
    } elseif ($e->isRateLimitError()) {
        // Handle rate limit error (429)
        logger()->warning('Rate limit exceeded', ['message' => $userMessage]);
    } else {
        // Handle other API errors
        logger()->error('API error', ['message' => $userMessage]);
    }

    // Get technical details
    $errorData = $e->getErrorData();
    logger()->error('Error details', [
        'message' => $errorData->getMessage(),
        'code' => $errorData->getCode(),
        'type' => $errorData->getType(),
    ]);
}
php
$dto = ChargeRequestBuilder::for('ORDER-123')
    ->amount(150, 'NGN')
    ->customer('[email protected]', 'John', 'Doe', '+234812345678')
    ->card('5531886652142950', '09', '32', '564')
    ->redirectUrl('https://example.com/callback')
    ->meta(['order_id' => '12345'])
    ->customizations('My Store', 'Complete your purchase')
    ->idempotencyKey('unique-key-' . time())
    ->traceId('trace-' . uniqid())
    ->userId(auth()->id())
    ->paymentId($payment->id)
    ->build();
php
use Gowelle\Flutterwave\Support\EncryptionService;

$encryptionService = new EncryptionService(config('flutterwave.encryption_key'));

$encryptedCard = $encryptionService->encryptCardData([
    'card_number' => '5531886652142950',
    'expiry_month' => '09',
    'expiry_year' => '32',
    'cvv' => '564',
]);

// Returns: [
//     'nonce' => 'random_12_char_nonce',
//     'encrypted_card_number' => 'base64_encoded_ciphertext',
//     'encrypted_expiry_month' => 'base64_encoded_ciphertext',
//     'encrypted_expiry_year' => 'base64_encoded_ciphertext',
//     'encrypted_cvv' => 'base64_encoded_ciphertext',
// ]
php
/**
 * Generate a cryptographically secure 12-character alphanumeric nonce
 */
function generateSecureNonce(int $length = 12): string
{
    $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    $charactersLength = strlen($characters);
    $nonce = '';

    // Generate cryptographically secure random bytes
    $randomBytes = random_bytes($length);

    // Map bytes to alphanumeric characters
    for ($i = 0; $i < $length; $i++) {
        $nonce .= $characters[ord($randomBytes[$i]) % $charactersLength];
    }

    return $nonce;
}

function encryptCardData(string $plainText, string $encryptionKey, string $nonce): string
{
    $key = base64_decode($encryptionKey);
    $iv = $nonce; // 12-character nonce

    // Encrypt using AES-256-GCM
    $encrypted = openssl_encrypt(
        $plainText,
        'aes-256-gcm',
        $key,
        OPENSSL_RAW_DATA,
        $iv,
        $tag
    );

    // Combine encrypted data with authentication tag
    $encryptedWithTag = $encrypted . $tag;

    // Base64 encode
    return base64_encode($encryptedWithTag);
}

// Usage
$encryptionKey = 'your_base64_encoded_encryption_key_from_dashboard';
$nonce = generateSecureNonce(12); // Generate cryptographically secure 12-character nonce

$encryptedCardNumber = encryptCardData('5531886652142950', $encryptionKey, $nonce);
$encryptedCvv = encryptCardData('564', $encryptionKey, $nonce);
$encryptedExpiryMonth = encryptCardData('09', $encryptionKey, $nonce);
$encryptedExpiryYear = encryptCardData('32', $encryptionKey, $nonce);
php
$charge = Flutterwave::directCharge()->create([
    'amount' => 1000,
    'currency' => 'TZS',
    'reference' => 'ORDER-123',
    'idempotency_key' => 'unique-key-' . time(),
    // ... other data
]);
php
use Gowelle\Flutterwave\Services\FlutterwaveDirectChargeService;

class PaymentController
{
    public function __construct(
        private FlutterwaveDirectChargeService $chargeService
    ) {}

    public function process()
    {
        $charge = $this->chargeService->create([...]);
    }
}