All things Shopify and commerce
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&X-Goog-Credential=merchant-assets%40shopify-tiers.iam.gserviceaccount.com%2F20250205%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=xxxx205T071539Z&X-Goog-Expires=604800&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!
June brought summer energy to our community. Members jumped in with solutions, clicked ...
By JasonH Jun 5, 2025Learn how to build powerful custom workflows in Shopify Flow with expert guidance from ...
By Jacqui May 7, 2025Did You Know? May is named after Maia, the Roman goddess of growth and flourishing! ...
By JasonH May 2, 2025