Passed
Pull Request — v3.0 (#541)
by
unknown
09:39
created

Helpers::setupSubscription()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 44
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
cc 6
eloc 26
c 4
b 0
f 1
nc 16
nop 3
dl 0
loc 44
rs 8.8817
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
    protected $payment_preferences;
32
    protected $has_setup_fee = false;
33
34
    /**
35
     * @var string
36
     */
37
    protected $return_url;
38
39
    /**
40
     * @var string
41
     */
42
    protected $cancel_url;
43
44
    /**
45
     * Setup a subscription.
46
     *
47
     * @param string $customer_name
48
     * @param string $customer_email
49
     * @param string $start_date
50
     *
51
     * @throws Throwable
52
     *
53
     * @return array|\Psr\Http\Message\StreamInterface|string
54
     */
55
    public function setupSubscription(string $customer_name, string $customer_email, string $start_date = '')
56
    {
57
        $start_date = isset($start_date) ? Carbon::parse($start_date)->toIso8601String() : Carbon::now()->toIso8601String();
58
59
60
        $body = [
61
            'plan_id'    => $this->billing_plan['id'],
62
            'start_time' => $start_date,
63
            'quantity'   => 1,
64
            'subscriber' => [
65
                'name'          => [
66
                    'given_name' => $customer_name,
67
                ],
68
                'email_address' => $customer_email,
69
            ],
70
        ];
71
72
        if($this->has_setup_fee) {
73
            $body['plan'] = [
74
                'payment_preferences' => $this->payment_preferences
75
            ];
76
        }
77
78
        if(isset($this->shipping_address)) {
79
            $body['subscriber']['shipping_address'] = $this->shipping_address;
80
        }
81
82
        if ($this->return_url && $this->cancel_url) {
83
            $body['application_context'] = [
84
                'return_url' => $this->return_url,
85
                'cancel_url' => $this->cancel_url,
86
            ];
87
        }
88
89
        $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

89
        /** @scrutinizer ignore-call */ 
90
        $subscription = $this->createSubscription($body);
Loading history...
90
91
        unset($this->product);
92
        unset($this->billing_plan);
93
        unset($this->payment_preferences);
94
        unset($this->trial_pricing);
95
        unset($this->return_url);
96
        unset($this->cancel_url);
97
98
        return $subscription;
99
    }
100
101
    /**
102
     * Add a subscription trial pricing tier.
103
     *
104
     * @param string    $interval_type
105
     * @param int       $interval_count
106
     * @param float|int $price
107
     *
108
     * @return \Srmklive\PayPal\Services\PayPal
109
     */
110
    public function addPlanTrialPricing(string $interval_type, int $interval_count, float $price = 0): \Srmklive\PayPal\Services\PayPal
111
    {
112
        $this->trial_pricing = $this->addPlanBillingCycle($interval_type, $interval_count, $price, true);
113
114
        return $this;
115
    }
116
117
    /**
118
     * Create a recurring daily billing plan.
119
     *
120
     * @param string    $name
121
     * @param string    $description
122
     * @param float|int $price
123
     *
124
     * @throws Throwable
125
     *
126
     * @return \Srmklive\PayPal\Services\PayPal
127
     */
128
    public function addDailyPlan(string $name, string $description, float $price): \Srmklive\PayPal\Services\PayPal
129
    {
130
        if (isset($this->billing_plan)) {
131
            return $this;
132
        }
133
134
        $plan_pricing = $this->addPlanBillingCycle('DAY', 1, $price);
135
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
136
137
        $this->addBillingPlan($name, $description, $billing_cycles);
138
139
        return $this;
140
    }
141
142
    /**
143
     * Create a recurring weekly billing plan.
144
     *
145
     * @param string    $name
146
     * @param string    $description
147
     * @param float|int $price
148
     *
149
     * @throws Throwable
150
     *
151
     * @return \Srmklive\PayPal\Services\PayPal
152
     */
153
    public function addWeeklyPlan(string $name, string $description, float $price): \Srmklive\PayPal\Services\PayPal
154
    {
155
        if (isset($this->billing_plan)) {
156
            return $this;
157
        }
158
159
        $plan_pricing = $this->addPlanBillingCycle('WEEK', 1, $price);
160
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
161
162
        $this->addBillingPlan($name, $description, $billing_cycles);
163
164
        return $this;
165
    }
166
167
    /**
168
     * Create a recurring monthly billing plan.
169
     *
170
     * @param string    $name
171
     * @param string    $description
172
     * @param float|int $price
173
     *
174
     * @throws Throwable
175
     *
176
     * @return \Srmklive\PayPal\Services\PayPal
177
     */
178
    public function addMonthlyPlan(string $name, string $description, float $price): \Srmklive\PayPal\Services\PayPal
179
    {
180
        if (isset($this->billing_plan)) {
181
            return $this;
182
        }
183
184
        $plan_pricing = $this->addPlanBillingCycle('MONTH', 1, $price);
185
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
186
187
        $this->addBillingPlan($name, $description, $billing_cycles);
188
189
        return $this;
190
    }
191
192
    /**
193
     * Create a recurring annual billing plan.
194
     *
195
     * @param string    $name
196
     * @param string    $description
197
     * @param float|int $price
198
     *
199
     * @throws Throwable
200
     *
201
     * @return \Srmklive\PayPal\Services\PayPal
202
     */
203
    public function addAnnualPlan(string $name, string $description, float $price): \Srmklive\PayPal\Services\PayPal
204
    {
205
        if (isset($this->billing_plan)) {
206
            return $this;
207
        }
208
209
        $plan_pricing = $this->addPlanBillingCycle('YEAR', 1, $price);
210
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
211
212
        $this->addBillingPlan($name, $description, $billing_cycles);
213
214
        return $this;
215
    }
216
217
    /**
218
     * Create a recurring billing plan with custom intervals.
219
     *
220
     * @param string    $name
221
     * @param string    $description
222
     * @param float|int $price
223
     * @param string    $interval_unit
224
     * @param int       $interval_count
225
     *
226
     * @throws Throwable
227
     *
228
     * @return \Srmklive\PayPal\Services\PayPal
229
     */
230
    public function addCustomPlan(string $name, string $description, float $price, string $interval_unit, int $interval_count): \Srmklive\PayPal\Services\PayPal
231
    {
232
        $billing_intervals = ['DAY', 'WEEK', 'MONTH', 'YEAR'];
233
234
        if (isset($this->billing_plan)) {
235
            return $this;
236
        }
237
238
        if (!in_array($interval_unit, $billing_intervals)) {
239
            throw new \RuntimeException('Billing intervals should either be '.implode(', ', $billing_intervals));
240
        }
241
242
        $plan_pricing = $this->addPlanBillingCycle($interval_unit, $interval_count, $price);
243
        $billing_cycles = empty($this->trial_pricing) ? [$plan_pricing] : collect([$this->trial_pricing, $plan_pricing])->filter()->toArray();
244
245
        $this->addBillingPlan($name, $description, $billing_cycles);
246
247
        return $this;
248
    }
249
250
    /**
251
     * Add Plan's Billing cycle.
252
     *
253
     * @param string $interval_unit
254
     * @param int    $interval_count
255
     * @param float  $price
256
     * @param bool   $trial
257
     *
258
     * @return array
259
     */
260
    protected function addPlanBillingCycle(string $interval_unit, int $interval_count, float $price, bool $trial = false): array
261
    {
262
        $pricing_scheme = [
263
            'fixed_price' => [
264
                'value'         => $price,
265
                '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

265
                'currency_code' => $this->/** @scrutinizer ignore-call */ getCurrency(),
Loading history...
266
            ],
267
        ];
268
269
        if (empty($this->trial_pricing)) {
270
            $plan_sequence = 1;
271
        } else {
272
            $plan_sequence = 2;
273
        }
274
275
        return [
276
            'frequency' => [
277
                'interval_unit'  => $interval_unit,
278
                'interval_count' => $interval_count,
279
            ],
280
            'tenure_type'    => ($trial === true) ? 'TRIAL' : 'REGULAR',
281
            'sequence'       => ($trial === true) ? 1 : $plan_sequence,
282
            'total_cycles'   => ($trial === true) ? 1 : 0,
283
            'pricing_scheme' => $pricing_scheme,
284
        ];
285
    }
286
287
    /**
288
     * Create a product for a subscription's billing plan.
289
     *
290
     * @param string $name
291
     * @param string $description
292
     * @param string $type
293
     * @param string $category
294
     *
295
     * @throws Throwable
296
     *
297
     * @return \Srmklive\PayPal\Services\PayPal
298
     */
299
    public function addProduct(string $name, string $description, string $type, string $category): \Srmklive\PayPal\Services\PayPal
300
    {
301
        if (isset($this->product)) {
302
            return $this;
303
        }
304
305
        $request_id = Str::random();
306
307
        $this->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

307
        /** @scrutinizer ignore-call */ 
308
        $this->product = $this->createProduct([
Loading history...
308
            'name'          => $name,
309
            'description'   => $description,
310
            'type'          => $type,
311
            'category'      => $category,
312
        ], $request_id);
313
314
        return $this;
315
    }
316
317
    /**
318
     * Add subscription's billing plan's product by ID.
319
     *
320
     * @param string $product_id
321
     *
322
     * @return \Srmklive\PayPal\Services\PayPal
323
     */
324
    public function addProductById(string $product_id): \Srmklive\PayPal\Services\PayPal
325
    {
326
        $this->product = [
327
            'id' => $product_id,
328
        ];
329
330
        return $this;
331
    }
332
333
    /**
334
     * Add subscription's billing plan by ID.
335
     *
336
     * @param string $plan_id
337
     *
338
     * @return \Srmklive\PayPal\Services\PayPal
339
     */
340
    public function addBillingPlanById(string $plan_id): \Srmklive\PayPal\Services\PayPal
341
    {
342
        $this->billing_plan = [
343
            'id' => $plan_id,
344
        ];
345
346
        return $this;
347
    }
348
349
    /**
350
     * Create a product for a subscription's billing plan.
351
     *
352
     * @param string $name
353
     * @param string $description
354
     * @param array  $billing_cycles
355
     *
356
     * @throws Throwable
357
     *
358
     * @return void
359
     */
360
    protected function addBillingPlan(string $name, string $description, array $billing_cycles): void
361
    {
362
        $request_id = Str::random();
363
364
365
        if (!isset($this->payment_preferences)) {
366
            $this->payment_preferences = [
367
                'auto_bill_outstanding'     => true,
368
                'setup_fee_failure_action'  => 'CONTINUE',
369
                'payment_failure_threshold' => $this->payment_failure_threshold,
370
            ];
371
        }
372
373
        $plan_params = [
374
            'product_id'          => $this->product['id'],
375
            'name'                => $name,
376
            'description'         => $description,
377
            'status'              => 'ACTIVE',
378
            'billing_cycles'      => $billing_cycles,
379
            'payment_preferences' => $this->payment_preferences,
380
        ];
381
382
        $this->billing_plan = $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

382
        /** @scrutinizer ignore-call */ 
383
        $this->billing_plan = $this->createPlan($plan_params, $request_id);
Loading history...
383
    }
384
385
    /**
386
     * Set return & cancel urls.
387
     *
388
     * @param string $return_url
389
     * @param string $cancel_url
390
     *
391
     * @return \Srmklive\PayPal\Services\PayPal
392
     */
393
    public function setReturnAndCancelUrl(string $return_url, string $cancel_url): \Srmklive\PayPal\Services\PayPal
394
    {
395
        $this->return_url = $return_url;
396
        $this->cancel_url = $cancel_url;
397
398
        return $this;
399
    }
400
401
    /**
402
     * @param float $price
403
     * @return \Srmklive\PayPal\Services\PayPal
404
     */
405
    public function addSetupFee(float $price): \Srmklive\PayPal\Services\PayPal
406
    {
407
        $this->has_setup_fee = true;
408
        $this->payment_preferences = [
409
            'auto_bill_outstanding'     => true,
410
            'setup_fee' => [
411
                'value' => $price,
412
                'currency_code' => $this->getCurrency(),
413
            ],
414
            'setup_fee_failure_action'  => 'CONTINUE',
415
            'payment_failure_threshold' => $this->payment_failure_threshold,
416
        ];
417
418
        return $this;
419
    }
420
421
    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
422
    {
423
        $this->shipping_address = [
0 ignored issues
show
Bug Best Practice introduced by
The property shipping_address does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
424
            "name" => [
425
                "full_name" => $full_name
426
            ],
427
            "address" =>  [
428
                "address_line_1" => $address_line_1,
429
                "address_line_2" => $address_line_2,
430
                "admin_area_2" => $admin_area_2,
431
                "admin_area_1" => $admin_area_1,
432
                "postal_code" => $postal_code,
433
                "country_code" => $country_code,
434
            ]
435
        ];
436
437
        return $this;
438
    }
439
}
440