DocumentationCommerceVideos and Tutorials

Create a downloadable product

Last update: May 9, 2024
  • Topics:
  • Catalog Management
  • Admin Workspace
  • Backend Development
  • Integration
  • REST

CREATED FOR:

  • Beginner
  • Developer
  • User

Learn how to create a downloadable product using the REST API and the Adobe Commerce Admin.

Who is this video for?

  • Website managers
  • eCommerce merchandisers
  • New Adobe Commerce developers who want to learn how to create products in Adobe Commerce using the REST API

Video content

Transcript
Creating a downloadable product for Adobe Commerce. This is the only product that does take a little extra time to set up. The reasoning is, is if you’re using a URL for a downloadable link, we wanna make sure that it is in a list of acceptable URLs that are defined in the app, etsyenv.php, just to make sure that for security reasons, you know, the expected domain is one that you want, and it’s not like an accidental or a typo in the admin. And you may be wondering what I’m talking about, so let’s just give it a quick demo and I’ll show you what I mean. So in this particular instance, I do not have anything defined in the etsyenv.php for additional URLs for downloadable products. So let’s just do a cat on app etsyenv.php. And if you’ll notice under my downloadable domains, I just have localhost. So what that means is if I’m in my Adobe Commerce instance and I try to create a downloadable product, and I try to create a downloadable product, and then the downloadable item is a URL. So let me just go ahead and get it started here. My admin created downloadable product. And I go down here to my link and I try to create a link, and I make this a URL. And I try to hit save. I will get a nice little message saying that the URL I’m trying to use is not configured in our env.php. The good thing is, is that it’s really easy to add it. So let me show you what that looks like. Here we go. So all you’re gonna do is run a command, bin magento, and then downloadable colon domains colon add. And then after that, just the domain that you want to add for an acceptable domain list. So we’re just gonna go ahead and hit enter. And now when we do our cat on env.php, you’ll notice that in our downloadable domains, we now have a new entry. So when we go back to our product and we go ahead and try to add another link, change this to a URL, and now when we hit save, our product is saved correctly. Let’s go ahead and show you what this looks like using an API. So we’re gonna go ahead and open up Postman, and we’re gonna do basically the exact same thing, but we’re gonna use an API instead of the commerce admin. So here I have the four, there’s only a couple of required attributes. I do believe there’s just four of them. One is the SKU, one is the name, one you definitely need a price, and then it is optional, but it seems counterintuitive. You don’t actually need the downloadable link created to create a downloadable product, but it’s not required. But for our use case, we’ll just consider it required. And you’ll notice here that the link URL is defined, and this is once again, just something that you’ll need to add. So I’ll just make sure that this is something a little special, just in case that’s the exact same name I used on my previous example. And I just wanna clarify that how you connect to your Adobe commerce instance is up to you. We do recommend using OAuth, but for this example, I am using just a bearer token. But once again, the recommendation is that you use OAuth or perhaps an admin user’s credentials. So the end point we’re gonna hit, we’re gonna use a post method, and we’re gonna hit the rest default V1 products. And then this is what our payload looks like. Once again, it’s only has a couple of required fields. So we’ll go ahead and hit send. And that looks good. We got our response back and a new ID number. So that means everything is going well. The other type of product that we have is still downloadable, but instead of it being a defined URL, it’s going to be a downloadable product that is actually physically somewhere on the server. The biggest difference between this one and the previous example is that the path that is defined when you’re doing this via API, you do have to know the nuance for how Adobe commerce takes your file that you’re uploading and then creates a couple of folders for it before it finds its final location. And we’ll go through that here in a minute. But we’re just gonna show you the admin version because this will give you a nice visual representation of the overall process. So now we’re gonna do a new product via the admin, and we’ll just give it some sort of price. We’ll go ahead and create our link down here. All right, and we’re gonna do an upload file. So we’ll just go ahead and we’ll just grab some zip file that I have right here. And what it’s doing now is it’s creating a temporary spot for it and it’s just double checking to see if certain validations are met before it allow it to be saved. So it looks like the file is legit. It’s happy with it. We have our couple of required fields. So we just go ahead and hit save. And it looks like our product was saved correctly. So I just wanna call out that the file name that we uploaded started with the two letters CO. And this is where the Adobe Commerce application and how it stores these files is important, especially when we talk about the API method for doing this. So in the Commerce application, when you upload a file, specifically for these downloadable products, inside of, let’s see here, Pub Media Downloadable, there’s a couple of new folders called files. So we’re gonna dig into that. And then inside of that, there’s a new file called links. So we’re gonna get into that. Okay, and so remember I said that the file that we just uploaded started with the letter C. So you’ll see there’s a new folder here called C. So let’s just dig into C. And once again, the second letter of our file was O. So you’ll see that there’s a new directory O. So we’ll just CD into that. And now you’ll see this is where our file was eventually stored. So the reason I bring this up is that the path to this file, when you do it via API, you have to know that it’s relative to this parent folder under Pub Media Downloads Files Links, the path that we define in our API starts with this first letter of the expected file. The other nuances, if you’re gonna do this via API, it is recommended that you go ahead and get these files in place first and make sure that you generate the proper folders. So that way, when you go to retrieve them, everything does line up the way you expect it. So for this case, if I was gonna do this via integration, I would have my integration go ahead and create the directory CNO for me, and then using FTP or R sync or whatever, then move the actual file to this final destination. Then when we get to our actual API that does this, when we go to define the link file, we start with the slash and then the file name. So my example here is C slash O and then commerce integration, blah, blah, blah. The other parts of this are basically identical. You need a SKU, you need a name, you need a price. And then from that point on, if you’re gonna do a downloadable link at the same time, this is where you define it. And once again, the only extra care that I would say is just make sure that this is path correctly and that the file exists. Otherwise you might run into some issues. So if we go ahead and hit send for this, okay, once again, I said that I’m looking for a file called C slash O and then commerce dash D introduction MD files dot zip, but I don’t know if you caught this, but the file that I actually uploaded was something completely different. So let me go ahead and I’m just going to make this file. Just copy it to make sure I don’t have any typos. Okay, so now if we do an LS, we’ll see our two files. So now if we go back to Postman, there we go, our product was saved as expected.

Allowed downloadable domains

You must specify which domains are permitted to allow downloads. Domains are added to the project’s env.php file via the command line. The env.php file details the domains allowed to contain downloadable content. An error occurs if a downloadable product is created using the REST API before the php.env file is updated:

{
    "message": "Link URL's domain is not in list of downloadable_domains in env.php."
}

To set the domain, connect to the server: bin/magento downloadable:domains:add www.example.com

Once that is complete, the env.php is modified inside the downloadable_domains array.

    'downloadable_domains' => [
        'www.example.com'
    ],

Now that the domain is added to the env.php, you can create a downloadable product in the Adobe Commerce Admin or by using the REST API.

See Configuration Reference to learn more.

IMPORTANT
On some versions of Adobe Commerce, you might get the following error when a product is edited in the Adobe Commerce Admin. The product is created using the REST API but the linked download has a null price.

Deprecated Functionality: number_format(): Passing null to parameter #1 ($num) of type float is deprecated in /app/vendor/magento/module-downloadable/Ui/DataProvider/Product/Form/Modifier/Data/Links.php on line 228.

To fix this error, use the update link API: POST V1/products/{sku}/downloadable-links.

See the Update a product download link using cURL section for more info.

Create a downloadable product using cURL (download from remote server)

This example shows how to create a downloadable product using cURL when the file to download is not on the same server. This use case is typical if the file is stored in an S3 bucket or other digital asset manager.

curl --location '{{your.url.here}}/rest/default/V1/products' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=b78cae2338f12d846d233d4e9486607e; private_content_version=564dde2976849891583a9a649073f01' \
--data '{
  "product": {
    "sku": "POSTMAN-download-product-1",
    "name": "POSTMAN download product-1",
    "attribute_set_id": 4,
    "price": 9.9,
    "status": 1,
    "visibility": 4,
    "type_id": "downloadable",
    "extension_attributes": {
        "website_ids": [
            1
        ],

        "downloadable_product_links": [
            {
                "title": "My url link",
                "sort_order": 1,
                "is_shareable": 1,
                "price": 0,
                "number_of_downloads": 0,
                "link_type": "url",
                "link_url": "{{location.url.where.file.exists}}/some-file.zip",
                "sample_type": "url",
                "sample_url": "{{location.url.where.file.exists}}/sample-file.zip"
            }
        ],
        "downloadable_product_samples": []
    },
    "product_links": [],
    "options": [],
    "media_gallery_entries": [],
    "tier_prices": []
  }
}
'

Create a downloadable product using cURL (download from Commerce application server)

This example demonstrates how to use cURL to create a downloadable product from the Adobe Commerce Admin when the file is stored on the same server as the Adobe Commerce application.

In this use case, when the administrator managing the catalog chooses upload file, the file is transferred to the pub/media/downloadable/files/links/ directory. Automation creates and moves the files to their respective locations based on the following pattern:

  • Each uploaded file is stored in a folder based on the first two characters of the file name.
  • When the upload is initiated, the Commerce application creates or uses existing folders to transfer the file.
  • When downloading the file, the link_file section of the path uses the portion of the path appended to the pub/media/downloadable/files/links/ directory.

For example, if the uploaded file is named download-example.zip:

  • The file is uploaded to the path pub/media/downloadable/files/links/d/o/.
    The subdirectories /d and /d/o are created if they do not exist.

  • The final path to the file is /pub/media/downloadable/files/links/d/o/download-example.zip.

  • The link_url value for this example is d/o/download-example.zip

curl --location '{{your.url.here}}/rest/default/V1/products' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=571f5ebe48857569cd56bde5d65f83a2; private_content_version=9f3286b427812be6aec0b04cae33ba35' \
--data '{
  "product": {
    "sku": "sample-local-download-file",
    "name": "A downloadable product with locally hosted download file",
    "attribute_set_id": 4,
    "price": 33,
    "status": 1,
    "visibility": 4,
    "type_id": "downloadable",
    "extension_attributes": {
        "website_ids": [
            1
        ],
        "downloadable_product_links": [
            {
                "title": "an api version of already upload file",
                "sort_order": 1,
                "is_shareable": 1,
                "price": 0,
                "number_of_downloads": 0,
                "link_type": "file",
                "link_file": "/d/o/downloadable-file-demo-file-upload.zip",
                "sample_type": null
            }
        ],
        "downloadable_product_samples": []
    },
    "product_links": [],
    "options": [],
    "media_gallery_entries": [],
    "tier_prices": []
  }
}'

Get a product using curl

curl --location '{{your.url.here}}/rest/default/V1/products/POSTMAN-download-product-1' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=b78cae2338f12d846d233d4e9486607e; private_content_version=564dde2976849891583a9a649073f01e'

Update the product using Postman

Use the endpoint rest/all/V1/products/{sku}/downloadable-links
The SKU is the product ID that was generated when the product was created. For example in the code sample below, it is the number 39, but make sure it is updated to use the ID from your website. This updates the links for the downloadable products.

{
  "link": {
    "id": 39,
    "title": "My swagger update",
    "sort_order": 0,
    "is_shareable": 0,
    "price": 0,
    "number_of_downloads": 0,
    "link_type": "url",
    "link_file": "{{your.url.here}}/some-file.zip",
    "link_url": "{{your.url.here}}/some-file.zip",
    "link_file_content": {
      "file_data": "{{your.url.here}}/some-file.zip",
      "extension_attributes": {}
    }
  },
  "isGlobalScopeContent": true
}

Update a product download link using CURL

When you update a product download link using cURL, the URL includes the SKU for the product that is being updated. In the following code example, the SKU is abcd12345. When you submit the command, change value to match the SKU for the product you want to update.

curl --location '{{your.url.here}}/rest/all/V1/products/abcd12345/downloadable-links' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {{Your Bearer Token}}' \
--header 'Cookie: PHPSESSID=fa5d76f4568982adf369f758e8cb9544; private_content_version=564dde2976849891583a9a649073f01e' \
--data '{
  "link": {
    "id": {{The ID of the download link for example 39}},
    "title": "My update",
    "sort_order": 0,
    "is_shareable": 0,
    "price": 0,
    "number_of_downloads": 0,
    "link_type": "url",
    "link_file": "{{your.url.here}}/some-file.zip",
    "link_url": "{{your.url.here}}/some-file.zip",
    "link_file_content": {
      "file_data": "{{your.url.here}}/some-file.zip",
      "extension_attributes": {}
    }
  },
  "isGlobalScopeContent": true
}'

Additional resources

  • Downloadable Product Type
  • Downloadable Domains Configuration Guide
  • Adobe Developer REST tutorials
  • Adobe Commerce REST ReDoc
recommendation-more-help
3a5f7e19-f383-4af8-8983-d01154c1402f