I am working on WordPress
site where I have multiple subscriptions and one simple type product.
Subscriptions like weekly, monthly, yearly etc.. using woocommerce subscription
plugin which is working perfectly fine.
And a simple type product as LifeTime access. (one time purchase).
Now I have another landing page where offering trials like 7 day trial, with subscriptions, the woocommerce subscription
plugin is providing options for trials and working perfectly fine. It allow users to checkout at zero price but taking CC details in order to charge them after trial is expired automatically. Now the problem comes when I am trying to apply same offer (trial offer) on my simple type product (LifeTime).
I have done some research and have added some code to make it work to some point. let me explain.
First I used woocommerce_product_options_pricing
action to add meta field to product for trial i.e.
function add_trial_period_field() {
woocommerce_wp_text_input( array(
'id' => PRODUCUT_TRIAL_PERIOD,
'label' => __( 'Trial Period (in days)', 'woocommerce' ),
'desc_tip' => 'true',
'description' => __( 'Enter the number of days for the trial period. (Field will work for only Simple Type product)', 'woocommerce' ),
'type' => 'number',
) );
}
then used woocommerce_process_product_meta_simple
to save the data.
And then by using woocommerce_add_cart_item_data
, woocommerce_before_calculate_totals
actions, I added the cart meta into it for the custom field i just created and set the item price to zero so user can be able to checkout with zero pricing. Works so far good but then payment fields weren't appearing so I did use woocommerce_cart_needs_payment
action to enable them by checking the product in cart.
Now the main problem is, it's saving the payment token for simple type products, it does save the customer_source_id
and stripe_source_id
in the order meta but I am unable to charge the user as it needs the stripe_payment_token
which is actually saved incase of subscription trial checkout.
I just want know how can I force woocommerce to create and save the users' stripe_payment_token
for my simple type product also along with subscription type products.
Sorry about the long post but I thought the details would be necessary in this case. My apologies if you still find the missing details in my question, but you can ask me, i'll share whatever is required.
Thanks.
After a lot of struggle I managed to handle it, so I am posting the solution here incase anyone else needs the same solution :)
First I captured the stripe customer id and payment token in filter woocommerce_payment_successful_result
.
public function woo_payment_success_filter($result, $order_id){
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
$order_data = $order->get_data();
$order_email = $order->get_billing_email();
$userInfo = get_user_by("email", $order_email);
$userid = $userInfo->data->ID;
$display_name = $userInfo->data->display_name;
// Iterating through each "line" items in the order
foreach ($order->get_items() as $item_id => $item ) {
$product_name = $item['name'];
$product_id = $item['product_id'];
$product = wc_get_product( $product_id );
if ( $product->is_type( 'simple' ) ) {
$trial_period = get_post_meta( $product_id, PRODUCUT_TRIAL_PERIOD, true );
if ( ! empty( $trial_period ) ) {
$order->update_meta_data(PRODUCUT_TRIAL_PERIOD, $trial_period);
$customFound = $order->get_meta("_customer_user", true); //In simple product Trial case mostly setting as 0 default;
if(!$customFound){
update_post_meta($order_id, "_customer_user", $userid);
}
$order->add_order_note( "Payment event scheduled after ${trial_period} days" );
$order->save();
update_user_meta($userid, "lifetime_trial_purchase", "yes");
// SETTING PAYMENT TOKEN
if(isset($_REQUEST['stripe_source'])){
$customer_id = get_post_meta($order_id, "_stripe_customer_id", true);
$source_id = $_REQUEST['stripe_source'];
update_user_meta($userid, "_stripe_customer_id", $customer_id);
update_user_meta($userid, "_stripe_source_id", $source_id);
$stripe_customer = new WC_Stripe_Customer( $userid );
$stripe_customer_args = array(
"email" => $order_email,
"description" => "Name: ".$display_name,
"name" => $display_name,
"metadata" => array()
);
if(!$stripe_customer->get_user_id()){
try {
$stripe_customer->create_customer($stripe_customer_args);
} catch (Exception $e) {
// DELETE ORDER AND REDIRECT TO ERROR PAGE (Homepage)
// you are order can not be processed at the time, please try again later.
wp_delete_post($order_id, true);
wp_redirect(get_site_url().'/#trialError');
exit;
}
} else {
$stripe_customer->set_id($customer_id);
$stripe_customer->update_customer($stripe_customer_args);
}
$stripe_sources = $stripe_customer->get_sources();
if($stripe_sources && is_array($stripe_sources) && count($stripe_sources) > 0){
$payment_token = WC_Payment_Tokens::get_customer_tokens( $userid, "stripe" );
if(!($payment_token && count($payment_token))){
try {
foreach ($stripe_sources as $key => $source) {
if($source->status == "chargeable"){
$wc_token = new WC_Payment_Token_CC();
$wc_token->set_token( $source->id );
$wc_token->set_gateway_id( 'stripe' );
$wc_token->set_card_type( strtolower( $source->card->brand ) );
$wc_token->set_last4( $source->card->last4 );
$wc_token->set_expiry_month( $source->card->exp_month );
$wc_token->set_expiry_year( $source->card->exp_year );
$wc_token->set_user_id( $userid );
$wc_token->save();
break;
}
}
} catch (Exception $e) {
wp_delete_post($order_id, true);
wp_redirect(get_site_url().'/#trialError');
exit;
}
}
} else {
try {
$stripe_customer->add_source($source_id);
} catch (Exception $e) {
wp_delete_post($order_id, true);
wp_redirect(get_site_url().'/#trialError');
exit;
}
}
$timestamp = strtotime( "+$trial_period days" );
$args = array( $order_id, 1, $userid, $product_id );
wp_schedule_single_event( $timestamp, 'process_payment_after_trial_ends', $args );
}
}
}
}
return $result;
}
In above filter, I am trying to create/update customer on Stripe and saving his payment token with CC/Source Card details
and in last wp_schedule_single_event
is set after trial time over which will charge the user. Code is below for charging customer.
function process_payment_after_trial_end_simple_type_product( $order_id, $counter, $user_id, $product_id ) {
global $woocommerce;
$order = wc_get_order( $order_id );
$product = wc_get_product( $product_id );
$checkIfPaymentDoneAlready = update_post_meta($order_id, "payment_cleared_for_trial", true);
if($checkIfPaymentDoneAlready == "yes"){
return;
}
$product_price = $product->get_regular_price();
if($order && $product){
// This is the method to get existing Stripe Sources from users older orders (we have saved the source in previous function)
$stripeData = $this->get_user_stripe_data($product_id,$user_id);
if(isset($stripeData) && count($stripeData) > 1){
$_GET['source'] = isset($stripeData['_stripe_source_id']['value']) ? $stripeData['_stripe_source_id']['value'] : '';
}
// Updating the order price as initial it was set to zero for trial checkout
foreach ($order->get_items('line_item') as $key => $item) {
$item->set_subtotal($product_price);
$item->set_quantity( 1 );
$item->set_total($product_price);
}
$order->calculate_totals();
$order->save();
$payment_token = WC_Payment_Tokens::get_customer_default_token( $user_id );
$payment_method = "stripe"; //Right now defualt and configured method is only stripe.
$_POST['wc-'.$payment_method.'-payment-token'] = $payment_token->get_id(); // setting post var as it will be required in process the payment
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
$stripe_response = $available_gateways[ $payment_method ]->process_payment( $order->get_id(), true, false, false, true );
if(isset($stripe_response['result']) && $stripe_response['result'] == "success"){
$note = __( 'Payment processed after trial period ended.', 'textdomain' );
$order->update_status( 'completed', $note, true );
$order->payment_complete();
update_post_meta($order_id, "payment_cleared_for_trial", "yes");
$args = array( $order_id, $counter, $user_id, $product_id );
wp_clear_scheduled_hook("process_payment_after_trial_ends", $args);
$user = get_user_by("id", $user_id);
} else {
if ($counter == 6) {
// UPDATE ORDER STATUS FAILED
$order->update_status( 'failed', "Failed payment after retrying 5 times", true );
$order->save();
return;
}
$note = __( "Payment failed: Try {$counter}", 'textdomain' );
$order->update_status( 'on-hold', $note, true );
$retryTime = strtotime( "+{$counter} days" );
$newCounter = $counter + 1;
$args = array( $order_id, $newCounter, $user_id, $product_id );
$timestamp = strtotime( "+1 days" );
// Retrials if incase card is declined or insufficient balance
// Sending Admin/Customer emails to notify them
if ($counter == 1){
// Customer Email
$customerEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Customer_Payment_Retry_One'];
$customerEmailOne->trigger( $order_id, wc_get_order($order_id) );
// Admin Email
$adminEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Admin_Payment_Retry_One'];
$adminEmailOne->trigger( $order_id, wc_get_order($order_id) );
$timestamp = strtotime( "+2 days" );
}
if ($counter == 2){
// Customer Email
$customerEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Customer_Payment_Retry_Two'];
$customerEmailOne->trigger( $order_id, wc_get_order($order_id) );
// Admin Email
$adminEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Admin_Payment_Retry_Two'];
$adminEmailOne->trigger( $order_id, wc_get_order($order_id) );
$timestamp = strtotime( "+2 days" );
}
if ($counter == 3){
// Customer Email
$customerEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Customer_Payment_Retry_Three'];
$customerEmailOne->trigger( $order_id, wc_get_order($order_id) );
// Admin Email
$adminEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Admin_Payment_Retry_Three'];
$adminEmailOne->trigger( $order_id, wc_get_order($order_id) );
$timestamp = strtotime( "+3 days" );
}
if ($counter == 4){
// Customer Email
$customerEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Customer_Payment_Retry_Four'];
$customerEmailOne->trigger( $order_id, wc_get_order($order_id) );
// Admin Email
$adminEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Admin_Payment_Retry_Four'];
$adminEmailOne->trigger( $order_id, wc_get_order($order_id) );
$timestamp = strtotime( "+5 days" );
}
if ($counter == 5){
// Customer Email
$customerEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Customer_Payment_Retry_Five'];
$customerEmailOne->trigger( $order_id, wc_get_order($order_id) );
// Admin Email
$adminEmailOne = WC()->mailer()->get_emails()['QA_WCS_Email_Admin_Payment_Retry_Five'];
$adminEmailOne->trigger( $order_id, wc_get_order($order_id) );
$timestamp = strtotime( "+4 days" );
}
wp_schedule_single_event( $timestamp, 'process_payment_after_trial_ends', $args );
}
$order->save();
}
}
That's all :) Hope this helps.. incase anyone needs more explanation or have any question regarding this issue.. feel free to ask :)