Ga naar de inhoud
agovena.
agovena.
Aan de slag
Community

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:

bash
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:

json
{
  "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:

php
$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() en label() identificeren de adapter.
  • capabilities() retourneert PaymentGatewayCapabilities.
  • initiate(PaymentInitiation) retourneert PaymentInitiationResult.
  • mapStatus(string) vertaalt providerstatussen naar Core PaymentStatus.
  • verifyWebhook(Request) authenticeert inkomende providerberichten.
  • parseWebhook(Request) maakt een genormaliseerde WebhookPayload.
  • refund(RefundRequest) retourneert RefundResult.
  • health() retourneert HealthResult.

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.

bash
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.

Doorzoek de documentatie

Zoek handleidingen, commando’s en API-endpoints

Waar ben je naar op zoek?

Documentatie