question regarding Woocommerce payment gateways.
I’ve customized a payment gateway to take purchase orders, and its loading correctly from a plugin, and appearing on the checkout page the way id like it to; I think its a pretty good start.
Anyway, I have a non-standard form entry (purchase order number) that id like people to fill out, but I don’t know how to attach it to my custom payment gateway so that it appears on the resulting admin side page.
This all works fine:
function payment_fields(){
if ( $description = $this->get_description() )
echo wpautop( wptexturize( $description ) );
global $woocommerce;
?>
<form class="form-horizontal" role="form" id="bv-form">
<fieldset>
<!-- Form Name -->
<legend>Purchase Order</legend>
<!-- Text input-->
<div class="form-group">
<label class="control-label" for="po_no">PO Number</label>
<div class="">
<input id="po_no" name="po_no" type="text" placeholder="" class="form-control input-md">
</div>
</div>
...
But when I get here, I don’t know how to modify process_payment( $order_id ) (or what to add to functions.php) to grab the form values from the payment fields. I see that there are $order->billingAddress1 etc., how would I submit the extra couple form fields along with the order, and then secondly, how would I access that to get po_no out of the newly created WC_Order?
WP: 3.9.2
WC: 2.1.11
Thanks for your help!
If you are creating a custom Payment Gateway then you should be implementing process_payment() within it. This code will have access to the $_POST variables were submitted by the payment form, so you can refer to it as:
$po_number = ( isset( $_POST['po_no'] ) )? $_POST['po_no'] : 0;
That said it sounds like from your question you may not be actually writing a custom gateway and are instead trying to attach custom fields to the order. If you want to hook into another gateway (like PayPal, Authorize.net, etc) then you should use the WooCommerce action hooks that are called as part of WC_Order.payment_complete() which is what the gateway calls after it collects payment. The actions are woocommerce_pre_payment_complete which runs before anything else and takes the order id as an argument, and woocommerce_payment_complete after stock/statuses/etc are set.
add_action( 'woocommerce_payment_complete', 'my_woocommerce_payment_complete');
function my_woocommerce_payment_complete( $order_id ){
$po_number = ( isset( $_POST['po_no'] ) )? $_POST['po_no'] : 0;
update_post_meta( $order_id, 'po_number', $po_number );
}
You can then display this custom field on the Order Edit page by adding a meta box. You'll need to hook into save_post and change the po_number to an input if you want to make it editable.
add_action( 'add_meta_boxes', 'po_number_metabox' );
function add_po_number_metabox(){
global $post;
if ( $post->post_type = 'shop_order' ){
add_meta_box( 'po-number', 'Title', 'po_number_metabox', 'shop_order', 'side', 'default');
}
}
function po_number_metabox(){
global $post;
$po_number = get_post_meta( $post->ID, 'po_number', true);
echo "PO:> {$po_number}";
}
Related
I am using ACF to add fields to my vendors' dashboard profile pages. I currently have a test ACF field loading the field from only the WP Admin profile page on all the vendors' product listing page using this simple hook in my child theme's functions.php:
add_action( 'woocommerce_archive_description', 'vendor_profile', 7 );
function vendor_profile() { ?>
<?php if(get_field('founded_on')) { ?>
<?php the_field('founded_on'); ?>
<?php }
}
Perhaps I'm pulling the wrong hook, but I can't seem to find the right hook in the Product Vendor frontend page.
I need help customizing it so that when you are on a vendor's product page, it pulls the ACF fields from that particular vendor's profile. Currently, it partially works; however it only pulls the WP main admin's data for all the different vendors'.
I know there is a way to pull the vendor's ID for each particular vendor's page and have it load their data for their page, but my php knowledge is very limited. I usually just hunt for existing code and tweak it. Unfortunately I haven't found any solutions that have worked, and this is the closest I've come to getting custom fields to work on a vendor's page.
Or if anyone can point me to a better solution to allow me to create customer fields for a vendor to fill out that will be loaded on their front end page, that would be great. I've tried Nicola Mustone's solution ( here ), which would have been perfect, except I couldn't get it to load the new custom fields on the vendor's store profile form page, nor have it load the fields into that vendor's storefront page. Based on comments, it only shows up for the site's Admin and only they can edit it. There's no visible way to have it load on the storefront, which defeats the purpose.
I imagine that the providers are users with a certain level within the WordPress system?
Considering that this is your case, the ACF fields need some additional parameters to become visible:
$post_id = false; // current post
$post_id = 1; // post ID = 1
$post_id = "user_2"; // user ID = 2
$post_id = "category_3"; // category term ID = 3
$post_id = "event_4"; // event (custom taxonomy) term ID = 4
$post_id = "option"; // options page
$post_id = "options"; // same as above
$value1 = the_field( 'my_field', $post_id );
$value2 = get_field( 'my_field', $post_id );
take some examples found in the ACF documentation, but in your particular case you have to pass the user's ID
the_field('founded_on', 'user_' . $user->ID );
echo get_field('founded_on', 'user_' . $user->ID );
function documentation the_field()
You need to pull the user ID of the user and then use the format user_{$user->ID} for the post ID as the second parameter of the ACF field.
If I understand your question, then this should work.
add_action( 'woocommerce_archive_description', 'vendor_profile', 7 );
function vendor_profile() {
$user = wp_get_current_user();
if ( get_field( 'founded_on', 'user_' . $user->ID ) ) {
the_field( 'founded_on', 'user_' . $user->ID );
}
}
So I'm trying to follow this tutorial but what I want to do is a bit different.
I want to have a unique link per coupon generator and the coupon is per customer and can only be used once (e.g. example.com/coupon/7AD8679adO).
Now I want to have a form for this page and just have a input boxes for users like first_name, last_name, and email. And the email field is the identifier that the current url coupon will be registered to that email.
I also tried to research and I found out that there's URL Coupons feature from Woocommerce (Not sure though if this is exactly what Im looking for), but suddenly, it is not free. So, any idea with this?
I have implemented rewrite rule in "twentytwentyone" theme, but you may implement it elsewhere in wordpress.
For more info, please refer to this.
https://developer.wordpress.org/reference/functions/add_rewrite_rule/
Implement in wordpress Theme
Add the following code to the bottom of next file
~/wp-content/themes/twentytwentyone/functions.php
// wp-content/themes/twentytwentyone/functions.php
...
// rewrite-rule
add_action( 'init', function() {
add_rewrite_rule( 'coupon/([a-zA-Z0-9]+)[/]?$', 'index.php?coupon=$matches[1]', 'top' );
} );
// whitelist "coupon" param
add_filter( 'query_vars', function( $query_vars ) {
$query_vars[] = 'coupon';
return $query_vars;
} );
// set template php file
add_action( 'template_include', function( $template ) {
if ( get_query_var( 'coupon' ) == false || get_query_var( 'coupon' ) == '' ) {
return $template;
}
//You can return wherever you want to go
return get_template_directory() . '/template-coupon.php';
} );
Create ~/wp-content/themes/twentytwentyone/template-coupon.php
// wp-content/themes/twentytwentyone/template-coupon.php
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
echo get_query_var( 'coupon' )." from POST".PHP_EOL;
var_dump($_POST);
// check coupon validation here
// $query = $wpdb->prepare("SELECT 1"); // Your query
// $result = $wpdb->get_var($query);
// var_dump($result);
} else {
echo get_query_var( 'coupon' )." from redirection GET".PHP_EOL;
// do something
}
?>
<?php //get_template_part( 'header' ); ?>
<form action="" method="POST">
<label for="first_name">First Name</label><input type="text" name="first_name" id="first_name"><br/>
<label for="last_name">Last Name</label><input type="text" name="last_name" id="last_name"><br/>
<label for="email">Email</label><input type="text" name="email" id="email"><br/>
<input type="submit">
</form>
<?php //get_template_part( 'footer' ); ?>
Implement in wordpress Plugin (Hello Dolly)
Add the above step 1 code to the bottom of next file
~/wp-content/plugins/hello.php
change
return get_template_directory() . '/template-coupon.php';
to
return __DIR__ . '/template-coupon.php';
Add the above step 2 code to ~/wp-content/plugins/template-coupon.php
Refresh permalinks cache
When you add/modify URL part in step1, you need to refresh permalniks cache, otherwise wordress does not apply rewrite rule.
Settings > Permalinks > Save Changes
Result
check http://your-domain/coupon/7AD8679adO/
I'm pretty sure you've seen this answer elsewhere, but I hope this is written clearly enough for you to understand it better.
It seems as though you may need to implement the "add_rewrite_rule()" in order for WordPress to recognise the element (the unique "/coupon/URL") and pass on the resulting code to the right page.
Additionally, it can sometimes be a bit more difficult to verify a coupon code without the user being logged into an account that can be relied on.
Therefore, I would recommend trying to store the uniquely generated "/coupon/URL" in the user's meta data, and then querying for the unique in the user's meta data, and validating the request if the current user and the related user's ID match. This is because the code doesn't just get tied to the email address, but also to a registered user.
I have created a plugin for wordpress that show a form to users by shortcode, i want submit the form ,get the values and store in defined table that created when my plugin is activated.
Now i don't know where this form should be submit. I define a submenu for plugin in admin panel (and set form action to this submenu slug) to get that value and store in db, but only those how logged in can submit that form.
You can handle any form submission using template_redirect hook if your form is in the frontend. If your form in the backend then you can use admin_init hook
Say, Your form code looks like in front end
<form method="post">
<input type="text" name="input_1"/>
<input type="number" name="input_2"/>
<?php wp_nonce_field( 'name_of_your_nonce_action', 'name_of_your_nonce_field' ) ?>
<input type="submit" name="submit_form" value="Submit" />
</form>
Now in theme functions.php file, you can handle this form like
<?php
add_action( 'template_redirect', 'wp1213_handle_custom_form', 11 );
function wp1213_handle_custom_form() {
if( ! isset( $_POST['submit_form'] ) ) {
return;
}
if( ! wp_verify_nonce( $_POST['name_of_your_nonce_field'], 'name_of_your_nonce_action' ) ) {
return;
}
// Then you can handle all post data ($_POST) and save those data in db
.......
}
I'm using the [Product Add-ons][1] extension for WooCommerce, which allows custom fields for products. This automatically displays on the single product template.
By some trial and error with the single product template, it appears to hook in somewhere in woocommerce_single_product_summary (single_meta?).
I need to get this same set of form fields to display on the archive-product template (the list of products). In my case, this is a field for a card request and one for delivery date. Both are needed before the product can be added to the cart from the product archive. I'm not sure if there is a function I can call for this, if it involves some more advanced coding.
Per Pelmered's answer, I was able to get the add-on fields to show up with adding this to functions.php:
add_action( 'woocommerce_after_shop_loop_item_title', array($GLOBALS['Product_Addon_Display'], 'display'), 10);
1ST ATTEMPT
Then, the problem was that the product archive was not creating a form element, only an anchor link for the add to cart button. So I've tried manually putting in a form. Code from content-product.php:
<?php do_action( 'woocommerce_before_shop_loop_item' ); ?>
<h2><?php the_title(); ?></h2>
<?php // MANUALLY PUT IN FORM, BECAUSE HOOKS ONLY PUT IN A BUTTON AND NO FORM. NEEDED FOR ADD-ON FIELDS ?>
<form class="cart" method="post" enctype='multipart/form-data'>
<?php
/**
* woocommerce_after_shop_loop_item_title hook
*
* #hooked woocommerce_template_loop_rating - 5
* #hooked woocommerce_template_loop_price - 10
*/
do_action( 'woocommerce_after_shop_loop_item_title' );
?>
<div itemprop="description">
<?php the_content(); ?>
</div>
<input type="hidden" name="add-to-cart" value="<?php echo esc_attr( $product->id ); ?>" />
<button type="submit" class="single_add_to_cart_button button alt"><?php echo $product->single_add_to_cart_text(); ?></button>
</form>
<?php //do_action( 'woocommerce_after_shop_loop_item' ); ?>
This worked, but kind of messes up the AJAX submission and flash messages. After clicking the add to cart button, the page appears to refresh, and then it looks like nothing happened. But when you go to another page, it displays the messages (and multiple can stack).
2ND ATTEMPT
So I saw that the original AJAX cart button (not within a form) was using a query string to send the product ID. So I am now attempting to tack on additional parameters by modifying the add to cart JS. This worked. What I did added as the accepted answer.
I've found a solution that works, using jQuery to add the form field values to the AJAX request. This requires overriding add-to-cart.js with your own version, so future updates to that file may need to be merged.
In functions.php
// display add-on fields in woocommerce_after_shop_loop_item_title
add_action( 'woocommerce_after_shop_loop_item_title', array($GLOBALS['Product_Addon_Display'], 'display'), 10);
// Our custom cart JS to allow for product add-on fields on product archive
add_action( 'wp_enqueue_scripts', 'load_woo_scripts', 9 );
function load_woo_scripts() {
wp_enqueue_script( 'wc-add-to-cart', get_template_directory_uri() . '/js/add-to-cart.js', array( 'jquery' ), WC_VERSION, true );
}
Duplicate /assets/add-to-cart.js from the woocommerce plugin folder, and add to the folder referenced in load_woo_scripts. Then add the following after the var date = { statement (line 21).
// =========================================================================
// ADD PRODUCT ADD-ON FIELDS TO SUBMISSION (to allow from product archive)
// Store name and value of fields in object.
var addonfields = {};
$(this).parent().find(".after-shop-loop-item-title :input").each(function() {
addonfields[this.name] = $(this).val();
});
// Merge new fields and existing data
data = $.extend(data, addonfields);
// Testing: final value of data
console.log( data );
// =========================================================================
This adds the names and values of all input fields within the .after-shop-loop-item-title div. I've added this div/class to my theme's woocommerce/content-product.php template:
<?php do_action( 'woocommerce_before_shop_loop_item' ); ?>
<h2><?php the_title(); ?></h2>
<div itemprop="description">
<?php the_content(); ?>
</div>
<div class="after-shop-loop-item-title">
<?php
/**
* woocommerce_after_shop_loop_item_title hook
*
* #hooked woocommerce_template_loop_rating - 5
* #hooked woocommerce_template_loop_price - 10
*/
do_action( 'woocommerce_after_shop_loop_item_title' );
?>
</div>
<?php do_action( 'woocommerce_after_shop_loop_item' ); ?>
Note that in my case, I am only using a textarea and a text input. Neither of which changes the price. More advanced usage of the extension may require more work.
I really hope that WooCommerce makes this easier in the future...
It's impossible to give you an exact copy-paste solution without access to the code, but it should be possible to attach the same function that is being hooked.
The hook that adds this to the single product page probably looks like this:
add_action( 'woocommerce_single_product_summary', array($this, 'woocommerce_product_add_ons_something'));
To attach this action to the achive template as well you should probably be able to do something like this:
add_action( 'woocommerce_after_shop_loop_item_title', array('WC_Product_Add_On', 'woocommerce_product_add_ons_something'));
WC_Product_Add_On is the class name of the plguin and needs to be changed to match the class that has the function/method in the action. Put this code in the functions.php file in your theme.
See if it works. If not, give me more information and I can expand my answer.
If anyone want to display woocommerce product addons fields everywhere, you can do this:
$custom_field = get_post_meta( $product_id, '_product_addons', false );
foreach( $custom_field as $field => $value) {
foreach( $value as $val => $v) {
echo $v["name"]; // or other field that you want!
}
}
Enjoy!
I wanted to create a plugin to batch manage posts' custom field data. I know I can add post meta by adding a meta box in the post edit screen and using add_action('save_post', 'function_to_update_meta') to trigger add meta functions.
But I don't know how to trigger the add_post_meta function in an admin menu page (such as a custom admin menu). How to do that?
Thank you in advance!
The example given in Wordpress' codex is probably the best and most secure in the way of processing information:
Add Meta Box
Copy and paste it and then fiddle around with it to get a good idea on how to control your posts and pages.
The nice part is that you don't need to worry about checking if you need to Add vs Update a given Post Meta field. Using Update Post Meta will ensure that the proper action is taken for you, even if the field doesn't exist.
The same goes for Update Option if you want to add some global controls that your plugin/theme might use.
BREAKDOWN EXAMPLE:
add_action( 'add_meta_boxes', 'myplugin_add_custom_box' );
add_action( 'save_post', 'myplugin_save_postdata' );
These are the action hooks. The first one is executed when meta boxes are being populated within the post editor, and the second is executed when a post is added OR updated.
function myplugin_add_custom_box()
{
add_meta_box(
'myplugin_sectionid',
__( 'My Post Section Title', 'myplugin_textdomain' ),
'myplugin_inner_custom_box',
'post'
);
add_meta_box(
'myplugin_sectionid',
__( 'My Post Section Title', 'myplugin_textdomain' ),
'myplugin_inner_custom_box',
'page'
);
}
This function is called by the 'add_meta_boxes' action hook. Notice the name of the function and the second argument of the action hook are exactly the same. This registers your meta boxes, which post types they're supposed to appear, and what callback is used to generate the form contained inside.
function myplugin_inner_custom_box( $post )
{
wp_nonce_field( plugin_basename( __FILE__ ), 'myplugin_noncename' );
$value = get_post_meta($post->ID, 'myplugin_new_field') ? get_post_meta($post->ID, 'myplugin_new_field') : 'New Field';
echo '<label for="myplugin_new_field">';
_e("Description for this field", 'myplugin_textdomain' );
echo '</label> ';
echo '<input type="text" id="myplugin_new_field" name="myplugin_new_field" value="'.$value.'" size="25" />';
}
This is the function that is called by the registered meta boxes to generate the form automatically. Notice how the function is called 'myplugin_inner_custom_box' and the 3rd argument in your meta box registration is also called 'myplugin_inner_custom_box'.
The wp_nonce_field() generates a hidden field in your form to verify that data being sent to the form actually came from Wordpress, and can also be used to end the function in case other plugins are using the 'save_post' action hook.
Notice also that the $post object is being passed in as an argument. This will allow you to use certain properties from the post object. I've taken the liberty of checking to see if there is get_post_meta() returns anything with the given post ID. If so, the field is filled with that value. If not, it is filled with 'New Field'.
function myplugin_save_postdata( $post_id )
{
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
if ( !wp_verify_nonce( $_POST['myplugin_noncename'], plugin_basename( __FILE__ ) ) )
return;
if ( 'page' == $_POST['post_type'] )
{
if ( !current_user_can( 'edit_page', $post_id ) )
return;
}
else
{
if ( !current_user_can( 'edit_post', $post_id ) )
return;
}
$mydata = $_POST['myplugin_new_field'];
update_post_meta($post_id, 'myplugin_new_field', $mydata);
}
This is the function that is called by the 'save_post' action hook. Notice how the second argument of the second action hook and this function are both called 'myplugin_save_postdata'. First, there are a series of verifications our plugin must pass before it can actually save any data.
First, we don't want our meta boxes to update every time the given post is auto-updating. If the post is auto-updating, cancel the process.
Secondly, we want to make sure the nonce data is available and verify it. If no nonce data is available or is not verified, cancel the process.
Thirdly, we want to make sure the given user has the edit_page permission. The function first checks the post type, and then checks the appropriate permission. If the user does not have that permission, cancel the process.
Lastly, our plugin has finally been verified and we want to save the information. I took the liberty of adding in the final update_post_meta() line to show you how it all comes together.
Notice how $post_id was passed into the function as an argument. This is one of the pieces needed for the update_post_meta() function. The key was named 'myplugin_new_field' and the value of that metadata is now saved as whatever you input into that custom input field in your custom meta box.
That's about as easy as I can explain the whole process. Just study it, and get your hands dirty with code. The best way to learn is through application rather than theory.
The answer was from the same question I asked somewhere else
And I created my version of example
I added some console.log function for testing, but this is basically doning the same thing as #Chris_() answer:
Menu callback function to generate menu content (PHP):
function ajax_menu_callback() {
?>
<div class="wrap">
<div id="icon-themes" class="icon32"></div>
<h2>Test</h2>
<br />
<form>
<input id="meta" type ="text" name="1" value="<?php echo esc_html( get_post_meta( 1, 'your_key', true) ); ?>" />
<?php submit_button(); ?>
</form>
</div>
<?php
}
Then the javascript to print on the admin side (javascript, don't forget to include a jquery library):
jQuery(document).ready(function() {
$("form").submit(function() {
console.log('Submit Function');
var postMeta = $('input[name="1"]').val();
console.log(postMeta);
var postID = 1;
var button = $('input[type="submit"]');
button.val('saving......');
$.ajax({
data: {action: "update_meta", post_id: postID, post_meta: postMeta, },
type: 'POST',
url: ajaxurl,
success: function( response ) { console.log('Well Done and got this from sever: ' + response); }
}); // end of ajax()
return false;
}); // end of document.ready
}); // end of form.submit
Then the PHP function handle update_post_meta (PHP):
add_action( 'wp_ajax_update_meta', 'my_ajax_callback' );
function my_ajax_callback() {
$post_id = $_POST['post_id'];
$post_meta = $_POST['post_meta'];
update_post_meta( $post_id, 'your_key', $post_meta );
echo 'Meta Updated';
die();
} // end of my_ajax_callback()