MS_Gateway_Authorize

Authorize.net Gateway.

Defined (1)

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

/app/gateway/authorize/class-ms-gateway-authorize.php  
  1. class MS_Gateway_Authorize extends MS_Gateway { 
  2.  
  3. const ID = 'authorize'; 
  4.  
  5. /** 
  6. * Gateway singleton instance. 
  7. * @since 1.0.0 
  8. * @var string $instance 
  9. */ 
  10. public static $instance; 
  11.  
  12. /** 
  13. * Authorize.net's Customer Information Manager wrapper. 
  14. * @since 1.0.0 
  15. * @var string $cim 
  16. */ 
  17. protected static $cim; 
  18.  
  19. /** 
  20. * Authorize.net API login IP. 
  21. * @see @link https://www.authorize.net/support/CP/helpfiles/Account/Settings/Security_Settings/General_Settings/API_Login_ID_and_Transaction_Key.htm 
  22. * @since 1.0.0 
  23. * @var string $api_login_id 
  24. */ 
  25. protected $api_login_id; 
  26.  
  27. /** 
  28. * Authorize.net API transaction key. 
  29. * @since 1.0.0 
  30. * @var string $api_transaction_key 
  31. */ 
  32. protected $api_transaction_key; 
  33.  
  34. /** 
  35. * Authorize.net custom log file. 
  36. * @since 1.0.0 
  37. * @var string $log_file 
  38. */ 
  39. protected $log_file; 
  40.  
  41.  
  42. /** 
  43. * Initialize the object. 
  44. * @since 1.0.0 
  45. */ 
  46. public function after_load() { 
  47. parent::after_load(); 
  48.  
  49. $this->id = self::ID; 
  50. $this->name = __( 'Authorize.net Gateway', MS_TEXT_DOMAIN ); 
  51. $this->group = 'Authorize.net'; 
  52. $this->manual_payment = false; 
  53. $this->pro_rate = true; 
  54.  
  55. /** 
  56. * Processes purchase action. 
  57. * This function is called when a payment was made: We check if the 
  58. * transaction was successful. If it was we call `$invoice->changed()` which 
  59. * will update the membership status accordingly. 
  60. * @since 1.0.0 
  61. * @param MS_Model_Relationship $subscription The related membership relationship. 
  62. */ 
  63. public function process_purchase( $subscription ) { 
  64. do_action( 
  65. 'ms_gateway_authorize_process_purchase_before',  
  66. $subscription,  
  67. $this 
  68. ); 
  69.  
  70. if ( ! is_ssl() ) { 
  71. throw new Exception( __( 'You must use HTTPS in order to do this', 'membership' ) ); 
  72.  
  73. $invoice = $subscription->get_current_invoice(); 
  74. $member = $subscription->get_member(); 
  75.  
  76. // manage authorize customer profile 
  77. $cim_profile_id = $this->get_cim_profile_id( $member ); 
  78.  
  79. if ( empty( $cim_profile_id ) ) { 
  80. $this->create_cim_profile( $member ); 
  81. } elseif ( $cim_payment_profile_id = trim( filter_input( INPUT_POST, 'profile' ) ) ) { 
  82. // Fetch for user selected cim profile 
  83. $response = $this->get_cim()->getCustomerPaymentProfile( $cim_profile_id, $cim_payment_profile_id ); 
  84.  
  85. if ( $response->isError() ) { 
  86. throw new Exception( 
  87. __( 'The selected payment profile is invalid, enter a new credit card', MS_TEXT_DOMAIN ) 
  88. ); 
  89. } else { 
  90. $this->update_cim_profile( $member ); 
  91.  
  92. if ( ! $invoice->is_paid() ) { 
  93. // Not paid yet, request the transaction. 
  94. $this->online_purchase( $invoice, $member ); 
  95. } elseif ( 0 == $invoice->total ) { 
  96. // Paid and free. 
  97. $invoice->changed(); 
  98.  
  99. return apply_filters( 
  100. 'ms_gateway_authorize_process_purchase',  
  101. $invoice,  
  102. $this 
  103. ); 
  104.  
  105. /** 
  106. * Request automatic payment to the gateway. 
  107. * @since 1.0.0 
  108. * @param MS_Model_Relationship $subscription The related membership relationship. 
  109. * @return bool True on success. 
  110. */ 
  111. public function request_payment( $subscription ) { 
  112. $was_paid = false; 
  113.  
  114. do_action( 
  115. 'ms_gateway_authorize_request_payment_before',  
  116. $subscription,  
  117. $this 
  118. ); 
  119.  
  120. $member = $subscription->get_member(); 
  121. $invoice = $subscription->get_current_invoice(); 
  122.  
  123. if ( ! $invoice->is_paid() ) { 
  124. // Not paid yet, request the transaction. 
  125. try { 
  126. $was_paid = $this->online_purchase( $invoice, $member ); 
  127. catch( Exception $e ) { 
  128. MS_Model_Event::save_event( MS_Model_Event::TYPE_PAYMENT_FAILED, $subscription ); 
  129. MS_Helper_Debug::log( $e->getMessage() ); 
  130. } else { 
  131. // Invoice was already paid earlier. 
  132. $was_paid = true; 
  133.  
  134. do_action( 
  135. 'ms_gateway_authorize_request_payment_after',  
  136. $subscription,  
  137. $was_paid,  
  138. $this 
  139. ); 
  140.  
  141. return $was_paid; 
  142.  
  143. /** 
  144. * Processes online payments. 
  145. * Send to Authorize.net to process the payment immediatly. 
  146. * @since 1.0.0 
  147. * @param MS_Model_Invoice $invoice The invoice to pay. 
  148. * @param MS_Model_Member The member paying the invoice. 
  149. * @return bool True on success, otherwise throws an exception. 
  150. */ 
  151. protected function online_purchase( &$invoice, $member ) { 
  152. do_action( 
  153. 'ms_gateway_authorize_online_purchase_before',  
  154. $invoice,  
  155. $member,  
  156. $this 
  157. ); 
  158.  
  159. if ( 0 == $invoice->total ) { 
  160. $invoice->pay_it( MS_Gateway_Free::ID, '' ); 
  161. $invoice->add_notes( __( 'Total is zero. Payment approved. Not sent to gateway.', MS_TEXT_DOMAIN ) ); 
  162. $invoice->save(); 
  163. $invoice->changed(); 
  164. return $invoice; 
  165. $amount = MS_Helper_Billing::format_price( $invoice->total ); 
  166.  
  167. if ( $this->mode == self::MODE_SANDBOX ) { 
  168. $invoice->add_notes( __( 'Sandbox', MS_TEXT_DOMAIN ) ); 
  169.  
  170. $cim_transaction = $this->get_cim_transaction( $member ); 
  171. $cim_transaction->amount = $amount; 
  172. $cim_transaction->order->invoiceNumber = $invoice->id; 
  173.  
  174. $invoice->timestamp = time(); 
  175. $invoice->save(); 
  176.  
  177. $response = $this->get_cim()->createCustomerProfileTransaction( 
  178. 'AuthCapture',  
  179. $cim_transaction 
  180. ); 
  181.  
  182. if ( $response->isOk() ) { 
  183. $transaction_response = $response->getTransactionResponse(); 
  184.  
  185. if ( $transaction_response->approved ) { 
  186. $external_id = $response->getTransactionResponse()->transaction_id; 
  187. $invoice->pay_it( $this->id, $external_id ); 
  188. } else { 
  189. throw new Exception( 
  190. sprintf( 
  191. __( 'Payment Failed: code %s, subcode %s, reason code %, reason %s', MS_TEXT_DOMAIN ),  
  192. $transaction_response->response_code,  
  193. $transaction_response->response_subcode,  
  194. $transaction_response->response_reason_code,  
  195. $transaction_response->response_reason 
  196. ); 
  197. } else { 
  198. throw new Exception( 
  199. __( 'Payment Failed: ', MS_TEXT_DOMAIN ) . $response->getMessageText() 
  200. ); 
  201.  
  202. $invoice = apply_filters( 
  203. 'ms_gateway_authorize_online_purchase_invoice',  
  204. $invoice,  
  205. $member,  
  206. $this 
  207. ); 
  208.  
  209. return true; 
  210.  
  211. /** 
  212. * Save card info. 
  213. * Save only 4 last digits and expire date. 
  214. * @since 1.0.0 
  215. * @param MS_Model_Member $member The member to save card info. 
  216. */ 
  217. public function save_card_info( $member ) { 
  218. $cim_profile_id = $this->get_cim_profile_id( $member ); 
  219. $cim_payment_profile_id = $this->get_cim_payment_profile_id( $member ); 
  220. $profile = $this->get_cim_profile( $member ); 
  221.  
  222. if ( ! empty( $profile['customerPaymentProfileId'] ) 
  223. && $cim_payment_profile_id == $profile['customerPaymentProfileId'] 
  224. ) { 
  225. $exp_year = filter_input( INPUT_POST, 'exp_year', FILTER_VALIDATE_INT ); 
  226. $exp_month = substr( filter_input( INPUT_POST, 'exp_month', FILTER_VALIDATE_INT ), -2 ); 
  227. $member->set_gateway_profile( 
  228. $this->id,  
  229. 'card_exp',  
  230. gmdate( 'Y-m-t', strtotime( "{$exp_year}-{$exp_month}-01" ) ) 
  231. ); 
  232.  
  233. $member->set_gateway_profile( 
  234. $this->id,  
  235. 'card_num',  
  236. str_replace( 'XXXX', '', $profile['payment']['creditCard']['cardNumber'] ) 
  237. ); 
  238.  
  239. do_action( 
  240. 'ms_gateway_authorize_save_card_info_after',  
  241. $member,  
  242. $this 
  243. ); 
  244.  
  245. /** 
  246. * Loads Authorize.net lib. 
  247. * @since 1.0.0 
  248. */ 
  249. protected function load_authorize_lib() { 
  250. do_action( 'ms_gateway_authorize_load_authorize_lib', $this ); 
  251.  
  252. require_once MS_Plugin::instance()->dir . '/lib/authorize.net/autoload.php'; 
  253.  
  254. /** 
  255. * Returns the instance of AuthorizeNetCIM class. 
  256. * @since 1.0.0 
  257. * @return AuthorizeNetCIM The instance of AuthorizeNetCIM class. 
  258. */ 
  259. protected function get_cim() { 
  260. $cim = null; 
  261.  
  262. if ( ! empty( self::$cim ) ) { 
  263. $cim = self::$cim; 
  264. } else { 
  265. $this->load_authorize_lib(); 
  266.  
  267. $cim = new AuthorizeNetCIM( $this->api_login_id, $this->api_transaction_key ); 
  268. $cim->setSandbox( $this->mode != self::MODE_LIVE ); 
  269.  
  270. if ( $this->log_file ) { 
  271. $cim->setLogFile( $this->log_file ); 
  272. self::$cim = $cim; 
  273.  
  274. return apply_filters( 
  275. 'ms_gateway_authorize_get_cim',  
  276. $cim,  
  277. $this 
  278. ); 
  279.  
  280. /** 
  281. * Get saved customer information manager profile id. 
  282. * @since 1.0.0 
  283. * @param int $user_id The user Id. 
  284. * @return string The CIM profile Id. 
  285. */ 
  286. public function get_cim_profile_id( $member ) { 
  287. $cim_profile_id = $member->get_gateway_profile( 
  288. $this->id,  
  289. 'cim_profile_id' 
  290. ); 
  291.  
  292. return apply_filters( 
  293. 'ms_gateway_authorize_get_cim_profile_id',  
  294. $cim_profile_id,  
  295. $member,  
  296. $this 
  297. ); 
  298.  
  299. /** 
  300. * Get saved customer information manager payment profile id. 
  301. * @since 1.0.0 
  302. * @param int $user_id The user Id. 
  303. * @return string The CIM payment profile Id. 
  304. */ 
  305. public function get_cim_payment_profile_id( $member ) { 
  306. $cim_payment_profile_id = $member->get_gateway_profile( 
  307. $this->id,  
  308. 'cim_payment_profile_id' 
  309. ); 
  310.  
  311. return apply_filters( 
  312. 'ms_gateway_authorize_get_cim_payment_profile_id',  
  313. $cim_payment_profile_id,  
  314. $member,  
  315. $this 
  316. ); 
  317.  
  318. /** 
  319. * Save CIM profile IDs. 
  320. * @since 1.0.0 
  321. * @param MS_Model_Member $member The member to save CIM IDs. 
  322. * @param string $cim_profile_id The CIM profile ID to save. 
  323. * @param string $cim_payment_profile_id The CIM payment profile ID to save. 
  324. */ 
  325. protected function save_cim_profile( $member, $cim_profile_id, $cim_payment_profile_id ) { 
  326. $member->set_gateway_profile( 
  327. $this->id,  
  328. 'cim_profile_id',  
  329. $cim_profile_id 
  330. ); 
  331. $member->set_gateway_profile( 
  332. $this->id,  
  333. 'cim_payment_profile_id',  
  334. $cim_payment_profile_id 
  335. ); 
  336.  
  337. $this->save_card_info( $member ); 
  338. $member->save(); 
  339.  
  340. do_action( 
  341. 'ms_gateway_authorize_save_cim_profile',  
  342. $member,  
  343. $cim_profile_id,  
  344. $cim_payment_profile_id,  
  345. $this 
  346. ); 
  347.  
  348. /** 
  349. * Get customer information manager profile from Authorize.net. 
  350. * @since 1.0.0 
  351. * @param MS_Model_Member $member The member. 
  352. * @return array The A.net payment profiles array structure. 
  353. */ 
  354. public function get_cim_profile( $member ) { 
  355. $cim_profiles = array(); 
  356. $cim_profile_id = $this->get_cim_profile_id( $member ); 
  357.  
  358. if ( $cim_profile_id ) { 
  359. $response = $this->get_cim()->getCustomerProfile( $cim_profile_id ); 
  360.  
  361. if ( $response->isOk() ) { 
  362. $cim_profiles = json_decode( json_encode( $response->xml->profile ), true ); 
  363.  
  364. if ( is_array( $cim_profiles ) 
  365. && ! empty( $cim_profiles['paymentProfiles'] ) 
  366. && is_array( $cim_profiles['paymentProfiles'] ) 
  367. ) { 
  368. $cim_profiles = $cim_profiles['paymentProfiles']; 
  369.  
  370. return apply_filters( 
  371. 'ms_gateway_authorize_get_cim_profile',  
  372. $cim_profiles,  
  373. $this 
  374. ); 
  375.  
  376. /** 
  377. * Creates Authorize.net CIM profile for current user. 
  378. * @since 1.0.0 
  379. * @param MS_Model_Member $member The member to create CIM profile to. 
  380. */ 
  381. protected function create_cim_profile( $member ) { 
  382. do_action( 
  383. 'ms_gateway_authorize_create_cim_profile_before',  
  384. $member,  
  385. $this 
  386. ); 
  387.  
  388. $this->load_authorize_lib(); 
  389. $customer = new AuthorizeNetCustomer(); 
  390. $customer->merchantCustomerId = $member->id; 
  391. $customer->email = $member->email; 
  392. $customer->paymentProfiles[] = $this->create_cim_payment_profile(); 
  393.  
  394. $response = $this->get_cim()->createCustomerProfile( $customer ); 
  395.  
  396. if ( $response->isError() ) { 
  397. MS_Helper_Debug::log( $response ); 
  398.  
  399. // Duplicate record, delete the old one. 
  400. if ( 'E00039' == $response->xml->messages->message->code ) { 
  401. $cim_profile_id = str_replace( 'A duplicate record with ID ', '', $response->xml->messages->message->text ); 
  402. $cim_profile_id = (int) str_replace( ' already exists.', '', $cim_profile_id ); 
  403.  
  404. $this->get_cim()->deleteCustomerProfile( $cim_profile_id ); 
  405.  
  406. // Try again 
  407. $this->create_cim_profile( $member ); 
  408. return; 
  409. } else { 
  410. throw new Exception( 
  411. __( 'Payment failed due to CIM profile not created: ', MS_TEXT_DOMAIN ) . $response->getMessageText() 
  412. ); 
  413.  
  414. $cim_profile_id = $response->getCustomerProfileId(); 
  415. $cim_payment_profile_id = $response->getCustomerPaymentProfileIds(); 
  416. $this->save_cim_profile( $member, $cim_profile_id, $cim_payment_profile_id ); 
  417.  
  418. /** 
  419. * Updates CIM profile by adding a new credit card. 
  420. * @since 1.0.0 
  421. * @param MS_Model_Member $member The member to update CIM profile. 
  422. */ 
  423. public function update_cim_profile( $member ) { 
  424. do_action( 
  425. 'ms_gateway_authorize_update_cim_profile_before',  
  426. $member,  
  427. $this 
  428. ); 
  429.  
  430. $cim_profile_id = $this->get_cim_profile_id( $member ); 
  431. $cim_payment_profile_id = $this->get_cim_payment_profile_id( $member ); 
  432.  
  433. if ( empty( $cim_payment_profile_id ) ) { 
  434. $response = $this->get_cim()->createCustomerPaymentProfile( 
  435. $cim_profile_id,  
  436. $this->create_cim_payment_profile() 
  437. ); 
  438. $cim_payment_profile_id = $response->getCustomerPaymentProfileIds(); 
  439. } else { 
  440. $response = $this->get_cim()->updateCustomerPaymentProfile( 
  441. $cim_profile_id,  
  442. $cim_payment_profile_id,  
  443. self::create_cim_payment_profile() 
  444. ); 
  445.  
  446. // If the error is not due to a duplicate customer payment profile. 
  447. if ( $response->isError() && 'E00039' != $response->xml->messages->message->code ) { 
  448. throw new Exception( 
  449. __( 'Payment failed due to CIM profile not updated: ', MS_TEXT_DOMAIN ) . $response->getMessageText() 
  450. ); 
  451.  
  452. $this->save_cim_profile( $member, $cim_profile_id, $cim_payment_profile_id ); 
  453.  
  454. /** 
  455. * Creates CIM payment profile and fills it with posted credit card data. 
  456. * @since 1.0.0 
  457. * @return AuthorizeNetPaymentProfile The instance of AuthorizeNetPaymentProfile class. 
  458. */ 
  459. protected function create_cim_payment_profile() { 
  460. $this->load_authorize_lib(); 
  461.  
  462. $payment = new AuthorizeNetPaymentProfile(); 
  463.  
  464. // billing information 
  465. $payment->billTo->firstName = substr( trim( filter_input( INPUT_POST, 'first_name' ) ), 0, 50 ); 
  466. $payment->billTo->lastName = substr( trim( filter_input( INPUT_POST, 'last_name' ) ), 0, 50 ); 
  467. $payment->billTo->company = substr( trim( filter_input( INPUT_POST, 'company' ) ), 0, 50 ); 
  468. $payment->billTo->address = substr( trim( filter_input( INPUT_POST, 'address' ) ), 0, 60 ); 
  469. $payment->billTo->city = substr( trim( filter_input( INPUT_POST, 'city' ) ), 0, 40 ); 
  470. $payment->billTo->state = substr( trim( filter_input( INPUT_POST, 'state' ) ), 0, 40 ); 
  471. $payment->billTo->zip = substr( trim( filter_input( INPUT_POST, 'zip' ) ), 0, 20 ); 
  472. $payment->billTo->country = substr( trim( filter_input( INPUT_POST, 'country' ) ), 0, 60 ); 
  473. $payment->billTo->phoneNumber = substr( trim( filter_input( INPUT_POST, 'phone' ) ), 0, 25 ); 
  474.  
  475. // card information 
  476. $payment->payment->creditCard->cardNumber = preg_replace( '/\D/', '', filter_input( INPUT_POST, 'card_num' ) ); 
  477. $payment->payment->creditCard->cardCode = trim( filter_input( INPUT_POST, 'card_code' ) ); 
  478. $payment->payment->creditCard->expirationDate = sprintf( 
  479. '%04d-%02d',  
  480. filter_input( INPUT_POST, 'exp_year', FILTER_VALIDATE_INT ),  
  481. substr( filter_input( INPUT_POST, 'exp_month', FILTER_VALIDATE_INT ), -2 ) 
  482. ); 
  483.  
  484. return apply_filters( 
  485. 'ms_gateway_authorize_create_cim_payment_profile',  
  486. $payment,  
  487. $this 
  488. ); 
  489.  
  490. /** 
  491. * Initializes and returns Authorize.net CIM transaction object. 
  492. * @since 1.0.0 
  493. * @param MS_Model_Member $member The member. 
  494. * @return AuthorizeNetTransaction The instance of AuthorizeNetTransaction class. 
  495. */ 
  496. protected function get_cim_transaction( $member ) { 
  497. $this->load_authorize_lib(); 
  498.  
  499. $cim_profile_id = $this->get_cim_profile_id( $member ); 
  500. $cim_payment_profile_id = $this->get_cim_payment_profile_id( $member ); 
  501.  
  502. if ( empty( $cim_profile_id ) || empty( $cim_payment_profile_id ) ) { 
  503. throw new Exception( __( 'CIM Payment profile not found', MS_TEXT_DOMAIN ) ); 
  504.  
  505. $transaction = new AuthorizeNetTransaction(); 
  506. $transaction->customerProfileId = $cim_profile_id; 
  507. $transaction->customerPaymentProfileId = $cim_payment_profile_id; 
  508.  
  509. return apply_filters( 
  510. 'ms_gateway_authorize_get_cim_transaction',  
  511. $transaction,  
  512. $member,  
  513. $this 
  514. ); 
  515.  
  516. /** 
  517. * Verify required fields. 
  518. * @since 1.0.0 
  519. * @return boolean True if it is configured. 
  520. */ 
  521. public function is_configured() { 
  522. $is_configured = true; 
  523. $required = array( 'api_login_id', 'api_transaction_key' ); 
  524.  
  525. foreach ( $required as $field ) { 
  526. $value = $this->$field; 
  527. if ( empty( $value ) ) { 
  528. $is_configured = false; 
  529. break; 
  530.  
  531. return apply_filters( 
  532. 'ms_gateway_authorize_is_configured',  
  533. $is_configured,  
  534. $this 
  535. );