Press enter after choosing selection

Paying with Points: Creating a Custom Payment Type in Ubercart

Mon, 07/28/2014 - 10:30am by ejk

Our players have kept the Summer Game Shop busy this summer; as of this writing, we have received and processed 1599 orders, with an additional 190 orders waiting to be filled this week, with a total of 14,841,000 Summer Game points spent. We use the Drupal module Ubercart to run our shop. It keeps track of item inventory, allows for customer cart & checkout, and keeps track of orders. Normally, Ubercart expects payment to be in US dollars via check or credit card, but it provides a nice set of API hooks to allow us to pay with points instead of dollars. Here's the code we use to talk to Ubercart:

First thing we need to do is to tell Ubercart that we have a new payment method to allow the user to choose at checkout, using hook_payment_method. Ours has a little wrinkle in that we need to allow payment any of your players if you have multiple players attached to your website account. So we load all of your players and list them all as separate payment methods. For each player, we grab your total points to display in the payment method text.

function uc_summergame_payment_method() { global $user; $methods = array(); foreach (summergame_player_load_all($user->uid) as $player) { $balance = 0; $tokens = 0; $uc_game_term = variable_get('uc_summergame_game_term', ''); $uc_token_term = variable_get('uc_summergame_token_term', ''); $player_points = summergame_get_player_points($player['pid']); foreach ($player_points as $game_term => $game_details) { if (strpos($game_term, $uc_game_term) !== FALSE) { $balance += $game_details['balance']; } else if (strpos($game_term, $uc_token_term) !== FALSE) { $tokens += $game_details['balance']; } } $playername = ($player['nickname'] ? $player['nickname'] : $player['name']); $methods[] = array( 'id' => 'sgpoints' . $player['pid'], 'name' => $playername . ' SG Points', 'title' => "$playername's Summer Game points (current balance: $balance points" . ($tokens ? "+ $tokens Tokens" : '') . ')', 'desc' => t('Pay with points earned by playing the Summer Game.'), 'callback' => 'uc_summergame_method_sgpoints', 'weight' => ++$weight, 'checkout' => TRUE, ); } return $methods; }

Next we need to tell Ubercart how to validate an order when it's processed. The main thing we need to check is that the player has enough points to pay for the order. The function named in the 'callback' element of the $methods array in the previous section of code will do this validation for us:

function uc_summergame_method_sgpoints($op, &$order) { if ($op == 'cart-process' && strpos($order->payment_method, 'sgpoints') === 0) { $valid = FALSE; $pid = str_replace('sgpoints', '', $order->payment_method); if ($player = summergame_player_load(array('pid' => $pid))) { $balance = 0; $tokens = 0; $uc_game_term = variable_get('uc_summergame_game_term', ''); $uc_token_term = variable_get('uc_summergame_token_term', ''); $player_points = summergame_get_player_points($player['pid']); foreach ($player_points as $game_term => $game_details) { if (strpos($game_term, $uc_game_term) !== FALSE) { $balance += $game_details['balance']; } else if (strpos($game_term, $uc_token_term) !== FALSE) { $tokens += $game_details['balance']; } } // Get token total (using the "weight" field of the products) $order->token_total = 0; foreach ($order->products as $product) { $order->token_total += $product->weight; } if ($balance < $order->order_total || $tokens < $order->token_total) { drupal_set_message("Your balance of $balance Summer Game points" . ($order->token_total ? " and $tokens Tokens" : '') . " is not enough to complete this purchase.", 'error'); } else { $valid = TRUE; } } else { drupal_set_message('You need to sign up for the Summer Game to earn points to make this purchase', 'error'); } return $valid; } }

Finally, we need to hook into the various order states to do the actual points transactions, using hook_order. If the order is being submitted, we deduct points on the player's ledger. If the order is canceled or deleted, we need to add a refund to their ledger.

function uc_summergame_order($op, &$order, $arg2) { // Get token total (using the "weight" field of the products) $order->token_total = 0; foreach ($order->products as $product) { $order->token_total += $product->weight; } switch ($op) { case 'submit': // fires when the order is submitted and adds/subtracts thier points if (strpos($order->payment_method, 'sgpoints') === 0) { $pid = str_replace('sgpoints', '', $order->payment_method); $player = summergame_player_load(array('pid' => $pid)); $order_link = l('Created Order #' . $order->order_id, 'user/' . $order->uid . '/order/' . $order->order_id); summergame_player_points($player['pid'], -$order->order_total, 'Shop Order', $order_link, "delete:no leaderboard:no"); if ($order->token_total) { summergame_player_points($player['pid'], -$order->token_total, 'Shop Order', $order_link, "delete:no leaderboard:no", variable_get('uc_summergame_token_term', 'SecretShopTokens')); } } // send label to printer if ($label_email = variable_get('uc_summergame_label_email', '')) { uc_summergame_print_premium_label($order, $label_email); } break; case 'update': // if the order is canceled we need to refund thier points if ($arg2 == 'canceled') { if (strpos($order->payment_method, 'sgpoints') === 0) { $pid = str_replace('sgpoints', '', $order->payment_method); $player = summergame_player_load(array('pid' => $pid)); $order_link = l('Refund for Order #' . $order->order_id, 'user/' . $order->uid . '/order/' . $order->order_id); summergame_player_points($player['pid'], $order->order_total, 'Shop Refund', $order_link, "delete:no leaderboard:no"); if ($order->token_total) { summergame_player_points($player['pid'], $order->token_total, 'Shop Refund', $order_link, "delete:no leaderboard:no", variable_get('uc_summergame_token_term', 'SecretShopTokens')); } } } if ($arg2 == 'completed') { // Send a message to the player uc_summergame_send_pickup_notice($order); } break; case 'delete': if (strpos($order->payment_method, 'sgpoints') === 0) { // Refund the points $pid = str_replace('sgpoints', '', $order->payment_method); $player = summergame_player_load(array('pid' => $pid)); $order_link = l('Refund for Order #' . $order->order_id, 'user/' . $order->uid . '/order/' . $order->order_id); summergame_player_points($player['pid'], $order->order_total, 'Shop Refund', $order_link, "delete:no leaderboard:no"); if ($order->token_total) { summergame_player_points($player['pid'], $order->token_total, 'Shop Refund', $order_link, "delete:no leaderboard:no", variable_get('uc_summergame_token_term', 'SecretShopTokens')); } } break; case 'can_delete'; return FALSE; break; } }

Keep those orders coming!

Ubercart: http://www.ubercart.org
Ubercart API: http://www.ubercart.org/docs/api
Summer Game: http://play.aadl.org/

Comments

Graphic for blog posts

Blog Post