PayPal Checkout buttons only appear on order confirmation page, not on payment p

  • Posts: 5
  • Thank you received: 0
  • Hikashop Business
3 weeks 4 days ago #372028

-- HikaShop version -- : 6.4.1
-- Joomla version -- : 3.10.2
-- PHP version -- : 7.4.33

Hello,
I am using the latest version of HikaShop with the PayPal Checkout plugin.
I want the PayPal Smart Buttons to appear directly on the payment checkout page. However, they do not load there at all. When the customer is on the payment step, they only see the radio button and the payment description. The actual PayPal buttons only appear later, on the final order confirmation page.
My checkout workflow has the Cart and Payment blocks configured together, but the buttons still refuse to render on the payment page.
Could you please help me figure out how to make the PayPal buttons load directly on the payment step instead of the confirmation page?
Thank you!

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

  • Posts: 85733
  • Thank you received: 14061
  • MODERATOR
3 weeks 4 days ago #372033

Hi,

The buttons are supposed to be displayed at the end of the checkout, once the order is created, not before. Otherwise, the user could pay for his cart while his cart could potentially be changed after this, before the order is created.

The "payment" block is for the selection of payment methods, which you can configure by creating payment methods based on different payment plugins in System>Payment methods. The payment block mechanism has no visibility and no control on the buttons displayed by the PayPal Checkout plugin.

So, unfortunately, I don't see a way to do what you want.

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

  • Posts: 5
  • Thank you received: 0
  • Hikashop Business
3 weeks 4 days ago #372039

Hi,

Thank you for the clarification.

I understand that the current PayPal Checkout plugin is designed to display the buttons only after the HikaShop order is created, and that the payment block itself is only for selecting the payment method.

Given that, I would like to ask about the best technical approach if we need a custom plugin that follows this flow:

1. Show the PayPal Smart Buttons directly on the payment step.
2. Let the customer approve the payment in the PayPal modal on the same page.
3. Create the HikaShop order only after PayPal approval.
4. Redirect the customer to the standard confirmation / after_end page once payment is completed.

My main concern is stability and security. I want to avoid creating the order too early, but I also do not want the checkout page to become slow or fragile.

Could you please confirm which approach would be the most appropriate for this kind of custom implementation?

- Should the plugin create a temporary checkout session or cart snapshot before calling PayPal?
- Is there a recommended HikaShop hook for rendering the PayPal buttons inside the payment step?
- Would it be acceptable to create the HikaShop order only in the PayPal return / approval callback?
- Are there any built-in mechanisms in HikaShop that would help with this flow, or would this require a fully custom plugin and custom checkout handling?

If you have any guidance on the safest architecture for this, I would really appreciate it.

Thank you.

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

  • Posts: 85733
  • Thank you received: 14061
  • MODERATOR
3 weeks 3 days ago #372041

Hello,

What you describe is doable as a custom plugin, and HikaShop actually gives you two clean places to render the buttons on the payment step, so you do not have to fight the checkout. Here is the architecture I would consider safest, point by point.

Rendering the buttons on the payment step, two options:

1) Payment-plugin custom HTML (simplest, recommended to start). A payment plugin can output its own HTML in the payment block: implement onPaymentDisplay() and set $method->custom_html on your method. HikaShop renders that HTML under the method, and only when the method is selected, which is exactly where you want the buttons. Several gateways (Klarna, Alma, AfterPay, Atos...) already use this to inject their widgets at the payment step.

2) Checkout API custom block (more control over placement). If you would rather have the buttons as their own checkout block instead of tied to the payment method, register a block with the Checkout API: onCheckoutStepList() to declare it so it shows up in the checkout workflow configuration, onInitCheckoutStep() to prepare its data, and onCheckoutStepDisplay() to output its HTML (for layouts prefixed with "plg."). The merchant then drops your block wherever they want in the workflow.

Start with option 1 (scoped to the method, shown on selection automatically); use option 2 if you need the buttons outside the payment-method list.

createOrder, before any HikaShop order exists
Compute the amount server-side from the current HikaShop cart (cart and currency classes), never from the client, and create the PayPal order for that amount in an endpoint of your plugin. At the same time, store a fingerprint of the cart (a hash of the line items, total and currency) in the HikaShop checkout session. That is your cart snapshot, re-checked at capture.

Creating the HikaShop order in the approval callback
Yes, creating the order only in the PayPal approval callback is the right choice to avoid early orders. Create it by going through HikaShop's own checkout order-creation path (the same one the confirmation step uses), not a hand-rolled insert, so every validation and every other plugin hooking order creation still runs. Before creating it, re-read the cart and compare to the fingerprint: if anything changed (price, stock, coupon, contents), abort and send the customer back. That "pay, then the cart changes" window is exactly what the standard end-of-checkout flow avoids by design. Then capture the payment server-side, verify the captured amount equals the order total, and on success redirect to that order's standard end/thanks page.

Security points not to skip
All amounts and the capture verification stay server-side; the client only carries the PayPal order id. You are effectively re-implementing the guarantees of HikaShop's confirmation step (terms acceptance, final stock/coupon/total validation, totals lock), and anything missed there becomes a consistency or security gap. Reuse the PayPal Checkout plugin's existing API class for create/capture instead of rewriting the PayPal calls; it already handles auth and the REST endpoints.

On performance, the step stays light: the SDK and the createOrder call only fire when your method is selected, and createOrder is a single server call. The heavy work (validation, order creation, capture) happens once, on approval, so the page itself does not get slower.

Skeleton to point you in the right direction (option 1, illustrative, not copy-paste ready):

// plugins/plg_hikashoppayment_paypalinline/paypalinline.php
class plgHikashoppaymentPaypalinline extends hikashopPaymentPlugin
{
    var $name = 'paypalinline';

    // 1) Render the buttons on the payment step (shown only when this method is selected)
    public function onPaymentDisplay(&$order, &$methods, &$usable_methods)
    {
        parent::onPaymentDisplay($order, $methods, $usable_methods);
        foreach ($methods as $method) {
            if ($method->payment_type != $this->name || !$method->enabled) continue;

            $clientId   = $method->payment_params->client_id;
            $createUrl  = hikashop_completeLink('checkout&task=notify&notif_payment=' . $this->name . '&action=create', true);
            $captureUrl = hikashop_completeLink('checkout&task=notify&notif_payment=' . $this->name . '&action=capture', true);

            $method->custom_html =
                '<div id="paypal-inline"></div>'
              . '<script src="https://www.paypal.com/sdk/js?client-id=' . htmlspecialchars($clientId) . '&components=buttons&intent=capture"></script>'
              . '<script>paypal.Buttons({'
              . '  createOrder: function(){ return fetch("' . $createUrl . '", {method:"POST"}).then(r=>r.json()).then(d=>d.id); },'
              . '  onApprove:  function(data){ return fetch("' . $captureUrl . '", {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({orderID:data.orderID})})'
              . '                 .then(r=>r.json()).then(function(res){ if(res.redirect) window.location = res.redirect; }); }'
              . '}).render("#paypal-inline");</script>';
        }
        return true;
    }

    // 2) Both AJAX actions arrive on the plugin's notify URL (same channel as the IPN)
    public function onPaymentNotification(&$statuses)
    {
        $action = hikaInput::get()->getCmd('action');
        $api    = $this->getPaypalApi(); // reuse the PayPal Checkout plugin's API class

        if ($action == 'create') {
            // amount computed SERVER-SIDE from the current cart, never from the client
            $cart = hikashop_get('class.cart')->loadFullCart();
            $total    = $cart->full_total->prices[0]->price_value_with_tax;
            $currency = $cart->full_total->prices[0]->price_currency_id;
            $this->storeCartFingerprint($cart);            // keep a snapshot in the checkout session
            $ppOrder = $api->createOrder($total, $currency);
            echo json_encode(array('id' => $ppOrder->id));
            exit;
        }

        if ($action == 'capture') {
            $in = json_decode(file_get_contents('php://input'));
            // 1. re-read the cart and verify it still matches the stored fingerprint, else abort
            // 2. create the HikaShop order via HikaShop's checkout order-creation path (so all plugins/validations run)
            // 3. capture the PayPal payment server-side
            $capture = $api->captureOrder($in->orderID);
            // 4. verify $capture amount == order total, set the order status to paid
            echo json_encode(array('redirect' => $thanksUrl));
            exit;
        }
    }
}

Net: plan for a fully custom plugin; the pieces you can safely reuse are the existing plugin's PayPal API class and HikaShop's standard order-creation path. If you would rather not re-implement the confirmation-step guarantees, the lower-risk alternative is to keep the standard flow and condense the checkout so the buttons appear as early as possible.

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

Time to create page: 0.058 seconds
Powered by Kunena Forum