Skip to content

Commit

Permalink
Merge pull request #12 from njoguamos/Add-Transaction-Status-Endpoint
Browse files Browse the repository at this point in the history
Add transaction status endpoint
  • Loading branch information
njoguamos authored Mar 18, 2024
2 parents 7e27411 + 6640973 commit ef60e07
Show file tree
Hide file tree
Showing 15 changed files with 285 additions and 23 deletions.
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ There are two ways to get the registered IPNs.

```php
use NjoguAmos\Pesapal\Pesapal;
use NjoguAmos\Pesapal\Pesapal;

$response = Pesapal::getIpns();
```
Expand Down Expand Up @@ -215,7 +216,14 @@ To submit an order request, you can use the `createOrder` method in the `Pesapal

```php

use NjoguAmos\Pesapal\Enums\ISOCurrencyCode;
use NjoguAmos\Pesapal\Enums\ISOCountryCode;
use NjoguAmos\Pesapal\Enums\RedirectMode;
use NjoguAmos\Pesapal\Pesapal;
use NjoguAmos\Pesapal\DTOs\PesapalOrderData;
use NjoguAmos\Pesapal\DTOs\PesapalAddressData;

$ipnId = PesapalIpn::latest()->first()->ipn_id;

$orderData = new PesapalOrderData(
Expand Down Expand Up @@ -264,6 +272,41 @@ If the response was successful, your response should be as follows.

You can now re-direct the user to the `redirect_url` to complete the payment.


### 5. Get Transaction Status Endpoint

You can check the status of a transaction using `OrderTrackingId` issued when creating an order.

```php
use NjoguAmos\Pesapal\Pesapal;

$transaction = Pesapal::getTransactionStatus(
orderTrackingId: 'b945e4af-80a5-4ec1-8706-e03f8332fb04',
);

// $transaction is an instance of TransactionStatusResponse DTO
```

You should be able to get response details for the `$transaction` DTO

| Property | Description | Sample response |
|--------------------------------------------|---------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|
| `$transaction->payment_method` | payment method used | `Visa`, `MPESA`, `""` |
| `$transaction->amount` | order amount | `1200` |
| `$transaction->created_date` | payment date in `UTC` | `2022-04-30T07:41:09.763` |
| `$transaction->confirmation_code` | payment provider code | `SCI5FB84RD` |
| `$transaction->payment_status_description` | transaction status | `INVALID`, `FAILED`, `COMPLETED` or `REVERSED`. |
| `$transaction->description` | payment status description | `"Unable to Authorize Transaction.Kindly contact your bank for assistance"`. |
| `$transaction->message` | whether transaction was processed successfully or not. | `"Request processed successfully"`. |
| `$transaction->payment_account` | Masked card number or phone number used during payment | `0700*****8`, `"` |
| `$transaction->call_back_url` | Payment redirect url | `https://test.com/?OrderTrackingId=7e6b62d9-883e-440f-a63e-e1105bbfadc3&OrderMerchantReference=1515111111` |
| `$transaction->status_code` | Payment status description | `0` - INVALID, `1` - COMPLETED, `2` - FAILED or `3` - REVERSED |
| `$transaction->merchant_reference` | Unique ID provided during SubmitOrderRequest | `1515111111`, `7e6b62d9-883e-440f-a63e-e1105bbfadc` |
| `$transaction->currency` | Transaction currency | `KES`, `USD` |
| `$transaction->status` | HTTP status code | `200`, `500` |
| `$transaction->error` | An error object containing `error_type`, `code`, `message` and `call_back_url`. | `{ "error_type": 'api_error, "code": 'payment_details_not_found, "message": 'Pending Payment, "call_back_url": null }` |


## Testing

> **Info** Where possible, the tests uses real [sandbox credentials](https://developer.pesapal.com/api3-demo-keys.txt), and as such the request is not mocked. This ensures the stability of the package. Where it is impossible to use real credentials, the request is mocked. Therefore you must be connected to the internet to run the some of the tests.
Expand Down
16 changes: 16 additions & 0 deletions src/DTOs/TransactionError.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace NjoguAmos\Pesapal\DTOs;

use Spatie\LaravelData\Dto;

class TransactionError extends Dto
{
public function __construct(
public ?string $error_type = null,
public ?string $code = null,
public ?string $message = null,
public ?string $call_back_url = null,
) {
}
}
27 changes: 27 additions & 0 deletions src/DTOs/TransactionStatusResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace NjoguAmos\Pesapal\DTOs;

use Spatie\LaravelData\Dto;

class TransactionStatusResponse extends Dto
{
public function __construct(
public float $amount,
public string $created_date,
public string $payment_status_description,
public string $call_back_url,
public string $message,
public string $merchant_reference,
public mixed $currency,
public TransactionError $error,
public int $status_code,
public int $status,
public string $payment_method = "",
public ?string $description = null,
public string $confirmation_code = "",
public string $payment_account = "",
public string $payment_status_code = "",
) {
}
}
2 changes: 1 addition & 1 deletion src/Enums/IpnStatus.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

enum IpnStatus: int
{
case Inactive = 0;
case Inactive = 0;

case Active = 1;
}
9 changes: 9 additions & 0 deletions src/Enums/TransactionStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace NjoguAmos\Pesapal\Enums;

enum TransactionStatus: int
{
case COMPLETE = 200;
case INCOMPLETE = 500;
}
11 changes: 11 additions & 0 deletions src/Enums/TransactionStatusCode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace NjoguAmos\Pesapal\Enums;

enum TransactionStatusCode: int
{
case INVALID = 0;
case COMPLETED = 1;
case FAILED = 2;
case REVERSED = 3;
}
15 changes: 15 additions & 0 deletions src/Pesapal.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use Carbon\Carbon;
use JsonException;
use NjoguAmos\Pesapal\Requests\GetPesapalIpns;
use NjoguAmos\Pesapal\Requests\GetPesapalTransactionStatus;
use Saloon\Exceptions\Request\FatalRequestException;
use Saloon\Exceptions\Request\RequestException;

Expand Down Expand Up @@ -118,4 +119,18 @@ public static function createOrder(PesapalOrderData $orderData, PesapalAddressDa

return null;
}

/**
* @throws FatalRequestException
* @throws RequestException
*/
public static function getTransactionStatus(string $orderTrackingId)
{
$connector = new PesapalConnector();
$request = new GetPesapalTransactionStatus(orderTrackingId: $orderTrackingId);

$response = $connector->send($request);

return $response->dtoOrFail();
}
}
61 changes: 61 additions & 0 deletions src/Requests/GetPesapalTransactionStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php

namespace NjoguAmos\Pesapal\Requests;

use JsonException;
use NjoguAmos\Pesapal\DTOs\TransactionError;
use NjoguAmos\Pesapal\DTOs\TransactionStatusResponse;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;

class GetPesapalTransactionStatus extends Request
{
protected Method $method = Method::GET;

public function __construct(public string $orderTrackingId)
{
}

public function resolveEndpoint(): string
{
return '/api/Transactions/GetTransactionStatus';
}

protected function defaultQuery(): array
{
return [
'orderTrackingId' => $this->orderTrackingId
];
}

/**
* @throws JsonException
*/
public function createDtoFromResponse(Response $response): TransactionStatusResponse
{
$data = $response->json();

return new TransactionStatusResponse(
amount: $data['amount'],
created_date: $data['created_date'],
payment_status_description: $data['payment_status_description'],
call_back_url: $data['call_back_url'],
message: $data['message'],
merchant_reference: $data['merchant_reference'],
currency: $data['currency'],
error: $data['error'] ? new TransactionError(
error_type: $data['error']['error_type'],
code: $data['error']['code'],
message: $data['error']['message'],
call_back_url: $data['error']['call_back_url'],
) : null,
status_code: $data['status_code'],
status: $data['status'],
payment_method: $data['payment_method'],
description: $data['description'],
confirmation_code: $data['confirmation_code'],
payment_account: $data['payment_account']
);
}
}
10 changes: 5 additions & 5 deletions tests/Fixtures/CreatePesapalIpn.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"statusCode": 200,
"headers": {
"Date": "Mon, 18 Mar 2024 13:22:15 GMT",
"Date": "Mon, 18 Mar 2024 15:25:46 GMT",
"Content-Type": "application\/json; charset=utf-8",
"Content-Length": "318",
"Content-Length": "328",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Pragma": "no-cache",
"Expires": "-1",
"X-AspNet-Version": "4.0.30319",
"X-Powered-By": "ASP.NET",
"CF-Cache-Status": "DYNAMIC",
"Set-Cookie": "__cf_bm=tSKV1RSqq4T3iONXHtLVyom4qWEJMKKJdL7Xhg0yJk4-1710768135-1.0.1.1-m6pyYyfmIKKbndye.uyza5.Qqk9EdNQo_UByAqrdmfu.VXai0RZiS3cJPhB69R.SVmyfFEruPPfM.izK4aZL4w; path=\/; expires=Mon, 18-Mar-24 13:52:15 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Set-Cookie": "__cf_bm=TMcbslOMXuGR4UHpnH84yzxHFueA8vNd_uAMSz_LwUg-1710775546-1.0.1.1-buzIBVOpfawq917VWQug09OCAjsF8odkr6UGRJARTFZ8FW0lxYDncd1DRhb4.R9_GENgAII0jbi8xJAQcFSg2A; path=\/; expires=Mon, 18-Mar-24 15:55:46 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Server": "cloudflare",
"CF-RAY": "866587ce6a8bb19c-NBO"
"CF-RAY": "86663cbb5b73b195-NBO"
},
"data": "{\"id\":4263,\"url\":\"https:\/\/bednar.com\/beatae-soluta-voluptatibus-rerum-numquam.html\",\"created_date\":\"2024-03-18T13:22:15.5582123Z\",\"ipn_id\":\"473c5154-2aec-4c5c-81ca-dd759e7754fe\",\"notification_type\":0,\"ipn_notification_type_description\":\"GET\",\"ipn_status\":1,\"ipn_status_decription\":\"Active\",\"error\":null,\"status\":\"200\"}"
"data": "{\"id\":4278,\"url\":\"http:\/\/www.padberg.com\/dolores-quia-porro-debitis-architecto-quia-repellat\",\"created_date\":\"2024-03-18T15:25:46.2652354Z\",\"ipn_id\":\"1da86d1c-1e8c-4183-bdfd-dd759463e4ce\",\"notification_type\":0,\"ipn_notification_type_description\":\"GET\",\"ipn_status\":1,\"ipn_status_decription\":\"Active\",\"error\":null,\"status\":\"200\"}"
}
8 changes: 4 additions & 4 deletions tests/Fixtures/CreatePesapalOrder.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"statusCode": 200,
"headers": {
"Date": "Mon, 18 Mar 2024 13:22:16 GMT",
"Date": "Mon, 18 Mar 2024 15:25:46 GMT",
"Content-Type": "application\/json; charset=utf-8",
"Content-Length": "279",
"Connection": "keep-alive",
Expand All @@ -11,9 +11,9 @@
"X-AspNet-Version": "4.0.30319",
"X-Powered-By": "ASP.NET",
"CF-Cache-Status": "DYNAMIC",
"Set-Cookie": "__cf_bm=PhzdXPBrBo4fPc5d99xktEU39qQdesB3RK6ESlnsPiI-1710768136-1.0.1.1-bXcC4ciDwxZN6rhbPQFn5nmfhMYYaMOhJFHdnIYZ0htJzIqtrHiyM3dl3.9919AZtQwGt7cU6AcGHB7MAt_HgQ; path=\/; expires=Mon, 18-Mar-24 13:52:16 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Set-Cookie": "__cf_bm=ZicRixwFP8abn3c3gMlD1OGnYzmkYxzHbVxTImGFxmg-1710775546-1.0.1.1-l6vEcr9eQw49NDq02A5dKehBpuN14uJKo1Tq3zJRiZ2f.vcHZL7Zpn1kGDPFN2T5dX4vvacskOWMSRRHMn7jEA; path=\/; expires=Mon, 18-Mar-24 15:55:46 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Server": "cloudflare",
"CF-RAY": "866587d15e13b199-NBO"
"CF-RAY": "86663cbe2833b196-NBO"
},
"data": "{\"order_tracking_id\":\"c8b0e0fb-23f0-44dc-b79b-dd751b446913\",\"merchant_reference\":\"2cb149ff-fdf0-3de8-9952-7ad353f0e613\",\"redirect_url\":\"https:\/\/cybqa.pesapal.com\/pesapaliframe\/PesapalIframe3\/Index?OrderTrackingId=c8b0e0fb-23f0-44dc-b79b-dd751b446913\",\"error\":null,\"status\":\"200\"}"
"data": "{\"order_tracking_id\":\"96a5e1d9-f711-4423-82ce-dd758f972713\",\"merchant_reference\":\"98771cd2-e240-3656-9e10-eb2f3d687ab2\",\"redirect_url\":\"https:\/\/cybqa.pesapal.com\/pesapaliframe\/PesapalIframe3\/Index?OrderTrackingId=96a5e1d9-f711-4423-82ce-dd758f972713\",\"error\":null,\"status\":\"200\"}"
}
10 changes: 5 additions & 5 deletions tests/Fixtures/CreatePesapalToken.json
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
{
"statusCode": 200,
"headers": {
"Date": "Mon, 18 Mar 2024 13:22:14 GMT",
"Date": "Mon, 18 Mar 2024 15:25:45 GMT",
"Content-Type": "application\/json; charset=utf-8",
"Content-Length": "563",
"Content-Length": "564",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Pragma": "no-cache",
"Expires": "-1",
"X-AspNet-Version": "4.0.30319",
"X-Powered-By": "ASP.NET",
"CF-Cache-Status": "DYNAMIC",
"Set-Cookie": "__cf_bm=yqPIIAwpoHWV20dRfvYL3xT7KFrOcZKBCIScGoL1CnA-1710768134-1.0.1.1-Cp9O07TlMP9CTVlcbLuwkbzGv9BKXPR3kmpJZSIWWSI8rKZuEjiqIskGq6i8viKD6kU7lq1E8LVCu7B2EWARXw; path=\/; expires=Mon, 18-Mar-24 13:52:14 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Set-Cookie": "__cf_bm=w90xIwC6czVEyR9EeDPt1wzz6lrfKVvHLuW8.nQlhbg-1710775545-1.0.1.1-L0EgE93GdXR3H2odZyz826x5l2B2KG46C1WmuX0R.xQIU5PTzcBo3BCyD4xxO970AHgYDBr50ulsdG01yw_Ctg; path=\/; expires=Mon, 18-Mar-24 15:55:45 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Server": "cloudflare",
"CF-RAY": "866587c4fabf1b5f-NBO"
"CF-RAY": "86663cb80af31b5f-NBO"
},
"data": "{\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3VzZXJkYXRhIjoiZWQ2MTkwMGYtZGNiMy00NjM2LWIxNGUtY2U1MGQwYzk2M2I1IiwidWlkIjoicWtpbzFCR0dZQVhUdTJKT2ZtN1hTWE5ydW9ac3JxRVciLCJuYmYiOjE3MTA3NjgxMzQsImV4cCI6MTcxMDc3MTczNCwiaWF0IjoxNzEwNzY4MTM0LCJpc3MiOiJodHRwOi8vY3licWEucGVzYXBhbC5jb20vIiwiYXVkIjoiaHR0cDovL2N5YnFhLnBlc2FwYWwuY29tLyJ9.a43PZJpsdO0J_fnUMweaD5Kko8HOWEzFmrMCiAYQvGk\",\"expiryDate\":\"2024-03-18T14:22:14.718041Z\",\"error\":null,\"status\":\"200\",\"message\":\"Request processed successfully\"}"
"data": "{\"token\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3VzZXJkYXRhIjoiZWQ2MTkwMGYtZGNiMy00NjM2LWIxNGUtY2U1MGQwYzk2M2I1IiwidWlkIjoicWtpbzFCR0dZQVhUdTJKT2ZtN1hTWE5ydW9ac3JxRVciLCJuYmYiOjE3MTA3NzU1NDUsImV4cCI6MTcxMDc3OTE0NSwiaWF0IjoxNzEwNzc1NTQ1LCJpc3MiOiJodHRwOi8vY3licWEucGVzYXBhbC5jb20vIiwiYXVkIjoiaHR0cDovL2N5YnFhLnBlc2FwYWwuY29tLyJ9.aoSsLyrovYoUXKeyqKfF3EA-Ra8hCkN3kkgvhnFdYEc\",\"expiryDate\":\"2024-03-18T16:25:45.7793772Z\",\"error\":null,\"status\":\"200\",\"message\":\"Request processed successfully\"}"
}
6 changes: 3 additions & 3 deletions tests/Fixtures/GetPesapalIpns.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"statusCode": 200,
"headers": {
"Date": "Mon, 18 Mar 2024 13:22:17 GMT",
"Date": "Mon, 18 Mar 2024 15:25:47 GMT",
"Content-Type": "application\/json; charset=utf-8",
"Content-Length": "2",
"Connection": "keep-alive",
Expand All @@ -11,9 +11,9 @@
"X-AspNet-Version": "4.0.30319",
"X-Powered-By": "ASP.NET",
"CF-Cache-Status": "DYNAMIC",
"Set-Cookie": "__cf_bm=KFC89asQFD9YyQrD1eDXymDOJ6nL0jZLjfOoM6IZgHQ-1710768137-1.0.1.1-MA1RoXRw5YFVAo0d.p_59wZbr6VVYv9veaknP8Dsk1SRAfRO5yj.buXE28jRhybi_P3fA.SYsrb8bteUEQSg9w; path=\/; expires=Mon, 18-Mar-24 13:52:17 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Set-Cookie": "__cf_bm=.5x62iPRqQ7qOp19pcyUHeU2YImorpnW.G03pNptRGs-1710775547-1.0.1.1-eMB95SzCS7SGOCT8vQmEkUHaHVyeijKwX.WRoqpFuZVX9AxqZaEzINjs_GSkSaxJZrlI6rFU3b7g99lVRC7P.g; path=\/; expires=Mon, 18-Mar-24 15:55:47 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Server": "cloudflare",
"CF-RAY": "866587d8fe980372-NBO"
"CF-RAY": "86663cc58f1db195-NBO"
},
"data": "[]"
}
19 changes: 19 additions & 0 deletions tests/Fixtures/GetPesapalTransactionStatus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"statusCode": 200,
"headers": {
"Date": "Mon, 18 Mar 2024 15:25:47 GMT",
"Content-Type": "application\/json; charset=utf-8",
"Content-Length": "819",
"Connection": "keep-alive",
"Cache-Control": "no-cache",
"Pragma": "no-cache",
"Expires": "-1",
"X-AspNet-Version": "4.0.30319",
"X-Powered-By": "ASP.NET",
"CF-Cache-Status": "DYNAMIC",
"Set-Cookie": "__cf_bm=WXB8x1wV3imG1BOzwTv.B.KIDmE9LEn.nFUxn_mRQCc-1710775547-1.0.1.1-1CasNIeXi5LQbByo9ubtlDZXKxJtnay7_YQ4kvoOsUaVqSLfFak01QLLTbs5U10u82_bP12E0jq4ZiDZ3BlkNw; path=\/; expires=Mon, 18-Mar-24 15:55:47 GMT; domain=.pesapal.com; HttpOnly; Secure; SameSite=None",
"Server": "cloudflare",
"CF-RAY": "86663cc19fa3b195-NBO"
},
"data": "{\"payment_method\":\"\",\"amount\":10.00,\"created_date\":\"2024-03-18T18:25:46.75\",\"confirmation_code\":\"\",\"order_tracking_id\":\"96a5e1d9-f711-4423-82ce-dd758f972713\",\"payment_status_description\":\"INVALID\",\"description\":null,\"message\":\"Request processed successfully\",\"payment_account\":\"\",\"call_back_url\":\"http:\/\/www.zemlak.com\/?OrderTrackingId=96a5e1d9-f711-4423-82ce-dd758f972713&OrderMerchantReference=98771cd2-e240-3656-9e10-eb2f3d687ab2\",\"status_code\":0,\"merchant_reference\":\"98771cd2-e240-3656-9e10-eb2f3d687ab2\",\"payment_status_code\":\"\",\"currency\":\"KES\",\"error\":{\"error_type\":\"api_error\",\"code\":\"payment_details_not_found\",\"message\":\"Pending Payment\",\"call_back_url\":\"http:\/\/www.zemlak.com\/?OrderTrackingId=96a5e1d9-f711-4423-82ce-dd758f972713&OrderMerchantReference=98771cd2-e240-3656-9e10-eb2f3d687ab2\"},\"status\":\"500\"}"
}
Loading

0 comments on commit ef60e07

Please sign in to comment.