Skip to content

Commit a4184fa

Browse files
author
Fernando González
committed
Adding Stripe link payments
1 parent efa2dbc commit a4184fa

File tree

55 files changed

+949
-81
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+949
-81
lines changed

application/config/config.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,5 +465,16 @@
465465
$config['rate_limiting'] = TRUE;
466466

467467

468+
/*
469+
|--------------------------------------------------------------------------
470+
| Stripe Payment Configuration
471+
|--------------------------------------------------------------------------
472+
|
473+
| Declare some of the global config values of the Stripe Payments
474+
|
475+
*/
476+
477+
$config['stripe_api_key'] = Config::STRIPE_API_KEY;
478+
468479
/* End of file config.php */
469480
/* Location: ./application/config/config.php */

application/controllers/Booking.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public function index()
9393

9494
foreach ($available_providers as &$available_provider)
9595
{
96-
// Only expose the required provider data.
96+
// Only expose the required provider data.
9797

9898
$this->providers_model->only($available_provider, [
9999
'id',
@@ -170,7 +170,7 @@ public function index()
170170
return;
171171
}
172172

173-
// Make sure the appointment can still be rescheduled.
173+
// Make sure the appointment can still be rescheduled.
174174

175175
$start_datetime = strtotime($results[0]['start_datetime']);
176176

@@ -203,6 +203,7 @@ public function index()
203203
$provider = $this->providers_model->find($appointment['id_users_provider']);
204204
$customer = $this->customers_model->find($appointment['id_users_customer']);
205205
$customer_token = md5(uniqid(mt_rand(), TRUE));
206+
$is_paid = $appointment['is_paid'];
206207

207208
// Cache the token for 10 minutes.
208209
$this->cache->save('customer-token-' . $customer_token, $customer['id'], 600);
@@ -214,6 +215,7 @@ public function index()
214215
$appointment = NULL;
215216
$provider = NULL;
216217
$customer = NULL;
218+
$is_paid = 0;
217219
}
218220

219221
script_vars([
@@ -268,9 +270,11 @@ public function index()
268270
'grouped_timezones' => $grouped_timezones,
269271
'manage_mode' => $manage_mode,
270272
'customer_token' => $customer_token,
273+
'is_paid' => $is_paid == 1,
271274
'appointment_data' => $appointment,
272275
'provider_data' => $provider,
273276
'customer_data' => $customer,
277+
'company_email' => setting('company_email'),
274278
]);
275279

276280
$this->load->view('pages/booking');

application/controllers/Calendar.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ public function save_appointment()
252252
'id_users_provider',
253253
'id_users_customer',
254254
'id_services',
255+
'is_paid',
255256
]);
256257

257258
$appointment['id'] = $this->appointments_model->save($appointment);
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
<?php defined('BASEPATH') or exit('No direct script access allowed');
2+
3+
/* ----------------------------------------------------------------------------
4+
* Easy!Appointments - Online Appointment Scheduler
5+
*
6+
* @package EasyAppointments
7+
* @author F.González <[email protected]>
8+
* @copyright Copyright (c) Alex Tselegidis
9+
* @license https://opensource.org/licenses/GPL-3.0 - GPLv3
10+
* @link https://easyappointments.org
11+
* @since v1.0.0
12+
* ---------------------------------------------------------------------------- */
13+
14+
/**
15+
* Payment confirmation controller.
16+
*
17+
* Handles the confirmation of a payment.
18+
*
19+
*
20+
* @package Controllers
21+
*/
22+
class Payment extends EA_Controller {
23+
/**
24+
* Booking constructor.
25+
*/
26+
public function __construct()
27+
{
28+
parent::__construct();
29+
30+
$this->load->model('appointments_model');
31+
$this->load->model('providers_model');
32+
$this->load->model('admins_model');
33+
$this->load->model('secretaries_model');
34+
$this->load->model('categories_model');
35+
$this->load->model('services_model');
36+
$this->load->model('customers_model');
37+
$this->load->model('settings_model');
38+
$this->load->model('consents_model');
39+
40+
$this->load->library('timezones');
41+
$this->load->library('synchronization');
42+
$this->load->library('notifications');
43+
$this->load->library('availability');
44+
$this->load->library('webhooks_client');
45+
46+
$this->load->driver('cache', ['adapter' => 'file']);
47+
}
48+
49+
/**
50+
* Render the payment confirmation page.
51+
*
52+
* This method sets a flag as paid for an appointment and renders a confirmation page.
53+
*/
54+
public function index()
55+
{
56+
if ( ! is_app_installed())
57+
{
58+
redirect('installation');
59+
60+
return;
61+
}
62+
63+
$appointment = html_vars('appointment');
64+
65+
if (empty($appointment)) {
66+
abort(404, "Forbidden");
67+
} else {
68+
$manage_mode = TRUE;
69+
$company_name = setting('company_name');
70+
$company_logo = setting('company_logo');
71+
$company_color = setting('company_color');
72+
$google_analytics_code = setting('google_analytics_code');
73+
$matomo_analytics_url = setting('matomo_analytics_url');
74+
$date_format = setting('date_format');
75+
$time_format = setting('time_format');
76+
77+
$display_first_name = setting('display_first_name');
78+
$require_first_name = setting('require_first_name');
79+
$display_last_name = setting('display_last_name');
80+
$require_last_name = setting('require_last_name');
81+
$display_email = setting('display_email');
82+
$require_email = setting('require_email');
83+
$display_phone_number = setting('display_phone_number');
84+
$require_phone_number = setting('require_phone_number');
85+
$display_address = setting('display_address');
86+
$require_address = setting('require_address');
87+
$display_city = setting('display_city');
88+
$require_city = setting('require_city');
89+
$display_zip_code = setting('display_zip_code');
90+
$require_zip_code = setting('require_zip_code');
91+
$display_notes = setting('display_notes');
92+
$require_notes = setting('require_notes');
93+
$display_cookie_notice = setting('display_cookie_notice');
94+
$cookie_notice_content = setting('cookie_notice_content');
95+
$display_terms_and_conditions = setting('display_terms_and_conditions');
96+
$terms_and_conditions_content = setting('terms_and_conditions_content');
97+
$display_privacy_policy = setting('display_privacy_policy');
98+
$privacy_policy_content = setting('privacy_policy_content');
99+
100+
$theme = request('theme', setting('theme', 'default'));
101+
if (empty($theme) || ! file_exists(__DIR__ . '/../../assets/css/themes/' . $theme . '.min.css'))
102+
{
103+
$theme = 'default';
104+
}
105+
106+
$timezones = $this->timezones->to_array();
107+
$grouped_timezones = $this->timezones->to_grouped_array();
108+
$provider = $this->providers_model->find($appointment['id_users_provider']);
109+
$customer = $this->customers_model->find($appointment['id_users_customer']);
110+
111+
script_vars([
112+
'date_format' => $date_format,
113+
'time_format' => $time_format,
114+
'display_cookie_notice' => $display_cookie_notice,
115+
'display_any_provider' => setting('display_any_provider'),
116+
]);
117+
118+
html_vars([
119+
'theme' => $theme,
120+
'company_name' => $company_name,
121+
'company_logo' => $company_logo,
122+
'company_color' => $company_color === '#ffffff' ? '' : $company_color,
123+
'date_format' => $date_format,
124+
'time_format' => $time_format,
125+
'display_first_name' => $display_first_name,
126+
'display_last_name' => $display_last_name,
127+
'display_email' => $display_email,
128+
'display_phone_number' => $display_phone_number,
129+
'display_address' => $display_address,
130+
'display_city' => $display_city,
131+
'display_zip_code' => $display_zip_code,
132+
'display_notes' => $display_notes,
133+
'google_analytics_code' => $google_analytics_code,
134+
'matomo_analytics_url' => $matomo_analytics_url,
135+
'timezones' => $timezones,
136+
'grouped_timezones' => $grouped_timezones,
137+
'appointment' => $appointment,
138+
'provider' => $provider,
139+
'customer' => $customer,
140+
]);
141+
142+
$this->load->view('pages/payment');
143+
}
144+
}
145+
146+
/**
147+
* Validates Stripe payment and render confirmation screen for the appointment.
148+
*
149+
* This method will call the "index" callback to handle the page rendering.
150+
*
151+
* @param string $checkout_session_id
152+
*/
153+
public function confirm(string $checkout_session_id)
154+
{
155+
try
156+
{
157+
$stripe_api_key = config('stripe_api_key');
158+
159+
$stripe = new \Stripe\StripeClient($stripe_api_key);
160+
161+
$session = $stripe->checkout->sessions->retrieve($checkout_session_id);
162+
163+
$appointment_hash = $session->client_reference_id;
164+
$payment_intent = $session->payment_intent;
165+
166+
$appointment = $this->set_paid($appointment_hash, $payment_intent);
167+
168+
html_vars(['appointment' => $appointment]);
169+
170+
$this->index();
171+
}
172+
catch (Throwable $e)
173+
{
174+
error_log( $e );
175+
abort(500, 'Internal server error');
176+
}
177+
}
178+
179+
/**
180+
* Register the appointment to the database.
181+
*/
182+
private function set_paid($appointment_hash, $payment_intent)
183+
{
184+
try
185+
{
186+
$manage_mode = TRUE;
187+
188+
$occurrences = $this->appointments_model->get(['hash' => $appointment_hash]);
189+
190+
if (empty($occurrences))
191+
{
192+
abort(404, 'Not Found');
193+
}
194+
195+
$appointment = $occurrences[0];
196+
197+
$provider = $this->providers_model->find($appointment['id_users_provider']);
198+
199+
$customer = $this->customers_model->find($appointment['id_users_customer']);
200+
201+
$service = $this->services_model->find($appointment['id_services']);
202+
203+
204+
$appointment['is_paid'] = 1;
205+
$appointment['payment_intent'] = $payment_intent;
206+
$this->appointments_model->only($appointment, [
207+
'id',
208+
'start_datetime',
209+
'end_datetime',
210+
'location',
211+
'notes',
212+
'color',
213+
'is_unavailability',
214+
'id_users_provider',
215+
'id_users_customer',
216+
'id_services',
217+
'is_paid',
218+
'payment_intent',
219+
]);
220+
$appointment_id = $this->appointments_model->save($appointment);
221+
$appointment = $this->appointments_model->find($appointment_id);
222+
223+
$settings = [
224+
'company_name' => setting('company_name'),
225+
'company_link' => setting('company_link'),
226+
'company_email' => setting('company_email'),
227+
'date_format' => setting('date_format'),
228+
'time_format' => setting('time_format')
229+
];
230+
231+
$this->synchronization->sync_appointment_saved($appointment, $service, $provider, $customer, $settings, $manage_mode);
232+
233+
$this->notifications->notify_appointment_saved($appointment, $service, $provider, $customer, $settings, $manage_mode);
234+
235+
$this->webhooks_client->trigger(WEBHOOK_APPOINTMENT_SAVE, $appointment);
236+
237+
return $appointment;
238+
}
239+
catch (Throwable $e)
240+
{
241+
error_log( $e );
242+
abort(500, 'Internal server error');
243+
}
244+
}
245+
246+
}

application/language/arabic/translations_lang.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,10 @@
405405
$lang['api_token'] = 'API Token';
406406
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
407407
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
408+
$lang['service_payment_link'] = 'Payment link';
409+
$lang['service_payment_link_description'] = 'You can include the following variables: {$appointment_hash}, {$customer_email}. In order to confirm the payment use /index.php/payment/confirm/{CHECKOUT_SESSION_ID} url as confirmation page in Stripe';
410+
$lang['appointment_payment_title'] = 'Payment details';
411+
$lang['appointment_payment_text'] = 'Click on the following link to proceed with payment';
412+
$lang['appointment_paymentPaid_text'] = 'El pago se ha realizado satisfactoriamente';
413+
$lang['payment_intent'] = 'Stripe Payment Intent';
408414
// End

application/language/bulgarian/translations_lang.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,10 @@
405405
$lang['api_token'] = 'API Token';
406406
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
407407
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
408+
$lang['service_payment_link'] = 'Payment link';
409+
$lang['service_payment_link_description'] = 'You can include the following variables: {$appointment_hash}, {$customer_email}. In order to confirm the payment use /index.php/payment/confirm/{CHECKOUT_SESSION_ID} url as confirmation page in Stripe';
410+
$lang['appointment_payment_title'] = 'Payment details';
411+
$lang['appointment_payment_text'] = 'Click on the following link to proceed with payment';
412+
$lang['appointment_paymentPaid_text'] = 'El pago se ha realizado satisfactoriamente';
413+
$lang['payment_intent'] = 'Stripe Payment Intent';
408414
// End

application/language/catalan/translations_lang.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,10 @@
405405
$lang['api_token'] = 'API Token';
406406
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
407407
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
408+
$lang['service_payment_link'] = 'Payment link';
409+
$lang['service_payment_link_description'] = 'You can include the following variables: {$appointment_hash}, {$customer_email}. In order to confirm the payment use /index.php/payment/confirm/{CHECKOUT_SESSION_ID} url as confirmation page in Stripe';
410+
$lang['appointment_payment_title'] = 'Payment details';
411+
$lang['appointment_payment_text'] = 'Click on the following link to proceed with payment';
412+
$lang['appointment_paymentPaid_text'] = 'El pago se ha realizado satisfactoriamente';
413+
$lang['payment_intent'] = 'Stripe Payment Intent';
408414
// End

application/language/chinese/translations_lang.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,10 @@
405405
$lang['api_token'] = 'API Token';
406406
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
407407
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
408+
$lang['service_payment_link'] = 'Payment link';
409+
$lang['service_payment_link_description'] = 'You can include the following variables: {$appointment_hash}, {$customer_email}. In order to confirm the payment use /index.php/payment/confirm/{CHECKOUT_SESSION_ID} url as confirmation page in Stripe';
410+
$lang['appointment_payment_title'] = 'Payment details';
411+
$lang['appointment_payment_text'] = 'Click on the following link to proceed with payment';
412+
$lang['appointment_paymentPaid_text'] = 'El pago se ha realizado satisfactoriamente';
413+
$lang['payment_intent'] = 'Stripe Payment Intent';
408414
// End

application/language/czech/translations_lang.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,10 @@
405405
$lang['api_token'] = 'API Token';
406406
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
407407
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
408+
$lang['service_payment_link'] = 'Payment link';
409+
$lang['service_payment_link_description'] = 'You can include the following variables: {$appointment_hash}, {$customer_email}. In order to confirm the payment use /index.php/payment/confirm/{CHECKOUT_SESSION_ID} url as confirmation page in Stripe';
410+
$lang['appointment_payment_title'] = 'Payment details';
411+
$lang['appointment_payment_text'] = 'Click on the following link to proceed with payment';
412+
$lang['appointment_paymentPaid_text'] = 'El pago se ha realizado satisfactoriamente';
413+
$lang['payment_intent'] = 'Stripe Payment Intent';
408414
// End

application/language/danish/translations_lang.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,4 +405,10 @@
405405
$lang['api_token'] = 'API Token';
406406
$lang['allow_rescheduling_cancellation_before'] = 'Allow Rescheduling/Cancellation Before';
407407
$lang['at_least_one_field'] = 'At least one field must be displayed in the booking page.';
408+
$lang['service_payment_link'] = 'Payment link';
409+
$lang['service_payment_link_description'] = 'You can include the following variables: {$appointment_hash}, {$customer_email}. In order to confirm the payment use /index.php/payment/confirm/{CHECKOUT_SESSION_ID} url as confirmation page in Stripe';
410+
$lang['appointment_payment_title'] = 'Payment details';
411+
$lang['appointment_payment_text'] = 'Click on the following link to proceed with payment';
412+
$lang['appointment_paymentPaid_text'] = 'El pago se ha realizado satisfactoriamente';
413+
$lang['payment_intent'] = 'Stripe Payment Intent';
408414
// End

0 commit comments

Comments
 (0)