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

Whop Symfony Bundle Laravel Package

devmatchable/whop-symfony-bundle

Symfony 7 bundle for the Whop PHP SDK. Autowires WhopApiClient and WebhookVerifier from config, provides a ready-to-use (overridable) webhook controller/route, and supports sandbox base URL and custom HTTP client selection.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package:

    composer require devmatchable/whop-symfony-bundle
    

    Ensure your project uses Symfony 7 and PHP 8.4+.

  2. Configure the bundle (config/packages/whop.yaml):

    whop:
        api_key: '%env(WHOP_API_KEY)%'
        webhook_secret: '%env(WHOP_WEBHOOK_SECRET)%'
        base_url: 'https://api.whop.com/api/v1'  # Use sandbox URL for testing
    
  3. Set environment variables in .env:

    WHOP_API_KEY=your_api_key_here
    WHOP_WEBHOOK_SECRET=your_webhook_secret_here
    
  4. Verify the route is auto-registered (default: /_whop/webhook). Test by sending a webhook to this endpoint.


First Use Case: Fetching a Payment

Autowire the WhopApiClient in a service:

use Matchable\Whop\WhopApiClient;

final readonly class PaymentService
{
    public function __construct(private WhopApiClient $whop) {}

    public function getPayment(string $paymentId): array
    {
        return $this->whop->payments->get($paymentId)->toArray();
    }
}

Implementation Patterns

API Client Usage

  • Type-hint WhopApiClient anywhere to access Whop’s API methods (e.g., payments, customers).
  • Custom HTTP client: Override http_client in config to use a specific PSR-18 client:
    whop:
        http_client: App\Client\CustomHttpClient
    

Webhook Handling

  1. Zero-config event listener (default behavior): Subscribe to WhopWebhookReceivedEvent:

    use Matchable\Whop\Bundle\Event\WhopWebhookReceivedEvent;
    use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
    
    #[AsEventListener]
    final class WebhookListener
    {
        public function __invoke(WhopWebhookReceivedEvent $event): void
        {
            $payload = $event->payload; // Decoded webhook data
            // Process payload (e.g., update database, trigger actions)
        }
    }
    
  2. Custom handler (override default behavior):

    • Implement WhopWebhookHandlerInterface:
      use Matchable\Whop\Bundle\Webhook\WhopWebhookHandlerInterface;
      
      final class CustomWebhookHandler implements WhopWebhookHandlerInterface
      {
          public function handle(array $payload, string $rawPayload): void
          {
              // Custom logic (e.g., validate, dispatch to services)
          }
      }
      
    • Alias the service in config/services.yaml:
      services:
          Matchable\Whop\Bundle\Webhook\WhopWebhookHandlerInterface:
              alias: App\CustomWebhookHandler
      
  3. Decorate the handler (wrap default logic):

    use Symfony\Component\DependencyInjection\Attribute\AsDecorator;
    
    #[AsDecorator('Matchable\Whop\Bundle\Webhook\WhopWebhookHandlerInterface')]
    final class LoggingWebhookHandler implements WhopWebhookHandlerInterface
    {
        public function __construct(private WhopWebhookHandlerInterface $decorated) {}
    
        public function handle(array $payload, string $rawPayload): void
        {
            // Log before/after delegating to the decorated handler
            $this->decorated->handle($payload, $rawPayload);
        }
    }
    

Integration Tips

  • Testing webhooks: Use the TestKernel from the bundle’s tests to mock webhook requests.
  • Sandbox mode: Set base_url to https://sandbox-api.whop.com/api/v1 in config for testing.
  • Environment separation: Use .env variables for WHOP_API_KEY and WHOP_WEBHOOK_SECRET to avoid hardcoding.

Gotchas and Tips

Pitfalls

  1. Webhook signature validation:

    • The bundle automatically rejects requests with invalid signatures (returns 401).
    • Ensure webhook_secret matches Whop’s configured secret (supports whsec_ or ws_ prefixes).
  2. Payload structure:

    • Webhook payloads are raw array<string, mixed>. Map them to your domain types inside listeners/handlers (the SDK does not provide DTOs for incoming events).
  3. Route conflicts:

    • The default route (/_whop/webhook) must not clash with existing routes. Customize webhook_path if needed:
      whop:
          webhook_path: '/custom/webhook/path'
      
  4. HTTP client dependency:

    • If you override http_client, ensure the service implements PSR-18 (or Symfony’s HttpClientInterface).

Debugging

  • Log webhook payloads: Add logging in your event listener or handler:
    error_log(print_r($event->payload, true));
    
  • Validate API responses: Use try-catch with WhopApiClient to handle SDK exceptions:
    try {
        $payment = $this->whop->payments->get($id);
    } catch (\Matchable\Whop\Exception\WhopException $e) {
        // Handle errors (e.g., 404, 403)
    }
    

Extension Points

  1. Custom DTOs:

    • Create your own DTOs for webhook payloads (e.g., PaymentCreatedDto) and map them in listeners:
      $dto = new PaymentCreatedDto(
          $event->payload['id'],
          $event->payload['amount']
      );
      
  2. Event subscribers:

    • Extend WhopWebhookReceivedEvent to add custom properties or methods:
      final class CustomWebhookEvent extends WhopWebhookReceivedEvent
      {
          public function isSuccessful(): bool
          {
              return $this->payload['status'] === 'completed';
          }
      }
      
  3. Middleware:

    • Decorate WhopApiClient to add middleware (e.g., logging, retries):
      services:
          Matchable\Whop\WhopApiClient:
              decorate: true
              arguments:
                  $decorated: '@Matchable\Whop\WhopApiClient.inner'
      

Config Quirks

  • Required fields: api_key and webhook_secret are mandatory. The bundle validates config at compile time.
  • Flex recipe: If using Symfony Flex, the bundle auto-configures routes and .env variables. For manual setup, register the bundle in config/bundles.php and import routes:
    # config/routes/whop.yaml
    whop:
        resource: '@WhopBundle/config/routes.php'
    
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.
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky