| @@ -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 | |