PHP code example of mrdulal / laravel-vipps

1. Go to this page and download the library: Download mrdulal/laravel-vipps 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/ */

    

mrdulal / laravel-vipps example snippets


// config/app.php
'providers' => [
    // ...
    Mrdulal\LaravelVipps\VippsServiceProvider::class,
],

'aliases' => [
    // ...
    'Vipps' => Mrdulal\LaravelVipps\Facades\Vipps::class,
],

return [
    // Environment (test/production)
    'environment' => env('VIPPS_ENVIRONMENT', 'test'),
    
    // API Credentials
    'client_id' => env('VIPPS_CLIENT_ID'),
    'client_secret' => env('VIPPS_CLIENT_SECRET'),
    'subscription_key' => env('VIPPS_SUBSCRIPTION_KEY'),
    'merchant_serial_number' => env('VIPPS_MERCHANT_SERIAL_NUMBER'),
    
    // Feature toggles
    'features' => [
        'epayment' => true,
        'checkout' => true,
        'express' => true,
        'recurring' => true,
        'order_management' => true,
        'webhooks' => true,
    ],
    
    // HTTP client settings
    'http' => [
        'timeout' => 30,
        'connect_timeout' => 10,
        'verify' => true,
    ],
    
    // Express checkout UI settings
    'express' => [
        'button_theme' => 'orange',  // orange, white, white-outline
        'button_size' => 'large',    // small, medium, large
        'show_on_cart' => true,
        'show_on_product' => true,
    ],
];

// routes/web.php or routes/api.php
Route::post('/vipps/webhook', [\Mrdulal\LaravelVipps\Http\Controllers\WebhookController::class, 'handle']);

use Mrdulal\LaravelVipps\Facades\Vipps;

// Create a payment
$payment = Vipps::ePayment()->create([
    'amount' => 10000, // Amount in øre (100.00 NOK)
    'currency' => 'NOK',
    'orderId' => 'order-123',
    'description' => 'Payment for order #123',
    'redirectUrl' => 'https://yoursite.com/payment/callback',
    'userFlow' => 'WEB_REDIRECT'
]);

// Get payment details
$details = Vipps::ePayment()->getPayment($payment['orderId']);

// Capture payment
$capture = Vipps::ePayment()->capture($payment['orderId'], [
    'amount' => 10000,
    'description' => 'Capture for order #123'
]);

use Mrdulal\LaravelVipps\Facades\Vipps;

// Create express checkout session
$session = Vipps::express()->create([
    'amount' => 10000,
    'currency' => 'NOK',
    'orderId' => 'order-123',
    'description' => 'Express checkout for order #123',
    'redirectUrl' => 'https://yoursite.com/express/callback',
    'userInfo' => [
        'userId' => 'user-123'
    ]
]);

// Generate QR code for express checkout
$qrCode = Vipps::express()->generateQrCode([
    'orderId' => 'order-123',
    'amount' => 10000,
    'currency' => 'NOK',
    'description' => 'Product purchase',
    'redirectUrl' => 'https://yoursite.com/callback',
    'qrFormat' => 'SVG', // or 'PNG'
    'qrSize' => 300
]);

// Create shareable payment link
$shareableLink = Vipps::express()->createShareableLink([
    'orderId' => 'order-456',
    'amount' => 15000,
    'currency' => 'NOK',
    'description' => 'Shareable payment link',
    'redirectUrl' => 'https://yoursite.com/callback',
    'expiresAt' => now()->addHours(24)
]);

use Mrdulal\LaravelVipps\Facades\Vipps;

// Create recurring agreement
$agreement = Vipps::recurring()->createAgreement([
    'currency' => 'NOK',
    'price' => 9900, // 99.00 NOK
    'productName' => 'Monthly Subscription',
    'productDescription' => 'Premium subscription service',
    'merchantRedirectUrl' => 'https://yoursite.com/recurring/callback',
    'merchantAgreementUrl' => 'https://yoursite.com/agreement/123',
    'interval' => 'MONTH',
    'intervalCount' => 1,
    'isApp' => false
]);

// Create charge for agreement
$charge = Vipps::recurring()->createCharge($agreement['agreementId'], [
    'amount' => 9900,
    'currency' => 'NOK',
    'description' => 'Monthly subscription charge',
    'orderId' => 'charge-456'
]);

// List all charges for an agreement
$charges = Vipps::recurring()->listCharges($agreement['agreementId']);

// Stop an agreement
$result = Vipps::recurring()->stopAgreement($agreement['agreementId'], [
    'reason' => 'Customer requested cancellation'
]);

Vipps::ePayment()->create([
    'amount' => 10000,              // Required: Amount in øre (100.00 NOK)
    'currency' => 'NOK',            // Optional: NOK, DKK, EUR (default: NOK)
    'orderId' => 'order-123',       // Required: Unique order identifier
    'description' => 'Payment for order #123',  // Required: Payment description
    'redirectUrl' => 'https://yoursite.com/callback',  // Required: Callback URL
    'userFlow' => 'WEB_REDIRECT',   // Optional: WEB_REDIRECT, NATIVE_REDIRECT
    'paymentMethod' => 'WALLET',    // Optional: WALLET, CARD
    'skipLandingPage' => false,     // Optional: Skip Vipps landing page
    'userInfo' => [                 // Optional: Pre-fill user information
        'userId' => 'user-123',
        'mobileNumber' => '+4712345678',
        'email' => '[email protected]'
    ],
    'reference' => 'ref-456'        // Optional: Your internal reference
]);

Vipps::ePayment()->capture('order-123', [
    'amount' => 10000,              // Required: Amount to capture
    'description' => 'Capture for order #123',  // Required
    'reference' => 'capture-ref'    // Optional
]);

Vipps::ePayment()->refund('order-123', [
    'amount' => 5000,               // Required: Amount to refund
    'description' => 'Partial refund',  // Required
    'reference' => 'refund-ref'     // Optional
]);

Vipps::express()->generateQrCode([
    'orderId' => 'qr-order-123',
    'amount' => 15000,
    'currency' => 'NOK',
    'description' => 'QR Code Payment',
    'redirectUrl' => 'https://yoursite.com/callback',
    'qrFormat' => 'SVG',            // SVG or PNG
    'qrSize' => 300                 // Size in pixels (100-2000)
]);

Vipps::express()->createShareableLink([
    'orderId' => 'share-order-123',
    'amount' => 20000,
    'currency' => 'NOK',
    'description' => 'Shareable Payment Link',
    'redirectUrl' => 'https://yoursite.com/callback',
    'expiresAt' => now()->addDays(7)  // Optional: Link expiration
]);

Vipps::recurring()->createAgreement([
    'currency' => 'NOK',
    'price' => 9900,                // Monthly price in øre
    'productName' => 'Premium Plan', // Max 45 characters
    'productDescription' => 'Monthly premium subscription',  // Max 100 chars
    'merchantRedirectUrl' => 'https://yoursite.com/recurring/success',
    'merchantAgreementUrl' => 'https://yoursite.com/terms',
    'interval' => 'MONTH',          // WEEK, MONTH, YEAR
    'intervalCount' => 1,           // How many intervals between charges
    'isApp' => false,               // True if initiated from mobile app
    'phoneNumber' => '+4712345678', // Optional: Pre-fill phone
    'campaign' => [                 // Optional: Campaign pricing
        'start' => now(),
        'end' => now()->addMonths(3),
        'price' => 4900             // Campaign price in øre
    ]
]);

$history = Vipps::orderManagement()->getPaymentHistory('order-123');
// Returns array of all transactions for the order

Vipps::orderManagement()->sendReceipt('order-123', [
    'orderLines' => [
        [
            'name' => 'Premium Subscription',
            'id' => 'sub-001',
            'totalAmount' => 9900,
            'totalAmountExcludingTax' => 7920,
            'totalTaxAmount' => 1980,
            'taxRate' => 25.0,
            'unitInfo' => [
                'unitPrice' => 9900,
                'quantity' => '1',
                'quantityUnit' => 'month'
            ]
        ]
    ],
    'bottomLine' => [
        'currency' => 'NOK',
        'tipAmount' => 500,
        'barcode' => [
            'format' => 'EAN-13',
            'data' => '1234567890123'
        ]
    ]
]);

// routes/web.php or routes/api.php
Route::post('/vipps/webhook', [\Mrdulal\LaravelVipps\Http\Controllers\WebhookController::class, 'handle']);

// app/Providers/EventServiceProvider.php
use Mrdulal\LaravelVipps\Events\{
    PaymentCompleted,
    PaymentCancelled,
    PaymentCaptured,
    PaymentRefunded,
    RecurringAgreementCreated,
    RecurringChargeCreated
};

protected $listen = [
    PaymentCompleted::class => [
        \App\Listeners\HandlePaymentCompleted::class,
    ],
    PaymentCancelled::class => [
        \App\Listeners\HandlePaymentCancelled::class,
    ],
    PaymentCaptured::class => [
        \App\Listeners\HandlePaymentCaptured::class,
    ],
    PaymentRefunded::class => [
        \App\Listeners\HandlePaymentRefunded::class,
    ],
    RecurringAgreementCreated::class => [
        \App\Listeners\HandleRecurringAgreementCreated::class,
    ],
    RecurringChargeCreated::class => [
        \App\Listeners\HandleRecurringChargeCreated::class,
    ],
];



namespace App\Listeners;

use Mrdulal\LaravelVipps\Events\PaymentCompleted;
use Illuminate\Contracts\Queue\ShouldQueue;

class HandlePaymentCompleted implements ShouldQueue
{
    public function handle(PaymentCompleted $event): void
    {
        $orderId = $event->orderId;
        $payload = $event->payload;
        
        // Update your order status
        $order = Order::where('order_id', $orderId)->first();
        if ($order) {
            $order->update(['status' => 'paid']);
            
            // Send confirmation email
            Mail::to($order->customer_email)->send(new PaymentConfirmation($order));
        }
    }
}

// Webhook verification is automatic, but you can customize it
'webhook' => [
    'secret' => env('VIPPS_WEBHOOK_SECRET'),
    'tolerance' => 300,              // 5 minutes tolerance
    'verify_signature' => true,      // Enable signature verification
],

// Listen to all Vipps events with a single listener
Event::listen('Mrdulal\LaravelVipps\Events\*', function ($eventName, array $data) {
    $eventObject = $data[0];
    Log::info("Vipps event fired: {$eventName}", [
        'order_id' => $eventObject->orderId,
        'payload' => $eventObject->payload
    ]);
});



namespace Tests\Feature;

use Tests\TestCase;
use Mrdulal\LaravelVipps\Facades\Vipps;
use Mrdulal\LaravelVipps\Services\VippsClient;
use Mockery;

class VippsPaymentTest extends TestCase
{
    public function test_can_create_payment()
    {
        // Mock the VippsClient
        $mockClient = Mockery::mock(VippsClient::class);
        $mockClient->shouldReceive('post')
            ->once()
            ->with('https://apitest.vipps.no/ecomm/v2/payments', Mockery::type('array'))
            ->andReturn([
                'orderId' => 'test-order-123',
                'url' => 'https://test.vipps.no/redirect-url',
                'token' => 'test-token'
            ]);

        $this->app->instance(VippsClient::class, $mockClient);

        // Test payment creation
        $response = Vipps::ePayment()->create([
            'amount' => 10000,
            'currency' => 'NOK',
            'orderId' => 'test-order-123',
            'description' => 'Test payment',
            'redirectUrl' => 'https://example.com/callback'
        ]);

        $this->assertEquals('test-order-123', $response['orderId']);
        $this->assertArrayHasKey('url', $response);
    }
}



namespace Tests\Feature;

use Tests\TestCase;
use Mrdulal\LaravelVipps\Events\PaymentCompleted;
use Illuminate\Support\Facades\Event;

class WebhookTest extends TestCase
{
    public function test_payment_completed_webhook()
    {
        Event::fake();

        $payload = [
            'eventType' => 'SALE',
            'transactionInfo' => [
                'orderId' => 'test-order-123',
                'amount' => 10000,
                'status' => 'COMPLETED'
            ]
        ];

        $response = $this->postJson('/vipps/webhook', $payload);

        $response->assertStatus(200);
        Event::assertDispatched(PaymentCompleted::class);
    }
}

// phpunit.xml
<phpunit>
    <php>
        <env name="VIPPS_ENVIRONMENT" value="test"/>
        <env name="VIPPS_CLIENT_ID" value="test_client_id"/>
        <env name="VIPPS_CLIENT_SECRET" value="test_client_secret"/>
        <env name="VIPPS_SUBSCRIPTION_KEY" value="test_subscription_key"/>
        <env name="VIPPS_MERCHANT_SERIAL_NUMBER" value="123456"/>
    </php>
</phpunit>

// config/vipps.php
return [
    // Environment & Credentials
    'environment' => env('VIPPS_ENVIRONMENT', 'test'),
    'client_id' => env('VIPPS_CLIENT_ID'),
    'client_secret' => env('VIPPS_CLIENT_SECRET'),
    'subscription_key' => env('VIPPS_SUBSCRIPTION_KEY'),
    'merchant_serial_number' => env('VIPPS_MERCHANT_SERIAL_NUMBER'),

    // API Endpoints (auto-configured based on environment)
    'api_endpoints' => [
        'test' => [
            'base_url' => 'https://apitest.vipps.no',
            'ecom_url' => 'https://apitest.vipps.no/ecomm/v2',
            'checkout_url' => 'https://apitest.vipps.no/checkout/v3',
            'recurring_url' => 'https://apitest.vipps.no/recurring/v3',
            'order_management_url' => 'https://apitest.vipps.no/order-management/v1',
        ],
        'production' => [
            'base_url' => 'https://api.vipps.no',
            'ecom_url' => 'https://api.vipps.no/ecomm/v2',
            'checkout_url' => 'https://api.vipps.no/checkout/v3',
            'recurring_url' => 'https://api.vipps.no/recurring/v3',
            'order_management_url' => 'https://api.vipps.no/order-management/v1',
        ],
    ],

    // HTTP Client Settings
    'http' => [
        'timeout' => 30,                // Request timeout in seconds
        'connect_timeout' => 10,        // Connection timeout
        'verify' => true,               // SSL certificate verification
        'headers' => [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ],

    // Webhook Security
    'webhook' => [
        'secret' => env('VIPPS_WEBHOOK_SECRET'),
        'tolerance' => 300,             // 5 minutes tolerance for timestamp
        'verify_signature' => true,     // Enable HMAC signature verification
    ],

    // Default Payment Settings
    'defaults' => [
        'currency' => 'NOK',
        'country' => 'NO',
        'language' => 'no',
        'skip_landing_page' => false,
        'user_flow' => 'WEB_REDIRECT',
    ],

    // Recurring Payment Limits
    'recurring' => [
        'default_interval' => 'MONTH',
        'default_interval_count' => 1,
        'max_amount' => 200000,         // 2000.00 NOK in øre
        'default_currency' => 'NOK',
    ],

    // Express Checkout UI
    'express' => [
        'enabled' => true,
        'button_theme' => 'orange',     // orange, white, white-outline
        'button_size' => 'large',       // small, medium, large
        'show_on_cart' => true,
        'show_on_product' => true,
        'show_on_category' => false,
    ],

    // Logging & Debugging
    'logging' => [
        'enabled' => env('VIPPS_LOGGING_ENABLED', false),
        'channel' => env('VIPPS_LOG_CHANNEL', 'default'),
        'level' => env('VIPPS_LOG_LEVEL', 'info'),
        'log_requests' => true,         // Log API requests
        'log_responses' => true,        // Log API responses
        'log_headers' => false,         // Log headers (may contain sensitive data)
    ],

    // Caching (for access tokens)
    'cache' => [
        'enabled' => true,
        'ttl' => 3600,                  // 1 hour cache TTL
        'prefix' => 'vipps_',
        'store' => null,                // Use default cache store
    ],

    // Error Handling
    'error_handling' => [
        'retry_attempts' => 3,
        'retry_delay' => 1000,          // milliseconds
        'throw_on_error' => true,
    ],

    // Feature Toggles
    'features' => [
        'epayment' => true,
        'checkout' => true,
        'express' => true,
        'recurring' => true,
        'order_management' => true,
        'webhooks' => true,
    ],
];

// Capture full or partial amount
$capture = Vipps::orderManagement()->capture('order-123', [
    'amount' => 5000,               // Partial capture (original: 10000)
    'description' => 'Partial shipment - Item 1'
]);

// Multiple captures are allowed until full amount is captured
$capture2 = Vipps::orderManagement()->capture('order-123', [
    'amount' => 5000,               // Remaining amount
    'description' => 'Final shipment - Item 2'
]);

// Refund captured payment
$refund = Vipps::orderManagement()->refund('order-123', [
    'amount' => 2000,               // Partial refund
    'description' => 'Product return - defective item'
]);

// Cancel uncaptured payment
$cancel = Vipps::orderManagement()->cancel('order-123', [
    'description' => 'Order cancelled by customer'
]);

// Get complete payment history
$history = Vipps::orderManagement()->getPaymentHistory('order-123');
// Returns: reservations, captures, refunds, cancellations

// Get current order status
$status = Vipps::orderManagement()->getOrderStatus('order-123');

VippsException                    // Base exception
├── VippsApiException            // API-related errors
├── VippsValidationException     // Input validation errors
└── Custom exceptions...

use Mrdulal\LaravelVipps\Exceptions\{VippsException, VippsValidationException, VippsApiException};

try {
    $payment = Vipps::ePayment()->create($paymentData);
} catch (VippsValidationException $e) {
    // Handle validation errors (400-level client errors)
    $errors = $e->getValidationErrors();
    
    foreach ($errors as $field => $messages) {
        Log::warning("Validation error on {$field}: " . implode(', ', $messages));
    }
    
    return response()->json(['errors' => $errors], 422);
    
} catch (VippsApiException $e) {
    // Handle API errors (Vipps API returned an error)
    $apiError = $e->getApiError();
    $statusCode = $e->getCode();
    
    Log::error('Vipps API Error', [
        'status_code' => $statusCode,
        'error' => $apiError,
        'message' => $e->getMessage()
    ]);
    
    // Handle specific API errors
    switch ($statusCode) {
        case 401:
            // Unauthorized - check credentials
            break;
        case 403:
            // Forbidden - check permissions
            break;
        case 429:
            // Rate limited - implement backoff
            break;
        case 500:
            // Server error - retry later
            break;
    }
    
} catch (VippsException $e) {
    // Handle general Vipps errors (network issues, etc.)
    Log::error('Vipps General Error: ' . $e->getMessage());
    
} catch (\Exception $e) {
    // Handle unexpected errors
    Log::critical('Unexpected error in Vipps integration', [
        'exception' => $e->getMessage(),
        'trace' => $e->getTraceAsString()
    ]);
}

// Error: Invalid credentials
[
    'code' => 401,
    'message' => 'Access denied',
    'details' => 'Invalid client credentials'
]

// Solution: Verify your credentials in .env file

// Error: Invalid amount
[
    'field' => 'amount',
    'errors' => ['The amount must be at least 100 øre (1.00 NOK)']
]

// Solution: Ensure amount is in øre (multiply by 100)
$amount = 99.50 * 100; // 9950 øre

// Error: Duplicate order ID
[
    'code' => 409,
    'message' => 'Order ID already exists'
]

// Solution: Ensure unique order IDs
$orderId = 'order-' . time() . '-' . uniqid();

// Test basic API connectivity
try {
    $client = app(VippsClient::class);
    $token = $client->getAccessToken();
    echo "✅ API connection successful. Token: " . substr($token, 0, 20) . "...";
} catch (Exception $e) {
    echo "❌ API connection failed: " . $e->getMessage();
}

// app/Console/Commands/VippsHealthCheck.php


namespace App\Console\Commands;

use Illuminate\Console\Command;
use Mrdulal\LaravelVipps\Services\VippsClient;

class VippsHealthCheck extends Command
{
    protected $signature = 'vipps:health-check';
    protected $description = 'Check Vipps API connectivity';

    public function handle()
    {
        try {
            $client = app(VippsClient::class);
            $token = $client->getAccessToken();
            
            $this->info('✅ Vipps API is accessible');
            $this->info('Token: ' . substr($token, 0, 20) . '...');
            
            return 0;
        } catch (\Exception $e) {
            $this->error('❌ Vipps API check failed: ' . $e->getMessage());
            return 1;
        }
    }
}

// Optimize access token caching
'cache' => [
    'enabled' => true,
    'ttl' => 3600,              // 1 hour (tokens are valid for 1 hour)
    'store' => 'redis',         // Use Redis for better performance
],

// Queue webhook processing for better performance
// app/Listeners/HandlePaymentCompleted.php
class HandlePaymentCompleted implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $queue = 'vipps-webhooks';
    public $timeout = 60;
    public $tries = 3;

    public function handle(PaymentCompleted $event): void
    {
        // Process payment completion
    }
}

// Add indexes for better query performance
Schema::table('vipps_payments', function (Blueprint $table) {
    $table->index(['status', 'created_at']);
    $table->index(['merchant_serial_number', 'order_id']);
});
bash
# Publish configuration file
php artisan vendor:publish --provider="Mrdulal\LaravelVipps\VippsServiceProvider" --tag="config"

# Publish migrations
php artisan vendor:publish --provider="Mrdulal\LaravelVipps\VippsServiceProvider" --tag="migrations"

# Publish views (optional)
php artisan vendor:publish --provider="Mrdulal\LaravelVipps\VippsServiceProvider" --tag="views"
bash
php artisan migrate
bash
# Cron job for health checks
# /etc/cron.d/vipps-health-check
*/5 * * * * php /path/to/artisan vipps:health-check || echo "Vipps API down" | mail -s "Alert: Vipps API Issue" [email protected]