Thank you for considering contributing to the Digistore24 API Client! This document provides guidelines and instructions for contributing.
- Code of Conduct
- Getting Started
- Development Setup
- Coding Standards
- Project Structure
- Making Changes
- Testing
- Submitting Changes
- Documentation
This project follows a professional code of conduct. Be respectful, constructive, and collaborative in all interactions.
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/YOUR-USERNAME/digistore24-api.git cd digistore24-api - Add upstream remote:
git remote add upstream https://github.com/GoSuccessHQ/digistore24-api.git
- PHP 8.4.0 or higher (required for property hooks)
- Composer
- Git
# Install dependencies
composer install
# Run tests to verify setup
composer testThis project requires PHP 8.4+ and uses modern features:
- ✅ Property hooks for validation
- ✅ Typed properties
- ✅ Constructor property promotion
- ✅ Enums
- ✅ Match expressions
Preferred:
final class BuyerData
{
public string $email {
set {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email");
}
$this->email = $value;
}
}
}Avoid:
final class BuyerData
{
public function __construct(
private string $email
) {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email");
}
}
}Preferred:
use GoSuccess\Digistore24\Api\Http\Response;
public ?Response $response = null;Avoid:
public ?\GoSuccess\Digistore24\Api\Http\Response $response = null;Each class, interface, or enum must be in its own file. Helper classes should be extracted:
Preferred:
src/Response/Eticket/EticketDetail.php # Helper class
src/Response/Eticket/GetEticketResponse.php # Main response
Avoid:
// Both in GetEticketResponse.php
final class GetEticketResponse { }
final class EticketDetail { } // ❌ Extract to own file- Directories: Singular form (
Exception/,Request/,Response/) - Classes: PascalCase (
CreateBuyUrlRequest,BuyerData) - Methods: camelCase (
getCommission(),createBuyUrl()) - Properties: camelCase (
$firstName,$productId) - Constants: PascalCase in enums (
Method::Get,StatusCode::Ok)
Preferred:
echo "Buy URL: {$response->url}\n";
throw new ApiException("Invalid status: {$status}");Avoid:
echo "Buy URL: " . $response->url . "\n";
throw new ApiException("Invalid status: " . $status);Always use strict types and full type hints:
declare(strict_types=1);
final class Example
{
public function process(int $id, ?string $name = null): bool
{
// ...
}
}Make classes final unless they're explicitly designed for inheritance:
final class BuyerData { } // ✅ Preferred
abstract class AbstractRequest { } // ✅ OK for base classes
class SomeData { } // ❌ Add finalsrc/
├── Base/ # Abstract base classes
├── Client/ # HTTP client implementation
├── Contract/ # Interfaces
├── DataTransferObject/ # DTOs with property hooks
├── Exception/ # Exception hierarchy
├── Http/ # HTTP-related classes
├── Request/ # API request classes
├── Resource/ # Resource classes (12 resources)
├── Response/ # API response classes
└── Util/ # Utility classes
- Create in
src/DataTransferObject/ - Use property hooks for validation
- Make class
final - Add comprehensive PHPDoc
declare(strict_types=1);
namespace GoSuccess\Digistore24\Api\DataTransferObject;
/**
* Buyer information for API requests.
*/
final class BuyerData
{
public string $email {
set {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException("Invalid email format");
}
$this->email = $value;
}
}
public ?string $firstName = null;
public ?string $lastName = null;
}- Create in appropriate
src/Request/*/subdirectory - Extend
AbstractRequest - Use DTOs instead of arrays
- Document all properties
declare(strict_types=1);
namespace GoSuccess\Digistore24\Api\Request\BuyUrl;
use GoSuccess\Digistore24\Api\Base\AbstractRequest;
use GoSuccess\Digistore24\Api\DTO\BuyerData;
/**
* Create a new buy URL.
*/
final class CreateBuyUrlRequest extends AbstractRequest
{
public int $productId;
public ?string $validUntil = null;
public ?BuyerData $buyer = null;
public function getEndpoint(): string
{
return '/json/createBuyUrl';
}
}- Create in appropriate
src/Response/*/subdirectory - Extend
AbstractResponse - Extract helper classes to separate files
declare(strict_types=1);
namespace GoSuccess\Digistore24\Api\Response\BuyUrl;
use GoSuccess\Digistore24\Api\Base\AbstractResponse;
/**
* Response from createBuyUrl endpoint.
*/
final class CreateBuyUrlResponse extends AbstractResponse
{
public string $url;
public ?string $buyUrlId = null;
}# Run all tests
composer test
# Run with coverage
composer test:coverage
# Run specific test file
./vendor/bin/phpunit tests/Unit/DataTransferObject/BuyerDataTest.phpEvery new feature must include tests:
declare(strict_types=1);
namespace GoSuccess\Digistore24\Api\Tests\Unit\DataTransferObject;
use GoSuccess\Digistore24\Api\DTO\BuyerData;
use PHPUnit\Framework\TestCase;
final class BuyerDataTest extends TestCase
{
public function testEmailValidationAcceptsValidEmail(): void
{
$buyer = new BuyerData();
$buyer->email = 'test@example.com';
$this->assertSame('test@example.com', $buyer->email);
}
public function testEmailValidationRejectsInvalidEmail(): void
{
$this->expectException(\InvalidArgumentException::class);
$buyer = new BuyerData();
$buyer->email = 'invalid-email';
}
}- Aim for 90%+ code coverage
- All DTOs must have validation tests
- All Request/Response classes must have unit tests
- Integration tests for complete workflows
feature/add-voucher-dto- New featuresfix/email-validation- Bug fixesdocs/update-readme- Documentationrefactor/extract-helper-class- Code refactoring
Follow conventional commit format:
type: Short description (max 72 chars)
- Detailed bullet point 1
- Detailed bullet point 2
- Reference to issue if applicable
Types:
feat:New featurefix:Bug fixdocs:Documentation changesrefactor:Code refactoringtest:Adding or updating testschore:Maintenance tasks
Examples:
git commit -m "feat: Add VoucherData DTO with validation" \
-m "- Implement property hooks for code, discount, validity" \
-m "- Add comprehensive unit tests" \
-m "- Update documentation"
git commit -m "fix: Correct email validation in BuyerData" \
-m "- Use filter_var with FILTER_VALIDATE_EMAIL" \
-m "- Add test for edge cases" \
-m "- Fixes #123"# Fetch upstream changes
git fetch upstream
# Merge into your main branch
git checkout main
git merge upstream/main
# Rebase your feature branch
git checkout feature/your-feature
git rebase main- Update your branch with latest upstream changes
- Run tests and ensure they pass:
composer test - Update documentation if needed
- Push to your fork:
git push origin feature/your-feature
- Create Pull Request on GitHub
- Tests pass locally (
composer test) - Code follows style guidelines
- One class per file
- Imports instead of FQCNs
- Property hooks used where applicable
- Comprehensive PHPDoc comments
- New features have tests
- Documentation updated
- Commit messages follow conventions
- No merge conflicts with main branch
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
How has this been tested?
## Checklist
- [ ] Tests pass
- [ ] Code follows style guide
- [ ] Documentation updated
- [ ] Commits follow conventionsUse comprehensive PHPDoc:
/**
* Create a new buy URL for a product.
*
* @param CreateBuyUrlRequest $request The request with product ID and options
* @return CreateBuyUrlResponse The response with generated URL
* @throws AuthenticationException If API key is invalid
* @throws ValidationException If request data is invalid
* @throws ApiException For other API errors
*/
public function create(CreateBuyUrlRequest $request): CreateBuyUrlResponse
{
// ...
}When adding a new endpoint, create documentation in docs/:
# endpointName
Brief description of what this endpoint does.
## Endpoint
\`\`\`
POST /json/endpointName
\`\`\`
## Request Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `param1` | string | Yes | Description |
| `param2` | int | No | Description |
## Response
\`\`\`php
[
'result' => 'success',
'data' => [
'field1' => 'value',
'field2' => 123
]
]
\`\`\`
## Usage Example
\`\`\`php
use GoSuccess\Digistore24\Api\Digistore24;
$api = new Digistore24($config);
$response = $api->resource->method($request);
\`\`\`
## Error Responses
| Code | Message | Description |
|------|---------|-------------|
| 400 | Bad Request | Invalid parameters |
| 404 | Not Found | Resource not found |- Open an issue for bugs or feature requests
- Check existing issues before creating new ones
- Be clear and provide examples
By contributing, you agree that your contributions will be licensed under the MIT License.
Thank you for contributing to the Digistore24 API Client! 🎉