Parspal::translateStatus()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 11
rs 10
1
<?php
2
3
namespace Shetabit\Multipay\Drivers\Parspal;
4
5
use GuzzleHttp\Client;
6
use Shetabit\Multipay\Abstracts\Driver;
7
use Shetabit\Multipay\Contracts\ReceiptInterface;
8
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
9
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
10
use Shetabit\Multipay\Invoice;
11
use Shetabit\Multipay\Receipt;
12
use Shetabit\Multipay\RedirectionForm;
13
use Shetabit\Multipay\Request;
14
use chillerlan\SimpleCache\CacheOptions;
15
use chillerlan\SimpleCache\FileCache;
16
17
class Parspal extends Driver
18
{
19
    /**
20
     * HTTP Client.
21
     *
22
     * @var object
23
     */
24
    protected $client;
25
26
    /**
27
     * Invoice
28
     *
29
     * @var Invoice
30
     */
31
    protected $invoice;
32
33
    /**
34
     * Driver settings
35
     *
36
     * @var object
37
     */
38
    protected $settings;
39
40
    /**
41
     * Cache
42
     *
43
     * @var FileCache
44
     */
45
    protected $cache;
46
47
    protected const PAYMENT_PURCHASE_STATUS_OK = 'ACCEPTED';
48
49
    protected const PAYMENT_VERIFY_STATUS_OK = 'SUCCESSFUL';
50
51
    /**
52
     * Constructor.
53
     * Construct the class with the relevant settings.
54
     *
55
     * @param Invoice $invoice
56
     * @param $settings
57
     */
58
    public function __construct(Invoice $invoice, $settings)
59
    {
60
        $this->invoice($invoice);
61
        $this->settings = (object) $settings;
62
        $this->client = new Client();
63
        $this->cache = new FileCache(
64
            new CacheOptions([
65
                'filestorage' => $this->settings->cachePath,
66
            ])
67
        );
68
    }
69
70
    /**
71
     * Retrieve data from details using its name.
72
     *
73
     * @return string
74
     */
75
    private function extractDetails($name)
76
    {
77
        return empty($this->invoice->getDetails()[$name]) ? null : $this->invoice->getDetails()[$name];
78
    }
79
80
    /**
81
     * Purchase Invoice.
82
     *
83
     * @return string
84
     *
85
     * @throws PurchaseFailedException
86
     */
87
    public function purchase()
88
    {
89
        $data = [
90
            'amount' => $this->getInvoiceAmount(),
91
            'return_url' => $this->settings->callbackUrl,
92
            'order_id' => $this->invoice->getUuid(),
93
            'description' => $this->extractDetails('description'),
94
            'payer' => [
95
                'name' => $this->extractDetails('name'),
96
                'mobile' => $this->extractDetails('mobile'),
97
                'email' => $this->extractDetails('email'),
98
            ],
99
        ];
100
101
        $response = $this->client->request(
102
            'POST',
103
            $this->getPurchaseUrl(),
104
            [
105
                'headers' => [
106
                    'ApiKey' => $this->settings->merchantId,
107
                    'Content-Type' => 'application/json',
108
                ],
109
                'json' => $data,
110
                'http_errors' => false,
111
            ]
112
        );
113
114
        $body = json_decode($response->getBody()->getContents(), true);
115
116
        if ($body['status'] !== self::PAYMENT_PURCHASE_STATUS_OK) {
117
            throw new PurchaseFailedException($body['message'], $body['error_code'] ?? 0);
118
        }
119
120
        $this->invoice->transactionId($body['payment_id']);
121
122
        $this->cache->set('payment_link_' . $body['payment_id'], $body['link'], $this->settings->cacheExpireTTL);
123
124
        // return the transaction's id
125
        return $this->invoice->getTransactionId();
126
    }
127
128
    /**
129
     * Pay the Invoice
130
     *
131
     * @return RedirectionForm
132
     */
133
    public function pay(): RedirectionForm
134
    {
135
        $payUrl = $this->cache->get('payment_link_' . $this->invoice->getTransactionId());
136
137
        return $this->redirectWithForm($payUrl, [], 'GET');
138
    }
139
140
    /**
141
     * Verify payment
142
     *
143
     * @return ReceiptInterface
144
     *
145
     * @throws InvalidPaymentException
146
     */
147
    public function verify(): ReceiptInterface
148
    {
149
        $paymentStatusCode = Request::input('status');
150
        $paymentReceiptNumber = Request::input('receipt_number');
151
152
        if ($paymentStatusCode != 100) {
153
            throw new InvalidPaymentException($this->translateStatus($paymentStatusCode), $paymentStatusCode);
154
        }
155
156
        $data = [
157
            'amount' => $this->getInvoiceAmount(),
158
            'receipt_number' => $paymentReceiptNumber,
159
        ];
160
161
        $response = $this->client->request(
162
            'POST',
163
            $this->getVerificationUrl(),
164
            [
165
                'headers' => [
166
                    'ApiKey' => $this->settings->merchantId,
167
                    'Content-Type' => 'application/json',
168
                ],
169
                'json' => $data,
170
                "http_errors" => false,
171
            ]
172
        );
173
174
        $body = json_decode($response->getBody()->getContents(), true);
175
176
        if ($body['status'] !== self::PAYMENT_VERIFY_STATUS_OK) {
177
            throw new InvalidPaymentException($body['message'], $body['error_code'] ?? 0);
178
        }
179
180
        $receipt =  $this->createReceipt($paymentReceiptNumber);
181
182
        $receipt->detail([
183
            'id' => $body['id'],
184
            'paid_amount' => $body['paid_amount'],
185
            'message' => $body['message'],
186
        ]);
187
188
        return $receipt;
189
    }
190
191
    /**
192
     * Generate the payment's receipt
193
     *
194
     * @param $referenceId
195
     *
196
     * @return Receipt
197
     */
198
    public function createReceipt($referenceId)
199
    {
200
        return new Receipt('parspal', $referenceId);
201
    }
202
203
    /**
204
     * Retrieve invoice amount
205
     *
206
     * @return int|float
207
     */
208
    protected function getInvoiceAmount()
209
    {
210
        return $this->invoice->getAmount() * (strtolower($this->settings->currency) === 't' ? 10 : 1); // convert to rial
211
    }
212
213
    /**
214
     * Retrieve purchase url
215
     *
216
     * @return string
217
     */
218
    protected function getPurchaseUrl(): string
219
    {
220
        return $this->isSandboxMode()
221
            ? $this->settings->sandboxApiPurchaseUrl
222
            : $this->settings->apiPurchaseUrl;
223
    }
224
225
    /**
226
     * Retrieve verification url
227
     *
228
     * @return string
229
     */
230
    protected function getVerificationUrl(): string
231
    {
232
        return $this->isSandboxMode()
233
            ? $this->settings->sandboxApiVerificationUrl
234
            : $this->settings->apiVerificationUrl;
235
    }
236
237
    /**
238
     * Retrieve payment in sandbox mode?
239
     *
240
     * @return bool
241
     */
242
    protected function isSandboxMode() : bool
243
    {
244
        return $this->settings->sandbox;
245
    }
246
247
    /**
248
     * Convert status to a readable message.
249
     *
250
     * @param $status
251
     *
252
     * @return mixed|string
253
     */
254
    private function translateStatus($status)
255
    {
256
        $translations = [
257
            '99' => 'انصراف کاربر از پرداخت',
258
            '88' => 'پرداخت ناموفق',
259
            '77' => 'لغو پرداخت توسط کاربر',
260
        ];
261
262
        $unknownError = 'خطای ناشناخته رخ داده است. در صورت کسر مبلغ از حساب حداکثر پس از 72 ساعت به حسابتان برمیگردد';
263
264
        return array_key_exists($status, $translations) ? $translations[$status] : $unknownError;
265
    }
266
}
267