Drupal Commerce: Getting 'coupons' to behave like 'gift cards'

Using the Commerce Coupon module as a starting point, it is possible to coax Drupal Commerce into adopting "gift card"-like behaviour. For our purposes, a gift card was considered to have the following features: - may be used multiple times (if there is a remaining balance) - balance is adjusted after purchases and persists until the next use - has a unique card code which can be input at checkout time This differs from the standard Commerce Coupon behaviour, which is to set up an item with a fixed amount and apply that fixed amount each time the coupon is redeemed. Necessary components: - Drupal Commerce - Commerce Coupon - Commerce Coupon Fixed Amount - A custom module space in which you can put code After installing Commerce Coupon, create several coupons at Admin > Store > Coupons (/admin/commerce/coupons). Set the "Maximum Number of Uses" field to something high such as "50", to allow the gift card (coupon) to be used multiple times. The first thing worth consideration is that by itself, Commerce Coupon will allow a coupon with a value greater than the value of the order total to be applied, resulting in a negative balance on checkout. To remedy this situation, the following Rule should be imported (as found in Issue #1318392: Negative Order Total and with thanks to Drupal user bradhawkins):

{ "rules_zero_out_negative_balance" :
{ "LABEL" : "Zero out Negative balance",
  "PLUGIN" : "reaction rule",
  "REQUIRES" : [ "commerce_payment", "rules", "entity" ],
  "ON" : [ "commerce_order_presave" ],
  "IF" : [ { "commerce_payment_order_balance_comparison" : { "commerce_order" : [ "commerce_order" ], "operator" : "\u003c", "value" : "0" } } ],
  "DO" : [ { "data_set" : { "data" : [ "commerce-order:commerce-order-total" ], "value" : { "value" : { "amount" : "0", "currency_code" : "USD" } } } } ]

With the above Rule in place, negative balances will be brought back up to $0. What remains to be done, then, is figure out how much - if any - from the coupon"s value is left over and subtract that from the coupon"s saved value for the next use, thereby creating "gift card" type functionality. To do so, we first need to save the negative balance for later retrieval. Edit the Rule that you have just imported (/admin/config/workflow/rules/reaction/manage/rules_zero_out_negative_balance) and add a new Action Type: Execute custom PHP code PHP Code value: your_module_commerce_save_prezeroed_amount($commerce_order); This new Action should be sorted up to the top of the Actions list, so that it appears above the data_set action. In your custom module, add the following function:

// called from the Zero Negative Balance Rule to preserve the original negative balance when a GC is used
function your_module_commerce_save_prezeroed_amount($order) {
  if ( $order->commerce_order_total["und"][0]["amount"] < 0 ) {
    $order->data["pre_zeroed_amount"] = $order->commerce_order_total["und"][0]["amount"];

This will save the remaining balance of the gift card. Next, we add an implementation of hook_commerce_checkout_complete

function your_module_commerce_checkout_complete($order) {
  if (isset($order->data["pre_zeroed_amount"])) {
    $coupon = commerce_coupon_load($order->commerce_coupon_order_reference["und"][0]["target_id"]);
    $coupon->commerce_coupon_fixed_amount["und"][0]["amount"] = $order->data["pre_zeroed_amount"] * -1;
    drupal_set_message("You have $" . ($coupon->commerce_coupon_fixed_amount["und"][0]["amount"] / 100) . " credit remaining on your gift card");

The above will update the coupon with the remaining balance, to be used again in the future and inform the user of the balance remaining on their gift card. If you wish to change the language in the store to better reflect that you are employing "gift cards" vs. "coupons", this can be accomplished either through the regular translation interface, the String Overrides module, or via a simple form_alter:

function your_module_form_alter(&$form, &$form_state, $form_id) {
  if ($form_id == "commerce_checkout_form_checkout") {
    // re-label "Coupon" as "Gift Card"
    $form["commerce_coupon"]["coupon_code"]["#title"] = t("Gift Card Number");
    $form["commerce_coupon"]["coupon_code"]["#description"] = t("Enter your gift card number if applicable.");
    $form["commerce_coupon"]["#title"] = t("Gift Cards");
  } else if ($form_id == "commerce_checkout_form_review") {
    $form["checkout_review"]["review"]["#data"]["commerce_coupon"]["title"] = t("Gift Card");
    $form["checkout_review"]["review"]["#data"]["commerce_coupon"]["data"] = str_replace("Coupon", "Gift Card", $form["checkout_review"]["review"]["#data"]["commerce_coupon"]["data"]);   }

Now your coupons behave like gift cards.