PluginProbe
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments / 4.3.3
SureCart – Ecommerce Made Easy For Selling Physical Products, Digital Downloads, Subscriptions, Donations, & Payments v4.3.3
4.7.2 4.7.1 4.7.0 4.6.6 4.6.5 4.6.4 4.6.3 4.6.2 4.6.1 4.6.0 4.5.1 4.5.0 4.4.2 4.4.1 4.4.0 4.3.3 4.3.2 4.3.1 4.3.0 4.2.3 4.2.2 4.2.1 1.0.3 1.0.4 1.0.5 All 281 releases
surecart / app / src / Integrations / IntegrationService.php
IntegrationService.php
469 lines 11.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SureCart\Integrations;
4
5 use SureCart\Integrations\Contracts\IntegrationInterface;
6 use SureCart\Integrations\Contracts\PurchaseSyncInterface;
7 use SureCart\Models\Integration;
8 use SureCart\Models\Purchase;
9
10 /**
11 * Base class for integrations to extend.
12 */
13 abstract class IntegrationService extends AbstractIntegration implements IntegrationInterface {
14 /**
15 * Purchase model for the integration.
16 *
17 * @var \SureCart\Models\Purchase
18 */
19 protected $purchase = null;
20
21 /**
22 * The current user for the integration.
23 *
24 * @var \WP_User
25 */
26 protected $user = null;
27
28 /**
29 * Get the slug for the integration.
30 *
31 * @return string
32 */
33 public function getName() {
34 return '';
35 }
36
37 /**
38 * Get the model for the integration.
39 *
40 * @return string
41 */
42 public function getModel() {
43 return '';
44 }
45
46
47 /**
48 * Get the logo for the integration.
49 *
50 * @return string
51 */
52 public function getLogo() {
53 return '';
54 }
55
56 /**
57 * Get the name for the integration.
58 *
59 * @return string
60 */
61 public function getLabel() {
62 return '';
63 }
64
65 /**
66 * Get the item label for the integration.
67 *
68 * @return string
69 */
70 public function getItemLabel() {
71 return '';
72 }
73
74 /**
75 * Get the item help label for the integration.
76 *
77 * @return string
78 */
79 public function getItemHelp() {
80 return '';
81 }
82
83 /**
84 * Get item listing for the integration.
85 *
86 * @param array $items The integration items.
87 * @param string $search The search query.
88 *
89 * @return array The items for the integration.
90 */
91 public function getItems( $items = [], $search = '' ) {
92 return $items;
93 }
94
95 /**
96 * Get the individual item.
97 *
98 * @param string $id Id for the record.
99 *
100 * @return array The item for the integration.
101 */
102 public function getItem( $id ) {
103 return [];
104 }
105
106 /**
107 * Enable by default.
108 *
109 * @return boolean
110 */
111 public function enabled() {
112 return true;
113 }
114
115 /**
116 * Map this class methods to specific events.
117 *
118 * @var array
119 */
120 protected $methods_map = [
121 'surecart/purchase_created' => 'onPurchaseCreated',
122 'surecart/purchase_invoked' => 'onPurchaseInvoked',
123 'surecart/purchase_revoked' => 'onPurchaseRevoked',
124 ];
125
126 /**
127 * Bootstrap the integration.
128 */
129 public function bootstrap() {
130 // index and list.
131 add_filter( "surecart/integrations/providers/list/{$this->getModel()}", [ $this, 'indexProviders' ], 9 );
132 add_filter( "surecart/integrations/providers/find/{$this->getName()}", [ $this, 'findProvider' ], 9 );
133
134 // get items.
135 add_filter( "surecart/integrations/providers/{$this->getName()}/{$this->getModel()}/items", [ $this, 'getItems' ], 9, 2 );
136 add_filter( "surecart/integrations/providers/{$this->getName()}/item", [ $this, '_getItem' ], 9, 2 );
137
138 // implement purchase events if purchase sync interface is implemented.
139 if ( is_subclass_of( $this, PurchaseSyncInterface::class ) ) {
140 add_action( 'surecart/purchase_created', [ $this, 'callMethod' ], 9 );
141 add_action( 'surecart/purchase_invoked', [ $this, 'callMethod' ], 9 );
142 add_action( 'surecart/purchase_revoked', [ $this, 'callMethod' ], 9 );
143 add_action( 'surecart/purchase_updated', [ $this, 'onPurchaseUpdated' ], 9, 2 );
144 }
145 }
146
147 /**
148 * Get the current purchase.
149 *
150 * @return \SureCart\Models\Purchase|null;
151 */
152 public function getPurchase() {
153 return $this->purchase;
154 }
155
156 public function getPurchaseId() {
157 return $this->purchase->id ?? null;
158 }
159
160 /**
161 * Get the user.
162 *
163 * @return \WP_User|null;
164 */
165 public function getUser() {
166 return $this->purchase->getWPUser();
167 }
168
169 /**
170 * The purchase has been updated.
171 * This is extendable, but is also abtracted into
172 * invoke/revoke and quantity update methods.
173 *
174 * @param Purchase $purchase The purchase.
175 * @param object $request The request.
176 *
177 * @return void
178 */
179 public function onPurchaseUpdated( Purchase $purchase, $request ) {
180 $this->purchase = $purchase;
181
182 $data = (object) $request->data->object ?? null;
183 $previous = (object) $request->data->previous_attributes ?? null;
184
185 // we need data or a previous.
186 if ( empty( $data ) || empty( $previous ) ) {
187 return;
188 }
189
190 // product has changed, let's revoke access to the old one
191 // and provide access to the new one.
192 if ( ! empty( $previous->product ) && $data->product !== $previous->product ) {
193 $previous_purchase = new Purchase(
194 array_merge(
195 $purchase->toArray(),
196 [
197 'product' => $previous->product,
198 'quantity' => $previous->quantity ?? 1,
199 ]
200 )
201 );
202 $this->onPurchaseProductUpdated( $purchase, $previous_purchase, $request );
203 return;
204 }
205
206 // price or variant has changed. We need to revoke access to the old one
207 // and provide access to the new one.
208 $previous_variant = $previous->variant ?? null;
209 $previous_price = $previous->price ?? null;
210 if ( $data->price !== $previous_price || $data->variant !== $previous_variant ) {
211 $previous_purchase = new Purchase(
212 array_merge(
213 $purchase->toArray(),
214 [
215 'price' => $previous_price,
216 'variant' => $previous_variant,
217 ]
218 )
219 );
220 $this->onPurchaseProductUpdated( $purchase, $previous_purchase, $request );
221 return;
222 }
223
224 // The quantity has not changed.
225 $previous_quantity = $previous->quantity ?? 1;
226 if ( (int) $data->quantity === $previous_quantity ) {
227 return;
228 }
229
230 // run quantity updated method.
231 $integrations = (array) $this->getIntegrationData( $purchase ) ?? [];
232 foreach ( $integrations as $integration ) {
233 if ( ! $integration->id ) {
234 continue;
235 }
236
237 if ( $this->purchaseIsNotMatchedWithPriceOrVariant( $integration, $purchase ) ) {
238 continue;
239 }
240
241 $this->onPurchaseQuantityUpdated(
242 $data->quantity,
243 $previous->quantity,
244 $integration,
245 $purchase->getWPUser()
246 );
247 }
248 }
249
250 /**
251 * When the purchase product is updated
252 *
253 * @param \SureCart\Models\Purchase $purchase The purchase.
254 * @param \SureCart\Models\Purchase $previous_purchase The previous purchase.
255 * @param array $request The request.
256 *
257 * @return void
258 */
259 public function onPurchaseProductUpdated( Purchase $purchase, Purchase $previous_purchase, $request ) {
260 $this->purchase = $purchase;
261
262 // product added.
263 $integrations = (array) $this->getIntegrationData( $purchase ) ?? [];
264 foreach ( $integrations as $integration ) {
265 if ( ! $integration->id ) {
266 continue;
267 }
268
269 if ( $this->purchaseIsNotMatchedWithPriceOrVariant( $integration, $purchase ) ) {
270 continue;
271 }
272
273 $this->onPurchaseProductAdded( $integration, $purchase->getWPUser(), $purchase );
274 }
275
276 // product removed.
277 $integrations = (array) $this->getIntegrationData( $previous_purchase ) ?? [];
278
279 // Check if product has changed for Product upgrade group change.
280 $product_changed = $purchase->product_id !== $previous_purchase->product_id;
281
282 foreach ( $integrations as $integration ) {
283 if ( ! $integration->id ) {
284 continue;
285 }
286
287 if ( $this->purchaseIsNotMatchedWithPriceOrVariant( $integration, $previous_purchase ) ) {
288 continue;
289 }
290
291 // If no price & variant has been added for the integration, don't revoke it.
292 // unless the product has changed with the upgrade group.
293 if ( empty( $integration->price_id ) && empty( $integration->variant_id ) && ! $product_changed ) {
294 continue;
295 }
296
297 $this->onPurchaseProductRemoved( $integration, $previous_purchase->getWPUser(), $previous_purchase );
298 }
299 }
300
301 /**
302 * Method to run when the quantity updates.
303 *
304 * @param integer $quantity The new quantity.
305 * @param integer $previous The previous quantity.
306 * @param Purchase $purchase The purchase.
307 * @param array $request The request.
308 *
309 * @return void
310 */
311 public function onPurchaseQuantityUpdated( $quantity, $previous, $purchase, $request ) {
312 $this->purchase = $purchase;
313 // Allow this to be extended to provide functionality. Do nothing by default.
314 }
315
316 /**
317 * Get the item.
318 *
319 * @param string $id Id for the record.
320 *
321 * @return object
322 */
323 public function _getItem( $id ) {
324 if ( ! $this->enabled() ) {
325 return;
326 }
327 $item = (object) $this->getItem( $id );
328 $item->logo = esc_url_raw( $this->getLogo() );
329 return $item;
330 }
331
332 /**
333 * Call the method for the integration.
334 *
335 * @param \SureCart\Models\Purchase $purchase The purchase model.
336 *
337 * @return void
338 */
339 public function callMethod( $purchase ) {
340 // store the current purchase.
341 $this->purchase = $purchase;
342
343 $method = $this->methods_map[ $this->getCurrentAction() ] ?? null;
344 if ( ! $method || ! method_exists( $this, $method ) ) {
345 return;
346 }
347
348 $integrations = (array) $this->getIntegrationData( $purchase ) ?? [];
349 foreach ( $integrations as $integration ) {
350 if ( ! $integration->id ) {
351 continue;
352 }
353
354 // If the integration has a price_id or variant_id, then we need to match with specific price or variant.
355 if ( $this->purchaseIsNotMatchedWithPriceOrVariant( $integration, $purchase ) ) {
356 continue;
357 }
358
359 $user = $purchase->getWPUser();
360 if ( ! $user ) {
361 // throw new \Exception( 'No WordPress user is linked to this customer. This means any integrations will not run. Please link this customer to a WordPress user.' );
362 continue;
363 }
364
365 $this->$method( $integration, $purchase->getWPUser(), $purchase );
366 }
367 }
368
369 /**
370 * Get the current called action.
371 *
372 * @return string
373 */
374 protected function getCurrentAction() {
375 return \current_action();
376 }
377
378 /**
379 * Get the Integration data from the purchase.
380 * This normalizes the integration data and the WP user.
381 *
382 * @param \SureCart\Models\Purchase $purchase Purchase model.
383 *
384 * @return array The integration data.
385 */
386 public function getIntegrationData( $purchase ) {
387 if ( is_string( $purchase ) ) {
388 $purchase = Purchase::find( $purchase );
389 }
390 if ( is_wp_error( $purchase ) ) {
391 return;
392 }
393
394 // Get the integrations from the purchase.
395 return $this->getIntegrationsFromPurchase( $purchase );
396 }
397
398 /**
399 * Add the provider to the list.
400 *
401 * @param array $list The list of providers.
402 * @return array
403 */
404 public function indexProviders( $list = [] ) {
405 $list[] = $this->findProvider();
406 return $list;
407 }
408
409 /**
410 * Find the provider.
411 *
412 * @return array
413 */
414 public function findProvider() {
415 return [
416 'name' => $this->getName(),
417 'label' => $this->getLabel(),
418 'disabled' => ! $this->enabled(),
419 'logo' => esc_url_raw( $this->getLogo() ),
420 'item_label' => $this->getItemLabel(),
421 'item_help' => $this->getItemHelp(),
422 ];
423 }
424
425 /**
426 * Get the integration based on the current purchase.
427 *
428 * @param \SureCart\Models\Purchase $purchase The purchase.
429 *
430 * @return array
431 */
432 public function getIntegrationsFromPurchase( $purchase ) {
433 // we need a product id.
434 $product_id = $purchase->product_id ?? null;
435 if ( ! $product_id ) {
436 return [];
437 }
438
439 $query = Integration::where( 'model_id', $product_id )->andWhere( 'provider', $this->getName() );
440
441 return (array) $query->get();
442 }
443
444 /**
445 * Check if the integration does not match with purchase price or variant.
446 *
447 * @param Integration $integration The integration.
448 * @param Purchase $purchase The purchase.
449 *
450 * @return boolean
451 */
452 public function purchaseIsNotMatchedWithPriceOrVariant( $integration, $purchase ): bool {
453 // Get Purchase price and variant.
454 $price_id = $purchase->price->id ?? $purchase->price ?? null;
455 $variant_id = $purchase->variant->id ?? $purchase->variant ?? null;
456
457 // If integration has price_id or variant_id,
458 // then we need to match with specific price or variant.
459 if (
460 ( ! empty( $integration->price_id ) && $integration->price_id !== $price_id )
461 || ( ! empty( $integration->variant_id ) && $integration->variant_id !== $variant_id )
462 ) {
463 return true;
464 }
465
466 return false;
467 }
468 }
469