MS_Gateway_Paypalstandard

Gateway: Paypal Standard.

Defined (1)

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

/app/gateway/paypalstandard/class-ms-gateway-paypalstandard.php  
  1. class MS_Gateway_Paypalstandard extends MS_Gateway { 
  2.  
  3. const ID = 'paypalstandard'; 
  4.  
  5. /** 
  6. * Gateway singleton instance. 
  7. * @since 1.0.0 
  8. * @var string $instance 
  9. */ 
  10. public static $instance; 
  11.  
  12. /** 
  13. * Paypal merchant ID. 
  14. * @since 1.0.0 
  15. * @var bool $merchant_id 
  16. */ 
  17. protected $merchant_id; 
  18.  
  19. /** 
  20. * Paypal country site. 
  21. * @since 1.0.0 
  22. * @var bool $paypal_site 
  23. */ 
  24. protected $paypal_site; 
  25.  
  26.  
  27. /** 
  28. * Hook to add custom transaction status. 
  29. * @since 1.0.0 
  30. */ 
  31. public function after_load() { 
  32. parent::after_load(); 
  33.  
  34. $this->id = self::ID; 
  35. $this->name = __( 'PayPal Standard Gateway', MS_TEXT_DOMAIN ); 
  36. $this->group = 'PayPal'; 
  37. $this->manual_payment = true; 
  38. $this->pro_rate = false; 
  39.  
  40. /** 
  41. * Processes gateway IPN return. 
  42. * @since 1.0.0 
  43. */ 
  44. public function handle_return() { 
  45. if ( ( isset($_POST['payment_status'] ) || isset( $_POST['txn_type'] ) ) 
  46. && ! empty( $_POST['invoice'] ) 
  47. ) { 
  48. if ( $this->is_live_mode() ) { 
  49. $domain = 'https://www.paypal.com'; 
  50. } else { 
  51. $domain = 'https://www.sandbox.paypal.com'; 
  52.  
  53. // Paypal post authenticity verification 
  54. $ipn_data = (array) stripslashes_deep( $_POST ); 
  55. $ipn_data['cmd'] = '_notify-validate'; 
  56. $response = wp_remote_post( 
  57. $domain . '/cgi-bin/webscr',  
  58. array( 
  59. 'timeout' => 60,  
  60. 'sslverify' => false,  
  61. 'httpversion' => '1.1',  
  62. 'body' => $ipn_data,  
  63. ); 
  64.  
  65. if ( ! is_wp_error( $response ) 
  66. && 200 == $response['response']['code'] 
  67. && ! empty( $response['body'] ) 
  68. && 'VERIFIED' == $response['body'] 
  69. ) { 
  70. MS_Helper_Debug::log( 'PayPal Transaction Verified' ); 
  71. } else { 
  72. $error = 'Response Error: Unexpected transaction response'; 
  73. MS_Helper_Debug::log( $error ); 
  74. MS_Helper_Debug::log( $response ); 
  75. wp_die( $error ); 
  76.  
  77. if ( empty( $_POST['invoice'] ) ) { 
  78. $error = 'Response Error: No relationship identification found.'; 
  79. MS_Helper_Debug::log( $error ); 
  80. MS_Helper_Debug::log( $response ); 
  81. exit; 
  82.  
  83. $invoice = MS_Factory::load( 'MS_Model_Invoice', $_POST['invoice'] ); 
  84. $subscription = $invoice->get_subscription(); 
  85. $membership = $subscription->get_membership(); 
  86. $member = $subscription->get_member(); 
  87.  
  88. $currency = $_POST['mc_currency']; 
  89. $status = null; 
  90. $notes_pay = ''; 
  91. $notes_txn = ''; 
  92. $external_id = null; 
  93. $amount = 0; 
  94. $pay_it = false; 
  95.  
  96. // @todo : Does this condition make sense? If $invoice would be 
  97. // empty then $subscription would also be invalid... 
  98. if ( empty( $invoice ) ) { 
  99. $invoice = $subscription->get_current_invoice(); 
  100.  
  101. // Process PayPal payment status 
  102. if ( ! empty( $_POST['payment_status'] ) ) { 
  103. $amount = (float) $_POST['mc_gross']; 
  104. $external_id = $_POST['txn_id']; 
  105.  
  106. switch ( $_POST['payment_status'] ) { 
  107. // Successful payment 
  108. case 'Completed': 
  109. case 'Processed': 
  110. if ( $amount == $invoice->total ) { 
  111. $pay_it = true; 
  112. } else { 
  113. $notes_pay = __( 'Payment amount differs from invoice total.', MS_TEXT_DOMAIN ); 
  114. $status = MS_Model_Invoice::STATUS_DENIED; 
  115. break; 
  116.  
  117. case 'Reversed': 
  118. $notes_pay = __( 'Last transaction has been reversed. Reason: Payment has been reversed (charge back).', MS_TEXT_DOMAIN ); 
  119. $status = MS_Model_Invoice::STATUS_DENIED; 
  120. break; 
  121.  
  122. case 'Refunded': 
  123. $notes_pay = __( 'Last transaction has been reversed. Reason: Payment has been refunded.', MS_TEXT_DOMAIN ); 
  124. $status = MS_Model_Invoice::STATUS_DENIED; 
  125. break; 
  126.  
  127. case 'Denied': 
  128. $notes_pay = __( 'Last transaction has been reversed. Reason: Payment Denied.', MS_TEXT_DOMAIN ); 
  129. $status = MS_Model_Invoice::STATUS_DENIED; 
  130. break; 
  131.  
  132. case 'Pending': 
  133. $pending_str = array( 
  134. 'address' => __( 'Customer did not include a confirmed shipping address', MS_TEXT_DOMAIN ),  
  135. 'authorization' => __( 'Funds not captured yet', MS_TEXT_DOMAIN ),  
  136. 'echeck' => __( 'eCheck that has not cleared yet', MS_TEXT_DOMAIN ),  
  137. 'intl' => __( 'Payment waiting for aproval by service provider', MS_TEXT_DOMAIN ),  
  138. 'multi-currency' => __( 'Payment waiting for service provider to handle multi-currency process', MS_TEXT_DOMAIN ),  
  139. 'unilateral' => __( 'Customer did not register or confirm his/her email yet', MS_TEXT_DOMAIN ),  
  140. 'upgrade' => __( 'Waiting for service provider to upgrade the PayPal account', MS_TEXT_DOMAIN ),  
  141. 'verify' => __( 'Waiting for service provider to verify his/her PayPal account', MS_TEXT_DOMAIN ),  
  142. '*' => '?',  
  143. ); 
  144.  
  145. lib2()->array->strip_slashes( $_POST, 'pending_reason' ); 
  146. $reason = $_POST['pending_reason']; 
  147. $notes_pay = __( 'Last transaction is pending. Reason: ', MS_TEXT_DOMAIN ) . 
  148. ( isset($pending_str[$reason] ) ? $pending_str[$reason] : $pending_str['*'] ); 
  149. $status = MS_Model_Invoice::STATUS_PENDING; 
  150. break; 
  151.  
  152. default: 
  153. case 'Partially-Refunded': 
  154. case 'In-Progress': 
  155. $notes_pay = __( 'Not handling payment_status: ', MS_TEXT_DOMAIN ) . 
  156. $_POST['payment_status']; 
  157. MS_Helper_Debug::log( $notes_pay ); 
  158. break; 
  159.  
  160. // Check for subscription details 
  161. if ( ! empty( $_POST['txn_type'] ) ) { 
  162. switch ( $_POST['txn_type'] ) { 
  163. case 'subscr_signup': 
  164. case 'subscr_payment': 
  165. // Payment was received 
  166. $notes_txn = __( 'Paypal subscripton profile has been created.', MS_TEXT_DOMAIN ); 
  167. if ( 0 == $invoice->total ) { 
  168. $pay_it = true; 
  169. break; 
  170.  
  171. case 'subscr_modify': 
  172. // Payment profile was modified 
  173. $notes_txn = __( 'Paypal subscription profile has been modified.', MS_TEXT_DOMAIN ); 
  174. break; 
  175.  
  176. case 'recurring_payment_profile_canceled': 
  177. case 'subscr_cancel': 
  178. // Subscription was manually cancelled. 
  179. $notes_txn = __( 'Paypal subscription profile has been canceled.', MS_TEXT_DOMAIN ); 
  180. $member->cancel_membership( $membership->id ); 
  181. $member->save(); 
  182. break; 
  183.  
  184. case 'recurring_payment_suspended': 
  185. // Recurring subscription was manually suspended. 
  186. $notes_txn = __( 'Paypal subscription profile has been suspended.', MS_TEXT_DOMAIN ); 
  187. $member->cancel_membership( $membership->id ); 
  188. $member->save(); 
  189. break; 
  190.  
  191. case 'recurring_payment_suspended_due_to_max_failed_payment': 
  192. // Recurring subscription was automatically suspended. 
  193. $notes_txn = __( 'Paypal subscription profile has failed.', MS_TEXT_DOMAIN ); 
  194. $member->cancel_membership( $membership->id ); 
  195. $member->save(); 
  196. break; 
  197.  
  198. case 'new_case': 
  199. // New Dispute was filed for a payment. 
  200. $status = MS_Model_Invoice::STATUS_DENIED; 
  201. break; 
  202.  
  203. case 'subscr_eot': 
  204. /** 
  205. * Meaning: Subscription expired. 
  206. * - after a one-time payment was madeafter last 
  207. * - after last transaction in a recurring subscription 
  208. * - payment failed 
  209. * - ... 
  210. * We do not handle this event... 
  211. * One time payment sends 3 messages: 
  212. * 1. subscr_start (new subscription starts) 
  213. * 2. subscr_payment (payment confirmed) 
  214. * 3. subscr_eot (subscription ends) 
  215. */ 
  216. $notes_txn = __( 'No more payments will be made for this subscription.', MS_TEXT_DOMAIN ); 
  217. break; 
  218.  
  219. default: 
  220. // Other event that we do not have a case for... 
  221. $notes_txn = __( 'Not handling txn_type: ', MS_TEXT_DOMAIN ) . $_POST['txn_type']; 
  222. MS_Helper_Debug::log( $notes_txn ); 
  223. break; 
  224.  
  225. if ( ! empty( $notes_pay ) ) { $invoice->add_notes( $notes_pay ); } 
  226. if ( ! empty( $notes_txn ) ) { $invoice->add_notes( $notes_txn ); } 
  227.  
  228. $invoice->save(); 
  229.  
  230. if ( $pay_it ) { 
  231. $invoice->pay_it( $this->id, $external_id ); 
  232. } elseif ( ! empty( $status ) ) { 
  233. $invoice->status = $status; 
  234. $invoice->save(); 
  235. $invoice->changed(); 
  236.  
  237. do_action( 
  238. 'ms_gateway_paypalstandard_payment_processed_' . $status,  
  239. $invoice,  
  240. $subscription 
  241. ); 
  242. } else { 
  243. // Did not find expected POST variables. Possible access attempt from a non PayPal site. 
  244.  
  245. $u_agent = $_SERVER['HTTP_USER_AGENT']; 
  246. if ( false === strpos( $u_agent, 'PayPal' ) ) { 
  247. // Very likely someone tried to open the URL manually. Redirect to home page 
  248. $notes = 'Error: Missing POST variables. Redirect user to Home-URL.'; 
  249. MS_Helper_Debug::log( $notes ); 
  250. wp_safe_redirect( home_url() ); 
  251. exit; 
  252. } else { 
  253. // PayPal did provide invalid details... 
  254. status_header( 404 ); 
  255. $notes = 'Error: Missing POST variables. Identification is not possible.'; 
  256. MS_Helper_Debug::log( $notes ); 
  257. exit; 
  258.  
  259. do_action( 
  260. 'ms_gateway_paypalstandard_handle_return_after',  
  261. $this 
  262. ); 
  263.  
  264. /** 
  265. * Get paypal country sites list. 
  266. * @see MS_Gateway::get_country_codes() 
  267. * @since 1.0.0 
  268. * @return array 
  269. */ 
  270. public function get_paypal_sites() { 
  271. return apply_filters( 
  272. 'ms_gateway_paylpaystandard_get_paypal_sites',  
  273. self::get_country_codes() 
  274. ); 
  275.  
  276. /** 
  277. * Verify required fields. 
  278. * @since 1.0.0 
  279. * @return boolean 
  280. */ 
  281. public function is_configured() { 
  282. $is_configured = true; 
  283. $required = array( 'merchant_id', 'paypal_site' ); 
  284.  
  285. foreach ( $required as $field ) { 
  286. $value = $this->$field; 
  287. if ( empty( $value ) ) { 
  288. $is_configured = false; 
  289. break; 
  290.  
  291. return apply_filters( 
  292. 'ms_gateway_paypalstandard_is_configured',  
  293. $is_configured 
  294. ); 
  295.  
  296. /** 
  297. * Validate specific property before set. 
  298. * @since 1.0.0 
  299. * @access public 
  300. * @param string $name The name of a property to associate. 
  301. * @param mixed $value The value of a property. 
  302. */ 
  303. public function __set( $property, $value ) { 
  304. if ( property_exists( $this, $property ) ) { 
  305. switch ( $property ) { 
  306. case 'paypal_site': 
  307. if ( array_key_exists( $value, self::get_paypal_sites() ) ) { 
  308. $this->$property = $value; 
  309. break; 
  310.  
  311. default: 
  312. parent::__set( $property, $value ); 
  313. break; 
  314.  
  315. do_action( 
  316. 'ms_gateway_paypalstandard__set_after',  
  317. $property,  
  318. $value,  
  319. $this 
  320. ); 
  321.