I have been stuck on this for weeks now so I'm dropping my question here, hopefully someone can help me. I have a website where people can sell their car. You buy an advertisement and you can upload your vehicle. I have integrated Mollie Payment API so people can pay with iDeal, but it doesn't seem to read the payment status.
When the payment has been successful, my system doesn't give out an advertisement. How can I check if the payment has been successful and then automatically give out an advertisement if the order has been paid?
This is the code that processes the payments:
<?php
namespace AutoListingsFrontend;
require_once __DIR__ . "/../mollie-api-php/vendor/autoload.php";
require_once __DIR__ . "/../mollie-api-php/examples/functions.php";
class Checkout {
/**
* Hold our entire Purchase data
*/
public $purchase_data = array();
public function __construct() {
add_action( 'init', array( $this, 'process_purchase' ) );
add_action( 'auto_listings_mark_as_pending', array( $this, 'pending_payment' ) );
add_action( 'auto_listings_send_to_gateway', array( $this, 'send_to_gateway' ) );
add_action( 'auto_listings_payment_successful', array( $this, 'payment_successful' ), 10, 2 );
add_action( 'auto_listings_insert_payment_note', array( $this, 'insert_payment_note' ), 10, 2 );
add_action( 'auto_listings_update_payment_status', array( $this, 'update_payment_status' ), 10, 2 );
//add_action( 'auto_listings_gateway_paypal', 'auto_listings_process_paypal_purchase' );
}
public function sanitize_post_data() {
// simple sanitizing
foreach ( $_POST as $key => $value ) {
$key = str_replace( 'auto-listings-', '', $key );
if( $key == 'user-id' ) {
$sanitized[ $key ] = absint( $value );
} else {
$sanitized[ $key ] = sanitize_text_field( $value );
}
}
return $sanitized;
}
/**
* Process Purchase Form
*
* Handles the purchase form process.
*/
public function process_purchase() {
do_action( 'auto_listings_pre_process_purchase' );
// Check if there is $_POST
if ( empty( $_POST ) ) return false;
if ( ! isset( $_POST['auto-listings-gateway'] ) || empty( $_POST['auto-listings-gateway'] ) )
return false;
if ( ! isset( $_POST['auto-listings-package'] ) || empty( $_POST['auto-listings-package'] ) )
return false;
if ( ! isset( $_POST['auto-listings-user-id'] ) || empty( $_POST['auto-listings-user-id'] ) )
return false;
$data = $this->sanitize_post_data();
// Verify there is a user_ID
if ( $data['user-id'] > 0 ) {
// Get the logged in user data
$user = get_userdata( $data['user-id'] );
// Verify data
if ( ! $user ) {
return false;
}
}
// Setup user information
$user_info = array(
'id' => $user->ID,
'email' => $user->user_email,
'first_name' => $user->first_name,
'last_name' => $user->last_name,
);
// Setup package information
$package_info = auto_listings_get_package( $data['package'] );
// Set up the unique purchase key.
$key = strtolower( md5( $user_info['id'] . date( 'Y-m-d H:i:s' ) . uniqid( 'auto', true ) ) );
// Setup purchase information
$purchase_data = array(
'package' => stripslashes_deep( $package_info ),
'purchase_key' => $key,
'user' => stripslashes_deep( $user_info ),
'date' => date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ),
'gateway' => $data['gateway'],
);
// If the total amount in the cart is 0, send to the manual gateway.
if ( ! $purchase_data['package']['price'] ) {
$purchase_data['gateway'] = 'manual';
}
// Allow the purchase data to be modified before it is sent to the gateway
$this->purchase_data = apply_filters(
'auto_listings_purchase_data_before_gateway',
$purchase_data,
$data
);
// Send info to create the pending payment
// Send info to the gateway for payment processing
do_action( 'auto_listings_mark_as_pending' );
do_action( 'auto_listings_send_to_gateway' );
}
/**
* Sends all the payment data to the specified gateway.
*/
public function send_to_gateway() {
$this->purchase_data['gateway_nonce'] = wp_create_nonce( 'auto-listings-gateway' );
// $gateway must match the ID used when registering the gateway
do_action( 'auto_listings_gateway_' . $this->purchase_data['gateway'], $this->purchase_data );
}
/**
* Insert Pending Payment
*
* #param array $payment_data Payment data to process
* #return int|bool Payment ID if payment is inserted, false otherwise
*/
public function pending_payment() {
if ( empty( $this->purchase_data ) ) {
return false;
}
$payment_title = $this->purchase_data['user']['email'];
if ( $purchase_data['gateway'] = 'ideal' ) {
/*
* Initialize the Mollie API library with your API key.
*w
* See: https://www.mollie.com/dashboard/developers/api-keys
*/
$mollie = new \Mollie\Api\MollieApiClient();
$mollie->setApiKey("MOLLIE_APIKEY"); // change to LIVE key when done
$packages = auto_listings_get_packages();
foreach ( $packages as $package_id => $package ){
$orderId = $payment->id;
$prijs = '';
if(isset($_POST["auto-listings-purchase"])) {
$package_id = $_POST['auto-listings-package'];
if($package_id == "3769") {
$prijs = "10.00";
} elseif ($package_id == "3767") {
$prijs = "40.00";
} else {
echo "Something went wrong. Please contact our staff via: info#vagplace.nl.";
}
}
// Create Mollie payment
$payment = $mollie->payments->create([
"amount" => [
"currency" => "EUR",
"value" => $prijs,
],
"description" => "VAGplace order: ".$orderId,
"redirectUrl" => "https://vagplace.nl/mijn-autos/?payment=success&gateway=ideal&order_id=".$orderId,
"webhookUrl" => "https://vagplace.nl/mollie-webhook",
"method" => \Mollie\Api\Types\PaymentMethod::IDEAL,
"metadata" => [
"order_id" => $orderId,
],
]);
database_write($orderId, $payment->status);
/*
* Send the customer off to complete the payment.
* This request should always be a GET, thus we enforce 303 http response code
*/
header("Location: " . $payment->getCheckoutUrl(), true, 303);
}
}
$payment_post = array(
'post_title' => $payment_title,
'post_status' => 'pending',
'post_date' => $this->purchase_data['date'],
'post_type' => 'package-payment',
'post_content' => '',
'meta_input' => array(
'_al_payment_data' => stripslashes_deep( $this->purchase_data ),
'_al_payment_package_status' => 'pending',
'_al_payment_user_id' => $this->purchase_data['user']['id'],
),
);
$payment_id = wp_insert_post( $payment_post );
if ( ! empty( $payment_id ) ) {
$this->purchase_data['payment_id'] = $payment_id;
}
// Return false if no payment was inserted
return false;
}
/**
* Add a note to a payment
*
* #param int $payment_id The payment ID to store a note for
* #param string $note The note to store
* #return int The new note ID
*/
public function insert_payment_note( $payment_id = 0, $note = '' ) {
if ( empty( $payment_id ) )
return false;
$existing_data = get_post_meta( $payment_id, '_al_payment_data', true );
do_action( 'auto_listings_pre_insert_payment_note', $payment_id, $note );
$commentdata = array(
'comment_post_ID' => $payment_id, // to which post the comment will show up
'comment_author' => '', //fixed value - can be dynamic
'comment_author_email' => $existing_data['user']['email'], //fixed value - can be dynamic
'comment_author_url' => '', //fixed value - can be dynamic
'comment_content' => $note['heading'] . ' - ' . $note['content'], //fixed value - can be dynamic
'comment_type' => '', //empty for regular comments, 'pingback' for pingbacks, 'trackback' for trackbacks
'comment_parent' => 0, //0 if it's not a reply to another comment; if it's a reply, mention the parent comment ID here
'user_id' => get_current_user_id() ? get_current_user_id() : 1, //passing current user ID or any predefined as per the demand
);
//Insert new comment and get the comment ID
$note_id = wp_new_comment( $commentdata );
return $note_id;
}
/**
* Updates a payment status.
*
* #param int $payment_id Payment ID
* #param string $new_status New Payment Status (default: publish)
* #return bool If the payment was successfully updated
*/
public function update_payment_status( $payment_id = 0, $new_status = 'publish' ) {
if ( empty( $payment_id ) ) {
return false;
}
if ( empty( $data ) ) {
return false;
}
//Trying to verify payment
//$payment = $mollie->payments->get($payment->id);
$payment = $mollie->payments->get($_POST["id"]);
$orderId = $payment->metadata->order_id;
if ($payment->isPaid())
{
echo "Payment received.";
}
$post_arr = array(
'ID' => $payment_id,
'post_status' => $new_status,
);
$updated = wp_update_post( $post_arr );
return $updated;
}
/**
* What to do when a payment completes successfully
*
* #param array $data payment success data, sent from gateway
* #return bool If the payment was successfully updated
*/
public function payment_successful( $data ) {
$start_time = current_time( 'timestamp', $gmt = 0 );
$end_time = null;
if( $data['package_id']['duration'] > 0 ) {
$end_time = strtotime( '+' . $data['package_id']['duration'] . ' days', date( 'Y-m-d H:i:s', $start_time ) );
}
update_post_meta( $data['payment_id'], '_al_payment_package_time_start', $start_time );
update_post_meta( $data['payment_id'], '_al_payment_package_time_end', $end_time );
update_post_meta( $data['payment_id'], '_al_payment_package_status', 'active' );
update_post_meta( $data['payment_id'], '_al_payment_package_listings', $data['package_id']['listings'] );
update_post_meta( $data['payment_id'], '_al_payment_package_listings_used', '0' );
}
}
Solved. I used the Mollie Webhook to check the status of the payment: https://github.com/mollie/mollie-api-php/blob/master/examples/payments/webhook.php.
Related
I want a user to enter their email in a gravity form and after submission they are emailed a unique coupon code that expires after two weeks. I have cobble together code from a few other solutions and I am successful at creating the unique code. But I can't get it to create the coupon in woocommerce.. Not being a PHP master I know I'm missing something obvious.
//* Define options/constants
define( 'ENGWP_FORM_ID', 276 ); // The ID of the form (integer)
define( 'ENGWP_SOURCE_FIELD_ID', 2 ); // The ID of the form field holding the code (integer)
define( 'ENGWP_CODE_LENGTH', 12 ); // Length of code (integer)
define( 'ENGWP_CODE_CHARS', '1234567890QWERTYUIOPASDFGHJKLZXCVBNM' ); // Available character for the code; default 0-9 and uppercase letters (string)
define( 'ENGWP_CODE_PREFIX', '17-' ); // Custom prefix for the code (string); default empty
define( 'ENGWP_DISCOUNT_TYPE', 'percent' ); // 'flat' or 'percent' (string)
define( 'ENGWP_DISCOUNT_AMOUNT', 10 ); // Value of discount (integer); $ if 'type' is 'flat' and % if 'type' is 'percent'
define( 'ENGWP_MAX_USES', 1 ); // Maximum number of uses per customer (integer)
define( 'ENGWP_MIN_PRICE', 0 ); // Minimum price for discount to apply (integer); default none
define( 'ENGWP_PRODUCT_REQS', '' ); // A comma-separated list of product IDs (string) the coupons apply to
define( 'ENGWP_REQS_CONDITION', '' ); // How to apply the discount to those products (string); accepts 'any' (at least one product ID must be in the cart) or 'all' (all products must be in the cart)
define( 'ENGWP_SINGLE_USE', 'use_once' ); // Whether the coupons generated can be used more than once by a single customer; default is set to one-time usage but can be set to false (boolean) for allowing multiple uses
define( 'ENGWP_EXCLUDE_PRODUCTS', '' ); // A comma-separated list of product IDs (string) to exclude from discount-applicability
$start_date = ''; # no date
// $start_date = '01/01/1900'; # static date
// $start_date = date( 'm/d/Y', strtotime("yesterday") );
$exp_date = date( 'm/d/Y', strtotime("+14 days") );
// $exp_date = '01/01/1900'; # static date
// $exp_date = ''; # no date
class GW_Create_Coupon {
public function __construct( $args = array() ) {
// set our default arguments, parse against the provided arguments, and store for use throughout the class
$this->_args = wp_parse_args( $args, array(
'form_id' => false,
'source_field_id' => false,
'plugin' => 'wc',
'amount' => 0,
'type' => '',
'meta' => array()
) );
// do version check in the init to make sure if GF is going to be loaded, it is already loaded
add_action( 'init', array( $this, 'init' ) );
}
public function init() {
// make sure we're running the required minimum version of Gravity Forms
if( ! property_exists( 'GFCommon', 'version' ) || ! version_compare( GFCommon::$version, '1.8', '>=' ) ) {
return;
}
add_action( 'gform_after_submission', array( $this, 'create_coupon' ), 10, 2 );
}
public function create_coupon( $entry, $form ) {
if( ! $this->is_applicable_form( $form ) ) {
return;
}
$coupon_code = rgar( $entry, $this->_args['source_field_id'] );
$amount = $this->_args['amount'];
$type = $this->_args['type'];
$plugin_func = array( $this, sprintf( 'create_coupon_%s', $this->_args['plugin'] ) );
if( is_callable( $plugin_func ) ) {
call_user_func( $plugin_func, $coupon_code, $amount, $type );
}
}
public function create_coupon_wc( $coupon_code, $amount, $type ) {
$coupon = array(
‘post_title’ => $coupon_code,
‘post_content’ => ”,
‘post_status’ => ‘publish’,
‘post_author’ => 1,
‘post_type’ => ‘shop_coupon’
);
$new_coupon_id = wp_insert_post( $coupon );
$meta = wp_parse_args( $this->_args[‘meta’], array(
‘discount_type’ => $type,
‘coupon_amount’ => $amount,
‘individual_use’ => ‘yes’,
‘product_ids’ => ”,
‘exclude_product_ids’ => ”,
‘usage_limit’ => ‘1’,
‘expiry_date’ => ”,
‘apply_before_tax’ => ‘no’,
‘free_shipping’ => ‘no’,
‘exclude_sale_items’ => ‘no’,
‘product_categories’ => ”,
‘exclude_product_categories’ => ”,
‘minimum_amount’ => ”,
‘customer_email’ => ”
) );
foreach( $meta as $meta_key => $meta_value ) {
update_post_meta( $new_coupon_id, $meta_key, $meta_value );
}
}
public function create_coupon_edd( $coupon_code, $amount, $type ) {
if( ! is_callable( 'edd_store_discount' ) ) {
return;
}
$meta = wp_parse_args( $this->_args['meta'], array(
'name' => $coupon_code,
'code' => $coupon_code,
'type' => $type,
'amount' => $amount,
'excluded_products' => array(),
'expiration' => '',
'is_not_global' => false,
'is_single_use' => false,
'max_uses' => '',
'min_price' => '',
'product_condition' => '',
'product_reqs' => array(),
'start' => '',
'uses' => '',
) );
// EDD will set it's own defaults in the edd_store_discount() so let's filter out our own empty defaults (their just here for easier reference)
$meta = array_filter( $meta );
// EDD takes a $details array which has some different keys than the meta, let's map the keys to the expected format
$edd_post_keys = array(
'max_uses' => 'max',
'product_reqs' => 'products',
'excluded_products' => 'excluded-products',
'is_not_global' => 'not_global',
'is_single_use' => 'use_once'
);
foreach( $meta as $key => $value ) {
$mod_key = rgar( $edd_post_keys, $key );
if( $mod_key ) {
$meta[$mod_key] = $value;
}
}
edd_store_discount( $meta );
}
function is_applicable_form( $form ) {
$form_id = isset( $form['id'] ) ? $form['id'] : $form;
return $form_id == $this->_args['form_id'];
}
}
//* Instantiate the class for EDD
new GW_Create_Coupon( array(
'form_id' => ENGWP_FORM_ID,
'source_field_id' => ENGWP_SOURCE_FIELD_ID,
'amount' => ENGWP_DISCOUNT_AMOUNT,
'type' => ENGWP_DISCOUNT_TYPE,
'meta' => array(
'excluded_products' => array( ENGWP_EXCLUDE_PRODUCTS ),
'expiration' => $exp_date,
'is_not_global' => 'not_global',
'is_single_use' => ENGWP_SINGLE_USE,
'max_uses' => ENGWP_MAX_USES,
'min_price' => ENGWP_MIN_PRICE,
'product_condition' => ENGWP_REQS_CONDITION,
'product_reqs' => array( ENGWP_PRODUCT_REQS ),
'start' => $start_date,
)
) );
/**
* Generate the random codes to be used for the EDD discounts
*/
//* Generates and returns the code
add_filter( 'gform_field_value_uuid', 'gw_generate_unique_code' );
function gw_generate_unique_code() {
$length = ENGWP_CODE_LENGTH;
$chars = ENGWP_CODE_CHARS;
$prefix = ENGWP_CODE_PREFIX;
$unique = '';
$chars_length = strlen( $chars )-1;
for( $i = 0 ; $i < $length ; $i++ ) {
$unique .= $chars[ rand( 0, $chars_length ) ];
}
do {
$unique = $prefix . str_shuffle( $unique );
} while ( !gw_check_unique_code( $unique ) );
return $unique;
}
//* Checks to make sure the code generated is unique (not already in use)
function gw_check_unique_code( $unique ) {
global $wpdb;
$table = $wpdb->prefix . 'rg_lead_detail';
$form_id = ENGWP_FORM_ID; // update to the form ID your unique id field belongs to
$field_id = ENGWP_SOURCE_FIELD_ID; // update to the field ID your unique id is being prepopulated in
$result = $wpdb->get_var( "SELECT value FROM $table WHERE form_id = '$form_id' AND field_number = '$field_id' AND value = '$unique'" );
if ( empty ( $result ) ) {
return true;
} else return false;
}
You can use WC_Coupon to generate a coupon code. try the below code.
add_action( 'gform_after_submission', array( $this, 'create_coupon' ), 10, 2 );
public function create_coupon( $entry, $form ){
$coupon_code = rgar( $entry, $this->_args['source_field_id'] );
$date_expires = date('Y-m-d', strtotime('+14 days'));
$discount_type = 'fixed_cart'; // 'store_credit' doesn't exist
$coupon = new WC_Coupon();
$coupon->set_code($coupon_code);
//the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type($discount_type);
//the discount amount, defaults to zero
$coupon->set_amount($amount );
$coupon->set_date_expires( $date_expires );
//save the coupon
$coupon->save();
}
I have a wordpress plugin that I'm looking to call one of the functions on a custom template. The function I want to call is get_ship_now_adjust_date_link.
Is it possible to just do the following on a template:
echo get_ship_now_adjust_date_link( $subscription['id'] );
Here's the plugins full code:
<?php
namespace Javorszky\Toolbox;
add_filter( 'wcs_view_subscription_actions', __NAMESPACE__ . '\\add_ship_reschedule_action', 10, 2 );
add_action( 'wp_loaded', __NAMESPACE__ . '\\handle_ship_now_adjust_date_request' );
/**
* Extra actions on the subscription. Only there if the subscription is active.
*
* #param array $actions existing actions on the subscription
* #param \WC_Subscription $subscription the subscription we're adding new actions to
* #return array $actions
*/
function add_ship_reschedule_action( $actions, $subscription ) {
$next_timestamp = $subscription->get_time( 'next_payment' );
if ( 0 != $next_timestamp && 'active' == $subscription->get_status() ) {
$new_actions = array(
'ship_now_recalculate' => array(
'url' => get_ship_now_adjust_date_link( $subscription ),
'name' => Utilities\replace_key_dates( Utilities\get_button_text( 'ship_reschedule_button_text', 'Ship now and recalculate from today' ), $subscription ),
),
);
$actions = array_merge( $actions, $new_actions );
}
return $actions;
}
/**
* URL to be used on the "Ship now and adjust the date" button.
*
* #param \WC_Subscription $subscription Subscription we're getting the link for
* #return string URL to trigger shipping now and keeping the date with
*/
function get_ship_now_adjust_date_link( $subscription ) {
if ( version_compare( \WC_Subscriptions::$version, '2.6.0', '>=' ) ) {
$completed_payments = $subscription->get_payment_count('completed');
} else {
$completed_payments = $subscription->get_completed_payment_count();
}
$action_link = Utilities\strip_custom_query_args();
$action_link = add_query_arg( array( 'subscription_id' => $subscription->get_id(), 'ship_now_adjust_date' => 1 ), $action_link );
$action_link = wp_nonce_url( $action_link, $subscription->get_id() . '_completed_adjust_' . $completed_payments );
return $action_link;
}
/**
* Hooked into `wp_loaded`, this is responsible for charging the subscription now and adjusting the date if certain
* GET variables are present.
*/
function handle_ship_now_adjust_date_request() {
if ( isset( $_GET['ship_now_adjust_date'] ) && isset( $_GET['subscription_id'] ) && isset( $_GET['_wpnonce'] ) && !isset( $_GET['wc-ajax'] ) ) {
$user_id = get_current_user_id();
$subscription = wcs_get_subscription( $_GET['subscription_id'] );
$nonce = $_GET['_wpnonce'];
if ( Utilities\Process\process_ship_now_adjust_date( $user_id, $subscription, $nonce ) ) {
wc_add_notice( _x( 'Your order has been placed!', 'Notice after ship now adjust date request succeeded.', 'jg-toolbox' ) );
wp_safe_redirect( wc_get_endpoint_url( 'view-subscription', $subscription->get_id(), wc_get_page_permalink( 'myaccount' ) ) );
exit;
}
}
}
try to use \Javorszky\Toolbox\get_ship_now_adjust_date_link
I have a WordPress Woo-commerce store with the Pro version of the delivery drivers for woo-commerce plugin (https://wordpress.org/plugins/delivery-drivers-for-woocommerce/).
Everything works fine as any user who is of user role : 'delivery driver' can view and claim all orders that are processed through the website, at the moment using woo-commerce delivery/shipping zones settings I have configured it so only customers who reside in a certain city can place an order and I am only approving delivery drivers in that area.
But I want to roll this out to other cities, I can add the postcodes so users can order from those postcodes but the problem is with the delivery drivers plugin - drivers will see all orders from all cities in their dashboard. I only want the drivers to view and claim orders in the city they reside in (the city they have assigned themselves to).
Here is the code to the functionality that displays all orders placed on the website to user role type 'delivery driver' :
driver-dashboard-shortcode.php
<?php
/**
* The Unclaimed Orders Shortcode.
**/
function ddwc_pro_dashboard_shortcode() {
// Check if user is logged in.
if ( is_user_logged_in() ) {
// Get the user ID.
$user_id = get_current_user_id();
// Get the user object.
$user_meta = get_userdata( $user_id );
// If user_id doesn't equal zero.
if ( 0 != $user_id ) {
// If claim delivery button is pushed.
if ( ! empty( $_GET['claim_delivery'] ) ) {
// Get deliver ID to claim.
$claim_delivery = $_GET['claim_delivery'];
// Update order status.
$order = wc_get_order( $claim_delivery );
$order->update_status( 'driver-assigned' );
// Update order with driver ID.
update_post_meta( $claim_delivery, 'ddwc_driver_id', $user_id, -1 );
// Redirect URL.
$redirect_url = apply_filters( 'ddwc_pro_claim_order_redirect_url', get_permalink( get_option( 'woocommerce_myaccount_page_id' ) ) . '/driver-dashboard/?orderid=' . $claim_delivery, $claim_delivery );
// Redirect driver to the order details.
wp_redirect( $redirect_url );
}
// Get all the user roles as an array.
$user_roles = $user_meta->roles;
// Check if the role you're interested in, is present in the array.
if ( in_array( 'driver', $user_roles, true ) ) {
// Set variable for driver ID.
if ( isset( $_GET['orderid'] ) && ( '' != $_GET['orderid'] ) ) {
$driver_id = get_post_meta( $_GET['orderid'], 'ddwc_driver_id', true );
}
/**
* Args for Orders with no driver ID attached.
*/
$args = array(
'post_type' => 'shop_order',
'posts_per_page' => -1,
'post_status' => 'any',
'post_parent' => 0
);
/**
* Get Orders with Driver ID attached
*/
$unclaimed_orders = get_posts( $args );
/**
* If there are orders to loop through.
*/
if ( $unclaimed_orders ) {
// Total for table thead.
$total_title = '<td>' . esc_attr__( 'Total', 'ddwc' ) . '</td>';
do_action( 'ddwc_pro_unclaimed_orders_table_before' );
echo '<table class="ddwc-dashboard">';
echo '<thead><tr><td>' . esc_attr__( 'Date', 'ddwc-pro' ) . '</td><td>' . esc_attr__( 'Address', 'ddwc-pro' ) . '</td>' . apply_filters( 'ddwc_pro_driver_dashboard_unclaimed_orders_total_title', $total_title ) . '<td></td></tr></thead>';
echo '<tbody>';
do_action( 'ddwc_pro_unclaimed_orders_table_tbody_before' );
foreach ( $unclaimed_orders as $driver_order ) {
// Get Driver ID (if set).
$driver_id_setting = get_post_meta( $driver_order->ID, 'ddwc_driver_id', TRUE );
// Get an instance of the WC_Order object.
$order = wc_get_order( $driver_order->ID );
// Get the required order data.
$order_data = $order->get_data();
$currency_code = $order_data['currency'];
$currency_symbol = get_woocommerce_currency_symbol( $currency_code );
$order_id = $order_data['id'];
$order_status = $order_data['status'];
$order_date_created = $order_data['date_created']->date( 'm-d-Y' );
## CART INFORMATION:
$order_total = $order_data['total'];
## BILLING INFORMATION:
$order_billing_city = $order_data['billing']['city'];
$order_billing_state = $order_data['billing']['state'];
$order_billing_postcode = $order_data['billing']['postcode'];
## SHIPPING INFORMATION:
$order_shipping_city = $order_data['shipping']['city'];
$order_shipping_state = $order_data['shipping']['state'];
$order_shipping_postcode = $order_data['shipping']['postcode'];
// Create address to use in the table.
$address = $order_billing_city . ' ' . $order_billing_state . ', ' . $order_billing_postcode;
// Set address to shipping (if available).
if ( isset( $order_shipping_city ) ) {
$address = $order_shipping_city . ' ' . $order_shipping_state . ', ' . $order_shipping_postcode;
}
// Allowed statuses.
$status_array = apply_filters( 'ddwc_pro_driver_dashboard_unclaimed_orders_status_array', array( 'processing' ) );
// Display unassigned orders.
if ( in_array( $order_status, $status_array ) && ( -1 == $driver_id_setting || '' === $driver_id_setting ) ) {
echo '<tr>';
echo '<td>' . $order_date_created . '</td>';
echo '<td>' . apply_filters( 'ddwc_pro_driver_dashboard_unclaimed_orders_table_address', $address ) . '</td>';
if ( isset( $order_total ) ) {
$order_total = '<td>' . $currency_symbol . $order_total . '</td>';
echo apply_filters( 'ddwc_pro_driver_dashboard_unclaimed_orders_total', $order_total );
} else {
echo '<td>-</td>';
}
echo '<td>' . apply_filters( 'ddwc_pro_driver_dashboard_unclaimed_orders_button_text', __( 'CLAIM', 'ddwc-pro' ) ) . '</td>';
echo '</tr>';
} else {
// Do nothing.
}
}
do_action( 'ddwc_pro_unclaimed_orders_table_tbody_after' );
echo '</tbody>';
echo '</table>';
do_action( 'ddwc_pro_unclaimed_orders_table_after' );
// Driver dashboard button.
$dashboard_button = '← ' . __( 'Driver Dashboard', 'ddwc-pro' ) . '';
// Filter "Driver Dashboard" button.
echo apply_filters( 'ddwc_pro_back_to_driver_dashboard_button', $dashboard_button );
} else {
do_action( 'ddwc_pro_assigned_orders_empty_before' );
// Message - No assigned orders.
$empty = '<h3 class="ddwc assigned-orders">' . __( 'Assigned Orders', 'ddwc-pro' ) . '</h3>';
$empty .= '<p>' . __( 'You do not have any assigned orders.', 'ddwc-pro' ) . '</p>';
echo apply_filters( 'ddwc_pro_assigned_orders_empty', $empty );
do_action( 'ddwc_pro_assigned_orders_empty_after' );
}
} else {
// Set the Access Denied page text.
$access_denied = '<h3 class="ddwc access-denied">' . __( 'Access Denied', 'ddwc-pro' ) . '</h3><p>' . __( 'Sorry, but you are not able to view this page.', 'ddwc-pro' ) . '</p>';
// Filter Access Denied text.
echo apply_filters( 'ddwc_access_denied', $access_denied );
}
} else {
// Do nothing.
}
} else {
apply_filters( 'ddwc_pro_dashboard_login_form', wp_login_form() );
}
}
add_shortcode( 'ddwc_pro_dashboard', 'ddwc_pro_dashboard_shortcode' );
I had thought of a way to implement this functionality - I have created a custom field (just for delivery drivers) in the woocommerce account details section 'my-account/edit-account'. The field is a dropdown list of cities a delivery driver can assign to himself in his account details.
And I want to call a function that checks if the 'town/city' address label matches the selected city in a drivers profile, if it does then the driver can see that order (orders). If a driver has not selected a city in that drop-down then he will not see any orders.
Here is how I have added the drop-down city list:
<?php
/**
* Get additional account fields.
*
* #return array
*/
function iconic_get_account_fields() {
return apply_filters( 'iconic_account_fields', array(
'city_select' => array(
'type' => 'select',
'label' => __( 'Select City', 'iconic' ),
'hide_in_account' => false,
'hide_in_admin' => false,
'required' => false,
'options' => array(
'' => __( 'Select an option...', 'iconic' ),
1 => __( 'Manchester', 'iconic' ),
2 => __( 'Birmingham', 'iconic' ),
3 => __( 'London', 'iconic' ),
),
'bank_name' => array(
'type' => 'text',
'label' => __( 'Bank Name', 'iconic' ),
'hide_in_account' => false,
'hide_in_admin' => false,
'required' => false,
),
) );
}
/**
* Add post values to account fields if set.
*
* #param array $fields
*
* #return array
*/
function iconic_add_post_data_to_account_fields( $fields ) {
if ( empty( $_POST ) ) {
return $fields;
}
foreach ( $fields as $key => $field_args ) {
if ( empty( $_POST[ $key ] ) ) {
$fields[ $key ]['value'] = '';
continue;
}
$fields[ $key ]['value'] = $_POST[ $key ];
}
return $fields;
}
add_filter( 'iconic_account_fields', 'iconic_add_post_data_to_account_fields', 10, 1 );
/**
* Add field to account area.
*/
function iconic_print_user_frontend_fields() {
$fields = iconic_get_account_fields();
$is_user_logged_in = is_user_logged_in();
foreach ( $fields as $key => $field_args ) {
$value = null;
if ( ! iconic_is_field_visible( $field_args ) ) {
continue;
}
if ( $is_user_logged_in ) {
$user_id = iconic_get_edit_user_id();
$value = iconic_get_userdata( $user_id, $key );
}
$value = isset( $field_args['value'] ) ? $field_args['value'] : $value;
woocommerce_form_field( $key, $field_args, $value );
}
}
add_action( 'woocommerce_edit_account_form', 'iconic_print_user_frontend_fields', 10 ); // my account
/**
* Get user data.
*
* #param $user_id
* #param $key
*
* #return mixed|string
*/
function iconic_get_userdata( $user_id, $key ) {
if ( ! iconic_is_userdata( $key ) ) {
return get_user_meta( $user_id, $key, true );
}
$userdata = get_userdata( $user_id );
if ( ! $userdata || ! isset( $userdata->{$key} ) ) {
return '';
}
return $userdata->{$key};
}
/**
* Get currently editing user ID (frontend account/edit profile/edit other user).
*
* #return int
*/
function iconic_get_edit_user_id() {
return isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : get_current_user_id();
}
/**
* Save registration fields.
*
* #param int $customer_id
*/
function iconic_save_account_fields( $customer_id ) {
$fields = iconic_get_account_fields();
$sanitized_data = array();
foreach ( $fields as $key => $field_args ) {
if ( ! iconic_is_field_visible( $field_args ) ) {
continue;
}
$sanitize = isset( $field_args['sanitize'] ) ? $field_args['sanitize'] : 'wc_clean';
$value = isset( $_POST[ $key ] ) ? call_user_func( $sanitize, $_POST[ $key ] ) : '';
if ( iconic_is_userdata( $key ) ) {
$sanitized_data[ $key ] = $value;
continue;
}
update_user_meta( $customer_id, $key, $value );
}
if ( ! empty( $sanitized_data ) ) {
$sanitized_data['ID'] = $customer_id;
wp_update_user( $sanitized_data );
}
}
add_action( 'personal_options_update', 'iconic_save_account_fields' ); // edit own account admin
add_action( 'edit_user_profile_update', 'iconic_save_account_fields' ); // edit other account
add_action( 'woocommerce_save_account_details', 'iconic_save_account_fields' ); // edit WC account
/**
* Is this field core user data.
*
* #param $key
*
* #return bool
*/
function iconic_is_userdata( $key ) {
$userdata = array(
'user_pass',
'user_login',
'user_nicename',
'user_url',
'user_email',
'display_name',
'nickname',
'first_name',
'last_name',
'description',
'rich_editing',
'user_registered',
'role',
'jabber',
'aim',
'yim',
'show_admin_bar_front',
);
return in_array( $key, $userdata );
}
/**
* Is field visible.
*
* #param $field_args
*
* #return bool
*/
function iconic_is_field_visible( $field_args ) {
$visible = true;
$action = filter_input( INPUT_POST, 'action' );
if ( is_admin() && ! empty( $field_args['hide_in_admin'] ) ) {
$visible = false;
} elseif ( ( is_account_page() || $action === 'save_account_details' ) && is_user_logged_in() && ! empty( $field_args['hide_in_account'] ) ) {
$visible = false;
}
return $visible;
}
/**
* Add fields to admin area.
*/
function iconic_print_user_admin_fields() {
$fields = iconic_get_account_fields();
?>
<h2><?php _e( 'Additional Information', 'iconic' ); ?></h2>
<table class="form-table" id="iconic-additional-information">
<tbody>
<?php foreach ( $fields as $key => $field_args ) { ?>
<?php
if ( ! iconic_is_field_visible( $field_args ) ) {
continue;
}
$user_id = iconic_get_edit_user_id();
$value = iconic_get_userdata( $user_id, $key );
?>
<tr>
<th>
<label for="<?php echo $key; ?>"><?php echo $field_args['label']; ?></label>
</th>
<td>
<?php $field_args['label'] = false; ?>
<?php woocommerce_form_field( $key, $field_args, $value ); ?>
</td>
</tr>
<?php } ?>
</tbody>
</table>
<?php
}
add_action( 'show_user_profile', 'iconic_print_user_admin_fields', 30 ); // admin: edit profile
add_action( 'edit_user_profile', 'iconic_print_user_admin_fields', 30 ); // admin: edit other users
/**
* Validate fields on frontend.
*
* #param WP_Error $errors
*
* #return WP_Error
*/
function iconic_validate_user_frontend_fields( $errors ) {
$fields = iconic_get_account_fields();
foreach ( $fields as $key => $field_args ) {
if ( empty( $field_args['required'] ) ) {
continue;
}
if ( ! isset( $_POST['register'] ) && ! empty( $field_args['hide_in_account'] ) ) {
continue;
}
if ( empty( $_POST[ $key ] ) ) {
$message = sprintf( __( '%s is a required field.', 'iconic' ), '<strong>' . $field_args['label'] . '</strong>' );
$errors->add( $key, $message );
}
}
return $errors;
}
add_filter( 'woocommerce_save_account_details_errors', 'iconic_validate_user_frontend_fields', 10 )
I am a novice to programming (especially PHP) I have tried different ways in adding a if statement to check if I can make functionality where before it displays the list of orders in the table, it checks if the drivers selected city matches the city in the customers address and then displays the same orders as the city the driver resides in, if not no orders should be displayed.
But I keep breaking my site, any assistance would be appreciated (as im beginning to think maybe this is not the file to add this sort of function).
Here's what I would do:
On the site options side (custom, or trough woocommerce delivery zone options):
Admin store a list of cities, with name and ID
You could do an ACF option field list, where you can fill a City name, and an ID (or postcode) (to prevent make queries based on cities name only)
Woocommerce also has delivery options where you can store shipping areas, so you could store your cities here and use them. Depending on how you integrated with Woocommerce.
On the user/driver side:
Your additional city field should let the user be able to select multiple cities from the list you defined
Data will store as a user_meta a serialized array of IDs or Postcode from selected cities
So each driver has a postcode/IDs of cities stored as user meta, that he can edits
On the order checkout process side :
If the postcode of delivery address is "locked" to the available cities list you made: great
If the postcode is a free field, you'll need to add something so the order is associated with a postcode/ID of the available city you've defined. To be able to match drivers and orders.
Depending on your checkout process, you could add an order field so the customer selects a city based on your admin list. Or make the postal_code field limited to your list. You need something so an order can be matched to one of the cities from your list.
On the driver orders list page :
You'll edit the WP_query querying that query orders, to add a meta_query
meta_query will query orders based on the postcode/IDs of user_meta from the current user driver
You are able to foreach on the user meta array values, and add meta_query "OR" for each drivers cities ID/Postcode to the orders query
order matching one of the driver's city IDs/postcodes will be displayed
I've built my first shipping method plugin based on flat rate with a few extra fields.
I have done the following:
1. Installed and activated the plugin
2. Added 2 instances of the shipping method to the UK zone
I can see in the top sub menu in the shipping section there appears to be some kind of "default" instance of the shipping plugin in a menu labelled "UK Flat Rate"
I was wondering if there's a way to remove this and ONLY have the plugin work in the shipping zones section.
The reason I ask is that then in checkout if I enter a UK address I see the 2 UK methods defined and then underneath them both there is also a radio button for UK Flat Rate which I'm trying to get rid of. It shows the default values based on the values entered in the sub-section.
if (!defined('ABSPATH')) {
exit;
}
if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
add_action( 'woocommerce_shipping_init', 'uk_shipping_method');
function uk_shipping_method() {
if (!class_exists('UK_WC_Shipping_Flat_Rate')) {
class UK_WC_Shipping_Flat_Rate extends WC_Shipping_Method {
/** #var string cost passed to [fee] shortcode */
protected $fee_cost = '';
/**
* Constructor.
*
* #param int $instance_id
*/
public function __construct($instance_id = 1) {
$this->id = 'uk_flat_rate';
$this->instance_id = absint($instance_id);
$this->enabled = "yes"; // This can be added as an setting but for this example its forced enabled
$this->method_title = __('UK Flat Rate');
$this->title = 'UK Flat Rate';
$this->method_description = __('Lets you charge a fixed rate for shipping but flags for UK Status Update.');
$this->supports = array(
'shipping-zones',
'instance-settings',
'instance-settings-modal',
);
$this->init();
add_action('woocommerce_update_options_shipping_' . $this->id, array( $this, 'process_admin_options'));
}
/**
* init user set variables.
*/
public function init() {
$this->instance_form_fields = include('includes/settings-flat-rate.php');
$this->title = $this->get_option( 'title' );
$this->tax_status = $this->get_option( 'tax_status' );
$this->cost = $this->get_option( 'cost' );
$this->type = $this->get_option( 'type', 'class' );
}
/**
* Evaluate a cost from a sum/string.
* #param string $sum
* #param array $args
* #return string
*/
protected function evaluate_cost( $sum, $args = array() ) {
include_once( WC()->plugin_path() . '/includes/libraries/class-wc-eval-math.php' );
// Allow 3rd parties to process shipping cost arguments
$args = apply_filters( 'woocommerce_evaluate_shipping_cost_args', $args, $sum, $this );
$locale = localeconv();
$decimals = array( wc_get_price_decimal_separator(), $locale['decimal_point'], $locale['mon_decimal_point'], ',' );
$this->fee_cost = $args['cost'];
// Expand shortcodes
add_shortcode( 'fee', array( $this, 'fee' ) );
$sum = do_shortcode( str_replace(
array(
'[qty]',
'[cost]',
),
array(
$args['qty'],
$args['cost'],
),
$sum
) );
remove_shortcode( 'fee', array( $this, 'fee' ) );
// Remove whitespace from string
$sum = preg_replace( '/\s+/', '', $sum );
// Remove locale from string
$sum = str_replace( $decimals, '.', $sum );
// Trim invalid start/end characters
$sum = rtrim( ltrim( $sum, "\t\n\r\0\x0B+*/" ), "\t\n\r\0\x0B+-*/" );
// Do the math
return $sum ? WC_Eval_Math::evaluate( $sum ) : 0;
}
/**
* Work out fee (shortcode).
* #param array $atts
* #return string
*/
public function fee( $atts ) {
$atts = shortcode_atts( array(
'percent' => '',
'min_fee' => '',
'max_fee' => '',
), $atts, 'fee' );
$calculated_fee = 0;
if ( $atts['percent'] ) {
$calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
}
if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
$calculated_fee = $atts['min_fee'];
}
if ( $atts['max_fee'] && $calculated_fee > $atts['max_fee'] ) {
$calculated_fee = $atts['max_fee'];
}
return $calculated_fee;
}
/**
* calculate_shipping function.
*
* #param array $package (default: array())
*/
public function calculate_shipping( $package = array() ) {
$rate = array(
'id' => $this->get_rate_id(),
'label' => $this->title,
'cost' => 0,
'package' => $package,
);
// Calculate the costs
$has_costs = false; // True when a cost is set. False if all costs are blank strings.
$cost = $this->get_option('cost');
if ( '' !== $cost ) {
$has_costs = true;
$rate['cost'] = $this->evaluate_cost( $cost, array(
'qty' => $this->get_package_item_qty( $package ),
'cost' => $package['contents_cost'],
) );
}
// Add shipping class costs.
$shipping_classes = WC()->shipping->get_shipping_classes();
if ( ! empty( $shipping_classes ) ) {
$found_shipping_classes = $this->find_shipping_classes( $package );
$highest_class_cost = 0;
foreach ( $found_shipping_classes as $shipping_class => $products ) {
// Also handles BW compatibility when slugs were used instead of ids
$shipping_class_term = get_term_by( 'slug', $shipping_class, 'product_shipping_class' );
$class_cost_string = $shipping_class_term && $shipping_class_term->term_id ? $this->get_option( 'class_cost_' . $shipping_class_term->term_id, $this->get_option( 'class_cost_' . $shipping_class, '' ) ) : $this->get_option( 'no_class_cost', '' );
if ( '' === $class_cost_string ) {
continue;
}
$has_costs = true;
$class_cost = $this->evaluate_cost( $class_cost_string, array(
'qty' => array_sum( wp_list_pluck( $products, 'quantity' ) ),
'cost' => array_sum( wp_list_pluck( $products, 'line_total' ) ),
) );
if ( 'class' === $this->type ) {
$rate['cost'] += $class_cost;
} else {
$highest_class_cost = $class_cost > $highest_class_cost ? $class_cost : $highest_class_cost;
}
}
if ( 'order' === $this->type && $highest_class_cost ) {
$rate['cost'] += $highest_class_cost;
}
}
// Add the rate
if ( $has_costs ) {
$this->add_rate( $rate );
}
/**
* Developers can add additional flat rates based on this one via this action since #version 2.4.
*
* Previously there were (overly complex) options to add additional rates however this was not user.
* friendly and goes against what Flat Rate Shipping was originally intended for.
*
* This example shows how you can add an extra rate based on this flat rate via custom function:
*
* add_action( 'woocommerce_flat_rate_shipping_add_rate', 'add_another_custom_flat_rate', 10, 2 );
*
* function add_another_custom_flat_rate( $method, $rate ) {
* $new_rate = $rate;
* $new_rate['id'] .= ':' . 'custom_rate_name'; // Append a custom ID.
* $new_rate['label'] = 'Rushed Shipping'; // Rename to 'Rushed Shipping'.
* $new_rate['cost'] += 2; // Add $2 to the cost.
*
* // Add it to WC.
* $method->add_rate( $new_rate );
* }.
*/
do_action( 'woocommerce_' . $this->id . '_shipping_add_rate', $this, $rate );
}
/**
* Get items in package.
* #param array $package
* #return int
*/
public function get_package_item_qty( $package ) {
$total_quantity = 0;
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['quantity'] > 0 && $values['data']->needs_shipping() ) {
$total_quantity += $values['quantity'];
}
}
return $total_quantity;
}
/**
* Finds and returns shipping classes and the products with said class.
* #param mixed $package
* #return array
*/
public function find_shipping_classes( $package ) {
$found_shipping_classes = array();
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['data']->needs_shipping() ) {
$found_class = $values['data']->get_shipping_class();
if ( ! isset( $found_shipping_classes[ $found_class ] ) ) {
$found_shipping_classes[ $found_class ] = array();
}
$found_shipping_classes[ $found_class ][ $item_id ] = $values;
}
}
return $found_shipping_classes;
}
}
}
function add_uk_shipping_method( $methods ) {
$methods['uk_flat_rate'] = 'UK_WC_Shipping_Flat_Rate';
return $methods;
}
add_filter( 'woocommerce_shipping_methods', 'add_uk_shipping_method' );
}
}
Is there a setting in the plugin I'm missing to enforce the method is only zones based?
Turns out I needed to add filters to remove the section and the first instance of the method from shipping like so
woocommerce_shipping_option_remove( $section ) {
unset($section['uk_flat_rate']);
return $section;
}
add_filter( 'woocommerce_get_sections_shipping', 'woocommerce_shipping_option_remove' ,1 );
function woocommerce_shipping_remove_method( $rates )
{
unset($rates['uk_flat_rate:1']);
return $rates;
}
add_filter('woocommerce_package_rates','woocommerce_shipping_remove_method', 100 );
}
}
I am using the woocommerce action below to call a custom function, but for some reason it's being triggered twice on every order. Does anyone know why this could be or how to fix it so that it only calls once per order?
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
function parent_referral_for_all( $order_id ) {
....
}
UPDATE
I thought the action was being triggered twice but I'm not so sure now. I'm using this action to add another referral inside the affiliatewp plugin, which is adding twice, yet my echo of "Thank You" only appears once.
Everything is working as intended except that the referral (and it's associated order note) are being added twice.
Any help would be greatly appreciated.
That full function:
function parent_referral_for_all( $order_id ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
To avoid this repetition, you couldadd a custom post meta data to the current order, once your "another" referral has been added the first time.
So your code will be:
function parent_referral_for_all( $order_id ) {
## HERE goes the condition to avoid the repetition
$referral_done = get_post_meta( $order_id, '_referral_done', true );
if( empty($referral_done) ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
## HERE you Create/update your custom post meta data to avoid repetition
update_post_meta( $order_id, '_referral_done', 'yes' )
}
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
I hope this will help.
You can check for the existence of the _thankyou_action_done post_meta key like so:
<?php
/**
* Allow code execution only once when the hook is fired.
*
* #param int $order_id The Woocommerce order ID.
* #return void
*/
function so41284646_trigger_thankyou_once( $order_id ) {
if ( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Your code here.
}
}
add_action( 'woocommerce_thankyou', 'so41284646_trigger_thankyou_once', 10, 1 );