Generate CloudFlare R2 Authorization Header to upload image via API by curl php

Thanks everyone for your support.

I have a php project that creates images from AI API, then uploads images to R2 to store and get image links from my domain. I’m having problems creating AH for curl to upload images to R2, here is my function:

function createS3AuthorizationHeader($accessKey, $secretKey, $bucket, $objectKey, $region, $contentType) {
        $service = 's3';
        $httpMethod = 'PUT';
        date_default_timezone_set('UTC'); // Ensure we use UTC time zone

        $now = time();
        $amzDate = gmdate('Ymd\THis\Z', $now);

        // Extract date components from amzDate
        $dateStamp = substr($amzDate, 0, 8);

        Log::debug('AMZDate: '. $amzDate);
        Log::debug('DateStamp: '. $dateStamp);

        // Canonical URI
        $canonicalUri = "/{$bucket}/{$objectKey}";

        // Canonical Query String (empty for Cloudflare)
        $canonicalQueryString = '';

        // Canonical Headers
        $canonicalHeaders = "content-type:$contentType\nhost:{$bucket}.{$region}\nx-amz-content-sha256:UNSIGNED-PAYLOAD\nx-amz-date:$amzDate\n";

        // Signed headers
        $signedHeaders = 'content-type;host;x-amz-content-sha256;x-amz-date';

        // Create canonical request
        $canonicalRequest = "$httpMethod\n$canonicalUri\n$canonicalQueryString\n$canonicalHeaders\n$signedHeaders\nUNSIGNED-PAYLOAD";

        // Hash canonical request with SHA256
        $canonicalRequestHash = hash('sha256', $canonicalRequest);

        // Create string to sign
        $stringToSign = "AWS4-HMAC-SHA256\n$amzDate\n$dateStamp/{$region}/r2/cloudflarestorage/aws4_request\n$canonicalRequestHash";

        // Create signing key
        $kDate = hash_hmac('sha256', $dateStamp, 'AWS4' . $secretKey, true);
        $kRegion = hash_hmac('sha256', $region, $kDate, true);
        $kService = hash_hmac('sha256', 'r2', $kRegion, true);
        $kSigning = hash_hmac('sha256', 'aws4_request', $kService, true);

        // Calculate signature
        $signature = hash_hmac('sha256', $stringToSign, $kSigning);

        // Create Authorization header
        $authorizationHeader = "AWS4-HMAC-SHA256 Credential={$accessKey}/{$dateStamp}/{$region}/r2/cloudflarestorage/aws4_request, SignedHeaders={$signedHeaders}, Signature={$signature}";

        // Return headers
        return array(
            'x-amz-content-sha256: UNSIGNED-PAYLOAD',
            'Content-Type: '. $contentType,
            'x-amz-date: '. $amzDate,
            'Authorization: '. $authorizationHeader

And here is the sample data that I logged:

AMZDate: 20240501T014516Z
DateStamp: 20240501  
String to sign:

Even though the data matches, I still get the error message:

“Invalid Argument: Credential signed date auto does not match 20240501 from x-amz-date header”