337 lines
11 KiB
PHP
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);
|
|
}
|
|
} |