PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / integrations / integration-trait.php

integration-trait.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/integrations/integration-trait.php

253 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine\Integrations;
4
5 use StoreEngine\Classes\Exceptions\StoreEngineException;
6 use StoreEngine\Classes\Integration;
7 use StoreEngine\Classes\Price;
8 use StoreEngine\Classes\Product\SimpleProduct;
9 use StoreEngine\Utils\Helper;
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit; // Exit if access directly
13 }
14
15 trait IntegrationTrait {
16 private string $integration_name;
17 private int $item_id;
18 private string $item_title;
19 private array $prices;
20
21 abstract protected function set_integration_config(): void;
22
23 public function init_integration() {
24 $this->set_integration_config();
25 }
26
27 public function handle_price_validation() {
28 $prices = [];
29
30 foreach ( $this->prices as $price_arr ) {
31 $type = isset( $price_arr['type'] ) ? sanitize_text_field( $price_arr['type'] ) : 'custom';
32 $price_id = isset( $price_arr['price_id'] ) ? (int) sanitize_text_field( $price_arr['price_id'] ) : 0;
33 $order = isset( $price_arr['order'] ) ? (int) sanitize_text_field( $price_arr['order'] ) : 0;
34 $product_id = isset( $price_arr['product_id'] ) ? absint( $price_arr['product_id'] ) : 0;
35
36 if ( 'custom' === $type ) {
37 if ( ! isset( $price_arr['price_type'] ) ) {
38 wp_send_json_error( [ 'message' => __( 'price_type is required', 'storeengine' ) ] );
39 }
40
41 if ( ! isset( $price_arr['price'] ) ) {
42 wp_send_json_error( [ 'message' => __( 'price is required', 'storeengine' ) ] );
43 }
44
45 if ( ! isset( $price_arr['price_name'] ) ) {
46 $price_arr['price_name'] = sanitize_text_field( $price_arr['price_type'] );
47 }
48 } else {
49 if ( 0 === $price_id ) {
50 wp_send_json_error( [ 'message' => __( 'price_id is required', 'storeengine' ) ] );
51 }
52 if ( 0 === $product_id ) {
53 wp_send_json_error( [ 'message' => __( 'product_id is required', 'storeengine' ) ] );
54 }
55
56 $prices[] = [
57 'product_id' => $product_id,
58 'type' => $type,
59 'price_id' => $price_id,
60 'order' => $order,
61 'price_name' => null,
62 'price_type' => null,
63 'price' => 0,
64 'compare_price' => null,
65 ];
66 continue;
67 }
68
69 $price_name = sanitize_text_field( $price_arr['price_name'] );
70 $price_type = sanitize_text_field( $price_arr['price_type'] );
71 $price = (float) sanitize_text_field( $price_arr['price'] );
72 $compare_price = isset( $price_arr['compare_price'] ) ? (float) sanitize_text_field( $price_arr['compare_price'] ) : null;
73 if ( $compare_price && $compare_price <= $price ) {
74 wp_send_json_error( [ 'message' => __( 'Sale price cannot be greater than or equals to compare price', 'storeengine' ) ] );
75 }
76
77 if ( 'subscription' === $price_type ) {
78 if ( ! isset( $price_arr['payment_duration'] ) ) {
79 wp_send_json_error( [
80 'message' => __( 'Duration is required', 'storeengine' ),
81 ] );
82 }
83
84 if ( ! isset( $price_arr['payment_duration_type'] ) ) {
85 wp_send_json_error( [
86 'message' => __( 'Duration type is required', 'storeengine' ),
87 ] );
88 }
89 }
90
91 $price_data = [
92 'product_id' => $product_id,
93 'type' => $type,
94 'price_id' => $price_id,
95 'price_name' => $price_name,
96 'price_type' => $price_type,
97 'price' => $price,
98 'compare_price' => $compare_price,
99 'order' => $order,
100 ];
101
102 if ( 'subscription' === $price_type ) {
103 $price_data['payment_duration'] = absint( $price_arr['payment_duration'] );
104 $price_data['payment_duration_type'] = sanitize_text_field( $price_arr['payment_duration_type'] );
105 }
106
107 $prices[] = $price_data;
108 }//end foreach
109
110 $this->prices = $prices;
111 }
112
113 public function create_product(): SimpleProduct {
114 $product = new SimpleProduct();
115 $product->set_name( $this->item_title );
116 $product->set_author_id( get_current_user_id() );
117 $product->set_shipping_type( 'digital' );
118 $product->set_digital_auto_complete( true );
119 $product->save();
120
121 if ( ! $product->get_id() ) {
122 wp_send_json_error( [ 'message' => __( 'Could not create product!', 'storeengine' ) ] );
123 }
124
125 // Stamp the owning integration so addons can identify their own
126 // auto-spawned products (e.g. to keep them out of the shop catalog).
127 // Predefined/user products never pass through here, so they are never
128 // stamped and stay fully visible.
129 update_post_meta( $product->get_id(), '_storeengine_integration_product', $this->integration_name );
130
131 return $product;
132 }
133
134 /**
135 * @throws StoreEngineException
136 */
137 public function create_prices_and_integration() {
138 foreach ( $this->prices as &$price_data ) {
139 $product_id = $price_data['product_id'];
140
141 if ( ! $product_id ) {
142 $product_id = $this->create_product()->get_id();
143 }
144
145 $price = new Price( $price_data['price_id'] );
146
147 if ( 'predefined' === $price_data['type'] ) {
148 $price->add_integration( $this->integration_name, $this->item_id );
149 continue;
150 }
151
152 $price->set_name( $price_data['price_name'] );
153 $price->set_product_id( $product_id );
154 $price->set_type( $price_data['price_type'] );
155
156 if ( $price_data['compare_price'] ) {
157 $price->set_price( $price_data['price'] );
158 $price->set_compare_price( $price_data['compare_price'] );
159 } else {
160 $price->set_price( $price_data['price'] );
161 $price->set_compare_price( null );
162 }
163
164 $price->set_menu_order( $price_data['order'] );
165
166 if ( 'subscription' === $price_data['price_type'] ) {
167 $price->set_payment_duration( $price_data['payment_duration'] );
168 $price->set_payment_duration_type( $price_data['payment_duration_type'] );
169 }
170
171 $price->save();
172
173 if ( 0 === $price_data['price_id'] ) {
174 $price_data['price_id'] = $price->get_id();
175 $price->add_integration( $this->integration_name, $this->item_id );
176 }
177 }
178 }
179
180 public function update_missing_pricing_ids() {
181 global $wpdb;
182
183 $price_ids = array_map( fn ( $price ) => $price['price_id'], $this->prices );
184
185 // @TODO make integrations table entity & collection for caching support.
186 // phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
187 $integrations = $wpdb->get_results( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}storeengine_integrations WHERE provider = %s AND integration_id = %d", $this->integration_name, $this->item_id ) );
188 // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
189
190 $existing_price_ids = array_map( fn( $integration ) => (int) $integration->price_id, $integrations );
191 $missing_price_ids = array_diff( $existing_price_ids, $price_ids );
192
193 foreach ( $missing_price_ids as $price_id ) {
194 // array_find() needs WP 6.8+; use a plain loop for wider compat.
195 $integration = null;
196 foreach ( $integrations as $candidate ) {
197 if ( (int) $candidate->price_id === $price_id ) {
198 $integration = $candidate;
199 break;
200 }
201 }
202 if ( $integration ) {
203 ( new Integration( (int) $integration->id ) )->delete();
204 }
205 }
206 }
207
208 public function get_integrations() {
209 $integrations = Helper::get_integration_repository_by_id( $this->integration_name, $this->item_id );
210 if ( empty( $integrations ) ) {
211 wp_send_json_success( [ 'prices' => [] ] );
212 }
213
214 $prices = [];
215 foreach ( $integrations as $integration ) {
216 $price = $this->format_price( $integration->integration->get_id(), $integration->price );
217 $prices[] = $price;
218 }
219
220 wp_send_json_success( [ 'prices' => $prices ] );
221 }
222
223 private function format_price( int $id, Price $price ): array {
224 $data = [
225 'id' => $id,
226 'product_id' => $price->get_product_id(),
227 'product_name' => get_the_title( $price->get_product_id() ),
228 'price_id' => $price->get_id(),
229 'price_name' => $price->get_name(),
230 'price_type' => $price->get_type(),
231 'price' => $price->get_price(),
232 'compare_price' => $price->get_compare_price(),
233 'order' => $price->get_menu_order(),
234 ];
235
236 if ( 'subscription' === $price->get_type() ) {
237 $data['payment_duration'] = $price->get_payment_duration();
238 $data['payment_duration_type'] = $price->get_payment_duration_type();
239 }
240
241 return $data;
242 }
243
244 /**
245 * @throws StoreEngineException
246 */
247 public function handle_integrations() {
248 $this->handle_price_validation();
249 $this->create_prices_and_integration();
250 $this->update_missing_pricing_ids();
251 }
252 }
253