1

I'm using WordPress 3.9.1 and the latest version of WooCommerce (2.1.10) and I'm trying to create a "Pay your invoice" page where people put their invoice number and amount and they go directly to the checkout page.

The way I'm doing it now:

I have a page with a form where people put in the amount:

<form action="#" onsubmit="location.href = 'http://protexfs.co/invoicepage/?date=' + this.elements.date.value; return false;">
<input type="text" name="date">
<input type="submit" value="Go">
</form>

The submit button leads to a page that automatically generates a Woocommerce product for the same amount and adds it to the cart and automatically redirects to the checkout page (I use the Insert PHP plugin for all my PHP needs):

[insert_php]
//empty cart
global $woocommerce;
$woocommerce->cart->empty_cart();
// Remove default cart message
$woocommerce->clear_messages();

//price
$invoiceprice = filter_input(INPUT_GET,"date",FILTER_SANITIZE_STRING);

//Generate title
$timestampedtitle = "Date: ".date("d/m/Y")." Amount: £".$invoiceprice;

//Generate message
$message = date_timestamp_get(date_create())." Date: ".date('m/d/Y h:i:s a', time())." Invoice amount: £".$invoiceprice;

$post = array(
'post_author' => '2',
'post_status' => "publish",
'post_title' => $timestampedtitle,
'post_content' => $message,
'post_parent' => '',
'post_type' => "product",
//'post_status' => 'private',
);

//Create post
$post_id = wp_insert_post( $post, $wp_error );
if($post_id){
$attach_id = get_post_meta($product->parent_id, "_thumbnail_id", true);
add_post_meta($post_id, '_thumbnail_id', $attach_id);
}
wp_set_object_terms($post_id, 'simple', 'product_type');
update_post_meta( $post_id, '_visibility', 'search' );
update_post_meta( $post_id, '_stock_status', 'instock');
update_post_meta( $post_id, '_virtual', 'yes');
update_post_meta( $post_id, '_regular_price', $invoiceprice );
update_post_meta( $post_id, '_sale_price', $invoiceprice );
update_post_meta( $post_id, '_purchase_note', "" );
update_post_meta( $post_id, '_featured', "no" );
update_post_meta( $post_id, '_weight', "" );
update_post_meta( $post_id, '_length', "" );
update_post_meta( $post_id, '_width', "" );
update_post_meta( $post_id, '_height', "" );
update_post_meta($post_id, '_sku', "");
update_post_meta( $post_id, '_product_attributes', array());
update_post_meta( $post_id, '_sale_price_dates_from', "" );
update_post_meta( $post_id, '_sale_price_dates_to', "" );
update_post_meta( $post_id, '_price', $invoiceprice );
update_post_meta( $post_id, '_sold_individually', "" );
update_post_meta( $post_id, '_manage_stock', "no" );
update_post_meta( $post_id, '_backorders', "no" );
update_post_meta( $post_id, '_stock', "" );
update_post_meta( $post_id, '_et_pb_page_layout', 'et_full_width_page' );

if( $woocommerce->cart ) {
     $woocommerce->cart->add_to_cart( $post_id, $quantity=1 );}

$url = $woocommerce->cart->get_checkout_url();
header("Location: $url");
[/insert_php]

This seems to work perfectly as long as I am logged in. However, if the user is not logged in, the product still gets generated but can't be added to the cart, showing the following message:"Sorry, this product cannot be purchased." (which ruins the whole thing).

The weird thing is that any other product I create through the WooCommerce interface can be accessed by the guests (so if I have a publicly available product and I programatically change its price and add it to the cart, it works -> but that creates problems when 2 people click the button at the same time).

Before you ask, I have enabled Guest checkout in the WooCommerce setting.

Any ideas on how to fix this? (Or possibly a completely different way to achieve my goal?)

Vlad

Vlad
  • 55
  • 1
  • 14

4 Answers4

1

I just encountered the same issue with a composite product.

The way I solved it is by working backwards from the error message.

I started by doing in my wp-content directory:

$ ack "Sorry, this product cannot be purchased."

Note: ack is a lot like grep, but way better.

This returned the following results (your results will vary by the plugins you have installed):

plugins/woocommerce/includes/class-wc-cart.php
825:                wc_add_notice( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ), 'error' );

plugins/woocommerce-composite-products/includes/class-wc-cp-display.php
118:            'i18n_unavailable_text'                    => __( 'Sorry, this product cannot be purchased at the moment.', 'woocommerce-composite-products' ),

From there, I noticed that there was a woocommerce i18n mapping from i18n_unavailable_text to my original search string.

In order not to miss any other occurrences, I diligently acked for:

$ ack i18n_unavailable_text

That yielded:

plugins/woocommerce/assets/js/frontend/add-to-cart-variation.js
392:                    $variation_form.find( '.single_variation' ).html( '<p>' + wc_add_to_cart_variation_params.i18n_unavailable_text + '</p>' );

plugins/woocommerce/includes/class-wc-frontend-scripts.php
197:                'i18n_unavailable_text'            => esc_attr__( 'Sorry, this product is unavailable. Please choose a different combination.', 'woocommerce' ),

plugins/woocommerce-composite-products/assets/js/add-to-cart-composite.js
2948:                       composite.disable_add_to_cart( wc_composite_params.i18n_unavailable_text );

plugins/woocommerce-composite-products/includes/class-wc-cp-display.php
118:            'i18n_unavailable_text'                    => __( 'Sorry, this product cannot be purchased at the moment.', 'woocommerce-composite-products' ),

Armed with that list, I fired up my text editor and looked at the two results that I identified to be relevant:

plugins/woocommerce-composite-products/assets/js/add-to-cart-composite.js
2948:                       composite.disable_add_to_cart( wc_composite_params.i18n_unavailable_text );
plugins/woocommerce/includes/class-wc-cart.php
825:                wc_add_notice( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ), 'error' );

I put a breakpoint in my browser for the js file, which by the way in my case I had to move into place, because of course the browser was using the minified version, without sourcemaps. That breakpoint was not hit, so I moved onto the php debugging session. That came up successful and led me further down the rabbit hole (pardon my vim line numbers - I use set relativenumber in vim - but the relevant line numbers in plugins/woocommerce/includes/class-wc-cart.php are 824-825):

   2       // Check product is_purchasable
   1       if ( ! $product_data->is_purchasable() ) {
825          wc_add_notice( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ), 'error' );
   1         return false;
   2       }

Documentation for: $product_data->is_purchasable()

I use kint for debugging, so I added some debug output, based on the if statement in the documentation above to see what was failing the checks for purchasability:

   2       // Check product is_purchasable
   1       if ( ! $product_data->is_purchasable() ) {
825          d(! $product_data->exists());
   1         d($product_data->get_price() === '');
   2         d($product_data->post->post_status !== 'publish');
   3         d(! current_user_can( 'edit_post', $product_data->id ));
   4         wc_add_notice( __( 'Sorry, this product cannot be purchased.', 'woocommerce' ), 'error' );
   5         return false;
   6       }

This informed me that $product_data->post->post_status !== 'publish' bool FALSE. That of course meant that somehow my post wasn't published. This led to yet another lower rung down into the rabbit hole, when I ran this sql query (10962 was the id of the culprit product/post and wp_4_posts is the table name for my wordpress network site posts table, where this post lived:

SELECT post_status FROM wp_4_posts WHERE id = '10962'

That returned post_status: publish. Someone shoot me now was my immediate reaction. But, I also noticed:

$product_data->get_price() === '' bool FALSE

So the price was not being calculated correctly for this product.

Onto the docs again, this time for $product_data->get_price()

After thinking about it for a few minutes, this led me to quickly realize that I had not added a base price to the product, thinking that having specified "Per-Item-Pricing", I wouldn't have to set a base price. Woe was me. It turned out that this was very well documented in the Composite Products documentation:

composite products per item pricing documentation

zealoushacker
  • 6,766
  • 5
  • 35
  • 44
0

I've seen this issue before. The product in question was missing some post meta. Compare your post_meta table after adding the product, then edit the product, save it and compare the table again. Check what keys WooCommerce has added.

It needs a strict selection of meta keys to allow you to purchase the product.

SELECT * FROM wp_postmeta WHERE post_id = '1234567' ORDER BY 'meta_key'

Senrab
  • 55
  • 1
  • 7
  • Wow, didn't expect a reply; thanks Kieran 'Kizbo' Barnes. The way I solved the problem was to manually create ~50 products (therefore accessible to the user). Whenever the user creates a new invoice (puts a value in the text field and presses ok), one of the products is picked at random and its price is changed to the value entered. This way the user has access to the product and it doesn't mess up if multiple people click the "Ok" button at the same time. – Vlad Aug 01 '14 at 19:04
0

I just ran into a similar issue and I wanted to share what I did to solve my problem. A little context first, I wanted the users to be able to buy adds (think like craigslist) on site that had variable price depending on the options you selected and the length of the add. So naturally I couldnt create fixed products that people could just buy. These adds were to be published at a later date (due to it being linked to a paper version of a newspapper) so the adds will be created in status "future" and wordpress will render them "publish" as the date is reached.

Everything worked fine when I was logged in as an admin, but when logged in as a regular user, people couldnt buy the adds, the error "sorry this product cannot be purchased" was showing up. What I did is what many wouldnt recommend at all (even myself) but it was the only solution I found for the limited time.

Woocommerce verifies these things, in this order to knwo if a product is purchasable:

product exists product has a price (postmeta _price != "") product is in "publish" status OR current user can edit the product (aka an admin for example)

I added the verification to that last one to allow the purchase of a product if the current user is the author of the product (thus the person creating the add will be able to buy it, but noone else).

So on the file woocommerce/includes/abstracts/abstract-wc-product.php (from your plugins directory) on the line 690 you have this

elseif ( $this->post->post_status !== 'publish' && ! current_user_can( 'edit_post', $this->id )

changed it to

elseif ( $this->post->post_status !== 'publish' && ! current_user_can( 'edit_post', $this->id ) && $this->post->post_author != get_current_user_id()) {

I dont recommend this if you can avoid it since it has the drawback that if woocommerce is updated, it may redownload this file and the change might be lost. But if it is your only solution like it was for me (mainly due to time constraints and not thinking too much ahead), well ...

Victor D.
  • 141
  • 9
0

As in my comment above, the way to resolve this is:

The way I solved the problem was to manually create ~50 products (therefore accessible to the user). Whenever the user creates a new invoice (puts a value in the text field and presses ok), one of the products is picked at random and its price is changed to the value entered. This way the user has access to the product and it doesn't mess up if multiple people click the "Ok" button at the same time.

Vlad
  • 55
  • 1
  • 14