Build a Mobile App to Sell Products with Drupal

Category: 

This tutorial describes how to create a website and mobile application to sell physical products. Customers on a desktop or laptop computer will be able to purchase the product through the website, much like a typical e-commerce store.

Once we have built the mobile application, customers who have downloaded and installed the mobile app onto their Android or iOS (iPhone, iPad) device will be able to purchase the product as an In-App Purchase.

For this example website and mobile app, we're going to sell bottles of beer. *Cough* - Please don't actually sell beer without first getting permission from your local Big Brother.

The main 3 sets of tools we will utilize are:

This tutorial was inspired by this Session from DrupalCon Austin 2014. If you're new to any of these tools mentioned above, please watch the video for an introduction. Otherwise, let's get started!

1. Download and Install Drupal 7

2. Download and Install the Commerce Module for Drupal

If you're new to Drupal Commerce, I'd recommend checking out Commerce KickStart for a nice introduction. Otherwise, the Commerce module comes packed with many sub modules. To get started, we'll want to make sure the following modules are enabled on our Drupal site:

  • Cart
  • Checkout
  • Commerce
  • Commerce UI
  • Customer
  • Customer UI
  • Line Item
  • Line Item UI
  • Order
  • Order UI
  • Payment
  • Payment UI
  • Price
  • Product
  • Product Pricing
  • Product Pricing UI
  • Product Reference
  • Product UI

Woah, that was a lot of modules. Pretty much enable them all except for the Tax modules, and don't tell the King about it.

2a. Give users permission to go through the Checkout process

  1. On the Drupal site, go to: admin/people/permissions
  2. Next to the "Access checkout" permission, check the box for Anonymous and Authenticated users
  3. Click the "Save permissions" button

3. Add a Product Type

Go to:

admin/commerce/products/types/add

With the following info:

  • Name: Beer
  • Description: Mmmm, beer.

Then click the "Save and add fields" button.

4. Add a "Size" Field to the Product type

As a bare minimum, we want our customers to be able to choose the size of their beer. So let's add a new field on our Product type to accommodate this:

admin/commerce/products/types/beer/fields

Under the "Add new field" section, enter the following info:

  • Label: Size
  • Field type: List (text)
  • Widget: Select list

Then click the "Save" button. Next up, in our "Allowed values list" for the field, let's copy/paste these values:

Pint
12 oz
22 oz
40 oz

Then click the "Save" button. Next, specify these values:

  • Label: Size
  • Required field: Check the box
  • Default value: Pint
  • Enable this field to function as an attribute field on Add to Cart forms: Check this box
  • Number of values: 1

Then click the "Save settings" button.

5. Add some Products

We're now ready to add some products to sell. In Drupal, go to:

admin/commerce/products/add/beer

Then enter the following values:

  • Product SKU: bells-oberon-12-oz
  • Title: Bells Oberon 12 oz
  • Price: $4.00
  • Size: 12 oz

Click the "Save and add another" button, then enter the following values:

  • Product SKU: bells-oberon-pint
  • Title: Bells Oberon Pint
  • Price: $5.00
  • Size: Pint

As you can see we've now created 2 products which allow us to sell a Bells Oberon beer in either a 12 oz or pint size.

Repeat this procedure as many times as you want to build your beer catalog. After a while, we'll have a list of beer like so:

For now, prohibition is tough on the Internet so we only have 5 products to sell, within 3 types of beer.

  • Bells Oberon
  • Shorts Brew Soft Parade
  • Sierra Nevada Torpedo Extra IPA

6. Add a Content Type to Display the Products

In Drupal, go to:

admin/structure/types/add

Enter Beer as the Name, then click the "Save and add fields" button.

6a. Add a Field to Reference the Products

Then under the "Add new field" section, enter the following info:

  • Label: Beer Products
  • Field type: Product reference
  • Widget: Check boxes/radio buttons

Then click the "Save" button, then click the "Save field settings" button.  Next, specify these values:

  • Label: Beer Products
  • Required field: Check the box
  • Products types that can be referenced: Beer
  • Default value: Pint
  • Number of values: Unlimited

Then click the "Save settings" button.

6b. Add an Image Field to Show a Picture

If you're not already there, navigate to this page in Drupal:

admin/structure/types/manage/beer/fields

Under the "Add new field" section, enter the following info:

  • Label: Photo
  • Field type: Image
  • Widget: Image

Then click the "Save" button, then click the "Save field settings" button.  Next, specify these values:

  • Label: Photo
  • Required field: Check the box

Leave all the other settings as the default values provided, then click the "Save settings" button.

7. Create Content to Display Products

Now that we have some beer products created, we need to create some beer content to reference the beer products. This allows the "Add to cart" functionality to work within Drupal Commerce. On your Drupal site, go to:

node/add/beer

Enter these values:

  • Title: Bells Oberon
  • Beer Products:
    • bells-oberon-12-oz: Check this box
    • bells-oberon-pint: Check this box
  • Photo: Upload a picture to use

Click the "Save" button, then repeat that process for the other two types of beer, using these values:

  • Title: Sierra Nevada Torpedo Extra IPA
  • Beer Products:
    • sierra-nevada-torpedo-extra-ipa-12-oz: Check this box
    • sierra-nevada-torpedo-extra-ipa-22-oz: Check this box
  • Photo: Upload a picture to use

Then one more time with:

  • Title: Shorts Brew Soft Parade
  • Beer Products:
    • shorts-brew-soft-parade-12: Check this box
  • Photo: Upload a picture to use

8. Create a Page to Display the Product Content

We'll use Views to make a page to display our Beer content, in Drupal go to:

admin/structure/views/add

Enter these values:

  • View name: Beers
  • Show: Content
  • of type: Beer
  • sorted by: Title

Next check the "Create a page" box and enter these values (note, the zero used below means display all items):

  • Page title: Beers
  • Path: beers
  • Display format: Table
  • Items to display: 0

Then check the "Create a menu link" checkbox and enter these values:

  • Menu: Main menu
  • Link text: Beers

Then click the "Continue & edit" button.

8a. Display an Image with the Product Listing Page

Now, in the "Fields" section, follow these steps:

  1. Click the "Add" button
  2. Enter this keyword in the Search box: photo
  3. Check the box next to "Content: Photo"
  4. Click the "Add and configure fields" button
  5. Uncheck the "Create a label" box
  6. On the "Image style" select list, choose "Thumbnail (100 x 100)"
  7. On the "Link image to" select list, choose "Content"
  8. Click the "Apply" button
  9. In the "Fields" section, click the down arrow then click the "Rearrange" button
  10. Grab the handle next to the Photo field, and drag it above the Title field
  11. Click the "Apply" button

Finally, we can click the "Save" button on our View, then click the "Home" button to go home. We should now see a "Beers" button in our main menu, let's click it to look at our fine selection:

9. Enable a Payment Method

For this tutorial, we'll be using Stripe to accept a demonstration payment.

  1. Signup (or login) with Stripe
  2. Download and enable the Commerce Stripe module
    1. pay close attention to the README with this module to install it properly
  3. Go to: admin/commerce/config/payment-methods
  4. Click "enable" on the disabled Stripe payment method rule
  5. Click "edit" next to the enabled Stripe payment method rule
  6. Click "edit" next to the Stripe action
  7. In a separate window, login to Stripe website
  8. Go to: Your Account -> Account Settings -> API Keys
  9. Copy the Secret and Publishable test keys from Stripe
  10. Paste them back into the corresponding text fields in the "Payment Settings" section back on the Drupal site
  11. Click "Save"
  12. Flush all of Drupal's caches

10. E-commerce... done!

That's it, we've built a fully functional e-commerce website with Drupal Commerce to sell beer. Customers can now browse the beer on our website, add it to their cart, and complete the checkout process.

Let's now move on to building the mobile application...

11. Enable DrupalGap, Commerce DrupalGap and Commerce DrupalGap Stripe modules in Drupal

Since we'll be using DrupalGap to build the mobile application, let's download and enable the modules we'll need on our Drupal site:

Refer to each module's README file for important installation instructions about their dependencies and setup configurations.

VERY IMPORTANT: At this time of writing this, the Commerce Services module (a dependancy of Commerce DrupalGap) isn't evolved enough to handle permissions properly. So for this to work properly we have to enable certain permissions for anonymous and authenticated users that we wouldn't normally grant, or we have to use user #1 to bypass the permissions. See this issue for more details: https://www.drupal.org/node/1943426

12. Set up the DrupalGap Mobile Application Development Kit

If you're new to DrupalGap, follow the Hello World guide to get started. For this example, we'll be developing the mobile app in Google Chrome using the Ripple plugin. Once everything is up and running, our app should look similar to the screen shot here.

13. Enable the Address Field, Commerce and Commerce DrupalGap Stripe modules for DrupalGap

Just like Drupal, DrupalGap has contributed modules. We'll need to download and these two modules to our app's modules directory within DrupalGap:

Then enable the modules in our settings.js file, for example:

Drupal.modules.contrib['addressfield'] = { minified: true };
Drupal.modules.contrib['commerce'] = { minified: true };
Drupal.modules.contrib['commerce_drupalgap_stripe'] = {};

14. Create a Custom DrupalGap Module to Customize the App

Again, just like Drupal, DrupalGap allows us to create our own modules to handle customization. Let's create a custom module in DrupalGap, so its JavaScript file lives here:

www/app/modules/custom/my_module/my_module.js

Then enable the module from within the settings.js file:

Drupal.modules.contrib['my_module'] = {};

15. Create a JSON Page URL in Drupal to Retrieve the Beer List

Let the fun begin... first we need to access our beer list via JSON, so we need to setup a URL that will return JSON to us, in Drupal go to:

admin/structure/views/view/beers/edit
  1. Click +Add in the Display section
  2. Click Page
  3. Change the Display name to JSON
  4. Change the Format to JSON Data Document, on This page (override), then click the Apply (this display) button
  5. Uncheck the Views API mode checkbox, then click the Apply (this display) button
  6. Under Page Settings, change the Path to beers.json
  7. Under Fields, change the Label on both fields, so the first character in the label is lower case, on this display only
  8. Next to Fields, click the Add button
  9. Enter nid into the Search box
  10. Check the box next to Content: Nid and then press the Apply (this display) button
  11. Change the Label to nid and then press the Apply (this display) button

Now if we preview our results, we should see something like this:

{
  "nodes" : [
    {
      "node" : {
        "photo" : { "src": "http://localhost/drupal-7/sites/default/files/styles/thumbnail/public/sierranevada_torpedoextraipa12oz_0.jpg?itok=8sCnt0hP" },
        "title" : "Sierra Nevada Torpedo Extra IPA",
        "nid" : "293"
      }
    },
    {
      "node" : {
        "photo" : { "src": "http://localhost/drupal-7/sites/default/files/styles/thumbnail/public/soft-parade-1.jpg?itok=UWiqK1fL" },
        "title" : "Shorts Brew Soft Parade",
        "nid" : "294"
      }
    },
    {
      "node" : {
        "photo" : { "src": "http://localhost/drupal-7/sites/default/files/styles/thumbnail/public/bells-oberon-12_0.jpg?itok=Phb_X8bE" },
        "title" : "Bells Oberon",
        "nid" : "292"
      }
    }
  ],
  "view" : {
    "name" : "beers",
    "display" : "page_1",
    "path" : "admin/structure/views/nojs/preview/beers/page_1",
    "root" : "nodes",
    "child" : "node",
    "pages" : null,
    "page" : null,
    "count" : 3,
    "limit" : null
  }
}

Click the "Save" button to save the View. We're now ready to consume our beer as JSON, be sure to eat some food before consuming beer, and after I suppose.

16. Create a Page in the App to Display the Beer List

Since we created a custom module in DrupalGap earlier, we can use the my_module.js file to create a custom page using hook_menu(), like so:

/**
 * Implements hook_menu().
 */
function my_module_menu() {
  try {
    var items = {};
    items['beer-list'] = {
      title: 'Beer List',
      page_callback: 'my_module_beer_list_page'
    };
    return items;
  }
  catch (error) {
    console.log('my_module_menu - ' + error);
  }
}

function my_module_beer_list_page() {
  try {
    var content = {};
    content['my_beer_list'] = {
      theme: 'view',
      format: 'ul',
      path: 'beers.json',
      row_callback: 'my_module_beer_list_page_row',
      empty_callback: 'my_module_beer_list_page_empty'
    };
    return content;
  }
  catch (error) { console.log('my_module_beer_list_page - ' + error); }
}

function my_module_beer_list_page_row(view, row) {
  try {
    var image = theme('image', { path: row.photo.src });
    var title = '<h2>' + row.title + '</h2>';
    var html = l(image + title, 'node/' + row.nid);
    return html;
  }
  catch (error) { console.log('my_module_beer_list_page_row - ' + error); }
}

function my_module_beer_list_page_empty(view) {
  try {
    return "Sorry, we are out of beer.";
  }
  catch (error) { console.log('my_module_beer_list_page_empty - ' + error); }
}

Then if we set this newly created page as our App's front page in the settings.js file:

// App Front Page
drupalgap.settings.front = 'beer-list';

We'll be able to see our beer list!

17. Let's Go Shopping for Beer!

Since DrupalGap comes with the ability to view nodes, and we chose to render our beer list with each item as a clickable link to its node page (e.g. node/123), we can click on an item in our beer list to view it. Since it is 9:30 AM at the time of writing this paragraph, I won't actually drink a beer, but if I had to choose, I'd click on the Oberon for now.

Now that we're viewing the beer node, we can see that our "Add to Cart" form and picture of the beer were automatically rendered for us, neat-o. If we were to scroll down in the app, we could see the whole picture, but you get the idea. Since its early, let's select a 12 0z instead of a pint, and click the add to cart button.

Then we can proceed through the checkout process, which is similar to most e-commerce workflows:

18. All Done!

Now if we were to look in the admin order section in Drupal:

admin/commerce/orders

We would see our new order sitting there with a status of pending:

Pretty cool huh? Not bad. I hope you enjoyed this tutorial, and are inspired to try some of these technologies to build your own mobile application to sell your Drupal Commerce products. Thanks and happy coding!

Comments

Great post! This type of full site build tutorials is something I think the community needs more of. Have you considered posting this in the recipes section of d.o docs? https://www.drupal.org/documentation/site-recipes

tyler's picture

Thank you Brian. I wasn't aware of the Site Recipes section until now, thanks. If it wouldn't be in bad taste, I'd consider adding a "Blog post" section that page to start collecting external blog posts that match a Site Recipe. I'm not sure if the recipe guide is meant for internal pages only.

Hi Tyler,

Thank you for this greaat tutorial.

I 've run through this and I'm getting the list. However, when I click a list item and go to the node page for the selection, the node title shows up, the node comments show up (if they are on) but no image, no options widgets, add to cart etc. appear. They are showing in the web app.

Any advice would be greatly appreciated.

Thank you again!

 

tyler's picture

Thanks for the kind words Jason.

Take a look at the commerce_cart_add_to_cart_form() function in commerce.js, there is a big bug listed next to the @TODO comment.

You'll most likely need to change the line below that to use the machine name of your product reference field on your content type that displays your products.

Try changing that to match your field's machine name. It would be great if you could figure out how to fix that line of code so it works for everyone, but I haven't been able to locate the field's machine name dynamically, so I just typed it in manually for now.

Also be sure to check out the DrupalGap display mode: http://www.drupalgap.org/node/184

Greetings!
I set the problem I describe above aside to test the Stripe payment method. I had been getting an error in mobile, so I went to confirm that the Drupal was processing cards properly. Nope.

I was getting this error:

  • Notice: Undefined index: stripeToken in commerce_stripe_submit_form_submit() (line 211 of/home/domain/public_html/sites/all/modules/commerce_stripe/commerce_stripe.module).
  • We received the following error processing your card. Please enter your information again or try a different card.
  • You must supply either a card or a customer id
  • Notice: Undefined property: Stripe_InvalidRequestError::$json_body in commerce_stripe_submit_form_submit()(line 275 of /home/domain/public_html/sites/all/modules/commerce_stripe/commerce_stripe.module).

Obviously, mobile won't work if web is not working. I discovered that the problem is with the deprecated event handler in jquery.  I have Jquery Update Module installed and I had the current version set to 1.10. When I set that to 1.8 because the event handler live() was removed in 1.9 (http://api.jquery.com/live/) Stripe payments via Drupal worked well.

One down! Now, go back and try my payment via Ripple for mobile. No such luck. This is throwing this error in console:

  1. POST http://www.domain.com/?q=drupalgap/commerce-payment-stripe.json&flatten_... 404 (Not found: Could not find resource commerce-payment-stripe.)
    1. (anonymous function)0
    2. services_get_csrf_token.success4
    3. services_get_csrf_token6
    4. Drupal.services.call3
    5. commerce_drupalgap_stripe_create5
    6. (anonymous function)2
    7. b.extend.each:3
    8. commerce_drupalgap_stripe_response1
    9. e.ajaxJSONP.success:1
    10. window.(anonymous function):1
    11. (anonymous function)
    12. Here the function b.extend.each is processed by jquery-1.9.1.min.js

      I'm thinking there's validity to the notion that the jquery version on web must match jquery version on mobile?

      I'll go to work resolving that. First by using jquery-1.8.x to test it. I'll post my results.

       

Now to my original question. The problem that I had was that the Display Settings for DrupalGap on the content type were not correct. I got the cart to display on my mobile app once I discovered that the content type display settings for DrupalGap needed to be adjusted and made sure to add the block code to the content region in settings.js
commerce_cart: {
       pages: {
         mode: 'exclude',
         value: ['cart', 'checkout/*', 'checkout/shipping/*']
       }
     }

However, the cart showed up only because I was logged in as a user that had added items to the cart in the web app. The mobile app "Add To Cart" is not working.

Notice here that I did: console.log('We get the selector as ' + selector);
I think that the problem here may also be jquery related. When I had jquery update module set to 1.10, the result of that console.log =
We get selector as:
#node_8 select._commerce_cart_attribute

Now with jquery update module set to 1.8.x the output of that console.log =
We get selector as:
[object HTMLDivElement]

So, I'm wondering if this is a byproduct of the jquery version mismatch? In any event, this function is failing.

I think this is the function you were referencing. The comment you mentioned @TODO was not present? My machine name for the product here is
function _commerce_product_display_get_current_product_id() {
  try {
    // Iterate over each attribute on the page, pull out the field_name and
    // value, and set them aside, so they can later be used to determine which
    // product is currently selected.
    var selector = '#' + drupalgap_get_page_id() + ' select._commerce_cart_attribute';
    //var selector = node_8;
    console.log('We get the selector as ' + selector);
    var attributes = { };
    $(selector).each(function(index, object) {
        var field_name = $(object).attr('field_name')
        var value = $(object).val();
        attributes[field_name] = value;
    });

Very sorry for the long post, however it might help others encountering the same problem.

Thanks again, Tyler!

 

 

 

tyler's picture

Thanks for the thorough report. It'd be better to post any issues, one at a time, in the Commerce DrupalGap issue queue(s):

I've had luck using the version of jQ and jQM that are packaged with DrupalGap. Also, I don't typically use jQuery update on the Drupal site.

Here is a patch that will resolve the problem is found here. https://www.drupal.org/node/2247523

 

I'm not certain how this will affect the communication with Drupalgap.

 

Thanks!

Hello Tyler, Thanks for your great tutorial. I have a little problem. All my commerce fields don't appear on the mobile app. You can access the app via the url: http://e-gap.model225.com/mobile-application/ Please, could you help me ? Thanks.

tyler's picture

Hi Frederic,

Thank you.

Take a look at your JavaScript Console in Chrome, it says:

Failed to load resource: the server responded with a status of 404 (Not found: Could not find resource product-display.)

This means you haven't set up the Commerce Services and/or Commerce DrupalGap modules properly on your Drupal site.

Be sure to closely follow the README:

https://github.com/signalpoint/commerce

Then make sure you're display settings are properly set:

http://www.drupalgap.org/node/184

Helllo Tyler,

I'm having the same problem as Frederic, the product-display/$node.json is returning a 404 error. On my product display (admin/commerce/products/types/product/display), there is no option for DrupalGap, but in the custom display settings, I can enable Node: DrupalGap. Could this be the problem?

 

Thanks,

 

-Lee

tyler's picture

The "manage display" is for the content type that references your products. The product reference field on that content type should be set to "add to cart form". You don't need to worry about the display settings on the product type.

Tyler,

Ahh.. got it. Thank you for the clairification. I still can't figure out why my product-display/$node.json is returning a 404. Is there anything I can do to debug?

Thanks,

-Lee

tyler's picture

What's the URL that 404's? Go to admin -> structure -> services, then edit the resources for drupalgap, and make sure the product display resource is enabled, then flush all of your caches. Stop by #drupal-drupalgap on IRC to chat.

Hi, I tried this tutorial and everything worked pretty fine, but, my app couldn't "send" to my drupal site the customer's Billing Information, I made a lot of tests to discover that neither your tutorial makes it work ( as I can see, your orders page can't show the customer's name).

Is this feature not working or am I doing something wrong?

tyler's picture

That feature hasn't been implemented yet, the billing information form is only used for the Stripe payment gateway and isn't tied to the Order itself. Please consider contributing to the solution(s), thanks!

I can't find settings.js ?

Is it inside a module?

tyler's picture

The settings.js file doesn't exist by default, you need to save a copy of default.settings.js as settings.js inside the "app" directory, for example: https://github.com/signalpoint/DrupalGap/tree/7.x-1.x/app

Hi Tyler, I tried your DrupalGap module and this tutorial - it's very cool.

I did everything, all is good, but path to product image in product node page is bad.

Error in Chrome console:

http://localhost/drupal_test_drpgpnew/mobile-application/localhost/drupa... Failed to load resource: the server responded with a status of 404 (Not Found)

Right path to image is localhost/drupal_test_drpgpnew/sites/default/files/2014-03-22%2017.41.03.jpg

 

Can you help me please?

Thank you very much.

Roger

(from Czech republic)

tyler's picture

Make sure your site_path in the settings.js file is set to:

http://localhost/drupal_test_drpgpnew

Other than that I'm not quite sure. So it'd be best to file a bug report: https://github.com/signalpoint/commerce

Thank you Tyler for your answer, my site_path was set only to 'localhost/drupal....', now it works fine with http://localhost...

Thank you again. Roger

Hello Tyler,

please can you tell me, how can I set up other payment method?

I mean for example cash payment when I deliver goods - In mobile app customer just select payment method (cash payment), go through and complete order.

Thank you.

tyler's picture

First you need to decide on what module to use on your Drupal site to handle the cash payment. Then that same module will have to be built for DrupalGap.

Is this tutorial enough for simple app for site admins for content approval? ( commerce/payment functionality doesn't required).

 

tyler's picture

Hi Nithin, DrupalGap can definitely be used to make a simple admin content approval app. This tutorial may not be the best starting point, but it has some valid related ideas in it. Checkout the Getting Started Guide for DrupalGap, it should have everything you need to build the app you describe.

Hallo,

Thank you verry much for the tutorial.But I don't quit get it.
I get the title Beer List, but I can't see any of the products.
Howerver I have a menu of the lefttop were I can clink on content en can select create content while users don't have permission to do that.

Sow I went wrong somewere.
The other problem is the config.xml.
I placed the example config.xml in het root but I still get this error in ripple.

 

Can you help ?

 

 

tyler's picture

Don't worry about the config.xml file in Chrome+Ripple, it isn't needed, and you can just ignore any errors about it.

OKé,

 

I have it working 50/50.
For some reasen I't only displays the product variantion but not the information dependend on the variaten type.
 

In my example I would like to sell furniture.

The image changes with the type of wood you choose just like the product code and the price.
How do I get that working?

I'm working in windows 7 with phonegap / google chrome ripple emulator
I'm working with drupal 7
It's not my first website, but the first time I use e-commerce and drupalgap.
I've watch the video and followed the tutorial.

tyler's picture

You'll most likely have to dig into the code that powers the Commerce module for DrupalGap (commerce.js). It was built for demonstration purposes, and to my knowledge hasn't been used in a production environment yet, so it needs some improvements to accommodate a wider range of Commerce set ups.