BoomPay/docs

PrestaShop

A PaymentModule with hookPaymentOptions, a redirect controller, and validateOrder() doing the actual order creation once the signed return checks out.

PHPPaymentModule + hookPaymentOptionsPrestaShop 1.7 / 8.x

PrestaShop’s payment extension point is the paymentOptions hook: return one or more PaymentOption objects and PrestaShop renders them as checkout buttons. BoomPay’s button points at a front controller that creates the intent and redirects; a second front controller handles the signed return and calls validateOrder() — the same method every PrestaShop payment module uses to actually turn a cart into an order.

Module files

boompay.php
<?php
if (!defined('_PS_VERSION_')) {
    exit;
}

use PrestaShop\PrestaShop\Core\Payment\PaymentOption;

class BoomPay extends PaymentModule
{
    public $name = 'boompay';
    public $tab = 'payments_gateways';
    public $version = '1.0.0';
    public $author = 'BoomPay';
    public $need_instance = 0;
    public $ps_versions_compliancy = ['min' => '1.7', 'max' => _PS_VERSION_];
    public $bootstrap = true;

    public function __construct()
    {
        parent::__construct();
        $this->displayName = $this->l('BoomPay (Boomcoin)');
        $this->description = $this->l("Accept Boomcoin (BMC) payments through BoomPay's hosted checkout.");
        $this->confirmUninstall = $this->l('Are you sure you want to uninstall BoomPay?');
    }

    public function install()
    {
        Configuration::updateValue('BOOMPAY_SANDBOX', 1);
        Configuration::updateValue('BOOMPAY_API_KEY', '');
        Configuration::updateValue('BOOMPAY_SANDBOX_API_KEY', '');

        return parent::install();
    }

    public function uninstall()
    {
        Configuration::deleteByName('BOOMPAY_SANDBOX');
        Configuration::deleteByName('BOOMPAY_API_KEY');
        Configuration::deleteByName('BOOMPAY_SANDBOX_API_KEY');

        return parent::uninstall();
    }

    public function getContent()
    {
        $output = '';
        if (Tools::isSubmit('submit_boompay')) {
            Configuration::updateValue('BOOMPAY_SANDBOX', (int) Tools::getValue('BOOMPAY_SANDBOX'));
            Configuration::updateValue('BOOMPAY_API_KEY', Tools::getValue('BOOMPAY_API_KEY'));
            Configuration::updateValue('BOOMPAY_SANDBOX_API_KEY', Tools::getValue('BOOMPAY_SANDBOX_API_KEY'));
            $output .= $this->displayConfirmation($this->l('Settings updated.'));
        }
        return $output . $this->renderForm();
    }

    protected function renderForm()
    {
        $fields_form = [[
            'form' => [
                'legend' => ['title' => $this->l('BoomPay settings')],
                'input' => [
                    ['type' => 'switch', 'label' => $this->l('Sandbox mode'), 'name' => 'BOOMPAY_SANDBOX',
                     'values' => ['query' => [
                         ['id' => 'on', 'value' => 1, 'label' => $this->l('Enabled')],
                         ['id' => 'off', 'value' => 0, 'label' => $this->l('Disabled')],
                     ], 'id' => 'id', 'name' => 'value']],
                    ['type' => 'text', 'label' => $this->l('Live API key'), 'name' => 'BOOMPAY_API_KEY'],
                    ['type' => 'text', 'label' => $this->l('Sandbox API key'), 'name' => 'BOOMPAY_SANDBOX_API_KEY'],
                ],
                'submit' => ['title' => $this->l('Save')],
            ],
        ]];

        $helper = new HelperForm();
        $helper->module = $this;
        $helper->name_controller = $this->name;
        $helper->token = Tools::getAdminTokenLite('AdminModules');
        $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name;
        $helper->submit_action = 'submit_boompay';
        $helper->fields_value = [
            'BOOMPAY_SANDBOX' => Configuration::get('BOOMPAY_SANDBOX'),
            'BOOMPAY_API_KEY' => Configuration::get('BOOMPAY_API_KEY'),
            'BOOMPAY_SANDBOX_API_KEY' => Configuration::get('BOOMPAY_SANDBOX_API_KEY'),
        ];

        return $helper->generateForm($fields_form);
    }

    public function hookPaymentOptions($params)
    {
        if (!$this->active) {
            return [];
        }

        $option = new PaymentOption();
        $option->setModuleName($this->name)
            ->setCallToActionText($this->l('Pay with Boomcoin'))
            ->setAction($this->context->link->getModuleLink('boompay', 'redirect', [], true))
            ->setAdditionalInformation(
                $this->context->smarty->fetch('module:boompay/views/templates/hook/payment_info.tpl')
            );

        return [$option];
    }

    public function hookPaymentReturn($params)
    {
        if (!$this->active || !isset($params['order'])) {
            return '';
        }
        $this->context->smarty->assign(['order_reference' => $params['order']->reference]);
        return $this->context->smarty->fetch('module:boompay/views/templates/hook/payment_return.tpl');
    }

    public function getBoomPayClient()
    {
        $sandbox = (bool) Configuration::get('BOOMPAY_SANDBOX');
        $apiKey = $sandbox ? Configuration::get('BOOMPAY_SANDBOX_API_KEY') : Configuration::get('BOOMPAY_API_KEY');
        require_once __DIR__ . '/classes/BoomPayClient.php';
        return new BoomPayClient($apiKey, $sandbox);
    }
}
classes/BoomPayClient.php
<?php

class BoomPayClient
{
    const SANDBOX_BASE = 'https://sapi.boom.market';
    const LIVE_BASE = 'https://api.boom.market';

    private $apiKey;
    private $baseUrl;

    public function __construct($apiKey, $sandbox = false)
    {
        $this->apiKey = $apiKey;
        $this->baseUrl = $sandbox ? self::SANDBOX_BASE : self::LIVE_BASE;
    }

    public function createIntent($amount, $successUrl, $failureUrl, $label, array $metadata = [])
    {
        return $this->request('POST', '/v1/boompay/paymentIntent', [
            'amount' => $amount,
            'successUrl' => $successUrl,
            'failureUrl' => $failureUrl,
            'label' => $label,
            'metadata' => $metadata,
        ]);
    }

    public function getPayment($id)
    {
        return $this->request('GET', '/v1/boompay/paymentIntent/' . rawurlencode($id));
    }

    /** signature = base64(HMAC-SHA1(apiKey, paymentIntentId)) -- see API Reference > Verifying the return */
    public function verifySignature($paymentIntentId, $signature)
    {
        $expected = base64_encode(hash_hmac('sha1', $paymentIntentId, $this->apiKey, true));
        $received = str_replace(' ', '+', (string) $signature);
        return hash_equals($expected, $received);
    }

    private function request($method, $path, $body = null)
    {
        $ch = curl_init($this->baseUrl . $path);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, ['x-api-key: ' . $this->apiKey, 'Content-Type: application/json']);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);

        if ($method === 'POST') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($body));
        }

        $response = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        $data = json_decode($response, true);

        if ($status < 200 || $status >= 300) {
            $message = $data['message'] ?? 'Unknown BoomPay API error';
            throw new Exception("BoomPay API error ({$status}): {$message}");
        }

        return $data;
    }
}
controllers/front/redirect.php
<?php

class BoomPayRedirectModuleFrontController extends ModuleFrontController
{
    public $ssl = true;

    public function postProcess()
    {
        $cart = $this->context->cart;

        if (!$this->module->active || !$this->context->customer->isLogged()) {
            Tools::redirect('index.php?controller=order');
        }

        $client = $this->module->getBoomPayClient();
        $total = (float) $cart->getOrderTotal(true, Cart::BOTH);

        $intent = $client->createIntent(
            $total,
            $this->context->link->getModuleLink('boompay', 'callback', ['status' => 'success', 'cart_id' => $cart->id], true),
            $this->context->link->getModuleLink('boompay', 'callback', ['status' => 'failure', 'cart_id' => $cart->id], true),
            'Cart #' . $cart->id,
            ['cart_id' => $cart->id]
        );

        Tools::redirect($intent['link']);
    }
}
controllers/front/callback.php
<?php

class BoomPayCallbackModuleFrontController extends ModuleFrontController
{
    public $ssl = true;

    public function postProcess()
    {
        $cartId = (int) Tools::getValue('cart_id');
        $intentId = (string) Tools::getValue('paymentIntentId');
        $signature = (string) Tools::getValue('X-Boom-Signature');
        $status = (string) Tools::getValue('status');

        $client = $this->module->getBoomPayClient();
        $cart = new Cart($cartId);

        if (!Validate::isLoadedObject($cart) || !$client->verifySignature($intentId, $signature)) {
            Tools::redirect('index.php?controller=order');
        }

        $payment = $client->getPayment($intentId);

        if ($status === 'success' && !empty($payment['paidAt'])) {
            $this->module->validateOrder(
                $cartId,
                (int) Configuration::get('PS_OS_PAYMENT'),
                $payment['amount'],
                $this->module->displayName,
                null,
                ['transaction_id' => $intentId],
                null,
                false,
                $cart->secure_key
            );

            Tools::redirect('index.php?controller=order-confirmation&id_cart=' . $cartId
                . '&id_module=' . $this->module->id
                . '&id_order=' . $this->module->currentOrder
                . '&key=' . $cart->secure_key);
        }

        Tools::redirect('index.php?controller=order&step=1');
    }
}
views/templates/hook/payment_info.tpl
<p>{l s='You will be redirected to BoomPay to approve payment from your Boom wallet.' mod='boompay'}</p>
views/templates/hook/payment_return.tpl
<p>
  {l s='Thank you! Your order' mod='boompay'} {$order_reference} {l s='has been placed and paid via BoomPay.' mod='boompay'}
</p>
i

Skip hand-writing config.xml — PrestaShop regenerates that cache file itself the first time the modules list loads, from the properties set in the constructor above.

Install

Zip the folder as boompay.zip with this structure preserved, then upload via Modules → Module Manager → Upload a module. Configure it from the same screen — set sandbox mode and a sandbox API key before doing anything else.

Testing in sandbox

Add an item to cart, reach the payment step, and choose Pay with Boomcoin. Confirm the redirect lands on sapi.boom.market, approve the test payment, and check that an order now exists under Orders with status Payment accepted and a transaction_id matching the intent id. Then run a deliberately cancelled payment and confirm it correctly returns to the cart instead of creating an order.

Go-live checklist

  • Switch off sandbox mode and set a live API key once a full test order has succeeded.
  • Confirm your shop forces HTTPS — $ssl = true on both front controllers expects it, and BoomPay’s own hosted page is HTTPS-only.
  • BoomPay settles in BMC only; if your catalog prices in another currency, convert before calling createIntent — there’s no FX endpoint to lean on.
  • Double check PS_OS_PAYMENT resolves to the order state you actually want new BoomPay orders to land in for your store’s workflow.