SignatureDoesNotMatch when trying to upload image to shopify using laravel

SignatureDoesNotMatch when trying to upload image to shopify using laravel

ropiaja
Shopify Partner
3 0 0

Hi,

I'm trying to create an app using Laravel and Shopify to upload images to Shopify's CDN. However, when attempting to upload an image to the stagedUploadsCreate URL, I encountered the following error:


<?xml version='1.0' encoding='UTF-8'?><Error><Code>SignatureDoesNotMatch<\/Code><Message>Access denied.<\/Message><Details>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.<\/Details><StringToSign>GOOG4-RSA-SHA256\n20xxxx9Z\n20250205\/auto\/storage\/goog4_request\nc162xxx36ed1ef6xxxxxxx591a091a331cxxxx38<\/StringToSign><CanonicalRequest>POST\n\/tmp\/xxxx\/files\/10b5xxx05-42f8-a55b-a0xxxd8\/sanmol.jpg\nX-Goog-Algorithm=GOOG4-RSA-SHA256&amp;X-Goog-Credential=merchant-assets%40shopify-tiers.iam.gserviceaccount.com%2F20250205%2Fauto%2Fstorage%2Fgoog4_request&amp;X-Goog-Date=xxxx205T071539Z&amp;X-Goog-Expires=604800&amp;X-Goog-SignedHeaders=host\nhost:shopify-staged-uploads.storage.googleapis.com\n\nhost\nUNSIGNED-PAYLOAD</CanonicalRequest></Error>
Here is my current implementation:

 

 

 

 

 

Route::post('/upload-to-shopify', function (Request $request) {
    $file = $request->file('file');
    if (!$file) {
        return response()->json(['error' => 'File tidak ditemukan'], 400);
    }

    // Ambil informasi file
    $fileName = $file->getClientOriginalName();
    $fileSize = (string) $file->getSize();
    $mimeType = $file->getMimeType();

    // Shopify API Credentials
    $shopifyStore = env('SHOPIFY_STORE');
    $shopifyUrl = "https://$shopifyStore.myshopify.com/admin/api/2024-01/graphql.json";
    $accessToken = env('SHOPIFY_ACCESS_TOKEN');

    // Query GraphQL untuk mendapatkan staged upload URL
    $query = [
        'query' => 'mutation stagedUploadsCreate($fileSize: UnsignedInt64!, $fileName: String!, $mimeType: String!) {
            stagedUploadsCreate(input: {fileSize: $fileSize, filename: $fileName, mimeType: $mimeType, resource: FILE}) {
                stagedTargets {
                    url
                    parameters {
                        name
                        value
                    }
                }
            }
        }',
        'variables' => [
            'fileSize' => $fileSize,
            'fileName' => $fileName,
            'mimeType' => $mimeType
        ]
    ];

    $response = Http::withHeaders([
        'X-Shopify-Access-Token' => $accessToken,
        'Content-Type' => 'application/json',
    ])->post($shopifyUrl, $query);

    if ($response->failed()) {
        Log::error('Shopify API Error:', ['status' => $response->status(), 'body' => $response->body()]);
        return response()->json(['error' => 'Gagal mendapatkan staged upload URL dari Shopify'], 500);
    }

    $responseData = $response->json();
    Log::info('Shopify Response:', $responseData);

    if (empty($responseData['data']['stagedUploadsCreate']['stagedTargets'])) {
        return response()->json(['error' => 'Staged targets kosong atau tidak valid'], 500);
    }

    $stagedTarget = $responseData['data']['stagedUploadsCreate']['stagedTargets'][0];
    $uploadUrl = $stagedTarget['url'];
    $parameters = $stagedTarget['parameters'];
    Log::info('upload url'.$uploadUrl);

    // multipart/form-data
    $multipart = [];

    // Tambahkan parameter dari Shopify ke form-data
    foreach ($parameters as $param) {
        $multipart[] = [
            'name' => $param['name'],
            'contents' => $param['value']
        ];
    }

    // Tambahkan file ke form-data
    $multipart[] = [
        'name' => 'file',
        'contents' => fopen($file->getPathname(), 'r'),
        'filename' => $fileName,
    ];

    Log::info('Upload Parameters:', $parameters);


    // Kirim request ke Google Cloud Storage yang diberikan Shopify
    $uploadResponse = Http::asMultipart()->post($uploadUrl, $multipart);

    Log::info('Upload Response Status:', ['status' => $uploadResponse->status()]);

    Log::info('Upload Response:', ['body' => $uploadResponse->body()]);

    if ($uploadResponse->failed()) {
        Log::error('Upload ke staged URL gagal', ['body' => $uploadResponse->body()]);
        return response()->json(['error' => 'Gagal mengunggah ke Shopify CDN', 'response' => $uploadResponse->body()], 500);
    }

    // Simpan file ke Shopify Files
    $mutation = [
        'query' => 'mutation fileCreate($fileName: String!, $uploadUrl: String!) {
            fileCreate(files: [{ alt: $fileName, contentType: "IMAGE", originalSource: $uploadUrl }]) {
                files {
                    url
                }
            }
        }',
        'variables' => [
            'fileName' => $fileName,
            'uploadUrl' => $uploadUrl
        ]
    ];

    $createFileResponse = Http::withHeaders([
        'X-Shopify-Access-Token' => $accessToken,
        'Content-Type' => 'application/json',
    ])->post($shopifyUrl, $mutation);

    if ($createFileResponse->failed()) {
        Log::error('Gagal menyimpan file ke Shopify', ['body' => $createFileResponse->body()]);
        return response()->json(['error' => 'Gagal menyimpan file ke Shopify'], 500);
    }
    Log::info('Upload createFileResponse:', ['body' => $createFileResponse->body()]);


    $fileUrl = $createFileResponse->json()['data']['fileCreate']['files'][0]['url'] ?? null;
    if (!$fileUrl) {
        return response()->json(['error' => 'Gagal mendapatkan URL file dari Shopify'], 500);
    }

    return response()->json(['fileUrl' => $fileUrl]);
});

 

 

 

 

 

Am I missing any steps in this implementation? I would appreciate any insights or suggestions to resolve this issue.

Thank you in advance!

Replies 0 (0)