Skip to content

Commit 9b26f96

Browse files
Merge pull request #61 from jules-paystack/chore/remove-yabacon-dependency
chore: remove yabacon/paystack-php, use custom API client
2 parents 0f5b9c4 + 80403eb commit 9b26f96

30 files changed

Lines changed: 775 additions & 280 deletions

.gitattributes

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Exclude development files from Composer exports (composer install / git archive)
2+
/dev export-ignore
3+
/.github export-ignore
4+
/.gitattributes export-ignore
5+
/.gitignore export-ignore
6+
/CONTRIBUTING.md export-ignore
7+
8+
# Force LF line endings for shell scripts (prevents CRLF issues in Docker)
9+
*.sh text eol=lf

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ composer.phar
1313
\#*
1414
/nbproject/
1515

16+
# Environment
1617
.env
18+
dev/.env

Controller/Payment/AbstractPaystackStandard.php

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,19 @@
2323
namespace Pstk\Paystack\Controller\Payment;
2424

2525
use Magento\Payment\Helper\Data as PaymentHelper;
26+
use Pstk\Paystack\Gateway\PaystackApiClient;
2627

2728

2829
abstract class AbstractPaystackStandard extends \Magento\Framework\App\Action\Action {
2930

3031
protected $resultPageFactory;
31-
32+
3233
/**
3334
*
34-
* @var \Magento\Sales\Api\OrderRepositoryInterface
35+
* @var \Magento\Sales\Api\OrderRepositoryInterface
3536
*/
3637
protected $orderRepository;
37-
38+
3839
/**
3940
*
4041
* @var \Magento\Sales\Api\Data\OrderInterface
@@ -43,33 +44,33 @@ abstract class AbstractPaystackStandard extends \Magento\Framework\App\Action\Ac
4344
protected $checkoutSession;
4445
protected $method;
4546
protected $messageManager;
46-
47+
4748
/**
4849
*
49-
* @var \Pstk\Paystack\Model\Ui\ConfigProvider
50+
* @var \Pstk\Paystack\Model\Ui\ConfigProvider
5051
*/
5152
protected $configProvider;
52-
53+
5354
/**
5455
*
55-
* @var \Yabacon\Paystack
56+
* @var PaystackApiClient
5657
*/
57-
protected $paystack;
58-
58+
protected $paystackClient;
59+
5960
/**
6061
* @var \Magento\Framework\Event\Manager
6162
*/
6263
protected $eventManager;
63-
64+
6465
/**
6566
*
6667
* @var \Psr\Log\LoggerInterface
6768
*/
6869
protected $logger;
69-
70+
7071
/**
7172
*
72-
* @var \Magento\Framework\App\Request\Http
73+
* @var \Magento\Framework\App\Request\Http
7374
*/
7475
protected $request;
7576

@@ -90,7 +91,8 @@ public function __construct(
9091
\Pstk\Paystack\Model\Ui\ConfigProvider $configProvider,
9192
\Magento\Framework\Event\Manager $eventManager,
9293
\Magento\Framework\App\Request\Http $request,
93-
\Psr\Log\LoggerInterface $logger
94+
\Psr\Log\LoggerInterface $logger,
95+
PaystackApiClient $paystackClient
9496
) {
9597
$this->resultPageFactory = $resultPageFactory;
9698
$this->orderRepository = $orderRepository;
@@ -102,21 +104,11 @@ public function __construct(
102104
$this->eventManager = $eventManager;
103105
$this->request = $request;
104106
$this->logger = $logger;
105-
106-
$this->paystack = $this->initPaystackPHP();
107-
108-
107+
$this->paystackClient = $paystackClient;
108+
109109
parent::__construct($context);
110110
}
111111

112-
protected function initPaystackPHP() {
113-
$secretKey = $this->method->getConfigData('live_secret_key');
114-
if ($this->method->getConfigData('test_mode')) {
115-
$secretKey = $this->method->getConfigData('test_secret_key');
116-
}
117-
return new \Yabacon\Paystack($secretKey);
118-
}
119-
120112
protected function redirectToFinal($successFul = true, $message="") {
121113
if($successFul){
122114
if($message) $this->messageManager->addSuccessMessage(__($message));

Controller/Payment/Callback.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ public function execute() {
3939
}
4040

4141
try {
42-
$transactionDetails = $this->paystack->transaction->verify([
43-
'reference' => $reference
44-
]);
42+
$transactionDetails = $this->paystackClient->verifyTransaction($reference);
4543

4644
$reference = explode('_', $transactionDetails->data->reference, 2);
4745
$reference = ($reference[0])?: 0;
@@ -60,7 +58,7 @@ public function execute() {
6058

6159
$message = "Invalid reference or order number";
6260

63-
} catch (\Yabacon\Paystack\Exception\ApiException $e) {
61+
} catch (\Pstk\Paystack\Gateway\Exception\ApiException $e) {
6462
$message = $e->getMessage();
6563

6664
} catch (Exception $e) {

Controller/Payment/Setup.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function execute() {
3737

3838
try {
3939
return $this->processAuthorization($order);
40-
} catch (\Yabacon\Paystack\Exception\ApiException $e) {
40+
} catch (\Pstk\Paystack\Gateway\Exception\ApiException $e) {
4141
$message = $e->getMessage();
4242
$order->addStatusToHistory($order->getStatus(), $message);
4343
$this->orderRepository->save($order);
@@ -48,7 +48,7 @@ public function execute() {
4848
}
4949

5050
protected function processAuthorization(\Magento\Sales\Model\Order $order) {
51-
$tranx = $this->paystack->transaction->initialize([
51+
$tranx = $this->paystackClient->initializeTransaction([
5252
'first_name' => $order->getCustomerFirstname(),
5353
'last_name' => $order->getCustomerLastname(),
5454
'amount' => $order->getGrandTotal() * 100, // in kobo

Controller/Payment/Webhook.php

Lines changed: 46 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,94 +3,87 @@
33
/**
44
* Paystack Magento2 Module using \Magento\Payment\Model\Method\AbstractMethod
55
* Copyright (C) 2019 Paystack.com
6-
*
6+
*
77
* This file is part of Pstk/Paystack.
8-
*
8+
*
99
* Pstk/Paystack is free software => you can redistribute it and/or modify
1010
* it under the terms of the GNU General Public License as published by
1111
* the Free Software Foundation, either version 3 of the License, or
1212
* (at your option) any later version.
13-
*
13+
*
1414
* This program is distributed in the hope that it will be useful,
1515
* but WITHOUT ANY WARRANTY; without even the implied warranty of
1616
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1717
* GNU General Public License for more details.
18-
*
18+
*
1919
* You should have received a copy of the GNU General Public License
2020
* along with this program. If not, see <http =>//www.gnu.org/licenses/>.
2121
*/
2222

2323
namespace Pstk\Paystack\Controller\Payment;
2424

25-
26-
use Magento\Sales\Model\Order;
27-
2825
class Webhook extends AbstractPaystackStandard
2926
{
3027

3128
public function execute() {
3229
$finalMessage = "failed";
33-
30+
3431
$resultFactory = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_RAW);
3532
try {
3633

3734
// Retrieve the request's body and parse it as JSON
38-
$event = \Yabacon\Paystack\Event::capture();
35+
$rawBody = $this->request->getContent();
3936
http_response_code(200);
40-
41-
/* It is a important to log all events received. Add code *
42-
* here to log the signature and body to db or file */
43-
$this->logger->debug("PAYSTACK_LOG: {$event->raw}");
4437

45-
/* Verify that the signature matches one of your keys */
46-
$secretKey = $this->configProvider->getSecretKeyArray();
47-
$owner = $event->discoverOwner($secretKey);
38+
$this->logger->info("Paystack Webhook: received request");
4839

49-
if (!$owner) {
50-
// None of the keys matched the event's signature
40+
// Validate webhook signature
41+
$signature = $this->request->getHeader('X-Paystack-Signature') ?: '';
42+
if (!$signature || !$this->paystackClient->validateWebhookSignature($rawBody, $signature)) {
43+
$this->logger->warning("Paystack Webhook: signature validation failed");
5144
$resultFactory->setContents("auth failed");
5245
return $resultFactory;
5346
}
5447

55-
// Do something with $event->obj
56-
// Give value to your customer but don't give any output
57-
// Remember that this is a call from Paystack's servers and
58-
// Your customer is not seeing the response here at all
59-
switch ($event->obj->event) {
60-
// charge.success
61-
case 'charge.success':
62-
if ('success' === $event->obj->data->status) {
63-
$transactionDetails = $this->paystack->transaction->verify([
64-
'reference' => $event->obj->data->reference
65-
]);
48+
$this->logger->info("Paystack Webhook: signature valid");
6649

67-
$reference = $transactionDetails->data->reference;
50+
$event = json_decode($rawBody);
51+
if (!$event) {
52+
$resultFactory->setContents("invalid payload");
53+
return $resultFactory;
54+
}
6855

69-
$order = $this->orderInterface->loadByIncrementId($reference);
56+
$this->logger->info("Paystack Webhook: event type = " . ($event->event ?? 'unknown'));
57+
58+
switch ($event->event) {
59+
case 'charge.success':
60+
if ('success' === $event->data->status) {
61+
$transactionDetails = $this->paystackClient->verifyTransaction($event->data->reference);
7062

71-
//if is popup mode, reference is generated by Paystack and we provided quoteId instead
72-
if((!$order || !$order->getId()) && isset($event->obj->data->metadata->quoteId)){
63+
$reference = $transactionDetails->data->reference;
64+
$this->logger->info("Paystack Webhook: verified transaction", ['reference' => $reference]);
7365

66+
$this->paystackClient->logTransactionSuccess($reference, $this->configProvider->getPublicKey());
7467

75-
$reference = $transactionDetails->data->reference;
76-
//PSTK_LOGGER HERE
77-
log_transaction_success($reference);
78-
//------------------------
79-
$order = $this->orderInterface->loadByIncrementId($reference);
80-
81-
//if is popup mode, reference is generated by Paystack and we provided quoteId instead
82-
if((!$order || !$order->getId()) && isset($event->obj->data->metadata->quoteId)){
83-
84-
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
85-
$searchCriteriaBuilder = $objectManager->create('Magento\Framework\Api\SearchCriteriaBuilder');
86-
$searchCriteria = $searchCriteriaBuilder->addFilter('quote_id', $event->obj->data->metadata->quoteId, 'eq')->create();
87-
$items = $this->orderRepository->getList($searchCriteria);
88-
if($items->getTotalCount() == 1){
89-
$order = $items->getFirstItem();
68+
$order = $this->orderInterface->loadByIncrementId($reference);
9069

91-
}
70+
// In popup mode, reference is generated by Paystack and we provided quoteId instead
71+
if ((!$order || !$order->getId()) && isset($event->data->metadata->quoteId)) {
72+
$this->logger->info("Paystack Webhook: order not found by reference, searching by quoteId", ['quoteId' => $event->data->metadata->quoteId]);
73+
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
74+
$searchCriteriaBuilder = $objectManager->create('Magento\Framework\Api\SearchCriteriaBuilder');
75+
$searchCriteria = $searchCriteriaBuilder->addFilter('quote_id', $event->data->metadata->quoteId, 'eq')->create();
76+
$items = $this->orderRepository->getList($searchCriteria);
77+
if ($items->getTotalCount() == 1) {
78+
$order = $items->getFirstItem();
79+
}
80+
}
9281

9382
if ($order && $order->getId()) {
83+
$this->logger->info("Paystack Webhook: order found, dispatching verify event", [
84+
'order_id' => $order->getIncrementId(),
85+
'current_status' => $order->getStatus(),
86+
]);
9487
// dispatch the `payment_verify_after` event to update the order status
9588
$this->eventManager->dispatch('paystack_payment_verify_after', [
9689
"paystack_order" => $order,
@@ -99,43 +92,16 @@ public function execute() {
9992
$resultFactory->setContents("success");
10093
return $resultFactory;
10194
}
95+
$this->logger->warning("Paystack Webhook: order not found for reference " . $reference);
10296
}
103-
}
10497
break;
10598
}
106-
}
107-
} catch (Exception $exc) {
99+
} catch (\Exception $exc) {
100+
$this->logger->error("Paystack Webhook: exception", ['error' => $exc->getMessage()]);
108101
$finalMessage = $exc->getMessage();
109102
}
110-
103+
111104
$resultFactory->setContents($finalMessage);
112105
return $resultFactory;
113106
}
114-
115-
function log_transaction_success($trx_ref){
116-
//send reference to logger along with plugin name and public key
117-
$url = "https://plugin-tracker.paystackintegrations.com/log/charge_success";
118-
$plugin_name = 'magento-2';
119-
$public_key = $this->configProvider->getPublicKey();
120-
121-
$fields = [
122-
'plugin_name' => $plugin_name,
123-
'transaction_reference' => $trx_ref,
124-
'public_key' => $public_key
125-
];
126-
127-
$fields_string = http_build_query($fields);
128-
129-
$ch = curl_init();
130-
131-
curl_setopt($ch,CURLOPT_URL, $url);
132-
curl_setopt($ch,CURLOPT_POST, true);
133-
curl_setopt($ch,CURLOPT_POSTFIELDS, $fields_string);
134-
135-
curl_setopt($ch,CURLOPT_RETURNTRANSFER, true);
136-
137-
//execute post
138-
$result = curl_exec($ch);
139-
// echo $result;
140-
}
141107
}

Dockerfile

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

Gateway/Exception/ApiException.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Pstk\Paystack\Gateway\Exception;
4+
5+
class ApiException extends \Exception
6+
{
7+
}

0 commit comments

Comments
 (0)