paystack/libraries/Paystack_gateway.php
2025-01-19 12:18:55 +00:00

378 lines
12 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Paystack_gateway extends App_gateway
{
protected $test_mode;
protected $api_url = 'https://api.paystack.co/';
public function __construct()
{
parent::__construct();
$this->test_mode = get_option('test_mode_enabled');
// Load the model using the full module path
$this->ci->load->model('paystack/paystack_model');
$this->setId('paystack');
$this->setName('Paystack');
/**
* Enhanced settings with additional security options
*/
$this->setSettings([
[
'name' => 'paystack_secret_key',
'encrypted' => true,
'label' => 'Paystack Secret Key',
'type' => 'input'
],
[
'name' => 'paystack_public_key',
'label' => 'Paystack Public Key',
'type' => 'input'
],
[
'name' => 'test_mode_enabled',
'label' => 'Test Mode',
'type' => 'yes_no',
'default_value' => 1
],
[
'name' => 'currencies',
'label' => 'settings_paymentmethod_currencies',
'default_value' => 'NGN,USD,GHS,ZAR'
],
[
'name' => 'webhook_secret',
'encrypted' => true,
'label' => 'Webhook Secret',
'type' => 'input'
],
[
'name' => 'max_retry_attempts',
'label' => 'Maximum Retry Attempts',
'type' => 'input',
'default_value' => '3'
]
]);
}
/**
* Process the payment with validation and security checks
*/
public function process_payment($data)
{
try {
// Log the payment data for debugging
$this->ci->paystack_model->add_payment_log([
'invoice_id' => $data['invoiceid'],
'amount' => $data['amount'],
'message' => 'Payment process initiated',
'log_type' => 'debug'
]);
// Generate payment reference
$reference = 'INV_' . $data['invoiceid'] . '_' . time();
// Store transaction data
$transaction_data = [
'invoice_id' => $data['invoiceid'],
'reference' => $reference,
'email' => $data['client']->email,
'amount' => $data['amount'],
'status' => 'pending',
'transaction_date' => date('Y-m-d H:i:s')
];
$this->ci->db->insert(db_prefix() . 'paystack_transactions', $transaction_data);
// Return HTML for the payment form
return $this->generate_payment_form($data, $reference);
} catch (Exception $e) {
// Log any errors
$this->ci->paystack_model->add_payment_log([
'invoice_id' => $data['invoiceid'],
'message' => 'Error: ' . $e->getMessage(),
'log_type' => 'error'
]);
return false;
}
}
private function generate_payment_form($data, $reference)
{
$public_key = $this->getSetting('paystack_public_key');
$form = '
<div id="paystack-payment-form">
<script src="https://js.paystack.co/v1/inline.js"></script>
<button type="button" class="btn btn-success" onclick="payWithPaystack()">Pay with Paystack</button>
<script>
function payWithPaystack() {
let handler = PaystackPop.setup({
key: "' . $public_key . '",
email: "' . $data['client']->email . '",
amount: ' . ($data['amount'] * 100) . ', // Convert to kobo
currency: "' . $data['currency'] . '",
ref: "' . $reference . '",
callback: function(response) {
if(response.status == "success") {
window.location.href = "' . site_url('paystack/verify_payment/') . '" + response.reference;
}
},
onClose: function() {
// Handle popup closed
console.log("Payment window closed");
}
});
handler.openIframe();
}
</script>
</div>';
return $form;
}
/**
* Record payment with additional validation
*/
public function record_payment($invoice_id, $transaction)
{
// Validate transaction data
if (!$this->validate_transaction_data($transaction)) {
$this->log_error('Invalid transaction data for payment recording');
return false;
}
$payment_data = [
'amount' => $transaction['data']->amount / 100,
'invoiceid' => $invoice_id,
'paymentmode' => $this->getId(),
'transactionid' => $transaction['data']->reference,
'note' => 'Paystack Transaction Reference: ' . $transaction['data']->reference
];
// Record payment
$this->ci->load->model('payments_model');
$payment_id = $this->ci->payments_model->add($payment_data);
if ($payment_id) {
$this->update_invoice_status($invoice_id);
$this->log_success($transaction['data']->reference, $payment_data['amount']);
return true;
}
$this->log_error('Failed to record payment for invoice #' . $invoice_id);
return false;
}
/**
* Make secure API request
*/
protected function make_api_request($endpoint, $method = 'GET', $data = null)
{
$url = $this->api_url . $endpoint;
$secret_key = $this->get_secret_key();
$headers = [
'Authorization: Bearer ' . $secret_key,
'Cache-Control: no-cache'
];
if ($data && in_array($method, ['POST', 'PUT'])) {
$headers[] = 'Content-Type: application/json';
}
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers
]);
if ($data && in_array($method, ['POST', 'PUT'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}
$response = curl_exec($ch);
$error = curl_error($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$this->log_api_request($endpoint, $method, $data, $http_code, $error);
if ($error) {
return [
'success' => false,
'message' => $error
];
}
$result = json_decode($response);
if (!$result || !$result->status) {
return [
'success' => false,
'message' => isset($result->message) ? $result->message : 'Invalid response'
];
}
return [
'success' => true,
'data' => $result
];
}
/**
* Validation methods
*/
protected function validate_payment_data($data)
{
$required_fields = ['amount', 'invoiceid', 'currency'];
foreach ($required_fields as $field) {
if (!isset($data[$field]) || empty($data[$field])) {
$this->log_error('Missing required field: ' . $field);
return false;
}
}
return true;
}
protected function validate_payment_amount($amount)
{
return is_numeric($amount) && $amount > 0;
}
protected function validate_payment_currency($currency)
{
$allowed_currencies = explode(',', get_option('currencies'));
return in_array(strtoupper($currency), $allowed_currencies);
}
public function verify_transaction($reference)
{
$secret_key = $this->decryptSetting('paystack_secret_key');
$ch = curl_init();
curl_setopt_array($ch, [
CURLOPT_URL => "https://api.paystack.co/transaction/verify/" . rawurlencode($reference),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . $secret_key,
"Cache-Control: no-cache",
],
]);
$response = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
if ($err) {
$this->ci->paystack_model->add_payment_log([
'message' => 'Verification Error: ' . $err,
'log_type' => 'error'
]);
return [
'success' => false,
'message' => $err
];
}
$result = json_decode($response);
return [
'success' => true,
'data' => $result->data
];
}
/**
* Security methods
*/
protected function encrypt_payment_data($data)
{
$this->ci->load->library('encryption');
return $this->ci->encryption->encrypt(serialize($data));
}
protected function decrypt_payment_data($encrypted_data)
{
if (!$encrypted_data) return false;
$this->ci->load->library('encryption');
$decrypted = $this->ci->encryption->decrypt($encrypted_data);
return $decrypted ? unserialize($decrypted) : false;
}
protected function get_secret_key()
{
$key_option = $this->test_mode ? 'paystack_test_secret_key' : 'paystack_live_secret_key';
return $this->decryptSetting($key_option);
}
/**
* Rate limiting
*/
protected function check_rate_limit($invoice_id)
{
$max_attempts = get_option('max_retry_attempts');
$timeframe = 15; // minutes
$this->ci->load->model('paystack_model');
$attempts = $this->ci->paystack_model->get_recent_failed_attempts($invoice_id, $timeframe);
return $attempts < $max_attempts;
}
/**
* Logging methods
*/
protected function log_payment_attempt($invoice_id, $amount)
{
$this->ci->load->model('paystack_model');
$this->ci->paystack_model->add_payment_log([
'invoice_id' => $invoice_id,
'amount' => $amount,
'log_type' => 'attempt',
'message' => 'Payment attempt initiated'
]);
}
protected function log_api_request($endpoint, $method, $data, $http_code, $error = null)
{
$this->ci->load->model('paystack_model');
$this->ci->paystack_model->add_payment_log([
'log_type' => 'api_request',
'message' => json_encode([
'endpoint' => $endpoint,
'method' => $method,
'http_code' => $http_code,
'error' => $error
])
]);
}
protected function log_success($reference, $amount)
{
$this->ci->load->model('paystack_model');
$this->ci->paystack_model->add_payment_log([
'log_type' => 'success',
'message' => "Payment successful - Reference: $reference, Amount: $amount"
]);
}
protected function log_error($message)
{
$this->ci->load->model('paystack_model');
$this->ci->paystack_model->add_payment_log([
'log_type' => 'error',
'message' => $message
]);
}
}