asciisd/cashier-paytiko

Paytiko payment processor for Laravel Cashier Core

1.2.2 2025-10-01 01:18 UTC

This package is auto-updated.

Last update: 2025-10-01 01:20:15 UTC


README

Latest Version on Packagist Total Downloads License

A Laravel package that integrates Paytiko payment processor with Cashier Core. This package provides a clean, scalable implementation of Paytiko's Hosted Page solution with comprehensive webhook handling.

Features

  • Simplified Payment API: Easy-to-use simpleCharge(amount, params) method with automatic configuration
  • Hosted Page Integration: Seamless integration with Paytiko's hosted payment pages
  • Automatic Configuration: URLs, order IDs, and descriptions auto-generated from config
  • Webhook Handling: Automatic webhook processing with signature verification
  • Event System: Laravel events for payment status updates
  • DTO Architecture: Clean data transfer objects for type safety
  • Comprehensive Testing: Full test suite with Pest
  • Scalable Design: Easy to extend for additional Paytiko features
  • Laravel Integration: Native Laravel package with service provider
  • Signature Security: Automatic signature generation and verification

Installation

Install the package via Composer:

composer require asciisd/cashier-paytiko

Publish the configuration file:

php artisan vendor:publish --provider="Asciisd\CashierPaytiko\CashierPaytikoServiceProvider" --tag="cashier-paytiko-config"

Configuration

Add the following environment variables to your .env file:

# Paytiko Configuration
PAYTIKO_MERCHANT_SECRET_KEY=your_merchant_secret_key
PAYTIKO_CORE_URL=https://your-paytiko-core-url.com
PAYTIKO_WEBHOOK_URL=https://yoursite.com/api/webhooks/paytiko
PAYTIKO_SUCCESS_REDIRECT_URL=https://yoursite.com/payment/success
PAYTIKO_FAILED_REDIRECT_URL=https://yoursite.com/payment/failed
PAYTIKO_DEFAULT_CURRENCY=USD

# Optional Settings
PAYTIKO_VERIFY_WEBHOOK_SIGNATURE=true
PAYTIKO_WEBHOOK_TOLERANCE=300
PAYTIKO_HTTP_TIMEOUT=30
PAYTIKO_LOGGING_ENABLED=true

Usage

Simplified Payment Processing (Recommended)

The new simpleCharge method provides a streamlined way to process payments with minimal configuration:

use Asciisd\CashierCore\Facades\PaymentFactory;

// Create Paytiko processor
$processor = PaymentFactory::create('paytiko', [
    'merchant_secret_key' => config('cashier-paytiko.merchant_secret_key'),
    'core_url' => config('cashier-paytiko.core_url'),
]);

// 1. Minimal payment - only amount and billing details required
$result = $processor->simpleCharge(100.00, [
    'billing_details' => [
        'first_name' => 'John',
        'last_name' => 'Doe',
        'email' => 'john@example.com',
        'country' => 'US',
        'phone' => '+1234567890',
    ]
]);

// 2. With custom parameters
$result = $processor->simpleCharge(250.50, [
    'currency' => 'EUR',
    'description' => 'Custom deposit for trading account',
    'billing_details' => [
        'first_name' => 'Jane',
        'last_name' => 'Smith',
        'email' => 'jane@example.com',
        'country' => 'CA',
        'phone' => '+1555123456',
    ],
    'metadata' => ['campaign_id' => 'xyz123'],
]);

// 3. Override default URLs
$result = $processor->simpleCharge(75.00, [
    'billing_details' => [
        'first_name' => 'Alice',
        'last_name' => 'Brown',
        'email' => 'alice@example.com',
        'country' => 'GB',
        'phone' => '+447123456789',
    ],
    'webhook_url' => 'https://my-app.com/custom-webhook',
    'success_redirect_url' => 'https://my-app.com/custom-success',
]);

if ($result->isSuccessful()) {
    $redirectUrl = $result->metadata['redirect_url'];
    // Redirect user to Paytiko hosted page
    return redirect($redirectUrl);
} else {
    // Handle error
    echo "Payment failed: {$result->message}";
}

What simpleCharge Auto-Generates

When using simpleCharge, the following are automatically handled:

  • Order ID: Generated using deposit-{timestamp}-{random}
  • Description: Auto-generated based on amount (e.g., "Deposit $100.00")
  • Webhook URL: Uses config value or falls back to Laravel route
  • Success/Failed URLs: Uses config values or falls back to Laravel routes
  • Currency: Uses config default (USD) if not specified
  • Timestamp: Automatically generated for signature creation

Basic Payment Processing (Legacy)

use Asciisd\CashierCore\Facades\PaymentFactory;

// Create Paytiko processor
$processor = PaymentFactory::create('paytiko', [
    'merchant_secret_key' => config('cashier-paytiko.merchant_secret_key'),
    'core_url' => config('cashier-paytiko.core_url'),
]);

// Process payment (creates hosted page)
$result = $processor->charge([
    'amount' => 20, // $20.00
    'currency' => 'USD',
    'order_id' => 'order_12345',
    'description' => 'Product purchase',
    'billing_details' => [
        'first_name' => 'John',
        'last_name' => 'Doe',
        'email' => 'john@example.com',
        'country' => 'US',
        'phone' => '+1234567890',
        'street' => '123 Main St',
        'city' => 'New York',
        'region' => 'NY',
        'zip_code' => '10001',
        'date_of_birth' => '1990-01-01',
        'gender' => 'Male',
        'currency' => 'USD',
    ],
    'webhook_url' => 'https://yoursite.com/api/webhooks/paytiko',
    'success_redirect_url' => 'https://yoursite.com/success',
    'failed_redirect_url' => 'https://yoursite.com/failed',
]);

if ($result->isSuccessful()) {
    $redirectUrl = $result->metadata['redirect_url'];
    // Redirect user to Paytiko hosted page
    return redirect($redirectUrl);
} else {
    // Handle error
    echo "Payment failed: {$result->message}";
}

Registering with Cashier Core

Add Paytiko to your config/cashier-core.php:

'processors' => [
    'paytiko' => [
        'class' => \Asciisd\CashierPaytiko\PaytikoProcessor::class,
        'config' => [
            'merchant_secret_key' => config('cashier-paytiko.merchant_secret_key'),
            'core_url' => config('cashier-paytiko.core_url'),
        ],
    ],
],

Webhook Handling

The package automatically registers a webhook endpoint at /api/webhooks/paytiko. Configure this URL in your Paytiko dashboard.

Listening to Events

use Asciisd\CashierPaytiko\Events\PaytikoPaymentSuccessful;
use Asciisd\CashierPaytiko\Events\PaytikoPaymentFailed;
use Asciisd\CashierPaytiko\Events\PaytikoRefundProcessed;
use Asciisd\CashierPaytiko\Events\PaytikoWebhookReceived;

// Listen to successful payments
Event::listen(PaytikoPaymentSuccessful::class, function ($event) {
    $webhookData = $event->webhookData;
    
    // Update your order status
    $order = Order::where('id', $webhookData->orderId)->first();
    $order->update(['status' => 'paid']);
    
    // Send confirmation email, etc.
});

// Listen to failed payments
Event::listen(PaytikoPaymentFailed::class, function ($event) {
    $webhookData = $event->webhookData;
    
    // Handle failed payment
    $order = Order::where('id', $webhookData->orderId)->first();
    $order->update(['status' => 'failed']);
});

// Listen to all webhook events
Event::listen(PaytikoWebhookReceived::class, function ($event) {
    $webhookData = $event->webhookData;
    $rawPayload = $event->rawPayload;
    
    // Log webhook for debugging
    Log::info('Paytiko webhook received', [
        'order_id' => $webhookData->orderId,
        'status' => $webhookData->transactionStatus,
    ]);
});

Advanced Configuration

Custom Webhook URL per Payment

$result = $processor->charge([
    // ... other data
    'webhook_url' => 'https://yoursite.com/custom/webhook/endpoint',
]);

Credit Card Only Mode

$result = $processor->charge([
    // ... other data
    'credit_card_only' => true,
]);

Disable Specific Payment Processors

$result = $processor->charge([
    // ... other data
    'disabled_psp_ids' => [12321, 54455, 34212],
]);

Payout Mode

$result = $processor->charge([
    // ... other data
    'is_pay_out' => true,
]);

Data Transfer Objects

The package uses DTOs for type safety and clean data handling:

PaytikoHostedPageRequest

use Asciisd\CashierPaytiko\DataObjects\PaytikoHostedPageRequest;
use Asciisd\CashierPaytiko\DataObjects\PaytikoBillingDetails;

$billingDetails = new PaytikoBillingDetails(
    firstName: 'John',
    email: 'john@example.com',
    country: 'US',
    phone: '+1234567890',
    currency: 'USD'
);

$request = new PaytikoHostedPageRequest(
    timestamp: (string) time(),
    orderId: 'order_123',
    signature: $signature,
    billingDetails: $billingDetails
);

PaytikoWebhookData

use Asciisd\CashierPaytiko\DataObjects\PaytikoWebhookData;

// Webhook data is automatically parsed from incoming webhooks
$webhookData = $event->webhookData;

// Check payment status
if ($webhookData->isSuccessful()) {
    // Payment succeeded
}

if ($webhookData->isPayIn()) {
    // This is a payment
}

if ($webhookData->isRefund()) {
    // This is a refund
}

Testing

Run the test suite:

composer test

Run tests with coverage:

composer test-coverage

Example Tests

Testing the Simplified Method

use Asciisd\CashierPaytiko\PaytikoProcessor;

it('processes payment with simpleCharge successfully', function () {
    $processor = new PaytikoProcessor([
        'merchant_secret_key' => 'test_secret',
        'core_url' => 'https://test.paytiko.com',
    ]);
    
    $result = $processor->simpleCharge(100.00, [
        'billing_details' => [
            'first_name' => 'John',
            'email' => 'john@example.com',
            'country' => 'US',
            'phone' => '+1234567890',
        ],
    ]);
    
    expect($result->isSuccessful())->toBeTrue();
});

Testing the Legacy Method

it('processes payment successfully', function () {
    $processor = new PaytikoProcessor([
        'merchant_secret_key' => 'test_secret',
        'core_url' => 'https://test.paytiko.com',
    ]);
    
    $paymentData = [
        'amount' => 2000,
        'order_id' => 'test_order',
        'billing_details' => [
            'first_name' => 'John',
            'email' => 'john@example.com',
            'country' => 'US',
            'phone' => '+1234567890',
        ],
    ];
    
    $result = $processor->charge($paymentData);
    
    expect($result->isSuccessful())->toBeTrue();
});

Security

  • Signature Verification: All webhooks are verified using SHA256 signatures
  • HTTPS Required: All API communications use HTTPS
  • Input Validation: Comprehensive validation of all input data
  • Error Handling: Secure error handling without exposing sensitive data

Extending the Package

The package is designed to be easily extensible. You can add new Paytiko features by:

  1. Creating new DTOs for request/response data
  2. Adding new methods to the PaytikoProcessor
  3. Creating new events for additional webhook types
  4. Extending the service classes for new API endpoints

Example: Adding Direct Payment API

// Add to PaytikoProcessor
public function directPayment(array $data): PaymentResult
{
    // Implement direct payment API integration
}

// Create new DTO
class PaytikoDirectPaymentRequest
{
    // Define direct payment request structure
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

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

Support

For support, please open an issue on GitHub or contact us at info@asciisd.com.

Changelog

v1.2.0 (dev-main)

  • NEW: Added simpleCharge(amount, params) method for simplified payment processing
  • NEW: Automatic order ID generation with deposit-{timestamp}-{random} format
  • NEW: Auto-generated descriptions based on payment amount
  • NEW: Automatic URL handling with config fallbacks and Laravel route support
  • NEW: Comprehensive test suite for simplified payment method
  • NEW: Example usage documentation and code samples
  • IMPROVED: Better separation between simplified and legacy payment methods
  • IMPROVED: Enhanced error handling and validation for simplified API

v1.0.0

  • Initial release
  • Hosted Page integration
  • Webhook handling with events
  • Comprehensive test suite
  • Full documentation