Skip to content

Commit a3d6653

Browse files
committed
feat: add supports for vpos
1 parent c198a91 commit a3d6653

12 files changed

Lines changed: 298 additions & 145 deletions

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# CHANGELOG
2+
3+
This changelog references the relevant changes (bug and security fixes) done
4+
5+
### 2.0.0 [unreleased]
6+
- Breaking Change: `pay` supports both `vpos` and `mobile` request
7+
- Added: `vpos` and `mobile` method to `Client`
8+
- Added: `VposResponse`, `Request`, `VposRequest`, `MobileRequest` class
9+
- Fixed: allow `$message` to be an empty string in `CheckResponse`
10+
- Fixed: `token` and `merchant` are now SensitiveParameters
11+
- Added: Support for `check`, `vpos`, `mobile` urls in `Environment`,
12+
- Removed: `getPaymentBaseUrl` method in `Environment`
13+
14+
### 1.0.1
15+
- Fixed: allow `$message` to be an empty string in `PaymentResponse`
16+
17+
### 1.0.0
18+
- Initial release

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ $flexpay = new Flexpay(
3636
```
3737

3838
### Create a Payment (Intention)
39+
3940
```php
40-
use Devscast\Flexpay\PaymentEntry;
41-
use Devscast\Flexpay\Data\Currency;
41+
use Devscast\Flexpay\Data\Currency;use Devscast\Flexpay\Request\MobileRequest;
4242

43-
$entry = new PaymentEntry(
43+
$entry = new MobileRequest(
4444
amount: 10, // 10 USD
4545
currency: Currency::USD,
4646
phone: "243999999999", // mandatory for mobile money

src/Client.php

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@
44

55
namespace Devscast\Flexpay;
66

7-
use Devscast\Flexpay\Data\Method;
87
use Devscast\Flexpay\Exception\NetworkException;
8+
use Devscast\Flexpay\Request\MobileRequest;
9+
use Devscast\Flexpay\Request\Request;
10+
use Devscast\Flexpay\Request\VposRequest;
911
use Devscast\Flexpay\Response\CheckResponse;
1012
use Devscast\Flexpay\Response\FlexpayResponse;
1113
use Devscast\Flexpay\Response\PaymentResponse;
14+
use Devscast\Flexpay\Response\VposResponse;
1215
use Symfony\Component\HttpClient\HttpClient;
1316
use Symfony\Component\HttpClient\Retry\GenericRetryStrategy;
1417
use Symfony\Component\HttpClient\RetryableHttpClient;
@@ -23,20 +26,22 @@
2326
*
2427
* @author bernard-ng <bernard@devscast.tech>
2528
*/
26-
final readonly class Client
29+
final class Client
2730
{
2831
private HttpClientInterface $http;
2932

3033
private Serializer $serializer;
3134

3235
public function __construct(
33-
public Credential $credential,
34-
public Environment $environment = Environment::SANDBOX
36+
public readonly Credential $credential,
37+
public readonly Environment $environment = Environment::SANDBOX,
3538
) {
36-
$this->serializer = new Serializer(normalizers: [
37-
new BackedEnumNormalizer(),
38-
new ObjectNormalizer(),
39-
]);
39+
$this->serializer = new Serializer(
40+
normalizers: [
41+
new ObjectNormalizer(),
42+
new BackedEnumNormalizer(),
43+
]
44+
);
4045

4146
$this->http = new RetryableHttpClient(
4247
client: HttpClient::create(
@@ -53,33 +58,21 @@ public function __construct(
5358
}
5459

5560
/**
56-
* Cette interface permet d’envoyer une requête de redirection de paiement à FlexPay
61+
* Permet d'envoyer une directement intention de paiement sur le mobile money du client
5762
*
58-
* @throws NetworkException quand une erreur réseau survient
59-
* @throws \InvalidArgumentException quand le numéro de téléphone n'est pas fourni pour un paiement mobile
63+
* @throws NetworkException
6064
*/
61-
public function pay(PaymentEntry $entry, Method $method = Method::MOBILE): PaymentResponse
65+
public function mobile(MobileRequest $request): PaymentResponse
6266
{
63-
/**
64-
* Définit ici, pour éviter de préciser manuellement le marchant à chaque fois
65-
*/
66-
$entry->setMerchant($this->credential->merchant);
67-
68-
if ($method === Method::MOBILE && $entry->phone === null) {
69-
throw new \InvalidArgumentException('phone number should be provided for mobile payment');
70-
}
67+
$request->setCredential($this->credential);
7168

7269
try {
7370
/** @var PaymentResponse $response */
7471
$response = $this->getMappedData(
7572
type: PaymentResponse::class,
76-
data: $this->http->request(
77-
method: 'POST',
78-
url: sprintf('%s/paymentService', $this->environment->getBaseUrl()),
79-
options: [
80-
'json' => $this->serializer->normalize($entry),
81-
]
82-
)->toArray()
73+
data: $this->http->request('POST', $this->environment->getMobilePaymentUrl(), [
74+
'json' => $request->getPayload(),
75+
])->toArray()
8376
);
8477

8578
return $response;
@@ -89,14 +82,41 @@ public function pay(PaymentEntry $entry, Method $method = Method::MOBILE): Payme
8982
}
9083

9184
/**
92-
* Cette interface permet d'obtenir une réponse de paiement provenant de FlexPay
85+
* Créer une URL unique de paiement via le gateway de Flexpay
86+
* Cela permet d'utiliser différente méthode de paiement
87+
* y compris une carte bancaire (VISA, MASTERCARD, etc.)
88+
*
89+
* @throws NetworkException
9390
*/
94-
public function handleCallback(array $data): PaymentResponse
91+
public function vpos(VposRequest $request): VposResponse
9592
{
96-
/** @var PaymentResponse $payment */
97-
$payment = $this->getMappedData(PaymentResponse::class, $data);
93+
$request->setCredential($this->credential);
9894

99-
return $payment;
95+
try {
96+
dd(
97+
/** @var VposResponse $response */
98+
$response = $this->getMappedData(
99+
type: VposResponse::class,
100+
data: $this->http->request('POST', $this->environment->getVposAskUrl(), [
101+
'json' => $request->getPayload(),
102+
])->toArray()
103+
)
104+
);
105+
} catch (\Throwable $e) {
106+
$this->createExceptionFromResponse($e);
107+
}
108+
}
109+
110+
/**
111+
* @throws NetworkException
112+
*/
113+
public function pay(Request $request): PaymentResponse|VposResponse
114+
{
115+
return match (true) {
116+
$request instanceof MobileRequest => $this->mobile($request),
117+
$request instanceof VposRequest => $this->vpos($request),
118+
default => throw new \RuntimeException('Unsupported request')
119+
};
100120
}
101121

102122
/**
@@ -112,10 +132,9 @@ public function check(string $orderNumber): CheckResponse
112132
/** @var CheckResponse $response */
113133
$response = $this->getMappedData(
114134
type: CheckResponse::class,
115-
data: $this->http->request(
116-
method: 'GET',
117-
url: sprintf('%s/check/%s', $this->environment->getBaseUrl(), $orderNumber)
118-
)->toArray()
135+
data: $this->http
136+
->request('GET', $this->environment->getCheckStatusUrl($orderNumber))
137+
->toArray()
119138
);
120139

121140
return $response;
@@ -124,6 +143,17 @@ public function check(string $orderNumber): CheckResponse
124143
}
125144
}
126145

146+
/**
147+
* Cette interface permet d'obtenir une réponse de paiement provenant de FlexPay
148+
*/
149+
public function handleCallback(array $data): PaymentResponse
150+
{
151+
/** @var PaymentResponse $payment */
152+
$payment = $this->getMappedData(PaymentResponse::class, $data);
153+
154+
return $payment;
155+
}
156+
127157
/**
128158
* @psalm-param class-string<FlexpayResponse> $type
129159
*/

src/Credential.php

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,14 @@
1414
final readonly class Credential
1515
{
1616
/**
17-
* Le token d’autorisation
17+
* @param string $token Le token d’autorisation
18+
* @param string $merchant Le code Marchand FlexPay, ex: ZANDO
1819
*/
19-
public string $token;
20-
21-
/**
22-
* Le code Marchand FlexPay, ex: ZANDO
23-
*/
24-
public string $merchant;
25-
2620
public function __construct(
27-
#[\SensitiveParameter] string $token,
28-
#[\SensitiveParameter] string $merchant,
21+
#[\SensitiveParameter] public string $token,
22+
#[\SensitiveParameter] public string $merchant,
2923
) {
30-
Assert::notEmpty($token, 'The authorization token cannot be empty');
31-
Assert::notEmpty($merchant, 'Merchant cannot be empty');
32-
33-
$this->token = $token;
34-
$this->merchant = $merchant;
24+
Assert::notEmpty($this->token, 'The authorization token cannot be empty');
25+
Assert::notEmpty($this->merchant, 'Merchant cannot be empty');
3526
}
3627
}

src/Environment.php

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,40 @@ enum Environment: string
1414
case LIVE = 'prod';
1515
case SANDBOX = 'dev';
1616

17-
public function getBaseUrl(): string
17+
public function getVposAskUrl(): string
1818
{
1919
return match ($this) {
20-
self::LIVE => 'https://backend.flexpay.cd/api/rest/v1',
21-
self::SANDBOX => 'https://beta-backend.flexpay.cd/api/rest/v1',
20+
self::LIVE, self::SANDBOX => sprintf('%s/vpos/ask', $this->getBaseUrl()),
21+
};
22+
}
23+
24+
public function getMobilePaymentUrl(): string
25+
{
26+
return match ($this) {
27+
self::LIVE, self::SANDBOX => sprintf('%s/paymentService', $this->getBaseUrl()),
2228
};
2329
}
2430

25-
public function getCardPaymentBaseUrl(): string
31+
public function getVposPaymentUrl(string $orderNumber): string
2632
{
2733
return match ($this) {
28-
self::LIVE => 'https://cardpayment.flexpay.cd/v1.1/pay',
29-
self::SANDBOX => 'https://beta-cardpayment.flexpay.cd/v1.1/pay'
34+
self::LIVE => sprintf('https://cardpayment.flexpay.cd/vpos/pay/%s', $orderNumber),
35+
self::SANDBOX => sprintf('https://beta-cardpayment.flexpay.cd/vpos/pay/%s', $orderNumber),
36+
};
37+
}
38+
39+
public function getCheckStatusUrl(string $orderNumber): string
40+
{
41+
return match ($this) {
42+
self::LIVE, self::SANDBOX => sprintf('%s/check/%s', $this->getBaseUrl(), $orderNumber),
43+
};
44+
}
45+
46+
private function getBaseUrl(): string
47+
{
48+
return match ($this) {
49+
self::LIVE => 'https://backend.flexpay.cd/api/rest/v1',
50+
self::SANDBOX => 'https://beta-backend.flexpay.cd/api/rest/v1',
3051
};
3152
}
3253
}

src/Exception/NetworkException.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public function __construct(
1919
public ?int $status = null
2020
) {
2121
if ($this->status !== null) {
22-
parent::__construct($message . ' (HTTP ' . $status . '/' . $type . ')');
22+
parent::__construct(sprintf('%s (HTTP %d/%s)', $message, $status, $type));
2323
} else {
2424
parent::__construct($message);
2525
}

src/PaymentEntry.php

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
 (0)