Custom shipping plugin (Packeta)

  • Posts: 2
  • Thank you received: 1
4 days 2 hours ago #367063

-- HikaShop version -- : 5.1.6
-- Joomla version -- : 5.2.5
-- PHP version -- : 8.3
-- Browser(s) name and version -- : Chrome

Hello,

I'm trying to develop a custom shipping plugin for Packeta. Packeta delivers to pick-up points.

How the plugin should work:

1) When the user selects "Packeta" as a shipping option, a button will appear next to it. Clicking the button would trigger Packeta's selector widget.
2) The checkout cannot proceed unless a pick-up point is selected.
3) After the order is created, a REST API call is made to Packeta to create the order on their side.

I've had a good dev experience with HikaShop so far (we have a business version on another account), but I'm a bit stumped how to start here. Is there an example code with similar functions? My main problems are:

1) I only need to show the button when the "Packeta" shipping option is selected. I can inject the button html during onShippingDisplay, but how to display=none it when another shipping option is selected?
2) How to create a custom check whether a pick-up point was selected, and not allow the checkout to continue without it?
3) In which function would I make the API call? I've read the Developer documentation, but couldn't find definitive info.

Thanks!

Please Log in or Create an account to join the conversation.

  • Posts: 83777
  • Thank you received: 13561
  • MODERATOR
4 days ago #367064

Hi,

The MyParcel plugins do the same as what you want to do, but for MyParcel:
- The pickup location selector plugin ( www.hikashop.com/marketplace/product/251...selector-plugin.html ) will allow you to add a shipping method to the checkout ( based on the shipping API: www.hikashop.com/support/documentation/6...tation.html#shipping ), and when selected, it will display a pickup location selector. The plugin won't allow the customer to finish the checkout if that shipping method is selected and a pickup location is not selected. And the plugin will also store the selected pickup location in the order.
- The label and track plugin ( www.hikashop.com/marketplace/product/245...shipping-plugin.html ) has an automatic export option which, when activated, will send the details of the order, along with the selected pickup location, to the MyParcel server when the order is created or confirmed. It has extra features, like printing the shipping label, retrieving the tracking information of the package etc.
So, you could potentially base yourself on these plugins. That might be quite helpful, but they are not free...

1. In onShippingDisplay you can add your custom HTML in the "custom_html" attribute of your shipping method object. HikaShop will automatically hide / display your HTML when the shipping method is selected or another shipping method is selected.
On top of the widget's HTML, you should add a hidden input with the name:

$map = 'checkout[shipping][custom]'.(isset($order->shipping_warehouse_id) ? ('['.$order->shipping_warehouse_id.']') : '').'['.$rate->shipping_id.']';

2. HikaShop will make sure the customer select something in the widget by making sure this input is filled before proceeding.
It will also store the data in that input in $order->cart->cart_params->shipping which will be available to you in the event onBeforeOrderCreate. There, you can store the data you want in either a custom order field, or in the order_shipping_params data of the order.

3. If you want to send the data of the order to the API when the order is created, I recommend doing so in the onAfterOrderCreate event. In that case, the data of the shipping method will be available in $order->cart->cart_params->shipping so you don't even need to store it in onBeforeOrderCreate, unless you want to display it in the order details, the emails, the backend, etc.
If you want to send the data of the order to the API when the order is confirmed (after the payment is made), then you want to implement onAfterOrderUpdate and check $order->order_status and $order->old->order_status to make sure the order status is being changed from created to confirmed.
You can read more about these events here:
www.hikashop.com/support/documentation/6...mentation.html#order

The following user(s) said Thank You: gertas

Please Log in or Create an account to join the conversation.

  • Posts: 2
  • Thank you received: 1
21 hours 16 minutes ago #367081

Thank you for the reply!

Ad plugins) Yes, those two plugins seem great, unfortunately that's a pretty steep price... Considering HikaShop itself cost 120 eur, paying 110 just for some code inspiration feels a bit brutal :(

Ad 1)
Thank you, turns out I was heading in the right direction, but I misunderstood the shipping cache. It was just printing extra nonsense on every page load. This is the current code that seems to be working, maybe it will help someone in the future:

public function onShippingDisplay(&$order, &$dbrates, &$usable_rates, &$messages)
	{
		// look in the cache first, maybe this has been done already
		if ($this->loadShippingCache($order, $usable_rates, $messages)) {
			return true;
		}
	
		$local_usable_rates = [];
		$local_messages = [];
		$ret = parent::onShippingDisplay($order, $dbrates, $local_usable_rates, $local_messages);
		if ($ret === false) {
			return false;
		}
	
		// we get a list of ALL rates, but only want to work with packeta
		foreach ($local_usable_rates as $rate) {
			if ($rate->shipping_type !== 'packeta') {
				continue;
			}
	
			$pickupPointInputName = 'checkout[shipping][custom]';
			if (isset($order->shipping_warehouse_id)) {
				$pickupPointInputName .= '[' . $order->shipping_warehouse_id . ']';
			}
			$pickupPointInputName .= '[' . $rate->shipping_id . ']';

                        // dirty load the script for now, better solution might be needed in production
			$rate->custom_html .= '<script src="https://widget.packeta.com/v6/www/js/library.js"></script>';
			
			// show the "select a pickup point" button including its functionality
			$rate->custom_html .= '
			<div class="packeta-widget-container" style="margin-top:8px;">
				<button type="button"
					class="btn hikashopPickupBtn packeta-selector-open"
					data-shipping-id="' . $rate->shipping_id . '">
					Select your pickup point
				</button>
				<input type="hidden"
					name="' . $pickupPointInputName . '"
					id="pickup_point_' . $rate->shipping_id . '"
					value="" />
				<div class="packeta-selector-value" id="pickup_display_' . $rate->shipping_id . '"></div>
			</div>
		
			<script>
				const packetaApiKey = "your_public_key";

				// generated from Packeta widget configurator
				const packetaOptions = {
					language: "cs",
					valueFormat: "name,city,street",
					view: "modal"
				};
		
				// mostly provided by Packeta, just make sure every shipping_id has its own function
				function showSelectedPickupPoint_' . $rate->shipping_id . '(point) {
					if (point) {
						const display = document.getElementById("pickup_display_' . $rate->shipping_id . '");
						const input = document.getElementById("pickup_point_' . $rate->shipping_id . '");
						display.innerText = "Address: " + point.formatedValue;
						input.value = point.id;
					}
				}
		
				document.addEventListener("DOMContentLoaded", function () {
					const btn = document.querySelector(".packeta-selector-open[data-shipping-id=\'' . $rate->shipping_id . '\']");
					if (btn) {
						btn.addEventListener("click", function () {
							Packeta.Widget.pick(packetaApiKey, showSelectedPickupPoint_' . $rate->shipping_id . ', packetaOptions);
						});
					}
				});
			</script>';
	
			$rate->custom_html_no_btn = true;
			$usable_rates[$rate->shipping_id] = $rate;
		}
	
		// save the processed methods into the cache
		$this->setShippingCache($order, $usable_rates, $messages);
	
		return true;
	}

Ad 2)
This is great! However, the one problem I'm having is that the fail happens completely silently. When the user doesn't select any pickup points, the cart just... does nothing on confirm. What exactly am I supposed to be intercepting here? How can I display a proper error message?

Ad 3)
I'll have a look into this later, but I think I understand what I'm supposed to do now.

The following user(s) said Thank You: nicolas

Please Log in or Create an account to join the conversation.

  • Posts: 83777
  • Thank you received: 13561
  • MODERATOR
12 hours 8 minutes ago #367085

Hi,

If you wish to display an error message to the customer when the hidden input of your custom html is not filled, you can implement the onBeforeCartSave event of the Cart API in your plugin. There, check if check if data is provided in the correct subarray (based on the warehouse id and the shipping id of your shipping method). If checkout[shipping][custom] is available in the POST but the data is not provided in the correct subarray, then you can cancel the cart save and use the enqueueMessage method of Joomla to display an error message to the user.

Please Log in or Create an account to join the conversation.

Time to create page: 0.058 seconds
Powered by Kunena Forum