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
better-payment / includes / Classes / Actions.php

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

797 lines 36.6 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 // 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 ),
144 ];
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
149 $better_form_fields = array_merge( $better_form_fields, $this->fetch_better_form_fields($el_settings, $_POST) );
150
151 if ( !empty( $better_form_fields[ 'primary_first_name' ] ) ) {
152 $request_data[ 'primary_first_name' ] = sanitize_text_field( $better_form_fields[ 'primary_first_name' ] );
153 }
154
155 if ( !empty( $better_form_fields[ 'primary_last_name' ] ) ) {
156 $request_data[ 'primary_last_name' ] = sanitize_text_field( $better_form_fields[ 'primary_last_name' ] );
157 }
158
159 if ( !empty( $better_form_fields[ 'primary_email' ] ) ) {
160 $request_data[ 'primary_email' ] = sanitize_email( $better_form_fields[ 'primary_email' ] );
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 }
166
167 $campaign_id = $guarded['campaign_id'];
168
169 Handler::payment_create(
170 [
171 'amount' => floatval( $primary_payment_amount ),
172 'order_id' => $order_id,
173 'payment_date' => date( 'Y-m-d H:i:s' ),
174 'source' => 'paypal',
175 'form_fields_info' => maybe_serialize( $better_form_fields ),
176 'currency' => sanitize_text_field($el_settings_currency),
177 'referer' => "widget",
178 'campaign_id' => $campaign_id,
179 ]
180 );
181 $paypal_url = "https://www.$path.com/cgi-bin/webscr?";
182 $paypal_url .= http_build_query( $request_data );
183 wp_redirect( esc_url_raw($paypal_url) );
184 }
185
186 /**
187 * Get the stripe token
188 *
189 * @since 0.0.1
190 */
191 public function better_payment_stripe_get_token() {
192 if ( !check_admin_referer( 'better-payment', 'security' ) ) {
193 wp_send_json_error();
194 }
195
196 $setting_data_page_id = isset($_POST[ 'setting_data' ]['page_id']) ? intval( $_POST[ 'setting_data' ]['page_id'] ) : 0;
197 $setting_data_widget_id = isset($_POST[ 'setting_data' ]['widget_id']) ? sanitize_text_field( $_POST[ 'setting_data' ]['widget_id'] ) : 0;
198
199 if ( !empty( $setting_data_page_id ) ) {
200 $page_id = $setting_data_page_id;
201 } else {
202 $err_msg = __( 'Page ID is missing', 'better-payment' );
203 wp_send_json_error( esc_html($err_msg) );
204 }
205
206 if ( !empty( $setting_data_widget_id ) ) {
207 $widget_id = sanitize_text_field( $setting_data_widget_id );
208 } else {
209 $err_msg = __( 'Widget ID is missing', 'better-payment' );
210 wp_send_json_error( esc_html($err_msg) );
211 }
212
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
220 $better_payment_keys = [
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' ] ),
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' ] ),
223 ];
224
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() ) );
239 }
240
241 $amount = $guarded['amount'];
242 $amount_quantity = $guarded['quantity'];
243 $product_quantities = $guarded['product_quantities'];
244
245 if ( empty( $better_payment_keys['public_key'] ) || empty( $better_payment_keys['secret_key'] ) ) {
246 wp_send_json_error( esc_html(__( 'Stripe Key missing', 'better-payment' )) );
247 }
248
249 $header_info = array(
250 'Authorization' => 'Basic ' . base64_encode( sanitize_text_field( $better_payment_keys['secret_key'] ) . ':' ),
251 'Stripe-Version' => '2019-05-16'
252 );
253
254 $order_id = Handler::new_order_id( 'stripe' );
255
256 $el_settings_currency = $el_settings[ 'better_payment_form_currency' ];
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];
261
262 $el_settings_currency = $guarded['currency'];
263
264 $el_settings_currency_symbol = $this->get_currency_symbol( esc_html($el_settings_currency) );
265
266 $request = [
267 'success_url' => add_query_arg( [
268 'better_payment_stripe_status' => 'success',
269 'better_payment_stripe_id' => $order_id,
270 'better_payment_widget_id' => $widget_id
271 ], get_permalink( $setting_data_page_id ) ),
272 'cancel_url' => add_query_arg( [
273 'better_payment_error_status' => 'error',
274 'better_payment_stripe_id' => $order_id,
275 'better_payment_widget_id' => $widget_id
276 ], get_permalink( $setting_data_page_id ) ),
277 'locale' => 'auto',
278 'payment_method_types' => [ 'card' ],
279 'client_reference_id' => time(),
280 'billing_address_collection' => 'required',
281 'metadata' => [
282 'order_id' => $order_id
283 ],
284 'line_items' => [
285 [
286 'amount' => (int) round( $amount * 100 ),
287 'currency' => $el_settings_currency,
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'),
289 'quantity' => 1
290 ]
291 ],
292 'payment_intent_data' => [
293 'capture_method' => 'automatic',
294 'description' => ! empty( $el_settings['better_payment_form_title'] ) ? esc_html__( $el_settings['better_payment_form_title'], 'better-payment' ) : esc_html__('Better Payment', 'better-payment'),
295 'metadata' => [
296 'order_id' => $order_id
297 ]
298 ]
299 ];
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
323 //Form fields data to send via email
324 $better_form_fields = [
325 'amount' => $el_settings_currency_symbol . $amount,
326 'referer_page_id' => $page_id,
327 'referer_widget_id' => $widget_id,
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 ),
336 ];
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
341 $better_form_fields = array_merge( $better_form_fields, $this->fetch_better_form_fields($el_settings, $_POST['fields']) );
342
343 if ( !empty( $better_form_fields['primary_first_name'] ) ) {
344 $request[ 'customer_name' ] = $better_form_fields['primary_first_name'];
345 }
346
347 if ( !empty( $better_form_fields['primary_last_name'] ) ) {
348 $request[ 'customer_name' ] = !empty( $request[ 'customer_name' ] ) ? $request[ 'customer_name' ] . ' ' . $better_form_fields['primary_last_name'] : $better_form_fields['primary_last_name'];
349 }
350
351 if ( !empty( $request[ 'customer_name' ] ) ) {
352 $request[ 'metadata' ][ 'customer_name' ] = $request[ 'customer_name' ];
353 $request[ 'payment_intent_data' ][ 'metadata' ][ 'customer_name' ] = $request[ 'customer_name' ];
354 unset( $request[ 'customer_name' ] );
355 }
356
357 if ( !empty( $better_form_fields['primary_email'] ) ) {
358 $request[ 'customer_email' ] = $better_form_fields['primary_email'];
359 $request[ 'metadata' ][ 'customer_email' ] = $request[ 'customer_email' ];
360 $request[ 'payment_intent_data' ][ 'metadata' ][ 'customer_email' ] = $request[ 'customer_email' ];
361
362 }
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
390 #ToDo: Need to prefill name field on stripe checkout
391 $response = wp_safe_remote_post(
392 'https://api.stripe.com/v1/checkout/sessions',
393 array(
394 'method' => 'POST',
395 'headers' => $header_info,
396 'body' => $request,
397 'timeout' => 70,
398 )
399 );
400
401 $response_ar = json_decode( $response[ 'body' ] );
402
403 if ( ! empty( $response_ar->payment_intent ) || ( ! empty( $response_ar->mode ) && 'subscription' === $response_ar->mode ) ) {
404 $campaign_id = $guarded['campaign_id'];
405
406 Handler::payment_create(
407 [
408 'amount' => $amount,
409 'order_id' => $order_id,
410 'payment_date' => date( 'Y-m-d H:i:s' ),
411 'source' => 'stripe',
412 'transaction_id' => sanitize_text_field($response_ar->payment_intent),
413 'customer_info' => maybe_serialize( $response_ar ),
414 'form_fields_info' => maybe_serialize( $better_form_fields ),
415 'obj_id' => sanitize_text_field($response_ar->id),
416 'status' => sanitize_text_field($response_ar->payment_status),
417 'currency' => $el_settings_currency,
418 'referer' => "widget",
419 'campaign_id' => $campaign_id,
420 ]
421 );
422 wp_send_json_success(
423 [
424 'stripe_data' => sanitize_text_field($response_ar->id),
425 'stripe_public_key' => sanitize_text_field( $better_payment_keys['public_key'] )
426 ]
427 );
428 } else {
429 $error_message = 'Something went wrong!';
430
431 if (isset($response_ar->error)){
432 $error_message = sanitize_text_field($response_ar->error->message);
433 }
434
435 wp_send_json_error( $error_message );
436 }
437 }
438
439 /**
440 * Get the paystack token
441 *
442 * @since 0.0.1
443 */
444 public function better_payment_paystack_get_token() {
445 if ( !check_admin_referer( 'better-payment', 'security' ) ) {
446 wp_send_json_error();
447 }
448
449 $error_message = '';
450
451 if( empty( $_POST[ 'setting_data' ]['page_id'] ) ){
452 $error_message = __( 'Page ID is missing', 'better-payment' );
453 }
454
455 if( empty( $_POST[ 'setting_data' ]['widget_id'] ) ){
456 $error_message = __( 'Widget ID is missing', 'better-payment' );
457 }
458
459 if( ! empty( $error_message ) ){
460 wp_send_json_error( esc_html( $error_message ) );
461 }
462
463 $page_id = intval( $_POST[ 'setting_data' ]['page_id'] );
464 $widget_id = sanitize_text_field( $_POST[ 'setting_data' ]['widget_id'] );
465 $el_settings = $this->better_payment_widget_settings( $page_id, $widget_id );
466
467 if ( empty( $el_settings ) ) {
468 wp_send_json_error( esc_html(__( 'Setting Data is missing', 'better-payment' )) );
469 }
470
471 if ( empty( $el_settings[ 'better_payment_paystack_public_key' ] ) || empty( $el_settings[ 'better_payment_paystack_secret_key' ] ) ) {
472 wp_send_json_error( esc_html(__( 'Paystack Key missing', 'better-payment' )) );
473 }
474
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() ) );
488 }
489
490 $amount = $guarded['amount'];
491 $amount_quantity = $guarded['quantity'];
492
493 $header_info = array(
494 'Authorization' => 'Bearer ' . sanitize_text_field( $el_settings[ 'better_payment_paystack_secret_key' ] ),
495 "Cache-Control: no-cache",
496 );
497
498 $order_id = Handler::new_order_id( 'paystack' );
499
500 $el_settings_currency = $el_settings[ 'better_payment_form_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];
505
506 $el_settings_currency = $guarded['currency'];
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
513 $request = [
514 'amount' => ( $amount * 100 ),
515 'currency' => $el_settings_currency,
516 'cart_id' => $order_id,
517 'callback_url' => add_query_arg( [
518 'better_payment_paystack_status' => 'success',
519 'better_payment_paystack_id' => $order_id,
520 'better_payment_widget_id' => $widget_id
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 ]
529 ];
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
538 //Form fields data to send via email
539 $better_form_fields = [
540 'amount' => $el_settings_currency_symbol . $amount,
541 'referer_page_id' => $page_id,
542 'referer_widget_id' => $widget_id,
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 ),
550 ];
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
555 $better_form_fields = array_merge( $better_form_fields, $this->fetch_better_form_fields($el_settings, $_POST['fields']) );
556
557 if ( !empty( $better_form_fields['primary_email'] ) ) {
558 $request[ 'email' ] = $better_form_fields['primary_email'];
559 }
560
561 if ( !empty( $better_form_fields['primary_first_name'] ) ) {
562 $request[ 'customer_name' ] = $better_form_fields['primary_first_name'];
563 }
564
565 if ( !empty( $better_form_fields['primary_last_name'] ) ) {
566 $request[ 'customer_name' ] = !empty( $request[ 'customer_name' ] ) ? $request[ 'customer_name' ] . ' ' . $better_form_fields['primary_last_name'] : $better_form_fields['primary_last_name'];
567 }
568
569 if ( !empty( $request[ 'customer_name' ] ) ) {
570 $request[ 'metadata' ][ 'customer_name' ] = $request[ 'customer_name' ];
571 unset( $request[ 'customer_name' ] );
572 }
573
574 $response = wp_safe_remote_post(
575 'https://api.paystack.co/transaction/initialize',
576 array(
577 'method' => 'POST',
578 'headers' => $header_info,
579 'body' => $request,
580 'timeout' => 70,
581 )
582 );
583
584 $response_ar = json_decode( $response[ 'body' ] );
585
586 if( empty( $response_ar->status ) || empty( $response_ar->data ) ){
587 $error_message = $response_ar->message ? sanitize_text_field($response_ar->message) : 'Something went wrong!';
588
589 if (isset($response_ar->error)){
590 $error_message = sanitize_text_field($response_ar->error->message);
591 }
592
593 wp_send_json_error( $error_message );
594 }
595
596 $campaign_id = $guarded['campaign_id'];
597
598 Handler::payment_create(
599 [
600 'amount' => $amount,
601 'order_id' => $order_id,
602 'payment_date' => date( 'Y-m-d H:i:s' ),
603 'source' => 'paystack',
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 ) : '',
606 'customer_info' => maybe_serialize( $response_ar ),
607 'form_fields_info' => maybe_serialize( $better_form_fields ),
608 'status' => 'unpaid',
609 'currency' => $el_settings_currency,
610 'referer' => "widget",
611 'campaign_id' => $campaign_id,
612 ]
613 );
614
615 $authorization_url = ! empty( $response_ar->data->authorization_url ) ? esc_url_raw( $response_ar->data->authorization_url ) : '';
616
617 wp_send_json_success(
618 [
619 'authorization_url' => $authorization_url,
620 ]
621 );
622 }
623
624 /**
625 * Get detailed product information
626 * @param array $product_ids Array of woo_product_ids and fluentcart_product_ids
627 *
628 * @since 1.4.5
629 */
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 }
656 }
657 }
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;
703 }
704
705 /**
706 * Widget settings
707 *
708 * @since 1.0.0
709 */
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 }
716
717 $settings = $this->get_elementor_widget_settings( $page_id, $widget_id );
718
719 return $settings;
720 }
721
722 /**
723 * Redirect to referer page
724 *
725 * @since 1.0.0
726 */
727 public function redirect_previous_page() {
728 $location = $_SERVER[ 'HTTP_REFERER' ];
729 wp_safe_redirect( $location );
730 exit();
731 }
732
733 /**
734 * Fetch form fields
735 *
736 * @since 0.0.4
737 */
738
739 public function fetch_better_form_fields($el_settings, $post_data_form_fields) {
740 $better_form_fields = array();
741
742 $better_payment_helper_obj = new Helper();
743 $post_data_primary_first_name = '';
744 $post_data_primary_last_name = '';
745 $post_data_primary_email = '';
746
747 $post_fields = $post_data_form_fields;
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
752 //in case of duplicate post name using the first one
753 if(isset($el_settings['better_payment_form_fields']) && count($el_settings['better_payment_form_fields'])){
754 foreach ($el_settings['better_payment_form_fields'] as $item) {
755
756 $item_field_name = $better_payment_helper_obj->titleToSnake($item["better_payment_field_name_heading"]);
757 if(!empty($item['better_payment_primary_field_type']) ){
758 $item_primary_field_type = $item['better_payment_primary_field_type'];
759
760 if( isset($post_fields[$item_primary_field_type]) ) {
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];
762 }
763
764 if( isset($post_fields[$item_field_name]) ) {
765 $post_fields[$item_field_name] = is_array($post_fields[$item_field_name]) ? $post_fields[$item_field_name][0] : $post_fields[$item_field_name];
766 }
767
768 if ( 'primary_first_name' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
769 $better_form_fields[$item_primary_field_type] = sanitize_text_field($post_fields[$item_primary_field_type]);
770 $post_data_primary_first_name = sanitize_text_field($post_fields[$item_primary_field_type]);
771
772 }else if ( 'primary_last_name' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
773 $better_form_fields[$item_primary_field_type] = sanitize_text_field($post_fields[$item_primary_field_type]);
774 $post_data_primary_last_name = sanitize_text_field($post_fields[$item_primary_field_type]);
775
776 }else if( 'primary_email' == $item_primary_field_type && isset($post_fields[$item_primary_field_type])){
777 $better_form_fields[$item_primary_field_type] = sanitize_email($post_fields[$item_primary_field_type]);
778 $post_data_primary_email = sanitize_email($post_fields[$item_primary_field_type]);
779
780 }else if ( 'primary_payment_amount' == $item_primary_field_type && isset($post_fields[$item_primary_field_type]) ){
781 $better_form_fields[$item_primary_field_type] = floatval($post_fields[$item_primary_field_type]);
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
786 } else {
787 $better_form_fields[$item_field_name] = !empty($post_fields[$item_field_name]) ? sanitize_text_field($post_fields[$item_field_name]) : '';
788 }
789
790 }
791 }
792 }
793
794 return $better_form_fields;
795 }
796 }
797