Skip to content

Instantly share code, notes, and snippets.

@wpmudev-sls
Last active May 14, 2022 17:09
Show Gist options
  • Save wpmudev-sls/0ff0dd003ed3f3e9b4ff0416da8ee25a to your computer and use it in GitHub Desktop.
Save wpmudev-sls/0ff0dd003ed3f3e9b4ff0416da8ee25a to your computer and use it in GitHub Desktop.
[Forminator] - Submit Form before PayPal payment. This snippet allows to submit the form before PayPal payment.
<?php
/**
* Plugin Name: [Forminator] - Submit Form before PayPal payment
* Plugin URI: https://premium.wpmudev.org/
* Description: This snippet allows to submit the form before PayPal payment
* Author: Panos Lyrakis @ WPMUDEV
* Author URI: https://premium.wpmudev.org/
* License: GPLv2 or later
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'WPMUDEV_Forminator_Submit_Before_PayPal' ) ) {
class WPMUDEV_Forminator_Submit_Before_PayPal {
private static $_instance = null;
private $forminator_entry_meta_tbl = null;
/**
* This is a prefix for the class field element that will hold the calculated amount
*/
private $form_pp_amount_holder_prefix = "forminator-paypal-amount-";
/**
* We set the id of the form that is being loaded localy available
* so we can use it when filtering PayPal's markup (form id not directly available there)
*/
private $form_id = null;
public static function get_instance() {
if( is_null( self::$_instance ) ){
self::$_instance = new WPMUDEV_Forminator_Submit_Before_PayPal();
}
return self::$_instance;
}
private function __construct() {
global $wpdb;
$this->forminator_entry_meta_tbl = "{$wpdb->prefix}frmt_form_entry_meta";
add_filter( 'forminator_render_form_markup', [ $this, 'prepare_form_markup' ], 20, 6 );
add_action( 'forminator_custom_form_submit_before_set_fields', [ $this, 'temporize_entry_id' ], 20, 3 );
add_filter( 'forminator_custom_form_ajax_submit_response', [ $this, 'inject_entry_to_response' ], 20 );
add_action( 'forminator_custom_form_before_save_entry', [ $this, 'validate_entry_inserted' ], 20, 2 );
add_action( 'wp_footer', [ $this, 'footer_js' ], 99 );
add_filter( 'forminator_custom_form_submit_field_data', [ $this, 'set_payment_initial_status' ], 20, 2 );
}
public function set_payment_initial_status( $field_data_array, $form_id ) {
$form = Forminator_Custom_Form_Model::model()->load( $form_id );
if ( ! $form->has_paypal_field() ) {
return $field_data_array;
}
$paypal_key = false;
foreach ( $form->fields as $key => $field ) {
$field = $field->to_formatted_array();
if ( isset( $field['type'] ) && 'paypal' === $field['type'] ) {
$paypal_key = $field[ 'element_id' ];
break;
}
}
if ( ! ! $paypal_key ) {
$data_key = array_search( $paypal_key, array_column( $field_data_array, 'name' ) );
if ( isset( $field_data_array[ $data_key ][ 'value' ][ 'status' ] ) ) {
$field_data_array[ $data_key ][ 'value' ][ 'status' ] = "pending";
}
}
return $field_data_array;
}
/*
** Prepares the form's html. Hides the PayPal buttons and injects the Submit button.
** Also provides some js to print a dynamic field containing the entry_id submitted before PayPal Payment button is shown
*/
public function prepare_form_markup( $html, $form_fields, $form_type, $form_settings, $form_design, $render_id ) {
if ( 'custom-form' != $form_type ) {
return $html;
}
$form_id = $form_settings['form_id'];
$form = Forminator_Custom_Form_Model::model()->load( $form_id );
if ( ! $form->has_paypal_field() ) {
return $html;
}
// Add an element outside the form that will hold the calculated amount so that is not removed on ajax submit
$form_openning = "<form";
$caclulations_holder = "<div id=\"{$this->form_pp_amount_holder_prefix}{$form_id}\"><input type=\"hidden\" id=\"{$this->form_pp_amount_holder_prefix}{$form_id}-field\" name=\"{$this->form_pp_amount_holder_prefix}{$form_id}\" /></div>";
$form_openning_new = "{$caclulations_holder}<form";
$buttons_field = "forminator-button-paypal";
$new_buttons_field = 'forminator-button-paypal forminator-hidden_" style="height: 0px; overflow:hidden;" ';
$form_closure = "</form>";
$new_form_closure = '<div class="forminator-row injected_confirmation_button"><div class="forminator-col"><div id="submit" class="forminator-field"><button class="forminator-button forminator-button-submit">Confirm</button></div></div></div></form>';
$replace_keys = array( $form_openning, $buttons_field, $form_closure );
$replace_values = array( $form_openning_new, $new_buttons_field, $new_form_closure );
$html = str_replace( $replace_keys , $replace_values, $html );
// Settings some fields outside form
// 1st is the new PayPal wrapper div which PP buttons will use to render
// 2nd is a fields outside form so it's value doesn't get affected by form submission
$html .= "<div id=\"wpmudev_forminator_pp_wrap-{$form_id}\" style=\"display:none;\"></div>";
$html .= "<input type=\"hidden\" id=\"wpmudev_forminator_pp_amount-{$form_id}\" value=\"17\" />";
return $html;
}
public function footer_js() {
global $post;
if ( ! $post instanceof WP_Post || ! has_shortcode( $post->post_content, 'forminator_form' ) ) {
return;
}
?>
<script type="text/javascript">
(($,d)=>{
if ( window.forminator_manage_paypal_payment ) {
return;
}
window.forminator_manage_paypal_payment = {
form_id: '',
render_element_id: "wpmudev_forminator_pp_wrap",
pp_buttons_shown: false,
amount_holder: '',
pluginName: "forminatorFrontPayPal",
defaults: {
type: 'paypal',
paymentEl: null,
paymentRequireSsl: false,
generalMessages: {},
},
run: function( form_id ) {
this.form_id = form_id;
this.render_element_id = `${this.render_element_id}-${form_id}`;
this.amount_holder = $( `#wpmudev_forminator_pp_amount-${form_id}` );
forminator_manage_paypal_payment.forminator_front_paypal();
//this.toggle_config_button();
setTimeout(
function()
{
forminator_manage_paypal_payment.toggle_config_button();
},100 );
$(d).ajaxComplete( function( event, xhr, settings ) {
let _this = forminator_manage_paypal_payment,
response_text = JSON.parse( xhr.responseText );
if ( ! _this.pp_buttons_shown ) {
_this.show_pp_buttons();
_this.pp_buttons_shown = true;
}
// After confirmation submitted
if (
typeof response_text.data.entry_id !== 'undefined' &&
typeof response_text.data.form_id !== 'undefined'
) {
let entry_id = response_text.data.entry_id
form_id = response_text.data.form_id,
form = $( `form#forminator-module-${form_id}` ),
field = $( '<input />', {
type: 'hidden',
name: '_forminator_entry_id_paypal_paid',
value: entry_id
} );
form.append( field );
// Set the calculated price in the new hidden field which will be used by PayPal buttons to render
if ( typeof response_text.data.amount !== 'undefined' && ! isNaN( response_text.data.amount ) ) {
_this.amount_holder.val( Number( response_text.data.amount ) );
$( `#${_this.render_element_id}` ).fadeIn( 300 );
}
}
// After payment response.
if (
typeof response_text.data.action !== 'undefined' &&
response_text.data.action === 'remove_paypal_buttons'
) {
$( `#${_this.render_element_id}` ).fadeOut( 300, function(){ $(this).remove(); } );
}
});
},
toggle_config_button: function(){
let form = $(d).find( `#forminator-module-${forminator_manage_paypal_payment.form_id}` ),
confirmation_button = form.find( '.injected_confirmation_button' );
confirmation_button.removeClass( 'forminator-hidden' );
confirmation_button.find( '.forminator-hidden' ).removeClass( 'forminator-hidden' );
if ( $( `#forminator-module-${forminator_manage_paypal_payment.form_id} .forminator-pagination-footer` ).length ) {
if ( form.find( '#forminator-submit' ).length ) {
confirmation_button.removeClass( 'forminator-hidden' );
confirmation_button.find( '.forminator-hidden' ).removeClass( 'forminator-hidden' );
} else {
confirmation_button.addClass( 'forminator-hidden' );
}
$(d).on( 'click', '.forminator-pagination-footer button', forminator_manage_paypal_payment.toggle_config_button );
} else {
confirmation_button.removeClass( 'forminator-hidden' );
confirmation_button.find( '.forminator-hidden' ).removeClass( 'forminator-hidden' );
}
},
show_pp_buttons: function() {
$.fn[forminator_manage_paypal_payment.pluginName] = function (options) {
return this.each(function () {
if (!$.data(this, forminator_manage_paypal_payment.pluginName)) {
$.data(this, forminator_manage_paypal_payment.pluginName, new WPMUDEV_ForminatorFrontPayPal(this, options));
}
});
};
},
forminator_front_paypal: function () {
// Avoid Plugin.prototype conflicts
$.extend( WPMUDEV_ForminatorFrontPayPal.prototype, {
init: function () {
if (!this.settings.paymentEl) {
return;
}
var self = this;
this.paypalData = self.settings.paymentEl;
this.render_paypal_button();
},
is_data_valid: function() {
var paypalData = this.configurePayPal(),
requireSsl = this.settings.paymentRequireSsl
;
if ( paypalData.amount <= 0 ) {
return false;
}
if ( requireSsl && 'https:' !== location.protocol ) {
return false;
}
return true;
},
is_form_valid: function() {
var validate = this.$el.validate(); // Get validate instance
var isValid = validate.checkForm(); // Valid?
validate.submitted = {}; // Reset immediate form field checking mode
return isValid;
},
render_paypal_button: function () {
var $form = this.$el,
self = this,
paypalData = this.configurePayPal(),
$target_message = $form.find('.forminator-response-message'),
paypalActions,
error_msg = ForminatorFront.cform.gateway.error,
requireSsl = this.settings.paymentRequireSsl,
generalMessage = this.settings.generalMessages,
style_data = {
shape: paypalData.shape,
color: paypalData.color,
label: paypalData.label,
layout: paypalData.layout,
height: parseInt( paypalData.height ),
};
if( paypalData.layout !== 'vertical' ) {
style_data.tagline = paypalData.tagline;
}
paypal.Buttons({
onInit: function(data, actions) {
actions.disable();
if ( paypalData.amount_type === 'variable' && paypalData.variable !== '' ) {
paypalData.amount = self.get_field_calculation( forminator_manage_paypal_payment.amount_holder );
}
// Listen for form field changes
$form.find('input, select, textarea').change( function() {
if ( self.is_data_valid() && self.is_form_valid() ) {
actions.enable();
}
});
// Check if the form is valid on init
if ( self.is_data_valid() && self.is_form_valid() ) {
actions.enable();
}
},
env: paypalData.mode,
style: style_data,
onClick: function () {
if( ! $form.valid() && paypalData.amount <= 0 ) {
$target_message.removeClass('forminator-accessible').addClass('forminator-error').html('').removeAttr( 'aria-hidden' );
$target_message.html('<label class="forminator-label--error"><span>' + generalMessage.payment_require_amount_error + '</span></label>');
self.focus_to_element($target_message);
} else if ( requireSsl && 'https:' !== location.protocol ) {
$target_message.removeClass('forminator-accessible').addClass('forminator-error').html('').removeAttr( 'aria-hidden' );
$target_message.html('<label class="forminator-label--error"><span>' + generalMessage.payment_require_ssl_error + '</span></label>');
self.focus_to_element($target_message);
}
if ( paypalData.amount_type === 'variable' && paypalData.variable !== '' ) {
paypalData.amount = self.get_field_calculation( forminator_manage_paypal_payment.amount_holder );
}
},
createOrder: function(data, actions) {
$form.addClass('forminator-partial-disabled');
return actions.order.create({
purchase_units: [{
amount: {
value: paypalData.amount
}
}]
});
},
onApprove: function(data, actions) {
if( typeof self.settings.has_loader !== "undefined" && self.settings.has_loader ) {
// Disable form fields
$form.addClass('forminator-fields-disabled');
$target_message.html('<p>' + self.settings.loader_label + '</p>');
$target_message.removeAttr("aria-hidden")
.prop("tabindex", "-1")
.removeClass('forminator-success forminator-error')
.addClass('forminator-loading forminator-show');
self.focus_to_element($target_message);
}
return actions.order.capture().then(function(details) {
var transaction_id = details['purchase_units'][0]['payments']['captures'][0]['id'];
if( typeof transaction_id === 'undefined' ) {
return false;
}
var get_nonce = $form.find('input[name="forminator_nonce"]').val(),
payment_data = {
details: details,
payment_id: transaction_id,
forminator_nonce: get_nonce,
action: 'forminator_submit_form_custom-forms'
};
jQuery.ajax({
type: 'POST',
url: ForminatorFront.ajaxUrl,
data: jQuery.param(payment_data),
success: function (response) {
if ( response && response.data.success === true ) {
$form.find('.forminator-paypal-input').val( transaction_id );
$form.trigger('submit');
} else {
error_msg = response.data.error ? response.data.error : error_msg;
$target_message.removeClass('forminator-accessible').addClass('forminator-error').html('').removeAttr( 'aria-hidden' );
$target_message.html('<label class="forminator-label--error"><span>' + error_msg + '</span></label>');
self.focus_to_element($target_message);
}
},
error: function (response) {
error_msg = response.data ? response.error_message : error_msg;
$target_message.removeClass('forminator-accessible').addClass('forminator-error').html('').removeAttr( 'aria-hidden' );
$target_message.html('<label class="forminator-label--error"><span>' + error_msg + '</span></label>');
self.focus_to_element($target_message);
}
});
});
},
onCancel: function (data, actions) {
if( typeof self.settings.has_loader !== "undefined" && self.settings.has_loader ) {
// Enable form fields
$form.removeClass('forminator-fields-disabled forminator-partial-disabled');
$target_message.removeClass('forminator-loading');
}
return actions.redirect();
},
onError: function () {
if( typeof self.settings.has_loader !== "undefined" && self.settings.has_loader ) {
// Enable form fields
$form.removeClass('forminator-fields-disabled forminator-partial-disabled');
$target_message.removeClass('forminator-loading');
}
$target_message.removeClass('forminator-accessible').addClass('forminator-error').html('').removeAttr( 'aria-hidden' );
$target_message.html('<label class="forminator-label--error"><span>' + error_msg + '</span></label>');
self.focus_to_element($target_message);
},
}).render( '#' + forminator_manage_paypal_payment.render_element_id );
},
configurePayPal: function () {
var self = this,
paypalConfig = {
form_id: this.getPayPalData('form_id'),
sandbox_id: this.getPayPalData('sandbox_id'),
currency: this.getPayPalData('currency'),
live_id: this.getPayPalData('live_id'),
amount: 0
};
paypalConfig.color = this.getPayPalData('color') ? this.getPayPalData('color') : 'gold';
paypalConfig.shape = this.getPayPalData('shape') ? this.getPayPalData('shape') : 'rect';
paypalConfig.label = this.getPayPalData('label') ? this.getPayPalData('label') : 'checkout';
paypalConfig.layout = this.getPayPalData('layout') ? this.getPayPalData('layout') : 'vertical';
paypalConfig.tagline = this.getPayPalData('tagline') ? this.getPayPalData('tagline') : 'true';
paypalConfig.redirect_url = this.getPayPalData('redirect_url') ? this.getPayPalData('redirect_url') : '';
paypalConfig.mode = this.getPayPalData('mode');
paypalConfig.locale = this.getPayPalData('locale') ? this.getPayPalData('locale') : 'en_US';
paypalConfig.debug_mode = this.getPayPalData('debug_mode') ? this.getPayPalData('debug_mode') : 'disable';
paypalConfig.amount_type = this.getPayPalData('amount_type') ? this.getPayPalData('amount_type') : 'fixed';
paypalConfig.variable = this.getPayPalData('variable') ? this.getPayPalData('variable') : '';
paypalConfig.height = this.getPayPalData('height') ? this.getPayPalData('height') : 55;
var amountType = this.getPayPalData('amount_type');
if (amountType === 'fixed') {
paypalConfig.amount = this.getPayPalData('amount');
} else if( amountType === 'variable' && paypalConfig.variable !== '' ) {
paypalConfig.amount = this.get_field_calculation( forminator_manage_paypal_payment.amount_holder );
}
return paypalConfig;
},
getPayPalData: function (key) {
if (typeof this.paypalData[key] !== 'undefined') {
return this.paypalData[key];
}
return null;
},
get_field_calculation: function (element) {
value = Number(element.val());
return isNaN(value) ? 0 : value;
},
focus_to_element: function ($element, fadeout) {
fadeout = fadeout || false;
if( fadeout ) {
fadeout = this.settings.fadeout;
}
var fadeout_time = this.settings.fadeout_time;
// force show in case its hidden of fadeOut
$element.show();
$('html,body').animate({scrollTop: ($element.offset().top - ($(window).height() - $element.outerHeight(true)) / 2)}, 500, function () {
if (!$element.attr("tabindex")) {
$element.attr("tabindex", -1);
}
$element.focus();
if (fadeout) {
$element.show().delay( fadeout_time ).fadeOut('slow');
}
});
},
});
}
};
$(d).on( 'after.load.forminator', function( e, form_id ){
forminator_manage_paypal_payment.run( form_id );
} );
// The actual plugin constructor
function WPMUDEV_ForminatorFrontPayPal(element, options) {
this.element = element;
this.$el = $(this.element);
// jQuery has an extend method which merges the contents of two or
// more objects, storing the result in the first object. The first object
// is generally empty as we don't want to alter the default options for
// future instances of the plugin
this.settings = $.extend({}, forminator_manage_paypal_payment.defaults, options);
this._defaults = forminator_manage_paypal_payment.defaults;
this._name = forminator_manage_paypal_payment.pluginName;
this.paypalData = null;
this.init();
}
})(jQuery,document);
</script>
<?php
}
/*
** Sets the entry id in a temporary $_POST key, _forminator_temp_entry_id
** By default it is not available. Setting it to the $_POST Global Var we can add it to ajax
** response on forminator_custom_form_ajax_submit_response filter
*/
public function temporize_entry_id( $entry, $form_id, $field_data_array ) {
$form = Forminator_Custom_Form_Model::model()->load( $form_id );
if ( ! $form->has_paypal_field() ) {
return;
}
$paypal_field = $this->get_form_paypal_field( $form );
$key = array_search( $paypal_field[ 'element_id' ], array_column( $field_data_array, 'name' ) );
$amount = isset( $field_data_array[ $key ][ 'value' ][ 'amount' ] ) ? $field_data_array[ $key ][ 'value' ][ 'amount' ] : false;
if ( ! $amount ) {
return;
}
$_POST[ '_forminator_temp_entry_id' ] = $entry->entry_id;
$_POST[ '_forminator_temp_form_id' ] = $form_id;
$_POST[ '_forminator_temp_amount' ] = $amount;
}
/*
** Inject the form id and entry id in ajax response
*/
public function inject_entry_to_response( $response ) {
if (
isset( $_POST[ '_forminator_temp_entry_id' ] ) && is_numeric( $_POST[ '_forminator_temp_entry_id' ] ) &&
isset( $_POST[ '_forminator_temp_form_id' ] ) && is_numeric( $_POST[ '_forminator_temp_form_id' ] )
) {
$response['amount'] = number_format( $_POST[ '_forminator_temp_amount' ], 2 );
$response['entry_id'] = (int) $_POST[ '_forminator_temp_entry_id' ];
$response['form_id'] = (int) $_POST[ '_forminator_temp_form_id' ];
$response['message'] = __( "Your information has been submitted. In order to verify please complete payment bellow.", Forminator::DOMAIN );
}
return $response;
}
public function get_form_paypal_field ( $form ) {
$fields = $form->fields;
foreach ( $fields as $field ) {
$field = $field->to_formatted_array();
if ( isset( $field['type'] ) && 'paypal' === $field['type'] ) {
return $field;
}
}
}
/**
* Check if paid entry has been inserted
*/
public function validate_entry_inserted( $form_id, $key ) {
if ( isset( $_POST[ '_forminator_entry_id_paypal_paid' ] ) && is_numeric( $_POST[ '_forminator_entry_id_paypal_paid' ] ) ) {
$form = Forminator_Custom_Form_Model::model()->load( $form_id );
if ( ! $form->has_paypal_field() ) {
return;
}
global $wpdb;
$fields = $form->get_fields();
$entry_id = intval( $_POST[ '_forminator_entry_id_paypal_paid' ] );
foreach ( $fields as $field ) {
$field = $field->to_formatted_array();
if ( isset( $field['type'] ) && 'paypal' === $field['type'] ) {
$paypal_key = $field['element_id'];
if ( ! isset( $_POST[ $paypal_key ] ) ) {
continue;
}
$transaction_id = sanitize_text_field( $_POST[ $paypal_key ] );
$meta_result = $wpdb->get_results(
$wpdb->prepare(
"SELECT `meta_value` FROM {$this->forminator_entry_meta_tbl} WHERE `entry_id`=%d AND `meta_key`=%s",
$entry_id, $paypal_key
)
);
$meta = ( is_object( $meta_result[0] ) && isset( $meta_result[0]->meta_value ) ) ? $meta_result[0]->meta_value : null;
if ( ! is_null( $meta ) ) {
$meta_value = maybe_unserialize( $meta );
$meta_value[ 'transaction_id' ] = $transaction_id;
$meta_value[ 'status' ] = 'success';
$meta_value = maybe_serialize( $meta_value );
$wpdb->query(
$wpdb->prepare(
"UPDATE {$this->forminator_entry_meta_tbl} SET `meta_value`=%s WHERE `entry_id`=%d AND `meta_key`=%s",
$meta_value, $entry_id, $paypal_key
)
);
$response = array(
'message' => __( "Thank you! Payment was successful!", Forminator::DOMAIN ),
'action' => 'remove_paypal_buttons',
'success' => true
);
wp_send_json_success( $response );
}
}
}
}
}
}
if ( ! function_exists( 'wpmudev_forminator_submit_before_payPal' ) ) {
function wpmudev_forminator_submit_before_payPal() {
return WPMUDEV_Forminator_Submit_Before_PayPal::get_instance();
};
add_action( 'plugins_loaded', 'wpmudev_forminator_submit_before_payPal', 10 );
}
}
@FJTrax
Copy link

FJTrax commented Jan 5, 2021

Hi There
I want to know where do i put the above code to get forminator to submit first and the pay via Paypal?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment