PageRenderTime 52ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/woocommerce-gateway-stripe/includes/class-wc-stripe-apple-pay-registration.php

https://gitlab.com/campus-academy/krowkaramel
PHP | 417 lines | 235 code | 57 blank | 125 comment | 25 complexity | b288e91faf0e377359376cb09ce4378b MD5 | raw file
  1. <?php
  2. /**
  3. * Stripe Apple Pay Registration Class.
  4. *
  5. * @since 4.0.6
  6. */
  7. if ( ! defined( 'ABSPATH' ) ) {
  8. exit;
  9. }
  10. class WC_Stripe_Apple_Pay_Registration {
  11. const DOMAIN_ASSOCIATION_FILE_NAME = 'apple-developer-merchantid-domain-association';
  12. const DOMAIN_ASSOCIATION_FILE_DIR = '.well-known';
  13. /**
  14. * Enabled.
  15. *
  16. * @var
  17. */
  18. public $stripe_settings;
  19. /**
  20. * Apple Pay Domain Set.
  21. *
  22. * @var bool
  23. */
  24. public $apple_pay_domain_set;
  25. /**
  26. * Current domain name.
  27. *
  28. * @var bool
  29. */
  30. private $domain_name;
  31. /**
  32. * Stores Apple Pay domain verification issues.
  33. *
  34. * @var string
  35. */
  36. public $apple_pay_verify_notice;
  37. public function __construct() {
  38. add_action( 'init', [ $this, 'add_domain_association_rewrite_rule' ] );
  39. add_action( 'admin_init', [ $this, 'verify_domain_on_domain_name_change' ] );
  40. add_action( 'admin_notices', [ $this, 'admin_notices' ] );
  41. add_filter( 'query_vars', [ $this, 'whitelist_domain_association_query_param' ], 10, 1 );
  42. add_action( 'parse_request', [ $this, 'parse_domain_association_request' ], 10, 1 );
  43. add_action( 'woocommerce_stripe_updated', [ $this, 'verify_domain_if_configured' ] );
  44. add_action( 'add_option_woocommerce_stripe_settings', [ $this, 'verify_domain_on_new_settings' ], 10, 2 );
  45. add_action( 'update_option_woocommerce_stripe_settings', [ $this, 'verify_domain_on_updated_settings' ], 10, 2 );
  46. $this->stripe_settings = get_option( 'woocommerce_stripe_settings', [] );
  47. $this->domain_name = isset( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : str_replace( array( 'https://', 'http://' ), '', get_site_url() ); // @codingStandardsIgnoreLine
  48. $this->apple_pay_domain_set = 'yes' === $this->get_option( 'apple_pay_domain_set', 'no' );
  49. $this->apple_pay_verify_notice = '';
  50. }
  51. /**
  52. * Gets the Stripe settings.
  53. *
  54. * @since 4.0.6
  55. * @param string $setting
  56. * @param string default
  57. * @return string $setting_value
  58. */
  59. public function get_option( $setting = '', $default = '' ) {
  60. if ( empty( $this->stripe_settings ) ) {
  61. return $default;
  62. }
  63. if ( ! empty( $this->stripe_settings[ $setting ] ) ) {
  64. return $this->stripe_settings[ $setting ];
  65. }
  66. return $default;
  67. }
  68. /**
  69. * Whether the gateway and Payment Request Button (prerequisites for Apple Pay) are enabled.
  70. *
  71. * @since 4.5.4
  72. * @return string Whether Apple Pay required settings are enabled.
  73. */
  74. private function is_enabled() {
  75. $stripe_enabled = 'yes' === $this->get_option( 'enabled', 'no' );
  76. $payment_request_button_enabled = 'yes' === $this->get_option( 'payment_request', 'yes' );
  77. return $stripe_enabled && $payment_request_button_enabled;
  78. }
  79. /**
  80. * Gets the Stripe secret key for the current mode.
  81. *
  82. * @since 4.5.3
  83. * @version 4.9.0
  84. * @return string Secret key.
  85. */
  86. private function get_secret_key() {
  87. return $this->get_option( 'secret_key' );
  88. }
  89. /**
  90. * Trigger Apple Pay registration upon domain name change.
  91. *
  92. * @since 4.9.0
  93. */
  94. public function verify_domain_on_domain_name_change() {
  95. if ( $this->domain_name !== $this->get_option( 'apple_pay_verified_domain' ) ) {
  96. $this->verify_domain_if_configured();
  97. }
  98. }
  99. /**
  100. * Vefifies if hosted domain association file is up to date
  101. * with the file from the plugin directory.
  102. *
  103. * @since 4.9.0
  104. * @return bool Whether file is up to date or not.
  105. */
  106. private function verify_hosted_domain_association_file_is_up_to_date() {
  107. // Contents of domain association file from plugin dir.
  108. $new_contents = @file_get_contents( WC_STRIPE_PLUGIN_PATH . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME ); // @codingStandardsIgnoreLine
  109. // Get file contents from local path and remote URL and check if either of which matches.
  110. $fullpath = untrailingslashit( ABSPATH ) . '/' . self::DOMAIN_ASSOCIATION_FILE_DIR . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME;
  111. $local_contents = @file_get_contents( $fullpath ); // @codingStandardsIgnoreLine
  112. $url = get_site_url() . '/' . self::DOMAIN_ASSOCIATION_FILE_DIR . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME;
  113. $response = @wp_remote_get( $url ); // @codingStandardsIgnoreLine
  114. $remote_contents = @wp_remote_retrieve_body( $response ); // @codingStandardsIgnoreLine
  115. return $local_contents === $new_contents || $remote_contents === $new_contents;
  116. }
  117. /**
  118. * Copies and overwrites domain association file.
  119. *
  120. * @since 4.9.0
  121. * @return null|string Error message.
  122. */
  123. private function copy_and_overwrite_domain_association_file() {
  124. $well_known_dir = untrailingslashit( ABSPATH ) . '/' . self::DOMAIN_ASSOCIATION_FILE_DIR;
  125. $fullpath = $well_known_dir . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME;
  126. if ( ! file_exists( $well_known_dir ) ) {
  127. if ( ! @mkdir( $well_known_dir, 0755 ) ) { // @codingStandardsIgnoreLine
  128. return __( 'Unable to create domain association folder to domain root.', 'woocommerce-gateway-stripe' );
  129. }
  130. }
  131. if ( ! @copy( WC_STRIPE_PLUGIN_PATH . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME, $fullpath ) ) { // @codingStandardsIgnoreLine
  132. return __( 'Unable to copy domain association file to domain root.', 'woocommerce-gateway-stripe' );
  133. }
  134. }
  135. /**
  136. * Updates the Apple Pay domain association file.
  137. * Reports failure only if file isn't already being served properly.
  138. *
  139. * @since 4.9.0
  140. */
  141. public function update_domain_association_file() {
  142. if ( $this->verify_hosted_domain_association_file_is_up_to_date() ) {
  143. return;
  144. }
  145. $error_message = $this->copy_and_overwrite_domain_association_file();
  146. if ( isset( $error_message ) ) {
  147. $url = get_site_url() . '/' . self::DOMAIN_ASSOCIATION_FILE_DIR . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME;
  148. WC_Stripe_Logger::log(
  149. 'Error: ' . $error_message . ' ' .
  150. /* translators: expected domain association file URL */
  151. sprintf( __( 'To enable Apple Pay, domain association file must be hosted at %s.', 'woocommerce-gateway-stripe' ), $url )
  152. );
  153. } else {
  154. WC_Stripe_Logger::log( __( 'Domain association file updated.', 'woocommerce-gateway-stripe' ) );
  155. }
  156. }
  157. /**
  158. * Adds a rewrite rule for serving the domain association file from the proper location.
  159. */
  160. public function add_domain_association_rewrite_rule() {
  161. $regex = '^\\' . self::DOMAIN_ASSOCIATION_FILE_DIR . '\/' . self::DOMAIN_ASSOCIATION_FILE_NAME . '$';
  162. $redirect = 'index.php?' . self::DOMAIN_ASSOCIATION_FILE_NAME . '=1';
  163. add_rewrite_rule( $regex, $redirect, 'top' );
  164. }
  165. /**
  166. * Add to the list of publicly allowed query variables.
  167. *
  168. * @param array $query_vars - provided public query vars.
  169. * @return array Updated public query vars.
  170. */
  171. public function whitelist_domain_association_query_param( $query_vars ) {
  172. $query_vars[] = self::DOMAIN_ASSOCIATION_FILE_NAME;
  173. return $query_vars;
  174. }
  175. /**
  176. * Serve domain association file when proper query param is provided.
  177. *
  178. * @param WP WordPress environment object.
  179. */
  180. public function parse_domain_association_request( $wp ) {
  181. if (
  182. ! isset( $wp->query_vars[ self::DOMAIN_ASSOCIATION_FILE_NAME ] ) ||
  183. '1' !== $wp->query_vars[ self::DOMAIN_ASSOCIATION_FILE_NAME ]
  184. ) {
  185. return;
  186. }
  187. $path = WC_STRIPE_PLUGIN_PATH . '/' . self::DOMAIN_ASSOCIATION_FILE_NAME;
  188. header( 'Content-Type: text/plain;charset=utf-8' );
  189. echo esc_html( file_get_contents( $path ) );
  190. exit;
  191. }
  192. /**
  193. * Makes request to register the domain with Stripe/Apple Pay.
  194. *
  195. * @since 3.1.0
  196. * @version 4.9.0
  197. * @param string $secret_key
  198. */
  199. private function make_domain_registration_request( $secret_key ) {
  200. if ( empty( $secret_key ) ) {
  201. throw new Exception( __( 'Unable to verify domain - missing secret key.', 'woocommerce-gateway-stripe' ) );
  202. }
  203. $endpoint = 'https://api.stripe.com/v1/apple_pay/domains';
  204. $data = [
  205. 'domain_name' => $this->domain_name,
  206. ];
  207. $headers = [
  208. 'User-Agent' => 'WooCommerce Stripe Apple Pay',
  209. 'Authorization' => 'Bearer ' . $secret_key,
  210. ];
  211. $response = wp_remote_post(
  212. $endpoint,
  213. [
  214. 'headers' => $headers,
  215. 'body' => http_build_query( $data ),
  216. 'timeout' => 30,
  217. ]
  218. );
  219. if ( is_wp_error( $response ) ) {
  220. /* translators: error message */
  221. throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $response->get_error_message() ) );
  222. }
  223. if ( 200 !== $response['response']['code'] ) {
  224. $parsed_response = json_decode( $response['body'] );
  225. $this->apple_pay_verify_notice = $parsed_response->error->message;
  226. /* translators: error message */
  227. throw new Exception( sprintf( __( 'Unable to verify domain - %s', 'woocommerce-gateway-stripe' ), $parsed_response->error->message ) );
  228. }
  229. }
  230. /**
  231. * Processes the Apple Pay domain verification.
  232. *
  233. * @since 3.1.0
  234. * @version 4.5.4
  235. *
  236. * @param string $secret_key
  237. *
  238. * @return bool Whether domain verification succeeded.
  239. */
  240. public function register_domain_with_apple( $secret_key ) {
  241. try {
  242. $this->make_domain_registration_request( $secret_key );
  243. // No errors to this point, verification success!
  244. $this->stripe_settings['apple_pay_verified_domain'] = $this->domain_name;
  245. $this->stripe_settings['apple_pay_domain_set'] = 'yes';
  246. $this->apple_pay_domain_set = true;
  247. update_option( 'woocommerce_stripe_settings', $this->stripe_settings );
  248. WC_Stripe_Logger::log( 'Your domain has been verified with Apple Pay!' );
  249. return true;
  250. } catch ( Exception $e ) {
  251. $this->stripe_settings['apple_pay_verified_domain'] = $this->domain_name;
  252. $this->stripe_settings['apple_pay_domain_set'] = 'no';
  253. $this->apple_pay_domain_set = false;
  254. update_option( 'woocommerce_stripe_settings', $this->stripe_settings );
  255. WC_Stripe_Logger::log( 'Error: ' . $e->getMessage() );
  256. return false;
  257. }
  258. }
  259. /**
  260. * Process the Apple Pay domain verification if proper settings are configured.
  261. *
  262. * @since 4.5.4
  263. * @version 4.9.0
  264. */
  265. public function verify_domain_if_configured() {
  266. $secret_key = $this->get_secret_key();
  267. if ( ! $this->is_enabled() || empty( $secret_key ) ) {
  268. return;
  269. }
  270. // Ensure that domain association file will be served.
  271. flush_rewrite_rules();
  272. // The rewrite rule method doesn't work if permalinks are set to Plain.
  273. // Create/update domain association file by copying it from the plugin folder as a fallback.
  274. $this->update_domain_association_file();
  275. // Register the domain with Apple Pay.
  276. $verification_complete = $this->register_domain_with_apple( $secret_key );
  277. // Show/hide notes if necessary.
  278. WC_Stripe_Inbox_Notes::notify_on_apple_pay_domain_verification( $verification_complete );
  279. }
  280. /**
  281. * Conditionally process the Apple Pay domain verification after settings are initially set.
  282. *
  283. * @since 4.5.4
  284. * @version 4.5.4
  285. */
  286. public function verify_domain_on_new_settings( $option, $settings ) {
  287. $this->verify_domain_on_updated_settings( [], $settings );
  288. }
  289. /**
  290. * Conditionally process the Apple Pay domain verification after settings are updated.
  291. *
  292. * @since 4.5.3
  293. * @version 4.5.4
  294. */
  295. public function verify_domain_on_updated_settings( $prev_settings, $settings ) {
  296. // Grab previous state and then update cached settings.
  297. $this->stripe_settings = $prev_settings;
  298. $prev_secret_key = $this->get_secret_key();
  299. $prev_is_enabled = $this->is_enabled();
  300. $this->stripe_settings = $settings;
  301. // If Stripe or Payment Request Button wasn't enabled (or secret key was different) then might need to verify now.
  302. if ( ! $prev_is_enabled || ( $this->get_secret_key() !== $prev_secret_key ) ) {
  303. $this->verify_domain_if_configured();
  304. }
  305. }
  306. /**
  307. * Display any admin notices to the user.
  308. *
  309. * @since 4.0.6
  310. */
  311. public function admin_notices() {
  312. if ( ! $this->is_enabled() ) {
  313. return;
  314. }
  315. if ( ! current_user_can( 'manage_woocommerce' ) ) {
  316. return;
  317. }
  318. $empty_notice = empty( $this->apple_pay_verify_notice );
  319. if ( $empty_notice && ( $this->apple_pay_domain_set || empty( $this->secret_key ) ) ) {
  320. return;
  321. }
  322. /**
  323. * Apple pay is enabled by default and domain verification initializes
  324. * when setting screen is displayed. So if domain verification is not set,
  325. * something went wrong so lets notify user.
  326. */
  327. $allowed_html = [
  328. 'a' => [
  329. 'href' => [],
  330. 'title' => [],
  331. ],
  332. ];
  333. $verification_failed_without_error = __( 'Apple Pay domain verification failed.', 'woocommerce-gateway-stripe' );
  334. $verification_failed_with_error = __( 'Apple Pay domain verification failed with the following error:', 'woocommerce-gateway-stripe' );
  335. $check_log_text = sprintf(
  336. /* translators: 1) HTML anchor open tag 2) HTML anchor closing tag */
  337. esc_html__( 'Please check the %1$slogs%2$s for more details on this issue. Logging must be enabled to see recorded logs.', 'woocommerce-gateway-stripe' ),
  338. '<a href="' . admin_url( 'admin.php?page=wc-status&tab=logs' ) . '">',
  339. '</a>'
  340. );
  341. ?>
  342. <div class="error stripe-apple-pay-message">
  343. <?php if ( $empty_notice ) : ?>
  344. <p><?php echo esc_html( $verification_failed_without_error ); ?></p>
  345. <?php else : ?>
  346. <p><?php echo esc_html( $verification_failed_with_error ); ?></p>
  347. <p><i><?php echo wp_kses( make_clickable( esc_html( $this->apple_pay_verify_notice ) ), $allowed_html ); ?></i></p>
  348. <?php endif; ?>
  349. <p><?php echo $check_log_text; ?></p>
  350. </div>
  351. <?php
  352. }
  353. }
  354. new WC_Stripe_Apple_Pay_Registration();