Prompt PayPal payment processing, part 3

Here's the remainder of the PHP code for the recent automation of my PayPal-based store, using PayPal's Instant Payment Notification (IPN) service. See part 1 for the introduction and part 2 for the start of the code.

So now we've got a valid transaction, so can begin to process it. As I previously said, I use a shopping cart, so I need to loop over the cart contents. I call the getPayPalShoppingCartValue() function (provided in part 2) to get values specific to a product; shared values can be copied easily. This should all work without a shopping cart, too. I save the values into a $license associative array, which is equivalent to a Cocoa NSDictionary.

    $count = $transaction['num_cart_items'];
   
    if ($count < 1)
        $count = 1;
   
    // Process each shopping cart item as a separate license:
    for ($index = 0; $index < $count; $index++)
    {
        // Construct the license:
        $license = array();
       
        $license['Parser'] = 'PayPal';
        $license['Parameters'] = $transaction;
        $license['Name'] = $transaction['first_name'] . " " . $transaction['last_name'];
        $license['Email'] = $transaction['payer_email'];
        $license['Postal'] = $transaction['address_street'] . ", " . $transaction['address_city'] . ", ";
        $license['Postal'] .= $transaction['address_state'] . " " . $transaction['address_zip'] . ", " . $transaction['address_country'];
        $license['PaymentAmount'] = getPayPalShoppingCartValue($transaction, 'mc_gross', $index);
        $license['TotalAmount'] = $transaction['mc_gross'];
        $license['Quantity'] = getPayPalShoppingCartValue($transaction, 'quantity', $index);
        $license['ProcessorNote'] = $transaction['payment_date'];
        $license['ProcessorTransID'] = $transaction['txn_id'];
        $license['ProcessorName'] = 'PayPal';
        $license['PaymentMethod'] = 'PayPal';
       
        $ref = getPayPalShoppingCartValue($transaction, 'option_selection1', $index);
       
        if (!$ref)
            $ref = 'paypal';
       
        $license['Referrer'] = $ref;
        $license['Special'] = getPayPalShoppingCartValue($transaction, 'option_selection2', $index);
       
        if ($testMode)
            $license['Test'] = 1;
       
        $license['ServerReferrer'] = $_SERVER['HTTP_REFERER'];
        $license['ServerIP'] = $_SERVER['REMOTE_ADDR'];
        $license['ServerAgent'] = $_SERVER['HTTP_USER_AGENT'];
       
        $itemName = getPayPalShoppingCartValue($transaction, 'item_name', $index);
        $isDonation = strcontains($itemName, 'Donation', true);
       
        setLicenseProduct($license, $itemName);

The setLicenseProduct() function simply maps the shopping cart's item name to my own product identifier.

Time to actually add the license. This is done with another function to be left as an exercise for the reader, addLicense(). It takes the $license array we just constructed, adds it to the license database, and returns (by reference) any error. If the sale was a donation for a freeware product or a pending sale, though, the add is bypassed, since a donation doesn't need a license, and a pending sale (if it reaches here) shouldn't be issued one.

We then construct an email to the customer, with different wording for freeware, pending, or normal sales. I'm omitting most of that, since it's specific to your situation:

        // Add the license, unless it's a donation or pending:
        if ($isDonation || $isPending || addLicense($license, $error))
        {
            $productName = $license['ProductName'];
            $version = $license['Version'];
            $name = $license['Name'];
            $email = $license['Email'];
           
            // Donations and pending transactions will use the item name for the product name,
            // as the latter is set in updateLicense(), which isn't called for them:
            if (!$productName)
                $productName = $itemName;
           
            if ($isDonation)
                $body = "Greetings.  Thank you for the $productName!  I appreciate it.\n\n\n";
            else if ($isPending)
            {
                $body = "Greetings.  Thank you for purchasing a $productName.\n\n\n";
                $body .= "Your PayPal transaction is still pending, so you will receive your license when the payment is complete.\n\n\n";
            }
            else
            {
                $body = "Greetings.  Thank you for purchasing $productName.\n\n\n";
                $body .= "Licensed Name: $name\n";
                $body .= "Licensed Email: $email\n";
                $body .= "License Kind: " . $license['KindName'] . "\n";
                $body .= "$productName $version Serial Number: " . $license['Serial'] . "\n\n\n";
                // Instructions on adding the license omitted...
            }
           
            // Info on forums, FAQ, email contacts, etc omitted...
           
            $headers = "From: MyCompany <sales@mycompany.com>\r\n";
            $headers .= "Bcc: MyCompany <sales@mycompany.com>\r\n";
            $headers .= "X-Mailer: PHP/" . phpversion() . "\r\n";
           
            if ($isDonation)
                $subject = $productName;
            else if ($isPending)
                $subject = "$productName purchase";
            else
                $subject = "$productName license";
           
            mail("$name <$email>", $subject, $body, $headers);
        }
        else if ($error)
            exitWithError('PayPal', $error, $license);
    }
   
    header("Content-Type: text/text");
    echo("Transaction completed.");
}

And we're done! I hope this is helpful. This all seems to work fine for me, but if you spot any bugs or have any suggestions for improvements, please let me know. If you have any questions, I'd be happy to elaborate more. I'd be keen to hear from people who use this code, too.