djweb/payments 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

djweb/payments

最新稳定版本:1.0.0

Composer 安装命令:

composer require djweb/payments

包简介

Payment and invoice integration package for Stripe and iFirma

README 文档

README

PHP Version Tests Coverage PHPStan PHPInsights PHPInsights PHPInsights License

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

**Created with ❤️ by DjWeb **

Modern PHP solutions for future businesses

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 1
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2025-09-19

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固