65.9K
CodeProject 正在变化。 阅读更多。
Home

用于管理 PayPal 即时付款通知的一些简单 PHP 类

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (5投票s)

2013年7月10日

CPOL

5分钟阅读

viewsIcon

41887

一些用于处理 PayPal 即时付款通知的类,以便在您的端处理必要的流程。

更新

  • 2014 年 10 月 29 日:整合了 Jeremy Falcon 的宝贵反馈
  • 2014 年 11 月 4 日:终于在 PayPal 沙盒中进行了测试,并修复了出现的 bug。

引言

这是一篇快速分享通过 PHP 管理 PayPal 即时付款通知 (IPN) 的方法的文章。纵观网上找到的几个示例,我对其杂乱无章的代码感到惊讶——它们似乎基于 PayPal 的示例代码,而该代码远非全面。此外,它们大多是单体的,很难理解所有必要的步骤。但多亏了它们,我得以弄清楚,本文中的代码就是结果。希望通过分享,其他人可以避免一些我所经历的困难。

下面的类也不是全面的,因为它们只处理了我对单品购买的要求。可能还会缺少其他一些功能,但它们对我来说是有效的,并且应该为那些希望进一步发展的人提供一个良好的起点。

我通过将 IPN 处理过程分解为负责逻辑子任务的对象来处理 IPN。这使得熟悉面向对象的人更容易理解一切。(如果您不熟悉,我希望这能帮助您看到其好处,并更熟悉该方法论。它将在您未来的工作中为您节省大量工作。)

CodeProject 上还有其他几种 IPN 付款方法,您也可以查看。我找到的那些是 C# 和 ASP.NET 语言的。截至 2013 年 7 月,AllanPolichbecker666Mat Stine 的文章似乎与此任务最相关。如果您发现了我遗漏的其他内容,请随时指出。(DaSpors 在评论中链接了一个令人印象深刻的 PHP/JavaScript/SQL 购物车解决方案,如果您需要额外的付款选项,它可以处理比 PayPal IPN 更多的事情。)

解决方案

为了不泄露我自己的设置中的重要细节,我将复制/粘贴我的代码并在下方更改相应的信息。这将导致一篇文章很长,看起来像一个代码转储,但我也会在此处添加一些注释,以便更好地理解该方法。我相信代码是自文档化的,这可能是一些安慰,但我仍然为不便之处道歉,因为如果您在自己的项目中使用这些代码,您需要复制和粘贴七个文件。如果未来有足够的需求,我会花时间创建一个由修改后的代码组成的 zip 文件,但现在我想尽快回到其他项目。

我的解决方案将流程分解为五个对象

  • payPalController - 此类处理 PayPal 流程中非 IPN 部分的逻辑。它检查 PayPal 发送的重复交易,并验证 IPN 交易中的产品是否确实是我网站上的一个。如果是,它会开始将购买添加到数据库并向客户发送电子邮件。
  • payPalIpn - 这是 IPN 处理发生的地方。非常重要!
  • rmwStoreDB - 此单元负责将交易添加到我这边的数据库。
  • myMailClass - 一个基本的邮件发送器,可以极大地扩展以更好地处理 HTML 电子邮件,但它提供了基本功能。它在底层依赖于 Swift Mailer,使其更加轻松。
  • myLoggerClass - 记录错误,并在出现问题时向您发送电子邮件,如果其设置中指定了此类操作。

此外,还有两个非类文件包含

  1. 数据库设置信息,和
  2. 电子邮件账户信息

要使其正常工作,您必须执行三个步骤。首先,您必须设置一个数据库,此处不赘述。如果您还没有,网上有很多资源可以帮助您完成该过程。然后您必须将五个类文件和两个密码文件复制到您网站最低可见目录下的一个子目录中,并根据您的规格进行修改(其中“上方”是指浏览器可查看的那些文件夹)。最后,您为 PayPal 指定的发送通知的网页(如果您选择此方式,可以按按钮逐个设置)必须看起来像这样

<!doctype html public '-//W3C//DTD HTML 4.01//EN'  'http://www.w3.org/TR/html4/strict.dtd'>
<html>
<head>
<title>RandomMonkeyWorks</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Generator" content="Notepad++">
<meta name="Description" content="PayPal IPN Processor">
<meta name="Keywords" content="Nothing">

</head>


<body>

<!-- The code to deal with the PalPal logic: -->
<?php

   //The following is for the directory below publicly visible,
   //where the processing stuff is:
   $base = dirname(dirname(dirname(__FILE__)));
   define('BASE_PATH', $base == DIRECTORY_SEPARATOR ? $base : $base . DIRECTORY_SEPARATOR);
   require_once (BASE_PATH . "phpPayPalController.php");
   
   $ppc = new payPalController();
   $ppc->setTesting(false);
   $ppc->setLogging(false);
   $ppc->processPayPalIpnPayment();
   ?>


   <!-- THINGS TO DO WHEN GOING FROM TESTING TO PRODUCTION: -->
   <!-- Change 'testing' to false via the '$ppc->setTesting' line above -->
   <!-- Make certain buttons are coded correctly on site to access this page -->
   <!-- Make certain the phpPayPalIpnClass opens socket to proper secured server -->
   
   </body>
   </html>

如您所见,payPalController 是页面唯一需要了解的单元。控制器会自行设置其他类

<?php
//This should be placed in the subdirectory beneath the user-accessible
//one.  On localhost this can't be done, so remember to do it manually
//when copying to the site.

require_once(BASE_PATH . "phpPayPalIpnClass.php");
require_once(BASE_PATH . "phpLoggerClass.php");
require_once(BASE_PATH . "phpRmwStoreDbActions.php");
require_once(BASE_PATH . "phpMyMailClass.php");


class payPalController {
   private $db;
   private $logger;
   private $testing;


   function __construct() {
      $this->logger = new myLogger();
      $this->logger->setLogFile("log.txt");
      $this->logger->setLogging(false);
      $this->db = new rmwStoreDB();
      $this->db->setLogger($this->logger);
      $this->testing = false;
      }


   function setTesting($state) {
      $this->testing = $state;
      $this->logger->setLogging($state);
      }


   function setLogging($state) { $this->logger->setLogging($state); }


   function processPayPalIpnPayment() {
      $processor = new payPalIpn();
      $processor->setTesting($this->testing);
      $processor->setLogger($this->logger);
      $this->logger->log("Processing a payment." . PHP_EOL);

      if (!$this->validatePostData()) return;
      if (!$processor->processPost()) return;
      if ($this->duplicateTransaction()) return;
      if (!$this->verify()) return;
      if (!$this->addOrderToDatabase()) return;
      $this->sendProduct();
      }
      
      
   function validatePostData() {
      $step = 0;
      $ret = true;
      if(!isset($_POST['txn_id']))            $step = 1;
      if(!isset($_POST['shipping']))          $step = 2;
      if(!isset($_POST['quantity']))          $step = 3;
      if(!isset($_POST['mc_gross']))          $step = 4;
      if(!isset($_POST['mc_gross']))          $step = 5;
      if(!isset($_POST['last_name']))         $step = 6;
      if(!isset($_POST['first_name']))        $step = 7;
      if(!isset($_POST['address_zip']))       $step = 8;
      if(!isset($_POST['mc_currency']))       $step = 9;
      if(!isset($_POST['item_number']))       $step = 10;
      if(!isset($_POST['payer_email']))       $step = 11;
      if(!isset($_POST['address_city']))      $step = 12;
      if(!isset($_POST['address_name']))      $step = 13;
      if(!isset($_POST['address_state']))     $step = 14;
      if(!isset($_POST['address_street']))    $step = 15;
      if(!isset($_POST['receiver_email']))    $step = 16;
      if(!isset($_POST['payment_status']))    $step = 17;
      if(!isset($_POST['address_country']))   $step = 18;
      //if(!isset($_POST['option_selection1'])) $step = 19;
      
      if ($step != 0) $ret = false;

      if ($ret == false) {
         $this->logger->log("POST DATA not set: $step" . PHP_EOL);
         $response = "";
         
         foreach ($_POST as $key => $value) {
            $numPosts += 1;
            if($magicQuotesFuncExists == true && get_magic_quotes_gpc() == 1) {
               $value = urlencode(stripslashes($value));
               }
            else {
               $value = urlencode($value);
               }
            $response .= "&$key=$value" . PHP_EOL;
            }
         $this->logger->log($response);
         
         }
      return $ret;
      }


   private function duplicateTransaction() {
      $ret = false;
      if ($this->db->itemExists("orders", "payPalTransId",  $_POST['txn_id'])) {
         $this->logger->log("Transaction: " . $_POST['txn_id'] . " exists" . PHP_EOL);
         $ret = true;
         }

      else {
         $this->logger->log("Transaction: " . $_POST['txn_id'] .
                     " does not exist" . PHP_EOL);
         }
      return $ret;
      }


   private function verify() {
      $nl = PHP_EOL;
      //First, check for valid item number:
      if (!$this->db->itemExists("products", "id", $_POST ['item_number'])) {
         $this->logger->log("Item number: " . $_POST['item_number'] .
                     " doesn't exist in database$nl");
         return false;
         }
      else {
         $this->logger->log("Item number: " . $_POST['item_number'] .
                     " exists in database$nl");
         }
      //Check that we received the proper amount, or more, in the case of Texas taxes:
      $this->dbPrice = $this->db->getCellValue("price", "products", "id",
                  $_POST['item_number']);
      if ($_POST['mc_gross'] < $this->dbPrice) {
         $this->logger->log("Payment received (" . $_POST ['mc_gross'] .
                     ") less than item price. (" . $this->dbPrice . PHP_EOL);
         return false;
         }
      else {
         $this->logger->log("Adequate payment received (" . $_POST ['mc_gross'] .
                     ").$nl");
         }

      if ($_POST['mc_currency'] != "USD") {
         $this->logger->log("Paid in non-US funds - need to investigate.$nl");
         return false;
         }

      else {
         $this->logger->log("US Currency received - OK.$nl");
         }

      if ($_POST['receiver_email'] != "someone@somewhere.com"
                  && $_POST['receiver_email'] != "someone@somewhere.com") {
         $this->logger->log("Incorrect receiver email received (" .
                     $_POST['receiver_email'] . ")$nl");
         return false;
         }
      else {
         $this->logger->log("Correct email received (
                     " . $_POST['receiver_email'] . ")$nl");
         }

      //And the most important one:
      if ($_POST['payment_status'] != "Completed") {
         $this->logger->log("Payment incomplete from PayPal$nl");
         return false;
         }
      return true;
      }

   private function addOrderToDatabase() {
      //Everything will revolve around email address as of primary importance; if there is
      //one in the database the record will be updated to reflect any changes to the
      //account.  If one doesn't exist, one will be created.
      $this->logger->log("Updating database." . PHP_EOL);
      $this->db->addOrUpdateUser();
      $this->db->addOrder();
      return true;
      }


   private function sendProduct() {
      $nl = PHP_EOL;
      $mailHandler = new myMailer();
      $mailHandler->setLogger($this->logger);

      if ($this->testing) {
         $mailTo = 'someone@somewhere.com';
         }
      else {
         $mailTo = $_POST['payer_email'];
         }

      if ($_POST['item_number'] == "something") {
         doSomething(); //You get the idea...
         }
      }

   }
   
?>

在上面的代码块中,为了更漂亮的格式,我换行了一些可能需要在移植到您的网站时取消换行的行。它们很可能保持原样,但以防万一,这是值得注意的。另外,请记住更改本文档中使用的电子邮件地址。

这就到了 payPalIpn 类。如前所述,它负责 IPN 处理,并将正确的响应发送回 PayPal。

<?php

require_once(BASE_PATH . "phpLoggerClass.php");


class payPalIpn {

   private $logger;  //This will actually be a reference; see 'setLogger'
                     //for mechanism which accomplishes this.
   private $ipnVerifiedC;
   private $testingC;

   function __construct() {
      $this->ipnVerifiedC = false;
      $this->testingC = false;
      }


   function ipnVerified() { return $this->ipnVerifiedC; }


   function setTesting($state) { $this->testingC = $state; }


   function setLogger(myLogger &$logFile) { $this->logger = $logFile; }


   function processPost() {
      // Send an empty HTTP 200 OK response to acknowledge receipt of the notification 
      //header('HTTP/1.1 200 OK'); 
      $nl = PHP_EOL;
      //Log the contents if the logger is set to do so:
      $this->logger->log("RECEIVED:$nl" . var_export($_POST, true) . PHP_EOL);

      //Everything below here is originally from
      //http://designertuts.com/wp-content/uploads/2007/10/paypalipnphp.txt, which was said
      //to be a copy of the example code specified on the Paypal site.  Of course, I modified
      //formatting and necessary logic.  Quite a lot, actually.

      //Paypal POSTs HTML FORM variables to this page.  We must return all the variables
      //back to PayPal unchanged and add an extra parameter 'cmd 'with value
      //'_notify-validate'

      $response = 'cmd=_notify-validate';

      //Check for magic quotes (this comes from a PayPal pdf on IPN usage - don't remember
      //the URL):
      $magicQuotesFuncExists = false;
      if(function_exists('get_magic_quotes_gpc')) {
         $magicQuotesFuncExists = true;
         }

      //The following variable is used for a quick, dummy check to keep from sending a
      //response unless the POST was long, meaning it probably came from PayPal:
      $numPosts = 0;
      // go through each of the POSTed vars and add them to the variable
      foreach ($_POST as $key => $value) {
         $numPosts += 1;
         if($magicQuotesFuncExists == true && get_magic_quotes_gpc() == 1) {
            $value = urlencode(stripslashes($value));
            }
         else {
            $value = urlencode($value);
            }
         $response .= "&$key=$value";
         }
         
      $this->logger->log("AFTER MAGIC QUOTES:$nl".var_export($_POST, true).PHP_EOL);


      // post back to PayPal system to validate
      $header = "POST /cgi-bin/webscr HTTP/1.1$nl";
      $header .= "Host: www.sandbox.paypal.com$nl";
      $header .= "Content-Type: application/x-www-form-urlencoded$nl";
      $header .= "Content-Length: " . strlen($response) . PHP_EOL . PHP_EOL;

      //In a live application send it back to www.paypal.com, but during development use
      //the PayPal sandbox.  Paypal Sandbox only seems to accept using ssl connections,
      //and on port 443.

      if ($this->testingC) {
         $socket = fsockopen ('ssl://www.sandbox.paypal.com', 443, $socketErrNum,
                     $socketErrStr, 30);
         }
      else {
         $socket = fsockopen ('ssl://www.paypal.com', 443, $socketErrNum,
                     $socketErrStr, 30);
         }
      //Oldie:   $socket = fsockopen ('www.paypal.com', 80, $socketErrNum,
      //            $socketErrStr, 30);

      if ($socket) $this->logger->log("Socket successful$nl");
      else $this->logger->log("Socket failed!$nl");

      
      if (!$socket) {
         // HTTP ERROR Failed to connect.  Send me an email notification:
         $mail_Body = "Error from fsockopen:$nl" . $socketErrStr .
                     "$nl$nlOriginal PayPal Post Data (COULD BE BOGUS!)$nl$nl";
         foreach ($_POST as $key => $value) {
            $value = urlencode(stripslashes($value));
            $mail_Body .= "&$key=$value" . PHP_EOL;
            }
         mail($myEmail, "IPN Error Noficiation: Failed to connect to PayPal", $mail_Body,
                     "someone@somewhere.com");

         // The original code used "fwrite($fh, $socketErrStr)" for the following:
         $this->logger->log("Socket error: " . $socketErrStr);
         return;
         }

      //Now respond to PayPal's posting.  The previous 'if' statement returned
      //and terminated this if there was a socket error, so we can concentrate on what we
      //need.  First, send our response to the POST, and check for verification:

      $receivedVerification = false;
      $this->logger->log("Number of posts: $numPosts$nl");

      //Only send a response if it comes from PayPal.
      //The quick and dirty way to do so is see how many posts were in the original message:
      if ($numPosts > 3) {
         //Actually send the response:
         $this->logger->log("Sending the post back:$nl");
         fputs ($socket, $header . $response);

         $this->logger->log("SENT:$nl$nl$header$response$nl$nl");

         //And get their response.  First lines will be transaction data, finally followed by
         //'VERIFIED' or 'UNVERIFIED' (or something).  It took a
         //little work to find out that SSL had to be used for the Sandbox, and to get it
         //working correctly.  But the previous '$socket' initialization finally
         //succeeded.
         $receivedVerification = false;
         $endOfStreamReached = false;
         
         while (!feof($socket) && !$endOfStreamReached && !$receivedVerification) {
            $result = fgets ($socket, 1024);  //Get a line of response
            $this->logger->log("RECEIVED: $result");
            
            if (strncmp ($result, "VERIFIED", 8) == 0) {
               $receivedVerification = true;
               //$this->logger->log("VERIFIED! VERIFIED! VERIFIED!$nl");
               }
            //else {
            //   $this->logger->log("NOT VERIFIED HERE!$nl");
            //   }
             if (strncmp ($result, "0", 1) == 0) $endOfStreamReached = true;
            }
         }
      fclose ($socket);

      $result = false;
      if ($receivedVerification == false) {
         $this->logger->log(
            "$nl$nlINVALID TRANSACTION! (Improper PayPal response received)$nl");
         }
      else {
         $this->ipnVerifiedC = true;
         $this->logger->log("TRANSACTION VERIFIED!$nl");
         $result = true;
         }

      return $result;
      }

   }
   ?>

接下来是数据库处理。第一行的 _db_config.php_ 文件存储数据库用户名和密码信息。关键是 _db_config.php_ 不能被外部世界看到,这就是为什么您必须将其放在网站最顶层公共文件夹的下方。

<?php

require_once(BASE_PATH . "pp_db_config.php");
require_once(BASE_PATH . "phpLoggerClass.php");


class rmwStoreDB {

   private $loggerC; //This will actually be a reference;
                     //see 'setloggerC' for mechanism which accomplishes this.
   private $lastRowC;

   function setlogger(mylogger $logFile) { $this->loggerC = $logFile; }

   
   function tellDb($sql) {
      $ret = mysql_query($sql);
      if (!$ret) {
         $this->loggerC->log("DATABASE ERROR:" . PHP_EOL . mysql_error() . PHP_EOL .
                     "Query: " . HtmlEntities($sql));
         die();
         }
      return $ret;
      }

      
   function itemExists($table, $column, $value) {
      $rows = $this->tellDb("Select * from " . $table . " where " . $column . " = '" .
                  $value . "'");
      $lastRowC = mysql_fetch_array($rows);
      if ($lastRowC) return true;
      return false;
      }


   function getCellValue($what, $table, $column, $theId) {
      //No checking will be done to ensure only one row is returned, and default value
      //will be '0.00.'
      $rows = $this->tellDb("Select " . $what . " from " . $table . " where " . $column .
                  " = " . $theId);
      $row = mysql_fetch_array($rows);
      if ($row) return $row['price'];
      else return 0.00;
      }


   function addOrUpdateUser() {
      //We are using the $_POST data, which is global to the application.

      //These are the fields we are dealing with: 'first_name'  'last_name' 
      //'payer_email', and, for shipped products ONLY - 'address_name'
      //'address_street' (can be 2 lines separated by PHP_EOL?)  'address_city' 
      //'address_state'  'address_zip'  'address_country_code'  'address_country'

      //First, see if the user exists:
      $rows = $this->tellDb("Select * from customers where email = '" .
                  $_POST['payer_email'] . "'");
      //Simply use the first of the returned rows:
      $row = mysql_fetch_array($rows);
      if (!$row) {
         $this->loggerC->log("Adding user to database");
         $this->addUser();
         }
      else {
         $this->loggerC->log("User already exists in DB." . PHP_EOL);
         //See if the records match the ones we have, and if not, change it:
         $this->updateUser($row);
         }
      }


   private function addUser() {
      $cmd = "Insert into customers (firstName, lastName, shippingName, email," . 
               "addressLine1, city, state, zipCode, country) values ('"
               . $_POST['first_name'] .  "', '" .
               $_POST['last_name'] . "', '" .
               $_POST['address_name'] ."', '" .
               $_POST['payer_email'] . "', '" .
               $_POST['address_street'] . "', '" .
               $_POST['address_city'] . "', '" .
               $_POST['address_state'] . "', '" .
               $_POST['address_zip'] . "', '" .
               $_POST['address_country'] . "')";

      $this->tellDb($cmd);

      $this->loggerC->log("Added: '" . $_POST['first_name'] . "', '" .
               $_POST['last_name'] . "', '" .
               $_POST['address_name'] . "', '" .
               $_POST['payer_email'] . "', '" .
               $_POST['address_street'] . "', '" .
               $_POST['address_city'] . "', '" .
               $_POST['address_state'] . "', '" .
               $_POST['address_zip'] . "', '" .
               $_POST['address_country'] . "')");
      }


   private function updateUser(array $row) {
      //First, check old values:
      if ($row['firstName']    != $_POST['first_name']     ||
          $row['lastName']     != $_POST['last_name']      ||
          $row['shippingName'] != $_POST['address_name']   ||
          $row['email']        != $_POST['payer_email']    ||
          $row['addressLine1'] != $_POST['address_street'] ||
          $row['city']         != $_POST['address_city']   ||
          $row['state']        != $_POST['address_state']  ||
          $row['zipCode']      != $_POST['address_zip']    ||
          $row['country']      != $_POST['address_country']) {

         //Form a command string:
         $cmd = "UPDATE customers SET ";
         $cmd .= "firstName = '"    . $_POST['first_name']      . "', ";
         $cmd .= "lastName = '"     . $_POST['last_name']       . "', ";
         $cmd .= "shippingName = '" . $_POST['address_name']    . "', ";
         $cmd .= "addressLine1 = '" . $_POST['address_street']  . "', ";
         $cmd .= "city = '"         . $_POST['address_city']    . "', ";
         $cmd .= "state = '"        . $_POST['address_state']   . "', ";
         $cmd .= "zipCode = '"      . $_POST['address_zip']     . "', ";
         $cmd .= "country = '"      . $_POST['address_country'] . "' ";
         $cmd .= "WHERE email = '"  . $_POST['payer_email'] . "'";
         $this->loggerC->log(PHP_EOL . "Changing user with email " .
                     $_POST['payer_email'] . PHP_EOL);

         $old = $row['firstName']   . ", " . $row['lastName'] . ", " .
               $row['shippingName'] . ", " . $row['email']    . ", " .
               $row['addressLine1'] . ", " . $row['city']     . ", " . 
               $row['state']        . ", " . $row['zipCode']  . ", " .
               $row['country'] . PHP_EOL . PHP_EOL;

         $this->loggerC->log($old);
         $this->loggerC->log($cmd . PHP_EOL);

         $this->tellDb($cmd);
         }
      }


   function addOrder() {
      $nl = PHP_EOL;
      //Everything will be in the $_POST values
      //First, get the customer number:
      $cmd = "Select id from customers where email = '" . $_POST['payer_email'] . "'";
      //We just entered this information into the database, so error checking isn't
      //really needed? We will put something in just for grins.
      $rows = $this->tellDb($cmd);
      $row = mysql_fetch_array($rows);
      if (!$row) {
         $this->loggerC->log("HUGE PROBLEM! CUSTOMER ID NOT FOUND - ABORTING$nl");
         die();
         }
      $id = $row['id'];
      $theDate = date('F j, Y, g:i a');
      $tz = date('T');
      $ppID = $_POST['txn_id'];
      $grossPay = $_POST['mc_gross'];
      $shipping = $_POST['shipping'];
      $cmd = "Insert into orders (customer, date, timeZone, payPalTransId, grossPmt, " .
            "shipping) values ('$id', '$theDate', '$tz', '$ppID', '$grossPay', '$shipping')";

      $this->tellDb($cmd);
      $this->loggerC->log("Inserting order into orders table:$nl$cmd$nl$nl");

      //Now we have to add the order items into the orderItems table.
      //First, we need to get the order number from the record which was just entered:

      $cmd = "Select id from orders where payPalTransId = '$ppID'";
      $rows = $this->tellDb($cmd);
      $row = mysql_fetch_array($rows);
      //Should not need this, but...
      if (!$row) {
         $this->loggerC->log("HUGE PROBLEM! ORDER ID NOT FOUND - ABORTING$nl");
         die();
         }

      $id = $row['id'];
      //And the command to enter the item:
      $itemNum = $_POST['item_number'];
      $qty = $_POST['quantity'];
      $info = $_POST['option_selection1'];
      $cmd = "Insert into orderItems (orderNumber, item, quantity, extraInfo) " .
                  "values('$id', '$itemNum', '$qty', '$info')";
      $this->loggerC->log("Inserting into order items:$nl$cmd$nl");
      $this->tellDb($cmd);
      }

   }

?>

太棒了!下面的类比前面的小得多!其中第一个是邮件发送单元。当然,它的简洁性可能与它可以使用更多完善有关,而且我应该发送比现在更好的格式化的 HTML 响应给购买者。一切都会在适当的时候完成。

<?php

require_once(BASE_PATH . "pp_hiddenPasswords.php");
require_once(BASE_PATH . "phpLoggerClass.php");
require_once(BASE_PATH . "lib/swift_required.php");

class myMailer {

   private $myEmail;
   private $logger;  //This will actually be a reference;
                     //see 'setLogger' for mechanism which accomplishes this.
   
   
   function __construct() {
      $this->myEmail = "someone@somewhere.com";
      }
      
      
   function setLogger(myLogger &$logFile) { $this->logger = $logFile; }


   function sendEbook($ebookType, $mailTo) {
      //You can either send a link to the product or a file.  The choice is yours.
      //Refer to swift mailer documentation, and other online resources for the appropriate
      //steps to take for each option.  If you want to send a file, the 'mailWithAttachments'
      //routine may be useful.  The usage of it is:
      ...
      $this->mailWithAttachment($fileName, BASE_PATH, $mailTo, $this->myEmail, $from,
                  $replyTo, $subject, $msg);
      }

   
   private function mailWithAttachment($filename, $path, $mailTo, $from_mail, $from_name,
               $replyto, $subject, $message) {
   
      $transport = Swift_SmtpTransport::newInstance('mail.some_server.com', 465, 'ssl')
         ->setUsername(hiddenEmailAccount())
         ->setPassword(hiddenEmailPassword())
         ;
      $mailer = Swift_Mailer::newInstance($transport);
      $message = Swift_Message::newInstance()
         ->setSubject($subject)
         ->setFrom(array($from_mail => $from_name))
         ->setTo(array($mailTo))
         ->setBody($message)
         //->addPart('<p>Here is the message itself</p>', 'text/html')
         ->attach(Swift_Attachment::fromPath($path.$filename))
         ;
      $this->logger->forceLog(date('F jS\, Y h:i:s A') . ": Sending " . $filename .
                  " to " . $mailTo . ": ");
      $result = $mailer->send($message);
      $this->logger->forceLog("Result = " . $result . PHP_EOL);
      
      }
   }
?>

最后是日志类

<?php
   
class myLogger {

   private $fileNameC;
   private $doLoggingC;
   
   function __construct() {
      $this->fileNameC = "log.txt";
      $this->doLoggingC = true;
      }
   
   function setLogFile($fileName) { $this->fileNameC = $fileName; }
   function setLogging($state) { $this->doLoggingC = $state; }
   
   function log($msg) {
      if ($this->doLoggingC == true) {
         file_put_contents($this->fileNameC, $msg, FILE_APPEND);
         }
      }   
   
   function forceLog($msg) {
      file_put_contents($this->fileNameC, $msg, FILE_APPEND);
      }
      
   }
   
   ?>

但仍有两个文件是必需的,尽管您也可以将其合并成一个,并进行适当的修改。它们用于存储数据库信息和电子邮件信息。同样,重要的是不要将它们暴露给外部世界,并且必须将其放在您网站最顶层可见文件夹的下方。

_db_config.php_ 文件的内容如下。但那些不是真实的用户、密码和数据库!

<?php
// Connection Parameters

$db_con = mysql_connect("localhost", "theUser", "thePassword", true) or die(mysql_error());
$db_selected = mysql_select_db("theDatabase") or die(mysql_error());

?>

最后,电子邮件密码文件(上面代码中的“_emailPassword.php_”),具有相同的注意事项

<?php
// Connection Parameters

function hiddenEmailAccount() {
   return "someone@somewhere.com";
   }

function hiddenEmailPassword() {
   return "thePassword";
   }

?>

我希望以上信息对您有所帮助,也许即使您不采用这种方式。感谢您的阅读,祝您编码愉快!如果您有任何改进,请在评论中发布。

© . All rights reserved.