uc_recurring_hosted.module

Tracking 6.x-2.x branch
  1. drupal
    1. 6 contributions/uc_recurring/modules/uc_recurring_hosted/uc_recurring_hosted.module

Provides hosted gateway specific code for recurring payments, specifically Authorize.net ARB and Paypal WPS

Constants

NameDescription
UC_PAYPAL_RECURRING_API
UC_RECURRING_FEE_STATUS_SUSPENDED_NOPROFILE

Functions & methods

NameDescription
uc_recurring_hosted_authorizenet_arb_cancelCancel the recurring fee using the ARB api.
uc_recurring_hosted_authorizenet_arb_processSet up the recurring fee using the ARB API.
uc_recurring_hosted_authorizenet_arb_renewRecurring fee renew callback for Authorize.net ARB.
uc_recurring_hosted_authorizenet_arb_update_formCreate form for updating credit card details for recurring fee.
uc_recurring_hosted_authorizenet_arb_update_form_submitImplements update form submit for the authorizenet ARB gateway.
uc_recurring_hosted_form_uc_paypal_wps_form_alterPaypal website payments standard process
uc_recurring_hosted_menuImplementation of hook_menu().
uc_recurring_hosted_paypal_cancel_formDisplay info on canceling the Paypal WPS subscription.
uc_recurring_hosted_paypal_wpp_cancelPayPal website payments pro renew.
uc_recurring_hosted_paypal_wpp_get_profileHelper function; Get a recurring payments profile from PayPal.
uc_recurring_hosted_paypal_wpp_processPayPal website payments pro process.
uc_recurring_hosted_paypal_wpp_profile_creation_form
uc_recurring_hosted_paypal_wpp_profile_creation_form_submit
uc_recurring_hosted_paypal_wpp_renewSetup a mock renew handler to process through UC Recurring.
uc_recurring_hosted_paypal_wpp_suspension_logSuspends a user's profile due to failed payment.
uc_recurring_hosted_paypal_wpp_updateUpdates a PayPal subscription; for simplicity's sake, Credit Card information except for expiration date cannot be updated at this time.
uc_recurring_hosted_paypal_wpp_update_formForm for updating the user credit card information
uc_recurring_hosted_paypal_wpp_update_form_submitUpdate form submission callback
uc_recurring_hosted_paypal_wps_processCreate the recurring fee.
uc_recurring_hosted_paypal_wps_renewSetup a mock renew handler to process through UC Recurring.
uc_recurring_hosted_recurring_accessImplementation of hook_recurring_access().
uc_recurring_hosted_recurring_infoImplementation of hook_recurring_info().
uc_recurring_hosted_subscription_deleteDelete a record by subscription ID or recurring fee ID.
uc_recurring_hosted_subscription_loadGet a hosted subscription ID and recurring fee ID.
uc_recurring_hosted_subscription_saveSave a new subscription ID
uc_recurring_hosted_uc_auth_arb_paymentImplementation of hook_uc_auth_arb_payment(). Called during an ARB silent post.
uc_recurring_hosted_uc_recurring_status_alterAdd a status label for "suspended (no agreement)"

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Provides hosted gateway specific code for recurring payments, specifically
  5. * Authorize.net ARB and Paypal WPS
  6. */
  7. define('UC_PAYPAL_RECURRING_API', '63.0');
  8. define('UC_RECURRING_FEE_STATUS_SUSPENDED_NOPROFILE', 20);
  9. /******************************************************************************
  10. * DRUPAL HOOKS
  11. *****************************************************************************/
  12. /**
  13. * Implementation of hook_menu().
  14. */
  15. function uc_recurring_hosted_menu() {
  16. $items = array();
  17. if (module_exists('uc_paypal')) {
  18. $items['uc_recurring_hosted/paypal/ipn'] = array(
  19. 'title' => 'PayPal IPN',
  20. 'page callback' => 'uc_recurring_hosted_paypal_ipn',
  21. 'access callback' => 'uc_paypal_ipn_access',
  22. 'type' => MENU_CALLBACK,
  23. 'file' => 'uc_recurring_hosted.paypal_ipn.inc'
  24. );
  25. $items['uc_recurring_hosted/paypal/ipn/%'] = array(
  26. 'title' => 'PayPal IPN',
  27. 'page callback' => 'uc_recurring_hosted_paypal_ipn',
  28. 'page arguments' => array(3),
  29. 'access callback' => 'uc_paypal_ipn_access',
  30. 'type' => MENU_CALLBACK,
  31. 'file' => 'uc_recurring_hosted.paypal_ipn.inc'
  32. );
  33. // A mock url for emulating the paypal IPN.
  34. $items['uc_recurring_hosted/paypal/cgi-bin/webscr'] = array(
  35. 'title' => 'PayPal Website',
  36. 'page callback' => '_uc_recurring_hosted_paypal_mock_web_page',
  37. 'access callback' => TRUE,
  38. 'type' => MENU_CALLBACK,
  39. 'file' => 'uc_recurring_hosted.paypal_ipn.inc'
  40. );
  41. }
  42. return $items;
  43. }
  44. /******************************************************************************
  45. * UBERCART HOOKS (including uc_recurring)
  46. *****************************************************************************/
  47. /**
  48. * Implementation of hook_recurring_info().
  49. */
  50. function uc_recurring_hosted_recurring_info() {
  51. // AUTHORIZE.NET ARB
  52. $items['authorizenet_arb'] = array(
  53. 'name' => t('Authorize.net (ARB)'),
  54. 'payment method' => 'credit',
  55. 'module' => 'uc_recurring_hosted',
  56. 'fee handler' => 'authorizenet_arb',
  57. 'process callback' => 'uc_recurring_hosted_authorizenet_arb_process',
  58. 'renew callback' => 'uc_recurring_hosted_authorizenet_arb_renew',
  59. 'cancel callback' => 'uc_recurring_hosted_authorizenet_arb_cancel',
  60. 'menu' => array(
  61. 'update' => array(
  62. 'title' => 'Update Account Details',
  63. 'page arguments' => array('uc_recurring_hosted_authorizenet_arb_update_form'),
  64. ),
  65. 'cancel' => UC_RECURRING_MENU_DEFAULT,
  66. ),
  67. 'own handler' => TRUE,
  68. );
  69. if (variable_get('uc_authnet_arb_mode', 'disabled') != 'disabled') {
  70. $items['authorizenet'] = $items['authorizenet_arb'];
  71. }
  72. // Paypal website payments standard.
  73. if (module_exists('uc_paypal')) {
  74. $items['paypal_wps'] = array(
  75. 'name' => t('Paypal website payments standard'),
  76. 'payment method' => 'paypal_wps',
  77. 'fee handler' => 'paypal_wps',
  78. 'module' => 'uc_recurring_hosted',
  79. 'process callback' => 'uc_recurring_hosted_paypal_wps_process',
  80. 'renew callback' => 'uc_recurring_hosted_paypal_wps_renew',
  81. 'own handler' => TRUE,
  82. 'menu' => array(
  83. 'cancel' => array(
  84. 'title' => t('Cancel'),
  85. 'page arguments' => array('uc_recurring_hosted_paypal_cancel_form'),
  86. ),
  87. ),
  88. );
  89. // PayPal Website Payments Pro
  90. $items['paypal_wpp'] = array(
  91. 'name' => t('PayPal website payments pro'),
  92. 'payment method' => 'credit',
  93. 'fee handler' => 'paypal_wpp',
  94. 'module' => 'uc_recurring_hosted',
  95. 'process callback' => 'uc_recurring_hosted_paypal_wpp_process',
  96. 'renew callback' => 'uc_recurring_hosted_paypal_wpp_renew',
  97. 'cancel callback' => 'uc_recurring_hosted_paypal_wpp_cancel',
  98. 'menu' => array(
  99. 'charge' => UC_RECURRING_MENU_DISABLED,
  100. 'edit' => UC_RECURRING_MENU_DISABLED,
  101. 'view agreement' => UC_RECURRING_MENU_DISABLED,
  102. 'create profile' => array(
  103. 'title' => t('Create billing profile'),
  104. 'page arguments' => array('uc_recurring_hosted_paypal_wpp_profile_creation_form'),
  105. ),
  106. 'update' => array(
  107. 'title' => t('Update account details'),
  108. 'page arguments' => array('uc_recurring_hosted_paypal_wpp_update_form'),
  109. ),
  110. 'reactivate' => array(
  111. 'title' => t('Reactivate account'),
  112. 'page arguments' => array('uc_recurring_hosted_paypal_wpp_reactivate_form'),
  113. ),
  114. 'cancel' => UC_RECURRING_MENU_DEFAULT,
  115. ),
  116. 'own handler' => TRUE,
  117. );
  118. }
  119. return $items;
  120. }
  121. /**
  122. * Get a hosted subscription ID and recurring fee ID.
  123. *
  124. * @param $id
  125. * The ID.
  126. * @param $type
  127. * The type of the get request. can be "rfid" for recurring fee ID, or
  128. * "subscription_id" for the subscription ID.
  129. * @return
  130. * Object with the subscription ID and recurring fee ID, or an empty array if
  131. * none found.
  132. */
  133. function uc_recurring_hosted_subscription_load($id, $type = 'rfid') {
  134. $return = array();
  135. if ($type == 'rfid') {
  136. $return = db_fetch_object(db_query("SELECT * FROM {uc_recurring_hosted} WHERE rfid = %d", $id));
  137. }
  138. elseif ($type == 'subscription_id') {
  139. $return = db_fetch_object(db_query("SELECT * FROM {uc_recurring_hosted} WHERE subscription_id = '%s'", $id));
  140. }
  141. drupal_alter('uc_recurring_hosted_subscription', $object);
  142. return $return;
  143. }
  144. /**
  145. * Save a new subscription ID
  146. */
  147. function uc_recurring_hosted_subscription_save($rfid, $subscription_id) {
  148. // Delete an existing record.
  149. uc_recurring_hosted_subscription_delete($rfid);
  150. $object = new stdClass;
  151. $object->rfid = $rfid;
  152. $object->subscription_id = $subscription_id;
  153. module_invoke_all('uc_recurring_hosted_subscription_save', $object);
  154. drupal_write_record('uc_recurring_hosted', $object);
  155. }
  156. /**
  157. * Delete a record by subscription ID or recurring fee ID.
  158. *
  159. * @param $id
  160. * The ID.
  161. * @param $type
  162. * The type of the get request. can be "rfid" for recuring fee ID, or
  163. * "subscription" for the subscription ID.
  164. */
  165. function uc_recurring_hosted_subscription_delete($id, $type = 'rfid') {
  166. module_invoke_all('uc_recurring_hosted_subscription_delete', $id, $type);
  167. if ($type == 'rfid') {
  168. $return = db_query("DELETE FROM {uc_recurring_hosted} WHERE rfid = %d", $id);
  169. }
  170. else {
  171. $return = db_query("DELETE FROM {uc_recurring_hosted} WHERE subscription_id = '%s'", $id);
  172. }
  173. }
  174. /**
  175. * Implementation of hook_recurring_access().
  176. */
  177. function uc_recurring_hosted_recurring_access($fee, $op, $account){
  178. if ($fee->fee_handler == 'paypal_wpp') {
  179. switch ($op) {
  180. case 'view agreement':
  181. if (!user_access('administer recurring fees') ||
  182. $fee->status == UC_RECURRING_FEE_STATUS_SUSPENDED_NOPROFILE ||
  183. $fee->status == UC_RECURRING_FEE_STATUS_CANCELLED) {
  184. return UC_RECURRING_ACCESS_DENY;
  185. }
  186. break;
  187. case 'reactivate':
  188. // Check whether the profile is inactive
  189. if ($fee->status == UC_RECURRING_FEE_STATUS_SUSPENDED) {
  190. return UC_RECURRING_ACCESS_ALLOW;
  191. }
  192. else {
  193. return UC_RECURRING_ACCESS_DENY;
  194. }
  195. break;
  196. case 'update':
  197. if ($fee->status == UC_RECURRING_FEE_STATUS_ACTIVE) {
  198. return UC_RECURRING_ACCESS_ALLOW;
  199. }
  200. else {
  201. return UC_RECURRING_ACCESS_DENY;
  202. }
  203. break;
  204. case 'create profile':
  205. if ($fee->status != UC_RECURRING_FEE_STATUS_SUSPENDED_NOPROFILE) {
  206. return UC_RECURRING_ACCESS_DENY;
  207. }
  208. break;
  209. }
  210. }
  211. // TODO: Consider if this can be made part of uc_recurring_user_access().
  212. if ($op == 'cancel') {
  213. if ($fee->status == UC_RECURRING_FEE_STATUS_CANCELLED) {
  214. return UC_RECURRING_ACCESS_DENY;
  215. }
  216. }
  217. }
  218. /**
  219. * Add a status label for "suspended (no agreement)"
  220. */
  221. function uc_recurring_hosted_uc_recurring_status_alter(&$labels) {
  222. $labels[UC_RECURRING_FEE_STATUS_SUSPENDED_NOPROFILE] = t('Suspended (no agreement)');
  223. }
  224. /******************************************************************************
  225. * AUTHORIZE.NET ARB
  226. *****************************************************************************/
  227. /**
  228. * Set up the recurring fee using the ARB API.
  229. *
  230. * @param $order
  231. * The order object.
  232. * @param $fee
  233. * The fee object.
  234. * @return
  235. * TRUE if recurring fee setup
  236. */
  237. function uc_recurring_hosted_authorizenet_arb_process($order, &$fee) {
  238. $fee->fee_handler = 'authorizenet_arb';
  239. // We can't just call the existing arb function in ubercart as that would try
  240. // and setup thing that uc_recurring does now for all recurring fees.
  241. // return uc_authorizenet_arb_create($order, $fee);
  242. $server = variable_get('uc_authnet_arb_mode', 'disabled');
  243. // Setup variables for the payment schedule.
  244. list($length, $unit) = explode(' ', $fee->regular_interval);
  245. list($trial_length, $trial_unit) = explode(' ', $fee->initial_charge);
  246. // Convert weeks and years to days.
  247. if ($unit == 'weeks') {
  248. $length *= 7;
  249. $unit = 'days';
  250. }
  251. elseif ($unit == 'years') {
  252. $length *= 365;
  253. $unit = 'days';
  254. }
  255. // Make sure we have valid values for Authorize.Net.
  256. if ($length <= 0 || $unit == 'days' && $length > 365 || $unit == 'months' && $length > 12) {
  257. watchdog('uc_authorizenet', 'Product @sku has invalid interval settings for Authorize.Net - @length @unit', array('@sku' => $fee->title, '@length' => $length, '@unit' => $unit), WATCHDOG_ERROR);
  258. return FALSE;
  259. }
  260. // Get the country data for the billing and shipping information.
  261. $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
  262. $delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
  263. // Build the data array for the request.
  264. $data = array(
  265. 'refId' => substr($order->order_id .'-'. time(), 0, 20),
  266. 'subscription' => array(
  267. 'name' => substr(t('Order @order_id', array('@order_id' => $order->order_id)), 0, 50),
  268. 'paymentSchedule' => array(
  269. 'interval' => array(
  270. 'length' => $length,
  271. 'unit' => $unit,
  272. ),
  273. 'startDate' => date('Y-m-d', $fee->next_charge),
  274. 'totalOccurrences' => $fee->remaining_intervals == -1 ? 9999 : $fee->remaining_intervals,
  275. 'trialOccurrences' => '0',
  276. ),
  277. 'amount' => round($fee->fee_amount, 2),
  278. 'trialAmount' => 0,
  279. 'payment' => array(), // Data inserted below based on payment method.
  280. 'order' => array(
  281. 'invoiceNumber' => substr($order->order_id, 0, 20),
  282. 'description' => substr(t('Order @order_id - @sku', array('@order_id' => $order->order_id, '@sku' => $fee->model)), 0, 255),
  283. ),
  284. 'customer' => array(
  285. 'id' => substr($order->uid, 0, 20),
  286. 'email' => substr($order->primary_email, 0, 255),
  287. 'phoneNumber' => substr($order->billing_phone, 0, 25),
  288. ),
  289. 'billTo' => array(
  290. 'firstName' => substr($order->billing_first_name, 0, 50),
  291. 'lastName' => substr($order->billing_last_name, 0, 50),
  292. 'company' => substr($order->billing_company, 0, 50),
  293. 'address' => substr($order->billing_street1, 0, 60),
  294. 'city' => substr($order->billing_city, 0, 40),
  295. 'state' => substr(uc_get_zone_code($order->billing_zone), 0, 2),
  296. 'zip' => substr($order->billing_postal_code, 0, 20),
  297. 'country' => !$billing_country ? '' : $billing_country[0]['country_iso_code_2'],
  298. ),
  299. 'shipTo' => array(
  300. 'firstName' => substr($order->delivery_first_name, 0, 50),
  301. 'lastName' => substr($order->delivery_last_name, 0, 50),
  302. 'company' => substr($order->delivery_company, 0, 50),
  303. 'address' => substr($order->delivery_street1, 0, 60),
  304. 'city' => substr($order->delivery_city, 0, 40),
  305. 'state' => substr(uc_get_zone_code($order->delivery_zone), 0, 2),
  306. 'zip' => substr($order->delivery_postal_code, 0, 20),
  307. 'country' => !$delivery_country ? '' : $delivery_country[0]['country_iso_code_2'],
  308. ),
  309. ),
  310. );
  311. // Strip out the shipping info if it isn't necessary.
  312. if (empty($data['subscription']['shipTo']['firstName'])) {
  313. unset($data['subscription']['shipTo']);
  314. }
  315. // Add the payment information to the data array based on the payment method.
  316. if ($order->payment_method == 'credit') {
  317. if ($order->payment_details['cc_exp_month'] < 10) {
  318. $order->payment_details['cc_exp_month'] = '0'. $order->payment_details['cc_exp_month'];
  319. }
  320. $data['subscription']['payment'] = array(
  321. 'creditCard' => array(
  322. 'cardNumber' => $order->payment_details['cc_number'],
  323. 'expirationDate' => $order->payment_details['cc_exp_year'] .'-'. $order->payment_details['cc_exp_month'],
  324. ),
  325. );
  326. }
  327. // Build the XML string.
  328. $xml = _uc_authorizenet_xml_api_wrapper('ARBCreateSubscriptionRequest', _uc_authorizenet_array_to_xml($data));
  329. // Send the request off to the server and get the response.
  330. $response = uc_authorizenet_xml_api($server, $xml);
  331. // Fail if the response is empty or FALSE.
  332. if (!$response) {
  333. return FALSE;
  334. }
  335. // Parse the response into a data array.
  336. $data = _uc_authorizenet_arb_parse_response($response);
  337. if ($data['resultCode'] == 'Error') {
  338. uc_order_comment_save($order->order_id, 0, t('Authorize.Net: Recurring fee for @model failed.<br />@error - @text', array('@model' => $fee->model, '@error' => $data['code'], '@text' => $data['text'])), 'admin');
  339. return FALSE;
  340. }
  341. uc_order_comment_save($order->order_id, 0, t('Authorize.Net: ARB subscription created - @id', array('@id' => $data['subscriptionId'])));
  342. // We need to save the rfid, we don't have it yet.
  343. if (!$fee->rfid) {
  344. $fee->rfid = uc_recurring_fee_user_save($fee);
  345. }
  346. // Save the new credit reference to the database.
  347. uc_recurring_hosted_subscription_save($fee->rfid, $data['subscriptionId']);
  348. $order->data = uc_credit_log_reference($order->order_id, $data['subscriptionId'], $order->payment_details['cc_number']);
  349. return TRUE;
  350. }
  351. /**
  352. * Recurring fee renew callback for Authorize.net ARB.
  353. *
  354. * @param $order
  355. * The reneal order object.
  356. * @param $fee
  357. * The recurring fee object.
  358. *
  359. * @return
  360. * Always returns TRUE.
  361. */
  362. function uc_recurring_hosted_authorizenet_arb_renew($order, $fee) {
  363. uc_payment_enter($order->order_id, $order->payment_method, $_POST['x_amount'], $fee->uid, NULL, t('Authorize.net ARB subsription ID: @subscription_id', array('@subscription_id' => $_POST['x_subscription_id'])));
  364. return TRUE;
  365. }
  366. /**
  367. * Implementation of hook_uc_auth_arb_payment().
  368. * Called during an ARB silent post.
  369. */
  370. function uc_recurring_hosted_uc_auth_arb_payment($post) {
  371. $fee = uc_recurring_hosted_subscription_load($post['x_subscription_id'], 'subscription_id');
  372. if (!empty($fee)) {
  373. $fee = uc_recurring_fee_user_load($fee->rfid);
  374. uc_recurring_renew($fee);
  375. // Log the ARB payment if enabled.
  376. if (variable_get('uc_authnet_report_arb_post', FALSE)) {
  377. watchdog('uc_authorizenet', 'ARB payment reported for order @order_id: <pre>@post</pre>', array('@order_id' => $fee->order_id, '@post' => print_r($post, TRUE)));
  378. }
  379. }
  380. }
  381. /**
  382. * Cancel the recurring fee using the ARB api.
  383. *
  384. * @param $order
  385. * The order object.
  386. * @param $fee
  387. * The fee object.
  388. * @return
  389. * TRUE if recurring fee was cancelled.
  390. */
  391. function uc_recurring_hosted_authorizenet_arb_cancel($order, &$fee) {
  392. $server = variable_get('uc_authnet_arb_mode', 'disabled');
  393. $order = uc_order_load($order->order_id);
  394. $subscription_id = end(array_keys($order->data['cc_txns']['references']));
  395. // Build the data array for the request.
  396. $data = array(
  397. 'refId' => substr($order->order_id .'-'. time(), 0, 20),
  398. 'subscriptionId' => $subscription_id,
  399. );
  400. // Build the XML string.
  401. $xml = _uc_authorizenet_xml_api_wrapper('ARBCancelSubscriptionRequest', _uc_authorizenet_array_to_xml($data));
  402. // Send the request off to the server and get the response.
  403. $response = uc_authorizenet_xml_api($server, $xml);
  404. // Fail if the response is empty or FALSE.
  405. if (!$response) {
  406. return FALSE;
  407. }
  408. // Parse the response into a data array.
  409. $data = _uc_authorizenet_arb_parse_response($response);
  410. if ($data['resultCode'] == 'Error') {
  411. if (!empty($order_id)) {
  412. uc_order_comment_save($order_id, 0, t('Authorize.Net: Subscription @subscription_id cancellation failed.<br />@error - @text', array('@subscription_id' => $subscription_id, '@error' => $data['code'], '@text' => $data['text'])), 'admin');
  413. }
  414. return FALSE;
  415. }
  416. uc_order_comment_save($order_id, 0, t('Authorize.Net: Subscription @subscription_id cancelled.', array('@subscription_id' => $subscription_id)), 'admin');
  417. return TRUE;
  418. }
  419. /**
  420. * Create form for updating credit card details for recurring fee.
  421. */
  422. function uc_recurring_hosted_authorizenet_arb_update_form($form_state, $rfid) {
  423. $form['rfid'] = array(
  424. '#type' => 'value',
  425. '#value' => $rfid,
  426. );
  427. $form['cc_data'] = array(
  428. '#type' => 'fieldset',
  429. '#title' => t('Credit card details'),
  430. '#theme' => 'uc_payment_method_credit_form',
  431. '#tree' => TRUE,
  432. );
  433. $form['cc_data'] += uc_payment_method_credit_form(array(), $order);
  434. unset($form['cc_data']['cc_policy']);
  435. $form['submit'] = array(
  436. '#type' => 'submit',
  437. '#value' => t('Update'),
  438. '#suffix' => l(t('Cancel'), 'user/'. $user->uid),
  439. );
  440. return $form;
  441. }
  442. /**
  443. * Implements update form submit for the authorizenet ARB gateway.
  444. */
  445. function uc_recurring_hosted_authorizenet_arb_update_form_submit(&$form, &$form_state) {
  446. $fee = uc_recurring_fee_user_load($form_state['values']['rfid']);
  447. $order = uc_order_load($fee->order_id);
  448. $order->payment_details = $form_state['values']['cc_data'];
  449. $subscription_id = end(array_keys($order->data['cc_txns']['references']));
  450. if ($order->payment_details['cc_exp_month'] < 10) {
  451. $order->payment_details['cc_exp_month'] = '0'. $order->payment_details['cc_exp_month'];
  452. }
  453. // Build the data array for the request.
  454. $updates = array(
  455. 'payment' => array(
  456. 'creditCard' => array(
  457. 'cardNumber' => $order->payment_details['cc_number'],
  458. 'expirationDate' => $order->payment_details['cc_exp_year'] .'-'. $order->payment_details['cc_exp_month'],
  459. ),
  460. ),
  461. );
  462. if (uc_authorizenet_arb_update($subscription_id, $updates, $order->order_id)) {
  463. drupal_set_message(t('Account updated.'));
  464. $form_state['redirect'] = 'user/'. $form_state['values']['uid'];
  465. }
  466. else {
  467. drupal_set_message(t('Account update failed.'), 'error');
  468. }
  469. }
  470. /******************************************************************************
  471. * PAYPAL WPS
  472. *****************************************************************************/
  473. /**
  474. * Create the recurring fee.
  475. */
  476. function uc_recurring_hosted_paypal_wps_process($order, &$fee) {
  477. // the recurring payment is setup at the time of product purchase
  478. if (!empty($_POST['subscr_id'])) {
  479. $fee->data['subscr_id'] = $_POST['subscr_id'];
  480. return TRUE;
  481. }
  482. return FALSE;
  483. }
  484. /**
  485. * Paypal website payments standard process
  486. *
  487. * Normally in a payment gateway we would just need to define the callback 'process':
  488. * e.g. function uc_recurring_hosted_wps_process($order, $fee)
  489. *
  490. * But paypal_wps is implemented by altering the checkout review form to
  491. * change the form so it is submitted directly to paypal, we are using this
  492. * same trick to alter the paypal form, its a messy hack but works for now.
  493. */
  494. function uc_recurring_hosted_form_uc_paypal_wps_form_alter(&$form, $form_state) {
  495. $order = $form['#parameters'][2];
  496. $recurring_fees = array();
  497. // if recurring fees exist in the order
  498. if (module_exists('uc_recurring_product')) {
  499. // uc_recurring_products support
  500. $recurring_fees = uc_recurring_product_get_recurring_products_in_order($order);
  501. }
  502. if (count($recurring_fees) > 0) {
  503. // TODO: what do we do if someone tries to order more then one subscription??
  504. // we will just process the first for now
  505. $recurring_fee = $recurring_fees[0];
  506. if (count($recurring_fees) > 1) {
  507. drupal_set_message(t('Sorry recurring payments can only be setup for one product at a time when paying via paypal, only the first recurring payment will be setup when you click on Submit order.'), 'warning');
  508. }
  509. // IPN control notify URL
  510. $data['notify_url'] = url('uc_recurring_hosted/paypal/ipn/'. $order->order_id, array('absolute' => TRUE));
  511. $data['cmd'] = '_xclick-subscriptions';
  512. $data['item_name'] = t('Order @order_id at !store', array('@order_id' => $order->order_id, '!store' => variable_get('uc_store_name', url('<front>', array('absolute' => TRUE)))));
  513. // first payment
  514. list($p, $t) = explode(' ', $recurring_fee['recurring product']->initial_charge);
  515. $data['a1'] = sprintf("%0.2f", $order->order_total);
  516. $data['p1'] = $p;
  517. $data['t1'] = strtoupper($t[0]);
  518. // recurring payments
  519. list($p, $t) = explode(' ', $recurring_fee['recurring product']->regular_interval);
  520. $data['a3'] = sprintf("%0.2f", $recurring_fee['recurring product']->fee_amount == 0 ? $recurring_fee['product']->price : $recurring_fee['recurring product']->fee_amount);
  521. $data['p3'] = $p;
  522. $data['t3'] = strtoupper($t[0]);
  523. $data['src'] = 1;
  524. if ($recurring_fee['recurring product']->number_intervals > 0) {
  525. $data['srt'] = $recurring_fee['recurring product']->number_intervals;
  526. }
  527. $data['sra'] = 1; // reattempt failed payments
  528. foreach ($data as $name => $value) {
  529. if (!empty($value)) {
  530. $form[$name] = array('#type' => 'hidden', '#value' => $value);
  531. }
  532. }
  533. }
  534. }
  535. /**
  536. * Display info on canceling the Paypal WPS subscription.
  537. */
  538. function uc_recurring_hosted_paypal_cancel_form($form_state, $rfid) {
  539. $form['message'] = array(
  540. '#value' => t('You can cancel your subscription from your paypal account, see <a href="@link">here</a> for instructions.', array('@link' => 'https://www.paypal.com/helpcenter/main.jsp?cmd=_help&t=solutionTab&solutionId=11750')),
  541. );
  542. return $form;
  543. }
  544. /**
  545. * Setup a mock renew handler to process through UC Recurring.
  546. *
  547. * @param $order
  548. * Order object.
  549. * @param $fee
  550. * Recurring fee object.
  551. * @return
  552. * TRUE if the payment has been received.
  553. */
  554. function uc_recurring_hosted_paypal_wps_renew($order, &$fee) {
  555. module_load_include('inc', 'uc_recurring_hosted', 'uc_recurring_hosted.paypal_ipn');
  556. $fee->ipn->order_id = $order->order_id;
  557. _uc_recurring_hosted_paypal_ipn_payment($fee->ipn);
  558. if ($fee->ipn->payment_status == 'Completed') {
  559. return TRUE;
  560. }
  561. return FALSE;
  562. }
  563. /******************************************************************************
  564. * PAYPAL WPP
  565. *****************************************************************************/
  566. /**
  567. * PayPal website payments pro process.
  568. */
  569. function uc_recurring_hosted_paypal_wpp_process($order, &$fee) {
  570. global $user;
  571. // Setup variables for the payment schedule.
  572. list($length, $unit) = explode(' ', $fee->regular_interval);
  573. list($trial_length, $trial_unit) = explode(' ', $fee->initial_charge);
  574. // Make sure we have valid values.
  575. if ($length <= 0 ||
  576. $unit == 'days' && $length > 365 ||
  577. $unit == 'months' && $length > 12 ||
  578. $unit == 'semimonths' && $length > 24 ||
  579. $unit == 'weeks' && $length > 52 ||
  580. $unit == 'years' && $length > 1
  581. ) {
  582. // Get a default SKU if none was supplied.
  583. if (empty($fee->model)) {
  584. $fee->model = db_result(db_query("SELECT model FROM {uc_products} WHERE nid = %d", $fee->nid));
  585. }
  586. watchdog('uc_recurring', 'Product @sku has invalid interval settings for PayPal - @length @unit', array('@sku' => $fee->model, '@length' => $length, '@unit' => $unit), WATCHDOG_ERROR);
  587. return FALSE;
  588. }
  589. // 'weeks' => 'Week', etc. PayPal API allows billing period to be one of
  590. // Day, Week, SemiMonth, or Year.
  591. if ($unit == 'semimonth') {
  592. $unit = 'SemiMonth';
  593. }
  594. else $unit = ucfirst(substr($unit, 0, -1));
  595. if ($trial_unit == 'semimonth') {
  596. $trial_unit = 'SemiMonth';
  597. }
  598. else $trial_unit = ucfirst(substr($trial_unit, 0, -1));
  599. if ($fee->initial_charge) {
  600. $start_date = date(DATE_ATOM, strtotime('+ '. $fee->initial_charge));
  601. }
  602. else {
  603. //the same as date(DATE_ATOM,$fee->next_charge)
  604. $start_date = date(DATE_ATOM, strtotime('+ '. $fee->regular_interval));
  605. }
  606. $cc_type = '';
  607. if (isset($order->payment_details['cc_type'])) {
  608. switch (strtolower($order->payment_details['cc_type'])) {
  609. case 'amex':
  610. case 'american express':
  611. $cc_type = 'Amex';
  612. break;
  613. case 'visa':
  614. $cc_type = 'Visa';
  615. break;
  616. case 'mastercard':
  617. case 'master card':
  618. $cc_type = 'MasterCard';
  619. break;
  620. case 'discover':
  621. $cc_type = 'Discover';
  622. break;
  623. }
  624. }
  625. if (empty($cc_type)) {
  626. $cc_type = _uc_paypal_card_type($order->payment_details['cc_number']);
  627. if ($cc_type === FALSE) {
  628. drupal_set_message(t('The credit card type did not pass validation.'), 'error');
  629. watchdog('uc_recurring', 'Could not figure out credit card type @type', array('@type' => $order->payment_details['cc_type']), WATCHDOG_ERROR);
  630. return FALSE;
  631. }
  632. }
  633. // Build an NVP request.
  634. // @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_api_WPRecurringPayments @endlink
  635. $nvp_request = array(
  636. // Set the version required for recurring payments.
  637. 'VERSION' => UC_PAYPAL_RECURRING_API,
  638. 'METHOD' => 'CreateRecurringPaymentsProfile',
  639. 'DESC' => 'Order '. $order->order_id .' at '. check_plain(variable_get('uc_store_name', 'Ubercart')),
  640. 'PROFILESTARTDATE' => $start_date,
  641. 'PROFILEREFERENCE' => $order->order_id,
  642. 'CREDITCARDTYPE' => $cc_type,
  643. 'ACCT' => $order->payment_details['cc_number'],
  644. 'EXPDATE' => date('mY', mktime(0, 0, 0, $order->payment_details['cc_exp_month'], 1, $order->payment_details['cc_exp_year'])),
  645. 'BILLINGPERIOD' => $unit,
  646. 'BILLINGFREQUENCY' => $length,
  647. // if TOTALBILLINGCYCLES = 0 the payments continue until the profile is
  648. // canceled or suspended.
  649. 'TOTALBILLINGCYCLES' => $fee->remaining_intervals > 0 ? $fee->remaining_intervals : 0,
  650. 'AMT' => round($fee->fee_amount, 2),
  651. 'EMAIL' => substr($order->primary_email, 0, 127),
  652. // The number of scheduled payments that can fail before the profile is
  653. // automatically suspended.
  654. // TODO: Remove hardcoding.
  655. 'MAXFAILEDPAYMENTS' => 1,
  656. );
  657. $nvp_request['CURRENCYCODE'] = variable_get('uc_paypal_wpp_currency', 'USD');
  658. // Add optional NVP request parameters.
  659. if (!empty($order->billing_first_name)) {
  660. $nvp_request['FIRSTNAME'] = substr($order->billing_first_name, 0, 25);
  661. }
  662. if (!empty($order->billing_last_name)) {
  663. $nvp_request['LASTNAME'] = substr($order->billing_last_name, 0, 25);
  664. }
  665. if (!empty($order->billing_street1)) {
  666. $nvp_request['STREET'] = substr($order->billing_street1, 0, 100);
  667. }
  668. if (!empty($order->billing_street2)) {
  669. $nvp_request['STREET2'] = substr($order->billing_street2, 0, 100);
  670. }
  671. if (!empty($order->billing_city)) {
  672. $nvp_request['CITY'] = substr($order->billing_city, 0, 40);
  673. }
  674. if (!empty($order->billing_country)) {
  675. $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
  676. if ($billing_country === FALSE) {
  677. $billing_country = array(0 => array('country_iso_code_2' => 'US'));
  678. }
  679. $nvp_request['COUNTRYCODE'] = $billing_country[0]['country_iso_code_2'];
  680. if (!empty($order->billing_zone)) {
  681. $nvp_request['STATE'] = uc_get_zone_code($order->billing_zone);
  682. }
  683. }
  684. if (!empty($order->billing_postal_code)) {
  685. $nvp_request['ZIP'] = check_plain($order->billing_postal_code);
  686. }
  687. if (!empty($order->billing_phone)) {
  688. $nvp_request['PHONENUM'] = substr($order->billing_phone, 0, 20);
  689. }
  690. // Only add trial if we have to wait to start payments. We make sure the trail
  691. // length is bigger then 1, otherwise the first month will be charged 0$.
  692. if ($trial_length > 1) {
  693. $nvp_request['TRIALBILLINGPERIOD'] = $trial_unit;
  694. $nvp_request['TRIALBILLINGFREQUENCY'] = $trial_length;
  695. $nvp_request['TRIALTOTALBILLINGCYCLES'] = 1;
  696. $nvp_request['TRIALAMT'] = 0;
  697. }
  698. if (variable_get('uc_credit_cvv_enabled', TRUE)) {
  699. $nvp_request['CVV2'] = $order->payment_details['cc_cvv'];
  700. }
  701. // Post the request, and parse the response.
  702. $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
  703. $types = uc_credit_transaction_types();
  704. // Get the $amount and $data from the fee object.
  705. $amount = $fee->data['amount'];
  706. $data = $fee->data['data'];
  707. $context = array(
  708. 'revision' => 'formatted-original',
  709. 'type' => 'amount',
  710. );
  711. $options = array(
  712. 'sign' => FALSE,
  713. 'thou' => FALSE,
  714. 'dec' => '.',
  715. );
  716. switch ($nvp_response['ACK']) {
  717. case 'SuccessWithWarning':
  718. watchdog('uc_payment', '<b>@type succeeded with a warning.</b>!paypal_message',
  719. array(
  720. '!paypal_message' => _uc_paypal_build_error_messages($nvp_response),
  721. '@type' => $types[$data['txn_type']],
  722. ),
  723. WATCHDOG_WARNING,
  724. l(t('view order'), 'admin/store/orders/'. $fee->order_id)
  725. );
  726. // Fall through.
  727. case 'Success':
  728. $message = t('<b>PayPal Recurring Profile Setup</b><br /><b>Recurring Profile Amount: </b>@amount @currency<br /><b>Recurring Profile ID: </b>@profile_id',
  729. array('@amount' => uc_price($nvp_request['AMT'], $context, array('sign' => FALSE)),
  730. '@currency' => $nvp_response['CURRENCYCODE'], '@profile_id' => $nvp_response['PROFILEID']));
  731. $result = array(
  732. 'success' => TRUE,
  733. 'comment' => t('PayPal Recurring Payment Profile ID: @profile_id', array('@profile_id' => $nvp_response['PROFILEID'])),
  734. 'message' => $message,
  735. 'data' => check_plain($nvp_response['PROFILEID']),
  736. 'uid' => $user->uid,
  737. );
  738. // We need to save the rfid, we don't have it yet.
  739. if (!$fee->rfid) {
  740. $fee->rfid = uc_recurring_fee_user_save($fee);
  741. }
  742. // Save the subscription so we have the information.
  743. uc_recurring_hosted_subscription_save($fee->rfid, $nvp_response['PROFILEID']);
  744. // Log the IPN to the database.
  745. db_query("INSERT INTO {uc_payment_paypal_ipn} (order_id, txn_id, txn_type, mc_gross, status, receiver_email, payer_email, received) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', %d)",
  746. $order->order_id, $nvp_response['TRANSACTIONID'], 'web_accept', $amount, 'Completed', '', $order->primary_email, time());
  747. break;
  748. case 'FailureWithWarning':
  749. // Fall through.
  750. case 'Failure':
  751. $message = t('<b>Recurring Payment Setup Failed.</b>') . _uc_paypal_build_error_messages($nvp_response);
  752. $result = array(
  753. 'success' => FALSE,
  754. 'message' => $message,
  755. 'uid' => $user->uid,
  756. );
  757. break;
  758. default:
  759. $message = t('Unexpected acknowledgement status: @status', array('@status' => $nvp_response['ACK']));
  760. $result = array(
  761. 'success' => NULL,
  762. 'message' => $message,
  763. 'uid' => $user->uid,
  764. );
  765. break;
  766. }
  767. uc_order_comment_save($fee->order_id, $user->uid, $message, 'admin');
  768. // Log this only if payment money wasn't actually captured.
  769. if (!empty($result['success']) && !in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY))) {
  770. // The transaction was successful. We need to populate some data in the
  771. // fee object, as we are now returning to uc_recurring_process_order()
  772. // and the result (e.g. the mesasge and the user ID) need to be passed back
  773. // to uc_payment_process().
  774. $fee->data['nvp result'] = $result;
  775. }
  776. return $result['success'];
  777. }
  778. /**
  779. * PayPal website payments pro renew.
  780. *
  781. * Note: the cancel handler gets just one parameter $fee. And it is passed by value (not by ref).
  782. * It differs to the other handlers (such as process or renew).
  783. */
  784. function uc_recurring_hosted_paypal_wpp_cancel($fee) {
  785. global $user;
  786. // Get the subscription ID.
  787. $subscription = uc_recurring_hosted_subscription_load($fee->rfid);
  788. // Build an NVP request.
  789. $nvp_request = array(
  790. // Set the version required for recurring payments.
  791. 'VERSION' => UC_PAYPAL_RECURRING_API,
  792. 'METHOD' => 'ManageRecurringPaymentsProfileStatus',
  793. 'PROFILEID' => $subscription->subscription_id,
  794. 'ACTION' => 'Cancel',
  795. );
  796. // Post the request, and parse the response.
  797. $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'), UC_PAYPAL_RECURRING_API);
  798. if ($nvp_response['ACK'] != 'Success' && $nvp_response['ACK'] != 'SuccessWithWarning') {
  799. watchdog('uc_recurring_hosted', 'Failed to cancel recurring @id', array('@id' => $fee->rfid), WATCHDOG_ERROR);
  800. return FALSE;
  801. }
  802. else {
  803. watchdog('uc_recurring_hosted', 'Success to cancel recurring @id', array('@id' => $fee->rfid), WATCHDOG_INFO);
  804. }
  805. return TRUE;
  806. }
  807. /**
  808. * Helper function; Get a recurring payments profile from PayPal.
  809. *
  810. * @param $rfid
  811. * The recurring fee ID.
  812. *
  813. * @return
  814. * FALSE on failure, otherwise, the NVP response from PayPal.
  815. */
  816. function uc_recurring_hosted_paypal_wpp_get_profile($rfid) {
  817. $fee = uc_recurring_fee_user_load($rfid);
  818. $subscription = uc_recurring_hosted_subscription_load($fee->rfid);
  819. // Build an NVP request.
  820. $nvp_request = array(
  821. 'METHOD' => 'GetRecurringPaymentsProfileDetails',
  822. 'PROFILEID' => $subscription->subscription_id,
  823. );
  824. // Post the request, and parse the response.
  825. $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'), UC_PAYPAL_RECURRING_API);
  826. if ($nvp_response['ACK'] != 'Success' && $nvp_response['ACK'] != 'SuccessWithWarning') {
  827. return FALSE;
  828. }
  829. return $nvp_response;
  830. }
  831. /**
  832. * Setup a mock renew handler to process through UC Recurring.
  833. *
  834. * @param $order
  835. * Order object.
  836. * @param $fee
  837. * Recurring fee object.
  838. * @return
  839. * TRUE if payment has been received.
  840. */
  841. function uc_recurring_hosted_paypal_wpp_renew($order, &$fee) {
  842. module_load_include('inc', 'uc_recurring_hosted', 'uc_recurring_hosted.paypal_ipn');
  843. $fee->ipn->order_id = $order->order_id;
  844. _uc_recurring_hosted_paypal_ipn_payment($fee->ipn);
  845. if ($fee->ipn->payment_status == 'Completed' && $fee->ipn->txn_type == 'recurring_payment') {
  846. return TRUE;
  847. }
  848. return FALSE;
  849. }
  850. /**
  851. * Form for updating the user credit card information
  852. * @param unknown_type $form_state
  853. * @param unknown_type $rfid
  854. * @return unknown_type
  855. */
  856. function uc_recurring_hosted_paypal_wpp_update_form($form_state, $rfid) {
  857. // Load fee.
  858. $fee = uc_recurring_fee_user_load($rfid);
  859. // Load corresponding order.
  860. $order = uc_order_load($fee->order_id);
  861. $form['rfid'] = array(
  862. '#type' => 'value',
  863. '#value' => $rfid,
  864. );
  865. $form['cc_data'] = array(
  866. '#type' => 'fieldset',
  867. '#title' => t('Credit card details'),
  868. '#theme' => 'uc_payment_method_credit_form',
  869. '#tree' => TRUE,
  870. );
  871. $form['cc_data'] += uc_payment_method_credit_form(array(), $order);
  872. unset($form['cc_data']['cc_policy']);
  873. $form['billing_data'] = array(
  874. '#type' => 'fieldset',
  875. '#title' => t('Billing Address Details'),
  876. );
  877. if (uc_address_field_enabled('first_name')) {
  878. $form['billing_data']['billing_first_name'] = uc_textfield(uc_get_field_name('first_name'), $order->billing_first_name, uc_address_field_required('first_name'));
  879. }
  880. if (uc_address_field_enabled('last_name')) {
  881. $form['billing_data']['billing_last_name'] = uc_textfield(uc_get_field_name('last_name'), $order->billing_last_name, uc_address_field_required('last_name'));
  882. }
  883. if (uc_address_field_enabled('company')) {
  884. $form['billing_data']['billing_company'] = uc_textfield(uc_get_field_name('company'), $order->billing_company, uc_address_field_required('company'), NULL, 64);
  885. }
  886. if (uc_address_field_enabled('street1')) {
  887. $form['billing_data']['billing_street1'] = uc_textfield(uc_get_field_name('street1'), $order->billing_street1, uc_address_field_required('street1'), NULL, 64);
  888. }
  889. if (uc_address_field_enabled('street2')) {
  890. $form['billing_data']['billing_street2'] = uc_textfield(uc_get_field_name('street2'), $order->billing_street2, uc_address_field_required('street2'), NULL, 64);
  891. }
  892. if (uc_address_field_enabled('city')) {
  893. $form['billing_data']['billing_city'] = uc_textfield(uc_get_field_name('city'), $order->billing_city, uc_address_field_required('city'));
  894. }
  895. if (uc_address_field_enabled('country')) {
  896. $form['billing_data']['billing_country'] = uc_country_select(uc_get_field_name('country'), $order->billing_country, NULL, 'name', uc_address_field_required('country'));
  897. }
  898. if (uc_address_field_enabled('zone')) {
  899. if (isset($_POST['panes']['billing']['billing_country'])) {
  900. $country_id = intval($_POST['panes']['billing']['billing_country']);
  901. }
  902. else {
  903. $country_id = $order->billing_country;
  904. }
  905. $form['billing_data']['billing_zone'] = uc_zone_select(uc_get_field_name('zone'), $order->billing_zone, NULL, $country_id, 'name', uc_address_field_required('zone'));
  906. if (isset($_POST['panes']) && count($contents['billing_zone']['#options']) == 1) {
  907. $form['billing_data']['billing_zone']['#required'] = FALSE;
  908. }
  909. }
  910. if (uc_address_field_enabled('postal_code')) {
  911. $form['billing_data']['billing_postal_code'] = uc_textfield(uc_get_field_name('postal_code'), $order->billing_postal_code, uc_address_field_required('postal_code'), NULL, 10, 10);
  912. }
  913. $form['submit'] = array(
  914. '#type' => 'submit',
  915. '#value' => t('Update Account'),
  916. '#suffix' => l(t('Cancel'), 'user/'. $order->uid .'/recurring-fees'),
  917. );
  918. return $form;
  919. }
  920. /**
  921. * Update form submission callback
  922. * @param unknown_type $form
  923. * @param unknown_type $form_state
  924. * @return unknown_type
  925. */
  926. function uc_recurring_hosted_paypal_wpp_update_form_submit($form, $form_state){
  927. $fee = uc_recurring_fee_user_load($form_state['values']['rfid']);
  928. $order = uc_order_load($fee->order_id);
  929. $order->payment_details = $form_state['values']['cc_data'];
  930. //update the billing details
  931. $order->billing_first_name = $form_state['values']['billing_first_name'];
  932. $order->billing_last_name = $form_state['values']['billing_last_name'];
  933. $order->billing_street1 = $form_state['values']['billing_street1'];
  934. $order->billing_street2 = $form_state['values']['billing_street2'];
  935. $order->billing_city = $form_state['values']['billing_city'];
  936. $order->billing_country = $form_state['values']['billing_country'];
  937. $order->billing_zone = $form_state['values']['billing_zone'];
  938. $order->billing_postal_code = $form_state['values']['billing_postal_code'];
  939. uc_order_save($order);
  940. $cc_type = '';
  941. if (isset($order->payment_details['cc_type'])) {
  942. switch (strtolower($order->payment_details['cc_type'])) {
  943. case 'amex':
  944. case 'american express':
  945. $cc_type = 'Amex';
  946. break;
  947. case 'visa':
  948. $cc_type = 'Visa';
  949. break;
  950. case 'mastercard':
  951. case 'master card':
  952. $cc_type = 'MasterCard';
  953. break;
  954. case 'discover':
  955. $cc_type = 'Discover';
  956. break;
  957. }
  958. }
  959. if (empty($cc_type)) {
  960. $cc_type = _uc_paypal_card_type($order->payment_details['cc_number']);
  961. if ($cc_type === FALSE) {
  962. drupal_set_message(t('The credit card type did not pass validation.'), 'error');
  963. watchdog('uc_recurring', 'Could not figure out credit card type @type', array('@type' => $order->payment_details['cc_type']), WATCHDOG_ERROR);
  964. return FALSE;
  965. }
  966. }
  967. // Build an NVP request.
  968. $nvp_request = array(
  969. 'CREDITCARDTYPE' => $cc_type,
  970. 'ACCT' => $order->payment_details['cc_number'],
  971. 'EXPDATE' => date('mY', mktime(0, 0, 0, $order->payment_details['cc_exp_month'], 1, $order->payment_details['cc_exp_year'])),
  972. );
  973. if (!empty($order->billing_first_name)) {
  974. $nvp_request['FIRSTNAME'] = substr($order->billing_first_name, 0, 25);
  975. }
  976. if (!empty($order->billing_last_name)) {
  977. $nvp_request['LASTNAME'] = substr($order->billing_last_name, 0, 25);
  978. }
  979. if (!empty($order->billing_street1)) {
  980. $nvp_request['STREET'] = substr($order->billing_street1, 0, 100);
  981. }
  982. if (!empty($order->billing_street2)) {
  983. $nvp_request['STREET2'] = substr($order->billing_street2, 0, 100);
  984. }
  985. if (!empty($order->billing_city)) {
  986. $nvp_request['CITY'] = substr($order->billing_city, 0, 40);
  987. }
  988. if (!empty($order->billing_country)) {
  989. $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
  990. if ($billing_country === FALSE) {
  991. $billing_country = array(0 => array('country_iso_code_2' => 'US'));
  992. }
  993. $nvp_request['COUNTRYCODE'] = $billing_country[0]['country_iso_code_2'];
  994. if (!empty($order->billing_zone)) {
  995. $nvp_request['STATE'] = uc_get_zone_code($order->billing_zone);
  996. }
  997. }
  998. if (!empty($order->billing_postal_code)) {
  999. $nvp_request['ZIP'] = check_plain($order->billing_postal_code);
  1000. }
  1001. if (!empty($order->billing_phone)) {
  1002. $nvp_request['PHONENUM'] = substr($order->billing_phone, 0, 20);
  1003. }
  1004. if ($message = uc_recurring_hosted_paypal_wpp_update($order, $fee, $nvp_request)) {
  1005. drupal_set_message(t('Your account has been updated successfully.'));
  1006. $form_state['redirect'] = 'user/'. $form_state['values']['uid'];
  1007. }
  1008. else {
  1009. drupal_set_message(t('Your account failed to update due to an error with your card information. Please try again or contact us for assistance.'), 'error');
  1010. }
  1011. }
  1012. /**
  1013. * Updates a PayPal subscription; for simplicity's sake, Credit Card
  1014. * information except for expiration date cannot be updated at this time.
  1015. *
  1016. * @param $order
  1017. * The order object.
  1018. * @param $fee
  1019. * The recurring fee object.
  1020. * @param $updates
  1021. * An array of data to update using key/ value pairs from the NVP API for
  1022. * PayPal.
  1023. *
  1024. * @return
  1025. * TRUE or FALSE indicating the success of the update.
  1026. */
  1027. function uc_recurring_hosted_paypal_wpp_update($order, $fee, $updates = array()) {
  1028. global $user;
  1029. watchdog('uc_recurring_paypal_wpp_update', print_r($fee, 1), NULL, WATCHDOG_DEBUG);
  1030. $profile = uc_recurring_hosted_subscription_load($fee->rfid);
  1031. // Build an NVP request.
  1032. $nvp_request = array(
  1033. 'METHOD' => 'UpdateRecurringPaymentsProfile',
  1034. 'PROFILEID' => $profile->subscription_id,
  1035. ) + $updates;
  1036. // Post the request, and parse the response.
  1037. $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'), UC_PAYPAL_RECURRING_API);
  1038. if ($nvp_response['ACK'] != 'Success' && $nvp_response['ACK'] != 'SuccessWithWarning') {
  1039. watchdog('uc_recurring', 'Failed to update recurring profile @id', array('@id' => $profile->subscription_id), WATCHDOG_ERROR);
  1040. return FALSE;
  1041. }
  1042. $message = t('<b>PayPal Recurring Profile Updated</b><br /><b>Field(s) Updated: </b>@fields<br /><b>Recurring Profile ID: </b>@profile_id',
  1043. array('@fields'=> implode(',', array_keys($updates)), '@profile_id' => $profile->subscription_id));
  1044. uc_order_comment_save($order->order_id, $user->uid, $message, 'admin');
  1045. return TRUE;
  1046. }
  1047. /**
  1048. * Suspends a user's profile due to failed payment.
  1049. *
  1050. * @param unknown_type $fee
  1051. * @param unknown_type $ipn
  1052. * @return unknown_type
  1053. */
  1054. function uc_recurring_hosted_paypal_wpp_suspension_log($fee,$ipn){
  1055. $profile = uc_recurring_hosted_paypal_wpp_get_profile($fee->rfid);
  1056. if($profile['LASTPAYMENTDATE']){
  1057. $date = date('m/d/Y H:i:s',strtotime($profile['LASTPAYMENTDATE']));
  1058. }else{
  1059. $date = 'N/A';
  1060. }
  1061. $message = t('<b>PayPal Recurring Profile Suspended</b><br /><b>Last Successful Payment Date: </b>@date<br /><b>Recurring Profile ID: </b>@profile_id',
  1062. array('@date'=> $date, '@profile_id' => $ipn->recurring_payment_id));
  1063. uc_order_comment_save($fee->order_id, 0, $message, 'admin');
  1064. $fee->status = 3;
  1065. uc_recurring_fee_user_save($fee);
  1066. }
  1067. function uc_recurring_hosted_paypal_wpp_profile_creation_form($form_state, $rfid){
  1068. // Load fee.
  1069. $fee = uc_recurring_fee_user_load($rfid);
  1070. // Load corresponding order.
  1071. $order = uc_order_load($fee->order_id);
  1072. $order->payment_details = null;
  1073. $form['rfid'] = array(
  1074. '#type' => 'value',
  1075. '#value' => $rfid,
  1076. );
  1077. $form['cc_data'] = array(
  1078. '#type' => 'fieldset',
  1079. '#title' => t('Credit card details'),
  1080. '#theme' => 'uc_payment_method_credit_form',
  1081. '#tree' => TRUE,
  1082. );
  1083. $form['cc_data'] += uc_payment_method_credit_form(array(), $order);
  1084. unset($form['cc_data']['cc_policy']);
  1085. $form['billing_data'] = array(
  1086. '#type' => 'fieldset',
  1087. '#title' => t('Billing Address Details'),
  1088. );
  1089. if (uc_address_field_enabled('first_name')) {
  1090. $form['billing_data']['billing_first_name'] = uc_textfield(uc_get_field_name('first_name'), $order->billing_first_name, uc_address_field_required('first_name'));
  1091. }
  1092. if (uc_address_field_enabled('last_name')) {
  1093. $form['billing_data']['billing_last_name'] = uc_textfield(uc_get_field_name('last_name'), $order->billing_last_name, uc_address_field_required('last_name'));
  1094. }
  1095. if (uc_address_field_enabled('company')) {
  1096. $form['billing_data']['billing_company'] = uc_textfield(uc_get_field_name('company'), $order->billing_company, uc_address_field_required('company'), NULL, 64);
  1097. }
  1098. if (uc_address_field_enabled('street1')) {
  1099. $form['billing_data']['billing_street1'] = uc_textfield(uc_get_field_name('street1'), $order->billing_street1, uc_address_field_required('street1'), NULL, 64);
  1100. }
  1101. if (uc_address_field_enabled('street2')) {
  1102. $form['billing_data']['billing_street2'] = uc_textfield(uc_get_field_name('street2'), $order->billing_street2, uc_address_field_required('street2'), NULL, 64);
  1103. }
  1104. if (uc_address_field_enabled('city')) {
  1105. $form['billing_data']['billing_city'] = uc_textfield(uc_get_field_name('city'), $order->billing_city, uc_address_field_required('city'));
  1106. }
  1107. if (uc_address_field_enabled('country')) {
  1108. $form['billing_data']['billing_country'] = uc_country_select(uc_get_field_name('country'), $order->billing_country, NULL, 'name', uc_address_field_required('country'));
  1109. }
  1110. if (uc_address_field_enabled('zone')) {
  1111. if (isset($_POST['panes']['billing']['billing_country'])) {
  1112. $country_id = intval($_POST['panes']['billing']['billing_country']);
  1113. }
  1114. else {
  1115. $country_id = $order->billing_country;
  1116. }
  1117. $form['billing_data']['billing_zone'] = uc_zone_select(uc_get_field_name('zone'), $order->billing_zone, NULL, $country_id, 'name', uc_address_field_required('zone'));
  1118. if (isset($_POST['panes']) && count($contents['billing_zone']['#options']) == 1) {
  1119. $form['billing_data']['billing_zone']['#required'] = FALSE;
  1120. }
  1121. }
  1122. if (uc_address_field_enabled('postal_code')) {
  1123. $form['billing_data']['billing_postal_code'] = uc_textfield(uc_get_field_name('postal_code'), $order->billing_postal_code, uc_address_field_required('postal_code'), NULL, 10, 10);
  1124. }
  1125. $form['submit'] = array(
  1126. '#type' => 'submit',
  1127. '#value' => t('Update Payment Information'),
  1128. );
  1129. return $form;
  1130. }
  1131. function uc_recurring_hosted_paypal_wpp_profile_creation_form_submit($form, &$form_state){
  1132. $fee = uc_recurring_fee_user_load($form_state['values']['rfid']);
  1133. $order = uc_order_load($fee->order_id);
  1134. $order->payment_details = $form_state['values']['cc_data'];
  1135. //update the billing details
  1136. $order->billing_first_name = $form_state['values']['billing_first_name'];
  1137. $order->billing_last_name = $form_state['values']['billing_last_name'];
  1138. $order->billing_street1 = $form_state['values']['billing_street1'];
  1139. $order->billing_street2 = $form_state['values']['billing_street2'];
  1140. $order->billing_city = $form_state['values']['billing_city'];
  1141. $order->billing_country = $form_state['values']['billing_country'];
  1142. $order->billing_zone = $form_state['values']['billing_zone'];
  1143. $order->billing_postal_code = $form_state['values']['billing_postal_code'];
  1144. $result = uc_recurring_hosted_paypal_wpp_process($order, $fee);
  1145. if($result){
  1146. db_query("UPDATE {uc_recurring_users} SET billing_assigned = 1 WHERE rfid = %d",$fee->rfid);
  1147. $cc_data = array(
  1148. 'cc_number' => substr($order->payment_details['cc_number'], -4),
  1149. 'cc_exp_month' => $order->payment_details['cc_exp_month'],
  1150. 'cc_exp_year' => $order->payment_details['cc_exp_year'],
  1151. 'cc_type' => $order->payment_details['cc_type'],
  1152. );
  1153. _save_cc_data_to_order($cc_data, $order->order_id);
  1154. uc_order_save($order);
  1155. $form_state['redirect'] = 'user/'.$order->uid.'/recurring/create/complete';
  1156. }else{
  1157. drupal_set_message(t('Recurring fee was not processed successfully. Please try again or contact customer service.'),'error');
  1158. }
  1159. }