canaltp/nmm-portal-bundle
Laravel bundle for building an NMM portal: provides prebuilt portal features, configuration, and integration helpers to accelerate development of a portal-style web app, including common modules, routing, and service setup.
Installation Add the bundle via Composer:
composer require canaltp/nmm-portal-bundle
Enable it in config/bundles.php (Symfony):
CanalTP\NmmPortalBundle\NmmPortalBundle::class => ['all' => true],
Configuration Publish the default config:
php bin/console nmm-portal:install
Update config/packages/nmm_portal.yaml with your API credentials (e.g., canal_tp_api_key).
First Use Case Fetch a basic portal resource (e.g., a user profile or transaction):
use CanalTP\NmmPortalBundle\Service\PortalClient;
class MyController extends AbstractController
{
public function __construct(private PortalClient $portalClient) {}
public function showProfile()
{
$response = $this->portalClient->get('/users/me');
return $this->json($response->getData());
}
}
API Integration
Use the PortalClient service for all API calls. Wrap requests in a service layer:
// src/Service/UserService.php
class UserService
{
public function __construct(private PortalClient $client) {}
public function fetchUser($id)
{
return $this->client->get("/users/{$id}");
}
}
Webhook Handling Register a controller to handle incoming webhooks (e.g., payment events):
# config/routes.yaml
nmm_portal_webhook:
path: /webhook/nmm-portal
controller: App\Controller\NmmWebhookController::handle
// src/Controller/NmmWebhookController.php
class NmmWebhookController extends AbstractController
{
public function handle(Request $request, PortalClient $client)
{
$payload = json_decode($request->getContent(), true);
$client->verifyWebhook($payload); // Built-in verification
// Process logic...
}
}
Entity Mapping Use Symfony’s Serializer to map API responses to entities:
// src/Entity/User.php
#[ORM\Entity]
class User
{
#[ORM\Id, ORM\GeneratedValue]
private ?int $id = null;
#[ORM\Column]
private string $canalId;
}
// src/Service/UserMapper.php
class UserMapper
{
public function map(array $data): User
{
$user = new User();
$user->setCanalId($data['id']);
return $user;
}
}
Error Handling Centralize API error responses in a decorator:
class PortalClientDecorator implements PortalClientInterface
{
public function __construct(private PortalClient $client) {}
public function get(string $endpoint)
{
try {
return $this->client->get($endpoint);
} catch (ApiException $e) {
// Log, retry, or throw custom exception
throw new PortalException($e->getMessage());
}
}
}
Rate Limiting The bundle doesn’t auto-retry on rate limits. Implement exponential backoff in your service layer:
use Symfony\Component\HttpClient\RetryableHttpClient;
$client = new RetryableHttpClient(
$httpClient,
[
'max_retries' => 3,
'delay' => 1000,
'multiplier' => 2,
'statuses' => [429],
]
);
Webhook Verification
Always verify webhook payloads using the verifyWebhook() method. Skipping this risks security vulnerabilities:
$client->verifyWebhook($payload, $request->getSignature());
Deprecated Endpoints The bundle may not document deprecated API routes. Check the CanalTP API docs for breaking changes.
Configuration Overrides
Avoid hardcoding API keys in services. Use Symfony’s ParameterBag:
# config/services.yaml
services:
CanalTP\NmmPortalBundle\Service\PortalClient:
arguments:
$apiKey: '%env(NMM_PORTAL_API_KEY)%'
debug: true in nmm_portal.yaml to log raw API responses:
nmm_portal:
debug: true
createMock() to test services without hitting the API:
$mockClient = $this->createMock(PortalClient::class);
$mockClient->method('get')->willReturn(new Response(json_encode(['id' => 123])));
Accept: application/json and Authorization: Bearer {key} headers are included in requests.Custom Responses
Extend the Response class to add domain-specific methods:
class CustomResponse extends Response
{
public function getUserId(): int
{
return $this->getData()['id'];
}
}
Override the bundle’s ResponseFactory to return your class.
Middleware
Add request/response middleware via Symfony’s kernel.request event:
// src/EventListener/NmmMiddleware.php
class NmmMiddleware implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [KernelEvents::REQUEST => 'onKernelRequest'];
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
$request->headers->set('X-Custom-Header', 'value');
}
}
Event Dispatching
Trigger events for API responses (e.g., user.fetched):
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
class PortalClientDecorator
{
public function __construct(
private PortalClient $client,
private EventDispatcherInterface $dispatcher
) {}
public function get(string $endpoint)
{
$response = $this->client->get($endpoint);
$this->dispatcher->dispatch(new UserFetchedEvent($response));
return $response;
}
}
How can I help you explore Laravel packages today?