PMProGateway_paypalstandard

The Paid Memberships Pro PMProGateway paypalstandard class.

Defined (1)

The class is defined in the following location(s).

/classes/gateways/class.pmprogateway_paypalstandard.php  
  1. class PMProGateway_paypalstandard extends PMProGateway 
  2. function __construct($gateway = NULL) 
  3. $this->gateway = $gateway; 
  4. return $this->gateway; 
  5. }  
  6.  
  7. /** 
  8. * Run on WP init 
  9. *  
  10. * @since 1.8 
  11. */ 
  12. static function init() 
  13. {  
  14. //make sure PayPal Express is a gateway option 
  15. add_filter('pmpro_gateways', array('PMProGateway_paypalstandard', 'pmpro_gateways')); 
  16.  
  17. //add fields to payment settings 
  18. add_filter('pmpro_payment_options', array('PMProGateway_paypalstandard', 'pmpro_payment_options')); 
  19.  
  20. /** 
  21. This code is the same for PayPal Website Payments Pro, PayPal Express, and PayPal Standard 
  22. So we only load it if we haven't already. 
  23. */ 
  24. global $pmpro_payment_option_fields_for_paypal; 
  25. if(empty($pmpro_payment_option_fields_for_paypal)) 
  26. {  
  27. add_filter('pmpro_payment_option_fields', array('PMProGateway_paypalstandard', 'pmpro_payment_option_fields'), 10, 2);  
  28. $pmpro_payment_option_fields_for_paypal = true; 
  29.  
  30. //code to add at checkout 
  31. $gateway = pmpro_getGateway(); 
  32. if($gateway == "paypalstandard") 
  33. {  
  34. add_filter('pmpro_include_billing_address_fields', '__return_false'); 
  35. add_filter('pmpro_include_payment_information_fields', '__return_false'); 
  36. add_filter('pmpro_required_billing_fields', array('PMProGateway_paypalstandard', 'pmpro_required_billing_fields')); 
  37. add_filter('pmpro_checkout_default_submit_button', array('PMProGateway_paypalstandard', 'pmpro_checkout_default_submit_button')); 
  38. add_filter('pmpro_checkout_before_change_membership_level', array('PMProGateway_paypalstandard', 'pmpro_checkout_before_change_membership_level'), 10, 2); 
  39.  
  40. /** 
  41. * Make sure this gateway is in the gateways list 
  42. *  
  43. * @since 1.8 
  44. */ 
  45. static function pmpro_gateways($gateways) 
  46. if(empty($gateways['paypalstandard'])) 
  47. $gateways['paypalstandard'] = __('PayPal Standard', 'paid-memberships-pro' ); 
  48.  
  49. return $gateways; 
  50.  
  51. /** 
  52. * Get a list of payment options that the this gateway needs/supports. 
  53. *  
  54. * @since 1.8 
  55. */ 
  56. static function getGatewayOptions() 
  57. {  
  58. $options = array( 
  59. 'sslseal',  
  60. 'nuclear_HTTPS',  
  61. 'gateway_environment',  
  62. 'gateway_email',  
  63. 'currency',  
  64. 'use_ssl',  
  65. 'tax_state',  
  66. 'tax_rate' 
  67. ); 
  68.  
  69. return $options; 
  70.  
  71. /** 
  72. * Set payment options for payment settings page. 
  73. *  
  74. * @since 1.8 
  75. */ 
  76. static function pmpro_payment_options($options) 
  77. {  
  78. //get stripe options 
  79. $paypal_options = PMProGateway_paypalexpress::getGatewayOptions(); 
  80.  
  81. //merge with others. 
  82. $options = array_merge($paypal_options, $options); 
  83.  
  84. return $options; 
  85.  
  86. /** 
  87. * Display fields for this gateway's options. 
  88. *  
  89. * @since 1.8 
  90. */ 
  91. static function pmpro_payment_option_fields($values, $gateway) 
  92. ?> 
  93. <tr class="pmpro_settings_divider gateway gateway_paypal gateway_paypalexpress gateway_paypalstandard" <?php if($gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "paypalstandard") { ?>style="display: none;"<?php } ?>> 
  94. <td colspan="2"> 
  95. <?php _e('PayPal Settings', 'paid-memberships-pro' ); ?> 
  96. </td> 
  97. </tr>  
  98. <tr class="gateway gateway_paypalstandard" <?php if($gateway != "paypalstandard") { ?>style="display: none;"<?php } ?>> 
  99. <td colspan="2"> 
  100. <strong><?php _e('Note', 'paid-memberships-pro' );?>:</strong> <?php _e('We do not recommend using PayPal Standard. We suggest using PayPal Express, Website Payments Pro (Legacy), or PayPal Pro (Payflow Pro). <a target="_blank" href="http://www.paidmembershipspro.com/2013/09/read-using-paypal-standard-paid-memberships-pro/">More information on why can be found here.</a>', 'paid-memberships-pro' );?> 
  101. </td>  
  102. </tr>  
  103. <tr class="gateway gateway_paypal gateway_paypalexpress gateway_paypalstandard" <?php if($gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "paypalstandard") { ?>style="display: none;"<?php } ?>> 
  104. <th scope="row" valign="top">  
  105. <label for="gateway_email"><?php _e('Gateway Account Email', 'paid-memberships-pro' );?>:</label> 
  106. </th> 
  107. <td> 
  108. <input type="text" id="gateway_email" name="gateway_email" size="60" value="<?php echo esc_attr($values['gateway_email'])?>" /> 
  109. </td> 
  110. </tr>  
  111. <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>> 
  112. <th scope="row" valign="top"> 
  113. <label for="apiusername"><?php _e('API Username', 'paid-memberships-pro' );?>:</label> 
  114. </th> 
  115. <td> 
  116. <input type="text" id="apiusername" name="apiusername" size="60" value="<?php echo esc_attr($values['apiusername'])?>" /> 
  117. </td> 
  118. </tr> 
  119. <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>> 
  120. <th scope="row" valign="top"> 
  121. <label for="apipassword"><?php _e('API Password', 'paid-memberships-pro' );?>:</label> 
  122. </th> 
  123. <td> 
  124. <input type="text" id="apipassword" name="apipassword" size="60" value="<?php echo esc_attr($values['apipassword'])?>" /> 
  125. </td> 
  126. </tr>  
  127. <tr class="gateway gateway_paypal gateway_paypalexpress" <?php if($gateway != "paypal" && $gateway != "paypalexpress") { ?>style="display: none;"<?php } ?>> 
  128. <th scope="row" valign="top"> 
  129. <label for="apisignature"><?php _e('API Signature', 'paid-memberships-pro' );?>:</label> 
  130. </th> 
  131. <td> 
  132. <input type="text" id="apisignature" name="apisignature" size="60" value="<?php echo esc_attr($values['apisignature'])?>" /> 
  133. </td> 
  134. </tr>  
  135. <tr class="gateway gateway_paypal gateway_paypalexpress gateway_paypalstandard" <?php if($gateway != "paypal" && $gateway != "paypalexpress" && $gateway != "paypalstandard") { ?>style="display: none;"<?php } ?>> 
  136. <th scope="row" valign="top"> 
  137. <label><?php _e('IPN Handler URL', 'paid-memberships-pro' );?>:</label> 
  138. </th> 
  139. <td> 
  140. <p><?php _e('Here is your IPN URL for reference. You SHOULD NOT set this in your PayPal settings.', 'paid-memberships-pro' );?> <pre><?php echo admin_url("admin-ajax.php") . "?action=ipnhandler";?></pre></p> 
  141. </td> 
  142. </tr> 
  143. <?php 
  144.  
  145. /** 
  146. * Remove required billing fields 
  147. *  
  148. * @since 1.8 
  149. */ 
  150. static function pmpro_required_billing_fields($fields) 
  151. unset($fields['bfirstname']); 
  152. unset($fields['blastname']); 
  153. unset($fields['baddress1']); 
  154. unset($fields['bcity']); 
  155. unset($fields['bstate']); 
  156. unset($fields['bzipcode']); 
  157. unset($fields['bphone']); 
  158. unset($fields['bemail']); 
  159. unset($fields['bcountry']); 
  160. unset($fields['CardType']); 
  161. unset($fields['AccountNumber']); 
  162. unset($fields['ExpirationMonth']); 
  163. unset($fields['ExpirationYear']); 
  164. unset($fields['CVV']); 
  165.  
  166. return $fields; 
  167.  
  168. /** 
  169. * Swap in our submit buttons. 
  170. * @since 1.8 
  171. */ 
  172. static function pmpro_checkout_default_submit_button($show) 
  173. global $gateway, $pmpro_requirebilling; 
  174.  
  175. //show our submit buttons 
  176. ?> 
  177. <span id="pmpro_paypalexpress_checkout" <?php if(($gateway != "paypalexpress" && $gateway != "paypalstandard") || !$pmpro_requirebilling) { ?>style="display: none;"<?php } ?>> 
  178. <input type="hidden" name="submit-checkout" value="1" />  
  179. <input type="image" value="<?php _e('Check Out with PayPal', 'paid-memberships-pro' );?> »" src="<?php echo apply_filters("pmpro_paypal_button_image", "https://www.paypal.com/en_US/i/btn/btn_xpressCheckout.gif");?>" /> 
  180. </span> 
  181.  
  182. <span id="pmpro_submit_span" <?php if(($gateway == "paypalexpress" || $gateway == "paypalstandard") && $pmpro_requirebilling) { ?>style="display: none;"<?php } ?>> 
  183. <input type="hidden" name="submit-checkout" value="1" />  
  184. <input type="submit" class="pmpro_btn pmpro_btn-submit-checkout" value="<?php if($pmpro_requirebilling) { _e('Submit and Check Out', 'paid-memberships-pro' ); } else { _e('Submit and Confirm', 'paid-memberships-pro' );}?> »" />  
  185. </span> 
  186. <?php 
  187.  
  188. //don't show the default 
  189. return false; 
  190.  
  191. /** 
  192. * Instead of change membership levels, send users to PayPal to pay. 
  193. * @since 1.8 
  194. */ 
  195. static function pmpro_checkout_before_change_membership_level($user_id, $morder) 
  196. global $discount_code_id, $wpdb; 
  197.  
  198. //if no order, no need to pay 
  199. if(empty($morder)) 
  200. return; 
  201.  
  202. $morder->user_id = $user_id;  
  203. $morder->saveOrder(); 
  204.  
  205. //save discount code use 
  206. if(!empty($discount_code_id)) 
  207. $wpdb->query("INSERT INTO $wpdb->pmpro_discount_codes_uses (code_id, user_id, order_id, timestamp) VALUES('" . $discount_code_id . "', '" . $user_id . "', '" . $morder->id . "', now())");  
  208.  
  209. do_action("pmpro_before_send_to_paypal_standard", $user_id, $morder); 
  210.  
  211. $morder->Gateway->sendToPayPal($morder); 
  212.  
  213. /** 
  214. * Process checkout. 
  215. *  
  216. */ 
  217. function process(&$order) 
  218. {  
  219. if(empty($order->code)) 
  220. $order->code = $order->getRandomCode();  
  221.  
  222. //clean up a couple values 
  223. $order->payment_type = "PayPal Standard"; 
  224. $order->CardType = ""; 
  225. $order->cardtype = ""; 
  226.  
  227. //just save, the user will go to PayPal to pay 
  228. $order->status = "review";  
  229. $order->saveOrder(); 
  230.  
  231. return true;  
  232.  
  233. function sendToPayPal(&$order) 
  234. {  
  235. global $pmpro_currency;  
  236.  
  237. //taxes on initial amount 
  238. $initial_payment = $order->InitialPayment; 
  239. $initial_payment_tax = $order->getTaxForPrice($initial_payment); 
  240. $initial_payment = round((float)$initial_payment + (float)$initial_payment_tax, 2); 
  241.  
  242. //taxes on the amount 
  243. $amount = $order->PaymentAmount; 
  244. $amount_tax = $order->getTaxForPrice($amount);  
  245. $amount = round((float)$amount + (float)$amount_tax, 2);  
  246.  
  247. //build PayPal Redirect  
  248. $environment = pmpro_getOption("gateway_environment"); 
  249. if("sandbox" === $environment || "beta-sandbox" === $environment) 
  250. $paypal_url ="https://www.sandbox.paypal.com/cgi-bin/webscr?business=" . urlencode(pmpro_getOption("gateway_email")); 
  251. else 
  252. $paypal_url = "https://www.paypal.com/cgi-bin/webscr?business=" . urlencode(pmpro_getOption("gateway_email")); 
  253.  
  254. if(pmpro_isLevelRecurring($order->membership_level)) 
  255. {  
  256. //convert billing period 
  257. if($order->BillingPeriod == "Day") 
  258. $period = "D"; 
  259. elseif($order->BillingPeriod == "Week") 
  260. $period = "W"; 
  261. elseif($order->BillingPeriod == "Month") 
  262. $period = "M"; 
  263. elseif($order->BillingPeriod == "Year") 
  264. $period = "Y";  
  265. else 
  266. $order->error = "Invalid billing period: " . $order->BillingPeriod; 
  267. $order->shorterror = "Invalid billing period: " . $order->BillingPeriod; 
  268. return false; 
  269.  
  270. //other args 
  271. $paypal_args = array(  
  272. 'cmd' => '_xclick-subscriptions',  
  273. 'a1' => number_format($initial_payment, 2),  
  274. 'p1' => $order->BillingFrequency,  
  275. 't1' => $period,  
  276. 'a3' => number_format($amount, 2),  
  277. 'p3' => $order->BillingFrequency,  
  278. 't3' => $period,  
  279. 'item_name' => substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127),  
  280. 'email' => $order->Email,  
  281. 'no_shipping' => '1',  
  282. 'shipping' => '0',  
  283. 'no_note' => '1',  
  284. 'currency_code' => $pmpro_currency,  
  285. 'item_number' => $order->code,  
  286. 'charset' => get_bloginfo( 'charset' ),  
  287. 'rm' => '2',  
  288. 'return' => pmpro_url("confirmation", "?level=" . $order->membership_level->id),  
  289. 'notify_url' => admin_url("admin-ajax.php") . "?action=ipnhandler",  
  290. 'src' => '1',  
  291. 'sra' => '1',  
  292. 'bn' => PAYPAL_BN_CODE 
  293. );  
  294.  
  295. //trial? 
  296. /** 
  297. Note here that the TrialBillingCycles value is being ignored. PayPal Standard only offers 1 payment during each trial period. 
  298. */ 
  299. if(!empty($order->TrialBillingPeriod)) 
  300. {  
  301. //if a1 and a2 are 0, let's just combine them. PayPal doesn't like a2 = 0. 
  302. if($paypal_args['a1'] == 0 && $order->TrialAmount == 0) 
  303. $paypal_args['p1'] = $paypal_args['p1'] + $order->TrialBillingFrequency; 
  304. else 
  305. $trial_amount = $order->TrialAmount; 
  306. $trial_tax = $order->getTaxForPrice($trial_amount); 
  307. $trial_amount = round((float)$trial_amount + (float)$trial_tax, 2); 
  308.  
  309. $paypal_args['a2'] = $trial_amount; 
  310. $paypal_args['p2'] = $order->TrialBillingFrequency; 
  311. $paypal_args['t2'] = $period; 
  312. else 
  313. //we can try to work in any change in ProfileStartDate 
  314. $psd = date_i18n("Y-m-d", strtotime("+ " . $order->BillingFrequency . " " . $order->BillingPeriod, current_time("timestamp"))) . "T0:0:0"; 
  315. $adjusted_psd = apply_filters("pmpro_profile_start_date", $psd, $order); 
  316. if($psd != $adjusted_psd) 
  317. //someone is trying to push the start date back 
  318. $adjusted_psd_time = strtotime($adjusted_psd, current_time("timestamp")); 
  319. $seconds_til_psd = $adjusted_psd_time - current_time('timestamp'); 
  320. $days_til_psd = floor($seconds_til_psd/(60*60*24)); 
  321.  
  322. //push back trial one by days_til_psd 
  323. if($days_til_psd > 90) 
  324. //we need to convert to weeks, because PayPal limits t1 to 90 days 
  325. $weeks_til_psd = round($days_til_psd / 7); 
  326. $paypal_args['p1'] = $weeks_til_psd; 
  327. $paypal_args['t1'] = "W"; 
  328. elseif($days_til_psd > 0) 
  329. {  
  330. //use days 
  331. $paypal_args['p1'] = $days_til_psd; 
  332. $paypal_args['t1'] = "D"; 
  333. }  
  334.  
  335. //billing limit? 
  336. if(!empty($order->TotalBillingCycles)) 
  337. if(!empty($trial_amount)) 
  338.  
  339. $srt = intval($order->TotalBillingCycles) - 1; //subtract one for the trial period  
  340. else 
  341. $srt = intval($order->TotalBillingCycles);  
  342.  
  343. //srt must be at least 2 or the subscription is not "recurring" according to paypal 
  344. if($srt > 1) 
  345. $paypal_args['srt'] = $srt; 
  346. else 
  347. $paypal_args['src'] = '0'; 
  348. else 
  349. $paypal_args['srt'] = '0'; //indefinite subscription 
  350. else 
  351. //other args 
  352. $paypal_args = array(  
  353. 'cmd' => '_xclick',  
  354. 'amount' => number_format($initial_payment, 2),  
  355. 'item_name' => substr($order->membership_level->name . " at " . get_bloginfo("name"), 0, 127),  
  356. 'email' => $order->Email,  
  357. 'no_shipping' => '1',  
  358. 'shipping' => '0',  
  359. 'no_note' => '1',  
  360. 'currency_code' => $pmpro_currency,  
  361. 'item_number' => $order->code,  
  362. 'charset' => get_bloginfo( 'charset' ),  
  363. 'rm' => '2',  
  364. 'return' => pmpro_url("confirmation", "?level=" . $order->membership_level->id),  
  365. 'notify_url' => admin_url("admin-ajax.php") . "?action=ipnhandler",  
  366. 'bn' => PAYPAL_BN_CODE 
  367. );  
  368. }  
  369.  
  370. $nvpStr = ""; 
  371. foreach($paypal_args as $key => $value) 
  372. $nvpStr .= "&" . $key . "=" . urlencode($value); 
  373.  
  374. //anything modders might add 
  375. $additional_parameters = apply_filters("pmpro_paypal_express_return_url_parameters", array());  
  376. if(!empty($additional_parameters)) 
  377. foreach($additional_parameters as $key => $value)  
  378. $nvpStr .= urlencode("&" . $key . "=" . $value); 
  379.  
  380. $account_optional = apply_filters('pmpro_paypal_account_optional', true); 
  381. if ($account_optional) 
  382. $nvpStr .= '&SOLUTIONTYPE=Sole&LANDINGPAGE=Billing'; 
  383.  
  384. $nvpStr = apply_filters("pmpro_paypal_standard_nvpstr", $nvpStr, $order); 
  385.  
  386. //redirect to paypal  
  387. $paypal_url .= $nvpStr;  
  388.  
  389. //wp_die(str_replace("&", "<br />", $paypal_url)); 
  390.  
  391. wp_redirect($paypal_url); 
  392. exit; 
  393.  
  394. function cancel(&$order) 
  395. {  
  396. //paypal profile stuff 
  397. $nvpStr = "";  
  398. $nvpStr .= "&PROFILEID=" . urlencode($order->subscription_transaction_id) . "&ACTION=Cancel&NOTE=" . urlencode("User requested cancel."); 
  399.  
  400. $this->httpParsedResponseAr = $this->PPHttpPost('ManageRecurringPaymentsProfileStatus', $nvpStr);  
  401.  
  402. if("SUCCESS" == strtoupper($this->httpParsedResponseAr["ACK"]) || "SUCCESSWITHWARNING" == strtoupper($this->httpParsedResponseAr["ACK"]))  
  403. {  
  404. $order->updateStatus("cancelled");  
  405. return true;  
  406. else 
  407. {  
  408. $order->status = "error"; 
  409. $order->errorcode = $this->httpParsedResponseAr['L_ERRORCODE0']; 
  410. $order->error = urldecode($this->httpParsedResponseAr['L_LONGMESSAGE0']) . ". " . __("Please contact the site owner or cancel your subscription from within PayPal to make sure you are not charged going forward.", 'paid-memberships-pro' ); 
  411. $order->shorterror = urldecode($this->httpParsedResponseAr['L_SHORTMESSAGE0']); 
  412.  
  413. return false;  
  414. }  
  415.  
  416. /** 
  417. * PAYPAL Function 
  418. * Send HTTP POST Request 
  419. * @param string The API method name 
  420. * @param string The POST Message fields in &name=value pair format 
  421. * @return array Parsed HTTP Response body 
  422. */ 
  423. function PPHttpPost($methodName_, $nvpStr_) { 
  424. global $gateway_environment; 
  425. $environment = $gateway_environment;  
  426.  
  427. $API_UserName = pmpro_getOption("apiusername"); 
  428. $API_Password = pmpro_getOption("apipassword"); 
  429. $API_Signature = pmpro_getOption("apisignature"); 
  430. $API_Endpoint = "https://api-3t.paypal.com/nvp"; 
  431. if("sandbox" === $environment || "beta-sandbox" === $environment) { 
  432. $API_Endpoint = "https://api-3t.$environment.paypal.com/nvp"; 
  433. }  
  434.  
  435. $version = urlencode('72.0'); 
  436.  
  437. // NVPRequest for submitting to server 
  438. $nvpreq = "METHOD=" . urlencode($methodName_) . "&VERSION=" . urlencode($version) . "&PWD=" . urlencode($API_Password) . "&USER=" . urlencode($API_UserName) . "&SIGNATURE=" . urlencode($API_Signature) . "&bn=" . urlencode(PAYPAL_BN_CODE) . $nvpStr_; 
  439.  
  440. //post to PayPal 
  441. $response = wp_remote_post( $API_Endpoint, array( 
  442. 'timeout' => 60,  
  443. 'sslverify' => FALSE,  
  444. 'httpversion' => '1.1',  
  445. 'body' => $nvpreq 
  446. ); 
  447.  
  448. if ( is_wp_error( $response ) ) { 
  449. $error_message = $response->get_error_message(); 
  450. die( "{$methodName_} failed: $error_message" ); 
  451. } else { 
  452. //extract the response details 
  453. $httpParsedResponseAr = array(); 
  454. parse_str(wp_remote_retrieve_body($response), $httpParsedResponseAr); 
  455.  
  456. //check for valid response 
  457. if((0 == sizeof($httpParsedResponseAr)) || !array_key_exists('ACK', $httpParsedResponseAr)) { 
  458. exit("Invalid HTTP Response for POST request($nvpreq) to $API_Endpoint."); 
  459.  
  460. return $httpParsedResponseAr;