PluginProbe
Subscriptions for WooCommerce with Stripe Recurring Payments / 2.2.0
Subscriptions for WooCommerce with Stripe Recurring Payments v2.2.0
2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 1.11.2 1.11.1 1.11.0 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.6 1.9.5 trunk 1.3.0 1.3.1 1.3.2 1.4.0 All 65 releases
← All changes | includes/Admin/OnboardingAjax.php +28 -171 1.10.4 → 2.2.0 View file →
@@ -4,9 +4,12 @@
4 4
5 5 /**
6 6 * AJAX handlers for the onboarding wizard.
7 7 *
8 - * Handles page-2 save (product create/update), variation fetch, and wizard reset.
8 + * The wizard creates a plan + duration and connects it to a product entirely
9 + * through the Plans REST API (wpsubscription/v1/plans). The one thing REST does
10 + * not offer is creating a WooCommerce product, so that single step lives here;
11 + * everything else — group, term, relation — is done client-side against REST.
9 12 *
10 13 * @package SpringDevs\Subscription\Admin
11 14 */
12 15 class OnboardingAjax {
@@ -14,19 +17,20 @@
14 17 /**
15 18 * Initialize the class.
16 19 */
17 20 public function __construct() {
18 - add_action( 'wp_ajax_subscrpt_save_wizard_page2', array( $this, 'save_wizard_page2' ) );
21 + add_action( 'wp_ajax_subscrpt_create_wizard_product', array( $this, 'create_wizard_product' ) );
19 22 add_action( 'wp_ajax_subscrpt_reset_wizard', array( $this, 'reset_wizard' ) );
20 - add_action( 'wp_ajax_subscrpt_get_product_variations', array( $this, 'get_product_variations' ) );
21 23 }
22 24
23 25 /**
24 - * Save wizard Page 2 data to session and create/update product.
26 + * Create a bare simple product (name + price) for the wizard to connect a
27 + * plan to. No subscription meta is written here — connecting the plan (the
28 + * REST relation) is what makes the product subscribable.
25 29 *
26 30 * @return void Sends JSON.
27 31 */
28 - public function save_wizard_page2() {
32 + public function create_wizard_product() {
29 33 check_ajax_referer( 'subscrpt_onboarding_wizard', 'nonce' );
30 34
31 35 if ( ! current_user_can( 'manage_options' ) ) {
32 36 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'subscription' ) ), 403 );
@@ -31,185 +35,38 @@
31 35 if ( ! current_user_can( 'manage_options' ) ) {
32 36 wp_send_json_error( array( 'message' => __( 'Permission denied.', 'subscription' ) ), 403 );
33 37 }
34 38
35 - if ( ! session_id() ) {
36 - session_start();
37 - }
39 + $product_name = isset( $_POST['product_name'] ) ? sanitize_text_field( wp_unslash( $_POST['product_name'] ) ) : '';
40 + $product_price = isset( $_POST['product_price'] ) ? sanitize_text_field( wp_unslash( $_POST['product_price'] ) ) : '';
38 41
39 - $product_mode = isset( $_POST['product_mode'] ) ? sanitize_text_field( wp_unslash( $_POST['product_mode'] ) ) : 'new';
40 - $product_name = isset( $_POST['product_name'] ) ? sanitize_text_field( wp_unslash( $_POST['product_name'] ) ) : '';
41 - $product_price = isset( $_POST['product_price'] ) ? sanitize_text_field( wp_unslash( $_POST['product_price'] ) ) : '';
42 - $existing_product = isset( $_POST['existing_product_id'] ) ? absint( $_POST['existing_product_id'] ) : 0;
43 - $variation_id = isset( $_POST['variation_id'] ) ? absint( $_POST['variation_id'] ) : 0;
44 - $timing_option = isset( $_POST['timing_option'] ) ? sanitize_text_field( wp_unslash( $_POST['timing_option'] ) ) : 'never';
45 - $billing_per = isset( $_POST['billing_per'] ) ? absint( $_POST['billing_per'] ) : 1;
46 - $billing_period = isset( $_POST['billing_period'] ) ? sanitize_text_field( wp_unslash( $_POST['billing_period'] ) ) : 'months';
47 - $trial_timing_per = isset( $_POST['trial_timing_per'] ) ? absint( $_POST['trial_timing_per'] ) : 0;
48 - $signup_fee = isset( $_POST['signup_fee'] ) ? sanitize_text_field( wp_unslash( $_POST['signup_fee'] ) ) : '';
49 - $trial_enabled = isset( $_POST['trial_enabled'] ) ? (int) $_POST['trial_enabled'] : 0;
50 - $trial_timing_opt = isset( $_POST['trial_timing_option'] ) ? sanitize_text_field( wp_unslash( $_POST['trial_timing_option'] ) ) : 'days';
51 - $length_enabled = isset( $_POST['length_enabled'] ) ? (int) $_POST['length_enabled'] : 0;
52 - $length_per = isset( $_POST['length_per'] ) ? absint( $_POST['length_per'] ) : 0;
53 - $length_option = isset( $_POST['length_option'] ) ? sanitize_text_field( wp_unslash( $_POST['length_option'] ) ) : 'months';
54 -
55 - $result_product_id = 0;
56 -
57 - if ( 'new' === $product_mode ) {
58 - $wc_product = new \WC_Product_Simple();
59 - $wc_product->set_name( $product_name );
60 - $wc_product->set_regular_price( wc_format_decimal( $product_price ) );
61 - $wc_product->set_status( 'publish' );
62 - $this->apply_subscription_meta( $wc_product, $billing_period, $billing_per, $trial_timing_per, $trial_timing_opt, $signup_fee );
63 - $result_product_id = $wc_product->save();
64 -
65 - if ( ! $result_product_id ) {
66 - wp_send_json_error( array( 'message' => __( 'Failed to create product. Please try again.', 'subscription' ) ) );
67 - }
68 - } elseif ( 'existing' === $product_mode && $existing_product > 0 ) {
69 - $parent = wc_get_product( $existing_product );
70 -
71 - if ( ! $parent ) {
72 - wp_send_json_error( array( 'message' => __( 'Product not found.', 'subscription' ) ) );
73 - }
74 -
75 - if ( $variation_id > 0 ) {
76 - // Variable product: update parent name only; apply meta + price on the variation.
77 - $parent->set_name( $product_name );
78 - $parent->save();
79 -
80 - $variation = wc_get_product( $variation_id );
81 - if ( $variation ) {
82 - $variation->set_regular_price( wc_format_decimal( $product_price ) );
83 - $this->apply_subscription_meta( $variation, $billing_period, $billing_per, $trial_timing_per, $trial_timing_opt, $signup_fee );
84 - $variation->save();
85 - }
86 - } else {
87 - // Simple product: update name, price, and subscription meta in one save.
88 - $parent->set_name( $product_name );
89 - $parent->set_regular_price( wc_format_decimal( $product_price ) );
90 - $this->apply_subscription_meta( $parent, $billing_period, $billing_per, $trial_timing_per, $trial_timing_opt, $signup_fee );
91 - $parent->save();
92 - }
93 -
94 - $result_product_id = $existing_product;
42 + if ( '' === trim( $product_name ) ) {
43 + wp_send_json_error( array( 'message' => __( 'Product name is required.', 'subscription' ) ) );
95 44 }
96 45
97 - $_SESSION['subscrpt_onboarding_wizard'] = array(
98 - 'page' => 3,
99 - 'product_id' => $result_product_id,
100 - 'product_mode' => $product_mode,
101 - 'product_name' => $product_name,
102 - 'product_price' => $product_price,
103 - 'existing_product' => $existing_product,
104 - 'variation_id' => $variation_id,
105 - 'timing_option' => $timing_option,
106 - 'billing_per' => $billing_per,
107 - 'billing_period' => $billing_period,
108 - 'trial_timing_per' => $trial_timing_per,
109 - 'signup_fee' => $signup_fee,
110 - 'trial_enabled' => $trial_enabled,
111 - 'trial_timing_option' => $trial_timing_opt,
112 - 'length_enabled' => $length_enabled,
113 - 'length_per' => $length_per,
114 - 'length_option' => $length_option,
115 - );
116 -
117 - wp_send_json_success( array( 'product_id' => $result_product_id ) );
118 - }
119 -
120 - /**
121 - * Stage subscription meta on a WC product object via the WC meta layer.
122 - * Does NOT call save() — the caller must save the object after calling this method.
123 - *
124 - * @param \WC_Product $product Product or variation object.
125 - * @param string $billing_period Billing period unit (days/weeks/months/years).
126 - * @param int $billing_per Billing interval count.
127 - * @param int $trial_per Free trial length.
128 - * @param string $trial_option Free trial period unit.
129 - * @param string $signup_fee One-time sign-up fee (pro only).
130 - *
131 - * @return void
132 - */
133 - private function apply_subscription_meta( $product, $billing_period, $billing_per, $trial_per, $trial_option, $signup_fee ) {
134 - $product->update_meta_data( '_subscrpt_enabled', true );
135 - $product->update_meta_data( '_subscrpt_timing_option', $billing_period );
136 - $product->update_meta_data( '_subscrpt_timing_per', $billing_per );
137 - $product->update_meta_data( '_subscrpt_trial_timing_per', $trial_per );
138 - $product->update_meta_data( '_subscrpt_trial_timing_option', $trial_option );
139 - $product->update_meta_data( '_subscrpt_payment_type', 'recurring' );
140 -
141 - if ( function_exists( 'subscrpt_pro_activated' ) && subscrpt_pro_activated() && '' !== $signup_fee ) {
142 - $product->update_meta_data( '_subscrpt_signup_fee', wc_format_decimal( $signup_fee ) );
46 + $product = new \WC_Product_Simple();
47 + $product->set_name( $product_name );
48 + if ( '' !== trim( $product_price ) ) {
49 + $product->set_regular_price( wc_format_decimal( $product_price ) );
143 50 }
144 - }
51 + $product->set_status( 'publish' );
52 + $product_id = $product->save();
145 53
146 - /**
147 - * Return available variations for a variable product (wizard use).
148 - *
149 - * @return void Sends JSON.
150 - */
151 - public function get_product_variations() {
152 - check_ajax_referer( 'subscrpt_onboarding_wizard', 'nonce' );
153 -
154 - if ( ! current_user_can( 'manage_options' ) ) {
155 - wp_send_json_error( array( 'message' => __( 'Permission denied.', 'subscription' ) ), 403 );
156 - }
157 -
158 - $product_id = isset( $_POST['product_id'] ) ? absint( $_POST['product_id'] ) : 0;
159 54 if ( ! $product_id ) {
160 - wp_send_json_error( array( 'message' => __( 'Invalid product.', 'subscription' ) ) );
55 + wp_send_json_error( array( 'message' => __( 'Failed to create product. Please try again.', 'subscription' ) ) );
161 56 }
162 57
163 - $product = wc_get_product( $product_id );
164 - if ( ! $product || 'variable' !== $product->get_type() ) {
165 - wp_send_json_error( array( 'message' => __( 'Not a variable product.', 'subscription' ) ) );
166 - }
167 -
168 - $available = $product->get_available_variations();
169 - $result = array();
170 -
171 - foreach ( $available as $variation ) {
172 - $v_id = $variation['variation_id'];
173 -
174 - // Build human-readable attribute label.
175 - $attr_labels = array();
176 - foreach ( $variation['attributes'] as $attr_key => $attr_value ) {
177 - if ( '' === $attr_value ) {
178 - $attr_labels[] = __( 'Any', 'subscription' );
179 - continue;
180 - }
181 - $taxonomy = str_replace( 'attribute_', '', $attr_key );
182 - if ( taxonomy_exists( $taxonomy ) ) {
183 - $term = get_term_by( 'slug', $attr_value, $taxonomy );
184 - $attr_labels[] = $term ? $term->name : ucfirst( str_replace( '-', ' ', $attr_value ) );
185 - } else {
186 - $attr_labels[] = ucfirst( str_replace( '-', ' ', $attr_value ) );
187 - }
188 - }
189 - $label = implode( ' / ', $attr_labels );
190 - if ( ! $label ) {
191 - /* translators: %d: variation ID */
192 - $label = sprintf( __( 'Variation #%d', 'subscription' ), $v_id );
193 - }
194 -
195 - $result[] = array(
196 - 'id' => $v_id,
197 - 'label' => $label,
198 - 'price' => $variation['display_price'],
199 - 'sku' => $variation['sku'],
200 - 'billing_period' => get_post_meta( $v_id, '_subscrpt_timing_option', true ),
201 - 'billing_per' => get_post_meta( $v_id, '_subscrpt_timing_per', true ) ?: 1,
202 - 'trial_per' => get_post_meta( $v_id, '_subscrpt_trial_timing_per', true ),
203 - 'signup_fee' => get_post_meta( $v_id, '_subscrpt_signup_fee', true ),
204 - );
205 - }
206 -
207 - wp_send_json_success( $result );
58 + wp_send_json_success(
59 + array(
60 + 'product_id' => $product_id,
61 + 'product_name' => $product_name,
62 + 'product_price' => $product_price,
63 + )
64 + );
208 65 }
209 66
210 67 /**
211 - * Reset wizard session (for "Add another" action).
68 + * Reset wizard session (for "Add another" / "Start over" actions).
212 69 *
213 70 * @return void Sends JSON.
214 71 */
215 72 public function reset_wizard() {