Gift Certificates in Drupal Commerce

4th May 2013 | Tags:

There isn’t really an out-​of-​the-​box solu­tion for gift cer­tifi­cates in Dru­pal Com­merce; there’s no mod­ule (at time of writ­ing) you can sim­ply down­load, enable and for­get about. Being Dru­pal, there are many ways to skin a cat, so to speak, so in this post I’m going to out­line how I got them work­ing on a site I built.

Of course, the way your gift cer­tifi­cates work may well dif­fer from site-​to-​site; I had a way I wanted it to work, but your require­ments might be sub­tly dif­fer­ent, but hope­fully what­ever your require­ments, some of what fol­lows is of some use.

To out­line, here’s what I wanted to achieve:

  1. Gift cer­tifi­cates avail­able as prod­ucts, which a cus­tomer can purchase
  2. A range of val­ues (£5, £10, £20 etc) available
  3. A prod­uct dis­play page that dif­fered from “con­ven­tional” prod­ucts — there are less fields required, so the lay­out was likely to be different
  4. I wanted a cus­tomer to be able to spec­ify a recip­i­ent for the gift certificate
  5. The cus­tomer needed to sup­ply an email address for the recip­i­ent, so we can send it to them via email
  6. I wanted the cus­tomer to be able to spec­ify the recipient’s name, to per­son­alise the email
  7. The cus­tomer ought to be able to enter their name as they wished it to appear on the email; the billing name on the order might well be too formal
  8. There ought to be an optional text field to add a per­sonal message
  9. The gift cer­tifi­cate needed to be sent out via email when the order was com­pleted, and paid for

As it turns out, step 3 — cre­at­ing a cus­tom prod­uct dis­play page type — was a require­ment, which I’ll dis­cuss later.

Okay, so the fun­da­men­tal basis of this task is the gift cer­tifi­cate itself; a redemp­tion code that could be used as pay­ment — or part-​payment — by the recipient.

This, there is a mod­ule for. Com­merce Coupon takes care of all this. How­ever out-​of-​the-​box, it only really allows you to gen­er­ate a coupon in the admin inter­face, whereas we need to auto­mate this.

Let’s start there, though — down­load and enable Com­merce Coupon in the nor­mal way.

This isn’t quite enough, though — there are two sub-​modules, for fixed-​amount coupons and percentage-​based coupons (money off). It’s the fixed amount ver­sion we’re inter­ested in, so down­load and enable that too. You will prob­a­bly want to enable the UI mod­ule bun­dled with Com­merce coupon, too.

Play around, and note how it works — it adds a new field in the check­out process, where a cus­tomer can enter a coupon code, and the value of the cor­re­spond­ing coupon gets deducted from their order balance.

So far, so good. One thing I imme­di­ately found, though, is that the ter­mi­nol­ogy didn’t really fit what I was try­ing to achieve.

For­tu­nately, there’s a sim­ple way to change small por­tions of text with­out hav­ing to mess around wit trans­la­tion files, and what-​not — the String Over­rides module.

Now go to Admin -> Con­fig­u­ra­tion -> Regional and Lan­guage -> String over­rides, where you can enter a string you want to change. I changed “Enter here your coupon code.” (remem­ber to add it exactly as it’s dis­played, which means includ­ing the full-​stop) to “If you have a gift cer­tifi­cate, enter its code here” and “Granted amount” to “Value”.

Now we need to cre­ate a new cus­tom line item type. It’ll be based on a prod­uct (as opposed to, say, a ship­ping or tax line item). You can cre­ate this by going to Admin -> Store -> Con­fig­u­ra­tion -> Line item types -> Add a prod­uct line item type, and giv­ing it a name, e.g. “Gift Cer­tifi­cate”, or there’s some code to do this auto­mat­i­cally in my exam­ple mod­ule on Github.

Next, we need to add some fields to the line item. This requires the Com­merce Cus­tom Prod­uct mod­ule. Again, this is done auto­mat­i­cally in my exam­ple mod­ule, or you might pre­fer to do it man­u­ally through the Field UI. I’ve added four fields, with the machine names in brack­ets — these are impor­tant, because we’ll use them later on in the code:

  • Recip­i­ent name (field\_gift\_recipient\_name)
  • Recip­i­ent email (field\_gift\_recipient\_email)
  • Sender name (field\_gift\_sender\_name)
  • Per­sonal mes­sage (field\_gift\_personal\_message)

Next up, we need to cre­ate a new Prod­uct Dis­play con­tent type, e.g. “Prod­uct Dis­play (Gift Cer­tifi­cate)”. I won’t go into the details on how to do this, you’ve prob­a­bly already done one. Remem­ber I said that this was required any­way — this is because the Add to Cart form needs to act dif­fer­ently for this con­tent type, because we need to add the fields that are spe­cific to gift cer­tifi­cates. Don’t for­get to add a prod­uct ref­er­ence field — you may well be able to re-​use the exist­ing field from your “con­ven­tional” products.

We need to ensure that the fields we cre­ated show up along­side the Add to Cart but­ton for the rel­e­vant prod­ucts; this is achieved through the Dis­play set­tings on the Prod­uct field of the newly cre­ated Prod­uct Dis­play type. Go to Admin -> Struc­ture -> Con­tent types -> Prod­uct Dis­play (Gift Cer­tifi­cate) -> Man­age Dis­play (sub­sti­tut­ing Prod­uct Dis­play (Gift Cer­tifi­cate) for what­ever you called your new con­tent type). Click the cog along­side the Prod­uct field to change the for­mat set­tings for the Add to Cart form, and change Add to Cart line item type to Gift Cer­tifi­cate (or what­ever you called the new line item type).

Now, if you visit the prod­uct page for our gift cer­tifi­cate prod­uct, you’ll see that instead of a sim­ple Add to Cart but­ton, we’ve got a form instead, where in order to add a gift cer­tifi­cate to their cart, a cus­tomer is required to enter this addi­tional information.

So, how do we send out a gift cer­tifi­cate when a cus­tomer orders it — only after, of course, the cus­tomer has paid for it.

This is where Rules comes in. Much of Commerce’s func­tion­al­ity is based around rules, so we’ll do this in much the same way.

As you’re prob­a­bly aware by now, a rule con­sists of three components:

  • an event; in this case when an order has been completed
  • a con­di­tion; we want this rule to fire when an order con­tains one or more gift certificates
  • an action; what we want to happen

Com­merce already pro­vides an action that fits our require­ments. This event is labelled “Com­plet­ing the check­out process”. Go to Store -> Con­fig­u­ra­tion -> Check­out set­tings -> Check­out rules. You’ll prob­a­bly see there are already a num­ber of rules for this event, depend­ing on what mod­ules you have installed (for exam­ple, I’m using Com­merce Stock, which decre­ments the stock level for each prod­uct, as well as Com­merce Mes­sage, which sends out emails when an order has been placed). Click Add a check­out rule, and give it a name — e.g. “Send out a gift certificate”.

Next up, we need to add a con­di­tion — we only want this rule to fire when a cus­tomer has ordered a gift certificate.

Click Add con­di­tion. Under Select the con­di­tion to add, select Order con­tains a par­tic­u­lar prod­uct under the sec­tion Com­merce Order.

Data selec­tor should be commerce_​order. The next field is Prod­uct SKU, so enter the SKU you spec­i­fied ear­lier, to spec­ify which prod­uct is of inter­est to us.

Oper­a­tor should be set to =, for obvi­ous rea­sons, and you can leave Quan­tity at 1. Ignore negate, and click Save.

For now, let’s use an action for debug­ging pur­poses. Click Add action and under Select the action to add scroll down to Sys­tem and select Dis­play a mes­sage on the site, and enter some mes­sage, e.g. “DEBUG­GING: The order con­tains a gift certificate!”.

Now, try order­ing a gift cer­tifi­cate, and when you com­plete the order you should see the mes­sage you just specified.

Here’s where it gets a lit­tle more com­pli­cated. The rules mod­ule pro­vides a num­ber of sim­ple actions, Com­merce adds a few more, and var­i­ous con­tributed mod­ules add more still. But what we want to achieve can’t really be achieved with­out writ­ing some cus­tom code to do what we want.

So, we need to cre­ate a new mod­ule to add the “action” to this rule. To sum­marise, this is what we want it to do:

  • get the order in question
  • load the line items for the order
  • look for the line items that con­tain prod­ucts (as opposed to, say, shipping)
  • find those line items which con­tain a gift certificate
  • deter­mine the value of the gift certificate
  • extract the recip­i­ent infor­ma­tion (name, email), and the other bits of per­son­al­i­sa­tion (sender’s name, per­sonal message)
  • gen­er­ate a coupon of the required value
  • con­struct an email, con­tain­ing the code for the new coupon, and using the infor­ma­tion pro­vided by the customer
  • send the email to the recipient’s email address

Hope­fully you’ll see why this requires a mod­ule; it’s not that com­plex, but enough that stan­dard actions just won’t cut it.

You’ll need the email mod­ule, for the recipient’s email field. Down­load and enable in the nor­mal way.

First step, as always, a mod­ule .info file (in a direc­tory called gift_certificates in your mod­ules direc­tory), or grab the code from Github:

name = Gift Certificates
description = Supporting functionality for gift certificates
core = 7.x
package = Commerce

dependencies[] = commerce
dependencies[] = commerce_coupon
dependencies[] = commerce_coupon_fixed_amount
dependencies[] = commerce_custom_product
dependencies[] = email

Note the depen­den­cies — but if you’ve been fol­low­ing along, you should have these already installed.

Next up, we need to define our action. We do this by imple­ment­ing hook_rules_action_info() thus:

/**
 * Implements hook_rules_action_info().
 */
function gift_certificates_rules_action_info() {
  return array(
    'gift_certificates_action_send' => array(
      'label' => t('Send Gift Certificate'),
      'group' => t('Gift Certificates'),
    ),
  );
}

Now let’s start on the func­tion which gets called when this action fires:

/**
 * Rule action; sends gift certificate(s).
 */
function gift_certificates_action_send() {

  // Get the order
  $order_id = arg(1);
  $order = commerce_order_load($order_id);

When the action fires, the order ID becomes avail­able in arg(1) — try call­ing dpm (with Devel installed) if you don’t believe me. There’s prob­a­bly a more ele­gant way of doing this, but let’s keep it sim­ple for now.

Next step is to iter­ate through the line items:

// Go through the line items
  foreach ($order->commerce_line_items[LANGUAGE_NONE] as $line_item_item) {
    $line_item_id = $line_item_item['line_item_id'];
    $line_item = commerce_line_item_load($line_item_id);

    // is this a gift certificate?
    if ($line_item->type == 'gift_certificate') {

Sim­ple enough; we iter­ate through the line item IDs in the order object — each item is in the form array('line_item_id' => 123).

Then, we load the line item and then check its type; we’re only inter­ested in the line item type we cre­ated ear­lier; note that you might need to change this depend­ing on how you set it up.

Next thing is to grab the prod­uct from the line item:

// create a params array, to use in the subsequent email
$params = array();

// get the product ID
$product_id = $line_item->commerce_product[LANGUAGE_NONE][0]['product_id'];

// load the product
$product = commerce_product_load($product_id);

Then we extract the infor­ma­tion from the fields we attached to the gift cer­tifi­cate line item:

// get the recipient's details
$params['recipient_name'] = $line_item->field_gift_recipient_name[LANGUAGE_NONE][0]['safe_value'];
$params['email'] = $line_item->field_gift_recipient_email[LANGUAGE_NONE][0]['email'];

// and the sender details
$params['sender_name'] = $line_item->field_gift_sender_name[LANGUAGE_NONE][0]['safe_value'];

// and optionally, the personal message
$params['personal_message'] = $line_item->field_gift_personal_message[LANGUAGE_NONE][0]['safe_value'];

Next, we pro­gram­mat­i­cally cre­ate the coupon:

// Create the coupon
$coupon = commerce_coupon_create('commerce_coupon_fixed');

// set to single use
$coupon->commerce_coupon_number_of_uses[LANGUAGE_NONE][0]['value'] = 1;

// set the amount
$coupon->commerce_coupon_fixed_amount[LANGUAGE_NONE][0] = array(
  'amount'  => $product->commerce_price[LANGUAGE_NONE][0]['amount'],
  'currency_code' => $product->commerce_price[LANGUAGE_NONE][0]['currency_code'],
);

// save the coupon
commerce_coupon_save($coupon);

// get the coupon code
$params['coupon_code'] = $coupon->commerce_coupon_code[LANGUAGE_NONE][0]['value'];

// add the formatted amount to the parameters
$params['coupon_amount'] = commerce_currency_format(
  $coupon->commerce_coupon_fixed_amount[LANGUAGE_NONE][0]['amount'],
  $coupon->commerce_coupon_fixed_amount[LANGUAGE_NONE][0]['currency_code']
);

There are a few things to note here:

  • We need to cre­ate a fixed amount coupon; as opposed to, say, a money-​off one
  • We don’t need to worry about set­ting the coupon code; it’s done for us
  • We only want the coupon to be used once, so we need to set the num­ber of uses to one
  • The price and cur­rency comes from the commerce_price com­po­nent of the prod­uct, which we extracted from the line item
  • We extract the coupon code from the coupon once we’ve saved it, and insert it into the $params array for use later
  • We also for­mat the price, and include it in the $params array too

Finally, we send the email to the recip­i­ent, pass­ing in the var­i­ous val­ues we’ve col­lected along the way:

// Now send the coupon via email to the recipient
gift_certificates_mail_send($params);

Here’s the def­i­n­i­tion of gift_certificates_mail_send:

/**
 * Sends an e-mail.
 *
 * @param $variables
 *   An array of variables used to send the mail, including the various parameters
 */
function gift_certificates_mail_send($variables) {

  $module = 'gift_certificates';
  $key = 'gift_certificate';

  // Specify 'to' and 'from' addresses.
  $to = $variables['email'];
  $from = variable_get('site_mail', 'admin@example.com');

  $params = $variables;

  $language = language_default();

  $send = TRUE;

  $result = drupal_mail($module, $key, $to, $language, $params, $from, $send);  

}

Stan­dard stuff; note that we get the recip­i­ent ($to) from the para­me­ters we passed through.

This then relies on an imple­men­ta­tion of hook_mail():

/**
 * Implements hook_mail().
 */
function gift_certificates_mail($key, &$message, $params) {
  global $user;
  global $base_url;

  $options = array(
    'langcode' => $message['language']->language,
  );

  switch ($key) {    
    case 'gift_certificate':

      $message['subject'] = t('@sender_name has sent you a @site-name gift certificate', array('@sender_name' => $params['sender_name'], '@site-name' => variable_get('site_name', 'Drupal')), $options);   

      $message['body'][] = t('@recipient_name,', array('@recipient_name' => $params['recipient_name']), $options);

      $message['body'][] = t('@sender_name has sent you a gift certificate for @coupon_amount to spend at @site-name.', array('@sender_name' => $params['sender_name'], '@coupon_amount' => $params['coupon_amount'], '@site-name' => variable_get('site_name', 'Drupal')), $options);

      $message['body'][] = t('To use your gift certificate, go to @site-url and enter the code: @coupon_code at checkout.', array('@coupon_code' => $params['coupon_code'], '@site-url' => $base_url), $options);

      if (strlen($params['personal_message'])) {
        $message['body'] = t('@sender_name says:', array('@sender_name' => $params['sender_name']), $options);           
        $message['body'][] = $params['personal_message'];
      }

      break;
  }
}

This sim­ply takes the val­ues we’ve col­lected — recip­i­ent name, sender name, per­sonal mes­sage, coupon amount (which we’ve already for­mat­ted) and coupon code, as well as the site name and its URL — and builds the email to send to the coupon’s recipient.

And that’s it, basic gift cer­tifi­cate func­tion­al­ity. It’s not quite there, as there are a few other considerations:

  • If an order con­tains only gift cer­tifi­cates, we prob­a­bly shouldn’t apply any ship­ping charges
  • You may need to enable Com­merce No Pay­ment in order to allow peo­ple to pay the full bal­ance on an order using a gift certificate

…but, hope­fully, this will get you started.

Comments

No comments yet.

Links and images are allowed, but please note that rel="nofollow" will be automactically appended to any links.