Helpers   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 503
Duplicated Lines 0 %

Importance

Changes 7
Bugs 1 Features 0
Metric Value
wmc 40
eloc 152
c 7
b 1
f 0
dl 0
loc 503
rs 9.2

17 Methods

Rating   Name   Duplication   Size   Complexity  
A addCustomId() 0 5 1
A addProductById() 0 7 1
A addPaymentFailureThreshold() 0 5 1
A addSetupFee() 0 14 1
A addBillingPlanById() 0 7 1
A addTaxes() 0 8 1
A addBillingPlan() 0 22 2
A addShippingAddress() 0 17 1
A addPlanTrialPricing() 0 5 1
A addPlanBillingCycle() 0 24 4
A addMonthlyPlan() 0 12 3
A addWeeklyPlan() 0 12 3
A addAnnualPlan() 0 12 3
A addDailyPlan() 0 12 3
A addCustomPlan() 0 18 4
A addProduct() 0 21 3
B setupSubscription() 0 50 7

How to fix   Complexity   

Complex Class

Complex classes like Helpers often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Helpers, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Srmklive\PayPal\Traits\PayPalAPI\Subscriptions;
4
5
use Carbon\Carbon;
6
use Illuminate\Support\Str;
7
use Throwable;
8
9
trait Helpers
10
{
11
    /**
12
     * @var array
13
     */
14
    protected $trial_pricing = [];
15
16
    /**
17
     * @var int
18
     */
19
    protected $payment_failure_threshold = 3;
20
21
    /**
22
     * @var array
23
     */
24
    protected $product;
25
26
    /**
27
     * @var array
28
     */
29
    protected $billing_plan;
30
31
    /**
32
     * @var array
33
     */
34
    protected $shipping_address;
35
36
    /**
37
     * @var array
38
     */
39
    protected $payment_preferences;
40
41
    /**
42
     * @var bool
43
     */
44
    protected $has_setup_fee = false;
45
46
    /**
47
     * @var array
48
     */
49
    protected $taxes;
50
51
    /**
52
     * @var string
53
     */
54
    protected $custom_id;
55
56
    /**
57
     * Setup a subscription.
58
     *
59
     * @param string $customer_name
60
     * @param string $customer_email
61
     * @param string $start_date
62
     *
63
     * @throws Throwable
64
     *
65
     * @return array|\Psr\Http\Message\StreamInterface|string
66
     */
67
    public function setupSubscription(string $customer_name, string $customer_email, string $start_date = '')
68
    {
69
        $body = [
70
            'plan_id'    => $this->billing_plan['id'],
71
            'quantity'   => 1,
72
            'subscriber' => [
73
                'name'          => [
74
                    'given_name' => $customer_name,
75
                ],
76
                'email_address' => $customer_email,
77
            ],
78
        ];
79
80
        if (!empty($start_date)) {
81
            $body['start_time'] = Carbon::parse($start_date)->toIso8601String();
82
        }
83
84
        if ($this->has_setup_fee) {
85
            $body['plan'] = [
86
                'payment_preferences' => $this->payment_preferences,
87
            ];
88
        }
89
90
        if (isset($this->shipping_address)) {
91
            $body['subscriber']['shipping_address'] = $this->shipping_address;
92
        }
93
94
        if (isset($this->experience_context)) {
95
            $body['application_context'] = $this->experience_context;
96
        }
97
98
        if (isset($this->taxes)) {
99
            $body['taxes'] = $this->taxes;
100
        }
101
102
        if (isset($this->custom_id)) {
103
            $body['custom_id'] = $this->custom_id;
104
        }
105
106
        $subscription = $this->createSubscription($body);
0 ignored issues
show
Bug introduced by
It seems like createSubscription() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

106
        /** @scrutinizer ignore-call */ 
107
        $subscription = $this->createSubscription($body);
Loading history...
107
        $subscription['billing_plan_id'] = $this->billing_plan['id'];
108
        $subscription['product_id'] = $this->product['id'];
109
110
        unset($this->product);
111
        unset($this->billing_plan);
112
        unset($this->trial_pricing);
113
        unset($this->return_url);
114
        unset($this->cancel_url);
115
116
        return $subscription;
117
    }
118
119
    /**
120
     * Add a subscription trial pricing tier.
121
     *
122
     * @param string    $interval_type
123
     * @param int       $interval_count
124
     * @param float|int $price
125
     * @param int       $total_cycles
126
     *
127
     * @return \Srmklive\PayPal\Services\PayPal
128
     */
129
    public function addPlanTrialPricing(string $interval_type, int $interval_count, float $price = 0, int $total_cycles = 1): \Srmklive\PayPal\Services\PayPal
130
    {
131
        $this->trial_pricing = $this->addPlanBillingCycle($interval_type, $interval_count, $price, $total_cycles, true);
132
133
        return $this;
134
    }
135
136
    /**
137
     * Create a recurring daily billing plan.
138
     *
139
     * @param string    $name
140
     * @param string    $description
141
     * @param float|int $price
142
     * @param int       $total_cycles
143
     *
144
     * @throws Throwable
145
     *
146
     * @return \Srmklive\PayPal\Services\PayPal
147
     */
148
    public function addDailyPlan(string $name, string $description, float $price, int $total_cycles = 0): \Srmklive\PayPal\Services\PayPal
149
    {
150
        if (isset($this->billing_plan)) {
151
            return $this;
152
        }
153
154
        $plan_pricing = $this->addPlanBillingCycle('DAY', 1, $price, $total_cycles);
155
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
156
157
        $this->addBillingPlan($name, $description, $billing_cycles);
158
159
        return $this;
160
    }
161
162
    /**
163
     * Create a recurring weekly billing plan.
164
     *
165
     * @param string    $name
166
     * @param string    $description
167
     * @param float|int $price
168
     * @param int       $total_cycles
169
     *
170
     * @throws Throwable
171
     *
172
     * @return \Srmklive\PayPal\Services\PayPal
173
     */
174
    public function addWeeklyPlan(string $name, string $description, float $price, int $total_cycles = 0): \Srmklive\PayPal\Services\PayPal
175
    {
176
        if (isset($this->billing_plan)) {
177
            return $this;
178
        }
179
180
        $plan_pricing = $this->addPlanBillingCycle('WEEK', 1, $price, $total_cycles);
181
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
182
183
        $this->addBillingPlan($name, $description, $billing_cycles);
184
185
        return $this;
186
    }
187
188
    /**
189
     * Create a recurring monthly billing plan.
190
     *
191
     * @param string    $name
192
     * @param string    $description
193
     * @param float|int $price
194
     * @param int       $total_cycles
195
     *
196
     * @throws Throwable
197
     *
198
     * @return \Srmklive\PayPal\Services\PayPal
199
     */
200
    public function addMonthlyPlan(string $name, string $description, float $price, int $total_cycles = 0): \Srmklive\PayPal\Services\PayPal
201
    {
202
        if (isset($this->billing_plan)) {
203
            return $this;
204
        }
205
206
        $plan_pricing = $this->addPlanBillingCycle('MONTH', 1, $price, $total_cycles);
207
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
208
209
        $this->addBillingPlan($name, $description, $billing_cycles);
210
211
        return $this;
212
    }
213
214
    /**
215
     * Create a recurring annual billing plan.
216
     *
217
     * @param string    $name
218
     * @param string    $description
219
     * @param float|int $price
220
     * @param int       $total_cycles
221
     *
222
     * @throws Throwable
223
     *
224
     * @return \Srmklive\PayPal\Services\PayPal
225
     */
226
    public function addAnnualPlan(string $name, string $description, float $price, int $total_cycles = 0): \Srmklive\PayPal\Services\PayPal
227
    {
228
        if (isset($this->billing_plan)) {
229
            return $this;
230
        }
231
232
        $plan_pricing = $this->addPlanBillingCycle('YEAR', 1, $price, $total_cycles);
233
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
234
235
        $this->addBillingPlan($name, $description, $billing_cycles);
236
237
        return $this;
238
    }
239
240
    /**
241
     * Create a recurring billing plan with custom intervals.
242
     *
243
     * @param string    $name
244
     * @param string    $description
245
     * @param float|int $price
246
     * @param string    $interval_unit
247
     * @param int       $interval_count
248
     * @param int       $total_cycles
249
     *
250
     * @throws Throwable
251
     *
252
     * @return \Srmklive\PayPal\Services\PayPal
253
     */
254
    public function addCustomPlan(string $name, string $description, float $price, string $interval_unit, int $interval_count, int $total_cycles = 0): \Srmklive\PayPal\Services\PayPal
255
    {
256
        $billing_intervals = ['DAY', 'WEEK', 'MONTH', 'YEAR'];
257
258
        if (isset($this->billing_plan)) {
259
            return $this;
260
        }
261
262
        if (!in_array($interval_unit, $billing_intervals)) {
263
            throw new \RuntimeException('Billing intervals should either be '.implode(', ', $billing_intervals));
264
        }
265
266
        $plan_pricing = $this->addPlanBillingCycle($interval_unit, $interval_count, $price, $total_cycles);
267
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
268
269
        $this->addBillingPlan($name, $description, $billing_cycles);
270
271
        return $this;
272
    }
273
274
    /**
275
     * Add Plan's Billing cycle.
276
     *
277
     * @param string $interval_unit
278
     * @param int    $interval_count
279
     * @param float  $price
280
     * @param int    $total_cycles
281
     * @param bool   $trial
282
     *
283
     * @return array
284
     */
285
    protected function addPlanBillingCycle(string $interval_unit, int $interval_count, float $price, int $total_cycles, bool $trial = false): array
286
    {
287
        $pricing_scheme = [
288
            'fixed_price' => [
289
                'value'         => bcdiv($price, 1, 2),
290
                'currency_code' => $this->getCurrency(),
0 ignored issues
show
Bug introduced by
It seems like getCurrency() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

290
                'currency_code' => $this->/** @scrutinizer ignore-call */ getCurrency(),
Loading history...
291
            ],
292
        ];
293
294
        if (empty($this->trial_pricing)) {
295
            $plan_sequence = 1;
296
        } else {
297
            $plan_sequence = 2;
298
        }
299
300
        return [
301
            'frequency'      => [
302
                'interval_unit'  => $interval_unit,
303
                'interval_count' => $interval_count,
304
            ],
305
            'tenure_type'    => ($trial === true) ? 'TRIAL' : 'REGULAR',
306
            'sequence'       => ($trial === true) ? 1 : $plan_sequence,
307
            'total_cycles'   => $total_cycles,
308
            'pricing_scheme' => $pricing_scheme,
309
        ];
310
    }
311
312
    /**
313
     * Create a product for a subscription's billing plan.
314
     *
315
     * @param string $name
316
     * @param string $description
317
     * @param string $type
318
     * @param string $category
319
     *
320
     * @throws Throwable
321
     *
322
     * @return \Srmklive\PayPal\Services\PayPal
323
     */
324
    public function addProduct(string $name, string $description, string $type, string $category): \Srmklive\PayPal\Services\PayPal
325
    {
326
        if (isset($this->product)) {
327
            return $this;
328
        }
329
330
        $request_id = Str::random();
331
332
        $product = $this->createProduct([
0 ignored issues
show
Bug introduced by
It seems like createProduct() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

332
        /** @scrutinizer ignore-call */ 
333
        $product = $this->createProduct([
Loading history...
333
            'name'        => $name,
334
            'description' => $description,
335
            'type'        => $type,
336
            'category'    => $category,
337
        ], $request_id);
338
339
        if ($error = data_get($product, 'error', false)) {
340
            throw new \RuntimeException(data_get($error, 'details.0.description', 'Failed to add product'));
341
        }
342
        $this->product = $product;
343
344
        return $this;
345
    }
346
347
    /**
348
     * Add subscription's billing plan's product by ID.
349
     *
350
     * @param string $product_id
351
     *
352
     * @return \Srmklive\PayPal\Services\PayPal
353
     */
354
    public function addProductById(string $product_id): \Srmklive\PayPal\Services\PayPal
355
    {
356
        $this->product = [
357
            'id' => $product_id,
358
        ];
359
360
        return $this;
361
    }
362
363
    /**
364
     * Add subscription's billing plan by ID.
365
     *
366
     * @param string $plan_id
367
     *
368
     * @return \Srmklive\PayPal\Services\PayPal
369
     */
370
    public function addBillingPlanById(string $plan_id): \Srmklive\PayPal\Services\PayPal
371
    {
372
        $this->billing_plan = [
373
            'id' => $plan_id,
374
        ];
375
376
        return $this;
377
    }
378
379
    /**
380
     * Create a product for a subscription's billing plan.
381
     *
382
     * @param string $name
383
     * @param string $description
384
     * @param array  $billing_cycles
385
     *
386
     * @throws Throwable
387
     *
388
     * @return void
389
     */
390
    protected function addBillingPlan(string $name, string $description, array $billing_cycles): void
391
    {
392
        $request_id = Str::random();
393
394
        $plan_params = [
395
            'product_id'          => $this->product['id'],
396
            'name'                => $name,
397
            'description'         => $description,
398
            'status'              => 'ACTIVE',
399
            'billing_cycles'      => $billing_cycles,
400
            'payment_preferences' => [
401
                'auto_bill_outstanding'     => true,
402
                'setup_fee_failure_action'  => 'CONTINUE',
403
                'payment_failure_threshold' => $this->payment_failure_threshold,
404
            ],
405
        ];
406
407
        $billingPlan = $this->createPlan($plan_params, $request_id);
0 ignored issues
show
Bug introduced by
It seems like createPlan() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

407
        /** @scrutinizer ignore-call */ 
408
        $billingPlan = $this->createPlan($plan_params, $request_id);
Loading history...
408
        if ($error = data_get($billingPlan, 'error', false)) {
409
            throw new \RuntimeException(data_get($error, 'details.0.description', 'Failed to add billing plan'));
410
        }
411
        $this->billing_plan = $billingPlan;
412
    }
413
414
    /**
415
     * Set custom failure threshold when adding a subscription.
416
     *
417
     * @param int $threshold
418
     *
419
     * @return \Srmklive\PayPal\Services\PayPal
420
     */
421
    public function addPaymentFailureThreshold(int $threshold): \Srmklive\PayPal\Services\PayPal
422
    {
423
        $this->payment_failure_threshold = $threshold;
424
425
        return $this;
426
    }
427
428
    /**
429
     * Add setup fee when adding a subscription.
430
     *
431
     * @param float $price
432
     *
433
     * @return \Srmklive\PayPal\Services\PayPal
434
     */
435
    public function addSetupFee(float $price): \Srmklive\PayPal\Services\PayPal
436
    {
437
        $this->has_setup_fee = true;
438
        $this->payment_preferences = [
439
            'auto_bill_outstanding'     => true,
440
            'setup_fee'                 => [
441
                'value'         => bcdiv($price, 1, 2),
442
                'currency_code' => $this->getCurrency(),
443
            ],
444
            'setup_fee_failure_action'  => 'CONTINUE',
445
            'payment_failure_threshold' => $this->payment_failure_threshold,
446
        ];
447
448
        return $this;
449
    }
450
451
    /**
452
     * Add shipping address.
453
     *
454
     * @param string $full_name
455
     * @param string $address_line_1
456
     * @param string $address_line_2
457
     * @param string $admin_area_2
458
     * @param string $admin_area_1
459
     * @param string $postal_code
460
     * @param string $country_code
461
     *
462
     * @return \Srmklive\PayPal\Services\PayPal
463
     */
464
    public function addShippingAddress(string $full_name, string $address_line_1, string $address_line_2, string $admin_area_2, string $admin_area_1, string $postal_code, string $country_code): \Srmklive\PayPal\Services\PayPal
465
    {
466
        $this->shipping_address = [
467
            'name'    => [
468
                'full_name' => $full_name,
469
            ],
470
            'address' => [
471
                'address_line_1' => $address_line_1,
472
                'address_line_2' => $address_line_2,
473
                'admin_area_2'   => $admin_area_2,
474
                'admin_area_1'   => $admin_area_1,
475
                'postal_code'    => $postal_code,
476
                'country_code'   => $country_code,
477
            ],
478
        ];
479
480
        return $this;
481
    }
482
483
    /**
484
     * Add taxes when creating a subscription.
485
     *
486
     * @param float $percentage
487
     *
488
     * @return \Srmklive\PayPal\Services\PayPal
489
     */
490
    public function addTaxes(float $percentage)
491
    {
492
        $this->taxes = [
493
            'percentage' => $percentage,
494
            'inclusive'  => false,
495
        ];
496
497
        return $this;
498
    }
499
500
    /**
501
     * Add custom id.
502
     *
503
     * @param string $custom_id
504
     *
505
     * @return \Srmklive\PayPal\Services\PayPal
506
     */
507
    public function addCustomId(string $custom_id)
508
    {
509
        $this->custom_id = $custom_id;
510
511
        return $this;
512
    }
513
}
514