PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.38
Timetics – Appointment Booking Calendar & Scheduling v1.0.38
1.0.62 1.0.63 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 All 64 releases
← All changes | core/integrations/stripe/api-stripe.php +6 -101 1.0.621.0.38 View file →
@@ -5,12 +5,9 @@
5 5 * @package Timetics
6 6 */
7 7 namespace Timetics\Core\Integrations\Stripe;
8 8
9 -defined( 'ABSPATH' ) || exit;
10 -
11 9 use Timetics\Base\Api;
12 -use Timetics\Core\Bookings\Booking;
13 10 use Timetics\Utils\Singleton;
14 11 use WP_HTTP_Response;
15 12
16 13 /**
@@ -64,91 +61,23 @@
64 61 *
65 62 * @return JSON
66 63 */
67 64 public function create_payment( $request ) {
68 - if ( $this->is_rate_limited() ) {
69 - return new WP_HTTP_Response(
70 - [
71 - 'success' => 0,
72 - 'status_code' => 429,
73 - 'message' => __( 'Too many requests. Please try again later.', 'timetics' ),
74 - ],
75 - 429
76 - );
77 - }
78 -
79 65 $data = json_decode( $request->get_body(), true );
80 66
81 - $amount = ! empty( $data['amount'] ) ? floatval( $data['amount'] ) : 0;
82 - $currency = ! empty( $data['currency'] ) ? sanitize_text_field( $data['currency'] ) : '';
83 - $booking_id = ! empty( $data['booking_id'] ) ? absint( $data['booking_id'] ) : 0;
84 - $token = ! empty( $data['security_token'] ) ? sanitize_text_field( $data['security_token'] ) : '';
67 + $amount = ! empty( $data['amount'] ) ? floatval( $data['amount'] ) : 0;
68 + $currency = ! empty( $data['currency'] ) ? sanitize_text_field( $data['currency'] ) : '';
85 69
86 - $metadata = [];
87 -
88 - // The card form (StripePayment.js) creates this PaymentIntent up front, before
89 - // a booking exists, purely from the meeting's price — so booking_id/token are
90 - // optional here. Binding happens later via bind_payment_intent(), and the booking
91 - // can only be marked paid there after its security_token is verified. An intent
92 - // created without a booking can never complete a payment, so this cannot be used
93 - // to steal funds; it can only let a caller create inert PaymentIntents in Stripe.
94 - if ( $booking_id > 0 && '' !== $token ) {
95 - $booking = new Booking( $booking_id );
96 -
97 - if ( ! $booking->is_booking() ) {
98 - return new WP_HTTP_Response(
99 - [
100 - 'success' => 0,
101 - 'status_code' => 404,
102 - 'message' => __( 'Invalid booking id.', 'timetics' ),
103 - ],
104 - 404
105 - );
106 - }
107 -
108 - $stored = (string) $booking->get_security_token();
109 -
110 - if ( '' === $stored || ! hash_equals( $stored, $token ) ) {
111 - return new WP_HTTP_Response(
112 - [
113 - 'success' => 0,
114 - 'status_code' => 403,
115 - 'message' => __( 'Invalid booking token.', 'timetics' ),
116 - ],
117 - 403
118 - );
119 - }
120 -
121 - $metadata['booking_id'] = $booking_id;
122 - $metadata['security_token'] = $stored;
123 -
124 - // Once bound, trust the booking's own total over whatever the client sent.
125 - $amount = (float) $booking->get_total();
126 - }
127 -
128 - // Sanity bounds — reject nonsense amounts regardless of binding.
129 - if ( $amount <= 0 || $amount > 1000000 || ! preg_match( '/^[A-Za-z]{3}$/', (string) $currency ) ) {
130 - return new WP_HTTP_Response(
131 - [
132 - 'success' => 0,
133 - 'status_code' => 400,
134 - 'message' => __( 'Invalid amount or currency.', 'timetics' ),
135 - ],
136 - 400
137 - );
138 - }
139 -
140 70 $payment = new StripePayment();
141 71
142 72 $payment = $payment->create_payment(
143 73 [
144 - 'amount' => $amount * 100,
145 - 'currency' => $currency,
146 - 'metadata' => $metadata,
147 - ]
74 + 'amount' => $amount * 100,
75 + 'currency' => $currency,
76 + ]
148 77 );
149 78
150 - if ( is_wp_error( $payment ) ) {
79 + if ( is_wp_error( $data ) ) {
151 80 $response = [
152 81 'success' => 0,
153 82 'status_code' => 403,
154 83 'message' => $payment->get_error_message(),
@@ -157,31 +86,7 @@
157 86 return new WP_HTTP_Response( $response, 403 );
158 87 }
159 88
160 89 return rest_ensure_response( $payment );
161 - }
162 -
163 - /**
164 - * Simple per-IP fixed-window limiter for the public payment-intent route.
165 - *
166 - * @return bool
167 - */
168 - private function is_rate_limited() {
169 - $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : '';
170 -
171 - if ( '' === $ip ) {
172 - return false;
173 - }
174 -
175 - $key = 'tt_stripe_rl_' . md5( $ip );
176 - $count = (int) get_transient( $key );
177 -
178 - if ( $count >= 20 ) {
179 - return true;
180 - }
181 -
182 - set_transient( $key, $count + 1, MINUTE_IN_SECONDS * 10 );
183 -
184 - return false;
185 90 }
186 91 }
187 92