There are several issues with nopCommerce's implementation of Paypal Standard. This fixes just a few...
This will allow you to have your item description, price and quantity FOR EACH ITEM passed to Paypal so it will show up when the customer is checking out with Paypal Standard. It also fixes the mistake (?) that was in the file where the nopCommerce team omitted the zip code from the string. Without the zip code Paypal won't pre-fill the address because a zip code is required. So this allows the pre-filling of addresses to work the way is should as well.
In "PaypalStandardPaymentProcessor.cs" replace "public string PostProcessPayment(Order order)" with this:
public string PostProcessPayment(Order order)
{
StringBuilder builder = new StringBuilder();
string returnURL = CommonHelper.GetStoreLocation(false) + "PaypalPDTHandler.aspx";
string cancel_returnURL = CommonHelper.GetStoreLocation(false) + "PaypalCancel.aspx";
builder.Append(GetPaypalUrl());
builder.AppendFormat("?cmd=_cart&business={0}", HttpUtility.UrlEncode(businessEmail));
builder.AppendFormat("&upload=1");
//get the items in the cart
var cartItems = order.NpOrderProductVariants;
int x = 1;
foreach (var item in cartItems)
{
//get the productvariant so we can get the name
var pName = ProductManager.GetProductVariantById(item.ProductVariantId);
//set the name
string vName = pName.FullProductName;
builder.AppendFormat("&item_name_" + x +"={0}", HttpUtility.UrlEncode(vName)); //name
builder.AppendFormat("&amount_" + x + "={0}", item.UnitPriceExclTax); //amount
builder.AppendFormat("&quantity_" + x + "={0}", item.Quantity); //quantity
x++;
}
builder.AppendFormat("&custom={0}", order.OrderGuid);
builder.Append(string.Format("&no_note=1¤cy_code={0}", HttpUtility.UrlEncode(CurrencyManager.PrimaryStoreCurrency.CurrencyCode)));
builder.AppendFormat("&invoice={0}", order.OrderId);
builder.AppendFormat("&rm=2", new object[0]);
if (order.ShippingStatus != ShippingStatusEnum.ShippingNotRequired)
builder.AppendFormat("&no_shipping=2", new object[0]);
else
builder.AppendFormat("&no_shipping=1", new object[0]);
builder.AppendFormat("&shipping_1={0}", order.OrderShippingExclTax);
builder.AppendFormat("&tax_1={0}", order.OrderTax);
//can use "handling" for extra charges - will be added to "shipping & handling"
//builder.AppendFormat("&handling_1={0}", GetAdditionalHandlingFee());
builder.AppendFormat("&return={0}&cancel_return={1}", HttpUtility.UrlEncode(returnURL), HttpUtility.UrlEncode(cancel_returnURL));
builder.AppendFormat("&first_name={0}", HttpUtility.UrlEncode(order.BillingFirstName));
builder.AppendFormat("&last_name={0}", HttpUtility.UrlEncode(order.BillingLastName));
builder.AppendFormat("&address1={0}", HttpUtility.UrlEncode(order.BillingAddress1));
builder.AppendFormat("&address2={0}", HttpUtility.UrlEncode(order.BillingAddress2));
builder.AppendFormat("&city={0}", HttpUtility.UrlEncode(order.BillingCity));
StateProvince billingStateProvince = StateProvinceManager.GetStateProvinceById(order.BillingStateProvinceId);
if (billingStateProvince != null)
builder.AppendFormat("&state={0}", HttpUtility.UrlEncode(billingStateProvince.Abbreviation));
else
builder.AppendFormat("&state={0}", HttpUtility.UrlEncode(order.BillingStateProvince));
Country billingCountry = CountryManager.GetCountryById(order.BillingCountryId);
if (billingCountry != null)
builder.AppendFormat("&country={0}", HttpUtility.UrlEncode(billingCountry.TwoLetterIsoCode));
else
builder.AppendFormat("&country={0}", HttpUtility.UrlEncode(order.BillingCountry));
//to send phone number - work on this later
//Paypal requires email addressess and phone numbers from all customers, so it would be good to pre-fill them
//var phoneN = order.BillingPhoneNumber;
//doesn't look like phone number is formatted in any particular way... hard to parse
//builder.AppendFormat("&night_phone_a=123"); //area code
//builder.AppendFormat("&night_phone_b=555"); //prefix
//builder.AppendFormat("&night_phone_c=1111"); //last four
builder.AppendFormat("&email={0}", HttpUtility.UrlEncode(order.BillingEmail));
builder.AppendFormat("&zip={0}", HttpUtility.UrlEncode(order.BillingZipPostalCode));
HttpContext.Current.Response.Redirect(builder.ToString());
return string.Empty;
}
Then rebuild and replace your Paypal files in the BIN.
Another fix I would like (which I'll probably have to do myself) is that when you hit "confirm" (the last button before being sent to Paypal) the order is automatically placed with nopCommerce and an email is sent to the admin and the customer about the order that isn't even finished yet.
What happens if the customer closes out the browser before completing the transaction with Paypal? Now you have an order that is not paid for but has been created and emails have been sent out... a mess! I would like to see the order creation in nopCommerce happen after the customer is sent back to the site on the return link or after the payment has been confirmed with Paypal - I'll have to look into the best way.
Also... if the customer changes his/her mind on something and hits the "cancel and return" link on Paypal then they are taken back to the main page of the site and their cart is empty! What if they wanted to change their order? Now they have to do the order ALL OVER AGAIN - lost sale! I'd like to see the "cancel and return" link on Paypal return the user to their cart which still contains their not-yet-processed order. I'll have to look into that as well...
Hope this code helps some people... if you find it doesn't support something that it should, or is doesn't work like it should then please let me know (post here) and I'll see what I can do.
B