el3wdy/skipcash-laravel

Production-ready Laravel package for SkipCash payment integration with first-class developer experience

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/el3wdy/skipcash-laravel

dev-main 2025-09-02 22:08 UTC

This package is not auto-updated.

Last update: 2026-01-07 21:58:21 UTC


README

Latest Version on Packagist GitHub Tests Action Status Total Downloads

A production-ready Laravel package for SkipCash payment integration with first-class developer experience. This package provides a clean API client, secure webhook handling, events, and comprehensive test coverage for Laravel 10/11/12 (PHP 8.2+).

Features

  • ๐Ÿš€ Clean API: Fluent, type-safe SDK for payment operations
  • ๐Ÿ” Security First: HMAC-SHA256 signature verification for all requests
  • ๐ŸŽฏ Laravel Events: Automatic event dispatching for payment status changes
  • ๐Ÿ”„ Webhook Handling: Secure, idempotent webhook processing with queue support
  • ๐Ÿ“Š Local Storage: Optional payment records storage with Eloquent model
  • ๐Ÿงช Fully Tested: Comprehensive test suite with 100% coverage
  • ๐Ÿ“ Type Safe: Full PHP 8.2+ type declarations and PHPDoc annotations
  • โšก Production Ready: Built-in retries, timeouts, and error handling

Installation

You can install the package via composer:

composer require el3wdy/skipcash-laravel

Publish the configuration file:

php artisan vendor:publish --tag="skipcash-config"

Optionally, publish the migrations:

php artisan vendor:publish --tag="skipcash-migrations"
php artisan migrate

Configuration

Add the following environment variables to your .env file:

# SkipCash Environment (sandbox or production)
SKIPCASH_ENV=sandbox

# API Credentials (from SkipCash Merchant Portal > Online Payments)
SKIPCASH_KEY_ID=your-key-id
SKIPCASH_KEY_SECRET=your-key-secret
SKIPCASH_WEBHOOK_KEY=your-webhook-key

# Optional: Webhook Configuration
SKIPCASH_WEBHOOK_PATH=/skipcash/webhook
SKIPCASH_WEBHOOK_QUEUE=default

# Optional: HTTP Client Settings
SKIPCASH_TIMEOUT=10
SKIPCASH_RETRIES=3

# Optional: Local Payment Storage
SKIPCASH_STORE_PAYMENTS=true

Getting Your API Credentials

  1. Log in to your SkipCash Merchant Portal
  2. Navigate to Online Payments section
  3. Copy your Key ID and Key Secret for API authentication
  4. Copy your Webhook Key for webhook signature verification
  5. For production, ensure you're using production credentials and set SKIPCASH_ENV=production

Usage

Creating a Payment

use SkipCash;

// In your controller
public function createPayment(Request $request)
{
    $payment = SkipCash::createPayment([
        // Required fields
        'Amount'     => number_format($order->total, 2, '.', ''),
        'FirstName'  => $user->first_name,
        'LastName'   => $user->last_name,
        'Phone'      => $user->phone,   // with country code (+965...)
        'Email'      => $user->email,
        
        // Optional fields
        'Street'     => $user->address,
        'City'       => $user->city,
        'Country'    => 'KW',
        'TransactionId' => $order->id,  // Your internal order ID
        'Custom1'    => 'any-custom-data',
    ]);

    // Redirect user to SkipCash payment page
    return redirect()->away($payment->payUrl);
}

Retrieving Payment Details

$payment = SkipCash::getPayment('pay_123456789');

echo $payment->paymentId;        // 'pay_123456789'
echo $payment->amount;           // '100.00'
echo $payment->statusId;         // 2 (Paid)
echo $payment->getStatusName();  // 'Paid'

// Check payment status
if ($payment->isPaid()) {
    // Payment successful
} elseif ($payment->isFailed()) {
    // Payment failed
} elseif ($payment->isPending()) {
    // Payment pending
}

Processing Refunds

$refund = SkipCash::refund(
    paymentId: 'pay_123456789',
    amount: '50.00',
    reason: 'Customer request'
);

Webhook Handling

The package automatically handles SkipCash webhooks and dispatches Laravel events based on payment status changes.

Webhook Configuration

  1. In your SkipCash Merchant Portal, set your webhook URL to:

    https://your-domain.com/skipcash/webhook
    
  2. The package automatically verifies webhook signatures and processes them asynchronously via Laravel queues.

Payment Status Events

The package dispatches the following events based on StatusId:

StatusId Event Description
1 PaymentPending Payment is pending
2 PaymentPaid Payment completed successfully
3 PaymentCanceled Payment was canceled
4, 5 PaymentFailed Payment failed
6 PaymentRefunded Payment was refunded
7 PaymentRefundPending Refund is pending
8 PaymentRefundFailed Refund failed

Listening to Payment Events

Create event listeners to handle payment status changes:

// In EventServiceProvider.php
use El3wdy\Skipcash\Events\PaymentPaid;
use El3wdy\Skipcash\Events\PaymentFailed;

protected $listen = [
    PaymentPaid::class => [
        UpdateOrderStatus::class,
        SendPaymentConfirmation::class,
    ],
    PaymentFailed::class => [
        HandleFailedPayment::class,
    ],
];

Example listener:

<?php

namespace App\Listeners;

use El3wdy\Skipcash\Events\PaymentPaid;
use App\Models\Order;

class UpdateOrderStatus
{
    public function handle(PaymentPaid $event): void
    {
        if ($event->transactionId) {
            $order = Order::find($event->transactionId);
            $order?->markAsPaid();
        }
    }
}

Webhook Security

  • All webhooks are automatically verified using HMAC-SHA256 signatures
  • Invalid signatures are rejected with 401 Unauthorized
  • Duplicate webhooks are handled idempotently using cache locks
  • Processing is deferred to queues for fast response times (<10s)

Webhook Retry Behavior

SkipCash retries failed webhooks with the following schedule:

  • Immediate retry
  • +1 hour retry
  • +1 day retry

The package handles out-of-order delivery and duplicate events automatically.

Local Payment Storage

When SKIPCASH_STORE_PAYMENTS=true (default), the package stores payment records locally:

use El3wdy\Skipcash\Models\SkipcashPayment;

// Find payments
$payment = SkipcashPayment::findByPaymentId('pay_123456789');
$payment = SkipcashPayment::findByTransactionId('ORDER-123');

// Query payments
$paidPayments = SkipcashPayment::paid()->get();
$failedPayments = SkipcashPayment::failed()->get();
$recentPayments = SkipcashPayment::where('created_at', '>', now()->subDays(7))->get();

// Access payment data
echo $payment->payment_id;     // 'pay_123456789'
echo $payment->amount;         // 100.00
echo $payment->status_name;    // 'Paid'
echo $payment->raw_payload;    // Full webhook payload

StatusId Reference

StatusId Status Name Description
0 New Payment created but not processed
1 Pending Payment is being processed
2 Paid Payment completed successfully
3 Canceled Payment was canceled by user
4 Failed Payment failed (generic)
5 Failed Payment failed (specific)
6 Refunded Payment was refunded
7 Pending Refund Refund is being processed
8 Refund Failed Refund processing failed

Testing

Webhook Simulator

For testing webhooks, you can use the SkipCash webhook simulator with these parameters:

{
  "PaymentId": "pay_test_123456789",
  "Amount": "100.00",
  "StatusId": 2,
  "TransactionId": "ORDER-123",
  "Custom1": "test-data",
  "VisaId": "visa_test_123456789"
}

Unit Testing

The package includes comprehensive tests. Run them with:

composer test

Testing in Your Application

Use the provided test helpers:

use El3wdy\Skipcash\Tests\TestCase;
use Illuminate\Support\Facades\Http;

class PaymentTest extends TestCase
{
    /** @test */
    public function it_creates_payment_successfully()
    {
        Http::fake([
            'skipcashtest.azurewebsites.net/*' => Http::response([
                'PaymentId' => 'pay_123456789',
                'PayUrl' => 'https://pay.skipcash.app/pay/123456789',
            ], 200)
        ]);

        $response = SkipCash::createPayment([
            'Amount' => '100.00',
            'FirstName' => 'John',
            'LastName' => 'Doe',
            'Phone' => '+96550000000',
            'Email' => 'john@example.com',
        ]);

        $this->assertEquals('pay_123456789', $response->paymentId);
    }
}

API Reference

SkipCash Facade Methods

// Create a payment
SkipCash::createPayment(array $paymentData): PaymentResponse

// Get payment details
SkipCash::getPayment(string $paymentId): PaymentDetails

// Process refund
SkipCash::refund(string $paymentId, string $amount, ?string $reason = null): array

// Get underlying HTTP client
SkipCash::getClient(): SkipCashClient

Data Objects

PaymentResponse

$response->paymentId;          // string
$response->payUrl;            // string
$response->visaTransactionId; // ?string
$response->rawResponse;       // array

PaymentDetails

$details->paymentId;      // string
$details->amount;         // string
$details->statusId;       // int
$details->transactionId;  // ?string
$details->custom1;        // ?string
$details->visaId;         // ?string
$details->firstName;      // ?string
$details->lastName;       // ?string
$details->email;          // ?string
$details->phone;          // ?string
$details->rawResponse;    // array

// Helper methods
$details->getStatusName(); // string
$details->isPaid();        // bool
$details->isFailed();      // bool
$details->isPending();     // bool

Environment URLs

The package automatically uses the correct API endpoints based on your environment:

  • Sandbox: https://skipcashtest.azurewebsites.net/api/v1/
  • Production: https://api.skipcash.app/api/v1/

Security Considerations

  • Never log or expose your SKIPCASH_KEY_SECRET or SKIPCASH_WEBHOOK_KEY
  • Always use HTTPS in production
  • Webhook signatures are verified automatically
  • All amounts are validated and normalized
  • Field ordering for signatures is strictly enforced per SkipCash documentation

Error Handling

The package throws SkipCashException for API errors:

use El3wdy\Skipcash\Exceptions\SkipCashException;

try {
    $payment = SkipCash::createPayment($data);
} catch (SkipCashException $e) {
    Log::error('SkipCash payment failed: ' . $e->getMessage());
    // Handle error appropriately
}

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.