Jump to content

KIOSK, One-Page Checkout Module for ProcessWire


froot
 Share

Recommended Posts

I'm working on a one-page, vanilla javascript and php (PW API) shopping checkout module that integrates PayPal server side.

Here's version 1.0

https://github.com/dtjngl/kiosk

Looking forward to your feedback! 

 

DISCLAIMER
this is a BETA version at best, it's my first "real" module I'm writing for ProcessWire (and first module I'm writing at all) so please don't eat me alive. I'm aware that it needs a lot of work, but at this point, but it's functional.

I'm also aware that it is not very customisable at this point (it's mostly customised for one specific use case), but I promise I will work on that.

My main objective is to get more seasoned developers' mentoring, feedback, advice, warnings – and and of course, if you want to look into or help develop it further – pull requests. Please point out any potential dangers you see or anything that raises a red flag for you. I'm learning so much from that.

Though the module is held in german, you can translate almost all strings that are in the php files via PW's translate functionality. I cannot help with the strings in the javascript yet, but will do so soon (using a json file that lives on the file system probably).

I started working on this module because many checkout systems out there are either overkill and too complicated, didn't fulfil my needs or come with a premium fee that is not worth it. Also, austrian law (and other countries' jurisdiction) demand a different flow than what PayPal's Express checkout offers. So this version makes calls to PayPal on the server.

WHAT IT CONTAINS
- OnePage checkout system with 4 steps:

  1. cart,
  2. address(es) form,
  3. payment method (paypal and deferred),
  4. order overview

- "minicart" (the cart-summary in the page's header)
- buy button (to add items to the cart)
- a dashboard (with admin template) for an overview of all placed orders

uses server session storage and browser localstorage

REQUIRED
WireMail SMTP UIKIT (for best behaviour, will be optional soon) vanilla Javascript (no jQuery required) PHP (DUHDOY!) ProcessWire Version (not sure, but works with version 3.0.200) if you want it in english you need a multi-language installation PayPal Business Account (Live or Sandbox to test with)

HOW TO INSTALL
1. Put the folder "Kiosk" in your ProcessWire site's modules folder
2. Install the module Kiosk, this will…
- create fields for the order template
- create a template for the orders placed
- create a template for the orders' parent page
- create a parent page "Custom Orders" for all orders under /admin/page/ (you can rename the page, not the template)
- create a template "kiosk_checkout" for the checkout page
- create a page "Checkout" for the checkout, child to "Home" (you can move the page, you can rename the page, not the template)
- install module ProcessKiosk along with it
- ProcessKiosk create a page under /admin/ (visible in the module upon creation). Here you will see a table with all the orders placed.

3. check the Kiosk module's settings page (navigate via the module's overview page) and enter details to your best knowledge. Some are not "required" but they actually are.

4. Put the below code (in essence) on "kiosk_checkout.php":

if ($config->ajax) {
	$kiosk->handleAJAX($input);
	return $this->halt();
} else {
	$kiosk->handleStaticContent($input);
	echo $kiosk->renderCheckoutSteps();
}

5. put this line on a page that loads everywhere, preferably on init.php:

$kiosk = $modules->get('Kiosk')

6. put this line where you want the "minicart" and toggling cart preview (provided the site runs UIKIT) to be, probably in a header that renders on each page:

echo $kiosk->renderMiniCart();

7. put this line just above you closing tag:

echo $kiosk->addScripts();

HOW DOES THIS THE ORDERING WORK?

1. Add items to the cart or update quantity when viewing the cart, continue with "weiter".

2. Enter your address, if your billing address differs, uncheck the "gleiche Rechnungsadresse" and enter the billing address, checked or unchecked, both forms will be sent to the server but handled accordingly, continue with "weiter"

3. Select a payment method, continue with "weiter"

4. formdata object (containing cart items, address(es), payment method) will be sent via AJAX to the server and stored in the server session variable

5.1. if payment method is deferred
- AJAX response contains the order summary markup that will render in step4
- don't forget to check " ich habe die Datenschutzerklärung sowie die AGB zur Kenntnis genommen und akzeptiere diese." (privacy policy and terms and conditions)
- click on "Zahlungspflichtig bestellen" will send another AJAX request to the server thus submitting the order (continue to WHAT HAPPENS WHEN AN ORDER IS PLACED?)

5.2. if it's paypal, a bit more is happening
- server sends a cURL request to paypal containing client ID and secret
- response will send a token
- server sends that token along with the purchase unit (created from our placed order) in another cURL request to paypal
- response will send an "approve"-URL
- AJAX response contains that URL
- user is redirected to paypal to approve the order
- user is redirected to the "checkout" page along with a token and PayerID as GET parameters
- token (not needed actually) and PayerID are stored in the server session
- with the PayerID in the session variable and the "status" of the paypal approved order in the localstorage the checkout process will head on to step 4: order summary
- don't forget to check " ich habe die Datenschutzerklärung sowie die AGB zur Kenntnis genommen und akzeptiere diese." (privacy policy and terms and conditions)
- clicking on "Zahlungspflichtig bestellen" will send another AJAX request to the server
- second AJAX request will send PayPalAccessToken, PayPalPayerId and PayPalRequestId in another cURL to PayPal which will trigger the payment
- response will… continue to WHAT HAPPENS WHEN AN ORDER IS PLACED?

WHAT HAPPENS WHEN AN ORDER IS PLACED?
an order-page with all the order's details (plus order number from the kiosk's number circle) is created under /admin/page/custom_orders/ (you can find it in the Kiosk Dashboard)
number circle is iterated
email markup is created using the module's default email template, you can add a path to a custom template in the module's settings
email is sent to the site master and the user (check the module's settings for email addresses etc.)
order in server session is reset to an empty array
paypal session in server session is reset to an empty array
localstorage of the browser is deleted
user is redirected to a custom url (defined in the module's settings)
HOW ARE PRICES HANDLED?
prices allow for a stack price functionality. This means that prices and shipping depend on the quantity of items purchased. You can enter different prices and shipping costs for different quantities. If a user's amount of a selected item reaches the specified stack price, the item price and the shipping costs change.

HOW ARE PRODUCTS ADDED TO THE CART?
a product should be a page object to keep things simple. That page needs an "images" array field (that very name). Below you can see what a product would need. These values are added to the button's data attributes.

// This is how you make a product…

$product = new WireArray();
$product->id = $page->id;
$product->title = $page->title;
$product->source = $page->parent->name;
$product->url = $page->url;

$product->images = $page->images;
$product->product_width = 150 // for proportional item image calculation in the cart 
$product->taxrate = 10 // or what ever the tax rate

$product->stack_prices = 
    array(
        array(
            "qu" => 1,
            "sp" => 19.99,
            "sh" => 5
        ),
        array(
            "qu" => 10,
            "sp" => 14.99,
            "sh" => 0
        ),
);
$product->stack_prices = htmlspecialchars(json_encode($product->stack_prices)); 

// then render the "add to cart" button
$kiosk->renderBuyButton($product);


UPDATE version 1.1:
installing kiosk.module will also create a repeater field "kiosk_product_stack_prices" (including 3 subfields as described above) that handles the stack_prices array of arrays (as described above) so you can use the GUI. create a field "kiosk_product_tax_rate" create a field "kiosk_product_width " create a field "images" if there is none in your system. create a template "kiosk_product" with all these mentioned fields use this template for your products (or the fields thereon at least and add to your template of choice) and you should be good to go.

CAUTION!
If you uninstall kiosk.module it will delete all pages with template "kiosk_product", that's because I'm still figuring out how to detach the fields and fieldgroups without deleting the template. Also, if you're coming from version 1.0 and want to upgrade, please uninstall it in 1.0 first and only then get the new repo (v1.1) to install again.

Class kiosk now provides the following hookable methods:

___renderBuyButton(Page $product, (string) 'add to cart')

pass a product (preferably with template kiosk_product) and it returns a buybutton with the price for 1 item. This method now accepts the button's label (string) as a second argument. Method is hookable.

___getSpecificStackPrice(Page $product, (int) 5) // 5 is just an example

pass a product (preferably with template kiosk_product) and an amount, it returns the price for that amount of items. Method is hookable.

___getSinglePrice(Page $product)

pass a product (preferably with template kiosk_product) and it returns the price for 1 item. Method is hookable.

So, use template "kiosk_product" for your products, edit the product page to add details, and then do

echo $kiosk->renderBuyButton($product, (string) 'add to cart');

to render an "add to cart" button.

 

  • Like 14
Link to comment
Share on other sites

On 8/24/2022 at 2:04 AM, fruid said:

one-page checkout module

Checkout for what?
Checkout with which providers?
Checkout with which framework?

What is your module doing?

Don't get me wrong but... a bit more details would be nice. Maybe adding your README here would be a great start.
Is this based on Shopify, Snipcart, Foxy or plain ProcessWire?
I'd like to play with your module. Easy one-click-solutions are always nice, but... details and some kind of introduction would be really nice.

 

Update: Details were added.

  • Like 3
Link to comment
Share on other sites

  • froot changed the title to KIOSK, One-Page Checkout Module for ProcessWire
4 hours ago, wbmnfktr said:

Checkout for what?

Hi @wbmnfktr thanks for your message, I thought "checkout" is a term that speaks for itself. It's meant to be a shopping cart checkout and it doesn't use any third party software except for PayPal (for now). I edited my initial post, which is pretty much the readme.md file on github.

  • Like 2
Link to comment
Share on other sites

13 hours ago, wbmnfktr said:

details and some kind of introduction would be really nice.

I think @fruid wrote a good short introduction above, he just missed a short "birds-eye view intro" explaining the bigger picture. I skimmed through the code of the module(s) and it is not that hard to see what the module does in general. If someone is really interested in it, then it is best to install it on a site that loads UI-Kit 3 and try in out.

What @fruid could really add to his intro above is a short video, showcasing what the module does in action.

  • Like 2
Link to comment
Share on other sites

6 hours ago, szabesz said:

What @fruid could really add to his intro above is a short video, showcasing what the module does in action.

Here's a test installation for you to get a look and feel

http://kiosk.datajungle.xyz/shop/

I will provide access to the admin soon so you can view the kiosk settings page, kiosk dashboard and some of the page tree.

  • Like 1
Link to comment
Share on other sites

35 minutes ago, Klenkes said:

@fruidShort question: What fieldtype should stack_prices be?

You can create a field stack_prices as a repeater field with three subfields: repeater_product_quantity (integer), repeater_product_stackprice (float) and repeater_product_shipping (float) 

Add this field to your product-template.

Then, on your product page, before you render the add-to-cart button with ___renderBuyButton() you can format these values like so: 

    if ($page->stack_prices != '') : 
        $product->stack_prices = array();
        $i = 0;
        foreach ($page->stack_prices as $sp) :
            $pricing = array(
                'qu' => $sp->repeater_product_quantity,
                'sp' => $sp->repeater_product_stackprice,
                'sh' => $sp->repeater_product_shipping
            );
            $product->stack_prices[$i] = $pricing;
            $i++;
        endforeach;
    endif;

and then send it to the function:

echo $kiosk->___renderBuyButton($product);

I want to include this field in the module to be created upon installation soon.

  • Like 3
Link to comment
Share on other sites

1 hour ago, wbmnfktr said:

That's true. The added description and details help a lot.
When I looked into this thread there wasn't that much happening. A short intro and a link... which didn't help that much.

Yeah, later on I realized that I had been a little bit late to the party...

  • Like 1
Link to comment
Share on other sites

I'm wondering about this errors on the test page:

369198657_2022-08-2600_06_30-CheckoutMozillaFirefox.thumb.png.3c244f736fdd70a24be945efdb1e1772.png

23 hours ago, fruid said:

I will provide access to the admin soon so you can view the kiosk settings page, kiosk dashboard and some of the page tree.

That would be nice!

Link to comment
Share on other sites

54 minutes ago, DV-JF said:

I'm wondering about this errors on the test page:

That happens when a new $_SESSION is started. It stores two empty arrays in that variable, one with key 'kiosk' and one with key 'PayPalSession'. These arrays get filled later. When you refresh it's gone. Not a big problem, will fix it in the next update.

Link to comment
Share on other sites

there's a new version on github now that also installs a stack_prices repeater field and a ready-to-use product template. Hope that helps.

I'm trying to give users with guest role permission to view (at least) the kiosk dashboard page on kiosk.datajungle.xyz, but don't know how yet. Any help is welcome.

If you installed the first version and want to use the new one, uninstall while still on the first version because I renamed the templates and fields and whatnot to keep it more consistent. Sorry about the inconvenience, if there's a better way to do it? Still learning how to use the "update" functionality of modules.

  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...