Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
yasserstudio avatar

Perfex Payment Gateway

  • 6 installs
  • 3 repo stars
  • Updated May 16, 2026
  • yasserstudio/perfex-crm-skills

Helps with ai & agent building tasks.

About

perfex-payment-gateway is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.

  • perfex-payment-gateway
  • AI & Agent Building
  • AI-coding skill

Perfex Payment Gateway by the numbers

  • 6 all-time installs (skills.sh)
  • +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
  • Ranked #12,825 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
  • Data as of Jul 27, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yasserstudio/perfex-crm-skills --skill perfex-payment-gateway

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs6
repo stars3
Last updatedMay 16, 2026
Repositoryyasserstudio/perfex-crm-skills

What it does

Helps with ai & agent building tasks.

Files

SKILL.mdMarkdownGitHub ↗

Perfex Payment Gateway Development

You are a Perfex CRM payment-gateway engineer. Your job is to build gateway modules that extend App_gateway correctly — with encrypted secrets, proper webhook CSRF exclusion, and defensive callback handling — so payments process reliably across Stripe API updates and concurrent invoice payments.

Perfex supports custom payment gateways as modules since v2.3.4. A gateway is a class in modules/<module>/libraries/<Id>_gateway.php that extends App_gateway and implements process_payment($data).

File structure

modules/my_gateway/
├── my_gateway.php                    # module entry (hooks, register_payment_gateway)
├── install.php                       # optional: module-owned tables (transaction log, webhook log)
├── libraries/
│   └── My_gateway_gateway.php        # class My_gateway_gateway extends App_gateway
├── controllers/
│   └── My_gateway_webhook.php        # webhook receiver (CSRF-excluded)
├── views/
│   └── payment_form.php              # optional inline payment form
└── language/
    └── english/
        └── my_gateway_lang.php

The library filename must end with _gateway.php. Class name must match filename (capitalized first letter).

Gateway class skeleton

<?php
defined('BASEPATH') or exit('No direct script access allowed');

class My_gateway_gateway extends App_gateway
{
    public function __construct()
    {
        parent::__construct();

        $this->setId('my_gateway');

        $this->setName('My Gateway');

        $this->setSettings([
            [
                'name'      => 'api_key',
                'encrypted' => true,
                'label'     => 'API Key',
                'type'      => 'input',
            ],
            [
                'name'      => 'api_secret',
                'encrypted' => true,
                'label'     => 'API Secret',
                'type'      => 'input',
            ],
            [
                'name'      => 'test_mode',
                'label'     => 'Test Mode',
                'type'      => 'yes_no',
                'default_value' => '1',
            ],
            [
                'name'          => 'currencies',
                'label'         => 'settings_paymentmethod_currencies',
                'default_value' => 'USD,EUR',
            ],
        ]);
    }

    public function process_payment($data)
    {
        // $data contains: invoice object, amount, currency, redirect URLs
        // Option 1: redirect to external checkout
        // Option 2: render inline form (load a view)
        // Option 3: call gateway API and redirect

        $invoice  = $data['invoice'];
        $amount   = $data['amount'];
        $currency = $data['currency'];

        // Build the charge via your gateway's API
        $api_key = $this->decryptSetting('api_key');

        // ... gateway-specific logic ...

        // Redirect to gateway checkout page
        redirect($checkout_url);
    }
}

Registration (module entry file)

// my_gateway.php
register_payment_gateway('my_gateway_gateway', 'my_gateway');

First param: class name (lowercase). Second param: module system name. After activation, the gateway appears in Setup → Settings → Payment Gateways.

Settings system

TypeRenders asStored as
inputText inputPlain or encrypted string
textareaMulti-line inputPlain or encrypted string
yes_noToggle switch'1' or '0'
(no type)Text inputPlain string

Encrypted settings

Set 'encrypted' => true on any setting holding secrets (API keys, webhook signing keys). Perfex encrypts at rest using the application encryption key. Access via $this->decryptSetting('name') — never read directly from DB.

// ✅ Correct — decrypts automatically
$secret = $this->decryptSetting('api_secret');

// ❌ Wrong — returns encrypted gibberish
$secret = $this->getSetting('api_secret');

Reading non-encrypted settings

$mode = $this->getSetting('test_mode');  // '1' or '0'
$currencies = explode(',', $this->getSetting('currencies'));

Webhook handling

External gateways POST payment confirmations to your callback URL. Two requirements:

1. CSRF exclusion

Perfex's global CSRF protection blocks external POSTs. Exclude your webhook route in application/config/config.php:

$config['csrf_exclude_uris'] = array_merge(
    $config['csrf_exclude_uris'] ?? [],
    ['my_gateway_webhook/handle']
);

Or via hook in your module entry:

hooks()->add_filter('csrf_exclude_uris', function ($uris) {
    $uris[] = 'my_gateway_webhook/handle';
    return $uris;
});

2. Webhook controller

<?php
defined('BASEPATH') or exit('No direct script access allowed');

class My_gateway_webhook extends CI_Controller
{
    public function handle()
    {
        $payload = file_get_contents('php://input');
        $sig     = $this->input->get_request_header('X-Signature');

        // Verify signature using webhook signing secret
        $secret = $this->my_gateway_gateway->decryptSetting('webhook_secret');
        if (!$this->verify_signature($payload, $sig, $secret)) {
            log_message('error', 'my_gateway: webhook signature mismatch');
            $this->output->set_status_header(401);
            return;
        }

        $event = json_decode($payload, true);

        if ($event['type'] === 'payment.completed') {
            $this->process_successful_payment($event);
        }

        $this->output->set_status_header(200);
    }

    private function process_successful_payment($event)
    {
        $invoice_id = $event['metadata']['invoice_id'];

        // Use Perfex's payment recording
        $this->load->model('payments_model');
        $payment_data = [
            'invoiceid'        => $invoice_id,
            'amount'           => $event['amount'] / 100,  // cents to dollars
            'paymentmode'      => 'my_gateway',
            'transactionid'    => $event['transaction_id'],
            'date'             => date('Y-m-d'),
        ];
        $this->payments_model->add($payment_data);
    }

    private function verify_signature($payload, $sig, $secret)
    {
        $expected = hash_hmac('sha256', $payload, $secret);
        return hash_equals($expected, $sig);
    }
}

The $data array in process_payment

Perfex passes an array containing:

KeyTypeDescription
invoiceobjectFull invoice row from tblinvoices
amountfloatAmount to charge (may be partial payment)
currencyobjectCurrency info (name, symbol, decimal_separator, etc.)

Access the invoice ID as $data['invoice']->id. The amount respects partial-payment settings — don't assume it equals the invoice total.

Stripe API (Basil) changes — Perfex 3.3.0+

Perfex 3.3.0 updated to Stripe API version "Basil". If your module wraps Stripe:

  • Webhooks must be recreated after upgrading to 3.3.0 — event payload format changed
  • Stripe now respects allowed payment methods from the Stripe Dashboard (no longer hardcoded in Perfex)
  • The after_invoice_added hook now fires before email sending (changed in 3.2.0) — if your gateway listens to this hook to auto-charge, the invoice email may not have been sent yet

Existing gateway reference

Study Perfex's built-in gateways for patterns:

  • application/libraries/gateways/ — Stripe, PayPal, 2Checkout, Mollie
  • application/controllers/gateways/ — webhook receivers for built-in gateways

Common pitfalls

  • Filename must end with `_gateway.php`My_gateway.php alone won't be detected.
  • `setId()` must be alphanumeric only — no hyphens, no underscores. Use snake_case in class name but plain string for ID.
  • Don't store card data — let the external gateway handle PCI compliance. Your module only stores transaction IDs.
  • `process_payment` is called on every "Pay Now" click — it must be idempotent or create a new checkout session each time. Don't create duplicate charges.
  • Test with multiple currencies — the currencies setting is a comma-separated string that Perfex checks before showing the gateway as available for an invoice.
  • Webhook retries — most gateways retry failed webhooks. Your handler must be idempotent (check if payment already recorded before inserting).

Related skills

  • `perfex-security` — CSRF exclusion mechanics, webhook signature verification, app_generate_hash() for nonces.
  • `perfex-module-dev` — module lifecycle, register_payment_gateway() lives in the module entry file.
  • `perfex-database` — if you add a tbl<module>_transactions table for logging.

Upstream docs

  • Perfex payment gateway guide: https://help.perfexcrm.com/module-as-payment-gateway/
  • Perfex module basics: https://help.perfexcrm.com/module-basics/
  • Stripe API changelog: https://stripe.com/docs/changelog

Related skills

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.