Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Phonepe Laravel Laravel Package

yogeshgupta/phonepe-laravel

Laravel integration for PhonePe payments. Provides simple configuration and helper methods to initiate transactions, generate required hashes/signatures, and handle callbacks/responses, making it easier to accept PhonePe payments in your Laravel app.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require yogeshgupta/phonepe-laravel
    php artisan vendor:publish --provider="Yogeshgupta\PhonepeLaravel\PhonepeServiceProvider"
    
  2. Configure .env

    PHONEPE_MERCHANT_ID=your_merchant_id
    PHONEPE_MERCHANT_SECRET=your_merchant_secret
    PHONEPE_ENV=test  # or 'live'
    PHONEPE_WEBHOOK_SECRET=your_webhook_secret
    
  3. First Payment Flow

    use Yogeshgupta\PhonepeLaravel\Facades\Phonepe;
    
    // 1. Initiate payment (redirect user to PhonePe)
    $paymentLink = Phonepe::initiatePayment([
        'amount' => 100.00,
        'txnId' => 'txn_' . Str::uuid(),
        'callbackUrl' => route('payment.callback'),
        'paymentInstrument' => [
            'type' => 'UPI',
            'target' => 'user@phonepe'
        ]
    ]);
    return redirect()->to($paymentLink);
    
    // 2. Handle callback (after user returns from PhonePe)
    public function handleCallback(Request $request) {
        $status = Phonepe::verifyPayment($request->txnId);
        // Update your database and show success page
    }
    
  4. Webhook Setup Add this route to your routes/web.php:

    Route::post('/phonepe/webhook', [PhonepeWebhookController::class, 'handle']);
    

Implementation Patterns

Daily Developer Workflows

1. Payment Initiation

// In your checkout controller
public function checkout(Request $request) {
    $payment = Phonepe::initiatePayment([
        'amount' => $request->amount,
        'txnId' => 'order_' . $request->order_id,
        'callbackUrl' => route('payment.callback'),
        'paymentInstrument' => [
            'type' => 'UPI',
            'target' => $request->upi_id
        ],
        'metadata' => [
            'order_id' => $request->order_id,
            'customer_email' => auth()->user()->email
        ]
    ]);

    return redirect()->to($payment->getRedirectUrl());
}

2. Webhook Processing

// PhonepeWebhookController.php
public function handle(Request $request) {
    // 1. Validate webhook signature
    if (!Phonepe::validateWebhook($request)) {
        abort(403);
    }

    // 2. Process the event
    $event = $request->event;
    $txnId = $request->txnId;

    // 3. Dispatch event or update database
    event(new PhonepePaymentEvent($event, $txnId));

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

3. Payment Verification

// After user returns from PhonePe
public function verifyPayment(Request $request) {
    $txnId = $request->txnId;
    $status = Phonepe::verifyPayment($txnId);

    if ($status->getStatus() === 'PAYMENT_SUCCESS') {
        // Update order status
        Order::where('txn_id', $txnId)->update(['status' => 'paid']);
        return view('payment.success');
    }

    return view('payment.failed');
}

4. Refund Handling

// Custom service for refunds
public function processRefund($orderId) {
    $order = Order::find($orderId);
    $refund = Phonepe::refundPayment([
        'txnId' => $order->txn_id,
        'amount' => $order->amount,
        'reason' => 'Customer requested refund'
    ]);

    if ($refund->getStatus() === 'REFUND_SUCCESS') {
        $order->update(['refund_status' => 'processed']);
    }
}

5. Testing Patterns

// Test payment initiation
public function testPaymentInitiation() {
    $mock = Mockery::mock('overload', Yogeshgupta\PhonepeLaravel\Facades\Phonepe::class);
    $mock->shouldReceive('initiatePayment')
         ->once()
         ->andReturn(new PaymentResponse('https://phonepe.com/pay', 'txn_123'));

    $response = $this->post('/checkout', ['amount' => 100]);
    $response->assertRedirect('https://phonepe.com/pay');
}

// Test webhook validation
public function testWebhookValidation() {
    $request = new Request([
        'event' => 'PAYMENT_SUCCESS',
        'txnId' => 'txn_123'
    ], [], [], [], [], [
        'HTTP_X_PHONEPE_SIGNATURE' => 'valid_signature'
    ]);

    $this->assertTrue(Phonepe::validateWebhook($request));
}

Gotchas and Tips

Common Pitfalls

  1. Webhook Signature Validation

    • Issue: Webhook requests may fail silently if the signature doesn't match.
    • Fix: Always validate signatures in your webhook endpoint:
      if (!Phonepe::validateWebhook($request)) {
          Log::warning('Invalid PhonePe webhook signature', [
              'signature' => $request->header('X-PhonePe-Signature'),
              'body' => $request->getContent()
          ]);
          abort(403);
      }
      
  2. OAuth Token Expiry

    • Issue: Tokens expire after 1 hour, causing failed API calls.
    • Fix: Implement token refresh logic in a middleware:
      public function handle($request, Closure $next) {
          if (Phonepe::isTokenExpired()) {
              Phonepe::refreshAccessToken();
          }
          return $next($request);
      }
      
  3. Idempotency Issues

    • Issue: Duplicate webhook events can cause double-processing.
    • Fix: Use Laravel's queue with unique jobs:
      Dispatch::later(now()->addMinutes(5), new ProcessPaymentWebhook($event))
          ->onQueue('phonepe')
          ->uniqueId($event->txnId);
      
  4. Amount Precision

    • Issue: PhonePe expects amounts in paise (not rupees).
    • Fix: Always convert to paise:
      $amountInPaise = $amount * 100;
      
  5. Webhook Retries

    • Issue: Failed webhook deliveries aren't automatically retried.
    • Fix: Implement exponential backoff in your queue worker:
      public function handle() {
          try {
              // Process webhook
          } catch (\Exception $e) {
              $this->release(60); // Retry after 1 minute
          }
      }
      

Debugging Tips

  1. Enable Debug Logging

    PHONEPE_DEBUG=true
    

    This will log all API requests/responses to storage/logs/laravel.log.

  2. Inspect Raw API Responses

    $response = Phonepe::initiatePayment([...]);
    Log::debug('PhonePe API Response', [
        'status' => $response->getStatus(),
        'raw' => $response->getRawContent()
    ]);
    
  3. Test in Sandbox First

    • Use PHONEPE_ENV=test to test all flows before going live.
    • PhonePe sandbox credentials are different from production.
  4. Monitor Failed Jobs

    php artisan queue:failed
    

    Look for Yogeshgupta\PhonepeLaravel\Jobs\ProcessWebhookJob failures.

Configuration Quirks

  1. Environment-Specific Settings

    • The package automatically loads different configs based on PHONEPE_ENV.
    • Override specific settings in config/phonepe.php:
      'test' => [
          'merchant_id' => env('PHONEPE_TEST_MERCHANT_ID'),
          'merchant_secret' => env('PHONEPE_TEST_MERCHANT_SECRET'),
          'base_url' => 'https://api.phonepe.com/test',
      ],
      
  2. Webhook Secret Management

    • Store PHONEPE_WEBHOOK_SECRET securely (use Laravel Vault or AWS Secrets Manager).
    • Never commit this to version control.
  3. Rate Limiting

    • PhonePe has rate limits (typically 100 requests/minute).
    • Implement caching for frequent operations:
      $token = Cache::remember('phonepe_access_token', now()->addHours(1), function() {
          return Phonepe::getAccessToken();
      });
      

Extension Points

  1. Custom Payment Methods
    • Extend the facade to support additional payment types:
      // app/Providers/PhonepeServiceProvider.php
      public function boot() {
          Phonepe::extend('netbanking', function($app) {
              return new NetBankingPaymentService();
          });
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime