class-wc-gateway-simplify-commerce.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. <?php
  2. if ( ! defined( 'ABSPATH' ) ) {
  3. exit; // Exit if accessed directly
  4. }
  5. /**
  6. * Simplify Commerce Gateway.
  7. *
  8. * @class WC_Gateway_Simplify_Commerce
  9. * @extends WC_Payment_Gateway_CC
  10. * @since 2.2.0
  11. * @version 1.0.0
  12. * @package WooCommerce/Classes/Payment
  13. * @author WooThemes
  14. */
  15. class WC_Gateway_Simplify_Commerce extends WC_Payment_Gateway_CC {
  16. /**
  17. * Constructor.
  18. */
  19. public function __construct() {
  20. $this->id = 'simplify_commerce';
  21. $this->method_title = __( 'Simplify Commerce', 'woocommerce' );
  22. $this->method_description = __( 'Take payments via Simplify Commerce - uses simplify.js to create card tokens and the Simplify Commerce SDK. Requires SSL when sandbox is disabled.', 'woocommerce' );
  23. $this->new_method_label = __( 'Use a new card', 'woocommerce' );
  24. $this->has_fields = true;
  25. $this->supports = array(
  26. 'subscriptions',
  27. 'products',
  28. 'subscription_cancellation',
  29. 'subscription_reactivation',
  30. 'subscription_suspension',
  31. 'subscription_amount_changes',
  32. 'subscription_payment_method_change', // Subscriptions 1.n compatibility
  33. 'subscription_payment_method_change_customer',
  34. 'subscription_payment_method_change_admin',
  35. 'subscription_date_changes',
  36. 'multiple_subscriptions',
  37. 'default_credit_card_form',
  38. 'tokenization',
  39. 'refunds',
  40. 'pre-orders',
  41. );
  42. $this->view_transaction_url = 'https://www.simplify.com/commerce/app#/payment/%s';
  43. // Load the form fields
  44. $this->init_form_fields();
  45. // Load the settings.
  46. $this->init_settings();
  47. // Get setting values
  48. $this->title = $this->get_option( 'title' );
  49. $this->description = $this->get_option( 'description' );
  50. $this->enabled = $this->get_option( 'enabled' );
  51. $this->mode = $this->get_option( 'mode', 'standard' );
  52. $this->modal_color = $this->get_option( 'modal_color', '#a46497' );
  53. $this->sandbox = $this->get_option( 'sandbox' );
  54. $this->public_key = ( 'no' === $this->sandbox ) ? $this->get_option( 'public_key' ) : $this->get_option( 'sandbox_public_key' );
  55. $this->private_key = ( 'no' === $this->sandbox ) ? $this->get_option( 'private_key' ) : $this->get_option( 'sandbox_private_key' );
  56. $this->init_simplify_sdk();
  57. // Hooks
  58. add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );
  59. add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
  60. add_action( 'woocommerce_receipt_' . $this->id, array( $this, 'receipt_page' ) );
  61. add_action( 'woocommerce_api_wc_gateway_simplify_commerce', array( $this, 'return_handler' ) );
  62. }
  63. /**
  64. * Init Simplify SDK.
  65. */
  66. protected function init_simplify_sdk() {
  67. // Include lib
  68. require_once dirname( __FILE__ ) . '/includes/Simplify.php';
  69. Simplify::$publicKey = $this->public_key;
  70. Simplify::$privateKey = $this->private_key;
  71. Simplify::$userAgent = 'WooCommerce/' . WC()->version;
  72. }
  73. /**
  74. * Admin Panel Options.
  75. * - Options for bits like 'title' and availability on a country-by-country basis.
  76. */
  77. public function admin_options() {
  78. ?>
  79. <h3><?php _e( 'Simplify Commerce by MasterCard', 'woocommerce' ); ?></h3>
  80. <?php if ( empty( $this->public_key ) ) : ?>
  81. <div class="simplify-commerce-banner updated">
  82. <img src="<?php echo WC()->plugin_url() . '/includes/gateways/simplify-commerce/assets/images/logo.png'; ?>" />
  83. <p class="main"><strong><?php _e( 'Getting started', 'woocommerce' ); ?></strong></p>
  84. <p><?php _e( 'Simplify Commerce is your merchant account and payment gateway all rolled into one. Choose Simplify Commerce as your WooCommerce payment gateway to get access to your money quickly with a powerful, secure payment engine backed by MasterCard.', 'woocommerce' ); ?></p>
  85. <p><a href="https://www.simplify.com/commerce/partners/woocommerce#/signup" target="_blank" class="button button-primary"><?php _e( 'Sign up for Simplify Commerce', 'woocommerce' ); ?></a> <a href="https://www.simplify.com/commerce/partners/woocommerce#/" target="_blank" class="button"><?php _e( 'Learn more', 'woocommerce' ); ?></a></p>
  86. </div>
  87. <?php else : ?>
  88. <p><?php _e( 'Simplify Commerce is your merchant account and payment gateway all rolled into one. Choose Simplify Commerce as your WooCommerce payment gateway to get access to your money quickly with a powerful, secure payment engine backed by MasterCard.', 'woocommerce' ); ?></p>
  89. <?php endif; ?>
  90. <?php $this->checks(); ?>
  91. <table class="form-table">
  92. <?php $this->generate_settings_html(); ?>
  93. <script type="text/javascript">
  94. jQuery( '#woocommerce_simplify_commerce_sandbox' ).on( 'change', function() {
  95. var sandbox = jQuery( '#woocommerce_simplify_commerce_sandbox_public_key, #woocommerce_simplify_commerce_sandbox_private_key' ).closest( 'tr' ),
  96. production = jQuery( '#woocommerce_simplify_commerce_public_key, #woocommerce_simplify_commerce_private_key' ).closest( 'tr' );
  97. if ( jQuery( this ).is( ':checked' ) ) {
  98. sandbox.show();
  99. production.hide();
  100. } else {
  101. sandbox.hide();
  102. production.show();
  103. }
  104. }).change();
  105. jQuery( '#woocommerce_simplify_commerce_mode' ).on( 'change', function() {
  106. var color = jQuery( '#woocommerce_simplify_commerce_modal_color' ).closest( 'tr' );
  107. if ( 'standard' === jQuery( this ).val() ) {
  108. color.hide();
  109. } else {
  110. color.show();
  111. }
  112. }).change();
  113. </script>
  114. </table>
  115. <?php
  116. }
  117. /**
  118. * Check if SSL is enabled and notify the user.
  119. */
  120. public function checks() {
  121. if ( 'no' == $this->enabled ) {
  122. return;
  123. }
  124. if ( version_compare( phpversion(), '5.3', '<' ) ) {
  125. // PHP Version
  126. echo '<div class="error"><p>' . sprintf( __( 'Simplify Commerce Error: Simplify commerce requires PHP 5.3 and above. You are using version %s.', 'woocommerce' ), phpversion() ) . '</p></div>';
  127. } elseif ( ! $this->public_key || ! $this->private_key ) {
  128. // Check required fields
  129. echo '<div class="error"><p>' . __( 'Simplify Commerce Error: Please enter your public and private keys', 'woocommerce' ) . '</p></div>';
  130. } elseif ( 'standard' == $this->mode && ! wc_checkout_is_https() ) {
  131. // Show message when using standard mode and no SSL on the checkout page
  132. echo '<div class="error"><p>' . sprintf( __( 'Simplify Commerce is enabled, but the <a href="%s">force SSL option</a> is disabled; your checkout may not be secure! Please enable SSL and ensure your server has a valid SSL certificate - Simplify Commerce will only work in sandbox mode.', 'woocommerce' ), admin_url( 'admin.php?page=wc-settings&tab=checkout' ) ) . '</p></div>';
  133. }
  134. }
  135. /**
  136. * Check if this gateway is enabled.
  137. *
  138. * @return bool
  139. */
  140. public function is_available() {
  141. if ( 'yes' !== $this->enabled ) {
  142. return false;
  143. }
  144. if ( 'standard' === $this->mode && 'yes' !== $this->sandbox && ! wc_checkout_is_https() ) {
  145. return false;
  146. }
  147. if ( ! $this->public_key || ! $this->private_key ) {
  148. return false;
  149. }
  150. return true;
  151. }
  152. /**
  153. * Initialise Gateway Settings Form Fields.
  154. */
  155. public function init_form_fields() {
  156. $this->form_fields = array(
  157. 'enabled' => array(
  158. 'title' => __( 'Enable/Disable', 'woocommerce' ),
  159. 'label' => __( 'Enable Simplify Commerce', 'woocommerce' ),
  160. 'type' => 'checkbox',
  161. 'description' => '',
  162. 'default' => 'no',
  163. ),
  164. 'title' => array(
  165. 'title' => __( 'Title', 'woocommerce' ),
  166. 'type' => 'text',
  167. 'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
  168. 'default' => __( 'Credit card', 'woocommerce' ),
  169. 'desc_tip' => true,
  170. ),
  171. 'description' => array(
  172. 'title' => __( 'Description', 'woocommerce' ),
  173. 'type' => 'text',
  174. 'description' => __( 'This controls the description which the user sees during checkout.', 'woocommerce' ),
  175. 'default' => 'Pay with your credit card via Simplify Commerce by MasterCard.',
  176. 'desc_tip' => true,
  177. ),
  178. 'mode' => array(
  179. 'title' => __( 'Payment mode', 'woocommerce' ),
  180. 'label' => __( 'Enable Hosted Payments', 'woocommerce' ),
  181. 'type' => 'select',
  182. 'description' => sprintf( __( 'Standard will display the credit card fields on your store (SSL required). %1$s Hosted Payments will display a Simplify Commerce modal dialog on your store (if SSL) or will redirect the customer to Simplify Commerce hosted page (if not SSL). %1$s Note: Hosted Payments need a new API Key pair with the hosted payments flag selected. %2$sFor more details check the Simplify Commerce docs%3$s.', 'woocommerce' ), '<br />', '<a href="https://simplify.desk.com/customer/portal/articles/1792405-how-do-i-enable-hosted-payments" target="_blank">', '</a>' ),
  183. 'default' => 'standard',
  184. 'options' => array(
  185. 'standard' => __( 'Standard', 'woocommerce' ),
  186. 'hosted' => __( 'Hosted Payments', 'woocommerce' ),
  187. ),
  188. ),
  189. 'modal_color' => array(
  190. 'title' => __( 'Modal color', 'woocommerce' ),
  191. 'type' => 'color',
  192. 'description' => __( 'Set the color of the buttons and titles on the modal dialog.', 'woocommerce' ),
  193. 'default' => '#a46497',
  194. 'desc_tip' => true,
  195. ),
  196. 'sandbox' => array(
  197. 'title' => __( 'Sandbox', 'woocommerce' ),
  198. 'label' => __( 'Enable sandbox mode', 'woocommerce' ),
  199. 'type' => 'checkbox',
  200. 'description' => __( 'Place the payment gateway in sandbox mode using sandbox API keys (real payments will not be taken).', 'woocommerce' ),
  201. 'default' => 'yes',
  202. ),
  203. 'sandbox_public_key' => array(
  204. 'title' => __( 'Sandbox public key', 'woocommerce' ),
  205. 'type' => 'text',
  206. 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ),
  207. 'default' => '',
  208. 'desc_tip' => true,
  209. ),
  210. 'sandbox_private_key' => array(
  211. 'title' => __( 'Sandbox private key', 'woocommerce' ),
  212. 'type' => 'text',
  213. 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ),
  214. 'default' => '',
  215. 'desc_tip' => true,
  216. ),
  217. 'public_key' => array(
  218. 'title' => __( 'Public key', 'woocommerce' ),
  219. 'type' => 'text',
  220. 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ),
  221. 'default' => '',
  222. 'desc_tip' => true,
  223. ),
  224. 'private_key' => array(
  225. 'title' => __( 'Private key', 'woocommerce' ),
  226. 'type' => 'text',
  227. 'description' => __( 'Get your API keys from your Simplify account: Settings > API Keys.', 'woocommerce' ),
  228. 'default' => '',
  229. 'desc_tip' => true,
  230. ),
  231. );
  232. }
  233. /**
  234. * Payment form on checkout page.
  235. */
  236. public function payment_fields() {
  237. $description = $this->get_description();
  238. if ( 'yes' == $this->sandbox ) {
  239. $description .= ' ' . sprintf( __( 'TEST MODE ENABLED. Use a test card: %s', 'woocommerce' ), '<a href="https://www.simplify.com/commerce/docs/tutorial/index#testing">https://www.simplify.com/commerce/docs/tutorial/index#testing</a>' );
  240. }
  241. if ( $description ) {
  242. echo wpautop( wptexturize( trim( $description ) ) );
  243. }
  244. if ( 'standard' == $this->mode ) {
  245. parent::payment_fields();
  246. }
  247. }
  248. /**
  249. * Outputs scripts used for simplify payment.
  250. */
  251. public function payment_scripts() {
  252. $load_scripts = false;
  253. if ( is_checkout() ) {
  254. $load_scripts = true;
  255. }
  256. if ( $this->is_available() ) {
  257. $load_scripts = true;
  258. }
  259. if ( false === $load_scripts ) {
  260. return;
  261. }
  262. wp_enqueue_script( 'simplify-commerce', 'https://www.simplify.com/commerce/v1/simplify.js', array( 'jquery' ), WC_VERSION, true );
  263. wp_enqueue_script( 'wc-simplify-commerce', WC()->plugin_url() . '/includes/gateways/simplify-commerce/assets/js/simplify-commerce.js', array( 'simplify-commerce', 'wc-credit-card-form' ), WC_VERSION, true );
  264. wp_localize_script(
  265. 'wc-simplify-commerce', 'Simplify_commerce_params', array(
  266. 'key' => $this->public_key,
  267. 'card.number' => __( 'Card number', 'woocommerce' ),
  268. 'card.expMonth' => __( 'Expiry month', 'woocommerce' ),
  269. 'card.expYear' => __( 'Expiry year', 'woocommerce' ),
  270. 'is_invalid' => __( 'is invalid', 'woocommerce' ),
  271. 'mode' => $this->mode,
  272. 'is_ssl' => is_ssl(),
  273. )
  274. );
  275. }
  276. public function add_payment_method() {
  277. if ( empty( $_POST['simplify_token'] ) ) {
  278. wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' );
  279. return;
  280. }
  281. $cart_token = wc_clean( $_POST['simplify_token'] );
  282. $customer_token = $this->get_users_token();
  283. $current_user = wp_get_current_user();
  284. $customer_info = array(
  285. 'email' => $current_user->user_email,
  286. 'name' => $current_user->display_name,
  287. );
  288. $token = $this->save_token( $customer_token, $cart_token, $customer_info );
  289. if ( is_null( $token ) ) {
  290. wc_add_notice( __( 'There was a problem adding this card.', 'woocommerce' ), 'error' );
  291. return;
  292. }
  293. return array(
  294. 'result' => 'success',
  295. 'redirect' => wc_get_endpoint_url( 'payment-methods' ),
  296. );
  297. }
  298. /**
  299. * Actually saves a customer token to the database.
  300. *
  301. * @param WC_Payment_Token $customer_token Payment Token
  302. * @param string $cart_token CC Token
  303. * @param array $customer_info 'email', 'name'
  304. *
  305. * @return null|WC_Payment_Token|WC_Payment_Token_CC
  306. */
  307. public function save_token( $customer_token, $cart_token, $customer_info ) {
  308. if ( ! is_null( $customer_token ) ) {
  309. $customer = Simplify_Customer::findCustomer( $customer_token->get_token() );
  310. $updates = array( 'token' => $cart_token );
  311. $customer->setAll( $updates );
  312. $customer->updateCustomer();
  313. $customer = Simplify_Customer::findCustomer( $customer_token->get_token() ); // get updated customer with new set card
  314. $token = $customer_token;
  315. } else {
  316. $customer = Simplify_Customer::createCustomer(
  317. array(
  318. 'token' => $cart_token,
  319. 'email' => $customer_info['email'],
  320. 'name' => $customer_info['name'],
  321. )
  322. );
  323. $token = new WC_Payment_Token_CC();
  324. $token->set_token( $customer->id );
  325. }
  326. // If we were able to create an save our card, save the data on our side too
  327. if ( is_object( $customer ) && '' != $customer->id ) {
  328. $customer_properties = $customer->getProperties();
  329. $card = $customer_properties['card'];
  330. $token->set_gateway_id( $this->id );
  331. $token->set_card_type( strtolower( $card->type ) );
  332. $token->set_last4( $card->last4 );
  333. $expiry_month = ( 1 === strlen( $card->expMonth ) ? '0' . $card->expMonth : $card->expMonth );
  334. $token->set_expiry_month( $expiry_month );
  335. $token->set_expiry_year( '20' . $card->expYear );
  336. if ( is_user_logged_in() ) {
  337. $token->set_user_id( get_current_user_id() );
  338. }
  339. $token->save();
  340. return $token;
  341. }
  342. return null;
  343. }
  344. /**
  345. * Process customer: updating or creating a new customer/saved CC
  346. *
  347. * @param WC_Order $order Order object
  348. * @param WC_Payment_Token $customer_token Payment Token
  349. * @param string $cart_token CC Token
  350. */
  351. protected function process_customer( $order, $customer_token = null, $cart_token = '' ) {
  352. // Are we saving a new payment method?
  353. if ( is_user_logged_in() && isset( $_POST['wc-simplify_commerce-new-payment-method'] ) && true === (bool) $_POST['wc-simplify_commerce-new-payment-method'] ) {
  354. $customer_info = array(
  355. 'email' => $order->get_billing_email(),
  356. 'name' => trim( $order->get_formatted_billing_full_name() ),
  357. );
  358. $token = $this->save_token( $customer_token, $cart_token, $customer_info );
  359. if ( ! is_null( $token ) ) {
  360. $order->add_payment_token( $token );
  361. }
  362. }
  363. }
  364. /**
  365. * Process standard payments.
  366. *
  367. * @param WC_Order $order
  368. * @param string $cart_token
  369. * @param string $customer_token
  370. *
  371. * @return array
  372. * @uses Simplify_ApiException
  373. * @uses Simplify_BadRequestException
  374. */
  375. protected function process_standard_payments( $order, $cart_token = '', $customer_token = '' ) {
  376. try {
  377. if ( empty( $cart_token ) && empty( $customer_token ) ) {
  378. $error_msg = __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' );
  379. if ( 'yes' == $this->sandbox ) {
  380. $error_msg .= ' ' . __( 'Developers: Please make sure that you are including jQuery and there are no JavaScript errors on the page.', 'woocommerce' );
  381. }
  382. throw new Simplify_ApiException( $error_msg );
  383. }
  384. // We need to figure out if we want to charge the card token (new unsaved token, no customer, etc)
  385. // or the customer token (just saved method, previously saved method)
  386. $pass_tokens = array();
  387. if ( ! empty( $cart_token ) ) {
  388. $pass_tokens['token'] = $cart_token;
  389. }
  390. if ( ! empty( $customer_token ) ) {
  391. $pass_tokens['customer'] = $customer_token;
  392. // Use the customer token only, since we already saved the (one time use) card token to the customer
  393. if ( isset( $_POST['wc-simplify_commerce-new-payment-method'] ) && true === (bool) $_POST['wc-simplify_commerce-new-payment-method'] ) {
  394. unset( $pass_tokens['token'] );
  395. }
  396. }
  397. // Did we create an account and save a payment method? We might need to use the customer token instead of the card token
  398. if ( isset( $_POST['createaccount'] ) && true === (bool) $_POST['createaccount'] && empty( $customer_token ) ) {
  399. $user_token = $this->get_users_token();
  400. if ( ! is_null( $user_token ) ) {
  401. $pass_tokens['customer'] = $user_token->get_token();
  402. unset( $pass_tokens['token'] );
  403. }
  404. }
  405. $payment_response = $this->do_payment( $order, $order->get_total(), $pass_tokens );
  406. if ( is_wp_error( $payment_response ) ) {
  407. throw new Simplify_ApiException( $payment_response->get_error_message() );
  408. } else {
  409. // Remove cart
  410. WC()->cart->empty_cart();
  411. // Return thank you page redirect
  412. return array(
  413. 'result' => 'success',
  414. 'redirect' => $this->get_return_url( $order ),
  415. );
  416. }
  417. } catch ( Simplify_ApiException $e ) {
  418. if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
  419. foreach ( $e->getFieldErrors() as $error ) {
  420. wc_add_notice( $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')', 'error' );
  421. }
  422. } else {
  423. wc_add_notice( $e->getMessage(), 'error' );
  424. }
  425. return array(
  426. 'result' => 'fail',
  427. 'redirect' => '',
  428. );
  429. }
  430. }
  431. /**
  432. * do payment function.
  433. *
  434. * @param WC_order $order
  435. * @param int $amount (default: 0)
  436. * @param array $token
  437. *
  438. * @return bool|WP_Error
  439. * @uses Simplify_BadRequestException
  440. */
  441. public function do_payment( $order, $amount = 0, $token = array() ) {
  442. if ( $amount * 100 < 50 ) {
  443. return new WP_Error( 'simplify_error', __( 'Sorry, the minimum allowed order total is 0.50 to use this payment method.', 'woocommerce' ) );
  444. }
  445. try {
  446. // Charge the customer
  447. $data = array(
  448. 'amount' => $amount * 100, // In cents.
  449. 'description' => sprintf( __( '%1$s - Order #%2$s', 'woocommerce' ), esc_html( get_bloginfo( 'name', 'display' ) ), $order->get_order_number() ),
  450. 'currency' => strtoupper( get_woocommerce_currency() ),
  451. 'reference' => $order->get_id(),
  452. );
  453. $data = array_merge( $data, $token );
  454. $payment = Simplify_Payment::createPayment( $data );
  455. } catch ( Exception $e ) {
  456. $error_message = $e->getMessage();
  457. if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
  458. $error_message = '';
  459. foreach ( $e->getFieldErrors() as $error ) {
  460. $error_message .= ' ' . $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')';
  461. }
  462. }
  463. $order->add_order_note( sprintf( __( 'Simplify payment error: %s.', 'woocommerce' ), $error_message ) );
  464. return new WP_Error( 'simplify_payment_declined', $e->getMessage(), array( 'status' => $e->getCode() ) );
  465. }
  466. if ( 'APPROVED' == $payment->paymentStatus ) {
  467. // Payment complete
  468. $order->payment_complete( $payment->id );
  469. // Add order note
  470. $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %1$s, Auth Code: %2$s)', 'woocommerce' ), $payment->id, $payment->authCode ) );
  471. return true;
  472. } else {
  473. $order->add_order_note( __( 'Simplify payment declined', 'woocommerce' ) );
  474. return new WP_Error( 'simplify_payment_declined', __( 'Payment was declined - please try another card.', 'woocommerce' ) );
  475. }
  476. }
  477. /**
  478. * Process standard payments.
  479. *
  480. * @param WC_Order $order
  481. * @return array
  482. */
  483. protected function process_hosted_payments( $order ) {
  484. return array(
  485. 'result' => 'success',
  486. 'redirect' => $order->get_checkout_payment_url( true ),
  487. );
  488. }
  489. protected function get_users_token() {
  490. $customer_token = null;
  491. if ( is_user_logged_in() ) {
  492. $tokens = WC_Payment_Tokens::get_customer_tokens( get_current_user_id() );
  493. foreach ( $tokens as $token ) {
  494. if ( $token->get_gateway_id() === $this->id ) {
  495. $customer_token = $token;
  496. break;
  497. }
  498. }
  499. }
  500. return $customer_token;
  501. }
  502. /**
  503. * Process the payment.
  504. *
  505. * @param int $order_id
  506. *
  507. * @return array|void
  508. */
  509. public function process_payment( $order_id ) {
  510. $order = wc_get_order( $order_id );
  511. // Payment/CC form is hosted on Simplify
  512. if ( 'hosted' === $this->mode ) {
  513. return $this->process_hosted_payments( $order );
  514. }
  515. // New CC info was entered
  516. if ( isset( $_POST['simplify_token'] ) ) {
  517. $cart_token = wc_clean( $_POST['simplify_token'] );
  518. $customer_token = $this->get_users_token();
  519. $customer_token_value = ( ! is_null( $customer_token ) ? $customer_token->get_token() : '' );
  520. $this->process_customer( $order, $customer_token, $cart_token );
  521. return $this->process_standard_payments( $order, $cart_token, $customer_token_value );
  522. }
  523. // Possibly Create (or update) customer/save payment token, use an existing token, and then process the payment
  524. if ( isset( $_POST['wc-simplify_commerce-payment-token'] ) && 'new' !== $_POST['wc-simplify_commerce-payment-token'] ) {
  525. $token_id = wc_clean( $_POST['wc-simplify_commerce-payment-token'] );
  526. $token = WC_Payment_Tokens::get( $token_id );
  527. if ( $token->get_user_id() !== get_current_user_id() ) {
  528. wc_add_notice( __( 'Please make sure your card details have been entered correctly and that your browser supports JavaScript.', 'woocommerce' ), 'error' );
  529. return;
  530. }
  531. $this->process_customer( $order, $token );
  532. return $this->process_standard_payments( $order, '', $token->get_token() );
  533. }
  534. }
  535. /**
  536. * Hosted payment args.
  537. *
  538. * @param WC_Order $order
  539. *
  540. * @return array
  541. */
  542. protected function get_hosted_payments_args( $order ) {
  543. $args = apply_filters(
  544. 'woocommerce_simplify_commerce_hosted_args', array(
  545. 'sc-key' => $this->public_key,
  546. 'amount' => $order->get_total() * 100,
  547. 'reference' => $order->get_id(),
  548. 'name' => esc_html( get_bloginfo( 'name', 'display' ) ),
  549. 'description' => sprintf( __( 'Order #%s', 'woocommerce' ), $order->get_order_number() ),
  550. 'receipt' => 'false',
  551. 'color' => $this->modal_color,
  552. 'redirect-url' => WC()->api_request_url( 'WC_Gateway_Simplify_Commerce' ),
  553. 'address' => $order->get_billing_address_1() . ' ' . $order->get_billing_address_2(),
  554. 'address-city' => $order->get_billing_city(),
  555. 'address-state' => $order->get_billing_state(),
  556. 'address-zip' => $order->get_billing_postcode(),
  557. 'address-country' => $order->get_billing_country(),
  558. 'operation' => 'create.token',
  559. ), $order->get_id()
  560. );
  561. return $args;
  562. }
  563. /**
  564. * Receipt page.
  565. *
  566. * @param int $order_id
  567. */
  568. public function receipt_page( $order_id ) {
  569. $order = wc_get_order( $order_id );
  570. echo '<p>' . __( 'Thank you for your order, please click the button below to pay with credit card using Simplify Commerce by MasterCard.', 'woocommerce' ) . '</p>';
  571. $args = $this->get_hosted_payments_args( $order );
  572. $button_args = array();
  573. foreach ( $args as $key => $value ) {
  574. $button_args[] = 'data-' . esc_attr( $key ) . '="' . esc_attr( $value ) . '"';
  575. }
  576. echo '<script type="text/javascript" src="https://www.simplify.com/commerce/simplify.pay.js"></script>
  577. <button class="button alt" id="simplify-payment-button" ' . implode( ' ', $button_args ) . '>' . __( 'Pay now', 'woocommerce' ) . '</button> <a class="button cancel" href="' . esc_url( $order->get_cancel_order_url() ) . '">' . __( 'Cancel order &amp; restore cart', 'woocommerce' ) . '</a>
  578. ';
  579. }
  580. /**
  581. * Return handler for Hosted Payments.
  582. */
  583. public function return_handler() {
  584. @ob_clean();
  585. header( 'HTTP/1.1 200 OK' );
  586. if ( isset( $_REQUEST['reference'] ) && isset( $_REQUEST['paymentId'] ) && isset( $_REQUEST['signature'] ) ) {
  587. $signature = strtoupper( md5( $_REQUEST['amount'] . $_REQUEST['reference'] . $_REQUEST['paymentId'] . $_REQUEST['paymentDate'] . $_REQUEST['paymentStatus'] . $this->private_key ) );
  588. $order_id = absint( $_REQUEST['reference'] );
  589. $order = wc_get_order( $order_id );
  590. if ( hash_equals( $signature, $_REQUEST['signature'] ) ) {
  591. $order_complete = $this->process_order_status( $order, $_REQUEST['paymentId'], $_REQUEST['paymentStatus'], $_REQUEST['paymentDate'] );
  592. if ( ! $order_complete ) {
  593. $order->update_status( 'failed', __( 'Payment was declined by Simplify Commerce.', 'woocommerce' ) );
  594. }
  595. wp_redirect( $this->get_return_url( $order ) );
  596. exit();
  597. }
  598. }
  599. wp_redirect( wc_get_page_permalink( 'cart' ) );
  600. exit();
  601. }
  602. /**
  603. * Process the order status.
  604. *
  605. * @param WC_Order $order
  606. * @param string $payment_id
  607. * @param string $status
  608. * @param string $auth_code
  609. *
  610. * @return bool
  611. */
  612. public function process_order_status( $order, $payment_id, $status, $auth_code ) {
  613. if ( 'APPROVED' == $status ) {
  614. // Payment complete
  615. $order->payment_complete( $payment_id );
  616. // Add order note
  617. $order->add_order_note( sprintf( __( 'Simplify payment approved (ID: %1$s, Auth Code: %2$s)', 'woocommerce' ), $payment_id, $auth_code ) );
  618. // Remove cart
  619. WC()->cart->empty_cart();
  620. return true;
  621. }
  622. return false;
  623. }
  624. /**
  625. * Process refunds.
  626. * WooCommerce 2.2 or later.
  627. *
  628. * @param int $order_id
  629. * @param float $amount
  630. * @param string $reason
  631. * @uses Simplify_ApiException
  632. * @uses Simplify_BadRequestException
  633. * @return bool|WP_Error
  634. */
  635. public function process_refund( $order_id, $amount = null, $reason = '' ) {
  636. try {
  637. $payment_id = get_post_meta( $order_id, '_transaction_id', true );
  638. $refund = Simplify_Refund::createRefund(
  639. array(
  640. 'amount' => $amount * 100, // In cents.
  641. 'payment' => $payment_id,
  642. 'reason' => $reason,
  643. 'reference' => $order_id,
  644. )
  645. );
  646. if ( 'APPROVED' == $refund->paymentStatus ) {
  647. return true;
  648. } else {
  649. throw new Simplify_ApiException( __( 'Refund was declined.', 'woocommerce' ) );
  650. }
  651. } catch ( Simplify_ApiException $e ) {
  652. if ( $e instanceof Simplify_BadRequestException && $e->hasFieldErrors() && $e->getFieldErrors() ) {
  653. foreach ( $e->getFieldErrors() as $error ) {
  654. return new WP_Error( 'simplify_refund_error', $error->getFieldName() . ': "' . $error->getMessage() . '" (' . $error->getErrorCode() . ')' );
  655. }
  656. } else {
  657. return new WP_Error( 'simplify_refund_error', $e->getMessage() );
  658. }
  659. }
  660. return false;
  661. }
  662. /**
  663. * Get gateway icon.
  664. *
  665. * @access public
  666. * @return string
  667. */
  668. public function get_icon() {
  669. $icon = '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/visa.svg' ) . '" alt="Visa" width="32" />';
  670. $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/mastercard.svg' ) . '" alt="MasterCard" width="32" />';
  671. $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/discover.svg' ) . '" alt="Discover" width="32" />';
  672. $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/amex.svg' ) . '" alt="Amex" width="32" />';
  673. $icon .= '<img src="' . WC_HTTPS::force_https_url( WC()->plugin_url() . '/assets/images/icons/credit-cards/jcb.svg' ) . '" alt="JCB" width="32" />';
  674. return apply_filters( 'woocommerce_gateway_icon', $icon, $this->id );
  675. }
  676. }