Skip to content

Commit baa1d8f

Browse files
committed
Fixed #1.
`ItnValidator` will default testing to false.
1 parent ac21b6e commit baa1d8f

5 files changed

Lines changed: 141 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## [0.2.4] 26-01-2022
4+
### Fixed
5+
* Exception thrown when using `ItnValidator->validate()` without calling `testing()`. The validator will now assume testing is `false`.
6+
37
## [0.2.3] 27-08-2021
48
### Fixed
59
* When posting to PayFast in production, the url should be `www.payfast.co.za` (with the "www" bit).

src/ItnValidator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class ItnValidator
1111
{
1212
protected array $data;
1313
protected PayFastResponse $response;
14-
protected bool $testing;
14+
protected bool $testing = false;
1515
protected ?string $error = null;
1616

1717
public function __construct(array $responseData)

src/Merchant.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
namespace TPG\PayFast;
66

7+
use GuzzleHttp\Client;
8+
use GuzzleHttp\Exception\ClientException;
9+
use Illuminate\Support\Arr;
10+
use TPG\PayFast\Exceptions\PayFastException;
11+
712
class Merchant
813
{
914
protected $attributes = [
@@ -22,6 +27,26 @@ public function __construct(string $merchantId, string $merchantKey, string $pas
2227
$this->passphrase = $passphrase;
2328
}
2429

30+
public function ping(bool $testing = false, Client $client = null): bool
31+
{
32+
33+
try {
34+
$response = (new Request($this))->testing($testing)->make('get', 'ping');
35+
36+
if (Arr::get($response, 'status') === 'failed') {
37+
// todo: There's a bug in the response from PayFast at the moment.
38+
// todo: The `data.message` appears to be boolean, while `data.response` appears to be a string.
39+
// todo: Also, the response from a live ping always returns a 500 error.
40+
throw new PayFastException(Arr::get($response, 'data.message'), $response['code']);
41+
}
42+
43+
return true;
44+
45+
} catch (ClientException $exception) {
46+
throw new PayFastException($exception->getMessage(), $exception->getCode());
47+
}
48+
}
49+
2550
public function merchantId(): string
2651
{
2752
return $this->attributes['merchant_id'];

src/Request.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace TPG\PayFast;
6+
7+
use GuzzleHttp\Client;
8+
use GuzzleHttp\Exception\ClientException;
9+
use http\Encoding\Stream;
10+
use TPG\PayFast\Exceptions\PayFastException;
11+
12+
class Request
13+
{
14+
protected const ENDPOINT = 'https://api.payfast.co.za';
15+
16+
protected Client $client;
17+
protected Merchant $merchant;
18+
protected bool $testing = false;
19+
20+
public function __construct(Merchant $merchant, Client $client = null)
21+
{
22+
$this->merchant = $merchant;
23+
$this->client = $client ?? new Client();
24+
}
25+
26+
public function testing(bool $testing = true): self
27+
{
28+
$this->testing = $testing;
29+
30+
return $this;
31+
}
32+
33+
public function make(string $method, string $uri, array $formParams = []): array
34+
{
35+
try {
36+
$response = $this->client->request($method, $this->endpoint($uri), [
37+
'headers' => $this->headers(),
38+
'form_params' => $formParams,
39+
'query' => [
40+
'testing' => $this->testing ? 'true' : 'false',
41+
]
42+
]);
43+
44+
if ($response->getStatusCode() !== 200) {
45+
throw new PayFastException('Bad response from PayFast API');
46+
}
47+
48+
return $this->extractJson($response->getBody()->getContents());
49+
50+
} catch (ClientException $exception) {
51+
52+
throw new PayFastException($this->getErrorMessage($exception->getCode()), $exception->getCode());
53+
54+
} catch (\JsonException $exception) {
55+
56+
throw new PayFastException('Invalid JSON response');
57+
58+
}
59+
}
60+
61+
protected function extractJson(string $contents): array
62+
{
63+
$json = substr(
64+
$contents,
65+
$startPos = strpos($contents, '{'),
66+
strrpos($contents, '}') - ($startPos - 1)
67+
);
68+
69+
return json_decode($json, true, 512, JSON_THROW_ON_ERROR);
70+
}
71+
72+
protected function headers(array $body = []): array
73+
{
74+
$headers = [
75+
'merchant-id' => $this->merchant->merchantId(),
76+
'version' => 'v1',
77+
'timestamp' => (new \DateTime())->format(DATE_ATOM),
78+
];
79+
80+
$headers['signature'] = (new Signature(array_merge($headers, $body), $this->merchant->passphrase()))->generate(true);
81+
82+
return $headers;
83+
}
84+
85+
protected function endpoint(string $uri = null): string
86+
{
87+
if ($uri[0] !== '/') {
88+
$uri = '/'.$uri;
89+
}
90+
91+
return self::ENDPOINT.$uri;
92+
}
93+
94+
protected function getErrorMessage(int $code): string
95+
{
96+
switch ($code) {
97+
case 400:
98+
return 'Invalid request';
99+
case 401:
100+
return 'Authorization failed';
101+
case 404:
102+
return 'Service not found';
103+
case 429:
104+
return 'Rate limited';
105+
case 500:
106+
return 'PayFast Application/Communication error';
107+
}
108+
}
109+
}

src/Subscription.php

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,30 +110,11 @@ protected function setData(array $data): void
110110

111111
protected function request(string $method, string $uri = null, array $body = []): array
112112
{
113-
$headers = [
114-
'merchant-id' => $this->merchant->merchantId(),
115-
'version' => 'v1',
116-
'timestamp' => (new \DateTime())->format(DATE_ATOM),
117-
];
118-
119-
$headers['signature'] = (new Signature(array_merge($headers, $body), $this->merchant->passphrase()))->generate(true);
120-
121113
try {
122-
$response = $this->client->request($method, self::ENDPOINT . '/' . $this->token . '/' . $uri, [
123-
'headers' => $headers,
124-
'form_params' => $body,
125-
'query' => [
126-
'testing' => $this->testing ? 'true' : 'false',
127-
],
128-
]);
129-
130-
$data = json_decode($response->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
131114

132-
if ($response->getStatusCode() !== 200) {
133-
throw new PayFastException($data['status'], $data['code']);
134-
}
115+
return (new Request($this->merchant, $this->client))->testing($this->testing)
116+
->make($method, $this->token.'/'.$uri, $body);
135117

136-
return $data;
137118
} catch (ClientException $exception) {
138119
throw new PayFastException(
139120
'Unable to communicate with PayFast',

0 commit comments

Comments
 (0)