MS_Gateway_Paypalsingle

Gateway: Paypal Single.

Defined (1)

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

/app/gateway/paypalsingle/class-ms-gateway-paypalsingle.php  
  1. class MS_Gateway_Paypalsingle extends MS_Gateway { 
  2.  
  3. const ID = 'paypalsingle'; 
  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/seller's email. 
  14. * @since 1.0.0 
  15. * @var bool $paypal_email 
  16. */ 
  17. protected $paypal_email; 
  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. * This is called by the MS_Factory 
  30. * @since 1.0.0 
  31. */ 
  32. public function after_load() { 
  33. parent::after_load(); 
  34.  
  35. $this->id = self::ID; 
  36. $this->name = __( 'PayPal Single Gateway', 'membership2' ); 
  37. $this->group = 'PayPal'; 
  38. $this->manual_payment = true; // Recurring billed/paid manually 
  39. $this->pro_rate = true; 
  40.  
  41. /** 
  42. * Processes gateway IPN return. 
  43. * @since 1.0.0 
  44. * @param MS_Model_Transactionlog $log Optional. A transaction log item 
  45. * that will be updated instead of creating a new log entry. 
  46. */ 
  47. public function handle_return( $log = false ) { 
  48. $success = false; 
  49. $exit = false; 
  50. $redirect = false; 
  51. $notes = ''; 
  52. $status = null; 
  53. $invoice_id = 0; 
  54. $subscription_id = 0; 
  55. $amount = 0; 
  56.  
  57. do_action( 
  58. 'ms_gateway_paypalsingle_handle_return_before',  
  59. $this 
  60. ); 
  61.  
  62. lib3()->array->strip_slashes( $_POST, 'pending_reason' ); 
  63.  
  64. if ( ( isset($_POST['payment_status'] ) || isset( $_POST['txn_type'] ) ) 
  65. && ! empty( $_POST['invoice'] ) 
  66. ) { 
  67. if ( $this->is_live_mode() ) { 
  68. $domain = 'https://www.paypal.com'; 
  69. } else { 
  70. $domain = 'https://www.sandbox.paypal.com'; 
  71.  
  72. // Ask PayPal to validate our $_POST data. 
  73. $ipn_data = (array) stripslashes_deep( $_POST ); 
  74. $ipn_data['cmd'] = '_notify-validate'; 
  75. $response = wp_remote_post( 
  76. $domain . '/cgi-bin/webscr',  
  77. array( 
  78. 'timeout' => 60,  
  79. 'sslverify' => false,  
  80. 'httpversion' => '1.1',  
  81. 'body' => $ipn_data,  
  82. ); 
  83.  
  84. $invoice_id = intval( $_POST['invoice'] ); 
  85. $external_id = $_POST['txn_id']; 
  86. $amount = (float) $_POST['mc_gross']; 
  87. $currency = $_POST['mc_currency']; 
  88. $invoice = MS_Factory::load( 'MS_Model_Invoice', $invoice_id ); 
  89.  
  90. if ( ! is_wp_error( $response ) 
  91. && ! MS_Model_Transactionlog::was_processed( self::ID, $external_id ) 
  92. && 200 == $response['response']['code'] 
  93. && ! empty( $response['body'] ) 
  94. && 'VERIFIED' == $response['body'] 
  95. && $invoice->id == $invoice_id 
  96. ) { 
  97. $new_status = false; 
  98. $subscription = $invoice->get_subscription(); 
  99. $membership = $subscription->get_membership(); 
  100. $member = $subscription->get_member(); 
  101. $subscription_id = $subscription->id; 
  102.  
  103. // Process PayPal response 
  104. switch ( $_POST['payment_status'] ) { 
  105. // Successful payment 
  106. case 'Completed': 
  107. case 'Processed': 
  108. $success = true; 
  109. if ( $amount == $invoice->total ) { 
  110. $notes .= __( 'Payment successful', 'membership2' ); 
  111. } else { 
  112. $notes .= __( 'Payment registered, though amount differs from invoice.', 'membership2' ); 
  113. break; 
  114.  
  115. case 'Reversed': 
  116. $notes = __( 'Last transaction has been reversed. Reason: Payment has been reversed (charge back). ', 'membership2' ); 
  117. $status = MS_Model_Invoice::STATUS_DENIED; 
  118. break; 
  119.  
  120. case 'Refunded': 
  121. $notes = __( 'Last transaction has been reversed. Reason: Payment has been refunded', 'membership2' ); 
  122. $status = MS_Model_Invoice::STATUS_DENIED; 
  123. break; 
  124.  
  125. case 'Denied': 
  126. $notes = __( 'Last transaction has been reversed. Reason: Payment Denied', 'membership2' ); 
  127. $status = MS_Model_Invoice::STATUS_DENIED; 
  128. break; 
  129.  
  130. case 'Pending': 
  131. $pending_str = array( 
  132. 'address' => __( 'Customer did not include a confirmed shipping address', 'membership2' ),  
  133. 'authorization' => __( 'Funds not captured yet', 'membership2' ),  
  134. 'echeck' => __( 'eCheck that has not cleared yet', 'membership2' ),  
  135. 'intl' => __( 'Payment waiting for aproval by service provider', 'membership2' ),  
  136. 'multi-currency' => __( 'Payment waiting for service provider to handle multi-currency process', 'membership2' ),  
  137. 'unilateral' => __( 'Customer did not register or confirm his/her email yet', 'membership2' ),  
  138. 'upgrade' => __( 'Waiting for service provider to upgrade the PayPal account', 'membership2' ),  
  139. 'verify' => __( 'Waiting for service provider to verify his/her PayPal account', 'membership2' ),  
  140. '*' => '',  
  141. ); 
  142.  
  143. $reason = $_POST['pending_reason']; 
  144. $notes = __( 'Last transaction is pending. Reason: ', 'membership2' ) . 
  145. ( isset($pending_str[$reason] ) ? $pending_str[$reason] : $pending_str['*'] ); 
  146. $status = MS_Model_Invoice::STATUS_PENDING; 
  147. break; 
  148.  
  149. default: 
  150. case 'Partially-Refunded': 
  151. case 'In-Progress': 
  152. $success = null; 
  153. break; 
  154.  
  155. if ( 'new_case' == $_POST['txn_type'] 
  156. && 'dispute' == $_POST['case_type'] 
  157. ) { 
  158. // Status: Dispute 
  159. $status = MS_Model_Invoice::STATUS_DENIED; 
  160. $notes = __( 'Dispute about this payment', 'membership2' ); 
  161.  
  162. if ( ! empty( $notes ) ) { $invoice->add_notes( $notes ); } 
  163. $invoice->save(); 
  164.  
  165. if ( $success ) { 
  166. $invoice->pay_it( $this->id, $external_id ); 
  167. } elseif ( ! empty( $status ) ) { 
  168. $invoice->status = $status; 
  169. $invoice->save(); 
  170. $invoice->changed(); 
  171.  
  172. do_action( 
  173. 'ms_gateway_paypalsingle_payment_processed_' . $status,  
  174. $invoice,  
  175. $subscription 
  176. ); 
  177. } else { 
  178. $reason = 'Unexpected transaction response'; 
  179. switch ( true ) { 
  180. case is_wp_error( $response ): 
  181. $reason = 'Response is error'; 
  182. break; 
  183.  
  184. case 200 != $response['response']['code']: 
  185. $reason = 'Response code is ' . $response['response']['code']; 
  186. break; 
  187.  
  188. case empty( $response['body'] ): 
  189. $reason = 'Response is empty'; 
  190. break; 
  191.  
  192. case 'VERIFIED' != $response['body']: 
  193. $reason = sprintf( 
  194. 'Expected response "%s" but got "%s"',  
  195. 'VERIFIED',  
  196. (string) $response['body'] 
  197. ); 
  198. break; 
  199.  
  200. case $invoice->id != $invoice_id: 
  201. $reason = sprintf( 
  202. 'Expected invoice_id "%s" but got "%s"',  
  203. $invoice->id,  
  204. $invoice_id 
  205. ); 
  206. break; 
  207.  
  208. case MS_Model_Transactionlog::was_processed( self::ID, $external_id ): 
  209. $reason = 'Duplicate: Already processed that transaction.'; 
  210. break; 
  211.  
  212. $notes = 'Response Error: ' . $reason; 
  213. $exit = true; 
  214. } else { 
  215. // Did not find expected POST variables. Possible access attempt from a non PayPal site. 
  216.  
  217. $u_agent = $_SERVER['HTTP_USER_AGENT']; 
  218. if ( false === strpos( $u_agent, 'PayPal' ) ) { 
  219. // Very likely someone tried to open the URL manually. Redirect to home page 
  220. $notes = 'Error: Missing POST variables. Redirect user to Home-URL.'; 
  221. $redirect = MS_Helper_Utility::home_url( '/' ); 
  222. } else { 
  223. $notes = 'Error: Missing POST variables. Identification is not possible.'; 
  224. $exit = true; 
  225.  
  226. if ( ! $log ) { 
  227. do_action( 
  228. 'ms_gateway_transaction_log',  
  229. self::ID, // gateway ID 
  230. 'handle', // request|process|handle 
  231. $success, // success flag 
  232. $subscription_id, // subscription ID 
  233. $invoice_id, // invoice ID 
  234. $amount, // charged amount 
  235. $notes, // Descriptive text 
  236. $external_id // External ID 
  237. ); 
  238.  
  239. if ( $redirect ) { 
  240. wp_safe_redirect( $redirect ); 
  241. exit; 
  242. if ( $exit ) { 
  243. exit; 
  244. } else { 
  245. $log->invoice_id = $invoice_id; 
  246. $log->subscription_id = $subscription_id; 
  247. $log->amount = $amount; 
  248. $log->description = $notes; 
  249. $log->external_id = $external_id; 
  250. if ( $success ) { 
  251. $log->manual_state( 'ok' ); 
  252. $log->save(); 
  253.  
  254. do_action( 
  255. 'ms_gateway_paypalsingle_handle_return_after',  
  256. $this,  
  257. $log 
  258. ); 
  259.  
  260. if ( $log ) { 
  261. return $log; 
  262.  
  263. /** 
  264. * Get paypal country sites list. 
  265. * @see MS_Gateway::get_country_codes() 
  266. * @since 1.0.0 
  267. * @return array 
  268. */ 
  269. public function get_paypal_sites() { 
  270. return apply_filters( 
  271. 'ms_gateway_paylpaysingle_get_paypal_sites',  
  272. self::get_country_codes() 
  273. ); 
  274.  
  275. /** 
  276. * Verify required fields. 
  277. * @since 1.0.0 
  278. * @return boolean 
  279. */ 
  280. public function is_configured() { 
  281. $is_configured = true; 
  282. $required = array( 'paypal_email', 'paypal_site' ); 
  283.  
  284. foreach ( $required as $field ) { 
  285. $value = $this->$field; 
  286. if ( empty( $value ) ) { 
  287. $is_configured = false; 
  288. break; 
  289.  
  290. return apply_filters( 
  291. 'ms_gateway_paypalsingle_is_configured',  
  292. $is_configured 
  293. ); 
  294.  
  295. /** 
  296. * Validate specific property before set. 
  297. * @since 1.0.0 
  298. * @access public 
  299. * @param string $name The name of a property to associate. 
  300. * @param mixed $value The value of a property. 
  301. */ 
  302. public function __set( $property, $value ) { 
  303. if ( property_exists( $this, $property ) ) { 
  304. switch ( $property ) { 
  305. case 'paypal_site': 
  306. if ( array_key_exists( $value, self::get_paypal_sites() ) ) { 
  307. $this->$property = $value; 
  308. break; 
  309.  
  310. default: 
  311. parent::__set( $property, $value ); 
  312. break; 
  313.  
  314. do_action( 
  315. 'ms_gateway_paypalsingle__set_after',  
  316. $property,  
  317. $value,  
  318. $this 
  319. ); 
  320.