phpclassic / php-shopify

PHP SDK for Shopify API
Apache License 2.0
568 stars 211 forks source link

When trying to add a product, or update, the api returns error 400. Reading and deleting products works fine. #330

Open rich-a-potter opened 1 month ago

rich-a-potter commented 1 month ago

I am unable to update or add products via the shopify API. I can read products fine, and delete products, but updating or adding will not work.

This is logged CurlException: Request failed with HTTP Code 400.

And this is the output to the screen Fatal error: Uncaught PHPShopify\Exception\CurlException: Request failed with HTTP Code 400. in /Users/xxxredactedxxxxx/Sites/MCMS/vendor/phpclassic/php-shopify/lib/HttpRequestJson.php:209

When called from the controller, the model class constructor sets up the connection

public function __construct()
{
    $config = array(
        "ShopUrl" => $_ENV["SHOPIFY_SHOP_URL"],
        "AccessToken" => $_ENV["SHOPIFY_ACCESS_TOKEN"],
        "Content_type" => "application/json",
        "Curl" => array(
            CURLOPT_TIMEOUT => 10,
            CURLOPT_FOLLOWLOCATION => true
        )
    );

    $this->shopify = new ShopifySDK($config);

}

and then the controller calls an update method

public function apiUpdateProduct($id, $data) : void
{
    $this->validate($data);

    if(!empty($this->errors)){
        return;
    };

    $data = json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);

    try {
        $response = $this->shopify->Product($id)->put($data);
    } catch (CurlException $e) {
        $this->addError("product", $e->getMessage());
        error_log("CurlException: " . $e->getMessage());
    } catch (\Exception $e) {
        $this->addError("product", $e->getMessage());
        error_log("Exception: " . $e->getMessage());
    }

}
tareqtms commented 1 month ago

@rich-a-potter 400 means, you have something wrong with your data. Can you show the code where $data is defined?

rich-a-potter commented 1 month ago

sure. Data is submitted by POST to the products controller which then assembles the data array and then passes it to the product model. Here's the create method in the products controller which is called by the action of the form

public function create() : Response
{
    $data = [
        "title" => $this->request->post["title"],
        "body_html" => empty($this->request->post["body_html"]) ? null : $this->request->post["body_html"],
    ];

    if($this->model->apiAddProduct($data)) {
        return $this->redirect("/products/");
    } else {
        return $this->view("Products/new.twig", [
            "errors" => $this->model->getErrors(),
            "product" => $data
        ]);
    }

}

and here's the data dumped to the screen before the try-catch statement

array(3) { ["id"]=> string(13) "9163066540297" ["title"]=> string(64) "3 Legged Thing Alfie L-bracket for Sony Alpha A7 IV - Slate Grey" ["body_html"]=> string(1414) " This dedicated L-bracket has been designed to perfectly fit the Sony Alpha A7 IV camera, and will also work with the A7R V, A7 III, A7R III, A7R IV, A7S II, A7S III, A9 II and A1 camera bodies.

Alfie is a standard 38mm Arca-Swiss compatible L-bracket and is made from aerospace-grade magnesium alloy. This L-bracket gives users access to the battery door and side ports on all cameras. The cut-out in the vertical aspect allows rear screens to be opened either upright or tilted, and Alfie also has two 1/4"-20 threads for attachment of accessories.

The base extends to enable tethering in portrait orientation and there will be a cable management tool included in the box to help support cables when plugged into the side of the camera. 3 Legged Thing have supplied a Peak Design v3 Capture compatible adaptor in the box to allow users to use Alfie with their v3 Capture Clip.

What's In The Box? Alfie Dedicated Sony Alpha L-bracket.

Self-contained screws for extending base.

ToolzTwo - multitool with two hex keys, bottle opener, and coin turn. Attaches magnetically.

Stagsden Stainless Steel 1/4"-20 camera screw.

Cable management tool.

Peak Design Capture-compatible adapter plate with longer 1/4"-20 pass-thru camera screw.

" }

tareqtms commented 1 month ago

I don't see from where you calling the function apiUpdateProduct()

rich-a-potter commented 1 month ago

sorry my bad, that's the create product one (which causes the same error) this is the update product one

public function update(string $id) : Response
{
    // build the product data array for submission
    $product["id"] = $id;
    $product["title"] = $this->request->post["title"];
    $product["body_html"] = $this->request->post["body_html"];

    $this->model->apiUpdateProduct($id, $product);

    if(empty($this->model->getErrors())) {
        return $this->redirect("/products/{$id}/show");
    } else {
        return $this->view("Products/edit.twig", [
            "errors" => $this->model->getErrors(),
            "product" => $product
        ]);
    }
}
tareqtms commented 1 month ago

Don't provide the id in the data array, it's in the other argument already.

rich-a-potter commented 1 month ago

I get the same error with or without the id in the array

here is the response header from the log

[06-Aug-2024 14:57:52 Europe/London] CurlException: Request failed with HTTP Code 400. [06-Aug-2024 14:57:52 Europe/London] Response Headers: Array ( [0] => HTTP/2 301

[1] => date: Tue, 06 Aug 2024 13:57:52 GMT

[2] => content-type: text/html; charset=utf-8

[3] => location: https://5c5c22-eb.myshopify.com/admin/api/2024-07/products/9162672472329.json

[4] => x-sorting-hat-podid: 264

[5] => x-sorting-hat-shopid: 72793489673

[6] => referrer-policy: origin-when-cross-origin

[7] => x-frame-options: DENY

[8] => x-shopid: 72793489673

[9] => x-shardid: 264

[10] => strict-transport-security: max-age=7889238

[11] => set-cookie: request_method=PUT; path=/; SameSite=Lax

[12] => x-request-id: ebf79270-0277-4a60-a2f6-fe88961dbdb7-1722952672

[13] => server-timing: processing;dur=20

[14] => content-security-policy: default-src 'self' data: blob: 'unsafe-inline' 'unsafe-eval' https://* shopify-pos://*; block-all-mixed-content; child-src 'self' https://* shopify-pos://*; connect-src 'self' wss://* https://*; frame-ancestors 'none'; img-src 'self' data: blob: https:; script-src https://cdn.shopify.com https://cdn.shopifycdn.net https://checkout.shopifycs.com https://api.stripe.com https://mpsnare.iesnare.com https://appcenter.intuit.com https://www.paypal.com https://js.braintreegateway.com https://c.paypal.com https://maps.googleapis.com https://www.google-analytics.com https://v.shopify.com 'self' 'unsafe-inline' 'unsafe-eval'; upgrade-insecure-requests; report-uri /csp-report?source%5Baction%5D=update&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Fproducts&source%5Bsection%5D=admin_api&source%5Buuid%5D=ebf79270-0277-4a60-a2f6-fe88961dbdb7-1722952672

[15] => x-content-type-options: nosniff

[16] => x-download-options: noopen

[17] => x-permitted-cross-domain-policies: none

[18] => x-xss-protection: 1; mode=block; report=/xss-report?source%5Baction%5D=update&source%5Bapp%5D=Shopify&source%5Bcontroller%5D=admin%2Fproducts&source%5Bsection%5D=admin_api&source%5Buuid%5D=ebf79270-0277-4a60-a2f6-fe88961dbdb7-1722952672

[19] => x-dc: gcp-europe-west2,gcp-europe-west3,gcp-europe-west3

[20] => cf-cache-status: DYNAMIC

[21] => report-to: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=5eIsGny84yB4VBIyGFFZXCb7a3rrhRlgMoy8mCFtrome9p84RS7c9nAyihGQlQA5kpPee8iN04KyRYm3EOcnzGQu2YUgGXHIAEi0Objcqj6albsMaHNXJ8JKTlV5gfHXIEjGCnkcJ1qk9GhsoHn4CgsZwGS8A1VeAWFg"}],"group":"cf-nel","max_age":604800}

[22] => nel: {"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}

[23] => server-timing: cfRequestDuration;dur=372.999907

[24] => server-timing: ipv6

[25] => server: cloudflare

[26] => cf-ray: 8aef89d8eb30b125-MAN

[27] => alt-svc: h3=":443"; ma=86400

[28] => 

[29] => HTTP/2 400 

[30] => server: cloudflare

[31] => date: Tue, 06 Aug 2024 13:57:52 GMT

[32] => content-type: text/html

[33] => content-length: 155

[34] => cf-ray: -

[35] => 

)

tareqtms commented 1 month ago

Do you have proper permissions set for the Products resources? I think you should better try the API calls directly with Postman or something and debug the request and response there first. We are only a PHP SDK which only sends request and receives response from Shopify. If your request doesn't work with Postman also, I think you can contact Shopify support.

rich-a-potter commented 1 month ago

Yes, I have read and write products enabled in the app scope.

In postman, I have set up a PUT request to this URL https://harrisoncameras.myshopify.com/admin/api/2024-07/products/{9162672472329}.json

and put the token in the headers as X-Shopify-Access-Token

with this JSON body

{"title":"1x10 Verbatim BD-R Blu-Ray Disc 25GB 6x Speed DL Wide Printable Cakebox","body_html":"\u003Cp\u003E10x BD-R blu ray discs in a spindle cakebox from Verbatim. High capacity storage discs, record and playback HD videos. Hardcoat Scratch Guard Protection against scratches, fingerprints, dust, oil and water. Wide Ink Jet Printable surface\u003C\/p\u003E"}

and it returns this

{ "errors": { "id": "expected String to be a id" } }

rich-a-potter commented 1 month ago

ok I dropped the {} from around the product id in the URL and it sent without error, but the data has not changed.