',
);
return $form;
}
}
// Process Instant Payment Notifiations from PayPal.
function uc_paypal_ipn($order_id = 0) {
watchdog('uc_paypal', t('Receiving IPN at URL for order @order_id.
@debug
',
array('@order_id' => $order_id, '@debug' => variable_get('uc_paypal_wps_debug_ipn', FALSE) ? print_r($_POST, TRUE) : '')));
if (!isset($_POST['invoice'])) {
watchdog('uc_paypal', t('IPN attempted with invalid order ID.'), WATCHDOG_ERROR);
return;
}
if (($len = strpos($_POST['invoice'], '-')) > 0) {
$order_id = intval(substr($_POST['invoice'], 0, $len));
}
else {
$order_id = intval($_POST['invoice']);
}
$order = uc_order_load($order_id);
if ($order == FALSE) {
watchdog('uc_paypal', t('IPN attempted for non-existent order.'), WATCHDOG_ERROR);
return;
}
// Assign posted variables to local variables
$payment_status = check_plain($_POST['payment_status']);
$payment_amount = check_plain($_POST['mc_gross']);
$payment_currency = check_plain($_POST['mc_currency']);
$payment_fee = check_plain($_POST['mc_fee']);
$receiver_email = check_plain($_POST['receiver_email']);
$txn_id = check_plain($_POST['txn_id']);
$txn_type = check_plain($_POST['txn_type']);
$payer_email = check_plain($_POST['payer_email']);
// Express Checkout IPNs may not have the WPS email stored. But if it is, make
// sure that the right account is being paid.
if (variable_get('uc_paypal_wps_email', '') && $receiver_email != variable_get('uc_paypal_wps_email', '')) {
watchdog('uc_paypal', t('IPN for a different PayPal account attempted.'), WATCHDOG_ERROR);
return;
}
$req = 'cmd=_notify-validate';
foreach ($_POST as $key => $value) {
$value = urlencode(stripslashes($value));
$req .= '&'. $key .'='. $value;
}
if (variable_get('uc_paypal_wpp_server', '') == 'https://api-3t.paypal.com/nvp') {
$host = 'https://www.paypal.com/cgi-bin/webscr';
}
else {
$host = variable_get('uc_paypal_wps_server', 'https://www.sandbox.paypal.com/cgi-bin/webscr');
}
$host = explode('/', substr($host, 8));
// Post back to PayPal to validate
$header = "POST /cgi-bin/webscr HTTP/1.0\r\n";
$header .= 'Host: '. $host[0] ."\r\n";
$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
$header .= 'Content-Length: '. strlen($req) ."\r\n\r\n";
// Address a screw-up on PayPal's Sandbox that prevents normal validation.
if (strpos($host[0], 'sandbox') !== FALSE && function_exists('openssl_open')) {
$fp = fsockopen('ssl://www.sandbox.paypal.com', 443, $errno, $errstr, 30);
}
else {
// The old "normal" way of validating an IPN.
$fp = fsockopen($host[0], 80, $errno, $errstr, 30);
}
if (!$fp) {
watchdog('uc_paypal', t('IPN failed with HTTP error.'), WATCHDOG_ERROR);
return;
}
fputs($fp, $header . $req);
while (!feof($fp)) {
$res = fgets($fp, 1024);
if (strcmp ($res, 'VERIFIED') == 0) {
watchdog('uc_paypal', t('IPN transaction verified.'));
$duplicate = db_result(db_query("SELECT COUNT(*) FROM {uc_payment_paypal_ipn} WHERE txn_id = '%s' AND status != 'Pending'", $txn_id));
if ($duplicate > 0) {
if ($order->payment_method != 'credit') {
watchdog('uc_paypal', t('IPN transaction ID has been processed before.'), WATCHDOG_NOTICE);
}
fclose($fp);
return;
}
db_query("INSERT INTO {uc_payment_paypal_ipn} VALUES (%d, '%s', '%s', '%s', "
."'%s', '%s', '%s', %d)", $order_id, $txn_id, $txn_type,
$payment_amount, $payment_status, $receiver_email, $payer_email,
time());
/*if (variable_get('uc_paypal_wps_email', '') != $receiver_email) {
watchdog('uc_paypal', t('IPN received for an e-mail that is not your primary e-mail: @email', array('@email' => $receiver_email)));
}*/
switch ($payment_status) {
case 'Canceled_Reversal':
uc_order_comment_save($order_id, 0, t('PayPal has cancelled the reversal and returned !amount !currency to your account.', array('!amount' => uc_currency_format($payment_amount, FALSE), '!currency' => $payment_currency)), 'admin');
break;
case 'Completed':
if ($payment_amount != $order->order_total) {
watchdog('uc_paypal', t('Payment @txn_id for order @order_id did not equal the order total.', array('@txn_id' => $txn_id, '@order_id' => $order->order_id)), WATCHDOG_WARNING, l(t('view'), 'admin/store/orders/'. $order->order_id));
}
$comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $txn_id));
uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
uc_cart_complete_sale($order);
uc_order_comment_save($order_id, 0, t('Payment of !amount !currency submitted through PayPal.', array('!amount' => uc_currency_format($payment_amount, FALSE), '!currency' => $payment_currency)), 'order', 'payment_received');
uc_order_comment_save($order_id, 0, t('PayPal IPN reported a payment of !amount !currency.', array('!amount' => uc_currency_format($payment_amount, FALSE), '!currency' => $payment_currency)));
break;
case 'Denied':
uc_order_comment_save($order_id, 0, t("You have denied the customer's payment."), 'admin');
break;
case 'Expired':
uc_order_comment_save($order_id, 0, t('The authorization has failed and cannot be captured.'), 'admin');
break;
case 'Failed':
uc_order_comment_save($order_id, 0, t("The customer's attempted payment from a bank account failed."), 'admin');
break;
case 'Pending':
uc_order_update_status($order_id, 'paypal_pending');
uc_order_comment_save($order_id, 0, t('Payment is pending at PayPal: @reason', array('@reason' => _uc_paypal_pending_message($_POST['pending_reason']))), 'admin');
break;
// You, the merchant, refunded the payment.
case 'Refunded':
$comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $txn_id));
uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
break;
case 'Reversed':
watchdog('uc_paypal', t('PayPal has reversed a payment!'), WATCHDOG_ERROR);
uc_order_comment_save($order_id, 0, t('Payment has been reversed by PayPal: @reason', array('@reason' => _uc_paypal_reversal_message($_POST['reason_code']))), 'admin');
break;
case 'Processed':
uc_order_comment_save($order_id, 0, t('A payment has been accepted.'), 'admin');
break;
case 'Voided':
uc_order_comment_save($order_id, 0, t('The authorization has been voided.'), 'admin');
break;
}
}
elseif (strcmp($res, 'INVALID') == 0) {
watchdog('uc_paypal', t('IPN transaction failed verification.'), WATCHDOG_ERROR);
uc_order_comment_save($order_id, 0, t('An IPN transaction failed verification for this order.'), 'admin');
}
}
fclose ($fp);
}
// Handles a complete Website Payments Standard sale.
function uc_paypal_complete($order_id = 0) {
// If the order ID specified in the return URL is not the same as the one in
// the user's session, we need to assume this is either a spoof or that the
// user tried to adjust the order on this side while at PayPal. If it was a
// legitimate checkout, the IPN will still come in from PayPal so the order
// gets processed correctly. We'll leave an ambiguous message just in case.
if (intval($_SESSION['cart_order']) != $order_id) {
drupal_set_message(t('Thank you for your order! We will be notified by PayPal that we have received your payment.'));
drupal_goto('cart');
}
if (!($order = uc_order_load($order_id)) || $order->payment_method != 'paypal_wps') {
drupal_goto('cart');
}
// This lets us know it's a legitimate access of the complete page.
$_SESSION['do_complete'] = TRUE;
drupal_goto('cart/checkout/complete');
}
// Handles a canceled Website Payments Standard sale.
function uc_paypal_cancel() {
unset($_SESSION['cart_order']);
drupal_set_message(t('Your PayPal.com payment was cancelled. Please feel free to continue shopping or contact us for assistance.'));
drupal_goto('cart');
}
// Handles the review page for Express Checkout Mark Flow.
function uc_paypal_ec_review_redirect() {
if (!isset($_SESSION['TOKEN']) || ($order = uc_order_load($_SESSION['cart_order'])) == FALSE) {
unset($_SESSION['cart_order']);
unset($_SESSION['have_details']);
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
drupal_set_message(t('An error has occurred in your PayPal payment. Please review your cart and try again.'));
drupal_goto('cart');
}
$nvp_request = array(
'METHOD' => 'GetExpressCheckoutDetails',
'TOKEN' => $_SESSION['TOKEN'],
);
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
$_SESSION['PAYERID'] = $nvp_response['PAYERID'];
drupal_goto('cart/checkout/review');
}
// Handles the review page for Express Checkout Shortcut Flow.
function uc_paypal_ec_review() {
if (!isset($_SESSION['TOKEN']) || ($order = uc_order_load($_SESSION['cart_order'])) == FALSE) {
unset($_SESSION['cart_order']);
unset($_SESSION['have_details']);
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
drupal_set_message(t('An error has occurred in your PayPal payment. Please review your cart and try again.'));
drupal_goto('cart');
}
if ($_SESSION['have_details'][$order->order_id] !== TRUE) {
$nvp_request = array(
'METHOD' => 'GetExpressCheckoutDetails',
'TOKEN' => $_SESSION['TOKEN'],
);
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
$_SESSION['PAYERID'] = $nvp_response['PAYERID'];
$shipname = check_plain($nvp_response['SHIPTONAME']);
if (strpos($shipname, ' ') > 0) {
$order->delivery_first_name = substr($shipname, 0, strrpos(trim($shipname), ' '));
$order->delivery_last_name = substr($shipname, strrpos(trim($shipname), ' ') + 1);
}
else {
$order->delivery_first_name = $shipname;
$order->delivery_last_name = '';
}
$zone_id = db_result(db_query("SELECT zone_id FROM {uc_zones} WHERE zone_code = '%s'", check_plain($nvp_response['SHIPTOSTATE'])));
$country_id = db_result(db_query("SELECT country_id FROM {uc_countries} WHERE country_iso_code_2 = '%s'", check_plain($nvp_response['SHIPTOCOUNTRYCODE'])));
$order->delivery_street1 = check_plain($nvp_response['SHIPTOSTREET']);
$order->delivery_city = check_plain($nvp_response['SHIPTOCITY']);
$order->delivery_zone = !empty($zone_id) ? $zone_id : 0;
$order->delivery_postal_code = check_plain($nvp_response['SHIPTOZIP']);
$order->delivery_country = !empty($country_id) ? $country_id : 840;
$order->billing_first_name = check_plain($nvp_response['FIRSTNAME']);
$order->billing_last_name = check_plain($nvp_response['LASTNAME']);
$order->billing_street1 = check_plain($nvp_response['EMAIL']);
if (empty($order->primary_email)) {
$order->primary_email = $nvp_response['EMAIL'];
}
$order->payment_method = 'paypal_ec';
uc_order_save($order);
$_SESSION['have_details'][$order->order_id] = TRUE;
}
$output = t("Your order is almost complete! Please fill in the following details and click 'Continue checkout' to finalize the purchase.");
$output .= drupal_get_form('uc_paypal_ec_review_form', $order);
return $output;
}
// Present the final total to the user for checkout!
function uc_paypal_ec_submit() {
if (!isset($_SESSION['TOKEN']) || ($order = uc_order_load($_SESSION['cart_order'])) == FALSE) {
unset($_SESSION['cart_order'], $_SESSION['have_details']);
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
drupal_set_message(t('An error has occurred in your PayPal payment. Please review your cart and try again.'));
drupal_goto('cart');
}
drupal_add_css(drupal_get_path('module', 'uc_cart') .'/uc_cart.css');
$output = '
'. t("Your order is not complete until you click the 'Submit order' button below. Your PayPal account will be charged for the amount shown above once your order is placed. You will receive confirmation once your payment is complete.") .'
';
$output .= drupal_get_form('uc_paypal_ec_submit_form');
return $output;
}
/*******************************************************************************
* Module and Helper Functions
******************************************************************************/
// Redirects if a customer selects PayPal Express Checkout as a payment method.
function uc_paypal_ec_checkout($form_id, $form_values) {
if ($form_values['panes']['payment']['payment_method'] != 'paypal_ec') {
return;
}
$order_id = intval($_SESSION['cart_order']);
$order = uc_order_load($order_id);
if ($order === FALSE || uc_order_status_data($order->order_status, 'state') != 'in_checkout') {
$_SESSION['cart_order'] = NULL;
unset($_SESSION['cart_order']);
drupal_goto('cart');
}
list($desc, $subtotal) = _uc_paypal_product_details($order->products);
$country = uc_get_country_data(array('country_id' => $order->billing_country));
if ($country === FALSE) {
$country = array(0 => array('country_iso_code_2' => 'US'));
}
$nvp_request = array(
'METHOD' => 'SetExpressCheckout',
'RETURNURL' => url('cart/echeckout/selected', NULL, NULL, TRUE),
'CANCELURL' => url('uc_paypal/wps/cancel', NULL, NULL, TRUE),
'AMT' => uc_currency_format($order->order_total, FALSE, FALSE, '.'),
'CURRENCYCODE' => variable_get('uc_paypal_wpp_currency', 'USD'),
'PAYMENTACTION' => variable_get('uc_paypal_wpp_payment_action', 'Sale'),
'DESC' => substr($desc, 0, 127),
'INVNUM' => $order->order_id .'-'. time(),
'REQCONFIRMSHIPPING' => variable_get('uc_paypal_ec_rqconfirmed_addr', 0),
'ADDROVERRIDE' => 1,
'BUTTONSOURCE' => 'Ubercart_ShoppingCart_EC_US',
'NOTIFYURL' => url('uc_paypal/ipn/'. $order->order_id, NULL, NULL, TRUE),
'SHIPTONAME' => substr($order->delivery_first_name .' '. $order->delivery_last_name, 0, 32),
'SHIPTOSTREET' => substr($order->delivery_street1, 0, 100),
'SHIPTOSTREET2' => substr($order->delivery_street2, 0, 100),
'SHIPTOCITY' => substr($order->delivery_city, 0, 40),
'SHIPTOSTATE' => uc_get_zone_code($order->delivery_zone),
'SHIPTOCOUNTRYCODE' => $country[0]['country_iso_code_2'],
'SHIPTOZIP' => substr($order->delivery_postal_code, 0, 20),
'PHONENUM' => substr($order->delivery_phone, 0, 20),
);
if (!uc_cart_is_shippable()) {
$nvp_request['NOSHIPPING'] = 1;
unset($nvp_request['ADDROVERRIDE']);
}
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
if ($nvp_response['ACK'] != 'Success') {
drupal_set_message(t('Error message from PayPal: @message', array('@message' => $nvp_response['L_LONGMESSAGE0'])), 'error');
drupal_goto('cart/checkout');
}
$_SESSION['TOKEN'] = $nvp_response['TOKEN'];
if (strpos(variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'), 'sandbox') > 0) {
$sandbox = 'sandbox.';
}
header('Location: https://www.'. $sandbox .'paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='. $_SESSION['TOKEN']);
exit();
}
// Returns the form for Express Checkout Shortcut Flow.
function uc_paypal_ec_form() {
if (!variable_get('uc_cap_uc_paypal_ec_enabled', FALSE)) {
return;
}
if (!function_exists('curl_init')) {
drupal_set_message(t('PayPal Express Checkout requires curl for PHP. Please talk to your system administrator to get this.'));
return;
}
// Hack to allow the image button to submit.
if (isset($_POST['submit_x'])) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => 'submit',
);
}
$form['submit_image'] = array(
'#value' => '',
);
return $form;
}
function uc_paypal_ec_form_submit($form_id, $form_values) {
global $user;
$items = uc_cart_get_contents();
if (!is_array($items) || count($items) == 0) {
drupal_set_message(t('You do not have any items in your shopping cart.'));
return;
}
list($desc, $subtotal) = _uc_paypal_product_details($items);
$order = uc_order_new($user->uid);
$nvp_request = array(
'METHOD' => 'SetExpressCheckout',
'RETURNURL' => url('cart/echeckout/review', NULL, NULL, TRUE),
'CANCELURL' => url('uc_paypal/wps/cancel', NULL, NULL, TRUE),
'AMT' => uc_currency_format($subtotal, FALSE, FALSE, '.'),
'CURRENCYCODE' => variable_get('uc_paypal_wpp_currency', 'USD'),
'PAYMENTACTION' => variable_get('uc_paypal_wpp_payment_action', 'Sale'),
'DESC' => substr($desc, 0, 127),
'INVNUM' => $order->order_id .'-'. time(),
'REQCONFIRMSHIPPING' => variable_get('uc_paypal_ec_rqconfirmed_addr', 0),
'BUTTONSOURCE' => 'Ubercart_ShoppingCart_EC_US',
'NOTIFYURL' => url('uc_paypal/ipn/'. $order->order_id, NULL, NULL, TRUE),
);
$order->products = $items;
uc_order_save($order);
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
$_SESSION['cart_order'] = $order->order_id;
$_SESSION['TOKEN'] = $nvp_response['TOKEN'];
if (strpos(variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'), 'sandbox') > 0) {
$sandbox = 'sandbox.';
}
header('Location: https://www.'. $sandbox .'paypal.com/cgi-bin/webscr?cmd=_express-checkout&token='. $_SESSION['TOKEN']);
exit();
}
// Returns the form for the custom Review Payment screen for Express Checkout.
function uc_paypal_ec_review_form($order) {
if (module_exists('uc_quote') && variable_get('uc_paypal_ec_review_shipping', TRUE) && uc_order_is_shippable($order)) {
$result = uc_checkout_pane_quotes('view', $order, NULL);
$form['panes']['quotes'] = array(
'#type' => 'fieldset',
'#title' => t('Calculate shipping'),
'#collapsible' => FALSE,
);
$form['panes']['quotes'] += $result['contents'];
$form['panes']['quotes']['quote_button']['#value'] = t('Click to refresh shipping options');
$form['panes']['quotes']['quote_div'] = array(
'#value' => '',
);
uc_add_js("\$(document).ready(function() { \$(' #edit-quote-button').click(); });", 'inline');
// Fake the checkout form delivery information.
$form['delivery_first_name'] = array('#type' => 'hidden', '#value' => $order->delivery_first_name);
$form['delivery_last_name'] = array('#type' => 'hidden', '#value' => $order->delivery_last_name);
$form['delivery_company'] = array('#type' => 'hidden', '#value' => $order->delivery_company);
$form['delivery_street1'] = array('#type' => 'hidden', '#value' => $order->delivery_street1);
$form['delivery_street2'] = array('#type' => 'hidden', '#value' => $order->delivery_street2);
$form['delivery_city'] = array('#type' => 'hidden', '#value' => $order->delivery_city);
$form['delivery_postal_code'] = array('#type' => 'hidden', '#value' => $order->delivery_postal_code);
$form['delivery_phone'] = array('#type' => 'hidden', '#value' => $order->delivery_phone);
$form['delivery_zone'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array($order->delivery_zone)),
'#default_value' => $order->delivery_zone,
'#attributes' => array('style' => 'display: none;'),
);
$form['delivery_country'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array($order->delivery_country)),
'#default_value' => $order->delivery_country,
'#attributes' => array('style' => 'display: none;'),
);
$form['shippable'] = array('#type' => 'hidden', '#value' => 'true');
}
if (variable_get('uc_paypal_ec_review_company', TRUE)) {
$form['delivery_company'] = array(
'#type' => 'textfield',
'#title' => uc_get_field_name('company'),
'#description' => uc_order_is_shippable($order) ? t('Leave blank if shipping to a residence.') : '',
'#default_value' => $order->delivery_company,
);
}
if (variable_get('uc_paypal_ec_review_phone', TRUE)) {
$form['delivery_phone'] = array(
'#type' => 'textfield',
'#title' => t('Contact phone number'),
'#default_value' => $order->delivery_phone,
'#size' => 24,
);
}
if (variable_get('uc_paypal_ec_review_comment', TRUE)) {
$form['order_comments'] = array(
'#type' => 'textarea',
'#title' => t('Order comments'),
'#description' => t('Special instructions or notes regarding your order.'),
);
}
if (empty($form)) {
drupal_goto('cart/echeckout/submit');
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Continue checkout'),
);
return $form;
}
function uc_paypal_ec_review_form_validate($form_id, $form_values) {
if ($form_values['shippable'] == 'true') {
if (!isset($_POST['quote-option'])) {
form_set_error('shipping', t('You must calculate and select a shipping option.'));
}
}
}
function uc_paypal_ec_review_form_submit($form_id, $form_values) {
$order = uc_order_load($_SESSION['cart_order']);
if ($form_values['shippable'] == 'true') {
$method_accessorials= split("---", isset($_POST['quote-option']) ? strval($_POST['quote-option']) : 0);
$order->quote['method'] = $method_accessorials[0];
$order->quote['accessorials'] = $method_accessorials[1];
$order->quote['rate'] = $_POST['rate'][isset($_POST['quote-option']) ? strval($_POST['quote-option']) : 0];
$order->quote['quote_form'] = rawurldecode($_POST['quote-form']);
$methods = module_invoke_all('shipping_method');
$method = $methods[$order->quote['method']];
$label = is_null($_POST['quote-option']) ? t('Error calculating shipping') : $method['quote']['accessorials'][$order->quote['accessorials']];
$result = db_query("SELECT line_item_id FROM {uc_order_line_items} WHERE order_id = %d AND type = 'shipping'", $order->order_id);
if ($lid = db_result($result)) {
uc_order_update_line_item($lid, $label, $order->quote['rate']);
}
else {
uc_order_line_item_add($order->order_id, 'shipping', $label, $order->quote['rate']);
}
}
if (variable_get('uc_paypal_ec_review_company', TRUE)) {
$order->delivery_company = $form_values['delivery_company'];
}
if (variable_get('uc_paypal_ec_review_phone', TRUE)) {
$order->delivery_phone = $form_values['delivery_phone'];
}
if (variable_get('uc_paypal_ec_review_comment', TRUE)) {
db_query("DELETE FROM {uc_order_comments} WHERE order_id = %d", $order->order_id);
uc_order_comment_save($order->order_id, 0, $form_values['order_comments'], 'order');
}
uc_order_save($order);
drupal_goto('cart/echeckout/submit');
}
// Submits an order, calling the NVP API to send the order total to PayPal.
function uc_paypal_ec_submit_form() {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit order'),
);
return $form;
}
function uc_paypal_ec_submit_form_submit($form_id, $form_values) {
if ($form_values['op'] == t('Back')) {
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
return;
}
$order = uc_order_load($_SESSION['cart_order']);
list($desc, $subtotal) = _uc_paypal_product_details($order->products);
$shipping = 0;
if (is_array($order->line_items)) {
foreach ($order->line_items as $item) {
if ($item['type'] == 'shipping') {
$shipping += $item['amount'];
}
}
}
$tax = 0;
if (module_exists('uc_taxes')) {
foreach (uc_taxes_calculate($order) as $tax_item) {
$tax += $tax_item['amount'];
}
}
$subtotal = $order->order_total - $tax - $shipping;
$country = uc_get_country_data(array('country_id' => $order->billing_country));
if ($country === FALSE) {
$country = array(0 => array('country_iso_code_2' => 'US'));
}
$nvp_request = array(
'METHOD' => 'DoExpressCheckoutPayment',
'TOKEN' => $_SESSION['TOKEN'],
'PAYMENTACTION' => variable_get('uc_paypal_wpp_payment_action', 'Sale'),
'PAYERID' => $_SESSION['PAYERID'],
'AMT' => uc_currency_format($order->order_total, FALSE, FALSE, '.'),
'DESC' => substr($desc, 0, 127),
'INVNUM' => $order->order_id .'-'. time(),
'BUTTONSOURCE' => 'Ubercart_ShoppingCart_EC_US',
'NOTIFYURL' => url('uc_paypal/ipn/'. $order->order_id, NULL, NULL, TRUE),
'ITEMAMT' => uc_currency_format($subtotal, FALSE, FALSE, '.'),
'SHIPPINGAMT' => uc_currency_format($shipping, FALSE, FALSE, '.'),
'TAXAMT' => uc_currency_format($tax, FALSE, FALSE, '.'),
'CURRENCYCODE' => variable_get('uc_paypal_wpp_currency', 'USD'),
);
$nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
unset($_SESSION['TOKEN'], $_SESSION['PAYERID']);
$_SESSION['do_complete'] = TRUE;
drupal_goto('cart/checkout/complete');
}
// Returns the form elements for the Website Payments Standard form.
function uc_paypal_wps_form($order) {
$shipping = 0;
foreach ($order->line_items as $item) {
if ($item['type'] == 'shipping') {
$shipping += $item['amount'];
}
}
$tax = 0;
if (module_exists('uc_taxes')) {
foreach (uc_taxes_calculate($order) as $tax_item) {
$tax += $tax_item['amount'];
}
}
$address = variable_get('uc_paypal_wps_address_selection', 'billing');
$country = uc_get_country_data(array('country_id' => $order->{$address .'_country'}));
if ($country === FALSE) {
$country = array(0 => array('country_iso_code_2' => 'US'));
}
for ($i = 0; $i < strlen($order->{$address .'_phone'}); $i++) {
if (is_numeric($order->{$address .'_phone'}[$i])) {
$phone .= $order->{$address .'_phone'}[$i];
}
}
/**
* night_phone_a: The area code for U.S. phone numbers, or the country code
* for phone numbers outside the U.S.
* night_phone_b: The three-digit prefix for U.S. phone numbers, or the
* entire phone number for phone numbers outside the U.S.,
* excluding country code.
* night_phone_c: The four-digit phone number for U.S. phone numbers.
* (Not Used for UK numbers)
**/
if ($country[0]['country_iso_code_2'] == 'US' || $country[0]['country_iso_code_2'] == 'CA') {
$phone = substr($phone, -10);
$phone_a = substr($phone, 0, 3);
$phone_b = substr($phone, 3, 3);
$phone_c = substr($phone, 6, 4);
}
$data = array(
// PayPal command variable
'cmd' => '_cart',
// IPN control notify URL
'notify_url' => url('uc_paypal/ipn/'. $order->order_id, NULL, NULL, TRUE),
// Display information
'cancel_return' => url('uc_paypal/wps/cancel', NULL, NULL, TRUE),
'no_note' => 1,
'no_shipping' => variable_get('uc_paypal_wps_no_shipping', 1),
'return' => url('uc_paypal/wps/complete/'. $order->order_id, NULL, NULL, TRUE),
'rm' => 2,
// Transaction information
'currency_code' => variable_get('uc_paypal_wps_currency', 'USD'),
'handling_cart' => uc_currency_format($shipping, FALSE, FALSE, '.'),
'invoice' => $order->order_id .'-'. time(),
'tax_cart' => uc_currency_format($tax, FALSE, FALSE, '.'),
// Shopping cart specific variables
'business' => variable_get('uc_paypal_wps_email', ''),
'upload' => 1,
'lc' => variable_get('uc_paypal_wps_language', 'US'),
// Prepopulating forms/address overriding
'address1' => substr($order->{$address .'_street1'}, 0, 100),
'address2' => substr($order->{$address .'_street2'}, 0, 100),
'city' => substr($order->{$address .'_city'}, 0, 40),
'country' => $country[0]['country_iso_code_2'],
'email' => $order->primary_email,
'first_name' => substr($order->{$address .'_first_name'}, 0, 32),
'last_name' => substr($order->{$address .'_last_name'}, 0, 64),
'state' => uc_get_zone_code($order->{$address .'_zone'}),
'zip' => $order->{$address .'_postal_code'},
'night_phone_a' => $phone_a,
'night_phone_b' => $phone_b,
'night_phone_c' => $phone_c,
);
if (variable_get('uc_paypal_wps_address_override', TRUE)) {
$data['address_override'] = 1;
}
// Account for stores that just want to authorize funds instead of capture.
if (variable_get('uc_paypal_wps_payment_action', 'Sale') == 'Authorization') {
$data['paymentaction'] = 'authorization';
}
if (variable_get('uc_paypal_wps_submit_method', 'single') == 'itemized') {
// List individual items
$i = 0;
foreach ($order->products as $item) {
$i++;
$data['amount_'. $i] = uc_currency_format($item->price, FALSE, FALSE, '.');
$data['item_name_'. $i] = $item->title;
$data['item_number_'. $i] = $item->model;
$data['quantity_'. $i] = $item->qty;
// PayPal will only display the first two...
if (is_array($item->data['attributes']) && count($item->data['attributes']) > 0) {
$o = 0;
foreach ($item->data['attributes'] as $name => $setting) {
$data['on'. $o .'_'. $i] = $name;
$data['os'. $o .'_'. $i] = $setting;
$o++;
}
}
}
}
else {
// List the whole cart as a single item to account for fees/discounts
$data['amount_1'] = uc_currency_format($order->order_total - $shipping - $tax, FALSE, FALSE, '.');
$data['item_name_1'] = t('Order @order_id at @store', array('@order_id' => $order->order_id, '@store' => variable_get('uc_store_name', url('', NULL, NULL, TRUE))));
$data['on0_1'] = t('Product count');
$data['os0_1'] = count($order->products);
}
$form['#action'] = variable_get('uc_paypal_wps_server', 'https://www.sandbox.paypal.com/cgi-bin/webscr');
foreach ($data as $name => $value) {
if (!empty($value)) {
$form[$name] = array('#type' => 'hidden', '#value' => $value);
}
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => variable_get('uc_paypal_wps_checkout_button', t('Submit Order')),
);
return $form;
}
// Sends a request to PayPal and returns a response array.
function uc_paypal_api_request($request, $server) {
$request['USER'] = variable_get('uc_paypal_api_username', '');
$request['PWD'] = variable_get('uc_paypal_api_password', '');
$request['VERSION'] = '3.0';
$request['SIGNATURE'] = variable_get('uc_paypal_api_signature', '');
$data = '';
foreach ($request as $key => $value) {
$data .= $key .'='. urlencode(ereg_replace(',', '', $value)) .'&';
}
$data = substr($data, 0, -1);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $server);
curl_setopt($ch, CURLOPT_VERBOSE, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_NOPROGRESS, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION,0);
$response = curl_exec($ch);
if ($error = curl_error($ch)) {
watchdog('uc_paypal', $error, WATCHDOG_ERROR);
}
curl_close($ch);
return _uc_paypal_nvp_to_array($response);
}
// Returns the description and subtotal of the products on an order.
function _uc_paypal_product_details($items) {
$desc = '';
$subtotal = 0;
if (!empty($items)) {
foreach ($items as $item) {
if (!empty($desc)) {
$desc .= ' / ';
}
$desc .= $item->qty .'x '. $item->title;
$subtotal += $item->qty * $item->price;
}
}
return array($desc, $subtotal);
}
// Returns the PayPal approved credit card type for a card number.
function _uc_paypal_card_type($cc_number) {
switch (substr(strval($cc_number), 0, 1)) {
case '3':
return 'Amex';
case '4':
return 'Visa';
case '5':
return 'MasterCard';
case '6':
return 'Discover';
}
return FALSE;
}
// Turns PayPal's NVP response to an API call into an associative array.
function _uc_paypal_nvp_to_array($nvpstr) {
foreach (explode('&', $nvpstr) as $nvp) {
list($key, $value) = explode('=', $nvp);
$nvp_array[urldecode($key)] = urldecode($value);
}
return $nvp_array;
}
// Returns a human readable message for the AVS code.
function _uc_paypal_avscode_message($code) {
if (is_numeric($code)) {
switch ($code) {
case '0':
return t('All the address information matched.');
case '1':
return t('None of the address information matched; transaction declined.');
case '2':
return t('Part of the address information matched.');
case '3':
return t('The merchant did not provide AVS information. Not processed.');
case '4':
return t('Address not checked, or acquirer had no response. Service not available.');
case 'Null':
default:
return t('No AVS response was obtained.');
}
}
switch ($code) {
case 'A':
case 'B':
return t('Address matched; postal code did not');
case 'C':
case 'N':
return t('Nothing matched; transaction declined');
case 'D':
case 'F':
case 'X':
case 'Y':
return t('Address and postal code matched');
case 'E':
return t('Not allowed for MOTO transactions; transaction declined');
case 'G':
return t('Global unavailable');
case 'I':
return t('International unavailable');
case 'P':
case 'W':
case 'Z':
return t('Postal code matched; address did not');
case 'R':
return t('Retry for validation');
case 'S':
return t('Service not supported');
case 'U':
return t('Unavailable');
default:
return t('An unknown error occurred.');
}
}
// Returns a human readable message for the CVV2 match code.
function _uc_paypal_cvvmatch_message($code) {
if (is_numeric($code)) {
switch ($code) {
case '0':
return t('Matched');
case '1':
return t('No match');
case '2':
return t('The merchant has not implemented CVV2 code handling.');
case '3':
return t('Merchant has indicated that CVV2 is not present on card.');
case '4':
return t('Service not available');
default:
return t('Unkown error');
}
}
switch ($code) {
case 'M':
return t('Match');
case 'N':
return t('No match');
case 'P':
return t('Not processed');
case 'S':
return t('Service not supported');
case 'U':
return t('Service not available');
case 'X':
return t('No response');
default:
return t('Not checked');
}
}
// Return a message for the pending reason of a PayPal payment.
function _uc_paypal_pending_message($reason) {
switch ($reason) {
case 'address':
return t('Customer did not include a confirmed shipping address per your address settings.');
case 'authorization':
return t('Waiting on you to capture the funds per your authorization settings.');
case 'echeck':
return t('eCheck has not yet cleared.');
case 'intl':
return t('You must manually accept or deny this international payment from your Account Overview.');
case 'multi-currency':
return t('You must manually accept or deny a payment of this currency from your Account Overview.');
case 'unilateral':
return t('Your e-mail address is not yet registered or confirmed.');
case 'upgrade':
return t('You must upgrade your account to Business or Premier status to receive credit card payments.');
case 'verify':
return t('You must verify your account before you can accept this payment.');
case 'other':
default:
return t('Reason unknown; contact PayPal Customer Service for more information.');
}
}
// Return a message for the reason code of a PayPal reversal.
function _uc_paypal_reversal_message($reason) {
switch ($reason) {
case 'chargeback':
return t('The customer has initiated a chargeback.');
case 'guarantee':
return t('The customer triggered a money-back guarantee.');
case 'buyer-complaint':
return t('The customer filed a complaint about the transaction.');
case 'refund':
return t('You gave the customer a refund.');
case 'other':
default:
return t('Reason unknown; contact PayPal Customer Service for more information.');
}
}
// Returns an array of possible currency codes.
function _uc_paypal_currency_array() {
return drupal_map_assoc(array('AUD', 'CAD', 'CHF', 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HUF', 'JPY', 'NOK', 'NZD', 'PLN', 'SEK', 'SGD', 'USD'));
}