Skip to main content
Version: 3.x

Payment (PSP) Connectors

Version
3.x
Updated
View markdown

How to add a payment method to CBX — an integration with a payment service provider (PSP) — by dropping a connector folder into the customization layer. A connector is a small, convention-based set of files (no class hierarchy to learn): metadata functions, an admin settings form, a checkout "bridge" that hands off to the PSP, a result page, and (if the PSP calls back) an IPN handler. CBX ships ~20 built-in connectors (PayPal, bank transfer, cash on delivery, and more) you can copy from.

This is a customization-first shadow mechanism: a connector folder in data/customization/psp_connectors/ is used instead of a built-in one of the same name, and new names simply add new methods.

Read com_configbox_customization_overview.md first. All paths are relative to the component root docroot/components/com_configbox/. Source references are point-in-time (component 3.4.1) — verify against the code.


1. What a connector is

A connector is a folder named after the payment method (lowercase, snake_case), e.g. paypal_wpstandard, banktransfer. The folder name is the connector name and is used to build every file path, function name and class name (so keep it stable and filesystem-safe — it's run through sanitizeFileName(), helpers/psp.php:31).

Resolution is customization-first (ConfigboxPspHelper::getPspConnectorFolder(), :29-41):

if (is_dir(self::getCustomDir().'/'.$subFolder)) { return customDir/<name>; } // customization wins
else { return defaultDir/<name>; } // else built-in

getConnectorNames() (:16-22) merges both dirs, so your custom connector appears in the payment-method admin alongside the built-ins; a custom folder with a built-in's name replaces that built-in.


2. The folder contract (files, functions, classes)

Everything is convention. For a connector named <name>:

FileRequired?What it provides
administration.phpyesPlain functions describing the connector (title, setting keys, IPN support). See §3.
settings.phpif it has settingsThe admin settings form template (the inputs for the keys in _get_setting_keys()). See §4.
bridge.phpyesThe checkout hand-off template — emits the form/redirect to the PSP. Rendered as ConfigboxViewCheckoutpspbridge. See §5.
result.phpusuallyThe return/confirmation page template, rendered as ConfigboxViewPaymentresult. See §6.
ipn.phponly if IPNThe instant payment notification handler class. See §7.
language/optionalConnector-specific language files.

There is no base class. A connector is files + named functions + (for IPN) one class — by convention, resolved by ConfigboxPspHelper. This is deliberately lightweight; follow a built-in connector as your template.


3. administration.php — the metadata functions

Plain functions, prefixed with the connector name (this is how the helper finds them). From the built-in paypal_wpstandard/administration.php:

<?php
function paypal_wpstandard_get_title() { // shown in the admin payment-method list
return 'PayPal Payments Standard';
}
function paypal_wpstandard_get_setting_keys() { // POST keys stored as this method's settings
return array('paypalid', 'testmode');
}
function paypal_wpstandard_has_instant_payment_notification() {// does the PSP call back? (IPN)
return true;
}
function paypal_wpstandard_get_product_url() { // optional: a "learn more" link
return 'https://www.paypal.com/…';
}
FunctionRead byPurpose
<name>_get_title()getPspConnectorTitle() (:79-95)Display name (falls back to the folder name).
<name>_get_setting_keys()getPspConnectorSettingKeys() (:50-72)The keys taken from POST when the payment method is saved; these become the connector's settings, available in settings.php, the order record's payment object, and the IPN class.
<name>_has_instant_payment_notification()pspSupportsIpn() (:102-119)true ⇒ CBX expects an ipn.php (§7).

The smallest possible administration.php is just <name>_get_title() + <name>_has_instant_payment_notification() returning false (e.g. the built-in banktransfer). A method with no PSP hand-off needs no settings and no IPN.


4. settings.php — the admin settings form

A plain PHP template rendering the inputs for the keys in _get_setting_keys(). $this is the payment-method admin view; read current values with $this->settings->get('<key>') and name each input after its setting key so it's picked up from POST on save. From paypal_wpstandard/settings.php:

<?php defined('CB_VALID_ENTRY') or die(); ?>

<div class="kenedo-property property-type-string">
<div class="property-label"><?php echo KText::_('PayPal email address'); ?></div>
<div class="property-body">
<input class="form-control" type="text" name="paypalid"
value="<?php echo hsc($this->settings->get('paypalid')); ?>" />
</div>
</div>

The input name="paypalid" matches a key returned by paypal_wpstandard_get_setting_keys(), so saving the payment method stores it. Escape values with hsc().


5. bridge.php — the checkout hand-off

After checkout, CBX renders the connector's bridge.php as the view ConfigboxViewCheckoutpspbridge. This is where you POST/redirect to the PSP. For a redirect PSP you typically emit a form (auto-submitted by the checkout JS) with the PSP's fields and your settings; for an offline method you just link to the result page. From banktransfer/bridge.php (offline — no PSP):

<?php
defined('CB_VALID_ENTRY') or die();
/** @var $this ConfigboxViewCheckoutpspbridge */
?>
<div id="checkoutform">
<a class="trigger-redirect-to-psp"
href="<?php echo KLink::getRoute('index.php?option=com_configbox&view=paymentresult&connector_name=banktransfer'); ?>"></a>
</div>

The a.trigger-redirect-to-psp element is the convention the checkout JS follows to advance. A real PSP bridge builds a <form> whose action is the PSP endpoint and whose hidden inputs carry the amount, order id, return URLs, and credentials from $this->settings. Copy a redirecting built-in (e.g. paypal_wpstandard/bridge.php) as your starting point.


6. result.php — the return / confirmation page

Rendered as ConfigboxViewPaymentresult (the view=paymentresult URL the bridge points at). $this exposes the order and shop context — $this->orderRecord (incl. payableAmount, id), $this->shopdata, and link vars ($this->linkToOrder, $this->linkToCustomerProfile, $this->linkToDefaultProductListing). From banktransfer/result.php (abridged):

<?php
defined('CB_VALID_ENTRY') or die();
/** @var ConfigboxViewPaymentresult $this */
?>
<div id="com_configbox" class="cb-content">
<div id="view-paymentresult">
<h2><?php echo KText::_('Payment via bank transaction'); ?></h2>
<p><?php echo KText::sprintf('Please transfer %s to the following account:', cbprice($this->orderRecord->payableAmount)); ?></p>
<!-- … account details from $this->shopdata, escaped with hsc()-->
<ul class="continue-links">
<li><a href="<?php echo $this->linkToOrder; ?>"><?php echo KText::_('See order details'); ?></a></li>
</ul>
</div>
</div>

Use cbprice() for money and hsc() for everything dynamic.


7. ipn.php — instant payment notification (when the PSP calls back)

If _has_instant_payment_notification() returns true, CBX routes the PSP's server-to-server callback to your IPN class. getIpnObject() (:126-155) loads ipn.php and instantiates the class:

$className = 'Ipn' . ucwords(strtolower($connectorName)); // :146

⚠️ The IPN class name has a quirk. ucwords() capitalizes after spaces, not underscores, and the name is first lowercased. So for connector paypal_wpstandard the expected class is Ipnpaypal_wpstandard (one leading capital, the rest lowercase, underscore preserved) — not IpnPaypalWpstandard. Name your IPN class exactly 'Ipn'.ucwords(strtolower('<name>')); copy a built-in's ipn.php and rename to be safe. A mismatch throws "IPN class not found" (:148-151).

The IPN class verifies the PSP's callback (authenticity + amount), then sets the order's status — usually by resolving a status code and updating the order. Settings from _get_setting_keys() are available to the IPN object (e.g. to pick test vs. live credentials). Use a built-in IPN handler (e.g. paypal_wpstandard/ipn.php) as the structural reference; the verification details are PSP-specific.

Security note: the IPN endpoint is public and unauthenticated by CBX — anyone can POST to it. Your handler is the only thing standing between a forged callback and a "paid" order. Always verify the notification with the PSP (signature/round-trip), check the amount and currency against the order, and be idempotent (the PSP may retry). SQL here is hand-built mysqli — quote/validate everything from the request.


8. Worked example — a minimal "Invoice on account" method

An offline method (no PSP, no IPN): the customer is told they'll be invoiced. Folder data/customization/psp_connectors/invoiceaccount/.

// administration.php
<?php
function invoiceaccount_get_title() {
return KText::_('Invoice on account');
}
function invoiceaccount_has_instant_payment_notification() {
return false; // offline — no callback, no ipn.php needed
}
// (no _get_setting_keys → no settings.php needed)
// bridge.php (rendered as ConfigboxViewCheckoutpspbridge)
<?php
defined('CB_VALID_ENTRY') or die();
/** @var $this ConfigboxViewCheckoutpspbridge */
?>
<div id="checkoutform">
<a class="trigger-redirect-to-psp"
href="<?php echo KLink::getRoute('index.php?option=com_configbox&view=paymentresult&connector_name=invoiceaccount'); ?>"></a>
</div>
// result.php (rendered as ConfigboxViewPaymentresult)
<?php
defined('CB_VALID_ENTRY') or die();
/** @var ConfigboxViewPaymentresult $this */
?>
<div id="com_configbox" class="cb-content"><div id="view-paymentresult">
<h2><?php echo KText::_('Thank you for your order'); ?></h2>
<p><?php echo KText::sprintf('We will invoice the amount of %s. Order number %s.',
cbprice($this->orderRecord->payableAmount), $this->orderRecord->id); ?></p>
<ul class="continue-links">
<li><a href="<?php echo $this->linkToOrder; ?>"><?php echo KText::_('See order details'); ?></a></li>
</ul>
</div></div>

Three files, no class. The method now appears in the admin payment-method list (title from administration.php) and works end-to-end at checkout. Enable/assign it like any payment method (functional/com_configbox_commerce_setup.md).


9. Deployment checklist

data/customization/psp_connectors/<name>/
administration.php ← <name>_get_title(), _get_setting_keys(), _has_instant_payment_notification()
settings.php ← admin settings form (only if there are setting keys)
bridge.php ← checkout hand-off (ConfigboxViewCheckoutpspbridge)
result.php ← return/confirmation page (ConfigboxViewPaymentresult)
ipn.php ← class Ipn<ucwords(strtolower(name))> (only if IPN supported)
language/ ← optional connector language files
  1. Pick a stable, lowercase folder name = the connector name (drives all paths/functions/classes).
  2. Write administration.php with the three core functions (title, setting keys, IPN flag).
  3. If it has settings, add settings.php with inputs named after the keys; read with $this->settings->get().
  4. Write bridge.php (PSP redirect form, or a link to view=paymentresult for offline).
  5. Write result.php for the customer-facing confirmation.
  6. If the PSP calls back, set _has_instant_payment_notification() to true, add ipn.php with class Ipn<ucwords(strtolower(name))> (§7), and verify every callback (auth + amount + idempotency).
  7. Configure the payment method in the admin (assign to groups/zones) — see the commerce-setup doc.
  8. Verify manually end-to-end — checkout → bridge → PSP → IPN → status change → result page — in the PSP's sandbox. CBX has zero automated tests.

10. Conventions & gotchas

  • Folder name = connector name = path/function/class prefix. Keep it lowercase, snake_case, stable.
  • Customization-first. A custom folder shadows a built-in of the same name install-wide; new names add new methods.
  • Functions are name-prefixed (<name>_get_title, …) — that's how the helper resolves them.
  • IPN class name quirk'Ipn'.ucwords(strtolower($name)) capitalizes after spaces only, so paypal_wpstandardIpnpaypal_wpstandard (§7). Copy + rename from a built-in.
  • IPN is public + unauthenticated — verify with the PSP, check amount/currency, be idempotent. This is the security-critical surface of any payment integration.
  • Templates, not classes (except IPN). bridge.php/result.php are view templates with $this bound to the respective core view; settings.php to the admin view.
  • Escape & quote. hsc() for output, cbprice() for money; hand-built mysqli — never trust request data in the IPN handler.
  • Copy a built-in that matches your PSP's flow (redirect vs. offline vs. API) as your starting point — the ~20 in psp_connectors/ cover most patterns.

See also

  • psp_connectors/ — the ~20 built-in connectors (banktransfer, paypal_wpstandard, cashondelivery, …) to copy from.
  • helpers/psp.phpConfigboxPspHelper: resolution (getPspConnectorFolder, :29), metadata (:50,:79,:102), IPN loading (getIpnObject, :126).
  • functional/com_configbox_commerce_setup.md — configuring/assigning payment methods in the admin.
  • com_configbox_customization_overview.md — the extension-point map.
  • com_configbox_events_and_observers.mdonConfigboxGetPaymentOptions (offer payment options programmatically) and onConfigBoxSetStatus (react to the status change an IPN triggers).