djweb / payments
Payment and invoice integration package for Stripe and iFirma
1.0.0
2025-09-19 08:23 UTC
Requires
- php: ^8.4
- guzzlehttp/guzzle: ^7.0
- league/iso3166: ^4.0
- psr/http-message: ^2.0
- psr/log: ^3.0
- stripe/stripe-php: ^17.0
Requires (Dev)
- mockery/mockery: ^1.6
- nunomaduro/phpinsights: ^2.11
- phpstan/phpstan: ^2.0
- phpstan/phpstan-mockery: ^2.0
- phpstan/phpstan-strict-rules: ^2.0
- phpunit/phpunit: ^12.0
- squizlabs/php_codesniffer: ^3.10
This package is not auto-updated.
Last update: 2025-09-20 07:20:37 UTC
README
Modern, secure and flexible payment system for PHP applications. Supports Stripe, invoice generation via IFirma and EU VAT data validation.
β¨ Features
- π Security - VAT validation, type checking, sensitive data protection
- π° Stripe Payments - Full Stripe PaymentIntents support with webhooks
- π IFirma Invoices - Automatic invoice generation with various VAT strategies
- π EU Support - VAT number validation, tax rates, regional requirements
- π§ͺ High Quality - 92% test coverage, PHPStan level 8, PHPInsights A+
- π Modern PHP - Uses latest PHP 8.4 features (property hooks, readonly classes)
π¦ Installation
composer require djweb/payments
π Quick Start
Stripe Payments
use DjWeb\Payments\Services\Payment\Stripe\StripePaymentGateway; use DjWeb\Payments\DTOs\PaymentRequest; use DjWeb\Payments\DTOs\CustomerData; use DjWeb\Payments\DTOs\AddressData; use DjWeb\Payments\ValueObjects\Money; use DjWeb\Payments\ValueObjects\VatNumber; // Gateway configuration $stripe = new StripePaymentGateway( secretKey: 'sk_test_...', webhookSecret: 'whsec_...' ); // Customer data $customer = new CustomerData( email: 'customer@example.com', firstName: 'John', lastName: 'Doe', address: new AddressData( street: '123 Example Street', city: 'Warsaw', postalCode: '00-001', country: 'PL' ), companyName: 'Example Company Ltd.', vatNumber: new VatNumber('PL', '5260001246') ); // Payment request $request = new PaymentRequest( amount: new Money(299.99, 'PLN'), customer: $customer, description: 'Premium Product Purchase' ); // Create PaymentIntent $intent = $stripe->createPaymentIntent($request); // Process payment $result = $stripe->processPayment($intent); if ($result->success) { echo "Payment successful: {$result->transactionId}"; }
IFirma Invoice Generation
use DjWeb\Payments\Services\Invoice\IFirma\IFirmaInvoiceService; use DjWeb\Payments\DTOs\InvoiceData; $invoiceService = new IFirmaInvoiceService( username: 'your_username', invoiceKey: 'your_invoice_key', apiUrl: 'https://www.ifirma.pl/iapi' ); $invoiceData = new InvoiceData( customer: $customer, amount: new Money(299.99, 'PLN'), originalAmount: new Money(299.99, 'PLN'), productName: 'Premium Product' ); $invoiceResult = $invoiceService->createInvoice($invoiceData); if ($invoiceResult->success) { echo "Invoice created: {$invoiceResult->invoiceNumber}"; echo "PDF: {$invoiceResult->pdfUrl}"; }
Working with Discounts
use DjWeb\Payments\DTOs\DiscountData; $discount = new DiscountData( code: 'SAVE20', percentage: 20.0, maxUsages: 100, currentUsages: 15, validUntil: new DateTimeImmutable('2024-12-31') ); if ($discount->isValid) { $originalAmount = 299.99; $discountAmount = $discount->calculateDiscountAmount($originalAmount); $finalAmount = $discount->calculateFinalAmount($originalAmount); echo "Discount: {$discountAmount} PLN"; echo "Total: {$finalAmount} PLN"; }
VAT and Country Validation
use DjWeb\Payments\ValueObjects\VatNumber; use DjWeb\Payments\ValueObjects\Country; // Create and validate VAT number $vatNumber = new VatNumber('PL', '526-000-12-46'); echo $vatNumber; // PL5260001246 // Check EU country $country = new Country('PL'); echo $country->name; // Poland echo $country->getVatRate(); // 0.23 (23%) echo $country->isEu ? 'EU' : 'Non-EU'; // EU // Check state/province requirements if ($country->requiresStateProvince()) { // Required for US, CA, AU, BR, MX, IN, MY, AR }
Stripe Webhook Handling
use DjWeb\Payments\DTOs\WebhookEvent; // Webhook signature verification $payload = file_get_contents('php://input'); $signature = $_SERVER['HTTP_STRIPE_SIGNATURE']; try { $isValid = $stripe->verifyWebhookSignature($payload, $signature); if ($isValid) { $eventData = json_decode($payload, true); $event = new WebhookEvent( id: $eventData['id'], type: $eventData['type'], data: $eventData['data'], source: 'stripe', createdAt: new DateTimeImmutable() ); // Handle different event types if ($event->isPaymentEvent) { // payment_intent.succeeded, payment_intent.payment_failed, etc. handlePaymentEvent($event); } elseif ($event->isInvoiceEvent) { // invoice.payment_succeeded, invoice.payment_failed, etc. handleInvoiceEvent($event); } } } catch (Exception $e) { http_response_code(400); exit('Webhook error: ' . $e->getMessage()); }
ποΈ Architecture
Design Patterns
- Strategy Pattern - Different invoicing strategies (domestic, EU B2B, export, OSS)
- Factory Pattern - Automatic strategy selection based on customer data
- Value Objects - Safe representations of money, countries, VAT numbers
- DTOs - Data transfer with validation and transformation
Project Structure
src/
βββ Contracts/ # Interfaces
β βββ Arrayable.php
β βββ InvoiceServiceContract.php
β βββ PaymentGatewayContract.php
β βββ WebhookHandlerContract.php
βββ DTOs/ # Data Transfer Objects
β βββ AddressData.php
β βββ CustomerData.php
β βββ DiscountData.php
β βββ InvoiceData.php
β βββ PaymentIntent.php
β βββ PaymentRequest.php
β βββ PaymentResult.php
β βββ WebhookEvent.php
βββ Exceptions/ # Business Exceptions
β βββ InvoiceError.php
β βββ PaymentError.php
βββ Services/
β βββ Invoice/ # Invoice Services
β β βββ IFirma/ # IFirma Implementation
β βββ Payment/ # Payment Gateways
β β βββ Stripe/ # Stripe Implementation
β βββ Validators/ # VAT Validators
βββ ValueObjects/ # Value Objects
βββ Country.php
βββ Money.php
βββ VatNumber.php
π§ͺ Code Quality
The project maintains the highest code quality through:
Test Coverage
- 92% class coverage (23/25)
- 91% method coverage (74/81)
- 89% line coverage (389/438)
- 276 tests, 1007 assertions
Quality Control Tools
# All tests vendor/bin/phpunit # With code coverage XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html coverage # Static analysis PHPStan (level 9) vendor/bin/phpstan analyse # Quality analysis PHPInsights vendor/bin/phpinsights analyse # Code standards check vendor/bin/phpcs
π Invoice Strategies
The system automatically selects the appropriate strategy based on customer data:
Strategy | Conditions | VAT | IFirma Endpoint |
---|---|---|---|
Domestic | Polish customer, PLN | 23% | /fakturakraj.json |
Currency | Polish customer, EUR/USD | 23% | /fakturawaluta.json |
EU B2B | EU customer with valid VAT | 0% (reverse charge) | /fakturaeksportuslugue.json |
OSS | EU consumer | Customer's country VAT | /fakturaoss.json |
Export | Non-EU customer | 0% | /fakturaeksportuslug.json |
Custom Strategy Example
use DjWeb\Payments\Services\Invoice\IFirma\Strategies\InvoiceStrategyContract; class CustomInvoiceStrategy implements InvoiceStrategyContract { public function shouldApply(CustomerData $customer): bool { return $customer->address->country->code === 'US' && $customer->companyName !== null; } public function getEndpoint(): string { return '/custom-us-b2b.json'; } public function prepareInvoiceData(InvoiceData $data): array { return [ 'NabywcaNazwa' => $data->customer->companyName, 'NabywcaAdres' => $data->customer->address->street, 'StawkaVat' => 0, // No VAT for export // ... more fields ]; } }
π Supported Countries and Currencies
EU Countries with Automatic VAT Rates
All 27 EU countries with automatic VAT rates:
Country | Code | VAT Rate | Requires State/Province |
---|---|---|---|
π΅π± Poland | PL | 23% | β |
π©πͺ Germany | DE | 19% | β |
π«π· France | FR | 20% | β |
πΊπΈ USA | US | 0% | β |
π¨π¦ Canada | CA | 0% | β |
Currencies
- EUR, USD, PLN, GBP - full support
- JPY, KRW - no decimal places
- Support for 100+ currencies via Stripe
// Automatic conversion to Stripe units $money = new Money(99.99, 'PLN'); echo $money->toSmallestUnit(); // 9999 (grosze) $yen = new Money(1000, 'JPY'); echo $yen->toSmallestUnit(); // 1000 (no conversion)
π Security
Built-in Protections
- Input validation - All DTOs validate data at construction
- Sensitive data protection -
#[SensitiveParameter]
attribute for API keys - Safe Value Objects - Immutable objects with validation
- VAT validation - Automatic checksum validation for Polish NIPs
- Webhook verification - Stripe signature checking
Error Handling Example
use DjWeb\Payments\Exceptions\PaymentError; use DjWeb\Payments\Exceptions\InvoiceError; try { $intent = $stripe->createPaymentIntent($request); } catch (PaymentError $e) { // Safe logging with context logger()->error('Payment failed', [ 'message' => $e->getMessage(), 'context' => $e->context, // Additional context data 'customer_id' => $request->customer->email ]); } // Error factories for different scenarios throw PaymentError::invalidAmount(-100); throw PaymentError::unsupportedCurrency('XYZ'); throw PaymentError::gatewayError('Stripe', 'Card declined'); throw InvoiceError::invalidCustomerData('vatNumber'); throw InvoiceError::unsupportedCountry('XX'); throw InvoiceError::apiError('IFirma', 'Connection timeout');
π§ Configuration
Environment Variables
# Stripe STRIPE_SECRET_KEY=sk_test_51234567890... STRIPE_WEBHOOK_SECRET=whsec_1234567890... # IFirma IFIRMA_USERNAME=your_username IFIRMA_INVOICE_KEY=123456789abcdef IFIRMA_API_URL=https://www.ifirma.pl/iapi # Optional APP_ENV=production LOG_LEVEL=info
Dependency Injection (Laravel/Symfony)
// Laravel Service Provider use DjWeb\Payments\Contracts\PaymentGatewayContract; use DjWeb\Payments\Contracts\InvoiceServiceContract; $this->app->bind(PaymentGatewayContract::class, function () { return new StripePaymentGateway( secretKey: config('services.stripe.secret'), webhookSecret: config('services.stripe.webhook_secret') ); }); $this->app->bind(InvoiceServiceContract::class, function () { return new IFirmaInvoiceService( username: config('services.ifirma.username'), invoiceKey: config('services.ifirma.invoice_key'), apiUrl: config('services.ifirma.api_url', 'https://www.ifirma.pl/iapi') ); });
π Quality Metrics
Metric | Value | Status |
---|---|---|
Class Coverage | 92% (23/25) | β Excellent |
Method Coverage | 91% (74/81) | β Excellent |
Line Coverage | 89% (389/438) | β Very Good |
PHPStan | Level 8/9 | β Perfect |
PHPInsights Code | 97% | β Excellent |
PHPInsights Complexity | 100% | β Excellent |
PHPInsights Architecture | 100% | β Excellent |
PHPInsights Style | 98% | β Excellent |
Tests | 276 passed | β All Green |
π License
MIT License. See LICENSE for details.
π Support
- π§ Email: kontakt@djweb.pl
- π Issues: GitHub Issues
- π Documentation: GitHub Wiki
- π¬ Discussions: GitHub Discussions
**Created with β€οΈ by DjWeb **
Modern PHP solutions for future businesses