Restaurant Reservations - Version 2.4.12

Version Description

(2022-02-05) = - Updated nonces, sanitizing and escaping - Added a new feature to the Stripe payment/deposit to place a hold on a card. This lets you separate authorization and capture, so you can create a charge now (at the time of booking), but capture the funds later.

Download this release

Release Info

Developer Rustaurius
Plugin Icon 128x128 Restaurant Reservations
Version 2.4.12
Comparing to
See all releases

Code changes from version 2.4.11 to 2.4.12

assets/css/admin.css CHANGED
@@ -247,11 +247,19 @@
247
  background-color: rgba(255,197,7,0.35);
248
  }
249
 
250
- #rtb-bookings-table .striped > tbody > tr.payment_failed .column-status::before {
251
  content: "\f14c";
252
  font-family: dashicons;
253
  padding: 0 5px 0 0;
254
- vertical-align: sub;
 
 
 
 
 
 
 
 
255
  }
256
 
257
  #rtb-bookings-table .striped>tbody>tr.confirmed {
247
  background-color: rgba(255,197,7,0.35);
248
  }
249
 
250
+ #rtb-bookings-table table.bookings > tbody > tr.payment_failed .column-status::before {
251
  content: "\f14c";
252
  font-family: dashicons;
253
  padding: 0 5px 0 0;
254
+ vertical-align: top;
255
+ }
256
+
257
+ #rtb-bookings-table table.bookings > tbody > tr.payment-on-hold .column-status::before {
258
+ content: "\f18c";
259
+ font-family: dashicons;
260
+ padding: 0 5px;
261
+ vertical-align: top;
262
+ display: inline-block;
263
  }
264
 
265
  #rtb-bookings-table .striped>tbody>tr.confirmed {
assets/js/booking-form.js CHANGED
@@ -805,7 +805,7 @@ jQuery(document).ready(function ($) {
805
  var params = {};
806
 
807
  params.action = 'rtb_set_reservation_arrived';
808
- params.nonce = rtb_admin.nonce;
809
  params.booking = {
810
  'ID': booking_id
811
  };
805
  var params = {};
806
 
807
  params.action = 'rtb_set_reservation_arrived';
808
+ params.nonce = rtb_booking_form_js_localize.nonce;
809
  params.booking = {
810
  'ID': booking_id
811
  };
assets/js/stripe-payment.js CHANGED
@@ -96,6 +96,7 @@ jQuery(document).ready(function($) {
96
  var booking_id = $(this).data( 'booking_id' );
97
  // Call your backend to create the Checkout Session
98
  var params = {
 
99
  'action': 'rtb_stripe_get_intent',
100
  'booking_id': booking_id
101
  };
@@ -114,6 +115,7 @@ jQuery(document).ready(function($) {
114
  }
115
  }).then(function(result) {
116
  params = {
 
117
  action: 'rtb_stripe_pmt_succeed',
118
  booking_id: booking_id
119
  };
@@ -128,7 +130,7 @@ jQuery(document).ready(function($) {
128
  var pi = result.paymentIntent;
129
 
130
  // The payment has been processed!
131
- if (pi.status === 'succeeded') {
132
  params['success'] = true;
133
  params['payment_amount'] = pi.amount;
134
  params['payment_id'] = pi.id;
@@ -152,6 +154,10 @@ jQuery(document).ready(function($) {
152
 
153
  window.location = url.href;
154
  }
 
 
 
 
155
  });
156
  });
157
  }
96
  var booking_id = $(this).data( 'booking_id' );
97
  // Call your backend to create the Checkout Session
98
  var params = {
99
+ 'nonce': rtb_stripe_payment.nonce,
100
  'action': 'rtb_stripe_get_intent',
101
  'booking_id': booking_id
102
  };
115
  }
116
  }).then(function(result) {
117
  params = {
118
+ nonce: rtb_stripe_payment.nonce,
119
  action: 'rtb_stripe_pmt_succeed',
120
  booking_id: booking_id
121
  };
130
  var pi = result.paymentIntent;
131
 
132
  // The payment has been processed!
133
+ if (pi.status === 'succeeded' || pi.status === 'requires_capture') {
134
  params['success'] = true;
135
  params['payment_amount'] = pi.amount;
136
  params['payment_id'] = pi.id;
154
 
155
  window.location = url.href;
156
  }
157
+ else {
158
+ error_handler(result.message);
159
+ console.log('RTB-Stripe error: ', result.message);
160
+ }
161
  });
162
  });
163
  }
includes/Booking.class.php CHANGED
@@ -44,7 +44,7 @@ class rtbBooking {
44
  $post = get_post( $post );
45
  }
46
 
47
- if ( get_class( $post ) == 'WP_Post' && $post->post_type == RTB_BOOKING_POST_TYPE ) {
48
  $this->load_wp_post( $post );
49
  return true;
50
  } else {
@@ -919,7 +919,7 @@ class rtbBooking {
919
 
920
  $args = array(
921
  'posts_per_page' => -1,
922
- 'post_status' => array( 'pending', 'payment_pending', 'confirmed', 'arrived' ),
923
  'date_query' => array(
924
  'before' => date( 'c', $before_time ),
925
  'after' => date( 'c', $after_time )
44
  $post = get_post( $post );
45
  }
46
 
47
+ if ( is_object( $post ) && get_class( $post ) == 'WP_Post' && $post->post_type == RTB_BOOKING_POST_TYPE ) {
48
  $this->load_wp_post( $post );
49
  return true;
50
  } else {
919
 
920
  $args = array(
921
  'posts_per_page' => -1,
922
+ 'post_status' => array( 'pending', 'payment_pending', 'confirmed', 'arrived' ),
923
  'date_query' => array(
924
  'before' => date( 'c', $before_time ),
925
  'after' => date( 'c', $after_time )
includes/PaymentGatewayStripe.class.php CHANGED
@@ -13,6 +13,12 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
13
 
14
  private final function __construct() {
15
 
 
 
 
 
 
 
16
  $this->register_hooks();
17
 
18
  }
@@ -57,6 +63,15 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
57
  add_filter( 'rtb_booking_metadata_defaults', [$this, 'default_booking_stripe_info'], 30, 1 );
58
  add_action( 'rtb_booking_load_post_data', [$this, 'populate_booking_stripe_info'], 30, 2 );
59
  add_filter( 'rtb_insert_booking_metadata', [$this, 'save_booking_gateway_info'], 30, 2 );
 
 
 
 
 
 
 
 
 
60
  }
61
 
62
  public function print_payment_form( $booking )
@@ -98,6 +113,7 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
98
  'rtb-stripe-payment',
99
  'rtb_stripe_payment',
100
  array(
 
101
  'stripe_mode' => $_gs( 'rtb-stripe-mode' ),
102
  'stripe_sca' => $SCA,
103
  'live_publishable_key' => $_gs( 'rtb-stripe-live-publishable' ),
@@ -105,10 +121,6 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
105
  )
106
  );
107
 
108
- $payment_amount = $_gs( 'rtb-currency-symbol-location' ) == 'before'
109
- ? $_gs( 'rtb-stripe-currency-symbol' ) . $booking->calculate_deposit()
110
- : $booking->calculate_deposit() . $_gs( 'rtb-stripe-currency-symbol' );
111
-
112
  $cc_exp_single_field = null != $_gs( 'rtb-expiration-field-single' )
113
  ? "<input type='text' data-stripe='exp_month_year' class='single-masked'>"
114
  : "<input type='text' size='2' data-stripe='exp_month'>
@@ -116,17 +128,13 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
116
  <input type='text' size='4' data-stripe='exp_year'>";
117
  ?>
118
 
119
- <h2>
120
- <?php echo __('Deposit Required: ', 'restaurant-reservations' ) . esc_html( $payment_amount ); ?>
121
- </h2>
122
-
123
  <div class='payment-errors'></div>
124
 
125
  <form
126
  action='#'
127
  method='POST'
128
  id='stripe-payment-form'
129
- data-booking_id='<?php echo $booking->ID ;?>'>
130
 
131
  <?php if( $SCA ) { ?>
132
 
@@ -150,9 +158,9 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
150
  <?php echo $cc_exp_single_field; ?>
151
  </div>
152
  <input type='hidden' name='action' value='rtb_stripe_booking_payment'/>
153
- <input type='hidden' name='currency' value='<?php echo $_gs( 'rtb-currency' ); ?>' data-stripe='currency' />
154
- <input type='hidden' name='payment_amount' value='<?php echo $booking->calculate_deposit(); ?>' />
155
- <input type='hidden' name='booking_id' value='<?php echo $booking->ID; ?>' />
156
 
157
  <?php } ?>
158
 
@@ -183,7 +191,7 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
183
 
184
  // Function alias
185
  $_gs = [$rtb_controller->settings, 'get_setting'];
186
- $booking_id = $_POST['booking_id'];
187
 
188
  // Define the form's action parameter
189
  $booking_page = $_gs( 'booking-page' );
@@ -198,26 +206,32 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
198
  require_once(RTB_PLUGIN_DIR . '/lib/stripe/init.php');
199
 
200
  // retrieve the token generated by stripe.js
201
- $token = $_POST['stripeToken'];
202
 
203
  // JPY currency does not have any decimal palces
204
  $is_JPY = "JPY" != $_gs( 'rtb-currency' );
205
- $payment_amount = $is_JPY ? ( $_POST['payment_amount'] * 100 ) : $_POST['payment_amount'];
206
-
207
- $stripe_secret = 'test' == $_gs( 'rtb-stripe-mode' )
208
- ? $_gs( 'rtb-stripe-test-secret' )
209
- : $_gs( 'rtb-stripe-live-secret' );
210
 
211
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
212
  $booking = new rtbBooking();
213
  $booking->load_post( $booking_id );
214
 
215
  try {
216
- \Stripe\Stripe::setApiKey( $stripe_secret );
 
 
 
 
 
 
 
 
 
217
  $charge = \Stripe\Charge::create(array(
218
- 'amount' => $payment_amount,
219
- 'currency' => strtolower( $_gs( 'rtb-currency' ) ),
220
- 'card' => $token
 
221
  )
222
  );
223
 
@@ -285,13 +299,17 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
285
  exit(0);
286
  };
287
 
 
 
 
 
288
  if( ! isset( $_POST['booking_id'] ) ) {
289
  $response(false, 'Invalid booking.');
290
  }
291
 
292
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
293
  $booking = new rtbBooking();
294
- $booking->load_post( $_POST['booking_id'] );
295
 
296
  $payment_amount = "JPY" != $rtb_controller->settings->get_setting( 'rtb-currency' )
297
  ? $booking->calculate_deposit() * 100
@@ -300,12 +318,8 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
300
  // load the stripe libraries
301
  require_once(RTB_PLUGIN_DIR . '/lib/stripe/init.php');
302
 
303
- $stripe_secret = 'test' == $rtb_controller->settings->get_setting( 'rtb-stripe-mode' )
304
- ? $rtb_controller->settings->get_setting( 'rtb-stripe-test-secret' )
305
- : $rtb_controller->settings->get_setting( 'rtb-stripe-live-secret' );
306
-
307
  try {
308
- \Stripe\Stripe::setApiKey( $stripe_secret );
309
 
310
  // $customer = \Stripe\Customer::create([
311
  // 'email' => $booking->email,
@@ -330,7 +344,7 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
330
  $desc = implode(', ', $metadata );
331
  $stmt_desc = substr( implode( ';', $metadata ), 0, 22 );
332
 
333
- $intent = \Stripe\PaymentIntent::create([
334
  'amount' => $payment_amount,
335
  'currency' => $rtb_controller->settings->get_setting( 'rtb-currency' ),
336
  'payment_method_types' => ['card'],
@@ -338,7 +352,22 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
338
  'description' => apply_filters( 'rtb-stripe-payment-desc', $desc ),
339
  'statement_descriptor' => apply_filters( 'rtb-stripe-payment-stmnt-desc', $stmt_desc ),
340
  'metadata' => $metadata
341
- ]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
  $response(
344
  true,
@@ -365,17 +394,28 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
365
  {
366
  global $rtb_controller;
367
 
368
- $response = function ($success = false, $urlParams = '') {
369
- echo json_encode([
370
- 'success' => $success,
371
- 'urlParams' => $urlParams
372
- ]);
 
 
 
 
 
 
 
 
 
373
 
374
  exit(0);
375
  };
376
 
 
377
  $success = false;
378
  $urlParams = '';
 
379
 
380
  if( ! isset( $_POST['booking_id'] ) ) {
381
  $response();
@@ -383,34 +423,61 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
383
 
384
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
385
  $booking = new rtbBooking();
386
- $booking->load_post( $_POST['booking_id'] );
387
 
388
- if( isset( $_POST['success'] ) && 'false' != $_POST['success'] ) {
389
 
390
- $booking->deposit = "JPY" != $rtb_controller->settings->get_setting( 'rtb-currency' )
391
- ? $_POST['payment_amount'] / 100
392
- : $_POST['payment_amount'];
393
 
394
- $booking->receipt_id = $_POST['payment_id'];
395
- $booking->payment_paid();
396
 
397
- // urlParams on successful payment
398
- $success = true;
 
399
 
400
- $urlParams = array(
401
- 'payment' => 'paid',
402
- 'booking_id' => $booking->ID
403
- );
 
 
 
 
 
 
 
 
404
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  }
406
- else {
407
- $payment_failure_message = ! empty( $_POST['message'] )
408
- ? $_POST['message'] : '';
409
 
410
- $booking->payment_failed( $payment_failure_message );
 
411
  }
412
 
413
- $response($success, $urlParams);
 
 
 
 
 
 
 
 
 
 
414
  }
415
 
416
  /**
@@ -419,13 +486,23 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
419
  * @param rtbBooking $booking
420
  * @param WP_Post $wp_post
421
  */
422
- public function populate_booking_stripe_info( rtbBooking $booking, $wp_post )
423
  {
424
- if ( is_array( $meta = get_post_meta( $booking->ID, 'rtb', true ) ) ) {
425
- $booking->stripe_customer_id = isset( $meta['stripe_customer_id'] )
426
- ? $meta['stripe_customer_id']
427
- : '';
 
 
 
 
 
 
 
 
428
  }
 
 
429
  }
430
 
431
  /**
@@ -441,14 +518,184 @@ class rtbPaymentGatewayStripe implements rtbPaymentGateway {
441
  return $info_list;
442
  }
443
 
444
- public function save_booking_gateway_info ( $meta, rtbBooking $booking )
445
  {
446
  if ( isset( $booking->stripe_customer_id ) && !empty( $booking->stripe_customer_id ) ) {
447
- $meta['stripe_customer_id'] = $this->stripe_customer_id;
 
 
 
 
 
 
 
 
448
  }
449
 
450
  return $meta;
451
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  }
453
 
454
  }
13
 
14
  private final function __construct() {
15
 
16
+ $this->HOLD = [
17
+ 'not-placed' => __( 'Not Placed', 'restaurant-reservations' ),
18
+ 'hold-placed' => __( 'Hold Placed', 'restaurant-reservations' ),
19
+ 'hold-captured' => __( 'Hold Captured', 'restaurant-reservations' )
20
+ ];
21
+
22
  $this->register_hooks();
23
 
24
  }
63
  add_filter( 'rtb_booking_metadata_defaults', [$this, 'default_booking_stripe_info'], 30, 1 );
64
  add_action( 'rtb_booking_load_post_data', [$this, 'populate_booking_stripe_info'], 30, 2 );
65
  add_filter( 'rtb_insert_booking_metadata', [$this, 'save_booking_gateway_info'], 30, 2 );
66
+
67
+ /**
68
+ * Adding info and capability to cahrge the hold manually in the bookings table for admin
69
+ */
70
+ add_filter( 'rtb_admin_bookings_list_row_classes', [$this, 'add_hold_class'], 30, 2 );
71
+ add_filter( 'rtb_bookings_table_column_details', [$this, 'add_hold_detail'], 30, 2 );
72
+ add_filter( 'rtb_bookings_table_bulk_actions', [$this, 'add_bulk_action'], 30, 1 );
73
+ add_filter( 'rtb_bookings_table_bulk_action', [$this, 'charge_the_hold'], 30, 3 );
74
+ add_action( 'rtb_payment_summary', [$this, 'payment_summary'] );
75
  }
76
 
77
  public function print_payment_form( $booking )
113
  'rtb-stripe-payment',
114
  'rtb_stripe_payment',
115
  array(
116
+ 'nonce' => wp_create_nonce( 'rtb-stripe-payment' ),
117
  'stripe_mode' => $_gs( 'rtb-stripe-mode' ),
118
  'stripe_sca' => $SCA,
119
  'live_publishable_key' => $_gs( 'rtb-stripe-live-publishable' ),
121
  )
122
  );
123
 
 
 
 
 
124
  $cc_exp_single_field = null != $_gs( 'rtb-expiration-field-single' )
125
  ? "<input type='text' data-stripe='exp_month_year' class='single-masked'>"
126
  : "<input type='text' size='2' data-stripe='exp_month'>
128
  <input type='text' size='4' data-stripe='exp_year'>";
129
  ?>
130
 
 
 
 
 
131
  <div class='payment-errors'></div>
132
 
133
  <form
134
  action='#'
135
  method='POST'
136
  id='stripe-payment-form'
137
+ data-booking_id='<?php echo esc_attr( $booking->ID ) ;?>'>
138
 
139
  <?php if( $SCA ) { ?>
140
 
158
  <?php echo $cc_exp_single_field; ?>
159
  </div>
160
  <input type='hidden' name='action' value='rtb_stripe_booking_payment'/>
161
+ <input type='hidden' name='currency' value='<?php echo esc_attr( $_gs( 'rtb-currency' ) ); ?>' data-stripe='currency' />
162
+ <input type='hidden' name='payment_amount' value='<?php echo esc_attr( $booking->calculate_deposit() ); ?>' />
163
+ <input type='hidden' name='booking_id' value='<?php echo esc_attr( $booking->ID ); ?>' />
164
 
165
  <?php } ?>
166
 
191
 
192
  // Function alias
193
  $_gs = [$rtb_controller->settings, 'get_setting'];
194
+ $booking_id = intval( $_POST['booking_id'] );
195
 
196
  // Define the form's action parameter
197
  $booking_page = $_gs( 'booking-page' );
206
  require_once(RTB_PLUGIN_DIR . '/lib/stripe/init.php');
207
 
208
  // retrieve the token generated by stripe.js
209
+ $token = sanitize_text_field( $_POST['stripeToken'] );
210
 
211
  // JPY currency does not have any decimal palces
212
  $is_JPY = "JPY" != $_gs( 'rtb-currency' );
213
+ $payment_amount = $is_JPY ? ( intval( $_POST['payment_amount'] ) * 100 ) : intval( $_POST['payment_amount'] );
 
 
 
 
214
 
215
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
216
  $booking = new rtbBooking();
217
  $booking->load_post( $booking_id );
218
 
219
  try {
220
+
221
+ $metadata = array_filter([
222
+ 'Booking ID' => $booking->ID,
223
+ 'Email' => $booking->email,
224
+ 'Name' => $booking->name,
225
+ 'Date' => $booking->date,
226
+ 'Party' => $booking->party
227
+ ]);
228
+
229
+ \Stripe\Stripe::setApiKey( $this->get_secret() );
230
  $charge = \Stripe\Charge::create(array(
231
+ 'amount' => $payment_amount,
232
+ 'currency' => strtolower( $_gs( 'rtb-currency' ) ),
233
+ 'card' => $token,
234
+ 'metadata' => $metadata
235
  )
236
  );
237
 
299
  exit(0);
300
  };
301
 
302
+ if ( !check_ajax_referer( 'rtb-stripe-payment', 'nonce' ) ) {
303
+ $response(false, __( 'The request has been rejected because it does not appear to have come from this site.', 'restaurant-reservations' ) );
304
+ }
305
+
306
  if( ! isset( $_POST['booking_id'] ) ) {
307
  $response(false, 'Invalid booking.');
308
  }
309
 
310
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
311
  $booking = new rtbBooking();
312
+ $booking->load_post( intval( $_POST['booking_id'] ) );
313
 
314
  $payment_amount = "JPY" != $rtb_controller->settings->get_setting( 'rtb-currency' )
315
  ? $booking->calculate_deposit() * 100
318
  // load the stripe libraries
319
  require_once(RTB_PLUGIN_DIR . '/lib/stripe/init.php');
320
 
 
 
 
 
321
  try {
322
+ \Stripe\Stripe::setApiKey( $this->get_secret() );
323
 
324
  // $customer = \Stripe\Customer::create([
325
  // 'email' => $booking->email,
344
  $desc = implode(', ', $metadata );
345
  $stmt_desc = substr( implode( ';', $metadata ), 0, 22 );
346
 
347
+ $intentData = [
348
  'amount' => $payment_amount,
349
  'currency' => $rtb_controller->settings->get_setting( 'rtb-currency' ),
350
  'payment_method_types' => ['card'],
352
  'description' => apply_filters( 'rtb-stripe-payment-desc', $desc ),
353
  'statement_descriptor' => apply_filters( 'rtb-stripe-payment-stmnt-desc', $stmt_desc ),
354
  'metadata' => $metadata
355
+ ];
356
+
357
+ if( $rtb_controller->settings->get_setting( 'rtb-stripe-hold' ) ) {
358
+ $intentData['capture_method'] = 'manual';
359
+ }
360
+
361
+ $intent = \Stripe\PaymentIntent::create( $intentData );
362
+
363
+ // Used this for verification of two step payment processing under SCA
364
+ $booking->stripe_payment_intent_id = $intent->id;
365
+
366
+ $booking->stripe_payment_hold_status = 'not-placed';
367
+ if( $rtb_controller->settings->get_setting( 'rtb-stripe-hold' ) ) {
368
+ $booking->stripe_payment_hold_status = 'hold-placed';
369
+ }
370
+ $booking->insert_post_data();
371
 
372
  $response(
373
  true,
394
  {
395
  global $rtb_controller;
396
 
397
+ if ( !check_ajax_referer( 'rtb-stripe-payment', 'nonce' ) ) {
398
+ $response(false, __( 'The request has been rejected because it does not appear to have come from this site.', 'restaurant-reservations' ) );
399
+ }
400
+
401
+ $response = function ($success, $urlParams, $data = []) {
402
+ echo json_encode(
403
+ array_merge(
404
+ [
405
+ 'success' => $success,
406
+ 'urlParams' => $urlParams
407
+ ],
408
+ $data
409
+ )
410
+ );
411
 
412
  exit(0);
413
  };
414
 
415
+ // Response variables with fallback defaults
416
  $success = false;
417
  $urlParams = '';
418
+ $data = [];
419
 
420
  if( ! isset( $_POST['booking_id'] ) ) {
421
  $response();
423
 
424
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
425
  $booking = new rtbBooking();
426
+ $loaded = $booking->load_post( intval( $_POST['booking_id'] ) );
427
 
 
428
 
429
+ try {
 
 
430
 
431
+ if( isset( $_POST['success'] ) && 'false' != sanitize_text_field( $_POST['success'] ) ) {
 
432
 
433
+ if( ! $loaded || ! $this->valid_payment( $booking ) ) {
434
+ throw new Exception( __( 'Invalid submission. Please contact admin', 'restaurant-reservations' ) );
435
+ }
436
 
437
+ $booking->deposit = "JPY" != $rtb_controller->settings->get_setting( 'rtb-currency' )
438
+ ? intval( $_POST['payment_amount'] ) / 100
439
+ : intval( $_POST['payment_amount'] );
440
+
441
+ $booking->receipt_id = sanitize_text_field( $_POST['payment_id'] );
442
+
443
+ // Not needed anymore
444
+ unset( $booking->stripe_payment_intent_id );
445
+ $booking->payment_paid();
446
+
447
+ // urlParams on successful payment
448
+ $success = true;
449
 
450
+ $urlParams = array(
451
+ 'payment' => 'paid',
452
+ 'booking_id' => intval( $booking->ID )
453
+ );
454
+
455
+ }
456
+ else {
457
+ $payment_failure_message = ! empty( $_POST['message'] )
458
+ ? sanitize_text_field( $_POST['message'] )
459
+ : __( 'Payment charge failed. Please try again', 'restaurant-reservations' );
460
+
461
+ throw new Exception( $payment_failure_message );
462
+ }
463
  }
464
+ catch(Exception $ex) {
 
 
465
 
466
+ $loaded && $booking->payment_failed( $ex->getMessage() );
467
+ $data['message'] = $ex->getMessage();
468
  }
469
 
470
+ $response($success, $urlParams, $data);
471
+ }
472
+
473
+ /**
474
+ * Validate the payment success request by verifing the payment_intent ID
475
+ *
476
+ * @return bool true on valid else false
477
+ */
478
+ public function valid_payment( $booking )
479
+ {
480
+ return sanitize_text_field( $_POST['payment_id'] ) == $booking->stripe_payment_intent_id;
481
  }
482
 
483
  /**
486
  * @param rtbBooking $booking
487
  * @param WP_Post $wp_post
488
  */
489
+ public function populate_booking_stripe_info( $booking, $wp_post )
490
  {
491
+ $meta = get_post_meta( $booking->ID, 'rtb', true );
492
+
493
+ if ( is_array( $meta ) && isset( $meta['stripe_customer_id'] ) ) {
494
+ $booking->stripe_customer_id = $meta['stripe_customer_id'];
495
+ }
496
+
497
+ if ( is_array( $meta ) && isset( $meta['stripe_payment_intent_id'] ) ) {
498
+ $booking->stripe_payment_intent_id = $meta['stripe_payment_intent_id'];
499
+ }
500
+
501
+ if ( is_array( $meta ) && isset( $meta['stripe_payment_hold_status'] ) ) {
502
+ $booking->stripe_payment_hold_status = $meta['stripe_payment_hold_status'];
503
  }
504
+
505
+
506
  }
507
 
508
  /**
518
  return $info_list;
519
  }
520
 
521
+ public function save_booking_gateway_info ( $meta, $booking )
522
  {
523
  if ( isset( $booking->stripe_customer_id ) && !empty( $booking->stripe_customer_id ) ) {
524
+ $meta['stripe_customer_id'] = $booking->stripe_customer_id;
525
+ }
526
+
527
+ if ( isset( $booking->stripe_payment_intent_id ) && !empty( $booking->stripe_payment_intent_id ) ) {
528
+ $meta['stripe_payment_intent_id'] = $booking->stripe_payment_intent_id;
529
+ }
530
+
531
+ if ( isset( $booking->stripe_payment_hold_status ) ) {
532
+ $meta['stripe_payment_hold_status'] = $booking->stripe_payment_hold_status;
533
  }
534
 
535
  return $meta;
536
  }
537
+
538
+ /**
539
+ * Add the CSS class to admin booking listing
540
+ * @param array $row_class_list css classes for the row
541
+ * @param rtbBooking $booking
542
+ */
543
+ public function add_hold_class($row_class_list, $booking)
544
+ {
545
+ if( $this->is_payment_on_hold($booking) ) {
546
+ $row_class_list[] = 'payment-on-hold';
547
+ }
548
+ return $row_class_list;
549
+ }
550
+
551
+ /**
552
+ * Add hold information in the details popup og the booking for admin
553
+ * @param array $details Lable/value item array
554
+ * @param rtbBooking $booking
555
+ */
556
+ public function add_hold_detail($details, $booking)
557
+ {
558
+ if ( $this->is_payment_on_hold($booking) ) {
559
+ $details[] = array(
560
+ 'label' => __('Payment on Hold', 'restaurant-reservations'),
561
+ 'value' => __('Payment has been held on the card, but not charged yet.', 'restaurant-reservations')
562
+ );
563
+ }
564
+ else if ( $this->is_payment_hold_captured($booking) ) {
565
+ $details[] = array(
566
+ 'label' => __('Held Payment Captured', 'restaurant-reservations'),
567
+ 'value' => __('Payment has been captured.', 'restaurant-reservations')
568
+ );
569
+ }
570
+
571
+ return $details;
572
+ }
573
+
574
+ /**
575
+ * Add Bulk action to booking page for admin to charge the hold manually
576
+ * @param array $actions
577
+ */
578
+ public function add_bulk_action($actions)
579
+ {
580
+ $actions['capture-payment'] = __( 'Charge Payment on Hold', 'restaurant-reservations' );
581
+
582
+ return $actions;
583
+ }
584
+
585
+ /**
586
+ * Complete the hold and charge the customer
587
+ * @param array $result Array with booking ID as key and value as message
588
+ * @param int $id booking-id
589
+ * @param string $action bulk action
590
+ * @return $result result array with result if we processed the booking
591
+ */
592
+ public function charge_the_hold($result, $id, $action)
593
+ {
594
+ if ( 'capture-payment' !== $action ) {
595
+ return $result;
596
+ }
597
+
598
+ require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
599
+ $booking = new rtbBooking();
600
+ if( $booking->load_post( intval( $id ) ) ) {
601
+ if( $this->is_payment_on_hold( $booking ) ) {
602
+
603
+ try {
604
+ // load the stripe libraries
605
+ require_once(RTB_PLUGIN_DIR . '/lib/stripe/init.php');
606
+
607
+ \Stripe\Stripe::setApiKey( $this->get_secret() );
608
+
609
+ $intent = \Stripe\PaymentIntent::retrieve( $booking->receipt_id );
610
+ $intent->capture();
611
+
612
+ if( 'succeeded' == $intent->status ) {
613
+ $this->hold_captured( $booking );
614
+ // Payment has been captured successfully
615
+ $result[$id] = true;
616
+ }
617
+ else {
618
+ $result[$id] = false;
619
+ if( defined('WP_DEBUG') and WP_DEBUG ) {
620
+ error_log(sprintf( __( 'Five Star RTB: Stripe Payment capture failed. Reason: %s', 'restaurant-reservations' ), $intent->status ));
621
+ }
622
+ }
623
+ }
624
+ catch(Exception $ex) {
625
+ $result[$id] = false;
626
+ if( defined('WP_DEBUG') and WP_DEBUG ) {
627
+ error_log( sprintf( __( 'Five Star RTB: Stripe Payment capture failed. Reason: %s', 'restaurant-reservations' ), $ex->getMessage() ) );
628
+ }
629
+ }
630
+ }
631
+ else {
632
+ // We do not have a hold for this Booking
633
+ // $result[$id] = true;
634
+ }
635
+ }
636
+ else {
637
+ $result[$id] = false;
638
+ if( defined('WP_DEBUG') and WP_DEBUG ) {
639
+ error_log( sprintf( __( 'Unable to find the Booking for ID %s', 'restaurant-reservations' ), $id ) );
640
+ }
641
+ }
642
+
643
+ return $result;
644
+ }
645
+
646
+ /**
647
+ * Check whether the payment is on hold or not
648
+ * @param rtbBooking $booking
649
+ * @return boolean
650
+ */
651
+ public function is_payment_on_hold($booking)
652
+ {
653
+ return isset( $booking->stripe_payment_hold_status ) && 'hold-placed' == $booking->stripe_payment_hold_status;
654
+ }
655
+
656
+ /**
657
+ * Check whether the payment is on hold or not
658
+ * @param rtbBooking $booking
659
+ * @return boolean
660
+ */
661
+ public function is_payment_hold_captured($booking)
662
+ {
663
+ return isset( $booking->stripe_payment_hold_status ) && 'hold-captured' == $booking->stripe_payment_hold_status;
664
+ }
665
+
666
+ /**
667
+ * Mark the Payment Hold for the booking as Captured
668
+ * @param rtbBooking $booking
669
+ * @return rtbPaymentGatewayStripe
670
+ */
671
+ public function hold_captured($booking)
672
+ {
673
+ $booking->stripe_payment_hold_status = 'hold-captured';
674
+ $booking->insert_post_data();
675
+
676
+ return $this;
677
+ }
678
+
679
+ /**
680
+ * Get Stripe secret
681
+ * @return string
682
+ */
683
+ public function get_secret()
684
+ {
685
+ global $rtb_controller;
686
+
687
+ return 'test' == $rtb_controller->settings->get_setting( 'rtb-stripe-mode' )
688
+ ? $rtb_controller->settings->get_setting( 'rtb-stripe-test-secret' )
689
+ : $rtb_controller->settings->get_setting( 'rtb-stripe-live-secret' );
690
+ }
691
+
692
+ public function payment_summary()
693
+ {
694
+ global $rtb_controller;
695
+ if( $rtb_controller->settings->get_setting( 'rtb-stripe-hold' ) ) {
696
+ echo '<p class="stripe-payment-hold-msg">' . __( 'We are only placing a hold for the above amount on your payment instrument. You will be charged later.', 'restaurant-reservations' ) . '</p>';
697
+ }
698
+ }
699
  }
700
 
701
  }
includes/PaymentManager.class.php CHANGED
@@ -285,6 +285,35 @@ class rtbPaymentManager {
285
  }
286
  }
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  /**
289
  * Print the payment form's HTML code, after a new booking has been inserted
290
  * notices.
285
  }
286
  }
287
 
288
+ /**
289
+ * Display booking summary on Payment Deposit Page
290
+ *
291
+ * @return rtbPaymentManager
292
+ */
293
+ public function print_payment_summary()
294
+ {
295
+ global $rtb_controller;
296
+
297
+ // Function alias
298
+ $_gs = [$rtb_controller->settings, 'get_setting'];
299
+
300
+ $payment_amount = $_gs( 'rtb-currency-symbol-location' ) == 'before'
301
+ ? $_gs( 'rtb-stripe-currency-symbol' ) . $this->booking->calculate_deposit()
302
+ : $this->booking->calculate_deposit() . $_gs( 'rtb-stripe-currency-symbol' );
303
+ ?>
304
+
305
+ <h2>
306
+ <?php echo __( 'Deposit Required: ', 'restaurant-reservations' ) . esc_html( $payment_amount ); ?>
307
+ </h2>
308
+ <?php
309
+
310
+ do_action( 'rtb_payment_summary' );
311
+
312
+ // TODO: Add a filter with label/value paris to disaply the booking summary here
313
+
314
+ return $this;
315
+ }
316
+
317
  /**
318
  * Print the payment form's HTML code, after a new booking has been inserted
319
  * notices.
includes/Settings.class.php CHANGED
@@ -429,7 +429,8 @@ class rtbSettings {
429
  $this->premium_permissions['payments'] = array(
430
  'disabled' => true,
431
  'disabled_image'=> '#',
432
- 'purchase_link' => 'https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/'
 
433
  );
434
  }
435
 
@@ -2412,6 +2413,18 @@ If you were not the one to cancel this booking, please contact us.
2412
  'description' => __( 'User will be redirected to Stripe and presented with 3D secure or bank redirect for payment authentication. (May be necessary for certain EU compliance.)', 'restaurant-reservations' )
2413
  )
2414
  );
 
 
 
 
 
 
 
 
 
 
 
 
2415
  $sap->add_setting(
2416
  'rtb-settings',
2417
  'rtb-stripe-payment',
429
  $this->premium_permissions['payments'] = array(
430
  'disabled' => true,
431
  'disabled_image'=> '#',
432
+ 'purchase_link' => 'https://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/',
433
+ 'ultimate_needed' => 'Yes'
434
  );
435
  }
436
 
2413
  'description' => __( 'User will be redirected to Stripe and presented with 3D secure or bank redirect for payment authentication. (May be necessary for certain EU compliance.)', 'restaurant-reservations' )
2414
  )
2415
  );
2416
+ $sap->add_setting(
2417
+ 'rtb-settings',
2418
+ 'rtb-stripe-payment',
2419
+ 'toggle',
2420
+ array(
2421
+ 'id' => 'rtb-stripe-hold',
2422
+ 'title' => __( 'Hold & Charge Separately', 'restaurant-reservations' ),
2423
+ 'description' => __( 'With this enabled, the deposit will be taken as a hold and not charged right away. The payment can then be charged/captured manually later. If not captured, the hold on the amount will be released after 7 days.', 'restaurant-reservations' ),
2424
+ 'conditional_on' => 'rtb-stripe-sca',
2425
+ 'conditional_on_value' => true
2426
+ )
2427
+ );
2428
  $sap->add_setting(
2429
  'rtb-settings',
2430
  'rtb-stripe-payment',
includes/template-functions.php CHANGED
@@ -174,12 +174,15 @@ function rtb_print_booking_form( $args = array() )
174
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
175
  $booking = new rtbBooking();
176
  $booking->load_post( $booking_id );
177
-
 
178
  $rtb_controller
179
  ->payment_manager
180
  ->set_booking( $booking )
 
181
  ->print_payment_form();
182
-
 
183
  elseif ( isset($_GET['bookingCancelled']) and $_GET['bookingCancelled'] == 'success') : ?>
184
  <div class="rtb-message">
185
  <p><?php _e( 'Your reservation has been successfully cancelled.', 'restaurant-reservations' ) ?></p>
174
  require_once( RTB_PLUGIN_DIR . '/includes/Booking.class.php' );
175
  $booking = new rtbBooking();
176
  $booking->load_post( $booking_id );
177
+
178
+ ?> <div class="booking-payment-wrapper"> <?php
179
  $rtb_controller
180
  ->payment_manager
181
  ->set_booking( $booking )
182
+ ->print_payment_summary()
183
  ->print_payment_form();
184
+ ?> <div class="booking-payment-wrapper"> <?php
185
+
186
  elseif ( isset($_GET['bookingCancelled']) and $_GET['bookingCancelled'] == 'success') : ?>
187
  <div class="rtb-message">
188
  <p><?php _e( 'Your reservation has been successfully cancelled.', 'restaurant-reservations' ) ?></p>
lib/simple-admin-pages/classes/AdminPageSection.class.php CHANGED
@@ -135,7 +135,7 @@ class sapAdminPageSection_2_6_1 {
135
  <?php echo ( isset($this->purchase_link ) ? "<div class='sap-premium-options-table-overlay'>" : '' ); ?>
136
  <div class="section-disabled">
137
  <img src="<?php echo plugins_url( '../img/options-asset-lock.png', __FILE__ ); ?>" alt="Upgrade to Premium">
138
- <p>Access this section by upgrading to premium</p>
139
  <a href="<?php echo $this->purchase_link; ?>" class="sap-dashboard-get-premium-widget-button" target="_blank">UPGRADE NOW</a>
140
  </div>
141
  <?php echo ( isset( $this->purchase_link ) ? "</div>" : '' ); ?>
135
  <?php echo ( isset($this->purchase_link ) ? "<div class='sap-premium-options-table-overlay'>" : '' ); ?>
136
  <div class="section-disabled">
137
  <img src="<?php echo plugins_url( '../img/options-asset-lock.png', __FILE__ ); ?>" alt="Upgrade to Premium">
138
+ <p>Access this section by upgrading to <?php echo ( isset( $this->ultimate_needed ) ? 'ultimate' : 'premium' ); ?></p>
139
  <a href="<?php echo $this->purchase_link; ?>" class="sap-dashboard-get-premium-widget-button" target="_blank">UPGRADE NOW</a>
140
  </div>
141
  <?php echo ( isset( $this->purchase_link ) ? "</div>" : '' ); ?>
readme.txt CHANGED
@@ -1,9 +1,9 @@
1
  === Five Star Restaurant Reservations - WordPress Booking Plugin ===
2
  Contributors: FiveStarPlugins
3
  Requires at Least: 4.4
4
- Tested Up To: 5.8
5
  Tags: reservation, reservations, restaurant reservations, reservation form, restaurant booking, restaurant reservation form, restaurant booking form, restaurant booking system, reservation system, online reservations, online restaurant booking, dinner reservations, restaurant form, gutenberg reservations, gutenberg restaurant reservations, gutenberg restaurant booking, mobile reservations, responsive reservations, table reservations, open table, book table, reserve table, easy reservations, simple reservations, quick restaurant reservations, custom reservation form, custom restaurant reservations
6
- Stable tag: 2.4.11
7
  License: GPLv3
8
  License URI:http://www.gnu.org/licenses/gpl-3.0.html
9
  Donate Link: https://www.etoilewebdesign.com/plugin-donations/
@@ -197,6 +197,10 @@ Find answers to even more questions in the [FAQ](http://doc.fivestarplugins.com/
197
 
198
  == Changelog ==
199
 
 
 
 
 
200
  = 2.4.11 (2022-01-18) =
201
  - Added a setting to specify a number of days after which booking data will be deleted.
202
  - Fixed an issue with the cancel link feature in customer email notifications.
1
  === Five Star Restaurant Reservations - WordPress Booking Plugin ===
2
  Contributors: FiveStarPlugins
3
  Requires at Least: 4.4
4
+ Tested Up To: 5.9
5
  Tags: reservation, reservations, restaurant reservations, reservation form, restaurant booking, restaurant reservation form, restaurant booking form, restaurant booking system, reservation system, online reservations, online restaurant booking, dinner reservations, restaurant form, gutenberg reservations, gutenberg restaurant reservations, gutenberg restaurant booking, mobile reservations, responsive reservations, table reservations, open table, book table, reserve table, easy reservations, simple reservations, quick restaurant reservations, custom reservation form, custom restaurant reservations
6
+ Stable tag: 2.4.12
7
  License: GPLv3
8
  License URI:http://www.gnu.org/licenses/gpl-3.0.html
9
  Donate Link: https://www.etoilewebdesign.com/plugin-donations/
197
 
198
  == Changelog ==
199
 
200
+ = 2.4.12 (2022-02-05) =
201
+ - Updated nonces, sanitizing and escaping
202
+ - Added a new feature to the Stripe payment/deposit to place a hold on a card. This lets you separate authorization and capture, so you can create a charge now (at the time of booking), but capture the funds later.
203
+
204
  = 2.4.11 (2022-01-18) =
205
  - Added a setting to specify a number of days after which booking data will be deleted.
206
  - Fixed an issue with the cancel link feature in customer email notifications.
restaurant-reservations.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: Five Star Restaurant Reservations - WordPress Booking Plugin
4
  * Plugin URI: http://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/
5
  * Description: Restaurant reservations made easy. Accept bookings online. Quickly confirm or reject reservations, send email notifications, set booking times and more.
6
- * Version: 2.4.11
7
  * Author: FiveStarPlugins
8
  * Author URI: https://profiles.wordpress.org/fivestarplugins/
9
  * Text Domain: restaurant-reservations
@@ -39,7 +39,7 @@ class rtbInit {
39
  public function __construct() {
40
 
41
  // Common strings
42
- define( 'RTB_VERSION', '2.4.11' );
43
  define( 'RTB_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
44
  define( 'RTB_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
45
  define( 'RTB_PLUGIN_FNAME', plugin_basename( __FILE__ ) );
3
  * Plugin Name: Five Star Restaurant Reservations - WordPress Booking Plugin
4
  * Plugin URI: http://www.fivestarplugins.com/plugins/five-star-restaurant-reservations/
5
  * Description: Restaurant reservations made easy. Accept bookings online. Quickly confirm or reject reservations, send email notifications, set booking times and more.
6
+ * Version: 2.4.12
7
  * Author: FiveStarPlugins
8
  * Author URI: https://profiles.wordpress.org/fivestarplugins/
9
  * Text Domain: restaurant-reservations
39
  public function __construct() {
40
 
41
  // Common strings
42
+ define( 'RTB_VERSION', '2.4.12' );
43
  define( 'RTB_PLUGIN_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
44
  define( 'RTB_PLUGIN_URL', untrailingslashit( plugins_url( basename( plugin_dir_path( __FILE__ ) ), basename( __FILE__ ) ) ) );
45
  define( 'RTB_PLUGIN_FNAME', plugin_basename( __FILE__ ) );