File "class-wc-swedbank-subscriptions.php"

Full Path: /home/siazco/grocery.siazco.se/wp-content/plugins/swedbank-pay-checkout/includes/class-wc-swedbank-subscriptions.php
File size: 14.07 KB
MIME-type: text/x-php
Charset: utf-8

<?php

namespace SwedbankPay\Checkout\WooCommerce;

defined( 'ABSPATH' ) || exit;

use WC_Payment_Gateway;
use WC_Subscription;
use WC_Order;
use Exception;

use WC_Gateway_Swedbank_Pay_Checkout;

class WC_Swedbank_Subscriptions {
	const PAYMENT_ID = 'payex_checkout';

	public function __construct() {
		// Add payment token when subscription was created
		add_action( 'woocommerce_payment_complete', __CLASS__ . '::add_subscription_card_id', 10, 1 );
		add_action(
			'woocommerce_payment_complete_order_status_on-hold',
			__CLASS__ . '::add_subscription_card_id',
			10,
			1
		);

		// Update failing payment method
		add_action(
			'woocommerce_subscription_failing_payment_method_updated_' . self::PAYMENT_ID,
			__CLASS__ . '::update_failing_payment_method',
			10,
			1
		);

		// Don't transfer customer meta to resubscribe orders
		add_action( 'wcs_resubscribe_order_created', __CLASS__ . '::delete_resubscribe_meta', 10 );

		// Allow store managers to manually set card id as the payment method on a subscription
		add_filter( 'woocommerce_subscription_payment_meta', __CLASS__ . '::add_subscription_payment_meta', 10, 2 );

		// Customize credit cards in admin
		add_action(
			'woocommerce_subscription_payment_meta_input_' . self::PAYMENT_ID . '_swedbankpay_meta_token_id',
			__CLASS__ . '::payment_meta_input',
			10,
			4
		);

		// Validate the payment meta data
		add_action(
			'woocommerce_subscription_validate_payment_meta',
			__CLASS__ . '::validate_subscription_payment_meta',
			10,
			3
		);

		// Save payment method meta data for the Subscription
		add_action( 'wcs_save_other_payment_meta', __CLASS__ . '::save_subscription_payment_meta', 10, 4 );

		// Charge the payment when a subscription payment is due
		add_action(
			'woocommerce_scheduled_subscription_payment_'  . self::PAYMENT_ID,
			__CLASS__ . '::scheduled_subscription_payment',
			10,
			2
		);

		// Display the credit card used for a subscription in the "My Subscriptions" table
		add_filter(
			'woocommerce_my_subscriptions_payment_method',
			__CLASS__ . '::maybe_render_subscription_payment_method',
			10,
			2
		);

		// Lock "Save card" if needs
		add_filter(
			'woocommerce_payment_gateway_save_new_payment_method_option_html',
			__CLASS__ . '::save_new_payment_method_option_html',
			10,
			2
		);

		add_action( 'woocommerce_get_customer_payment_tokens', array( $this, 'filter_tokens' ), 3, 10 );
	}

	/**
	 * Add Card ID when Subscription was created
	 *
	 * @param $order_id
	 */
	public static function add_subscription_card_id( $order_id ) {
		if ( ! function_exists( 'wcs_get_subscriptions_for_order' ) ) {
			return;
		}

		$order = wc_get_order( $order_id );
		$subscriptions = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'parent' ) );
		foreach ( $subscriptions as $subscription ) {
			/** @var WC_Subscription $subscription */
			// Add payment meta
			$paymentId = $order->get_meta( '_payex_payment_id' );
			$paymentOrderId = $order->get_meta( '_payex_paymentorder_id' );

			/** @var \WC_Subscription $subscription */
			if ( ! empty( $paymentId ) ) {
				$subscription->update_meta_data( '_payex_payment_id', $paymentId );
			}

			if ( ! empty( $paymentOrderId ) ) {
				$subscription->update_meta_data( '_payex_paymentorder_id', $paymentOrderId );
			}

			// Add payment token
			$tokens = $order->get_payment_tokens();
			if ( count( $tokens ) > 0 ) {
				foreach ( $tokens as $token_id ) {
					$token = new WC_Payment_Token_Swedbank_Pay( $token_id );
					if ( self::PAYMENT_ID !== $token->get_gateway_id() ) {
						continue;
					}

					$subscription->add_payment_token( $token );
					$subscription->add_order_note(
						sprintf(
							__( 'Card: %s', 'swedbank-pay-woocommerce-checkout' ),
							strip_tags( $token->get_display_name() )
						)
					);

					break;
				}
			}

			$subscription->save_meta_data();
			$subscription->save();
		}
	}

	/**
	 * Update the card meta for a subscription after using this payment method
	 * to complete a payment to make up for an automatic renewal payment which previously failed.
	 *
	 * @access public
	 *
	 * @param WC_Subscription $subscription The subscription for which the failing payment method relates.
	 * @param WC_Order $renewal_order The order which recorded the successful payment (to make up for the failed automatic payment).
	 *
	 * @return void
	 */
	public static function update_failing_payment_method( $subscription, $renewal_order ) {
		// Delete tokens
		//$subscription->delete_meta_data( '_payment_tokens' );
		//$subscription->save();
	}

	/**
	 * Don't transfer customer meta to resubscribe orders.
	 *
	 * @access public
	 *
	 * @param WC_Order $resubscribe_order The order created for the customer to resubscribe to the old expired/cancelled subscription
	 *
	 * @return void
	 */
	public static function delete_resubscribe_meta( $resubscribe_order ) {
		// Delete tokens
		$resubscribe_order->delete_meta_data( '_payment_tokens' );
		$resubscribe_order->save();
	}

	/**
	 * Include the payment meta data required to process automatic recurring payments so that store managers can
	 * manually set up automatic recurring payments for a customer via the Edit Subscription screen in Subscriptions v2.0+.
	 *
	 * @param array $payment_meta associative array of meta data required for automatic payments
	 * @param WC_Subscription $subscription An instance of a subscription object
	 *
	 * @return array
	 */
	public static function add_subscription_payment_meta( $payment_meta, $subscription ) {
		$payment_meta[ self::PAYMENT_ID ] = array(
			'swedbankpay_meta' => array(
				'token_id' => array(
					'value' => implode( ',', $subscription->get_payment_tokens() ),
					'label' => __( 'Card Token ID', 'swedbank-pay-woocommerce-checkout' ),
				),
			),
		);

		return $payment_meta;
	}

	/**
	 * @param WC_Subscription $subscription
	 * @param $field_id
	 * @param $field_value
	 * @param $meta_data
	 */
	public static function payment_meta_input( $subscription, $field_id, $field_value, $meta_data ) {
		$tokens = \WC_Payment_Tokens::get_tokens( array(
			'gateway_id' => self::PAYMENT_ID,
			'user_id' => $subscription->get_user_id()
		) );

		echo '<select class="short" name="' . esc_attr( $field_id ) . '" id="' . esc_attr( $field_id ) . '">';
		echo '<option value="">' . __( 'Please select', 'swedbank-pay-woocommerce-checkout' ) . '</option>';

		foreach ($tokens as $token):
			/** @var WC_Payment_Token_Swedbank_Pay $token */

			$selected = $field_value == $token->get_id() ? ' selected ' : '';
			echo '<option value="' . esc_attr( $token->get_id() ) . '" ' . $selected . ' >' .
				 esc_html(
					$token->get_meta( 'masked_pan' ) .
					'(' . $token->get_expiry_month() . '/' . substr( $token->get_expiry_year(), 2 ) . ')'
				 ) .
				 '</option>'
			?>
			<?php
		endforeach;

		echo '</select>';
	}

	/**
	 * Validate the payment meta data required to process automatic recurring payments so that store managers can
	 * manually set up automatic recurring payments for a customer via the Edit Subscription screen in Subscriptions 2.0+.
	 *
	 * @param string $payment_method_id The ID of the payment method to validate
	 * @param array $payment_meta associative array of meta data required for automatic payments
	 * @param WC_Subscription $subscription
	 *
	 * @throws Exception
	 */
	public static function validate_subscription_payment_meta( $payment_method_id, $payment_meta, $subscription ) {
		if ( $payment_method_id === self::PAYMENT_ID ) {
			if ( empty( $payment_meta['swedbankpay_meta']['token_id']['value'] ) ) {
				throw new Exception( 'A "Card Token ID" value is required.' );
			}

			$tokens = explode( ',', $payment_meta['swedbankpay_meta']['token_id']['value'] );
			foreach ( $tokens as $token_id ) {
				$token = new WC_Payment_Token_Swedbank_Pay( $token_id );
				if ( ! $token->get_id() ) {
					throw new Exception( 'This "Card Token ID" value not found.' );
				}

				if ( $token->get_gateway_id() !== self::PAYMENT_ID ) {
					throw new Exception( 'This "Card Token ID" value should related to Swedbank Pay.' );
				}

				if ( $token->get_user_id() !== $subscription->get_user_id() ) {
					throw new Exception( 'Access denied for this "Card Token ID" value.' );
				}
			}
		}
	}

	/**
	 * Save payment method meta data for the Subscription
	 *
	 * @param WC_Subscription $subscription
	 * @param string $meta_table
	 * @param string $meta_key
	 * @param string $meta_value
	 */
	public static function save_subscription_payment_meta( $subscription, $meta_table, $meta_key, $meta_value ) {
		if ( $subscription->get_payment_method() !== self::PAYMENT_ID ) {
			return;
		}

		if ( 'swedbankpay_meta' === $meta_table && 'token_id' === $meta_key ) {
			// Delete tokens
			$subscription->delete_meta_data( '_payment_tokens' );
			$subscription->save();

			// Add tokens
			$tokens = explode( ',', $meta_value );
			foreach ( $tokens as $token_id ) {
				$token = new WC_Payment_Token_Swedbank_Pay( $token_id );
				if ( $token->get_id() ) {
					$subscription->add_payment_token( $token );
				}
			}
		}
	}

	/**
	 * When a subscription payment is due.
	 *
	 * @param          $amount_to_charge
	 * @param WC_Order $renewal_order
	 */
	public static function scheduled_subscription_payment( $amount_to_charge, $renewal_order ) {
		$user_agent = $renewal_order->get_customer_user_agent();
		if ( empty( $user_agent ) ) {
			$renewal_order->set_customer_user_agent( 'WooCommerce/' . WC()->version );
			$renewal_order->save();
		}

		try {
			$tokens = $renewal_order->get_payment_tokens();

			foreach ( $tokens as $token_id ) {
				$token = new WC_Payment_Token_Swedbank_Pay( $token_id );
				if ( $token->get_gateway_id() !== self::PAYMENT_ID ) {
					continue;
				}

				if ( ! $token->get_id() ) {
					throw new Exception( 'Invalid Token Id' );
				}

				$gateway = self::get_payment_gateway();
				$gateway->process_recurring_payment( $renewal_order, $token->get_recurrence_token() );

				break;
			}
		} catch ( \Exception $e ) {
			$renewal_order->update_status( 'failed' );
			$renewal_order->add_order_note(
				sprintf(
				/* translators: 1: amount 2: error */ __( 'Failed to charge "%1$s". %2$s.', 'woocommerce' ),
					wc_price( $amount_to_charge ),
					$e->getMessage()
				)
			);
		}
	}

	/**
	 * Render the payment method used for a subscription in the "My Subscriptions" table
	 *
	 * @param string $payment_method_to_display the default payment method text to display
	 * @param WC_Subscription $subscription the subscription details
	 *
	 * @return string the subscription payment method
	 */
	public static function maybe_render_subscription_payment_method( $payment_method_to_display, $subscription ) {
		if ( self::PAYMENT_ID !== $subscription->get_payment_method() || ! $subscription->get_user_id() ) {
			return $payment_method_to_display;
		}

		$tokens = $subscription->get_payment_tokens();
		foreach ( $tokens as $token_id ) {
			try {
				$token = new WC_Payment_Token_Swedbank_Pay( $token_id );
			} catch ( \Exception $e ) {
				continue;
			}

			if ( $token->get_gateway_id() !== self::PAYMENT_ID ) {
				continue;
			}

			return sprintf(
			/* translators: 1: pan 2: month 3: year */ __( 'Via %1$s card ending in %2$s/%3$s', 'swedbank-pay-woocommerce-checkout' ),
				$token->get_masked_pan(),
				$token->get_expiry_month(),
				$token->get_expiry_year()
			);
		}

		return $payment_method_to_display;
	}

	/**
	 * Modify "Save to account" to lock that if needs.
	 *
	 * @param string $html
	 * @param WC_Payment_Gateway $gateway
	 *
	 * @return string
	 */
	public static function save_new_payment_method_option_html( $html, $gateway ) {
		if ( self::PAYMENT_ID !== $gateway->id ) {
			return $html;
		}

		// Lock "Save to Account" for Recurring Payments / Payment Change
		if ( self::wcs_cart_has_subscription() || self::wcs_is_payment_change() ) {
			// Load XML
			libxml_use_internal_errors( true );
			$doc = new \DOMDocument();
			$status = @$doc->loadXML( $html );
			if ( false !== $status ) {
				$item = $doc->getElementsByTagName('input')->item( 0 );
				$item->setAttribute( 'checked','checked' );
				$item->setAttribute( 'readonly','readonly' );
				$item->setAttribute( 'onclick', 'return false;');

				$html = $doc->saveHTML($doc->documentElement);
			}
		}

		return $html;
	}

	/**
	 * Filter tokens.
	 *
	 * @param $tokens
	 * @param $customer_id
	 * @param $gateway_id
	 *
	 * @return array|mixed
	 */
	public function filter_tokens( $tokens, $customer_id, $gateway_id ) {
		if ( self::PAYMENT_ID !== $gateway_id ) {
			return $tokens;
		}

		$result = [];
		foreach ( $tokens as $token ) {
			/** @var \WC_Payment_Token $token */
			if ( self::PAYMENT_ID !== $token->get_gateway_id() ) {
				$result[] = $token;

				continue;
			}

			/** @var WC_Payment_Token_Swedbank_Pay $token */
			$recurrence_token = $token->get_recurrence_token();
			if ( self::wcs_cart_has_subscription() && empty( $recurrence_token ) ) {
				// Don't show token
				continue;
			}

			$unscheduled_token = $token->get_unscheduled_token();
			if ( self::wcs_is_payment_change() && empty( $unscheduled_token ) ) {
				// Don't show token
				continue;
			}

			$result[] = $token;
		}

		return $result;
	}

	/**
	 * Get Payment Method Instance.
	 *
	 * @return WC_Gateway_Swedbank_Pay_Checkout
	 * @throws Exception
	 */
	private static function get_payment_gateway() {
		$gateways = WC()->payment_gateways()->payment_gateways();

		if ( isset( $gateways[ self::PAYMENT_ID ] ) ) {
			return $gateways[ self::PAYMENT_ID ];
		}

		throw new \Exception( 'The checkout payment gateway is unavailable.' );
	}


	/**
	 * Check is Cart have Subscription Products.
	 *
	 * @return bool
	 */
	private static function wcs_cart_has_subscription() {
		if ( ! class_exists( 'WC_Subscriptions_Product' ) ) {
			return false;
		}

		// Check is Recurring Payment
		$cart = WC()->cart->get_cart();
		foreach ( $cart as $item ) {
			if ( is_object( $item['data'] ) && \WC_Subscriptions_Product::is_subscription( $item['data'] ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * WC Subscriptions: Is Payment Change.
	 *
	 * @return bool
	 */
	private static function wcs_is_payment_change() {
		return class_exists( '\\WC_Subscriptions_Change_Payment_Gateway', false )
			   && \WC_Subscriptions_Change_Payment_Gateway::$is_request_to_change_payment;
	}
}

new WC_Swedbank_Subscriptions();