This guide explains how to integrate Skrill payment gateway with the Laravel Payments package using their PSD2 API.
Skrill (formerly Moneybookers) is a global digital wallet and payment gateway that supports:
Skrill integration uses direct API calls through their PSD2 API, so no additional package is required. Just ensure you have the Laravel Payments package installed and configured.
Add your Skrill credentials to your .env file:
SKRILL_MERCHANT_ID=your_merchant_id_here
SKRILL_API_KEY=your_api_key_here
SKRILL_TEST_MODE=true
SKRILL_WEBHOOK_URL=https://yoursite.com/skrill/webhook
SKRILL_RETURN_URL=https://yoursite.com/payment/success
You can obtain these credentials from your Skrill merchant dashboard.
For development/testing:
SKRILL_TEST_MODE=true
SKRILL_MERCHANT_ID=test_merchant_id
SKRILL_API_KEY=test_api_key
For production:
SKRILL_TEST_MODE=false
SKRILL_MERCHANT_ID=your_production_merchant_id
SKRILL_API_KEY=your_production_api_key
You also need to add the configuration to your config/services.php:
'skrill' => [
'merchant_id' => env('SKRILL_MERCHANT_ID'),
'api_key' => env('SKRILL_API_KEY'),
'test_mode' => env('SKRILL_TEST_MODE', true),
'webhook_url' => env('SKRILL_WEBHOOK_URL'),
'return_url' => env('SKRILL_RETURN_URL'),
],
use Mdiqbal\LaravelPayments\Facades\Payment;
$paymentRequest = [
'amount' => 100.00,
'currency' => 'EUR', // Skrill's primary currency
'email' => 'customer@example.com',
'transaction_id' => 'TXN' . time(),
'redirect_url' => 'https://yoursite.com/payment/callback',
'customer' => [
'name' => 'John Doe',
'phone' => '+447123456789',
'address' => '123 Main St',
'city' => 'London',
'state' => 'London',
'country' => 'GB',
'postal_code' => 'SW1A 0AA'
],
'metadata' => [
'order_id' => 'ORD123456',
'user_id' => 789,
'customer_id' => 12345
]
];
$payment = Payment::gateway('skrill')->pay($paymentRequest);
This will return a payment URL that you need to redirect the user to:
if ($payment['success']) {
// Store session_id for later verification
session(['skrill_session_id' => $payment['session_id']]);
// Redirect to Skrill payment page
return redirect($payment['payment_url']);
}
// routes/web.php
Route::get('/payment/success', [PaymentController::class, 'success']);
Route::post('/skrill/webhook', [SkrillController::class, 'webhook']);
// app/Http/Controllers/SkrillController.php
use Mdiqbal\LaravelPayments\Facades\Payment;
class SkrillController extends Controller
{
public function webhook(Request $request)
{
// Parse webhook data
$gateway = Payment::gateway('skrill');
$webhookData = $gateway->parseCallback($request);
// Process the webhook
$result = $gateway->verify($webhookData);
if ($result['success']) {
$transactionId = $result['transaction_id'];
$status = $result['status'];
if ($status === 'completed') {
// Update order status
$order = Order::where('transaction_id', $transactionId)->first();
if ($order) {
$order->status = 'paid';
$order->paid_at = now();
$order->payment_method = $result['payment_method'];
$order->skrill_session_id = $result['session_id'];
$order->save();
}
}
}
// Always return a 200 OK response to acknowledge receipt
return response('OK', 200);
}
public function success(Request $request)
{
// User returned from Skrill after successful payment
// Note: Always rely on webhook for final status confirmation
return view('payment.success');
}
}
// First, get the session ID (stored during initialization)
$sessionId = session('skrill_session_id');
$verification = Payment::gateway('skrill')->verify($sessionId);
if ($verification['success']) {
$status = $verification['status'];
if ($status === 'completed') {
// Payment was successful
$sessionId = $verification['session_id'];
$amount = $verification['amount'];
$currency = $verification['currency'];
$paymentMethod = $verification['payment_method'];
}
}
$refundData = [
'session_id' => 'SKRILL_SESSION_ID',
'amount' => 50.00, // Optional - omit for full refund
'reason' => 'Customer requested refund'
];
$refund = Payment::gateway('skrill')->refund($refundData);
$sessionId = 'SKRILL_SESSION_ID';
$status = Payment::gateway('skrill')->getTransactionStatus($sessionId);
// Search with filters
$results = Payment::gateway('skrill')->searchTransactions([
'status' => 'processed',
'from_date' => '2024-01-01',
'to_date' => '2024-12-31',
'limit' => 20,
'offset' => 0
]);
$paymentRequest = [
'amount' => 100.00,
'currency' => 'EUR',
'email' => 'customer@example.com',
'transaction_id' => 'TXN' . time(),
'metadata' => [
'payment_methods' => ['cc', 'wallet', 'bank'], // Allowed payment methods
'language' => 'en' // Payment page language
],
'redirect_url' => 'https://yoursite.com/payment/callback'
];
$payment = Payment::gateway('skrill')->pay($paymentRequest);
$subscriptionData = [
'amount' => 29.99,
'currency' => 'EUR',
'email' => 'customer@example.com',
'customer_name' => 'John Doe',
'description' => 'Monthly Premium Subscription',
'frequency' => 1, // 1 = monthly, 2 = quarterly, 3 = yearly
'period' => 'M', // M = monthly, Q = quarterly, Y = yearly
'cycles' => 12, // Number of cycles (0 = unlimited)
'return_url' => 'https://yoursite.com/subscription/success'
];
$subscription = Payment::gateway('skrill')->createSubscription($subscriptionData);
$subscriptionId = 'SKRILL_SUBSCRIPTION_ID';
$result = Payment::gateway('skrill')->cancelSubscription($subscriptionId);
Skrill uses webhooks to notify your application about payment status changes.
Configure your webhook URL in the Skrill merchant dashboard under "Settings" → "Notifications"
Create a route to handle webhooks:
// routes/web.php
Route::post('/skrill/webhook', [SkrillWebhookController::class, 'handleWebhook']);
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Mdiqbal\LaravelPayments\Facades\Payment;
class SkrillWebhookController extends Controller
{
public function handleWebhook(Request $request)
{
$gateway = Payment::gateway('skrill');
$webhookData = $gateway->parseCallback($request);
// Process the webhook
$result = $gateway->verify($webhookData);
if ($result['success']) {
$eventType = $result['event_type'];
$transactionId = $result['transaction_id'];
$sessionId = $result['session_id'];
switch ($eventType) {
case 'payment.completed':
$this->handleSuccessfulPayment($result);
break;
case 'payment.pending':
$this->handlePendingPayment($result);
break;
case 'payment.failed':
$this->handleFailedPayment($result);
break;
case 'payment.cancelled':
$this->handleCancelledPayment($result);
break;
case 'payment.refunded':
$this->handleRefundedPayment($result);
break;
default:
$this->logInfo('Unknown webhook event: ' . $eventType, $result);
}
}
// Always return 200 OK to acknowledge receipt
return response('OK', 200);
}
protected function handleSuccessfulPayment($data)
{
// Update order status
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'paid';
$order->paid_at = now();
$order->payment_method = $data['payment_method'];
$order->skrill_session_id = $data['session_id'];
$order->merchant_info = array_merge($order->merchant_info ?? [], $data['merchant_info']);
$order->save();
// Send confirmation email
Mail::to($data['customer_info']['email'])->send(new PaymentConfirmation($order));
}
}
protected function handlePendingPayment($data)
{
// Payment is being processed
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'processing';
$order->save();
}
}
protected function handleFailedPayment($data)
{
// Log failed payment
Log::warning('Skrill payment failed', [
'transaction_id' => $data['transaction_id'],
'session_id' => $data['session_id']
]);
// Update order status
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'failed';
$order->save();
}
}
protected function handleCancelledPayment($data)
{
// Handle cancelled payment
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'cancelled';
$order->save();
}
}
protected function handleRefundedPayment($data)
{
// Process refund notification
$order = Order::where('transaction_id', $data['transaction_id'])->first();
if ($order) {
$order->status = 'refunded';
$order->save();
}
}
protected function logInfo($message, $data)
{
Log::info($message, $data);
}
}
pay() to create a payment sessionverify() to confirm payment statusSkrill webhook security features:
The Skrill gateway provides detailed error messages:
$payment = Payment::gateway('skrill')->pay($paymentRequest);
if (!$payment['success']) {
$error = $payment['error'];
$message = $error['message'];
$code = $error['code'];
// Handle error based on type
if ($code === 'PAYMENT_FAILED') {
// Payment initialization failed
}
}
PAYMENT_FAILED - Payment initialization failedVERIFICATION_FAILED - Transaction verification failedREFUND_FAILED - Refund processing failedWEBHOOK_FAILED - Webhook processing failedINVALID_CURRENCY - Currency not supportedINVALID_REQUEST - Invalid request parametersSESSION_EXPIRED - Payment session expiredINSUFFICIENT_FUNDS - Insufficient funds in customer accountUse test credentials for development:
SKRILL_TEST_MODE=true
For testing in sandbox mode:
cc - Credit/Debit Cardwallet - Skrill Walletbank - Bank Transferlocal_method - Country-specific local payment methodcrypto - CryptocurrencySkrill supports a wide range of currencies:
Skrill operates globally with varying feature availability:
Skrill implements reasonable rate limits:
pay() - Initialize a payment and create sessionverify($payload) - Verify webhook payloadverify($sessionId) - Verify a transaction status via APIrefund() - Process a refund (full or partial)getTransactionStatus() - Get transaction statussearchTransactions() - Search transactions with filterscreateSubscription() - Create a recurring subscriptioncancelSubscription() - Cancel an active subscriptionparseCallback() - Parse webhook parameters from requestgetSupportedCurrencies() - Get supported currenciesgetGatewayConfig() - Get gateway configurationgetPaymentMethodsForCountry() - Get payment methods for a country$paymentRequest = [
'amount' => 100.00,
'currency' => 'EUR',
'email' => 'customer@example.com',
'transaction_id' => 'TXN' . time(),
'metadata' => [
'product_id' => 'PROD_123', // Custom field 1
'affiliate_id' => 'AFF_456', // Custom field 2
'campaign_id' => 'CAM_789', // Custom field 3
'user_level' => 'premium', // Custom field 4
'source' => 'mobile_app', // Custom field 5
],
'redirect_url' => 'https://yoursite.com/payment/callback'
];
$payment = Payment::gateway('skrill')->pay($paymentRequest);
class SubscriptionController extends Controller
{
public function createSubscription(Request $request)
{
$subscriptionData = [
'amount' => 29.99,
'currency' => 'EUR',
'email' => $request->input('email'),
'customer_name' => $request->input('name'),
'description' => 'Monthly Premium Plan',
'frequency' => 1, // Monthly
'period' => 'M', // Monthly
'cycles' => 12, // 12 months
'return_url' => route('subscription.success'),
'metadata' => [
'plan_type' => 'premium',
'user_id' => Auth::id()
]
];
$subscription = Payment::gateway('skrill')->createSubscription($subscriptionData);
if ($subscription['success']) {
// Store subscription details
Subscription::create([
'user_id' => Auth::id(),
'plan_id' => 'premium_monthly',
'amount' => 29.99,
'currency' => 'EUR',
'status' => 'pending',
'skrill_session_id' => $subscription['session_id']
]);
return redirect($subscription['payment_url']);
}
return back()->with('error', 'Failed to create subscription');
}
public function processSubscription($sessionId)
{
$subscription = Subscription::where('skrill_session_id', $sessionId)->first();
if ($subscription) {
$status = Payment::gateway('skrill')->verify($sessionId);
if ($status['success'] && $status['status'] === 'completed') {
$subscription->update([
'status' => 'active',
'activated_at' => now()
]);
// Grant user access
Auth::user()->update(['premium_until' => now()->addMonth()]);
}
}
}
}
class PaymentController extends Controller
{
public function createPayment(Request $request)
{
$userCurrency = $this->getUserCurrency($request->ip());
$amount = $this->convertAmount($request->input('amount'), 'USD', $userCurrency);
$paymentRequest = [
'amount' => $amount,
'currency' => $userCurrency,
'email' => Auth::user()->email,
'transaction_id' => 'TXN_' . time() . '_' . $userCurrency,
'customer' => [
'name' => Auth::user()->name,
'country' => $this->getCountryCode($request->ip()),
],
'redirect_url' => route('payment.callback')
];
return Payment::gateway('skrill')->pay($paymentRequest);
}
private function convertAmount($amount, $from, $to)
{
// Implement currency conversion logic
return $amount; // Simplified for example
}
}
class SkrillPaymentService
{
protected function handleApiError($response, $request)
{
$errorData = $response->json();
Log::error('Skrill API Error', [
'status_code' => $response->status(),
'error_code' => $errorData['error']['code'] ?? 'unknown',
'error_message' => $errorData['error']['message'] ?? 'Unknown error',
'request_data' => $request,
'timestamp' => now()
]);
// Send alert to admin for critical errors
if ($response->status() >= 500) {
$this->sendAlertToAdmin('Skrill API Error', $errorData['error']['message'] ?? 'API Error');
}
// Return user-friendly error message
return $this->getUserFriendlyErrorMessage($errorData['error']['code'] ?? 'unknown');
}
private function getUserFriendlyErrorMessage($errorCode)
{
$errorMessages = [
'INVALID_AMOUNT' => 'Invalid payment amount',
'INVALID_CURRENCY' => 'Currency not supported',
'INVALID_EMAIL' => 'Invalid email address',
'SESSION_EXPIRED' => 'Payment session has expired',
'INSUFFICIENT_FUNDS' => 'Insufficient funds in account',
'CARD_DECLINED' => 'Payment card was declined',
'NETWORK_ERROR' => 'Payment processing error, please try again'
];
return $errorMessages[$errorCode] ?? 'Payment processing failed';
}
}
For Skrill-specific support:
How can I help you explore Laravel packages today?