Passed
Push — master ( 099c1f...a97164 )
by mahdi
14:36 queued 12:20
created

src/PaymentManager.php (1 issue)

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