PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.4.1
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.4.1
2.4.1 2.4.0 2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 All 68 releases
← All changes | includes/Classes/Actions.php +325 -97 1.0.3 → 2.4.1 View file →
@@ -1,7 +1,8 @@
1 1 <?php
2 2
3 3 namespace Better_Payment\Lite\Classes;
4 +use Better_Payment\Lite\Traits\Helper as TraitsHelper;
4 5
5 6 /**
6 7 * Exit if accessed directly
7 8 */
@@ -14,8 +15,9 @@
14 15 *
15 16 * @since 0.0.1
16 17 */
17 18 class Actions {
19 + use TraitsHelper;
18 20 /**
19 21 * Constructor
20 22 *
21 23 * @since 0.0.1
@@ -25,11 +27,15 @@
25 27 add_action( 'admin_post_nopriv_paypal_form_handle', [ $this, 'paypal_form_handle' ] );
26 28
27 29 add_action( 'wp_ajax_better_payment_stripe_get_token', [ $this, 'better_payment_stripe_get_token' ] );
28 30 add_action( 'wp_ajax_nopriv_better_payment_stripe_get_token', [ $this, 'better_payment_stripe_get_token' ] );
29 -
31 +
30 32 add_action( 'wp_ajax_better_payment_paystack_get_token', [ $this, 'better_payment_paystack_get_token' ] );
31 33 add_action( 'wp_ajax_nopriv_better_payment_paystack_get_token', [ $this, 'better_payment_paystack_get_token' ] );
34 +
35 + // NOTE: PayPal IPN + verification-poll hooks are registered unconditionally
36 + // in better-payment.php (init_plugin), not here — they must run even when
37 + // Elementor is inactive (block/campaign payment flow).
32 38 }
33 39
34 40
35 41 /**
@@ -40,11 +46,11 @@
40 46 public function paypal_form_handle() {
41 47
42 48 check_admin_referer( 'better-payment-paypal', 'security' );
43 49
44 - if ( !empty( $_POST[ 'better_payment_page_id' ] ) ) {
45 - $page_id = intval( $_POST[ 'better_payment_page_id' ], 10 );
46 - } else {
50 + $page_id = ! empty( $_POST[ 'better_payment_page_id' ] ) ? intval( $_POST[ 'better_payment_page_id' ], 10 ) : 0;
51 +
52 + if ( ! $page_id ) {
47 53 $this->redirect_previous_page();
48 54 }
49 55
50 56 if ( !empty( $_POST[ 'better_payment_widget_id' ] ) ) {
@@ -69,44 +75,80 @@
69 75 $path = "sandbox.paypal";
70 76 }
71 77
72 78 $el_settings_currency = $el_settings[ 'better_payment_form_currency' ];
73 -
74 - if(!empty($el_settings['better_payment_form_currency_use_woocommerce']) && 'yes' === $el_settings['better_payment_form_currency_use_woocommerce'] &&
75 - !empty($el_settings['better_payment_form_currency_woocommerce'])){
76 - $el_settings_currency = $el_settings['better_payment_form_currency_woocommerce'];
79 + $woo_product_id = !empty($el_settings["better_payment_form_woocommerce_product_id"]) ? intval($el_settings["better_payment_form_woocommerce_product_id"]) : 0;
80 + $woo_product_ids = !empty($el_settings["better_payment_form_woocommerce_product_ids"]) ? $el_settings["better_payment_form_woocommerce_product_ids"] : [0];
81 + $fluentcart_product_id = !empty($el_settings["better_payment_form_fluentcart_product_id"]) ? intval($el_settings["better_payment_form_fluentcart_product_id"]) : 0;
82 + $fluentcart_product_ids = !empty($el_settings["better_payment_form_fluentcart_product_ids"]) ? $el_settings["better_payment_form_fluentcart_product_ids"] : [0];
83 +
84 + // Amount, quantity, currency and campaign come from the stored settings, never
85 + // from the request — see PaymentRequestGuard.
86 + $guarded = ( new PaymentRequestGuard() )->resolve_form_payment(
87 + $el_settings,
88 + wp_unslash( $_POST ),
89 + [
90 + 'page_id' => $page_id,
91 + 'gateway' => 'paypal',
92 + ]
93 + );
94 +
95 + if ( is_wp_error( $guarded ) ) {
96 + PaymentRequestGuard::reject_form_post( $guarded );
97 + return;
77 98 }
78 99
79 - $el_settings_currency_symbol = \Better_Payment\Lite\Classes\Handler::get_currency_symbols( esc_html($el_settings_currency) );
100 + $el_settings_currency = $guarded['currency'];
101 + $el_settings_currency_symbol = $this->get_currency_symbol( esc_html($el_settings_currency) );
80 102
81 - $primary_payment_amount = floatval( $_POST[ 'primary_payment_amount' ] );
103 + $primary_payment_amount = $guarded['amount'];
104 + $primary_payment_amount_quantity = $guarded['quantity'];
82 105
83 - if( empty( $_POST['primary_payment_amount'] ) && ! empty( $_POST['primary_payment_amount_radio'] ) ){
84 - $primary_payment_amount = floatval( $_POST['primary_payment_amount_radio'] );
85 - }
86 - $order_id = 'paypal_' . uniqid();
106 + $order_id = Handler::new_order_id( 'paypal' );
87 107 $request_data = [
88 108 'business' => $el_settings[ 'better_payment_paypal_business_email' ],
89 109 'currency_code' => $el_settings_currency,
90 110 'rm' => '2',
91 111 'return' => esc_url_raw( $_POST[ 'return' ] ),
92 - 'cancel_return' => esc_url_raw( $_POST[ 'cancel_return' ] ),
112 + 'cancel_return' => add_query_arg( 'better_payment_paypal_id', $order_id, esc_url_raw( $_POST[ 'cancel_return' ] ) ),
93 113 'item_number' => $order_id,
94 114 'item_name' => ! empty( $el_settings['better_payment_form_title'] ) ? esc_html__( $el_settings['better_payment_form_title'], 'better-payment' ) : esc_html__('Better Payment', 'better-payment'),
95 115 'amount' => $primary_payment_amount,
96 116 'cmd' => $el_settings[ 'better_payment_paypal_button_type' ],
117 + 'notify_url' => admin_url( 'admin-post.php?action=better_payment_paypal_ipn' ),
97 118 ];
98 119
120 + $product_ids = [
121 + 'woo_product_ids' => $woo_product_ids,
122 + 'fluentcart_product_ids' => $fluentcart_product_ids,
123 + ];
124 +
125 + $detailed_product_info = $this->get_detailed_product_info( $product_ids );
126 +
99 127 //Form fields data to send via email
100 128 $better_form_fields = [
101 129 'amount' => sanitize_text_field($el_settings_currency_symbol) . $primary_payment_amount,
102 130 'referer_page_id' => $page_id,
103 131 'referer_widget_id' => $widget_id,
104 - 'source' => 'paypal'
132 + 'woo_product_id' => $woo_product_id,
133 + 'woo_product_ids' => maybe_serialize( $woo_product_ids ),
134 + 'fluentcart_product_id' => $fluentcart_product_id,
135 + 'fluentcart_product_ids' => maybe_serialize( $fluentcart_product_ids ),
136 + 'source' => 'paypal',
137 + 'amount_quantity' => ! empty( $primary_payment_amount_quantity ) ? intval( $primary_payment_amount_quantity ) : '',
138 + 'detailed_product_info' => maybe_serialize( $detailed_product_info ),
139 + 'paypal_business_email' => sanitize_email( $el_settings['better_payment_paypal_business_email'] ),
140 + // The PayPal environment the order was sent to; the IPN must come from the same one.
141 + 'paypal_mode' => 'yes' === $el_settings['better_payment_paypal_live_mode'] ? 'live' : 'sandbox',
142 + // Persisted so the async PayPal IPN can rebuild the e-mail body + on/off toggle.
143 + 'email_settings' => Handler::extract_email_settings( $el_settings ),
105 144 ];
106 145
146 + // Fields only Pro's forms store (layout 6's platform flags) — see PaymentEntitlement.
147 + $better_form_fields = PaymentEntitlement::transaction_fields( $better_form_fields, $el_settings );
148 +
107 149 $better_form_fields = array_merge( $better_form_fields, $this->fetch_better_form_fields($el_settings, $_POST) );
108 -
150 +
109 151 if ( !empty( $better_form_fields[ 'primary_first_name' ] ) ) {
110 152 $request_data[ 'primary_first_name' ] = sanitize_text_field( $better_form_fields[ 'primary_first_name' ] );
111 153 }
112 154
@@ -116,12 +158,18 @@
116 158
117 159 if ( !empty( $better_form_fields[ 'primary_email' ] ) ) {
118 160 $request_data[ 'primary_email' ] = sanitize_email( $better_form_fields[ 'primary_email' ] );
119 161 }
162 +
163 + if ( !empty( $better_form_fields[ 'primary_reference_number' ] ) ) {
164 + $request_data[ 'invoice' ] = sanitize_text_field( $better_form_fields[ 'primary_reference_number' ] );
165 + }
120 166
167 + $campaign_id = $guarded['campaign_id'];
168 +
121 169 Handler::payment_create(
122 170 [
123 - 'amount' => floatval( $_POST[ 'primary_payment_amount' ] ),
171 + 'amount' => floatval( $primary_payment_amount ),
124 172 'order_id' => $order_id,
125 173 'payment_date' => date( 'Y-m-d H:i:s' ),
126 174 'source' => 'paypal',
127 175 'form_fields_info' => maybe_serialize( $better_form_fields ),
@@ -126,8 +174,9 @@
126 174 'source' => 'paypal',
127 175 'form_fields_info' => maybe_serialize( $better_form_fields ),
128 176 'currency' => sanitize_text_field($el_settings_currency),
129 177 'referer' => "widget",
178 + 'campaign_id' => $campaign_id,
130 179 ]
131 180 );
132 181 $paypal_url = "https://www.$path.com/cgi-bin/webscr?";
133 182 $paypal_url .= http_build_query( $request_data );
@@ -161,22 +210,39 @@
161 210 wp_send_json_error( esc_html($err_msg) );
162 211 }
163 212
164 213 $el_settings = $this->better_payment_widget_settings( $page_id, $widget_id );
214 +
215 + // Before any setting is read: an unknown, or unpublished, form has none.
216 + if ( empty( $el_settings ) ) {
217 + wp_send_json_error( esc_html(__( 'Setting Data is missing', 'better-payment' )) );
218 + }
219 +
165 220 $better_payment_keys = [
166 221 'public_key' => 'yes' === sanitize_text_field( $el_settings[ 'better_payment_stripe_live_mode' ] ) ? sanitize_text_field( $el_settings[ 'better_payment_stripe_public_key_live' ] ) : sanitize_text_field( $el_settings[ 'better_payment_stripe_public_key' ] ),
167 222 'secret_key' => 'yes' === sanitize_text_field( $el_settings[ 'better_payment_stripe_live_mode' ] ) ? sanitize_text_field( $el_settings[ 'better_payment_stripe_secret_key_live' ] ) : sanitize_text_field( $el_settings[ 'better_payment_stripe_secret_key' ] ),
168 223 ];
169 224
170 - if ( empty( $el_settings ) ) {
171 - wp_send_json_error( esc_html(__( 'Setting Data is missing', 'better-payment' )) );
225 + // Amount, quantity, currency, price ids, coupon and campaign come from the stored
226 + // settings, never from the request — see PaymentRequestGuard.
227 + $guard = new PaymentRequestGuard();
228 + $guarded = $guard->resolve_form_payment(
229 + $el_settings,
230 + isset( $_POST['fields'] ) && is_array( $_POST['fields'] ) ? wp_unslash( $_POST['fields'] ) : [],
231 + [
232 + 'page_id' => $page_id,
233 + 'gateway' => 'stripe',
234 + ]
235 + );
236 +
237 + if ( is_wp_error( $guarded ) ) {
238 + wp_send_json_error( esc_html( $guarded->get_error_message() ) );
172 239 }
173 240
174 - $amount = isset($_POST[ 'fields' ][ 'primary_payment_amount' ]) ? floatval($_POST[ 'fields' ][ 'primary_payment_amount' ]) : 0;
175 - if ( empty( $_POST[ 'fields' ][ 'primary_payment_amount' ] ) && ! empty( $_POST[ 'fields' ][ 'primary_payment_amount_radio' ] ) ) {
176 - $amount = floatval($_POST[ 'fields' ][ 'primary_payment_amount_radio' ]);
177 - }
178 -
241 + $amount = $guarded['amount'];
242 + $amount_quantity = $guarded['quantity'];
243 + $product_quantities = $guarded['product_quantities'];
244 +
179 245 if ( empty( $better_payment_keys['public_key'] ) || empty( $better_payment_keys['secret_key'] ) ) {
180 246 wp_send_json_error( esc_html(__( 'Stripe Key missing', 'better-payment' )) );
181 247 }
182 248
@@ -184,19 +250,20 @@
184 250 'Authorization' => 'Basic ' . base64_encode( sanitize_text_field( $better_payment_keys['secret_key'] ) . ':' ),
185 251 'Stripe-Version' => '2019-05-16'
186 252 );
187 253
188 - $order_id = 'stripe_' . uniqid();
254 + $order_id = Handler::new_order_id( 'stripe' );
189 255
190 256 $el_settings_currency = $el_settings[ 'better_payment_form_currency' ];
191 -
192 - if(!empty($settings['better_payment_form_currency_use_woocommerce']) && 'yes' === $settings['better_payment_form_currency_use_woocommerce'] &&
193 - !empty($settings['better_payment_form_currency_woocommerce'])){
194 - $el_settings_currency = $settings['better_payment_form_currency_woocommerce'];
195 - }
257 + $woo_product_id = !empty($el_settings["better_payment_form_woocommerce_product_id"]) ? intval($el_settings["better_payment_form_woocommerce_product_id"]) : 0;
258 + $woo_product_ids = !empty($el_settings["better_payment_form_woocommerce_product_ids"]) ? $el_settings["better_payment_form_woocommerce_product_ids"] : [0];
259 + $fluentcart_product_id = !empty($el_settings["better_payment_form_fluentcart_product_id"]) ? intval($el_settings["better_payment_form_fluentcart_product_id"]) : 0;
260 + $fluentcart_product_ids = !empty($el_settings["better_payment_form_fluentcart_product_ids"]) ? $el_settings["better_payment_form_fluentcart_product_ids"] : [0];
196 261
197 - $el_settings_currency_symbol = \Better_Payment\Lite\Classes\Handler::get_currency_symbols( esc_html($el_settings_currency) );
262 + $el_settings_currency = $guarded['currency'];
198 263
264 + $el_settings_currency_symbol = $this->get_currency_symbol( esc_html($el_settings_currency) );
265 +
199 266 $request = [
200 267 'success_url' => add_query_arg( [
201 268 'better_payment_stripe_status' => 'success',
202 269 'better_payment_stripe_id' => $order_id,
@@ -215,9 +282,9 @@
215 282 'order_id' => $order_id
216 283 ],
217 284 'line_items' => [
218 285 [
219 - 'amount' => ( $amount * 100 ),
286 + 'amount' => (int) round( $amount * 100 ),
220 287 'currency' => $el_settings_currency,
221 288 'name' => ! empty( $el_settings['better_payment_form_title'] ) ? esc_html__( $el_settings['better_payment_form_title'], 'better-payment' ) : esc_html__('Better Payment', 'better-payment'),
222 289 'quantity' => 1
223 290 ]
@@ -229,17 +296,49 @@
229 296 'order_id' => $order_id
230 297 ]
231 298 ]
232 299 ];
233 -
300 +
301 + // The typed code is kept for the receipt; what Stripe applies is decided by the guard —
302 + // an active promotion code, or a coupon the merchant tagged public. A bare coupon id
303 + // no longer works, because every coupon in the account (internal 100%-off ones
304 + // included) would be one guess away.
305 + $coupon_code = $guarded['coupon_code'];
306 + $discount = $guard->resolve_stripe_discount( $coupon_code, $better_payment_keys['secret_key'] );
307 +
308 + if ( is_wp_error( $discount ) ) {
309 + wp_send_json_error( esc_html( $discount->get_error_message() ) );
310 + }
311 +
312 + if ( ! empty( $discount ) ) {
313 + $request['discounts'] = [ $discount ];
314 + }
315 +
316 + $product_ids = [
317 + 'woo_product_ids' => $woo_product_ids,
318 + 'fluentcart_product_ids' => $fluentcart_product_ids,
319 + ];
320 +
321 + $detailed_product_info = $this->get_detailed_product_info( $product_ids, $product_quantities );
322 +
234 323 //Form fields data to send via email
235 324 $better_form_fields = [
236 325 'amount' => $el_settings_currency_symbol . $amount,
237 326 'referer_page_id' => $page_id,
238 327 'referer_widget_id' => $widget_id,
239 - 'source' => 'stripe'
328 + 'woo_product_id' => $woo_product_id,
329 + 'woo_product_ids' => maybe_serialize( $woo_product_ids ),
330 + 'fluentcart_product_id' => $fluentcart_product_id,
331 + 'fluentcart_product_ids' => maybe_serialize( $fluentcart_product_ids ),
332 + 'source' => 'stripe',
333 + 'amount_quantity' => ! empty( $amount_quantity ) ? $amount_quantity : '',
334 + 'stripe_coupon_code' => $coupon_code,
335 + 'detailed_product_info' => maybe_serialize( $detailed_product_info ),
240 336 ];
241 337
338 + // Fields only Pro's forms store (layout 6's platform flags) — see PaymentEntitlement.
339 + $better_form_fields = PaymentEntitlement::transaction_fields( $better_form_fields, $el_settings );
340 +
242 341 $better_form_fields = array_merge( $better_form_fields, $this->fetch_better_form_fields($el_settings, $_POST['fields']) );
243 342
244 343 if ( !empty( $better_form_fields['primary_first_name'] ) ) {
245 344 $request[ 'customer_name' ] = $better_form_fields['primary_first_name'];
@@ -261,8 +360,34 @@
261 360 $request[ 'payment_intent_data' ][ 'metadata' ][ 'customer_email' ] = $request[ 'customer_email' ];
262 361
263 362 }
264 363
364 + // Recurring, split-payment and mixed checkouts are Pro's: refused without Pro, built by
365 + // Pro (or, for an older Pro, by Lite's transitional copy) — see PaymentEntitlement.
366 + if ( PaymentEntitlement::is_pro_form( $el_settings ) ) {
367 + $checkout = PaymentEntitlement::stripe_checkout(
368 + [
369 + 'request' => $request,
370 + 'form_fields' => $better_form_fields,
371 + ],
372 + [
373 + 'settings' => $el_settings,
374 + 'fields' => isset( $_POST['fields'] ) && is_array( $_POST['fields'] ) ? wp_unslash( $_POST['fields'] ) : [],
375 + 'guarded' => $guarded,
376 + 'order_id' => $order_id,
377 + 'widget_id' => $widget_id,
378 + 'header_info' => $header_info,
379 + ]
380 + );
381 +
382 + if ( is_wp_error( $checkout ) ) {
383 + wp_send_json_error( esc_html( $checkout->get_error_message() ) );
384 + }
385 +
386 + $request = $checkout['request'];
387 + $better_form_fields = $checkout['form_fields'];
388 + }
389 +
265 390 #ToDo: Need to prefill name field on stripe checkout
266 391 $response = wp_safe_remote_post(
267 392 'https://api.stripe.com/v1/checkout/sessions',
268 393 array(
@@ -273,10 +398,12 @@
273 398 )
274 399 );
275 400
276 401 $response_ar = json_decode( $response[ 'body' ] );
277 -
278 - if ( !empty( $response_ar->payment_intent ) ) {
402 +
403 + if ( ! empty( $response_ar->payment_intent ) || ( ! empty( $response_ar->mode ) && 'subscription' === $response_ar->mode ) ) {
404 + $campaign_id = $guarded['campaign_id'];
405 +
279 406 Handler::payment_create(
280 407 [
281 408 'amount' => $amount,
282 409 'order_id' => $order_id,
@@ -288,8 +415,9 @@
288 415 'obj_id' => sanitize_text_field($response_ar->id),
289 416 'status' => sanitize_text_field($response_ar->payment_status),
290 417 'currency' => $el_settings_currency,
291 418 'referer' => "widget",
419 + 'campaign_id' => $campaign_id,
292 420 ]
293 421 );
294 422 wp_send_json_success(
295 423 [
@@ -316,9 +444,9 @@
316 444 public function better_payment_paystack_get_token() {
317 445 if ( !check_admin_referer( 'better-payment', 'security' ) ) {
318 446 wp_send_json_error();
319 447 }
320 -
448 +
321 449 $error_message = '';
322 450
323 451 if( empty( $_POST[ 'setting_data' ]['page_id'] ) ){
324 452 $error_message = __( 'Page ID is missing', 'better-payment' );
@@ -334,9 +462,9 @@
334 462
335 463 $page_id = intval( $_POST[ 'setting_data' ]['page_id'] );
336 464 $widget_id = sanitize_text_field( $_POST[ 'setting_data' ]['widget_id'] );
337 465 $el_settings = $this->better_payment_widget_settings( $page_id, $widget_id );
338 -
466 +
339 467 if ( empty( $el_settings ) ) {
340 468 wp_send_json_error( esc_html(__( 'Setting Data is missing', 'better-payment' )) );
341 469 }
342 470
@@ -343,35 +471,49 @@
343 471 if ( empty( $el_settings[ 'better_payment_paystack_public_key' ] ) || empty( $el_settings[ 'better_payment_paystack_secret_key' ] ) ) {
344 472 wp_send_json_error( esc_html(__( 'Paystack Key missing', 'better-payment' )) );
345 473 }
346 474
347 - $amount = isset($_POST[ 'fields' ][ 'primary_payment_amount' ]) ? floatval($_POST[ 'fields' ][ 'primary_payment_amount' ]) : 0;
348 -
349 - if ( empty( $_POST[ 'fields' ][ 'primary_payment_amount' ] ) && ! empty( $_POST[ 'fields' ][ 'primary_payment_amount_radio' ] ) ) {
350 - $amount = floatval($_POST[ 'fields' ][ 'primary_payment_amount_radio' ]);
475 + // Amount, quantity, currency and campaign come from the stored settings, never
476 + // from the request — see PaymentRequestGuard.
477 + $guarded = ( new PaymentRequestGuard() )->resolve_form_payment(
478 + $el_settings,
479 + isset( $_POST['fields'] ) && is_array( $_POST['fields'] ) ? wp_unslash( $_POST['fields'] ) : [],
480 + [
481 + 'page_id' => $page_id,
482 + 'gateway' => 'paystack',
483 + ]
484 + );
485 +
486 + if ( is_wp_error( $guarded ) ) {
487 + wp_send_json_error( esc_html( $guarded->get_error_message() ) );
351 488 }
352 489
490 + $amount = $guarded['amount'];
491 + $amount_quantity = $guarded['quantity'];
492 +
353 493 $header_info = array(
354 494 'Authorization' => 'Bearer ' . sanitize_text_field( $el_settings[ 'better_payment_paystack_secret_key' ] ),
355 495 "Cache-Control: no-cache",
356 496 );
357 497
358 - $order_id = 'paystack_' . uniqid();
498 + $order_id = Handler::new_order_id( 'paystack' );
359 499
360 500 $el_settings_currency = $el_settings[ 'better_payment_form_currency' ];
361 -
362 - if(!empty($settings['better_payment_form_currency_use_woocommerce']) && 'yes' === $settings['better_payment_form_currency_use_woocommerce'] &&
363 - !empty($settings['better_payment_form_currency_woocommerce'])){
364 - $el_settings_currency = $settings['better_payment_form_currency_woocommerce'];
365 - }
366 -
367 - $el_settings_currency_symbol = \Better_Payment\Lite\Classes\Handler::get_currency_symbols( esc_html($el_settings_currency) );
501 + $woo_product_id = !empty($el_settings["better_payment_form_woocommerce_product_id"]) ? intval($el_settings["better_payment_form_woocommerce_product_id"]) : 0;
502 + $woo_product_ids = !empty($el_settings["better_payment_form_woocommerce_product_ids"]) ? $el_settings["better_payment_form_woocommerce_product_ids"] : [0];
503 + $fluentcart_product_id = !empty($el_settings["better_payment_form_fluentcart_product_id"]) ? intval($el_settings["better_payment_form_fluentcart_product_id"]) : 0;
504 + $fluentcart_product_ids = !empty($el_settings["better_payment_form_fluentcart_product_ids"]) ? $el_settings["better_payment_form_fluentcart_product_ids"] : [0];
368 505
369 - $redirection_url_success = ! empty( $el_settings['better_payment_form_success_page_url']['url'] ) ? esc_url( $el_settings['better_payment_form_success_page_url']['url'] ) : get_permalink( $page_id );
370 - $redirection_url_error = ! empty( $el_settings['better_payment_form_error_page_url']['url'] ) ? esc_url( $el_settings['better_payment_form_error_page_url']['url'] ) : get_permalink( $page_id );
506 + $el_settings_currency = $guarded['currency'];
371 507
508 + $el_settings_currency_symbol = $this->get_currency_symbol( esc_html($el_settings_currency) );
509 +
510 + $redirection_url_success = get_permalink( $page_id );
511 + $redirection_url_error = get_permalink( $page_id );
512 +
372 513 $request = [
373 514 'amount' => ( $amount * 100 ),
515 + 'currency' => $el_settings_currency,
374 516 'cart_id' => $order_id,
375 517 'callback_url' => add_query_arg( [
376 518 'better_payment_paystack_status' => 'success',
377 519 'better_payment_paystack_id' => $order_id,
@@ -376,18 +518,41 @@
376 518 'better_payment_paystack_status' => 'success',
377 519 'better_payment_paystack_id' => $order_id,
378 520 'better_payment_widget_id' => $widget_id
379 521 ], $redirection_url_success ),
522 + 'metadata' => [
523 + 'cancel_action' => add_query_arg( [
524 + 'better_payment_error_status' => 'error',
525 + 'better_payment_paystack_id' => $order_id,
526 + 'better_payment_widget_id' => $widget_id
527 + ], $redirection_url_error )
528 + ]
380 529 ];
381 -
530 +
531 + $product_ids = [
532 + 'woo_product_ids' => $woo_product_ids,
533 + 'fluentcart_product_ids' => $fluentcart_product_ids,
534 + ];
535 +
536 + $detailed_product_info = $this->get_detailed_product_info( $product_ids );
537 +
382 538 //Form fields data to send via email
383 539 $better_form_fields = [
384 540 'amount' => $el_settings_currency_symbol . $amount,
385 541 'referer_page_id' => $page_id,
386 542 'referer_widget_id' => $widget_id,
387 - 'source' => 'paystack'
543 + 'woo_product_id' => $woo_product_id,
544 + 'woo_product_ids' => maybe_serialize( $woo_product_ids ),
545 + 'fluentcart_product_id' => $fluentcart_product_id,
546 + 'fluentcart_product_ids' => maybe_serialize( $fluentcart_product_ids ),
547 + 'source' => 'paystack',
548 + 'amount_quantity' => ! empty( $amount_quantity ) ? $amount_quantity : '',
549 + 'detailed_product_info' => maybe_serialize( $detailed_product_info ),
388 550 ];
389 551
552 + // Fields only Pro's forms store (layout 6's platform flags) — see PaymentEntitlement.
553 + $better_form_fields = PaymentEntitlement::transaction_fields( $better_form_fields, $el_settings );
554 +
390 555 $better_form_fields = array_merge( $better_form_fields, $this->fetch_better_form_fields($el_settings, $_POST['fields']) );
391 556
392 557 if ( !empty( $better_form_fields['primary_email'] ) ) {
393 558 $request[ 'email' ] = $better_form_fields['primary_email'];
@@ -416,11 +581,11 @@
416 581 )
417 582 );
418 583
419 584 $response_ar = json_decode( $response[ 'body' ] );
420 -
585 +
421 586 if( empty( $response_ar->status ) || empty( $response_ar->data ) ){
422 - $error_message = 'Something went wrong!';
587 + $error_message = $response_ar->message ? sanitize_text_field($response_ar->message) : 'Something went wrong!';
423 588
424 589 if (isset($response_ar->error)){
425 590 $error_message = sanitize_text_field($response_ar->error->message);
426 591 }
@@ -427,8 +592,10 @@
427 592
428 593 wp_send_json_error( $error_message );
429 594 }
430 595
596 + $campaign_id = $guarded['campaign_id'];
597 +
431 598 Handler::payment_create(
432 599 [
433 600 'amount' => $amount,
434 601 'order_id' => $order_id,
@@ -433,19 +600,21 @@
433 600 'amount' => $amount,
434 601 'order_id' => $order_id,
435 602 'payment_date' => date( 'Y-m-d H:i:s' ),
436 603 'source' => 'paystack',
437 - 'transaction_id' => '',
604 + // Paystack's reference, so verification can bind it to this order.
605 + 'transaction_id' => ! empty( $response_ar->data->reference ) ? sanitize_text_field( $response_ar->data->reference ) : '',
438 606 'customer_info' => maybe_serialize( $response_ar ),
439 607 'form_fields_info' => maybe_serialize( $better_form_fields ),
440 608 'status' => 'unpaid',
441 609 'currency' => $el_settings_currency,
442 610 'referer' => "widget",
611 + 'campaign_id' => $campaign_id,
443 612 ]
444 613 );
445 614
446 615 $authorization_url = ! empty( $response_ar->data->authorization_url ) ? esc_url_raw( $response_ar->data->authorization_url ) : '';
447 -
616 +
448 617 wp_send_json_success(
449 618 [
450 619 'authorization_url' => $authorization_url,
451 620 ]
@@ -452,48 +621,103 @@
452 621 );
453 622 }
454 623
455 624 /**
456 - * Widget settings
625 + * Get detailed product information
626 + * @param array $product_ids Array of woo_product_ids and fluentcart_product_ids
457 627 *
458 - * @since 1.0.0
628 + * @since 1.4.5
459 629 */
460 - public function better_payment_widget_settings( $page_id, $widget_id ) {
461 - $document = \Elementor\Plugin::$instance->documents->get( $page_id );
462 - $settings = [];
463 - if ( $document ) {
464 - $elements = \Elementor\Plugin::instance()->documents->get( $page_id )->get_elements_data();
465 - $widget_data = $this->find_element_recursive( $elements, $widget_id );
466 - $widget = \Elementor\Plugin::instance()->elements_manager->create_element_instance( $widget_data );
467 - if ( $widget ) {
468 - $settings = $widget->get_settings_for_display();
630 + public function get_detailed_product_info( $product_ids, $product_quantities = [] ) {
631 + $detailed_product_info = [];
632 +
633 + if (function_exists('wc_get_product') && !empty($product_ids['woo_product_ids'])) {
634 + foreach ($product_ids['woo_product_ids'] as $key => $product_id) {
635 + if (empty($product_id)) continue;
636 +
637 + $product = wc_get_product($product_id);
638 + if ($product) {
639 + $product_image_src_array = wp_get_attachment_image_src( get_post_thumbnail_id( $product->get_id() ), 'single-post-thumbnail' );
640 + $product_image_src = is_array( $product_image_src_array ) && count( $product_image_src_array ) ? $product_image_src_array[0] : '';
641 +
642 + $quantity = !empty($product_quantities[$key]) ? intval($product_quantities[$key]) : 1;
643 + $price = floatval( $product->get_price() );
644 + $total_price = floatval( $price * $quantity );
645 +
646 + $detailed_product_info['woo_products'][$product_id] = [
647 + 'name' => $product->get_name(),
648 + 'product_id' => intval( $product_id ),
649 + 'permalink' => esc_url( get_permalink($product->get_id()) ),
650 + 'image_src' => esc_url( $product_image_src ),
651 + 'price' => $price,
652 + 'quantity' => $quantity,
653 + 'total_price' => $total_price,
654 + ];
655 + }
469 656 }
470 657 }
471 - return $settings;
658 +
659 + if (function_exists('fluentCart') && class_exists('\FluentCart\App\Models\Product') && !empty($product_ids['fluentcart_product_ids'])) {
660 + foreach ($product_ids['fluentcart_product_ids'] as $key => $product_id) {
661 + if (empty($product_id)) continue;
662 +
663 + try {
664 + $fluentcart_product = \FluentCart\App\Models\Product::query()
665 + ->with(['detail', 'variants'])
666 + ->find($product_id);
667 +
668 + if ($fluentcart_product) {
669 + $product_price = 0;
670 + if (!empty($fluentcart_product->variants) && count($fluentcart_product->variants) > 0) {
671 + $product_price = $fluentcart_product->variants[0]->item_price;
672 + } elseif (!empty($fluentcart_product->detail)) {
673 + $product_price = $fluentcart_product->detail->min_price;
674 + }
675 +
676 + if ( ! empty( $product_price ) ) {
677 + $product_price = floatval( $product_price / 100 );
678 + }
679 +
680 + $product_image_src_array = wp_get_attachment_image_src( get_post_thumbnail_id( $fluentcart_product->ID ), 'single-post-thumbnail' );
681 + $product_image_src = is_array( $product_image_src_array ) && count( $product_image_src_array ) ? $product_image_src_array[0] : '';
682 +
683 + $quantity = !empty($product_quantities[$key]) ? intval($product_quantities[$key]) : 1;
684 + $total_price = floatval( $product_price * $quantity );
685 +
686 + $detailed_product_info['fluentcart_products'][$product_id] = [
687 + 'name' => sanitize_text_field( $fluentcart_product->post_title ),
688 + 'product_id' => intval( $product_id ),
689 + 'permalink' => esc_url( get_permalink($fluentcart_product->ID) ),
690 + 'image_src' => esc_url( $product_image_src ),
691 + 'price' => $product_price,
692 + 'quantity' => $quantity,
693 + 'total_price' => $total_price,
694 + ];
695 + }
696 + } catch (\Exception $e) {
697 + //
698 + }
699 + }
700 + }
701 +
702 + return $detailed_product_info;
472 703 }
473 704
474 705 /**
475 - * Find element recursive
706 + * Widget settings
476 707 *
477 708 * @since 1.0.0
478 709 */
479 - public function find_element_recursive( $elements, $form_id ) {
710 + public function better_payment_widget_settings( $page_id, $widget_id ) {
711 + // page_id is the visitor's to choose. A form on a page they cannot see — draft,
712 + // private, scheduled, password-protected — must be neither payable nor probeable.
713 + if ( ! $this->is_payable_form_page( $page_id ) ) {
714 + return [];
715 + }
480 716
481 - foreach ( $elements as $element ) {
482 - if ( $form_id === $element[ 'id' ] ) {
483 - return $element;
484 - }
717 + $settings = $this->get_elementor_widget_settings( $page_id, $widget_id );
485 718
486 - if ( !empty( $element[ 'elements' ] ) ) {
487 - $element = $this->find_element_recursive( $element[ 'elements' ], $form_id );
488 -
489 - if ( $element ) {
490 - return $element;
491 - }
492 - }
493 - }
494 -
495 - return false;
719 + return $settings;
496 720 }
497 721
498 722 /**
499 723 * Redirect to referer page
@@ -518,11 +742,14 @@
518 742 $better_payment_helper_obj = new Helper();
519 743 $post_data_primary_first_name = '';
520 744 $post_data_primary_last_name = '';
521 745 $post_data_primary_email = '';
522 -
746 +
523 747 $post_fields = $post_data_form_fields;
524 748
749 + // The fields repeater a Pro form renders is Pro's to name — see PaymentEntitlement.
750 + $el_settings['better_payment_form_fields'] = PaymentEntitlement::form_fields( is_array( $el_settings ) ? $el_settings : [] );
751 +
525 752 //in case of duplicate post name using the first one
526 753 if(isset($el_settings['better_payment_form_fields']) && count($el_settings['better_payment_form_fields'])){
527 754 foreach ($el_settings['better_payment_form_fields'] as $item) {
528 755
@@ -532,28 +759,31 @@
532 759
533 760 if( isset($post_fields[$item_primary_field_type]) ) {
534 761 $post_fields[$item_primary_field_type] = is_array($post_fields[$item_primary_field_type]) ? $post_fields[$item_primary_field_type][0] : $post_fields[$item_primary_field_type];
535 762 }
536 -
763 +
537 764 if( isset($post_fields[$item_field_name]) ) {
538 765 $post_fields[$item_field_name] = is_array($post_fields[$item_field_name]) ? $post_fields[$item_field_name][0] : $post_fields[$item_field_name];
539 766 }
540 767
541 - if ( 'primary_first_name' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
768 + if ( 'primary_first_name' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
542 769 $better_form_fields[$item_primary_field_type] = sanitize_text_field($post_fields[$item_primary_field_type]);
543 770 $post_data_primary_first_name = sanitize_text_field($post_fields[$item_primary_field_type]);
544 -
771 +
545 772 }else if ( 'primary_last_name' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
546 773 $better_form_fields[$item_primary_field_type] = sanitize_text_field($post_fields[$item_primary_field_type]);
547 774 $post_data_primary_last_name = sanitize_text_field($post_fields[$item_primary_field_type]);
548 -
775 +
549 776 }else if( 'primary_email' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
550 777 $better_form_fields[$item_primary_field_type] = sanitize_email($post_fields[$item_primary_field_type]);
551 778 $post_data_primary_email = sanitize_email($post_fields[$item_primary_field_type]);
552 -
779 +
553 780 }else if ( 'primary_payment_amount' == $item_primary_field_type && isset($post_fields[$item_primary_field_type]) ){
554 781 $better_form_fields[$item_primary_field_type] = floatval($post_fields[$item_primary_field_type]);
555 -
782 +
783 + }else if ( 'primary_reference_number' == $item_primary_field_type && isset($post_fields[$item_primary_field_type]) ){
784 + $better_form_fields[$item_primary_field_type] = sanitize_text_field($post_fields[$item_primary_field_type]);
785 +
556 786 } else {
557 787 $better_form_fields[$item_field_name] = !empty($post_fields[$item_field_name]) ? sanitize_text_field($post_fields[$item_field_name]) : '';
558 788 }
559 789
@@ -559,10 +789,8 @@
559 789
560 790 }
561 791 }
562 792 }
563 -
793 +
564 794 return $better_form_fields;
565 795 }
566 -
567 796 }
568 -