/** * Standard modular run function for realtime-rain hooks. * * @param TIME Start of time range. * @param TIME End of time range. * @return array A list of template parameter sets for rendering a 'drop'. */ function run($from, $to) { $drops = array(); if (has_actual_page_access(get_member(), 'admin_ecommerce')) { $rows = $GLOBALS['SITE_DB']->query('SELECT amount,item,t_time AS timestamp FROM ' . $GLOBALS['SITE_DB']->get_table_prefix() . 'transactions WHERE t_time BETWEEN ' . strval($from) . ' AND ' . strval($to)); foreach ($rows as $row) { require_code('ecommerce'); list($product, ) = find_product_row($row['item']); if (!is_null($product)) { $title = $product[4]; } else { require_lang('ecommerce'); $title = do_lang('SALE_MADE'); } $timestamp = $row['timestamp']; $ticker_text = do_lang('KA_CHING', ecommerce_get_currency_symbol(), $row['amount']); $drops[] = rain_get_special_icons(NULL, $timestamp, NULL, $ticker_text) + array('TYPE' => 'ecommerce', 'FROM_MEMBER_ID' => NULL, 'TO_MEMBER_ID' => NULL, 'TITLE' => $title, 'IMAGE' => find_theme_image('bigicons/ecommerce'), 'TIMESTAMP' => strval($timestamp), 'RELATIVE_TIMESTAMP' => strval($timestamp - $from), 'TICKER_TEXT' => $ticker_text, 'URL' => NULL, 'IS_POSITIVE' => true, 'IS_NEGATIVE' => false, 'FROM_ID' => NULL, 'TO_ID' => NULL, 'GROUP_ID' => 'product_' . $row['item']); } } return $drops; }
/** * Handle IPN's that have been confirmed as backed up by real money. * * @param ID_TEXT The ID of the purchase-type (meaning depends on item_name) * @param SHORT_TEXT The item being purchased (aka the product) (blank: subscription, so we need to look it up). One might wonder why we use $item_name instead of $product. This is because we pass human-readable-names (hopefully unique!!!) through payment gateways because they are visually shown to the user. (blank: it's a subscription, so look up via a key map across the subscriptions table) * @param SHORT_TEXT The status this transaction is telling of * @set SModified SCancelled Completed Pending Failed * @param SHORT_TEXT The code that gives reason to the status * @param SHORT_TEXT The reason it is in pending status (if it is) * @param SHORT_TEXT A note attached to the transaction * @param SHORT_TEXT The amount of money * @param SHORT_TEXT The currency the amount is in * @param SHORT_TEXT The transaction ID * @param SHORT_TEXT The ID of the parent transaction * @param ID_TEXT The ID of a special source for the transaction * @param string The subscription period (blank: N/A) */ function handle_confirmed_transaction($purchase_id, $item_name, $payment_status, $reason_code, $pending_reason, $memo, $mc_gross, $mc_currency, $txn_id, $parent_txn_id, $source = '', $period = '') { /*#####################################################################################*/ //Temporary setting - force payment setting to "completed" for test mode transactions if (get_option('ecommerce_test_mode') == "1") { $payment_status = 'Completed'; } /*#####################################################################################*/ // Try and locate the product if ($item_name == '') { $product = $GLOBALS['SITE_DB']->query_value_null_ok('subscriptions', 's_type_code', array('id' => intval($purchase_id))); // Note that s_type_code is not numeric, it is a $product if (is_null($product)) { warn_exit(do_lang_tempcode('NO_SUCH_SUBSCRIPTION', strval($purchase_id))); } $item_name = '_' . $product; // Check what we sold list($found, ) = find_product_row($product, true, false); if (!is_null($found)) { $item_name = $found[4]; } } else { // Check what we sold list($found, $product) = find_product_row($item_name, true, true); } if (is_null($found)) { my_exit(do_lang('PRODUCT_NO_SUCH') . ' - ' . $item_name); } // Check price if ($mc_gross != $found[1] && $found[1] != '?') { if ($payment_status == 'SModified') { $GLOBALS['SITE_DB']->query_update('subscriptions', array('s_state' => 'new'), array('id' => intval($purchase_id)), '', 1); } if ($payment_status != 'SCancelled' && substr($txn_id, 0, 6) != 'manual') { my_exit(do_lang('PURCHASE_WRONG_PRICE', $item_name)); } } if ($period != '') { $length = array_key_exists('length', $found[3]) ? strval($found[3]['length']) : '1'; $length_units = array_key_exists('length_units', $found[3]) ? $found[3]['length_units'] : 'm'; if (strtolower($period) != strtolower($length . ' ' . $length_units)) { my_exit(do_lang('IPN_SUB_PERIOD_WRONG')); } } // Store $GLOBALS['SITE_DB']->query_insert('transactions', array('id' => $txn_id, 't_memo' => $memo, 'purchase_id' => $purchase_id, 'status' => $payment_status, 'pending_reason' => $pending_reason, 'reason' => $reason_code, 'amount' => $mc_gross, 't_currency' => $mc_currency, 'linked' => $parent_txn_id, 't_time' => time(), 'item' => $product, 't_via' => $source)); $found['txn_id'] = $txn_id; // Check currency if ($mc_currency != get_option('currency')) { if ($payment_status == 'SModified') { $GLOBALS['SITE_DB']->query_update('subscriptions', array('s_state' => 'new'), array('id' => intval($purchase_id)), '', 1); } if ($payment_status != 'SCancelled' && substr($txn_id, 0, 6) != 'manual') { my_exit(do_lang('PURCHASE_WRONG_CURRENCY')); } } // Pending if ($payment_status == 'Pending' && $found[0] == PRODUCT_INVOICE) { $GLOBALS['SITE_DB']->query_update('invoices', array('i_state' => 'pending'), array('id' => intval($purchase_id)), '', 1); } elseif ($payment_status == 'Pending' && $found[0] == PRODUCT_SUBSCRIPTION) { $GLOBALS['SITE_DB']->query_update('subscriptions', array('s_state' => 'pending'), array('id' => intval($purchase_id)), '', 1); if ($found[2] != '') { call_user_func_array($found[2], array($purchase_id, $found, $product, true)); } // Run cancel code } elseif ($payment_status == 'Pending' && $item_name == do_lang('CART_ORDER', $purchase_id)) { $found['ORDER_STATUS'] = 'ORDER_STATUS_awaiting_payment'; if ($found[2] != '') { call_user_func_array($found[2], array($purchase_id, $found, $product, true)); } // Set order status } elseif ($payment_status == 'SCancelled' && $found[0] == PRODUCT_SUBSCRIPTION) { $GLOBALS['SITE_DB']->query_update('subscriptions', array('s_auto_fund_source' => $source, 's_auto_fund_key' => $txn_id, 's_state' => 'cancelled'), array('id' => intval($purchase_id)), '', 1); } elseif ($found[0] == PRODUCT_SUBSCRIPTION) { $GLOBALS['SITE_DB']->query_update('subscriptions', array('s_auto_fund_source' => $source, 's_auto_fund_key' => $txn_id, 's_state' => 'active'), array('id' => intval($purchase_id)), '', 1); } elseif ($payment_status != 'Completed' && $payment_status != 'SCancelled' && get_option('ecommerce_test_mode') != '1') { my_exit(do_lang('TRANSACTION_NOT_COMPLETE', $product . ':' . strval($purchase_id), $payment_status), true); } // Invoice: Check price if ($found[0] == PRODUCT_INVOICE) { $price = $GLOBALS['SITE_DB']->query_value('invoices', 'i_amount', array('id' => intval($purchase_id))); if ($price != $mc_gross) { if (substr($txn_id, 0, 6) != 'manual') { my_exit(do_lang('PURCHASE_WRONG_PRICE', $item_name)); } } } /* At this point we know our order (or subscription cancellation) is good */ // Dispatch if ($payment_status == 'Completed' || $payment_status == 'SCancelled') { //Find product hooks of this order to check dispatch type $object = find_product($product, true); if (is_object($object) && !method_exists($object, 'get_product_dispatch_type')) { //If hook does not have dispatch method setting take dispatch method as automatic $found['ORDER_STATUS'] = 'ORDER_STATUS_dispatched'; } elseif (is_object($object) && $object->get_product_dispatch_type($purchase_id) == 'automatic') { $found['ORDER_STATUS'] = 'ORDER_STATUS_dispatched'; } else { $found['ORDER_STATUS'] = 'ORDER_STATUS_payment_received'; } if ($found[2] != '') { call_user_func_array($found[2], array($purchase_id, $found, $product)); } // Send out notification to staff if ($found[0] == PRODUCT_SUBSCRIPTION) { require_code('notifications'); $member_id = $GLOBALS['SITE_DB']->query_value_null_ok('subscriptions', 's_member_id', array('id' => intval($purchase_id))); if (!is_null($member_id)) { $username = $GLOBALS['FORUM_DRIVER']->get_username($member_id); if (is_null($username)) { $username = do_lang('GUEST'); } if ($payment_status == 'Completed') { $subject = do_lang('SERVICE_PAID_FOR', $item_name, $username, get_site_name(), get_site_default_lang()); $body = do_lang('_SERVICE_PAID_FOR', $item_name, $username, get_site_name(), get_site_default_lang()); dispatch_notification('service_paid_for_staff', NULL, $subject, $body); } else { $subject = do_lang('SERVICE_CANCELLED', $item_name, $username, get_site_name(), get_site_default_lang()); $body = do_lang('_SERVICE_CANCELLED', $item_name, $username, get_site_name(), get_site_default_lang()); dispatch_notification('service_cancelled_staff', NULL, $subject, $body); } } } } // Invoice handling if ($found[0] == PRODUCT_INVOICE) { $GLOBALS['SITE_DB']->query_update('invoices', array('i_state' => 'paid'), array('id' => intval($purchase_id)), '', 1); } // Subscription: Delete if cancelled if ($payment_status == 'SCancelled' && $found[0] == PRODUCT_SUBSCRIPTION) { $GLOBALS['SITE_DB']->query_delete('subscriptions', array('id' => intval($purchase_id)), '', 1); } }
/** * View an overview of the members adverts on the system. * * @return tempcode The UI */ function adverts() { require_lang('classifieds'); require_code('catalogues'); require_code('ecommerce'); $member_id = get_param_integer('member_id', get_member()); $title = get_page_title($member_id == get_member() ? 'CLASSIFIED_ADVERTS' : '_CLASSIFIED_ADVERTS', true, array($GLOBALS['FORUM_DRIVER']->get_username($member_id))); if (is_guest()) { access_denied('NOT_AS_GUEST'); } enforce_personal_access($member_id); $start = get_param_integer('start', 0); $max = get_param_integer('max', 30); require_code('templates_results_browser'); $max_rows = $GLOBALS['SITE_DB']->query_value('catalogue_entries e JOIN ' . get_table_prefix() . 'classifieds_prices c ON c.c_catalogue_name=e.c_name', 'COUNT(*)', array('ce_submitter' => $member_id)); $rows = $GLOBALS['SITE_DB']->query_select('catalogue_entries e JOIN ' . get_table_prefix() . 'classifieds_prices c ON c.c_catalogue_name=e.c_name', array('e.*'), array('ce_submitter' => $member_id), 'GROUP BY e.id ORDER BY ce_add_date DESC'); if (count($rows) == 0) { inform_exit(do_lang_tempcode('NO_ENTRIES')); } $ads = array(); foreach ($rows as $row) { $root = get_param_integer('root', NULL); $data_map = get_catalogue_entry_map($row, NULL, 'CATEGORY', 'DEFAULT', $root, NULL, array(0)); $ad_title = $data_map['FIELD_0']; $purchase_url = build_url(array('page' => 'purchase', 'type' => 'misc', 'filter' => 'CLASSIFIEDS_ADVERT', 'id' => $row['id']), get_module_zone('purchase')); // We'll show all transactions against this ad $transaction_details = $GLOBALS['SITE_DB']->query('SELECT * FROM ' . get_table_prefix() . 'transactions WHERE purchase_id=' . strval($row['id']) . ' AND item LIKE \'' . db_encode_like('CLASSIFIEDS\\_ADVERT\\_%') . '\''); $_transaction_details = array(); foreach ($transaction_details as $t) { list($found, ) = find_product_row($t['item']); if (!is_null($found)) { $item_title = $found[4]; } else { $item_title = $t['item']; } $_transaction_details[] = array('T_ID' => strval($t['id']), 'PURCHASE_ID' => strval($t['purchase_id']), 'STATUS' => $t['status'], 'REASON' => $t['reason'], 'AMOUNT' => float_format($t['amount']), 'T_CURRENCY' => $t['t_currency'], 'LINKED' => $t['linked'], 'T_TIME' => strval($t['t_time']), 'ITEM' => $t['item'], 'ITEM_TITLE' => $item_title, 'PENDING_REASON' => $t['pending_reason'], 'T_MEMO' => $t['t_memo'], 'T_VIA' => $t['t_via']); } $url_map = array('page' => 'catalogues', 'type' => 'entry', 'id' => $row['id'], 'root' => $root); $url = build_url($url_map, '_SELF'); // No known expiry status: put on free, or let expire if ($row['ce_last_moved'] == $row['ce_add_date']) { require_code('classifieds'); initialise_classified_listing($row); } $ads[] = array('AD_TITLE' => $ad_title, 'TRANSACTION_DETAILS' => $_transaction_details, 'DATE' => get_timezoned_date($row['ce_add_date']), 'DATE_RAW' => strval($row['ce_add_date']), 'EXPIRES_DATE' => get_timezoned_date($row['ce_last_moved']), 'EXPIRES_DATE_RAW' => strval($row['ce_last_moved']), 'ACTIVE' => $row['ce_validated'] == 1, 'PURCHASE_URL' => $purchase_url, 'ID' => strval($row['id']), 'URL' => $url, 'NUM_VIEWS' => integer_format($row['ce_views'])); } $results_browser = results_browser(do_lang('_CLASSIFIED_ADVERTS'), NULL, $start, 'start', $max, 'max', $max_rows, NULL, NULL, true); return do_template('CLASSIFIED_ADVERTS_SCREEN', array('TITLE' => $title, 'RESULTS_BROWSER' => $results_browser, 'ADS' => $ads)); }