paystack/controllers/Paystack.php
2025-01-19 12:18:55 +00:00

337 lines
11 KiB
PHP

<?php
defined('BASEPATH') or exit('No direct script access allowed');
class Paystack extends App_Controller
{
public function __construct()
{
parent::__construct();
$this->load->model('invoices_model');
$this->load->model('clients_model');
$this->load->library('paystack_gateway');
$this->load->model('paystack/paystack_model');
}
/**
* Show payment form
*/
public function make_payment()
{
$payment_data = $this->session->userdata('paystack_payment_data');
if (!$payment_data) {
set_alert('danger', _l('invalid_payment'));
redirect(site_url('invoices'));
}
$invoice = $this->invoices_model->get($payment_data['invoice_id']);
if (!$invoice) {
set_alert('danger', _l('invoice_not_found'));
redirect(site_url('invoices'));
}
// Get client data
$client = $this->clients_model->get($invoice->clientid);
$data = [];
$data['invoice'] = $invoice;
$data['payment_data'] = $payment_data;
$data['client'] = $client;
$this->load->view('paystack/payment', $data);
}
/**
* Verify payment callback
*/
public function verify_payment($reference = null)
{
if (!$reference) {
redirect(site_url('clients/invoices'));
}
// Log verification attempt
$this->paystack_model->add_payment_log([
'message' => 'Verifying payment: ' . $reference,
'log_type' => 'info'
]);
$transaction = $this->paystack_gateway->verify_transaction($reference);
if ($transaction['success']) {
// Get the transaction from our database
$local_transaction = $this->paystack_model->get_transaction_by_reference($reference);
if ($local_transaction) {
// Update local transaction status
$this->db->where('id', $local_transaction['id']);
$this->db->update(db_prefix() . 'paystack_transactions', [
'status' => 'success',
'transaction_date' => date('Y-m-d H:i:s')
]);
// Record the payment in Perfex CRM
$payment_data = [
'amount' => $local_transaction['amount'],
'invoiceid' => $local_transaction['invoice_id'],
'paymentmode' => 'paystack',
'transactionid' => $reference
];
$this->load->model('payments_model');
$payment_id = $this->payments_model->add($payment_data);
if ($payment_id) {
set_alert('success', _l('payment_recorded_successfully'));
} else {
set_alert('danger', _l('payment_record_failed'));
}
}
} else {
set_alert('danger', _l('payment_failed'));
}
// Redirect to invoice
redirect(site_url('clients/invoices'));
}
/**
* Handle Paystack webhook
*/
// public function webhook()
// {
// if ((strtoupper($_SERVER['REQUEST_METHOD']) != 'POST') || !array_key_exists('HTTP_X_PAYSTACK_SIGNATURE', $_SERVER)) {
// exit();
// }
//
// $input = file_get_contents("php://input");
//
// // Verify webhook signature
// $secret_key = $this->paystack_gateway->decryptSetting('paystack_secret_key');
// if ($_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] !== hash_hmac('sha512', $input, $secret_key)) {
// exit();
// }
//
// http_response_code(200);
//
// $event = json_decode($input);
//
// // Handle the event
// switch ($event->event) {
// case 'charge.success':
// $this->handle_successful_charge($event->data);
// break;
// case 'transfer.success':
// $this->handle_successful_transfer($event->data);
// break;
// case 'charge.failed':
// $this->handle_failed_charge($event->data);
// break;
// }
//
// exit();
// }
public function webhook()
{
$this->load->helper('paystack_security');
// Get payload and signature
$payload = file_get_contents('php://input');
$signature = isset($_SERVER['HTTP_X_PAYSTACK_SIGNATURE']) ? $_SERVER['HTTP_X_PAYSTACK_SIGNATURE'] : '';
// Log webhook request
$this->paystack_model->add_webhook_log([
'event' => 'webhook_received',
'payload' => $payload,
'signature' => $signature,
'status' => 'received'
]);
// Verify signature
if (!verify_paystack_webhook_signature($payload, $signature)) {
$this->paystack_model->add_webhook_log([
'event' => 'webhook_verification_failed',
'payload' => $payload,
'status' => 'failed',
'message' => 'Invalid signature'
]);
header('HTTP/1.1 401 Unauthorized');
exit();
}
// Parse payload
$event = json_decode($payload);
// Validate payload
if (!is_object($event) || !isset($event->event)) {
$this->paystack_model->add_webhook_log([
'event' => 'webhook_invalid_payload',
'payload' => $payload,
'status' => 'failed',
'message' => 'Invalid payload format'
]);
header('HTTP/1.1 400 Bad Request');
exit();
}
try {
// Process different event types
switch ($event->event) {
case 'charge.success':
$this->handle_successful_charge($event->data);
break;
case 'charge.failed':
$this->handle_failed_charge($event->data);
break;
case 'transfer.success':
$this->handle_successful_transfer($event->data);
break;
case 'transfer.failed':
$this->handle_failed_transfer($event->data);
break;
default:
// Log unknown event type
$this->paystack_model->add_webhook_log([
'event' => $event->event,
'payload' => $payload,
'status' => 'skipped',
'message' => 'Unknown event type'
]);
break;
}
header('HTTP/1.1 200 OK');
echo json_encode(['status' => 'success']);
} catch (Exception $e) {
// Log error
$this->paystack_model->add_webhook_log([
'event' => $event->event,
'payload' => $payload,
'status' => 'error',
'message' => $e->getMessage()
]);
header('HTTP/1.1 500 Internal Server Error');
echo json_encode(['status' => 'error', 'message' => 'Internal processing error']);
}
}
/**
* Handle successful charge
*/
private function handle_successful_charge($data)
{
// Validate the charge data
if (!isset($data->reference) || !isset($data->amount)) {
throw new Exception('Invalid charge data');
}
// Find the transaction
$transaction = $this->paystack_model->get_transaction_by_reference($data->reference);
if (!$transaction) {
throw new Exception('Transaction not found');
}
// Verify amount
$expected_amount = $transaction['amount'] * 100; // Convert to kobo
if ($data->amount !== $expected_amount) {
throw new Exception('Amount mismatch');
}
// Update transaction status
$this->paystack_model->update_transaction($transaction['id'], [
'status' => 'success',
'transaction_date' => date('Y-m-d H:i:s')
]);
// Add to payment logs
$this->paystack_model->add_payment_log([
'transaction_id' => $transaction['id'],
'invoice_id' => $transaction['invoice_id'],
'amount' => $transaction['amount'],
'message' => 'Payment successful',
'log_type' => 'success'
]);
// Record payment in Perfex CRM
$payment_data = [
'amount' => $transaction['amount'],
'invoiceid' => $transaction['invoice_id'],
'paymentmode' => 'paystack',
'transactionid' => $data->reference
];
$this->load->model('payments_model');
$payment_id = $this->payments_model->add($payment_data);
if (!$payment_id) {
throw new Exception('Failed to record payment');
}
// Send email notification
$this->send_payment_notification($transaction['invoice_id'], $payment_id);
}
/**
* Handle failed charge
*/
private function handle_failed_charge($data)
{
if (!isset($data->reference)) {
throw new Exception('Invalid charge data');
}
$transaction = $this->paystack_model->get_transaction_by_reference($data->reference);
if ($transaction) {
// Update transaction status
$this->paystack_model->update_transaction($transaction['id'], [
'status' => 'failed',
'transaction_date' => date('Y-m-d H:i:s')
]);
// Add to payment logs
$this->paystack_model->add_payment_log([
'transaction_id' => $transaction['id'],
'invoice_id' => $transaction['invoice_id'],
'amount' => $transaction['amount'],
'message' => 'Payment failed: ' . ($data->gateway_response ?? 'Unknown error'),
'log_type' => 'error'
]);
}
}
/**
* Handle successful transfer
*/
private function handle_successful_transfer($data)
{
log_activity('Paystack Webhook: Successful transfer - Reference: ' . $data->reference);
}
/**
* Send payment success email
*/
private function send_payment_success_email($invoice_id, $transaction)
{
$this->load->model('emails_model');
$invoice = $this->invoices_model->get($invoice_id);
$client = $this->clients_model->get($invoice->clientid);
$email_template = 'invoice-payment-recorded';
$merge_fields = [];
$merge_fields = array_merge($merge_fields, get_invoice_merge_fields($invoice_id));
$merge_fields = array_merge($merge_fields, get_client_merge_fields($client->userid));
$merge_fields['{payment_total}'] = app_format_money($transaction['data']->amount / 100, $invoice->currency_name);
$merge_fields['{payment_reference}'] = $transaction['data']->reference;
$this->emails_model->send_email_template($email_template, $client->email, $merge_fields);
}
}