This guide walks you through building a payment gateway adapter to integrate providers like Stripe, PayPal, or any payment processor into FOSSBilling.
How Payment Gateways Work
Section titled “How Payment Gateways Work”- Payment Initiation — Client clicks "Pay", FOSSBilling calls
getHtml()to display the payment form - Payment Processing — Gateway processes the payment
- Verification — Gateway calls
ipn.php, which triggersprocessTransaction()to verify and record the payment
Getting Started
Section titled “Getting Started”File Location
Section titled “File Location”Place your adapter in:
src/library/Payment/Adapter/├── MyGateway.php # Single file└── MyGateway/ └── MyGateway.php # Or subdirectory for complex gatewaysNaming
Section titled “Naming”- File:
MyGateway.php(PascalCase) - Class:
Payment_Adapter_MyGateway
Basic Adapter Structure
Section titled “Basic Adapter Structure”<?php
class Payment_Adapter_MyGateway implements FOSSBilling\InjectionAwareInterface{ protected ?Pimple\Container $di = null;
public function __construct(private $config) { if (!isset($this->config['api_key'])) { throw new Payment_Exception( 'The ":pay_gateway" payment gateway is not fully configured. Please configure the :missing', [':pay_gateway' => 'MyGateway', ':missing' => 'API Key'], 4001 ); } }
public function setDi(Pimple\Container $di): void { $this->di = $di; }
public function getDi(): ?Pimple\Container { return $this->di; }
public static function getConfig(): array { return [ 'supports_one_time_payments' => true, 'supports_subscriptions' => false, 'description' => 'Accept payments via MyGateway.', 'logo' => [ 'logo' => 'mygateway.png', 'height' => '30px', 'width' => '65px', ], 'form' => [ 'api_key' => ['text', ['label' => 'API Key:']], 'api_secret' => ['password', ['label' => 'API Secret:']], ], ]; }
public function getHtml($api_admin, $invoice_id, $subscription): string { $invoice = $api_admin->invoice_get(['id' => $invoice_id]);
$fields = [ 'merchant_id' => $this->config['api_key'], 'amount' => number_format($invoice['total'], 2, '.', ''), 'currency' => $invoice['currency'], 'callback_url' => $this->config['notify_url'], 'success_url' => $this->config['thankyou_url'], ];
$form = '<form action="https://api.mygateway.com/checkout" method="post">'; foreach ($fields as $key => $value) { $form .= sprintf( '<input type="hidden" name="%s" value="%s" />', htmlspecialchars($key), htmlspecialchars($value) ); } $form .= '<input type="submit" value="Pay Now" class="btn btn-primary" />'; $form .= '</form>';
return $form; }
public function processTransaction($api_admin, $id, $data, $gateway_id): void { $tx = $this->di['db']->getExistingModelById('Transaction', $id); $invoiceId = $data['get']['invoice_id'] ?? $data['post']['invoice_id']; $invoice = $this->di['db']->getExistingModelById('Invoice', $invoiceId);
$paymentId = $data['post']['payment_id']; $payment = $this->verifyPaymentWithGateway($paymentId);
if ($payment['status'] === 'completed') { $clientService = $this->di['mod_service']('Client'); $invoiceService = $this->di['mod_service']('Invoice');
$client = $this->di['db']->getExistingModelById('Client', $invoice->client_id); $clientService->addFunds($client, $payment['amount'], 'MyGateway payment');
if (!$invoiceService->isInvoiceTypeDeposit($invoice)) { $invoiceService->payInvoiceWithCredits($invoice); }
$tx->status = 'processed'; } else { $tx->status = 'error'; $tx->error = 'Payment failed'; }
$this->di['db']->store($tx); }}Configuration Options
Section titled “Configuration Options”FOSSBilling injects these into $this->config:
| Key | Purpose |
|---|---|
notify_url | IPN/webhook callback URL |
return_url | Redirect after successful payment |
cancel_url | Redirect if client cancels |
thankyou_url | Thank you page URL |
test_mode | Whether in sandbox mode |
Best Practices
Section titled “Best Practices”- Always verify server-side — Never trust client-side data
- Check amounts and currency — Ensure they match the invoice
- Handle errors gracefully — Record errors on the transaction
- Prevent duplicates — Check
$tx->statusbefore processing - Log for debugging — Use
$this->di['logger']when needed
Testing
Section titled “Testing”- Enable test mode in the admin panel
- Create a test invoice
- Check Invoicing → Transactions for results
- Test the full flow: payment → verification → invoice marking
Examples
Section titled “Examples”See existing adapters in the source code for real-world implementations like Stripe and PayPal.