diff --git a/Dockerfile b/Dockerfile index ec6560a..866b0c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,11 +6,14 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends unzip wget gettext vim \ && wget https://downloads.wordpress.org/plugin/woocommerce.${woocommerce_version}.zip -O /tmp/woocommerce.zip \ && wget https://downloads.wordpress.org/plugin/relative-url.0.1.7.zip -O /tmp/relative-url.zip \ + && wget https://github.com/wp-premium/woocommerce-subscriptions/archive/refs/tags/3.0.1.zip -O /tmp/woocommerce-subscriptions.zip \ && cd /usr/src/wordpress/wp-content/plugins \ && unzip /tmp/woocommerce.zip \ && unzip /tmp/relative-url.zip \ + && unzip /tmp/woocommerce-subscriptions.zip \ && rm /tmp/woocommerce.zip \ && rm /tmp/relative-url.zip \ + && rm /tmp/woocommerce-subscriptions.zip \ && rm -rf /var/lib/apt/lists/* COPY --from=composer /usr/bin/composer /usr/bin/composer diff --git a/class-wc-gateway-komoju.php b/class-wc-gateway-komoju.php index 16abeac..36a1079 100755 --- a/class-wc-gateway-komoju.php +++ b/class-wc-gateway-komoju.php @@ -57,6 +57,7 @@ public function __construct() // Filters // Actions add_action('woocommerce_update_options_payment_gateways_' . $this->id, [$this, 'process_admin_options']); + add_action('woocommerce_scheduled_subscription_payment_' . $this->id, [$this, 'process_subscription'], 10, 3); if ($this->id === 'komoju') { include_once 'includes/class-wc-gateway-komoju-ipn-handler.php'; @@ -192,6 +193,10 @@ public function process_payment($order_id, $payment_type = null) ], 'line_items' => $line_items, ]; + if (class_exists('WC_Subscriptions_Order') && WC_Subscriptions_Order::order_contains_subscription($order_id)) { + $session_params['mode'] = 'customer'; + } + $remove_nulls = function ($v) { return !is_null($v); }; $session_params['payment_data'] = array_filter( $session_params['payment_data'], @@ -206,6 +211,44 @@ public function process_payment($order_id, $payment_type = null) ]; } + /** + * Process an incoming subscription charge + */ + public function process_subscription($amount_to_charge, $order) + { + foreach (wcs_get_subscriptions_for_order($order, ['order_type' => 'any']) as $subscription) { + $parent_order_id = $subscription->get_parent_id(); + } + + // fetch token from the subscription's parent order + $token = get_post_meta($parent_order_id, 'komoju_payment_token', true); + if (empty($token)) { + $this->log('ERROR: token missing on subscription payment metadata'); + WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); + + return; + } + try { + $komoju_request = $this->create_komoju_payment($token, $order); + WC_Subscriptions_Manager::process_subscription_payments_on_order($order); + } catch (Exception $e) { + $order->add_order_note('KOMOJU Subscription payment failed'); + WC_Subscriptions_Manager::process_subscription_payment_failure_on_order($order); + } + } + + public function create_komoju_payment($customer, $order) + { + $currency = $order->get_currency(); + + return $this->komoju_api->createPayment([ + 'amount' => self::to_cents($order->get_total(), $currency), + 'external_order_num' => $this->external_order_num($order), + 'currency' => $currency, + 'customer' => $customer, + ]); + } + /** * Payment form on checkout page */ diff --git a/docker-compose.yml b/docker-compose.yml index 27121e4..3aa3ad2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -21,7 +21,7 @@ services: context: . dockerfile: Dockerfile args: - woocommerce_version: 4.0.1 + woocommerce_version: 4.4.0 ports: - "8000:80" restart: always diff --git a/docs/dev_setup.md b/docs/dev_setup.md index 6250565..8787edd 100644 --- a/docs/dev_setup.md +++ b/docs/dev_setup.md @@ -81,3 +81,14 @@ To be able to test the checkout you will first need to have a purchasable produc If you go to http://127.0.0.1:8000/?post_type=product you should be able to see the shop, with an item for purchase you can use to test the Komoju integration. + +## Testing subscriptions + +1. Follow the setup instructions above +2. Confirm that the woocommerce subscriptions plugin appears on the list of installed plugins, and that it is enabled. +3. Create a new product in the same manner as above, but this time choose "Simple Subscription" for product type +4. select the price and billing cadence +5. go through the normal checkout flow for the store, with the new subscription product in the cart +6. complete payment flow +7. go to the "subscriptions" tab under woocommerce category on the admin and confirm the presence of the subscription +8. to test subscription renewals - go to the newly created subscription in the subscriptions tab, and select "Process renewal" under subscription actions at the top right of the page diff --git a/includes/class-wc-gateway-komoju-ipn-handler.php b/includes/class-wc-gateway-komoju-ipn-handler.php index 02c9cc7..0d50bd1 100755 --- a/includes/class-wc-gateway-komoju-ipn-handler.php +++ b/includes/class-wc-gateway-komoju-ipn-handler.php @@ -41,6 +41,17 @@ public function check_response() // callback from session page if (isset($_GET['session_id'])) { $session = $this->get_session($_GET['session_id']); + // session was customer mode + if (!is_null($session->customer_id)) { + $external_order_num_param = $_GET['external_order_num']; // only passed in customer mode + update_post_meta($external_order_num_param, 'komoju_payment_token', wc_clean($session->customer_id)); + // create initial payment for parent order of subscription + $this->gateway->create_komoju_payment($session->customer_id, wc_get_order($external_order_num_param)); + $success_url = $this->gateway->get_return_url($order); + wp_redirect($success_url); + exit; + } + $order = $this->get_order_from_komoju_session($session, $this->invoice_prefix); // null payment on a session indicates incomplete payment flow diff --git a/includes/class-wc-gateway-komoju-single-slug.php b/includes/class-wc-gateway-komoju-single-slug.php index 524a2c2..75d18b1 100644 --- a/includes/class-wc-gateway-komoju-single-slug.php +++ b/includes/class-wc-gateway-komoju-single-slug.php @@ -31,6 +31,20 @@ public function __construct($payment_method) $this->supports[] = 'refunds'; } + if (in_array($slug, ['credit_card', 'paypay'])) { + array_push($this->supports, + 'subscriptions', + 'subscription_cancellation', + 'subscription_suspension', + 'subscription_reactivation', + 'subscription_amount_changes', + 'subscription_date_changes', + 'subscription_payment_method_changes', + 'subscription_payment_method_change_admin', + 'multiple_subscriptions', + ); + } + parent::__construct(); } diff --git a/komoju-php/komoju-php/lib/komoju/KomojuApi.php b/komoju-php/komoju-php/lib/komoju/KomojuApi.php index 359ce58..c158531 100644 --- a/komoju-php/komoju-php/lib/komoju/KomojuApi.php +++ b/komoju-php/komoju-php/lib/komoju/KomojuApi.php @@ -44,6 +44,11 @@ public function session($sessionUuid) return $this->get('/api/v1/sessions/' . $sessionUuid); } + public function createPayment($payload) + { + return $this->post('/api/v1/payments', $payload); + } + public function refund($paymentUuid, $payload) { return $this->post('/api/v1/payments/' . $paymentUuid . '/refund', $payload);