cedricziel/canva-extension-helper
Installation:
composer require cedricziel/canva-extension-helper
Basic Controller Structure (for a Publish extension):
use Canva\HttpHelper;
use Canva\Publish\UploadRequest;
use Canva\Request as CanvaRequest;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class PublishController {
private string $canvaSecret;
public function __construct(string $canvaSecret) {
$this->canvaSecret = $canvaSecret;
}
public function upload(Request $request): Response {
$timestamp = $request->headers->get(CanvaRequest::HEADER_TIMESTAMP);
if (!HttpHelper::verifyTimestamp($timestamp, time())) {
throw new \RuntimeException('Invalid timestamp');
}
$signature = HttpHelper::calculatePostSignature(
$timestamp,
'/publish/resources/upload',
$request->getContent(),
$this->canvaSecret
);
if (!in_array($signature, explode(',', $request->headers->get(CanvaRequest::HEADER_SIGNATURES)))) {
throw new \RuntimeException('Invalid signature');
}
$uploadRequest = $request->getContent();
// Process $uploadRequest (e.g., deserialize with Symfony Serializer)
return new Response('Success');
}
}
First Use Case:
/publish/resources/upload request by validating Canva’s signature and timestamp.UploadRequest using Symfony’s SerializerInterface.Request Validation Middleware:
Use Canva\Middleware\PostHMACMiddleware and Canva\MiddlewareTimestampMiddleware for automatic validation:
$app->mount('/canva', $app->make(CanvaMiddleware::class));
Serialization:
Configure Symfony’s SerializerInterface to handle Canva\Publish\* request/response classes:
$serializer = new Serializer([
new ObjectNormalizer(null, null, null, new PropertyInfoExtractor(
[],
[new PhpDocExtractor(), new ReflectionExtractor()]
)),
], [new JsonEncoder()]);
Extension Types:
UploadRequest, GetResourceRequest, etc.Canva\Design\* classes for design-specific logic.Canva\RequestInterface.Error Handling:
Return Canva\ErrorResponse with standardized error codes (e.g., Error::CODE_INVALID_REQUEST).
Route::post('/canva/publish/upload', [PublishController::class, 'upload'])
->middleware([CanvaMiddleware::class]);
CanvaRequest and HttpHelper for unit tests:
$request = new Request([], [], [], [], [], [
'HTTP_X_CANVA_TIMESTAMP' => time(),
'HTTP_X_CANVA_SIGNATURES' => 'valid_signature',
]);
if (!$timestampValid) {
\Log::warning('Canva timestamp skew detected', ['local' => time(), 'remote' => $timestamp]);
}
Timestamp Skew:
HttpHelper::verifyTimestamp() leniency if your server clock drifts:
HttpHelper::verifyTimestamp($remoteTs, time(), 600); // 10-minute window
Signature Mismatches:
$canvaSecret (verify in services.yaml/config/services.php).X-Canva-Signatures header.$calculated = HttpHelper::calculatePostSignature(...);
\Log::debug('Signature mismatch', ['calculated' => $calculated, 'received' => $receivedSignatures]);
Serialization Issues:
UploadRequest fails to deserialize if assets array is malformed.$data = json_decode($request->getContent(), true);
if (empty($data['assets'])) {
throw new \InvalidArgumentException('Missing assets');
}
Middleware Order:
PostHMACMiddleware after TimestampMiddleware to avoid signature checks on invalid timestamps.Reuse Models:
Extend Canva\RequestInterface for custom requests:
class MyCustomRequest implements RequestInterface {
private string $customField;
// ...
}
Performance:
$cacheKey = md5($request->getContent());
$requestData = Cache::remember($cacheKey, 300, fn() => $serializer->deserialize(...));
Testing:
Canva\HttpHelper directly to generate test signatures:
$testSignature = HttpHelper::calculatePostSignature(time(), '/test', '{}', 'secret');
HttpClientInterface for upload/download tests.Configuration:
$canvaSecret in Laravel’s .env:
CANVA_SECRET=your_secret_here
AppServiceProvider:
$this->app->bind(PublishController::class, fn() => new PublishController(config('services.canva.secret')));
Extensions Beyond Publish:
Canva\Design\* classes (e.g., DesignRequest).Deprecation:
How can I help you explore Laravel packages today?