PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.4.0
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.4.0
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
better-payment / includes / Classes / Actions.php

Actions.php in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.4.0, at includes/Classes/Actions.php

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