DeLac
DeLac

Reputation: 1122

custom External Payment Gateway - WebHook to handle the response without user visit

I've followed the skeleton of GitHub and read the official documentation about payments. I'm relating to the "external" example, where the user is redirected to an external service, makes the payment, and then the gateway notifies the result. Anyway I don't get how to properly handle the response.

The example redirects the user to a page (validation.php) that, once opened, closes the order. But what if the user close the page before loading it? Or if the user is not logged-in anymore? I'd rather to send a trigger from the gateway-server to Prestashop, without the interaction of the user. But I don't know how to do that.

The gateway service already has 2 params: one for the user redirection, one for the cient webhook to trigger. But I don't know this last one.

This is the main .php file of the module, where I list my custom gateway, and then call external.php if the user chooses this payment method.

    private function getExternalPaymentOption($cart)
    {
        $externalOption = new PaymentOption();
        $externalOption->setModuleName($this->name);
        $externalOption->setAction($this->context->link->getModuleLink($this->name, 'external', [], true));
        return $externalOption;
    }

This below is external.php, that redirects the user to the external gateway. In the call, the redirect_ok is user redirection in case of success, that url is the validation.php page. Anyway I don't know what to insert as webhook_ok: the webhook to trigger in case of success.

 public function initContent()
    {
        parent::initContent();

        // take the cart to pass to the external gateway
        $cart = $this->context->cart;

        // prepare the redirect url on success
        $ok_redirect = $this->context->link->getModuleLink(
                                                           $this->module->name,     
                                                            'validation', 
                                                            [],
                                                            true
                                                           );
         // === I DON'T KNOW WHAT INSERT HERE =====
         $ok_webhook = "???????????";
         // =======================================

         // call the external gateway
         $ch = curl_init("https://external-gatway.com");
         $payload = [
                     cart        => $cart,
                     redirect_ok => $ok_redirect,
                     webhook_ok  => $ok_webhook
                    ];
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        $response = curl_exec($ch);

        // the response has the url to be opened by the user
        $response = json_decode($response, true);  
 
        //redirect the user to that url
        Tools::redirect($response['data']['url']);       
    }

Once the user makes the payment, it will be redirected to $ok_redirect, that is the validation.php page.

    public function postProcess()
    {
        $customer = new Customer($this->context->cart->id_customer);

        // transform the cart in order
        $this->module->validateOrder(
             (int) $this->context->cart->id,
             (int) $this->getOrderState(),
             (float) $this->context->cart->getOrderTotal(true, Cart::BOTH),
             $this->module->displayName,  // $this->getOptionName(),
             null,
             [],
             (int) $this->context->currency->id,
             false,
             $customer->secure_key
         );

        // redirect to the order confirmation page
        Tools::redirect($this->context->link->getPageLink(
            'order-confirmation',
            true,
            (int) $this->context->language->id,
            [
                'id_cart' => (int) $this->context->cart->id,
                'id_module' => (int) $this->module->id,
                'id_order' => (int) $this->module->currentOrder,
                'key' => $customer->secure_key,
            ]
        ));
    }

This last step works, but just if the user visit that page, and he is logged. I need to replicate this step just as Server-To-Server without the user visit.

Upvotes: 0

Views: 384

Answers (1)

DeLac
DeLac

Reputation: 1122

I ended up creating 2 controllers, very similar but one has not checks on logged user:

  • validation checks context, current user, current cart;
  • webhook checks a custom header for the server gateway signature. No user logged needed.

Both of them close the order setting the status as 'paid'. Since 'webhook' can't take data from context, I retrieve them from the DB.

So steps are:

  1. prepare redirect_url
$ok_redirect = $this->context->link->getModuleLink($this->module->name, 'validation', [], true);
  1. prepare webooh_url
$ok_webhook = $this->context->link->getModuleLink($this->module->name,  'webhookok',  [], true);
  1. make the request to the payment gateway, passing $ok_redirect and $ok_webhook;
  2. get the payment_id together to the checkout_url where to redirect the user to start the checkout process;
  3. save in DB needed data (customer_id, cart_id, customer_safe_key, cart_total) with payment_id as primary key;
  4. redirect the user to checkout_url;
  5. once completed, the gateway triggers the 'webhookok', passing the payment_id;
  6. retrieve all the data from the DB and close the order as 'paid'.

Upvotes: 1

Related Questions