PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.1.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.1.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / api / payment-methods.php

payment-methods.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.1.0, at includes/api/payment-methods.php

210 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Saved payment methods REST controller.
4 *
5 * Replaces the legacy admin-ajax handler at includes/post/saved-payment-method.php.
6 * Used by the storefront "Add a payment method" form (Stripe Card Element)
7 * and by the dashboard's saved-card list (delete + set-default).
8 *
9 * Routes:
10 * POST /wp-json/storeengine/v1/payment-methods — add a payment method (gateway add_payment_method)
11 * POST /wp-json/storeengine/v1/payment-methods/setup-intent — create a SetupIntent (no cart/order required)
12 * DELETE /wp-json/storeengine/v1/payment-methods/<token_id> — delete a saved token
13 * POST /wp-json/storeengine/v1/payment-methods/<token_id>/default — mark a token as default
14 *
15 * Auth: standard WP REST cookie + X-WP-Nonce. Logged-in users only.
16 */
17
18 namespace StoreEngine\API;
19
20 use StoreEngine\Classes\Exceptions\StoreEngineException;
21 use StoreEngine\Classes\PaymentTokens\PaymentTokens;
22 use StoreEngine\Utils\Formatting;
23 use StoreEngine\Utils\Helper;
24 use WP_Error;
25 use WP_REST_Request;
26 use WP_REST_Server;
27
28 if ( ! defined( 'ABSPATH' ) ) {
29 exit;
30 }
31
32 class PaymentMethods extends AbstractRestApiController {
33
34 protected $rest_base = 'payment-methods';
35
36 public static function init() {
37 $self = new self();
38 add_action( 'rest_api_init', [ $self, 'register_routes' ] );
39 }
40
41 public function register_routes() {
42 register_rest_route( $this->namespace, '/' . $this->rest_base, [
43 [
44 'methods' => WP_REST_Server::CREATABLE,
45 'callback' => [ $this, 'add_payment_method' ],
46 'permission_callback' => [ $this, 'logged_in_callback' ],
47 'args' => [
48 'payment_method' => [ 'type' => 'string', 'required' => true ],
49 'payment_payload' => [ 'type' => 'object', 'required' => false ],
50 'fields' => [ 'type' => 'object', 'required' => false ],
51 ],
52 ],
53 ] );
54
55 register_rest_route( $this->namespace, '/' . $this->rest_base . '/setup-intent', [
56 [
57 'methods' => WP_REST_Server::CREATABLE,
58 'callback' => [ $this, 'create_setup_intent' ],
59 'permission_callback' => [ $this, 'logged_in_callback' ],
60 'args' => [
61 'payment_method' => [ 'type' => 'string', 'required' => true ],
62 'return_url' => [ 'type' => 'string', 'required' => false ],
63 ],
64 ],
65 ] );
66
67 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<token_id>\d+)', [
68 'args' => [
69 'token_id' => [ 'type' => 'integer', 'required' => true ],
70 ],
71 [
72 'methods' => WP_REST_Server::DELETABLE,
73 'callback' => [ $this, 'delete_payment_method' ],
74 'permission_callback' => [ $this, 'logged_in_callback' ],
75 ],
76 ] );
77
78 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<token_id>\d+)/default', [
79 'args' => [
80 'token_id' => [ 'type' => 'integer', 'required' => true ],
81 ],
82 [
83 'methods' => WP_REST_Server::CREATABLE,
84 'callback' => [ $this, 'set_default_payment_method' ],
85 'permission_callback' => [ $this, 'logged_in_callback' ],
86 ],
87 ] );
88 }
89
90 public function logged_in_callback() {
91 if ( ! is_user_logged_in() ) {
92 return new WP_Error( 'storeengine_payment_methods_login_required', __( 'You must be logged in.', 'storeengine' ), [ 'status' => 401 ] );
93 }
94 return true;
95 }
96
97 public function add_payment_method( WP_REST_Request $request ) {
98 $payment_method = sanitize_text_field( (string) $request->get_param( 'payment_method' ) );
99 $payment_data = (array) $request->get_param( 'payment_payload' );
100 $fields = (array) $request->get_param( 'fields' );
101
102 if ( '' === $payment_method ) {
103 return new WP_Error( 'storeengine_payment_methods_required', __( 'Payment method is required.', 'storeengine' ), [ 'status' => 422 ] );
104 }
105
106 $gateway = Helper::get_payment_gateways()->get_available_payment_gateway( $payment_method );
107 if ( ! $gateway ) {
108 return new WP_Error( 'storeengine_payment_methods_invalid_gateway', __( 'Invalid payment gateway.', 'storeengine' ), [ 'status' => 422 ] );
109 }
110 if ( ! $gateway->supports( 'add_payment_method' ) && ! $gateway->supports( 'tokenization' ) ) {
111 return new WP_Error( 'storeengine_payment_methods_unsupported', __( 'Gateway does not support saved methods.', 'storeengine' ), [ 'status' => 422 ] );
112 }
113
114 // Pipe form fields + gateway payload into $_POST so legacy gateway
115 // validate_fields() / add_payment_method() (written for admin-ajax)
116 // keep working unmodified.
117 foreach ( $fields as $k => $v ) {
118 if ( is_scalar( $v ) ) {
119 $_POST[ $k ] = $v;
120 $_REQUEST[ $k ] = $v;
121 }
122 }
123 foreach ( $payment_data as $k => $v ) {
124 if ( is_scalar( $v ) ) {
125 $_POST[ $k ] = $v;
126 $_REQUEST[ $k ] = $v;
127 }
128 }
129
130 try {
131 $gateway->validate_fields();
132 $result = $gateway->add_payment_method( Formatting::clean( wp_unslash( $_POST ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
133 } catch ( StoreEngineException $e ) {
134 return new WP_Error( $e->get_wp_error_code() ?: 'storeengine_payment_methods_failed', $e->getMessage(), [ 'status' => 422 ] );
135 }
136
137 return rest_ensure_response( $result );
138 }
139
140 /**
141 * Create a gateway SetupIntent for saving a payment method WITHOUT a cart
142 * or order (the dashboard "Add payment method" form). The checkout flow's
143 * /checkout/payment-intent/{gw} endpoint requires a populated cart and a
144 * draft order; this one bypasses both.
145 *
146 * Currently only Stripe is supported (the only gateway in-tree that
147 * supports tokenization without a charge). Other gateways return 422.
148 */
149 public function create_setup_intent( WP_REST_Request $request ) {
150 $payment_method = sanitize_text_field( (string) $request->get_param( 'payment_method' ) );
151 if ( '' === $payment_method ) {
152 return new WP_Error( 'storeengine_payment_methods_required', __( 'Payment method is required.', 'storeengine' ), [ 'status' => 422 ] );
153 }
154
155 if ( 'stripe' !== $payment_method ) {
156 return new WP_Error(
157 'storeengine_payment_methods_setup_unsupported',
158 /* translators: %s: gateway id */
159 sprintf( __( 'Gateway "%s" does not support saving methods without a charge.', 'storeengine' ), $payment_method ),
160 [ 'status' => 422 ]
161 );
162 }
163
164 if ( ! class_exists( '\\StoreEngine\\Addons\\Stripe\\StripeService' ) ) {
165 return new WP_Error( 'storeengine_payment_methods_stripe_inactive', __( 'Stripe addon is not active.', 'storeengine' ), [ 'status' => 422 ] );
166 }
167
168 try {
169 $intent = \StoreEngine\Addons\Stripe\StripeService::init()->create_setup_intent(
170 get_current_user_id(),
171 (string) $request->get_param( 'return_url' )
172 );
173 } catch ( \Throwable $e ) {
174 return new WP_Error( 'storeengine_payment_methods_setup_failed', $e->getMessage(), [ 'status' => 500 ] );
175 }
176
177 return rest_ensure_response( [
178 'client_secret' => $intent->client_secret,
179 'intent_id' => $intent->id,
180 'mode' => 'setup',
181 ] );
182 }
183
184 public function delete_payment_method( WP_REST_Request $request ) {
185 $token_id = (int) $request->get_param( 'token_id' );
186 $token = PaymentTokens::get_token( $token_id );
187
188 if ( ! $token || get_current_user_id() !== $token->get_user_id() ) {
189 return new WP_Error( 'storeengine_payment_methods_invalid_token', __( 'Invalid payment method.', 'storeengine' ), [ 'status' => 404 ] );
190 }
191
192 PaymentTokens::delete( $token_id );
193
194 return rest_ensure_response( [ 'deleted' => true, 'token_id' => $token_id ] );
195 }
196
197 public function set_default_payment_method( WP_REST_Request $request ) {
198 $token_id = (int) $request->get_param( 'token_id' );
199 $token = PaymentTokens::get_token( $token_id );
200
201 if ( ! $token || get_current_user_id() !== $token->get_user_id() ) {
202 return new WP_Error( 'storeengine_payment_methods_invalid_token', __( 'Invalid payment method.', 'storeengine' ), [ 'status' => 404 ] );
203 }
204
205 PaymentTokens::set_users_default( $token->get_user_id(), $token_id );
206
207 return rest_ensure_response( [ 'default' => true, 'token_id' => $token_id ] );
208 }
209 }
210