Conversations about creating, managing, and using metafields to store and retrieve custom data for apps and themes.
Hello.
We've been attempting to make a PDF upload to shopify thanks to the documentation and this thread
The final goal is to attach these PDF files to customer orders later down the line via metafields.
With it, we've succeeded to get the staged upload site, however, any attempt at this point yields errors that we cannot find responses to.
We are currently working strictly on Postman to understand the request flow we seek
Our current progress is:
POST https://{{Environment}}.myshopify.com/admin/api/2024-01/graphql.json (Request Id = 1f80e45d-deb0-4b6f-980a-b2fb58d4a5cf)
Yields 200 with body:
{ "data": { "stagedUploadsCreate": { "stagedTargets": [ { "parameters": [ { "name": "Content-Type", "value": "application/pdf" }, { "name": "success_action_status", "value": "201" }, { "name": "acl", "value": "private" }, { "name": "key", "value": "tmp/(numbers)/files/(Guid)/F1234.pdf" }, { "name": "x-goog-date", "value": "20240125T093520Z" }, { "name": "x-goog-credential", "value": "merchant-assets@shopify-tiers.iam.gserviceaccount.com/20240125/auto/storage/goog4_request" }, { "name": "x-goog-algorithm", "value": "GOOG4-RSA-SHA256" }, { "name": "x-goog-signature", "value": "(long string of chars)" }, { "name": "policy", "value": "eyJjb25kaXRpb25zIjpbeyJDb250ZW50LVR5cGUiOiJhcHBsaWNhdGlvblwvcGRmIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSxbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwxLDIwOTcxNTIwXSx7ImJ1Y2tldCI6InNob3BpZnktc3RhZ2VkLXVwbG9hZHMifSx7ImtleSI6InRtcFwvNzY1Mzk0OTQ3MDBcL2ZpbGVzXC8yZDM0NzA5Ny00OWM0LTRjMDItYjU0Mi1kN2Q4NDA1OWM0MWZcL0YxMjM0LnBkZiJ9LHsieC1nb29nLWRhdGUiOiIyMDI0MDEyNVQwOTM1MjBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6Im1lcmNoYW50LWFzc2V0c0BzaG9waWZ5LXRpZXJzLmlhbS5nc2VydmljZWFjY291bnQuY29tXC8yMDI0MDEyNVwvYXV0b1wvc3RvcmFnZVwvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDI0LTAxLTI2VDA5OjM1OjIwWiJ9" } ], "url": "https://shopify-staged-uploads.storage.googleapis.com/", "resourceUrl": "https://shopify-staged-uploads.storage.googleapis.com/tmp/(numbers)/files/(Guid)/F1234.pdf" } ] } }, "extensions": { "cost": { "requestedQueryCost": 11, "actualQueryCost": 11, "throttleStatus": { "maximumAvailable": 2000.0, "currentlyAvailable": 1989, "restoreRate": 100.0 } } } }
However, when it comes to sending the file itself
both
PUT https://shopify-staged-uploads.storage.googleapis.com
and
PUT https://shopify-staged-uploads.storage.googleapis.com/tmp/(numbers)/files/(Guid)/F1234.pdf
with parameters (copied from Postman's bulk edit):
Content-Type:application/pdf success_action_status:201 acl:private key:tmp/(numbers)/files/(Guid)/F1234.pdf x-goog-date:20240125T093520Z x-goog-credential:merchant-assets@shopify-tiers.iam.gserviceaccount.com/20240125/auto/storage/goog4_request x-goog-algorithm:GOOG4-RSA-SHA256 x-goog-signature: (long string of chars) policy:eyJjb25kaXRpb25zIjpbeyJDb250ZW50LVR5cGUiOiJhcHBsaWNhdGlvblwvcGRmIn0seyJzdWNjZXNzX2FjdGlvbl9zdGF0dXMiOiIyMDEifSx7ImFjbCI6InByaXZhdGUifSxbImNvbnRlbnQtbGVuZ3RoLXJhbmdlIiwxLDIwOTcxNTIwXSx7ImJ1Y2tldCI6InNob3BpZnktc3RhZ2VkLXVwbG9hZHMifSx7ImtleSI6InRtcFwvNzY1Mzk0OTQ3MDBcL2ZpbGVzXC8yZDM0NzA5Ny00OWM0LTRjMDItYjU0Mi1kN2Q4NDA1OWM0MWZcL0YxMjM0LnBkZiJ9LHsieC1nb29nLWRhdGUiOiIyMDI0MDEyNVQwOTM1MjBaIn0seyJ4LWdvb2ctY3JlZGVudGlhbCI6Im1lcmNoYW50LWFzc2V0c0BzaG9waWZ5LXRpZXJzLmlhbS5nc2VydmljZWFjY291bnQuY29tXC8yMDI0MDEyNVwvYXV0b1wvc3RvcmFnZVwvZ29vZzRfcmVxdWVzdCJ9LHsieC1nb29nLWFsZ29yaXRobSI6IkdPT0c0LVJTQS1TSEEyNTYifV0sImV4cGlyYXRpb24iOiIyMDI0LTAxLTI2VDA5OjM1OjIwWiJ9
and a PDF file, in binary, as the body
yields 400:
<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>MissingSecurityHeader</Code>
<Message>Invalid argument.</Message>
<Details>Your request was missing a required header. Missing headers: x-goog-expires</Details>
</Error>
So, we've attempted to add headers as it requests (x-goog-expires:4800 and x-goog-signedHeaders:host), but we are then met with
<?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
20240125T093520Z
20240125/auto/storage/goog4_request
dd7020393f65ba8691d0bbbcd3c7caf5b2fc5f29c959e426ad21b9adc057911c</StringToSign>
<CanonicalRequest>PUT
(the path and params are pasted here)
host
UNSIGNED-PAYLOAD</CanonicalRequest>
</Error>
Any help would be welcome!
Hi Killian- the MissingSecurityHeader
error suggests that the required x-goog-expires
header was not included in your PUT request. This header is necessary for Google Cloud Storage to know how long the signed URL should be valid. You mentioned adding x-goog-expires:4800
, which should theoretically solve this issue. Ensure this header is correctly formatted and included in your request. The SignatureDoesNotMatch
error is more complex. It indicates that the signature you're providing doesn't match what Google Cloud Storage expects. This is often due to:
Incorrect Signing Method: Make sure you're using the correct method for signing your request. It needs to match with what you've specified in the x-goog-algorithm
header.
Incorrect Secret Key: Verify that the secret key you're using is correct and has the necessary permissions.
String To Sign Mismatch: The string you're using to generate the signature might not match what Google Cloud expects. Double-check that you're including all the necessary components in the correct order: HTTP method, URL path, query parameters, headers, and payload hash.
Time Sync Issue: Ensure that your system's clock is synchronized with NTP. A time mismatch can lead to signature mismatches.
Hope this helps,
Liam | Developer Advocate @ Shopify
- Was my reply helpful? Click Like to let me know!
- Was your question answered? Mark it as an Accepted Solution
- To learn more visit Shopify.dev or the Shopify Web Design and Development Blog
Hi,
We are trying to upload a file to store. But Response of stagedUploadsCreate only keys 2 parameters as content_type and acl. NO google headers.
And the url given in response , wehn we try it , gives 403 Forbidden.
Below is our grpahql request:
mutation generateStagedUploads {
stagedUploadsCreate(input: [
{
filename: "tryon.png",
mimeType: "image/png",
resource: IMAGE
}
])
{
stagedTargets {
url
resourceUrl
parameters {
name
value
}
}
userErrors {
field, message
}
}
}