/** * Process the transaction. * Verifies that the transaction is valid, then records the purchase and * notifies the buyer and administrator * * @uses Validate() * @uses BaseIPN::isUniqueTxnId() * @uses BaseIPN::handlePurchase() */ public function Process() { if ($this->Validate() > 0) { return false; } if (1 != $this->pp_data['pmt_status']) { return false; } else { $this->pp_data['status'] = 'paid'; } if (!$this->isUniqueTxnId($this->pp_data)) { return false; } // Log the IPN. Verified is 'true' if we got this far. $LogID = $this->Log(true); // shopping cart $fees_paid = $this->pp_data['pmt_tax'] + $this->pp_data['pmt_shipping'] + $this->pp_data['pmt_handling']; PAYPAL_debug("Received {$item_gross} gross payment"); if ($this->isSufficientFunds()) { $this->handlePurchase(); return true; } else { return false; } }
/** * Process an incoming IPN transaction * Do the following: * 1. Verify IPN * 2. Log IPN * 3. Check that transaction is complete * 4. Check that transaction is unique * 5. Check for valid receiver email address * 6. Process IPN * * @uses BaseIPN::AddItem() * @uses BaseIPN::handleFailure() * @uses BaseIPN::handlePurchase() * @uses BaseIPN::isUniqueTxnId() * @uses BaseIPN::isSufficientFunds() * @uses BaseIPN::Log() * @uses Verify() * @uses isStatusCompleted() * @param array $in POST variables of transaction * @return boolean true if processing valid and completed, false otherwise */ public function Process() { // If no data has been received, then there's nothing to do. if (empty($this->ipn_data)) { return false; } if (!$this->Verify()) { $logId = $this->Log(false); $this->handleFailure(PAYPAL_FAILURE_VERIFY, "({$logId}) Verification failed"); return false; } else { $logId = $this->Log(true); } // Set the custom data field to the exploded value. This has to // be done after Verify() or the Paypal verification will fail. $this->pp_data['custom'] = $this->custom; switch ($this->ipn_data['txn_type']) { case 'web_accept': //usually buy now //usually buy now case 'send_money': //usually donation/send money // Process Buy Now & Send Money $fees_paid = $this->ipn_data['tax'] + $this->pp_data['pmt_shipping'] + $this->pp_data['pmt_handling']; if (!empty($this->ipn_data['item_number'])) { if (!isset($this->ipn_data['quantity']) || (double) $this->ipn_data['quantity'] == 0) { $this->ipn_data['quantity'] = 1; } $payment_gross = $this->pp_data['pmt_gross'] - $fees_paid; $unit_price = $payment_gross / $this->ipn_data['quantity']; $this->AddItem($this->ipn_data['item_number'], $this->ipn_data['quantity'], $unit_price, $this->ipn_data['item_name'], $this->pp_data['pmt_shipping'], $this->pp_data['pmt_handling']); $currency = $this->pp_data['currency']; PAYPAL_debug("Net Settled: {$payment_gross} {$currency}"); if ($this->isSufficientFunds()) { $this->handlePurchase(); } else { $this->handleFailure(PAYPAL_FAILURE_FUNDS, "({$logId}) Insufficient funds for purchase"); return false; } } break; case 'cart': // shopping cart $fees_paid = $this->pp_data['pmt_tax'] + $this->pp_data['pmt_shipping'] + $this->pp_data['pmt_handling']; USES_paypal_class_cart(); if (empty($this->pp_data['custom']['cart_id'])) { $this->handleFailure(NULL, 'Missing Cart ID'); return false; } // Create a cart and read the info from the cart table. // Actual items purchased and prices will come from the IPN. $ppCart = new ppCart($this->pp_data['custom']['cart_id']); $Cart = $ppCart->Cart(); $items = array(); for ($i = 1; $i <= $this->ipn_data['num_cart_items']; $i++) { // PayPal returns the total price as mc_gross_X, so divide // by the quantity to get back to a unit price. if (!isset($this->ipn_data["quantity{$i}"]) || (double) $this->ipn_data["quantity{$i}"] == 0) { $this->ipn_data["quantity{$i}"] = 1; } $item_gross = $this->ipn_data["mc_gross_{$i}"]; if (isset($this->ipn_data["mc_shipping{$i}"])) { $item_shipping = (double) $this->ipn_data["mc_shipping{$i}"]; $item_gross -= $item_shipping; } else { $item_shipping = 0; } if (isset($this->ipn_data["tax{$i}"])) { $item_tax = (double) $this->ipn_data["tax{$i}"]; $item_gross -= $item_tax; } else { $item_tax = 0; } if (isset($this->ipn_data["mc_handling{$i}"])) { $item_handling = (double) $this->ipn_data["mc_handling{$i}"]; $item_gross -= $item_handling; } else { $item_handling = 0; } $unit_price = $item_gross / (double) $this->ipn_data["quantity{$i}"]; // Add the item to the array for the order creation. // IPN item numbers are indexes into the cart, so get the // actual product ID from the cart $this->AddItem($Cart[$this->ipn_data["item_number{$i}"]]['item_id'], $this->ipn_data["quantity{$i}"], $unit_price, $this->ipn_data["item_name{$i}"], $item_shipping, $item_handling, $item_tax, $Cart[$this->ipn_data["item_number{$i}"]]['extras']); } $payment_gross = $this->ipn_data['mc_gross'] - $fees_paid; PAYPAL_debug("Received {$payment_gross} gross payment"); //$currency = $this->ipn_data['mc_currency']; if ($this->isSufficientFunds()) { $this->handlePurchase(); } else { $this->handleFailure(PAYPAL_FAILURE_FUNDS, "({$logId}) Insufficient/incorrect funds for purchase"); return false; } break; // other, unknown, unsupported // other, unknown, unsupported default: switch ($this->ipn_data['reason_code']) { case 'refund': $this->handleRefund(); break; default: $this->handleFailure(PAYPAL_FAILURE_UNKNOWN, "({$logId}) Unknown transaction type"); return false; break; } break; } return true; }
/** * Send an email to the buyer * * @param string $status Order status (pending, paid, etc.) * @param string $msg Optional message to include with email */ public function Notify($status = '', $gw_msg = '') { global $_CONF, $_PP_CONF, $_TABLES; // Check if we're supposed to send a notification if ($this->uid != 1 && $_PP_CONF['purch_email_user'] || $this->uid == 1 && $_PP_CONF['purch_email_anon']) { PAYPAL_debug("Sending email to " . $this->uid); // setup templates $message = new Template(PAYPAL_PI_PATH . '/templates'); $message->set_file(array('subject' => 'purchase_email_subject.txt', 'msg_admin' => 'purchase_email_admin.txt', 'msg_user' => 'purchase_email_user.txt', 'msg_body' => 'purchase_email_body.txt')); // Add all the items to the message $total = (double) 0; // Track total purchase value $files = array(); // Array of filenames, for attachments $num_format = "%5.2f"; $item_total = 0; $have_physical = 0; // Assume no physical items. $dl_links = ''; // Start with empty download links USES_paypal_class_product(); foreach ($this->items as $id => $item) { if (!PAYPAL_is_plugin_item($item['product_id'])) { $P = new Product($item['product_id']); if ($P->prod_type & PP_PROD_PHYSICAL == PP_PROD_PHYSICAL) { $have_physical = 1; } // Add the file to the filename array, if any. Download // links are only included if the order status is 'paid' $file = $P->file; if (!empty($file) && $this->status == 'paid') { $files[] = $file; $dl_url = PAYPAL_URL . '/download.php?'; // There should always be a token, but fall back to the // product ID if there isn't if (!empty($item['token'])) { $dl_url .= 'token=' . urlencode($item['token']); } else { $dl_url .= 'id=' . $item['item_number']; } $dl_links .= "<a href=\"{$dl_url}\">{$dl_url}</a><br />"; } } $ext = (double) $item['quantity'] * (double) $item['price']; $item_total += $ext; $item_descr = isset($item['description']) ? $item['description'] : $item['descrip']; //$message->set_block('message', 'ItemList', 'List'); $opts = json_decode($item['options_text'], true); if ($opts) { foreach ($opts as $opt_text) { $options_text .= " -- {$opt_text}<br />"; } } $message->set_block('msg_body', 'ItemList', 'List'); $message->set_var(array('qty' => $item['quantity'], 'price' => sprintf($num_format, $item['price']), 'ext' => sprintf($num_format, $ext), 'name' => $item_descr, 'options_text' => $options_text)); //PAYPAL_debug("Qty: {$item['quantity']} : Amount: {$item['price']} : Name: {$item['name']}", 'debug_ipn'); $message->parse('List', 'ItemList', true); } // Determine if files will be attached to this message based on // global config and whether there are actually any files to // attach. Affects the 'files' flag in the email template and // which email function is used. if ((is_numeric($this->uid) && $this->uid != 1 && $_PP_CONF['purch_email_user_attach'] || (!is_numeric($this->uid) || $this->uid == 1) && $_PP_CONF['purch_email_anon_attach']) && count($files) > 0) { $do_send_attachments = true; } else { $do_send_attachments = false; } $total_amount = $item_total + $this->tax + $this->shipping + $this->handling; $user_name = COM_getDisplayName($this->uid); if ($this->billto_name == '') { $this->billto_name = $user_name; } $message->set_var(array('payment_gross' => sprintf($num_format, $total_amount), 'payment_items' => sprintf($num_format, $item_total), 'tax' => sprintf($num_format, $this->tax), 'shipping' => sprintf($num_format, $this->shipping), 'handling' => sprintf($num_format, $this->handling), 'payment_date' => $_PP_CONF['now']->toMySQL(true), 'payer_email' => $this->buyer_email, 'payer_name' => $this->billto_name, 'site_name' => $_CONF['site_name'], 'txn_id' => $this->pmt_txn_id, 'pi_url' => PAYPAL_URL, 'pi_admin_url' => PAYPAL_ADMIN_URL, 'dl_links' => $dl_links, 'files' => $do_send_attachments ? 'true' : '', 'buyer_uid' => $this->uid, 'user_name' => $user_name, 'gateway_name' => $this->pmt_method, 'pending' => $this->status == 'pending' ? 'true' : '', 'gw_msg' => $gw_msg, 'status' => $this->status, 'order_instr' => $this->instructions)); // parse templates for subject/text $subject = trim($message->parse('output', 'subject')); $message->set_var('purchase_details', $message->parse('detail', 'msg_body')); $user_text = $message->parse('user_out', 'msg_user'); $admin_text = $message->parse('admin_out', 'msg_admin'); if ($this->buyer_email != '') { // if specified to mail attachment, do so, otherwise skip // attachment if ($do_send_attachments) { // Make sure plugin functions are available USES_paypal_functions(); PAYPAL_mailAttachment($this->buyer_email, $subject, $user_text, $_CONF['site_email'], true, 0, '', '', $files); } else { // Otherwise send a standard notification COM_emailNotification(array('to' => array($this->buyer_email), 'from' => $_CONF['site_mail'], 'htmlmessage' => $user_text, 'subject' => $subject)); } } // Send a notification to the administrator, new purchases only if ($status == '') { if ($_PP_CONF['purch_email_admin'] == 2 || $have_physical && $_PP_CONF['purch_email_admin'] == 1) { PAYPAL_debug('Sending email to Admin'); $email_addr = empty($_PP_CONF['admin_email_addr']) ? $_CONF['site_mail'] : $_PP_CONF['admin_email_addr']; COM_emailNotification(array('to' => array($email_addr), 'from' => $_CONF['noreply_mail'], 'htmlmessage' => $admin_text, 'subject' => $subject)); } } } }
/** * Determines if the current record is valid. * Checks various items that can't be empty or combinations that * don't make sense. * Accumulates all error messages in the Errors array. * As of version 0.5.0, the category is allowed to be empty. * * @deprecated * @return boolean True if ok, False when first test fails. */ private function isValidRecord() { global $LANG_PP; // Check that basic required fields are filled in if ($this->name == '') { $this->Errors[] = $LANG_PP['err_missing_name']; } if ($this->short_description == '') { $this->Errors[] = $LANG_PP['err_missing_desc']; } if ($this->prod_type == PP_PROD_DOWNLOAD) { if ($this->file == '') { // Must have a file for a downloadable product $this->Errors[] = $LANG_PP['err_missing_file']; } if ($this->expiration < 1) { // Must have an expiration period for downloads $this->Errors[] = $LANG_PP['err_missing_exp']; } } elseif ($this->prod_type == PP_PROD_PHYSICAL && $this->price < 0.01) { // Paypal won't accept a zero amount, so non-downloadable items // must have a positive price. Use "Other Virtual" for free items. $this->Errors[] = $LANG_PP['err_phys_need_price']; } if (!empty($this->Errors)) { PAYPAL_debug('Errors encountered: ' . print_r($this->Errors, true)); return false; } else { PAYPAL_debug('isValidRecord(): No errors'); return true; } }
/** * Send an email with attachments. * This is a verbatim copy of COM_mail(), but with the $attachments * paramater added and 3 extra lines of code near the end. * * @param string $to Receiver's email address * @param string $from Sender's email address * @param string $subject Message Subject * @param string $message Message Body * @param boolean $html True for HTML message, False for Text * @param integer $priority Message priority value * @param string $cc Other recipients * @param string $altBody Alt. body (text) * @param array $attachments Array of attachments * @return boolean True on success, False on Failure */ function PAYPAL_mailAttachment($to, $subject, $message, $from = '', $html = false, $priority = 0, $cc = '', $altBody = '', $attachments = array()) { global $_CONF; $subject = substr($subject, 0, strcspn($subject, "\r\n")); $subject = COM_emailEscape($subject); require_once $_CONF['path'] . 'lib/phpmailer/class.phpmailer.php'; $mail = new PHPMailer(); $mail->SetLanguage('en', $_CONF['path'] . 'lib/phpmailer/language/'); $mail->CharSet = COM_getCharset(); if ($_CONF['mail_backend'] == 'smtp') { $mail->IsSMTP(); $mail->Host = $_CONF['mail_smtp_host']; $mail->Port = $_CONF['mail_smtp_port']; if ($_CONF['mail_smtp_secure'] != 'none') { $mail->SMTPSecure = $_CONF['mail_smtp_secure']; } if ($_CONF['mail_smtp_auth']) { $mail->SMTPAuth = true; $mail->Username = $_CONF['mail_smtp_username']; $mail->Password = $_CONF['mail_smtp_password']; } $mail->Mailer = "smtp"; } elseif ($_CONF['mail_backend'] == 'sendmail') { $mail->Mailer = "sendmail"; $mail->Sendmail = $_CONF['mail_sendmail_path']; } else { $mail->Mailer = "mail"; } $mail->WordWrap = 76; $mail->IsHTML($html); $mail->Body = $message; if ($altBody != '') { $mail->AltBody = $altBody; } $mail->Subject = $subject; if (is_array($from) && isset($from[0]) && $from[0] != '') { if ($_CONF['use_from_site_mail'] == 1) { $mail->From = $_CONF['site_mail']; $mail->AddReplyTo($from[0]); } else { $mail->From = $from[0]; } } else { $mail->From = $_CONF['site_mail']; } if (is_array($from) && isset($from[1]) && $from[1] != '') { $mail->FromName = $from[1]; } else { $mail->FromName = $_CONF['site_name']; } if (is_array($to) && isset($to[0]) && $to[0] != '') { if (isset($to[1]) && $to[1] != '') { $mail->AddAddress($to[0], $to[1]); } else { $mail->AddAddress($to[0]); } } else { // assume old style.... $mail->AddAddress($to); } if (isset($cc[0]) && $cc[0] != '') { if (isset($cc[1]) && $cc[1] != '') { $mail->AddCC($cc[0], $cc[1]); } else { $mail->AddCC($cc[0]); } } else { // assume old style.... if (isset($cc) && $cc != '') { $mail->AddCC($cc); } } if ($priority) { $mail->Priority = 1; } PAYPAL_debug('Attachments: ' . print_r($attachments, true)); // Add attachments foreach ($attachments as $key => $value) { $mail->AddAttachment($value); } if (!$mail->Send()) { COM_errorLog("Email Error: " . $mail->ErrorInfo); return false; } return true; }
/** * Process an incoming IPN transaction * Do the following: * 1. Verify IPN * 2. Log IPN * 3. Check that transaction is complete * 4. Check that transaction is unique * 5. Check for valid receiver email address * 6. Process IPN * * @uses BaseIPN::AddItem() * @uses BaseIPN::handleFailure() * @uses BaseIPN::handlePurchase() * @uses BaseIPN::isUniqueTxnId() * @uses BaseIPN::isSufficientFunds() * @uses BaseIPN::Log() * @uses Verify() * @uses isStatusCompleted() * @param array $in POST variables of transaction * @return boolean true if processing valid and completed, false otherwise */ public function Process() { // If no data has been received, then there's nothing to do. if (empty($this->ipn_data)) { return false; } if (!$this->Verify()) { $logId = $this->Log(false); $this->handleFailure(PAYPAL_FAILURE_VERIFY, "({$logId}) Verification failed"); return false; } else { $logId = $this->Log(true); } // Set the custom data field to the exploded value. This has to // be done after Verify() or the Paypal verification will fail. $this->pp_data['custom'] = $this->custom; //if (!$this->isStatusCompleted($this->pp_data['pmt_status'])) { // Not logged since this probably isn't an error // $this->handleFailure(PAYPAL_FAILURE_COMPLETED, // "($logId) Status not complete"); // return false; //} /*if (!$this->isUniqueTxnId($this->pp_data)) { $this->handleFailure(PAYPAL_FAILURE_UNIQUE, "($logId) Non-unique transaction id"); return false; }*/ switch ($this->ipn_data['txn_type']) { case 'web_accept': //usually buy now //usually buy now case 'send_money': //usually donation/send money // Process Buy Now & Send Money $fees_paid = $this->ipn_data['tax'] + $this->pp_data['pmt_shipping'] + $this->pp_data['pmt_handling']; if (!empty($this->ipn_data['item_number'])) { if (!isset($this->ipn_data['quantity']) || (double) $this->ipn_data['quantity'] == 0) { $this->ipn_data['quantity'] = 1; } $payment_gross = $this->pp_data['pmt_gross'] - $fees_paid; $unit_price = $payment_gross / $this->ipn_data['quantity']; $this->AddItem($this->ipn_data['item_number'], $this->ipn_data['quantity'], $unit_price, $this->ipn_data['item_name'], $this->pp_data['pmt_shipping'], $this->pp_data['pmt_handling']); $currency = $this->pp_data['currency']; PAYPAL_debug("Net Settled: {$payment_gross} {$currency}"); if ($this->isSufficientFunds()) { $this->handlePurchase(); } else { $this->handleFailure(PAYPAL_FAILURE_FUNDS, "({$logId}) Insufficient funds for purchase"); return false; } } break; case 'cart': // shopping cart $fees_paid = $this->pp_data['pmt_tax'] + $this->pp_data['pmt_shipping'] + $this->pp_data['pmt_handling']; $items = array(); for ($i = 1; $i <= $this->ipn_data['num_cart_items']; $i++) { // PayPal returns the total price as mc_gross_X, so divide // by the quantity to get back to a unit price. if (!isset($this->ipn_data["quantity{$i}"]) || (double) $this->ipn_data["quantity{$i}"] == 0) { $this->ipn_data["quantity{$i}"] = 1; } $item_gross = $this->ipn_data["mc_gross_{$i}"]; if (isset($this->ipn_data["mc_shipping{$i}"])) { $item_shipping = (double) $this->ipn_data["mc_shipping{$i}"]; $item_gross -= $item_shipping; } else { $item_shipping = 0; } if (isset($this->ipn_data["tax{$i}"])) { $item_tax = (double) $this->ipn_data["tax{$i}"]; $item_gross -= $item_tax; } else { $item_tax = 0; } if (isset($this->ipn_data["mc_handling{$i}"])) { $item_handling = (double) $this->ipn_data["mc_handling{$i}"]; $item_gross -= $item_handling; } else { $item_handling = 0; } $unit_price = $item_gross / (double) $this->ipn_data["quantity{$i}"]; $this->AddItem($this->ipn_data["item_number{$i}"], $this->ipn_data["quantity{$i}"], $unit_price, $this->ipn_data["item_name{$i}"], $item_shipping, $item_handling, $item_tax); } $payment_gross = $this->ipn_data['mc_gross'] - $fees_paid; PAYPAL_debug("Received {$payment_gross} gross payment"); //$currency = $this->ipn_data['mc_currency']; if ($this->isSufficientFunds()) { $this->handlePurchase(); } else { $this->handleFailure(PAYPAL_FAILURE_FUNDS, "({$logId}) Insufficient/incorrect funds for purchase"); return false; } break; // other, unknown, unsupported // other, unknown, unsupported default: switch ($this->ipn_data['reason_code']) { case 'refund': $this->handleRefund(); break; default: $this->handleFailure(PAYPAL_FAILURE_UNKNOWN, "({$logId}) Unknown transaction type"); return false; break; } break; } return true; }
/** * Processes the purchase, for purchases made without an IPN message. * * @param array $vals Submitted values, e.g. $_POST */ public function handlePurchase($vals = array()) { global $_TABLES, $_CONF, $_PP_CONF; USES_paypal_functions(); USES_paypal_class_cart(); USES_paypal_class_order(); USES_paypal_class_product(); if (!empty($vals['cart_id'])) { $cart = new ppCart($vals['cart_id']); if (!$cart->hasItems()) { return; } // shouldn't be empty $items = $cart->Cart(); } else { $cart = new ppCart(); } // Create an order record to get the order ID $Order = $this->CreateOrder($vals, $cart); $db_order_id = DB_escapeString($Order->order_id); $prod_types = 0; // For each item purchased, record purchase in purchase table foreach ($items as $id => $item) { //COM_errorLog("Processing item: $id"); list($item_number, $item_opts) = PAYPAL_explode_opts($id, true); // If the item number is numeric, assume it's an // inventory item. Otherwise, it should be a plugin-supplied // item with the item number like pi_name:item_number:options if (PAYPAL_is_plugin_item($item_number)) { PAYPAL_debug("handlePurchase for Plugin item " . $item_number); // Initialize item info array to be used later $A = array(); // Split the item number into component parts. It could // be just a single string, depending on the plugin's needs. $pi_info = explode(':', $item['item_number']); PAYPAL_debug('Paymentgw::handlePurchase() pi_info: ' . print_r($pi_info, true)); $status = LGLIB_invokeService($pi_info[0], 'productinfo', array($item_number, $item_opts), $product_info, $svc_msg); if ($status != PLG_RET_OK) { $product_info = array(); } if (!empty($product_info)) { $items[$id]['name'] = $product_info['name']; } PAYPAL_debug("Paymentgw::handlePurchase() Got name " . $items[$id]['name']); $vars = array('item' => $item, 'ipn_data' => array()); $status = LGLIB_invokeService($pi_info[0], 'handlePurchase', $vars, $A, $svc_msg); if ($status != PLG_RET_OK) { $A = array(); } // Mark what type of product this is $prod_types |= PP_PROD_VIRTUAL; } else { PAYPAL_debug("Paypal item " . $item_number); $P = new Product($item_number); $A = array('name' => $P->name, 'short_description' => $P->short_description, 'expiration' => $P->expiration, 'prod_type' => $P->prod_type, 'file' => $P->file, 'price' => $item['price']); if (!empty($item_opts)) { $opts = explode(',', $itemopts); $opt_str = $P->getOptionDesc($opts); if (!empty($opt_str)) { $A['short_description'] .= " ({$opt_str})"; } $item_number .= '|' . $item_opts; } // Mark what type of product this is $prod_types |= $P->prod_type; } // An invalid item number, or nothing returned for a plugin if (empty($A)) { //$this->Error("Item {$item['item_number']} not found"); continue; } // If it's a downloadable item, then get the full path to the file. // TODO: pp_data isn't available here, should be from $vals? if (!empty($A['file'])) { $this->items[$id]['file'] = $_PP_CONF['download_path'] . $A['file']; $token_base = $this->pp_data['txn_id'] . time() . rand(0, 99); $token = md5($token_base); $this->items[$id]['token'] = $token; } else { $token = ''; } $items[$id]['prod_type'] = $A['prod_type']; // If a custom name was supplied by the gateway's IPN processor, // then use that. Otherwise, plug in the name from inventory or // the plugin, for the notification email. if (empty($item['name'])) { $items[$id]['name'] = $A['short_description']; } // Add the purchase to the paypal purchase table $uid = isset($vals['uid']) ? (int) $vals['uid'] : $_USER['uid']; $sql = "INSERT INTO {$_TABLES['paypal.purchases']} SET \n order_id = '{$db_order_id}',\n product_id = '{$item_number}',\n description = '{$items[$id]['name']}',\n quantity = '{$item['quantity']}', \n user_id = '{$uid}', \n txn_type = '{$this->gw_id}',\n txn_id = '', \n purchase_date = '{$_PP_CONF['now']->toMySQL()}', \n status = 'complete',\n token = '{$token}',\n price = " . (double) $item['price'] . ",\n options = '" . DB_escapeString($item_opts) . "'"; // add an expiration date if appropriate if (is_numeric($A['expiration']) && $A['expiration'] > 0) { $sql .= ", expiration = DATE_ADD('{$_PP_CONF['now']->toMySQL()}', INTERVAL {$A['expiration']} DAY)"; } //echo $sql;die; PAYPAL_debug($sql); DB_query($sql); } // foreach item // If this was a user's cart, then clear that also if (isset($vals['cart_id']) && !empty($vals['cart_id'])) { DB_delete($_TABLES['paypal.cart'], 'cart_id', $vals['cart_id']); } }
/** * Create and populate an Order record for this purchase. * Gets the billto and shipto addresses from the cart, if any. * Items are saved in the purchases table by handlePurchase(). * * This function is called only by our own handlePurchase() function, * but is made "protected" so a derived class can use it if necessary. * * @return string Order ID, to link to the purchases table */ protected function CreateOrder() { global $_TABLES, $_PP_CONF; // See if an order already exists for this transaction. // If so, load it and update the status. If not, continue on // and create a new order $order_id = DB_getItem($_TABLES['paypal.orders'], 'order_id', "pmt_txn_id='" . DB_escapeString($this->pp_data['txn_id']) . "'"); if (!empty($order_id)) { $this->Order = new ppOrder($order_id); if ($this->Order->order_id != '') { $this->Order->log_user = $this->gw->Description(); $this->Order->UpdateStatus($this->pp_data['status']); } return 2; } $this->Order = new ppOrder(); USES_paypal_class_cart(); if (isset($this->pp_data['custom']['cart_id'])) { $cart = new ppCart($this->pp_data['custom']['cart_id']); if (!$_PP_CONF['sys_test_ipn'] && !$cart->hasItems()) { return 1; // shouldn't normally be empty except during testing } } else { $cart = NULL; } $uid = (int) $this->pp_data['custom']['uid']; $this->Order->uid = $uid; $this->Order->status = !empty($this->pp_data['status']) ? $this->pp_data['status'] : 'pending'; if ($uid > 1) { USES_paypal_class_userinfo(); $U = new ppUserInfo($uid); } // Get the billing and shipping addresses from the cart record, // if any. There may not be a cart in the database if it was // removed by a previous IPN, e.g. this is the 'completed' message // and we already processed a 'pending' message if ($cart) { $BillTo = $cart->getAddress('billto'); } if (empty($BillTo) && $uid > 1) { $BillTo = $U->getDefaultAddress('billto'); } if (is_array($BillTo)) { $this->Order->setBilling($BillTo); } $ShipTo = $this->pp_data['shipto']; if (empty($ShipTo)) { if ($cart) { $ShipTo = $cart->getAddress('shipto'); } if (empty($ShipTo) && $uid > 1) { $ShipTo = $U->getDefaultAddress('shipto'); } } if (is_array($ShipTo)) { $this->Order->setShipping($ShipTo); } if (isset($this->pp_data['shipto']['phone'])) { $this->Order->phone = $this->pp_data['shipto']['phone']; } $this->Order->pmt_method = $this->gw_id; $this->Order->pmt_txn_id = $this->pp_data['txn_id']; $this->Order->tax = $this->pp_data['pmt_tax']; $this->Order->shipping = $this->pp_data['pmt_shipping']; $this->Order->handling = $this->pp_data['pmt_handling']; $this->Order->buyer_email = $this->pp_data['payer_email']; $this->Order->log_user = $this->gw->Description(); $order_id = $this->Order->Save(); $db_order_id = DB_escapeString($order_id); $this->Order->items = array(); foreach ($this->items as $id => $item) { $options = DB_escapeString($item['options']); list($item_number, $options) = explode('|', $item['item_number']); //if (is_numeric($item['item_number'])) { if (is_numeric($item_number)) { // For Paypal catalog options, check for options and append // to the description. Update quantity on hand if tracking // is enabled. These actions don't apply to items from // other plugins. if (!empty($options)) { // options is expected as CSV $sql = "SELECT attr_value\n FROM {$_TABLES['paypal.prod_attr']}\n WHERE attr_id IN ({$options})"; $optres = DB_query($sql); $opt_str = ''; while ($O = DB_fetchArray($optres, false)) { $opt_str .= ', ' . $O['attr_value']; } $item['name'] .= $opt_str; } /*$sql = "UPDATE {$_TABLES['paypal.products']} SET onhand = GREATEST(0, onhand - " . (int)$item['quantity'] . ") WHERE id = '" . (int)$item['item_number'] . "' AND track_onhand > 0";*/ //COM_errorLog($sql); DB_query($sql, 1); } $sql = "INSERT INTO {$_TABLES['paypal.purchases']} SET \n order_id = '{$db_order_id}',\n product_id = '{$item['item_number']}',\n description = '" . DB_escapeString($item['name']) . "',\n quantity = '{$item['quantity']}', \n user_id = '{$this->pp_data['custom']['uid']}', \n txn_type = '{$this->pp_data['custom']['transtype']}',\n txn_id = '{$this->pp_data['txn_id']}', \n purchase_date = '{$this->sql_date}', \n status = 'pending',\n token = '" . md5(time()) . "',\n price = " . (double) $item['price'] . ",\n options = '{$options}'"; // add an expiration date if appropriate if (is_numeric($item['expiration']) && $item['expiration'] > 0) { $sql .= ", expiration = DATE_ADD('{$_PP_CONF['now']}', INTERVAL {$item['expiration']} DAY)"; } PAYPAL_debug($sql); DB_query($sql); } // foreach item // Reload the order to get the items $this->Order->Load(); // If this was a user's cart, then clear that also if (isset($this->pp_data['custom']['cart_id']) && !empty($this->pp_data['custom']['cart_id'])) { if (!$_PP_CONF['sys_test_ipn']) { DB_delete($_TABLES['paypal.cart'], 'cart_id', $this->pp_data['custom']['cart_id']); PAYPAL_debug('Cart ' . $this->pp_data['custom']['cart_id'] . ' deleted'); } } else { PAYPAL_debug('no cart to delete'); } return 0; }