Skip to content

Commit 11cfdf8

Browse files
Copilotnielsdrost7
andcommitted
Improve Mollie gateway: handle payment states, better error handling, session security
Co-authored-by: nielsdrost7 <47660417+nielsdrost7@users.noreply.github.com>
1 parent 40464ff commit 11cfdf8

2 files changed

Lines changed: 115 additions & 43 deletions

File tree

application/libraries/gateways/MollieLib.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
exit('No direct script access allowed');
55
}
66

7-
use Omnipay\Omnipay;
7+
use Omnipay\Common\Exception\InvalidRequestException;
8+
use Omnipay\Common\Exception\InvalidResponseException;
89
use Omnipay\Mollie\Gateway;
10+
use Omnipay\Omnipay;
911

1012
#[AllowDynamicProperties]
1113
class MollieLib
@@ -67,6 +69,20 @@ public function createPayment(array $payment_information): array
6769
'message' => $response->getMessage(),
6870
];
6971
}
72+
} catch (InvalidRequestException $e) {
73+
log_message('error', 'Mollie library payment creation - invalid request: ' . $e->getMessage());
74+
75+
return [
76+
'status' => false,
77+
'message' => 'Invalid payment request: ' . $e->getMessage(),
78+
];
79+
} catch (InvalidResponseException $e) {
80+
log_message('error', 'Mollie library payment creation - invalid response: ' . $e->getMessage());
81+
82+
return [
83+
'status' => false,
84+
'message' => 'Invalid response from Mollie: ' . $e->getMessage(),
85+
];
7086
} catch (Exception $e) {
7187
log_message('error', 'Mollie library payment creation exception: ' . $e->getMessage());
7288

@@ -96,6 +112,20 @@ public function getPayment(string $transaction_reference): array
96112
'status' => true,
97113
'response' => $response,
98114
];
115+
} catch (InvalidRequestException $e) {
116+
log_message('error', 'Mollie library get payment - invalid request: ' . $e->getMessage());
117+
118+
return [
119+
'status' => false,
120+
'message' => 'Invalid payment fetch request: ' . $e->getMessage(),
121+
];
122+
} catch (InvalidResponseException $e) {
123+
log_message('error', 'Mollie library get payment - invalid response: ' . $e->getMessage());
124+
125+
return [
126+
'status' => false,
127+
'message' => 'Invalid response from Mollie: ' . $e->getMessage(),
128+
];
99129
} catch (Exception $e) {
100130
log_message('error', 'Mollie library get payment failed: ' . $e->getMessage());
101131

application/modules/guest/controllers/gateways/Mollie.php

Lines changed: 84 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,22 @@ public function mollie_create_payment($invoice_url_key)
5454

5555
// Handle the payment creation
5656
if ($mollie_response['status']) {
57-
// Save the transaction reference in session for callback
58-
$this->session->set_userdata('mollie_transaction_ref', $mollie_response['reference']);
57+
$transaction_ref = $mollie_response['reference'];
5958

60-
// Redirect to Mollie payment page
59+
// Save the transaction reference in session for callback (backup)
60+
$this->session->set_userdata('mollie_transaction_ref_' . $invoice_url_key, $transaction_ref);
61+
62+
// Record successful payment creation
63+
$this->db->insert('ip_merchant_responses', [
64+
'invoice_id' => $invoice->invoice_id,
65+
'merchant_response_successful' => true,
66+
'merchant_response_date' => date('Y-m-d'),
67+
'merchant_response_driver' => 'mollie',
68+
'merchant_response' => 'Payment created, awaiting customer action',
69+
'merchant_response_reference' => 'transaction_ref: ' . $transaction_ref,
70+
]);
71+
72+
// Redirect to Mollie payment page with transaction ref in URL
6173
redirect($mollie_response['redirect_url']);
6274
} else {
6375
// Record the failed transaction
@@ -80,14 +92,28 @@ public function mollie_create_payment($invoice_url_key)
8092
* The callback endpoint called by Mollie after payment.
8193
*
8294
* @param string $invoice_url_key
95+
* @param string $transaction_ref (optional, from URL parameter)
8396
*
8497
* @return void
8598
*/
86-
public function callback($invoice_url_key)
99+
public function callback($invoice_url_key, $transaction_ref = null)
87100
{
101+
$user_message = '';
102+
$alert_type = 'error';
103+
88104
try {
89-
// Get transaction reference from session
90-
$transaction_ref = $this->session->userdata('mollie_transaction_ref');
105+
// Retrieve the invoice first
106+
$this->load->model('invoices/mdl_invoices');
107+
$invoice = $this->mdl_invoices->where('ip_invoices.invoice_url_key', $invoice_url_key)->get()->row();
108+
109+
if ( ! $invoice) {
110+
throw new Exception('Invoice not found');
111+
}
112+
113+
// Get transaction reference from URL parameter or session (fallback)
114+
if ( ! $transaction_ref) {
115+
$transaction_ref = $this->session->userdata('mollie_transaction_ref_' . $invoice_url_key);
116+
}
91117

92118
if ( ! $transaction_ref) {
93119
throw new Exception('No transaction reference found');
@@ -97,26 +123,23 @@ public function callback($invoice_url_key)
97123
$mollie_response = $this->lib_mollie->getPayment($transaction_ref);
98124

99125
if ( ! $mollie_response['status']) {
100-
throw new Exception('Failed to fetch payment details');
126+
throw new Exception('Failed to fetch payment details: ' . ($mollie_response['message'] ?? 'Unknown error'));
101127
}
102128

103-
$payment = $mollie_response['response'];
129+
$payment = $mollie_response['response'];
130+
$payment_data = $payment->getData();
131+
$status = $payment_data['status'] ?? 'unknown';
104132

105-
// Retrieve the invoice
106-
$this->load->model('invoices/mdl_invoices');
107-
$invoice = $this->mdl_invoices->where('ip_invoices.invoice_url_key', $invoice_url_key)->get()->row();
108-
109-
if (!$invoice) {
110-
throw new Exception('Invoice not found');
133+
// Verify the payment belongs to this invoice
134+
$metadata = $payment_data['metadata'] ?? [];
135+
if (isset($metadata['invoice_key']) && $metadata['invoice_key'] !== $invoice_url_key) {
136+
throw new Exception('Payment does not belong to this invoice');
111137
}
112138

113-
// Check if payment was successful
114-
$paid = $payment->isSuccessful();
115-
116-
if ($paid) {
117-
// Save the payment
139+
// Handle different payment states
140+
if ($payment->isSuccessful()) {
141+
// Payment successful - record it
118142
$this->load->model('payments/mdl_payments');
119-
$payment_data = $payment->getData();
120143
$payment_amount = isset($payment_data['amount']['value'])
121144
? (float) $payment_data['amount']['value']
122145
: (float) $invoice->invoice_balance;
@@ -128,39 +151,53 @@ public function callback($invoice_url_key)
128151
'payment_method_id' => get_setting('gateway_mollie_payment_method'),
129152
'payment_note' => trans('online_payment_intent_id') . ': ' . $transaction_ref,
130153
]);
154+
155+
$response_msg = 'Payment successful: ' . $status;
156+
$user_message = sprintf(trans('online_payment_successful'), '#' . $invoice->invoice_number);
157+
$alert_type = 'success';
158+
$success = true;
159+
} elseif ($payment->isPending()) {
160+
// Payment is pending (e.g., bank transfer)
161+
$response_msg = 'Payment pending: ' . $status;
162+
$user_message = trans('online_payment_pending');
163+
$alert_type = 'info';
164+
$success = false;
165+
} elseif ($payment->isCancelled()) {
166+
// Payment was cancelled by user
167+
$response_msg = 'Payment cancelled: ' . $status;
168+
$user_message = trans('online_payment_cancelled');
169+
$alert_type = 'info';
170+
$success = false;
171+
} else {
172+
// Payment failed, expired, or other status
173+
$response_msg = 'Payment ' . $status;
174+
$user_message = trans('online_payment_failed');
175+
$alert_type = 'error';
176+
$success = false;
131177
}
132178

133179
// Record merchant response
134-
$response_msg = $paid ? 'Payment successful'
135-
: 'Payment ' . ($payment->getData()['status'] ?? 'failed');
136-
137180
$this->db->insert('ip_merchant_responses', [
138181
'invoice_id' => $invoice->invoice_id,
139-
'merchant_response_successful' => (int) $paid,
182+
'merchant_response_successful' => (int) $success,
140183
'merchant_response_date' => date('Y-m-d'),
141184
'merchant_response_driver' => 'mollie',
142185
'merchant_response' => $response_msg,
143186
'merchant_response_reference' => 'transaction_ref: ' . $transaction_ref,
144187
]);
145188

146-
// Notify user
147-
if ($paid) {
148-
$this->session->set_flashdata(
149-
'alert_success',
150-
sprintf(trans('online_payment_successful'), '#' . $invoice->invoice_number)
151-
);
152-
} else {
153-
$this->session->set_flashdata(
154-
'alert_info',
155-
trans('online_payment_failed')
156-
);
157-
}
189+
// Set user notification
190+
$this->session->set_flashdata('alert_' . $alert_type, $user_message);
158191

159192
// Clean up session
160-
$this->session->unset_userdata('mollie_transaction_ref');
193+
$this->session->unset_userdata('mollie_transaction_ref_' . $invoice_url_key);
161194
} catch (Error|Exception|ErrorException $e) {
162-
// Log the error
163-
log_message('error', 'Mollie callback exception: ' . $e->getMessage());
195+
// Log the error with context
196+
$error_context = 'Mollie callback exception for invoice ' . $invoice_url_key;
197+
if (isset($transaction_ref)) {
198+
$error_context .= ', transaction: ' . $transaction_ref;
199+
}
200+
log_message('error', $error_context . ' - ' . $e->getMessage());
164201

165202
// Record error in merchant responses
166203
if (isset($invoice)) {
@@ -169,12 +206,17 @@ public function callback($invoice_url_key)
169206
'merchant_response_successful' => false,
170207
'merchant_response_date' => date('Y-m-d'),
171208
'merchant_response_driver' => 'mollie',
172-
'merchant_response' => 'Error: ' . $e->getMessage(),
173-
'merchant_response_reference' => 'none',
209+
'merchant_response' => 'Callback error: ' . $e->getMessage(),
210+
'merchant_response_reference' => isset($transaction_ref) ? $transaction_ref : 'none',
174211
]);
175212
}
176213

177-
$this->session->set_flashdata('alert_error', trans('online_payment_error'));
214+
// Provide more specific error message
215+
$error_message = trans('online_payment_error');
216+
if (str_contains($e->getMessage(), 'not found')) {
217+
$error_message .= ' ' . trans('invoice_not_found');
218+
}
219+
$this->session->set_flashdata('alert_error', $error_message);
178220
} finally {
179221
// Redirect to invoice
180222
redirect('guest/view/invoice/' . $invoice_url_key);

0 commit comments

Comments
 (0)