Voor ontwikkelaars
Extensions bouwen
Providerneutrale Extensionmanifests, registratie, instellingen, levenscyclus en adaptercontracten.
Op deze pagina
Breid een contract uit, niet het hele platform
Een Extension verbindt een bestaand Agovena-domein met een provider of levert een bijdrage aan een bestaand uitbreidingspunt. Core beheert de commerceregels, een Module kan het optionele domein beheren en de Extension beheert de adapter. Deze handleiding beschrijft het ontwikkelcontract, niet de providergebonden accountinstellingen voor winkeliers.
De publieke Extension-interface declareert id(): string en register(ExtensionContext $context): void. Een gebruikelijke Laravel-serviceprovider biedt extension(): Extension. Houd diens Laravel-methode register(): void gescheiden van de contextregistratie op het Extensionobject.
Genereer en controleer het skelet
Voer dit vanuit Core uit met een ingestelde sibling-repository:
php artisan agovena:make-extension acme-pay --category=payment_gateway
Als AGOVENA_OPTIONAL_PACKAGES_PATH naar de sibling-checkout verwijst, schrijft de generator naar optional-packages/extensions/payments/acme-pay. De categoriewaarde is payment_gateway, maar de bijbehorende map heet payments. Het manifest-ID bepaalt de identiteit, niet de categorie in het bestandspad.
ID's beginnen met een letter en gebruiken kleine letters en cijfers met enkele koppeltekens. Een bestaand doel vereist expliciet --force om de gegenereerde bestanden te overschrijven. Net als bij Modules moet je de huidige gegenereerde voorwaarde agovena: "^0.1" controleren tegen de geïnstalleerde platformversie. De terugvalmap extensions/ in Core wordt zonder extra configuratie niet door de runtime ontdekt.
Bronnen: MakeExtensionCommand en ScaffoldingGenerator.
Manifestschema
Dit is een illustratief manifest voor een eigen adapter, geen meegeleverde provider:
{
"id": "acme-pay",
"name": "Acme Pay",
"version": "0.1.0",
"description": "Een eigen betaaladapter.",
"author": "Je team",
"category": "payment_gateway",
"agovena": "^0.0.1",
"provider": "Acme\\Payments\\AcmePayServiceProvider",
"autoload": {"psr-4": {"Acme\\Payments\\": "src/"}},
"dependencies": [],
"module_dependencies": [],
"production_ready": false,
"settings": [{
"key": "api_key",
"label": "API-sleutel",
"type": "string",
"secret": true,
"required": true,
"help": "Bewaar de sleutel in versleutelde Extension-instellingen."
}]
}
id, name, provider en category zijn verplicht. Optionele velden zijn onder meer version (standaard 0.0.0), description, author, agovena (standaard *), dependencies, module_dependencies, settings en autoload.psr-4.
dependencies bevat Extension-ID's. module_dependencies bevat Module-ID's. Deze lijsten bevatten geen versievoorwaarden en installeren niets automatisch. Een provisioningadapter moet aangeven welke domeinmodule hij werkelijk nodig heeft.
production_ready moet een boolean zijn en is standaard false. De manager staat false alleen toe in local of testing; andere omgevingen vereisen true. Zet deze vlag niet alleen aan om een omgevingscontrole te omzeilen voordat de adapter is geverifieerd.
Geldige categorieën zijn payment_gateway, provisioning, shipping, authentication, storage, notifications, analytics, tax, domain en other. Een categorielabel maakt op zichzelf geen registratie of integratiegedrag aan.
Bronnen: ExtensionManifest en ExtensionCategory.
Registratiepunten
ExtensionContext biedt:
| Methode | Doel |
|---|---|
extensionId() |
Huidig Extension-ID |
admin() |
Gedeelde Adminregistratie |
settings() / getSetting($key, $default) |
Instellingen per Extension lezen |
setting($definition) |
Instellingenveld tijdens runtime declareren |
paymentGateway($gateway) |
Een PaymentGateway registreren |
provisioner($provider) |
Een Provisioner registreren |
shippingCarrier($carrier) |
Een ShippingCarrier registreren |
cartRequirements($contributor) |
Een CartRequirementContributor toevoegen |
invoiceDocument($view) |
Factuurpresentatie kiezen |
health($callback) |
Callback registreren die HealthResult retourneert |
Een bestaande betaaladapter gebruikt bijvoorbeeld deze registratie:
$context->setting(new ExtensionSettingDefinition(
key: 'api_key',
label: 'mollie::messages.settings.api_key',
type: 'string',
secret: true,
required: true,
help: 'mollie::messages.settings.api_key_help',
));
$context->paymentGateway(app(MolliePaymentGateway::class));
$context->health(static fn () => app(MolliePaymentGateway::class)->health());
Dit fragment toont alleen het registratiepatroon. Imports en providerbindings staan in MollieExtension en MollieServiceProvider. Gebruik voor een andere provider eigen namespaces, labels en een eigen adapterimplementatie.
Providercontracten
Een betaaladapter implementeert PaymentGateway:
id()enlabel()identificeren de adapter.capabilities()retourneertPaymentGatewayCapabilities.initiate(PaymentInitiation)retourneertPaymentInitiationResult.mapStatus(string)vertaalt providerstatussen naar CorePaymentStatus.verifyWebhook(Request)authenticeert inkomende providerberichten.parseWebhook(Request)maakt een genormaliseerdeWebhookPayload.refund(RefundRequest)retourneertRefundResult.health()retourneertHealthResult.
De capabilityvlaggen zijn refunds, partialRefunds, recurring, webhooks, redirect, statusSync en cancelPending. Alle zijn standaard false. Geef alleen ondersteund gedrag aan en implementeer aanvullende contracten wanneer die daarbij horen. Een geslaagde browserredirect is nooit betalingsbewijs. De Core-webhookhandler verifieert eerst, bewaart een identiteit voor herhalingen en past daarna de genormaliseerde status toe.
De basiscontracten Provisioner en ShippingCarrier vereisen alleen id() en label(). Operationele functies zijn aparte optionele interfaces. Leid mogelijkheden niet af uit de naam van een provider.
Interfaces voor de operationele levenscyclus
ProvisionerLifecycle ontvangt ServiceInstanceInfo, geen leverancierspecifiek SDK-model. De interface definieert provision(), activate(), suspend(), unsuspend() en terminate() met void als terugkeertype. poll() en syncStatus() retourneren bijgewerkte ServiceInstanceInfo. changePlan() ontvangt dezelfde instantie plus een string of gestructureerde array voor het plan. Houd de vertaling van providerstatussen binnen de adapter en test herhaalde levenscyclusverzoeken zonder externe resources te dupliceren.
ProvisionerActions definieert actions($instance): array met ProvisionerAction-objecten en runAction($instance, $actionId): void. ProvisionerPanel retourneert nullable ProvisionerPanelData voor veilige weergavevelden. Geen van beide interfaces staat willekeurige door klanten opgegeven providercommando's toe.
Bij verzending zijn aanmaak en tracking gescheiden. CreatesCarrierShipments declareert createShipment(Order $order, string $serviceCode): CarrierShipmentResult en cancelShipment(string $externalId): void. TracksShipments declareert tracking(string $externalId): array, met status, nullable tracking_number en nullable tracking_url. Tariefoffertes hebben eigen contracten en worden niet vanzelf ondersteund door een trackingimplementatie.
Instellingen en geheimen
Manifestinstellingen normaliseren key, label, type, secret, required, default en help. Via de context kun je daarnaast runtimevelddefinities registreren. Houd beide definities gelijk. Installatie bewaart een niet-null standaardwaarde alleen als er nog geen bestaande instellingswaarde is.
ExtensionSettingsRepository versleutelt waarden die met secret: true worden opgeslagen. Niet-geheime waarden krijgen JSON-codering. Een mislukte ontsleuteling markeert de instelling als corrupt en retourneert de gevraagde standaardwaarde in plaats van versleutelde inhoud te tonen.
De omgevingsnaam is AGOVENA_EXT_<EXTENSION>_<KEY>, waarbij niet-alfanumerieke scheidingstekens underscores worden. Dit is geen algemene credentialoverride: de omgeving wordt alleen gelezen als er geen databaserij is. Sleutels met gevoelige patronen zoals API key, token, secret, password, credential of DSN worden geweigerd. Bewaar credentials via de versleutelde instellingenflow. Bestaande opgeslagen waarden hebben voorrang.
Levenscyclus en afhankelijkheden
ExtensionManager ontdekt eerst geïnstalleerde opslag, daarna de optional-checkout en vervolgens extra paden. Zowel extensions/<id> als extensions/<category>/<id> wordt ondersteund. Het eerste ID wint.
Installatie controleert compatibiliteit, vindbare Extension-afhankelijkheden en geïnstalleerde Module-afhankelijkheden. Daarna worden een uitgeschakeld installatierecord, migraties en standaardinstellingen verwerkt. Inschakelen vereist daarnaast ingeschakelde afhankelijkheden, voert migraties uit, bewaart de status, registreert de provider en roept het Extensioncontract aan.
Uitschakelen behoudt instellingen en gegevens en schakelt afhankelijke Extensions uit. De runtime wordt opnieuw opgebouwd nadat betaal-, provisioning-, vervoerder- en geregistreerde runtimeproviderregistraties zijn leeggemaakt. Dit is geen universele terugdraaiing van willekeurige Laravel-listeners of externe neveneffecten. Maak registratie idempotent en test uitschakelen voor ieder uitbreidingspunt dat je gebruikt.
Geïnstalleerde Extensions doen mee aan migratie-upgrades, ook als ze uitgeschakeld zijn. Verwijderen haalt het installatierecord weg. Generiek gegevens wissen is niet geïmplementeerd; purgeData: true wordt geweigerd.
Test meer dan één geslaagde providerresponse
Gebruik fakes voor providerverkeer en controleer de genormaliseerde domeinuitkomst. Test ongeldige callbacks, dubbele berichten, afwijkende bedragen of valuta waar relevant, ontbrekende configuratie, time-outs en uitgeschakelde afhankelijkheden. Houd livecredentials buiten de testomgeving.
php artisan test tests/Feature/ExtensionRuntimeTest.php
php artisan test tests/Feature/ExtensionPluginSeamsTest.php
php artisan test tests/Feature/ExtensionModuleDependencyTest.php
Dit zijn bestaande Core-testsuites. Voeg adapterspecifieke tests toe bij de relevante package-integratietests. Zie Events en webhooks voor het verschil tussen inkomende en uitgaande berichten en bijdragen voor de bredere kwaliteitscontroles.