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

Xerobundle Laravel Package

blackoptic/xerobundle

Symfony bundle that wraps the Xero API with a Guzzle-based client. Configure your Xero consumer key/secret and private key, then fetch resources like Invoices via the blackoptic.xero.client service for simple authenticated requests.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation Add the package via Composer:

    composer require blackoptic/xerobundle
    

    Ensure your composer.json includes the package under require.

  2. Bundle Registration Register the bundle in config/bundles.php (Symfony 4+) or app/AppKernel.php (Symfony 2/3):

    BlackOptic\Bundle\XeroBundle\BlackOpticXeroBundle::class => ['all' => true],
    
  3. Configuration Add Xero API credentials to config/packages/black_optic_xero.yaml (Symfony 4+) or app/config/config.yml (Symfony 2/3):

    black_optic_xero:
        consumer_key: "%env(XERO_CONSUMER_KEY)%"
        consumer_secret: "%env(XERO_CONSUMER_SECRET)%"
        private_key: "%kernel.project_dir%/config/xero_private_key.p8"
    

    Store sensitive keys in .env:

    XERO_CONSUMER_KEY=your_consumer_key
    XERO_CONSUMER_SECRET=your_consumer_secret
    
  4. First API Call Inject the Xero client service into a controller or command:

    use BlackOptic\Bundle\XeroBundle\Service\XeroClient;
    
    class InvoiceController extends AbstractController
    {
        public function __construct(private XeroClient $xeroClient) {}
    
        public function listInvoices()
        {
            $response = $this->xeroClient->get('Invoices')->send();
            return $this->json($response->json());
        }
    }
    

Implementation Patterns

Common Workflows

  1. OAuth2 Authentication The bundle handles OAuth2 token generation automatically. Refresh tokens silently if expired:

    $response = $this->xeroClient->get('Contacts')->send();
    
  2. Resource Operations Use standard HTTP methods for CRUD operations:

    // Create
    $this->xeroClient->post('Invoices', ['Invoice' => $invoiceData])->send();
    
    // Update
    $this->xeroClient->put("Invoices/{$invoiceId}", ['Invoice' => $updatedData])->send();
    
    // Delete
    $this->xeroClient->delete("Invoices/{$invoiceId}")->send();
    
  3. Pagination Handle paginated responses with XeroPaginator:

    $paginator = new XeroPaginator($this->xeroClient, 'Contacts');
    foreach ($paginator as $page) {
        foreach ($page as $contact) {
            // Process contact
        }
    }
    
  4. Webhooks Register webhook endpoints in config/packages/black_optic_xero.yaml:

    black_optic_xero:
        webhooks:
            - endpoint: /xero/webhook
              events: ['CREATED', 'UPDATED']
    
  5. Error Handling Use middleware to catch Xero API errors:

    $this->xeroClient->get('Invoices')->onResponse(function (Response $response) {
        if ($response->getStatusCode() === 400) {
            throw new \RuntimeException($response->json()['error']['message']);
        }
    })->send();
    

Integration Tips

  1. Dependency Injection Prefer constructor injection for the XeroClient service to ensure testability:

    public function __construct(private XeroClient $xeroClient) {}
    
  2. Configuration Overrides Override default config in config/packages/black_optic_xero.yaml:

    black_optic_xero:
        base_uri: 'https://api.xero.com/api.xro/2.0'  # Override if needed
        timeout: 30
    
  3. Logging Enable Guzzle logging for debugging:

    black_optic_xero:
        options:
            on_request: [BlackOptic\Bundle\XeroBundle\Logger\XeroRequestLogger]
    
  4. Testing Mock the XeroClient in tests:

    $mockClient = $this->createMock(XeroClient::class);
    $mockClient->method('get')->willReturn(new Response(200, [], json_encode(['Invoices' => []])));
    $this->container->set('blackoptic.xero.client', $mockClient);
    
  5. Batch Processing Use Xero’s batch endpoints for bulk operations:

    $batch = $this->xeroClient->batch();
    $batch->add('POST', 'Invoices', ['Invoice' => $data1]);
    $batch->add('POST', 'Invoices', ['Invoice' => $data2]);
    $batch->send();
    

Gotchas and Tips

Pitfalls

  1. Private Key Path

    • Issue: Hardcoding private key paths in config can cause deployment failures.
    • Fix: Use environment variables or Symfony’s %kernel.project_dir%:
      private_key: "%env(XERO_PRIVATE_KEY_PATH)%"
      
  2. Token Expiry

    • Issue: OAuth2 tokens expire silently, causing 401 Unauthorized errors.
    • Fix: Implement a retry mechanism or use the bundle’s built-in token refresh:
      $this->xeroClient->setOption('on_request', function (RequestInterface $request) {
          if ($request->hasHeader('Authorization') && strpos($request->getHeader('Authorization')[0], 'Bearer') === 0) {
              // Handle token refresh logic here
          }
      });
      
  3. Rate Limiting

    • Issue: Xero enforces rate limits (e.g., 10 requests/second).
    • Fix: Add delays between requests:
      $this->xeroClient->get('Invoices')->delay(100)->send();
      
  4. Webhook Verification

    • Issue: Xero webhooks require verification of the X-Xero-Request-ID header.
    • Fix: Validate headers in your webhook endpoint:
      public function handleWebhook(Request $request)
      {
          $xeroRequestId = $request->headers->get('X-Xero-Request-ID');
          if (!$xeroRequestId) {
              throw new \RuntimeException('Invalid Xero webhook request');
          }
          // Process payload
      }
      
  5. Deprecated Endpoints

    • Issue: Xero deprecates endpoints without notice (e.g., api.xero.comapi.xero.com/api.xro/2.0).
    • Fix: Monitor Xero’s API changelog and update base_uri in config.

Debugging Tips

  1. Enable Guzzle Debugging Add this to your config to log all requests/responses:

    black_optic_xero:
        options:
            debug: true
            on_request: [GuzzleHttp\Middleware::log]
            on_response: [GuzzleHttp\Middleware::log]
    
  2. Check Token Validity Verify your OAuth2 token hasn’t expired:

    $token = $this->xeroClient->getToken();
    if ($token->getExpiresAt() < new \DateTime()) {
        $this->xeroClient->refreshToken();
    }
    
  3. Validate Private Key Ensure your .p8 private key is correctly formatted (PEM format):

    openssl rsa -in xero_private_key.p8 -inform PEM -pubout
    

    If invalid, regenerate the key in the Xero Developer Portal.

  4. Handle Idempotency Use Xero’s Idempotency-Key header for safe retries:

    $this->xeroClient->post('Invoices', ['Invoice' => $data])
        ->setHeader('Idempotency-Key', uniqid())
        ->send();
    

Extension Points

  1. Custom Middleware Add middleware to modify requests/responses:

    $this->xeroClient->getMiddleware()->push(
        \GuzzleHttp\Middleware::mapRequest(function (RequestInterface $request) {
            $request = $request->withHeader('X-Custom-Header', 'value');
            return $request;
        })
    );
    
  2. Event Listeners Subscribe to Xero events (e.g., after token refresh):

    // In a service
    public function __construct(private XeroClient $xeroClient)
    {
        $this->xeroClient->getEventDispatcher()->addListener(
            XeroEvents::TOKEN_REFRESHED,
            function (TokenRefreshedEvent $event) {
                // Log or notify about token refresh
            }
        );
    }
    
  3. Custom Response Handlers Override response handling for

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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle