Code

< 40 %
40-60 %
> 60 %
1
<?php declare(strict_types=1);
2
3
namespace Karser\PayumSaferpay;
4
5
use Http\Message\MessageFactory;
6
use Karser\PayumSaferpay\Exception\SaferpayHttpException;
7
use Payum\Core\Bridge\Spl\ArrayObject;
8
use Payum\Core\Exception\InvalidArgumentException;
9
use Payum\Core\HttpClientInterface;
10
11
class Api
12
{
13
    const SPEC_VERSION = '1.10';
14
    const PAYMENT_PAGE_INIT_PATH = '/Payment/v1/PaymentPage/Initialize';
15
    const PAYMENT_PAGE_ASSERT_PATH = '/Payment/v1/PaymentPage/Assert';
16
    const TRANSACTION_INIT_PATH = '/Payment/v1/Transaction/Initialize';
17
    const TRANSACTION_AUTHORIZE_PATH = '/Payment/v1/Transaction/Authorize';
18
    const TRANSACTION_AUTHORIZE_REFERENCED_PATH = '/Payment/v1/Transaction/AuthorizeReferenced';
19
    const TRANSACTION_CAPTURE_PATH = '/Payment/v1/Transaction/Capture';
20
    const TRANSACTION_REFUND_PATH = '/Payment/v1/Transaction/Refund';
21
    const ALIAS_INSERT_PATH = '/Payment/v1/Alias/Insert';
22
    const ALIAS_ASSERT_INSERT_PATH = '/Payment/v1/Alias/AssertInsert';
23
    const ALIAS_DELETE_PATH = '/Payment/v1/Alias/Delete';
24
25
    /**
26
     * @var HttpClientInterface
27
     */
28
    protected $client;
29
30
    /**
31
     * @var MessageFactory
32
     */
33
    protected $messageFactory;
34
35
    /**
36
     * @var array
37
     */
38
    protected $options = array(
39
        'username' => null,
40
        'password' => null,
41
        'customerId' => null,
42
        'terminalId' => null,
43
        'sandbox' => null,
44
        'interface' => null,
45
        'optionalParameters' => null,
46
    );
47
48 38
    public function __construct(array $options, HttpClientInterface $client, MessageFactory $messageFactory)
49
    {
50 38
        $options = ArrayObject::ensureArrayObject($options);
51 38
        $options->defaults($this->options);
52 38
        $options->validateNotEmpty([
53 38
            'username', 'password', 'customerId', 'terminalId',
54 38
        ]);
55 37
        if (!is_bool($options['sandbox'])) {
56
            throw new InvalidArgumentException('The boolean sandbox option must be set.');
57
        }
58
59 37
        $this->options = $options;
60 37
        $this->client = $client;
61 37
        $this->messageFactory = $messageFactory;
62
    }
63
64 32
    protected function doRequest(string $path, array $fields): array
65
    {
66 32
        $headers = [
67 32
            'Authorization' => 'Basic ' . base64_encode($this->options['username'] . ':' . $this->options['password']),
68 32
            'Accept' => 'application/json',
69 32
            'Content-Type' => 'application/json',
70 32
        ];
71 32
        $fields = array_merge([
72 32
            'RequestHeader' => [
73 32
                'SpecVersion' => self::SPEC_VERSION,
74 32
                'CustomerId' => $this->options['customerId'],
75 32
                'RequestId' => uniqid(),
76 32
                'RetryIndicator' => 0,
77 32
            ],
78 32
        ], $fields);
79
80 32
        $request = $this->messageFactory->createRequest(
81 32
            'POST',
82 32
            $this->getApiEndpoint() . $path,
83 32
            $headers,
84 32
            json_encode($fields)
85 32
        );
86
87 32
        $response = $this->client->send($request);
88
89 32
        if (!($response->getStatusCode() >= 200 && $response->getStatusCode() < 300)) {
90 2
            throw SaferpayHttpException::factory($request, $response);
91
        }
92
93 31
        return $this->parseResponse(
94 31
            $response->getBody()->getContents()
95 31
        );
96
    }
97
98 31
    private function parseResponse($content)
99
    {
100 31
        return json_decode($content, true);
101
    }
102
103 15
    public function initTransaction(array $model): array
104
    {
105 15
        $payload = [
106 15
            'TerminalId' => $this->options['terminalId'],
107 15
            'Payment' => $model['Payment'],
108 15
            'Payer' => $model['Payer'] ?? [
109 15
                'LanguageCode' => 'en',
110
            ],
111 15
            'ReturnUrls' => $model['ReturnUrls'],
112 15
        ];
113
114 15
        $payload = $this->addOptionalInterfaceParams(Constants::INTERFACE_TRANSACTION, $payload);
115
116 15
        $paymentMeans = $model['PaymentMeans'] ?? null;
117
118 15
        if (null !== $paymentMeans) {
119 2
            $payload['PaymentMeans'] = $paymentMeans;
120
        }
121
122 15
        return $this->doRequest(self::TRANSACTION_INIT_PATH, $payload);
123
    }
124
125 6
    public function initPaymentPage(array $model): array
126
    {
127 6
        $payload = [
128 6
            'TerminalId' => $this->options['terminalId'],
129 6
            'Payment' => $model['Payment'],
130 6
            'Payer' => $model['Payer'] ?? [
131 6
                'LanguageCode' => 'en',
132
            ],
133 6
            'ReturnUrls' => $model['ReturnUrls'],
134 6
        ];
135
136 6
        $payload = $this->addOptionalInterfaceParams(Constants::INTERFACE_PAYMENT_PAGE, $payload);
137
138 6
        $notification = $model['Notification'] ?? null;
139
140 6
        if (null !== $notification) {
141 6
            $payload['Notification'] = $notification;
142
        }
143
144 6
        return $this->doRequest(self::PAYMENT_PAGE_INIT_PATH, $payload);
145
    }
146
147 11
    public function authorizeTransaction(string $token, ?string $condition = null, ?array $alias = null): array
148
    {
149 11
        $payload = [
150 11
            'Token' => $token,
151 11
        ];
152 11
        if (null !== $condition) {
153 2
            $payload['Condition'] = $condition;
154
        }
155 11
        if (null !== $alias) {
156 2
            $payload['RegisterAlias'] = array_merge(['IdGenerator' => Constants::ALIAS_ID_GENERATOR_RANDOM], $alias);
157
        }
158 11
        return $this->doRequest(self::TRANSACTION_AUTHORIZE_PATH, $payload);
159
    }
160
161 3
    public function authorizeReferencedTransaction(array $payment, string $transactionReferenceId): array
162
    {
163 3
        $payload = [
164 3
            'TerminalId' => $this->options['terminalId'],
165 3
            'Payment' => $payment,
166 3
            'TransactionReference' => ['TransactionId' => $transactionReferenceId],
167 3
        ];
168 3
        return $this->doRequest(self::TRANSACTION_AUTHORIZE_REFERENCED_PATH, $payload);
169
    }
170
171 12
    public function captureTransaction(string $transactionId): array
172
    {
173 12
        $payload = [
174 12
            'TransactionReference' => [
175 12
                'TransactionId' => $transactionId,
176 12
            ],
177 12
        ];
178 12
        return $this->doRequest(self::TRANSACTION_CAPTURE_PATH, $payload);
179
    }
180
181 2
    public function refundTransaction(array $refund, string $captureId): array
182
    {
183 2
        $payload = [
184 2
            'Refund' => $refund,
185 2
            'CaptureReference' => [
186 2
                'CaptureId' => $captureId,
187 2
            ],
188 2
        ];
189 2
        return $this->doRequest(self::TRANSACTION_REFUND_PATH, $payload);
190
    }
191
192 4
    public function assertPaymentPage(string $token): array
193
    {
194 4
        $payload = [
195 4
            'Token' => $token,
196 4
        ];
197 4
        return $this->doRequest(self::PAYMENT_PAGE_ASSERT_PATH, $payload);
198
    }
199
200 4
    public function insertAlias(array $returnUrls, array $alias, string $type): array
201
    {
202 4
        $payload = [
203 4
            'RegisterAlias' => $alias,
204 4
            'Type' => $type ?? Constants::ALIAS_TYPE_CARD,
205 4
            'ReturnUrls' => $returnUrls,
206 4
            'LanguageCode' => 'en',
207 4
        ];
208 4
        return $this->doRequest(self::ALIAS_INSERT_PATH, $payload);
209
    }
210
211 4
    public function assertInsertAlias(string $token): array
212
    {
213 4
        $payload = [
214 4
            'Token' => $token,
215 4
        ];
216 4
        return $this->doRequest(self::ALIAS_ASSERT_INSERT_PATH, $payload);
217
    }
218
219 2
    public function deleteAlias(string $id): array
220
    {
221 2
        $payload = [
222 2
            'AliasId' => $id,
223 2
        ];
224 2
        return $this->doRequest(self::ALIAS_DELETE_PATH, $payload);
225
    }
226
227 34
    public function getApiEndpoint(): string
228
    {
229 34
        return $this->options['sandbox'] ? 'https://test.saferpay.com/api' : 'https://www.saferpay.com/api';
230
    }
231
232 15
    public function doInstantCapturing(): bool
233
    {
234 15
        return $this->options['instantCapturing'] === true;
235
    }
236
237 13
    public function getCaptureStrategy(): string
238
    {
239 13
        if (isset($this->options['interface']) && is_string($this->options['interface'])) {
240
            return $this->options['interface'];
241
        }
242
243 13
        return Constants::INTERFACE_TRANSACTION;
244
    }
245
246 21
    protected function addOptionalInterfaceParams(string $interface, array $payload): array
247
    {
248 21
        $allowedOptions = [
249 21
            Constants::INTERFACE_PAYMENT_PAGE => [
250 21
                'config_set',
251 21
                'payment_methods',
252 21
                'wallets',
253 21
                'notification_merchant_email',
254 21
                'notification_payer_email',
255 21
                'styling_css_url',
256 21
                'styling_content_security_enabled',
257 21
                'styling_theme',
258 21
                'payer_note',
259 21
            ],
260 21
            Constants::INTERFACE_TRANSACTION => [
261 21
                'config_set',
262 21
                'payment_methods',
263 21
                'styling_css_url', // deprecated
264 21
                'styling_content_security_enabled',
265 21
                'styling_theme',
266 21
                'payer_note',
267 21
            ]
268 21
        ];
269
270 21
        $optionalInterfaceOptions = $this->options['optionalParameters'] ?? [];
271
272 21
        foreach ($optionalInterfaceOptions as $optionName => $optionValue) {
273
274
            if (empty($optionValue)) {
275
                continue;
276
            }
277
278
            if (!in_array($optionName, $allowedOptions[$interface])) {
279
                continue;
280
            }
281
282
            switch($optionName) {
283
                case 'config_set':
284
                    $payload['ConfigSet'] = (string) $optionValue;
285
                    break;
286
                case 'payment_methods':
287
                    $payload['PaymentMethods'] = $this->trimExplode($optionValue);
288
                    break;
289
                case 'wallets':
290
                    $payload['Wallets'] = $this->trimExplode($optionValue);
291
                    break;
292
                case 'notification_merchant_email':
293
                    $payload['Notification'] = $payload['Notification'] ?? [];
294
                    $payload['Notification']['MerchantEmails'] = $this->trimExplode($optionValue);
295
                    break;
296
                case 'notification_payer_email':
297
                    $payload['Notification'] = $payload['Notification'] ?? [];
298
                    $payload['Notification']['PayerEmail'] = (string) $optionValue;
299
                    break;
300
                case 'styling_css_url':
301
                    $payload['Styling'] = $payload['Styling'] ?? [];
302
                    $payload['Styling']['CssUrl'] = $optionValue;
303
                    break;
304
                case 'styling_content_security_enabled':
305
                    $payload['Styling'] = $payload['Styling'] ?? [];
306
                    $payload['Styling']['ContentSecurityEnabled'] = $optionValue;
307
                    break;
308
                case 'styling_theme':
309
                    $payload['Styling'] = $payload['Styling'] ?? [];
310
                    $payload['Styling']['Theme'] = $optionValue;
311
                    break;
312
                 case 'payer_note':
313
                    $payload['PayerNote'] = $optionValue;
314
                    break;
315
            }
316
        }
317
318 21
        return $payload;
319
    }
320
321
    protected function trimExplode(string $data, string $delimiter = ','): array {
322
        return array_map('trim', explode($delimiter, $data));
323
    }
324
}
325