Passed
Push — master ( a6aefd...a6f236 )
by mahdi
02:46
created

Payment::singletoneEventRegistrar()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
1
<?php
2
3
namespace Shetabit\Multipay;
4
5
use Shetabit\Multipay\Contracts\DriverInterface;
6
use Shetabit\Multipay\Contracts\ReceiptInterface;
7
use Shetabit\Multipay\Exceptions\DriverNotFoundException;
8
use Shetabit\Multipay\Exceptions\InvoiceNotFoundException;
9
use Shetabit\Multipay\Traits\HasPaymentEvents;
10
use Shetabit\Multipay\Traits\InteractsWithRedirectionForm;
11
12
class Payment
13
{
14
    use InteractsWithRedirectionForm;
15
    use HasPaymentEvents;
16
17
    /**
18
     * Payment Configuration.
19
     *
20
     * @var array
21
     */
22
    protected $config;
23
24
    /**
25
     * Payment Driver Settings.
26
     *
27
     * @var array
28
     */
29
    protected $settings;
30
31
    /**
32
     * callbackUrl
33
     *
34
     * @var string
35
     */
36
    protected $callbackUrl;
37
38
    /**
39
     * Payment Driver Name.
40
     *
41
     * @var string
42
     */
43
    protected $driver;
44
45
    /**
46
     * Payment Driver Instance.
47
     *
48
     * @var object
49
     */
50
    protected $driverInstance;
51
52
    /**
53
     * @var Invoice
54
     */
55
    protected $invoice;
56
57
    /**
58
     * PaymentManager constructor.
59
     *
60
     * @param array $config
61
     *
62
     * @throws \Exception
63
     */
64
    public function __construct(array $config = [])
65
    {
66
        $this->config = empty($config) ? $this->loadDefaultConfig() : $config;
67
        $this->invoice(new Invoice());
68
        $this->via($this->config['default']);
69
    }
70
71
    /**
72
     * Retrieve Default config's path.
73
     *
74
     * @return string
75
     */
76
    public static function getDefaultConfigPath() : string
77
    {
78
        return dirname(__DIR__).'/config/payment.php';
79
    }
80
81
    /**
82
     * Set custom configs
83
     * we can use this method when we want to use dynamic configs
84
     *
85
     * @param $key
86
     * @param $value|null
87
     *
88
     * @return $this
89
     */
90
    public function config($key, $value = null)
91
    {
92
        $configs = [];
93
94
        $key = is_array($key) ? $key : [$key => $value];
95
96
        foreach ($key as $k => $v) {
97
            $configs[$k] = $v;
98
        }
99
100
        $this->settings = array_merge($this->settings, $configs);
101
102
        return $this;
103
    }
104
105
    /**
106
     * Set callbackUrl.
107
     *
108
     * @param $url|null
109
     * @return $this
110
     */
111
    public function callbackUrl($url = null)
112
    {
113
        $this->callbackUrl = $url;
114
115
        return $this;
116
    }
117
118
    /**
119
     * Reset the callbackUrl to its original that exists in configs.
120
     *
121
     * @return $this
122
     */
123
    public function resetCallbackUrl()
124
    {
125
        $this->callbackUrl();
126
127
        return $this;
128
    }
129
130
    /**
131
     * Set payment amount.
132
     *
133
     * @param $amount
134
     * @return $this
135
     * @throws \Exception
136
     */
137
    public function amount($amount)
138
    {
139
        $this->invoice->amount($amount);
140
141
        return $this;
142
    }
143
144
    /**
145
     * Set a piece of data to the details.
146
     *
147
     * @param $key
148
     *
149
     * @param $value|null
150
     *
151
     * @return $this
152
     */
153
    public function detail($key, $value = null)
154
    {
155
        $this->invoice->detail($key, $value);
156
157
        return $this;
158
    }
159
160
    /**
161
     * Set transaction's id
162
     *
163
     * @param $id
164
     *
165
     * @return $this
166
     */
167
    public function transactionId($id)
168
    {
169
        $this->invoice->transactionId($id);
170
171
        return $this;
172
    }
173
174
    /**
175
     * Change the driver on the fly.
176
     *
177
     * @param $driver
178
     *
179
     * @return $this
180
     *
181
     * @throws \Exception
182
     */
183
    public function via($driver)
184
    {
185
        $this->driver = $driver;
186
        $this->validateDriver();
187
        $this->invoice->via($driver);
188
        $this->settings = $this->config['drivers'][$driver];
189
190
        return $this;
191
    }
192
193
    /**
194
     * Purchase the invoice
195
     *
196
     * @param Invoice $invoice|null
197
     * @param $finalizeCallback|null
198
     *
199
     * @return $this
200
     *
201
     * @throws \Exception
202
     */
203
    public function purchase(Invoice $invoice = null, $finalizeCallback = null)
204
    {
205
        if ($invoice) { // create new invoice
206
            $this->invoice($invoice);
207
        }
208
209
        $this->driverInstance = $this->getFreshDriverInstance();
210
211
        //purchase the invoice
212
        $transactionId = $this->driverInstance->purchase();
213
        if ($finalizeCallback) {
214
            call_user_func_array($finalizeCallback, [$this->driverInstance, $transactionId]);
215
        }
216
217
        // dispatch event
218
        $this->dispatchEvent(
219
            'purchase',
220
            $this->driverInstance,
221
            $this->driverInstance->getInvoice()
222
        );
223
224
        return $this;
225
    }
226
227
    /**
228
     * Pay the purchased invoice.
229
     *
230
     * @param $initializeCallback|null
231
     *
232
     * @return mixed
233
     *
234
     * @throws \Exception
235
     */
236
    public function pay($initializeCallback = null)
237
    {
238
        $this->driverInstance = $this->getDriverInstance();
239
240
        if ($initializeCallback) {
241
            call_user_func($initializeCallback, $this->driverInstance);
242
        }
243
244
        $this->validateInvoice();
245
246
        // dispatch event
247
        $this->dispatchEvent(
248
            'pay',
249
            $this->driverInstance,
250
            $this->driverInstance->getInvoice()
251
        );
252
253
        return $this->driverInstance->pay();
254
    }
255
256
    /**
257
     * Verifies the payment
258
     *
259
     * @param $finalizeCallback|null
260
     *
261
     * @return ReceiptInterface
262
     *
263
     * @throws InvoiceNotFoundException
264
     */
265
    public function verify($finalizeCallback = null) : ReceiptInterface
266
    {
267
        $this->driverInstance = $this->getDriverInstance();
268
        $this->validateInvoice();
269
        $receipt = $this->driverInstance->verify();
270
271
        if (!empty($finalizeCallback)) {
272
            call_user_func($finalizeCallback, $receipt, $this->driverInstance);
273
        }
274
275
        // dispatch event
276
        $this->dispatchEvent(
277
            'verify',
278
            $receipt,
279
            $this->driverInstance,
280
            $this->driverInstance->getInvoice()
281
        );
282
283
        return $receipt;
284
    }
285
286
    /**
287
     * Retrieve default config.
288
     *
289
     * @return array
290
     */
291
    protected function loadDefaultConfig() : array
292
    {
293
        return require(static::getDefaultConfigPath());
294
    }
295
296
    /**
297
     * Set invoice instance.
298
     *
299
     * @param Invoice $invoice
300
     *
301
     * @return self
302
     */
303
    protected function invoice(Invoice $invoice)
304
    {
305
        $this->invoice = $invoice;
306
307
        return $this;
308
    }
309
310
    /**
311
     * Retrieve current driver instance or generate new one.
312
     *
313
     * @return mixed
314
     * @throws \Exception
315
     */
316
    protected function getDriverInstance()
317
    {
318
        if (!empty($this->driverInstance)) {
319
            return $this->driverInstance;
320
        }
321
322
        return $this->getFreshDriverInstance();
323
    }
324
325
    /**
326
     * Get new driver instance
327
     *
328
     * @return mixed
329
     * @throws \Exception
330
     */
331
    protected function getFreshDriverInstance()
332
    {
333
        $this->validateDriver();
334
        $class = $this->config['map'][$this->driver];
335
336
        if (!empty($this->callbackUrl)) { // use custom callbackUrl if exists
337
            $this->settings['callbackUrl'] = $this->callbackUrl;
338
        }
339
340
        return new $class($this->invoice, $this->settings);
341
    }
342
343
    /**
344
     * Validate Invoice.
345
     *
346
     * @throws InvoiceNotFoundException
347
     */
348
    protected function validateInvoice()
349
    {
350
        if (empty($this->invoice)) {
351
            throw new InvoiceNotFoundException('Invoice not selected or does not exist.');
352
        }
353
    }
354
355
    /**
356
     * Validate driver.
357
     *
358
     * @throws \Exception
359
     */
360
    protected function validateDriver()
361
    {
362
        if (empty($this->driver)) {
363
            throw new DriverNotFoundException('Driver not selected or default driver does not exist.');
364
        }
365
366
        if (empty($this->config['drivers'][$this->driver]) || empty($this->config['map'][$this->driver])) {
367
            throw new DriverNotFoundException('Driver not found in config file. Try updating the package.');
368
        }
369
370
        if (!class_exists($this->config['map'][$this->driver])) {
371
            throw new DriverNotFoundException('Driver source not found. Please update the package.');
372
        }
373
374
        $reflect = new \ReflectionClass($this->config['map'][$this->driver]);
375
376
        if (!$reflect->implementsInterface(DriverInterface::class)) {
377
            throw new \Exception("Driver must be an instance of Contracts\DriverInterface.");
378
        }
379
    }
380
}
381