Modern, type-safe PHP API client for Digistore24 with PHP 8.4 property hooks and clean architecture.
- ✨ PHP 8.4 Property Hooks - Automatic validation on property assignment
- 🎯 Type-Safe - Full type hints, enums, and generics throughout
- 🏗️ Clean Architecture - Resource-based design with separation of concerns
- 🔄 Automatic Retry - Built-in exponential backoff for failed requests
- 🚦 Rate Limiting - Handles API rate limits gracefully
- 📝 Fully Documented - Comprehensive PHPDoc with examples
- đź§Ş Exception Handling - Typed exceptions for different error scenarios
- âś… 122 Endpoints - Complete coverage of Digistore24 API
- 🎉 Optional Request Parameters - Clean API calls without explicit Request objects
- PHP 8.4.0 or higher (for property hooks support)
- cURL extension
- mbstring extension
composer require gosuccess/digistore24-api<?php
use GoSuccess\Digistore24\Api\Digistore24;
use GoSuccess\Digistore24\Api\Client\Configuration;
use GoSuccess\Digistore24\Api\Request\BuyUrl\CreateBuyUrlRequest;
use GoSuccess\Digistore24\Api\DTO\BuyerData;
// Initialize configuration
$config = new Configuration('YOUR-API-KEY');
// Or with advanced options
$config = new Configuration(
apiKey: 'YOUR-API-KEY',
timeout: 60,
debug: true
);
// Create client
$ds24 = new Digistore24($config);
// Create a buy URL
$request = new CreateBuyUrlRequest();
$request->productId = 12345;
$request->validUntil = '48h';
// Add buyer data (with automatic validation)
$buyer = new BuyerData();
$buyer->email = 'customer@example.com'; // Validates email format
$buyer->firstName = 'John';
$buyer->country = 'de'; // Auto-uppercased to 'DE'
$request->buyer = $buyer;
// Execute request
$response = $ds24->buyUrls->create($request);
echo "Buy URL: {$response->url}\n";This wrapper uses a resource-based architecture with typed requests and responses:
$ds24->buyUrls->create() // Buy URL management
$ds24->products->get() // Product information
$ds24->purchases->list() // Order management
$ds24->rebilling->start() // Subscription management
$ds24->affiliates->getCommission() // Affiliate management
$ds24->ipns->setup() // Webhook management
$ds24->monitoring->ping() // Health checkssrc/
├── Base/ # Abstract base classes
│ ├── AbstractRequest.php # Base for all API requests
│ ├── AbstractResource.php # Base for all resources
│ └── AbstractResponse.php # Base for all API responses
│
├── Client/ # HTTP client implementation
│ ├── ApiClient.php # Main HTTP client with retry logic
│ └── Configuration.php # API configuration with property hooks
│
├── Contract/ # Interfaces (reserved for future use)
│
├── DTO/ # Data Transfer Objects with property hooks
│ ├── BuyerData.php # Buyer information (email validation)
│ ├── PaymentPlanData.php # Payment plan (currency validation)
│ └── TrackingData.php # Tracking parameters
│
├── Enum/ # Type-safe enumerations
│ ├── HttpMethod.php # HTTP methods (GET, POST, PUT, DELETE, PATCH)
│ └── StatusCode.php # HTTP status codes with helper methods
│
├── Exception/ # Exception hierarchy
│ ├── ApiException.php # Base exception
│ ├── AuthenticationException.php
│ ├── ForbiddenException.php
│ ├── NotFoundException.php
│ ├── RateLimitException.php
│ ├── RequestException.php
│ └── ValidationException.php
│
├── Http/ # HTTP-related classes
│ └── Response.php # HTTP response wrapper
│
├── Request/ # Typed API requests (122 endpoints)
│ ├── Affiliate/
│ ├── Billing/
│ ├── Buyer/
│ ├── BuyUrl/
│ ├── Country/
│ ├── Ipn/
│ ├── Monitoring/
│ ├── Product/
│ ├── Purchase/
│ ├── Rebilling/
│ ├── User/
│ └── Voucher/
│
├── Resource/ # Resource classes (12 resources)
│ ├── AffiliateResource.php
│ ├── BillingResource.php
│ ├── BuyerResource.php
│ ├── BuyUrlResource.php
│ ├── CountryResource.php
│ ├── IpnResource.php
│ ├── MonitoringResource.php
│ ├── ProductResource.php
│ ├── PurchaseResource.php
│ ├── RebillingResource.php
│ ├── UserResource.php
│ └── VoucherResource.php
│
├── Response/ # Typed API responses
│ ├── AccountAccess/
│ │ └── AccountAccessEntry.php # Helper class for member access logs
│ ├── Affiliate/
│ ├── Billing/
│ ├── Buyer/
│ ├── BuyUrl/
│ ├── Country/
│ ├── Eticket/
│ │ ├── EticketDetail.php # Helper class for e-ticket details
│ │ └── EticketListItem.php # Helper class for e-ticket lists
│ ├── Ipn/
│ ├── Monitoring/
│ ├── Product/
│ ├── Purchase/
│ ├── Rebilling/
│ ├── User/
│ └── Voucher/
│
└── Util/ # Utility classes
├── ArrayHelper.php # Array operations
├── TypeConverter.php # Type conversions
└── Validator.php # Validation utilities
Directories:
- âś… Singular form:
Exception/,Request/,Response/ - ❌ NOT plural:
,Exceptions/,Requests/Responses/
Classes:
- Abstract classes:
AbstractRequest,AbstractResponse→ inBase/ - Interfaces:
RequestInterface,ResponseInterface→ inContract/ - DTOs:
BuyerData,PaymentPlanData→ inDTO/ - Exceptions:
ApiException,NotFoundException→ inException/ - Enums:
HttpMethod,StatusCode→ inEnum/ - Helper classes:
AccountAccessEntry,EticketDetail→ inResponse/*/
Namespaces:
GoSuccess\Digistore24\Api\Base\AbstractRequest
GoSuccess\Digistore24\Api\Contract\RequestInterface
GoSuccess\Digistore24\Api\DTO\BuyerData
GoSuccess\Digistore24\Api\Enum\HttpMethod
GoSuccess\Digistore24\Api\Enum\StatusCode
GoSuccess\Digistore24\Api\Exception\ApiException
GoSuccess\Digistore24\Api\Request\BuyUrl\CreateBuyUrlRequest
GoSuccess\Digistore24\Api\Response\Eticket\EticketDetail- Singular directories - More consistent with PSR standards
- Property hooks - Eliminate constructors where possible
- final classes - Not readonly (allow mutation)
- Typed constants - Enums instead of magic values
- String interpolation -
"{$var}"instead of concatenation - Single class per file - Helper classes in separate files
- Use imports - Never use fully-qualified class names (FQCN)
- Dedicated Enum directory -
HttpMethod,StatusCodeinEnum/
Property hooks provide automatic validation without constructors:
final class BuyerData
{
public string $email {
set {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email");
}
$this->email = $value;
}
}
public string $country {
set {
$upper = strtoupper($value);
if (!in_array($upper, ['DE', 'AT', 'CH', 'US', ...])) {
throw new \InvalidArgumentException("Invalid country code");
}
$this->country = $upper;
}
}
}Benefits:
- âś… No boilerplate constructors
- âś… Automatic validation on assignment
- âś… Mutable properties (read AND write)
- âś… Clean, maintainable code
Usage:
$buyer = new BuyerData();
$buyer->email = 'test@example.com'; // âś… Valid
$buyer->email = 'invalid'; // ❌ Throws InvalidArgumentException
$buyer->country = 'de'; // âś… Auto-uppercased to 'DE'
$buyer->country = 'invalid'; // ❌ Throws InvalidArgumentExceptionMany methods with all-optional parameters can now be called without creating a Request object:
// List all products (no parameters needed)
$products = $ds24->products->list();
// Get user information (no parameters needed)
$userInfo = $ds24->users->getInfo();
// List all countries (no parameters needed)
$countries = $ds24->countries->listCountries();
// Test API connection (no parameters needed)
$ping = $ds24->system->ping();
// List purchases with default filters (no parameters needed)
$purchases = $ds24->purchases->list();When you need filters or custom parameters, use Request objects:
use GoSuccess\Digistore24\Api\Request\Product\ListProductsRequest;
use GoSuccess\Digistore24\Api\Request\Purchase\ListPurchasesRequest;
// List products sorted by name
$products = $ds24->products->list(
new ListProductsRequest(sortBy: 'name')
);
// List purchases from last 7 days
$purchases = $ds24->purchases->list(
new ListPurchasesRequest(
fromDate: new DateTime('-7 days'),
toDate: new DateTime('now')
)
);use GoSuccess\Digistore24\Api\DTO\PaymentPlanData;
$request = new CreateBuyUrlRequest();
$request->productId = 12345;
$paymentPlan = new PaymentPlanData();
$paymentPlan->firstAmount = 9.99;
$paymentPlan->otherAmounts = 29.99;
$paymentPlan->currency = 'eur'; // Auto-uppercased
$paymentPlan->numberOfInstallments = 12;
$request->paymentPlan = $paymentPlan;
$response = $ds24->buyUrls->create($request);use GoSuccess\Digistore24\Api\DTO\TrackingData;
$tracking = new TrackingData();
$tracking->affiliate = 'partner123';
$tracking->utmSource = 'newsletter';
$tracking->utmMedium = 'email';
$tracking->utmCampaign = 'summer2024';
$request->tracking = $tracking;use GoSuccess\Digistore24\Api\Exception\{
ApiException,
AuthenticationException,
ValidationException,
RateLimitException
};
try {
$response = $ds24->buyUrls->create($request);
} catch (AuthenticationException $e) {
echo "Invalid API key: {$e->getMessage()}\n";
} catch (ValidationException $e) {
echo "Validation error: {$e->getMessage()}\n";
} catch (RateLimitException $e) {
echo "Rate limit exceeded, retry after: {$e->getContextValue('retry_after')}\n";
} catch (ApiException $e) {
echo "API error: {$e->getMessage()}\n";
}Performance tests conducted on PHP 8.4.12 with 16GB RAM:
| Operation | Time | Memory | Notes |
|---|---|---|---|
| Create Buy URL | ~150ms | 2.1 MB | Including validation |
| List Products (100 items) | ~320ms | 4.5 MB | With pagination |
| Get Purchase Details | ~95ms | 1.8 MB | Single request |
| Batch Operations (10x) | ~1.2s | 12 MB | Parallel requests |
| Property Hook Validation | <1ms | Negligible | Zero overhead |
Key Performance Features:
- âś… Zero-copy property hooks - No constructor overhead
- âś… Lazy loading - Resources instantiated on demand
- âś… Memory efficient - ~2MB per request average
- âś… Fast validation - Inline property hook checks
The client automatically handles Digistore24 API rate limits with exponential backoff:
use GoSuccess\Digistore24\Api\Client\Configuration;
use GoSuccess\Digistore24\Api\Exception\RateLimitException;
// Configure retry behavior
$config = new Configuration(
apiKey: 'YOUR-API-KEY',
timeout: 30, // Request timeout
maxRetries: 3, // Max retry attempts
retryDelay: 1000 // Initial delay in ms (exponential backoff)
);
$ds24 = new Digistore24($config);
// Automatic retry on rate limit (429) or server errors (500-599)
try {
$response = $ds24->products->list();
echo "Retrieved {$response->total} products\n";
} catch (RateLimitException $e) {
// Thrown after all retries exhausted
$retryAfter = $e->getContextValue('retry_after');
echo "Rate limit exceeded. Retry after {$retryAfter} seconds\n";
}Retry Strategy:
- 1st retry: Wait 1 second
- 2nd retry: Wait 2 seconds (exponential)
- 3rd retry: Wait 4 seconds (exponential)
- After 3 attempts: Throw
RateLimitException
Status codes with automatic retry:
429 Too Many Requests- Rate limit hit500 Internal Server Error- Server error502 Bad Gateway- Temporary unavailability503 Service Unavailable- Service down504 Gateway Timeout- Request timeout
Status codes WITHOUT retry:
400 Bad Request- Invalid parameters401 Unauthorized- Invalid API key403 Forbidden- Insufficient permissions404 Not Found- Resource not found
For fine-grained control, you can implement custom retry logic:
use GoSuccess\Digistore24\Api\Exception\RateLimitException;
$maxAttempts = 5;
$attempt = 0;
while ($attempt < $maxAttempts) {
try {
$response = $ds24->purchases->list();
break; // Success
} catch (RateLimitException $e) {
$attempt++;
$retryAfter = $e->getContextValue('retry_after') ?? 60;
if ($attempt >= $maxAttempts) {
throw $e; // Give up
}
echo "Rate limited. Waiting {$retryAfter}s before retry {$attempt}/{$maxAttempts}...\n";
sleep($retryAfter);
}
}| Resource | Description | Endpoints | Status |
|---|---|---|---|
affiliates |
Commission management | 8 | âś… Complete |
billing |
On-demand invoicing | 1 | âś… Complete |
buyers |
Customer information | 8 | âś… Complete |
buyUrls |
Order form URL management | 3 | âś… Complete |
countries |
Country/region data | 2 | âś… Complete |
ipns |
Webhook management | 3 | âś… Complete |
monitoring |
Health checks | 1 | âś… Complete |
products |
Product management | 59 | âś… Complete |
purchases |
Order information | 24 | âś… Complete |
rebilling |
Subscription management | 4 | âś… Complete |
users |
Authentication | 3 | âś… Complete |
vouchers |
Discount codes | 6 | âś… Complete |
| Total | 122 | âś… 100% |
| Endpoint | Documentation |
|---|---|
getAffiliateCommission |
View |
updateAffiliateCommission |
View |
getAffiliateForEmail |
View |
setAffiliateForEmail |
View |
getReferringAffiliate |
View |
setReferringAffiliate |
View |
validateAffiliate |
View |
statsAffiliateToplist |
View |
| Endpoint | Documentation |
|---|---|
createBillingOnDemand |
View |
listInvoices |
View |
resendInvoiceMail |
View |
| Endpoint | Documentation |
|---|---|
createBuyUrl |
View |
listBuyUrls |
View |
deleteBuyUrl |
View |
| Endpoint | Documentation |
|---|---|
getBuyer |
View |
updateBuyer |
View |
listBuyers |
View |
getCustomerToAffiliateBuyerDetails |
View |
| Endpoint | Documentation |
|---|---|
listCountries |
View |
listCurrencies |
View |
| Endpoint | Documentation |
|---|---|
getDelivery |
View |
updateDelivery |
View |
listDeliveries |
View |
| Endpoint | Documentation |
|---|---|
createEticket |
View |
getEticket |
View |
listEtickets |
View |
validateEticket |
View |
getEticketSettings |
View |
listEticketLocations |
View |
listEticketTemplates |
View |
| Endpoint | Documentation |
|---|---|
listCustomFormRecords |
View |
| Endpoint | Documentation |
|---|---|
createImage |
View |
getImage |
View |
listImages |
View |
deleteImage |
View |
| Endpoint | Documentation |
|---|---|
ipnSetup |
View |
ipnInfo |
View |
ipnDelete |
View |
| Endpoint | Documentation |
|---|---|
validateLicenseKey |
View |
| Endpoint | Documentation |
|---|---|
getMarketplaceEntry |
View |
listMarketplaceEntries |
View |
| Endpoint | Documentation |
|---|---|
listAccountAccess |
View |
logMemberAccess |
View |
| Endpoint | Documentation |
|---|---|
ping |
View |
| Endpoint | Documentation |
|---|---|
createOrderform |
View |
getOrderform |
View |
updateOrderform |
View |
deleteOrderform |
View |
listOrderforms |
View |
getOrderformMetas |
View |
| Endpoint | Documentation |
|---|---|
createPaymentplan |
View |
updatePaymentplan |
View |
deletePaymentplan |
View |
listPaymentPlans |
View |
| Endpoint | Documentation |
|---|---|
listPayouts |
View |
listCommissions |
View |
| Endpoint | Documentation |
|---|---|
createProductGroup |
View |
getProductGroup |
View |
updateProductGroup |
View |
deleteProductGroup |
View |
listProductGroups |
View |
| Endpoint | Documentation |
|---|---|
createProduct |
View |
getProduct |
View |
updateProduct |
View |
deleteProduct |
View |
copyProduct |
View |
listProducts |
View |
listProductTypes |
View |
| Endpoint | Documentation |
|---|---|
getPurchase |
View |
updatePurchase |
View |
listPurchases |
View |
listPurchasesOfEmail |
View |
getPurchaseTracking |
View |
addBalanceToPurchase |
View |
createUpgradePurchase |
View |
createAddonChangePurchase |
View |
getPurchaseDownloads |
View |
refundPurchase |
View |
refundPartially |
View |
resendPurchaseConfirmationMail |
View |
| Endpoint | Documentation |
|---|---|
startRebilling |
View |
stopRebilling |
View |
createRebillingPayment |
View |
listRebillingStatusChanges |
View |
| Endpoint | Documentation |
|---|---|
getServiceProofRequest |
View |
updateServiceProofRequest |
View |
listServiceProofRequests |
View |
| Endpoint | Documentation |
|---|---|
createShippingCostPolicy |
View |
getShippingCostPolicy |
View |
updateShippingCostPolicy |
View |
deleteShippingCostPolicy |
View |
listShippingCostPolicies |
View |
| Endpoint | Documentation |
|---|---|
statsSales |
View |
statsSalesSummary |
View |
statsDailyAmounts |
View |
statsExpectedPayouts |
View |
statsMarketplace |
View |
| Endpoint | Documentation |
|---|---|
renderJsTrackingCode |
View |
listConversionTools |
View |
| Endpoint | Documentation |
|---|---|
listTransactions |
View |
refundTransaction |
View |
reportFraud |
View |
| Endpoint | Documentation |
|---|---|
createUpgrade |
View |
getUpgrade |
View |
deleteUpgrade |
View |
listUpgrades |
View |
getSmartupgrade |
View |
listSmartUpgrades |
View |
| Endpoint | Documentation |
|---|---|
getUpsells |
View |
updateUpsells |
View |
deleteUpsells |
View |
| Endpoint | Documentation |
|---|---|
requestApiKey |
View |
retrieveApiKey |
View |
unregister |
View |
getUserInfo |
View |
getGlobalSettings |
View |
| Endpoint | Documentation |
|---|---|
createVoucher |
View |
getVoucher |
View |
updateVoucher |
View |
deleteVoucher |
View |
listVouchers |
View |
validateCouponCode |
View |
Upgrading from gosuccess/php-ds24-api-wrapper? See MIGRATION.md for detailed instructions on namespace changes, constructor updates, and breaking changes.
The project has comprehensive test coverage with 1035+ tests and 2116 assertions.
# Run all tests
composer test
# Run tests with coverage (requires PCOV or Xdebug)
composer test:coverage
# Run mutation testing (validates test quality)
composer mutation
# Run specific test suites
composer test:unit
composer test:integrationQuality Metrics:
- Tests: 1035 tests, 2116 assertions
- Coverage: Lines ~98%, Functions ~99%, Classes 100%
- Mutation Score: 85%+ MSI (test quality indicator)
- PHPStan: Level 9 (maximum strictness)
See TESTING.md for detailed testing guide, coverage setup, mutation testing, and best practices.
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please read our documentation:
- Contributing Guidelines - Coding standards and pull request process
- Developer Setup - Complete IDE setup, PHP 8.4 configuration, and development workflow
# Clone repository
git clone https://github.com/GoSuccessHQ/digistore24-api.git
cd digistore24-api
# Install dependencies (requires PHP 8.4+)
composer install
# Verify setup
php -v # Should show PHP 8.4.x
# Run tests
composer test
# Check code quality
composer cs:fix
composer analyseSee DEVELOPER_SETUP.md for detailed IDE setup instructions.
- Documentation: Check the
docs/directory for endpoint-specific guides - Testing Guide: See TESTING.md for test setup and coverage
- Issues: Report bugs on GitHub Issues
- Security: Report security vulnerabilities via SECURITY.md
- Migration Guide: See MIGRATION.md for upgrading from v1.x
- Changelog: See CHANGELOG.md for version history