Payment Integration Guide for Cotonti

The Payments module in Cotonti is a versatile tool for implementing payment acceptance in extensions. It supports integration with various payment systems, such as PayPal, Payeer, Unitpay, Enot, Robokassa, bank cards, cryptocurrencies, and others.

This guide is intended for developers creating e-commerce extensions (such as online stores, paid services, or consultations) for the Cotonti platform. It provides detailed instructions on how to set up payment acceptance using the Payments module, including integration with payment systems, creating a custom payment plugin, handling webhooks, and testing. The guide is clearly structured and includes practical examples.

Overview of the Payments Module

The Payments module in Cotonti is a versatile tool for implementing payment acceptance in extensions. It supports integration with various payment systems, such as PayPal, Payeer, Unitpay, Enot, Robokassa, bank cards, cryptocurrencies, and others. The module allows you to create payments, track their statuses, and redirect users to payment pages. After a successful payment, the module triggers a hook to update the payment status, allowing the extension to activate paid services automatically.

Key Features of the Module

  • Creating payments linked to orders or services.
  • Support for any payment system via plugins.
  • Automatic status handling via webhooks.
  • Redirecting users after payment to a specified URL.
  • Testing support via the Nullbilling plugin.

Creating a Payment in Your Extension

Preparing to Create a Payment

To implement payment processing in your extension (e.g., an online store or a paid service), use the cot_payments_create_order() function. It creates a payment in the Cotonti system and redirects the user to the payment system selection page.

Example code to create a payment in your extension’s controller after an order is created:

$amount = 500; // Payment amount (in RUB, cents as decimal, e.g., 500.00)
$area = 'orderType'; // Payment type (e.g., 'order' or 'service')
$options = [
    // Optional. Duration of the paid service (in seconds).
    'time' => $months * 30 * 24 * 60 * 60, // Example: 1 month = 30 days
    
    // Optional. Payment description.
    'desc' => 'Payment for order #123',
    
    // Optional. Order or user identifier in your extension.
    'code' => $orderId,
    
    // Optional. Redirect URL after successful payment.
    'redirect' => cot_url('extension-code', ['m' => 'orders', 'id' => $orderId], '', true)
];

cot_payments_create_order($area, $amount, $options);

Explanation of Parameters

  • $amount: Payment amount. Specify in the currency supported by the payment system. Cents are passed as a decimal (e.g., 500.50 for 500 rubles and 50 kopecks).
  • $area: Payment type. Used to identify the payment within your system (e.g., 'order' or 'subscription'). Together with $options['code'], it should uniquely identify the payment.
  • $options['time']: Duration of the paid service (in seconds), e.g., for subscriptions.
  • $options['desc']: Payment description visible to the user.
  • $options['code']: Unique identifier for the order or user in your extension.
  • $options['redirect']: URL to redirect the user after successful payment. If not specified, the user will be taken to the payment history page (https://domain.tld/payments?m=balance&n=history).

Processing the Payment After Completion

After successful payment, the payment system notifies Cotonti via webhook, and the Payments module updates the payment status to "Paid". At this point, the payments.payment.success hook is triggered. Your extension should handle this hook to activate the paid service.

Example of hook handling:

// File: plugins/yourExtension/yourExtension.payments.payment.success.php
global $db, $db_payments;

if (cot_module_active('payments')) {
    // Get payment data
    $payment = $params; // $params is an array from the cot_payments table
    
    // Check that the payment belongs to your extension
    if ($payment['pay_area'] === 'orderType' && !empty($payment['pay_code'])) {
        // Your logic to activate the service
        // For example, update the order status in the database
        $db->update($db_orders, ['order_status' => 'paid'], "order_id = ?", [$payment['pay_code']]);
    }
}

Important: The payments.payment.success hook is triggered by the payment system server via webhook. Therefore, you must not output any data, headers, or perform redirects in it. Use the $options['redirect'] page to show results to the user.

Testing with the Nullbilling Plugin

To test your extension’s integration with the Payments module, use the Nullbilling plugin. It simulates a successful payment without real transactions.

Steps for Testing

  1. Install the Nullbilling plugin via Cotonti admin panel.
  2. Create a test order in your extension using the code above.
  3. Go to the payment page and select Nullbilling as the payment method.
  4. Confirm the "payment" — the plugin will simulate a successful transaction.
  5. Verify that the payments.payment.success hook worked and the service was activated (e.g., the order status changed).

Creating a Custom Payment Plugin

If your payment system doesn’t have a ready-made plugin, you can create your own. The plugin should register the payment system in Cotonti, handle webhooks, and manage user redirects.

Registering a Payment System

Your plugin should register itself in the $cot_billings array using the payments.billing.register hook. Example:

// File: plugins/myPaymentSystem/myPaymentSystem.payments.billing.register.php
if (
    Cot::$cfg['plugin']['myPaymentSystem']['on']
    && !empty(Cot::$cfg['plugin']['myPaymentSystem']['apiKey'])
    && !empty(Cot::$cfg['plugin']['myPaymentSystem']['secret'])
) {
    $cot_billings['myPaymentSystem'] = [
        'plug' => 'myPaymentSystem',
        'title' => Cot::$L['myPaymentSystem_title'], // System name, e.g. "My Payment System"
        'icon' => Cot::$cfg['plugins_dir'] . '/myPaymentSystem/images/logo.png',
    ];
}

Explanation

  • Ensure the plugin is enabled and API keys are configured.
  • plug: Plugin code (must match the plugin folder name).
  • title: Name of the payment system shown to users.
  • icon: Path to the payment system’s logo.

Disabling Anti-XSS for Webhooks

If the payment system sends notifications via POST, you need to disable Anti-XSS protection for the webhook handler so Cotonti can accept the request. Use the input hook:

// File: plugins/myPaymentSystem/myPaymentSystem.input.php
if (
    isset($_GET['e'])
    && $_GET['e'] === 'myPaymentSystem'
    && isset($_GET['a'])
    && $_GET['a'] === 'notify'
    && $_SERVER['REQUEST_METHOD'] === 'POST'
) {
    define('COT_NO_ANTIXSS', 1);
    Cot::$cfg['referercheck'] = false;
}

Explanation

  • Check request parameters (e, a) and method (POST).
  • Disable Anti-XSS and referrer check for webhook requests.

Removing the “Open” Button in Admin Panel

Payment plugins don’t require standalone pages for user interaction, so the "Open" button in the admin panel can be removed using the admin.extensions.plug.list.loop and admin.extensions.details hooks:

// File: plugins/myPaymentSystem/myPaymentSystem.admin.extensions.plug.list.loop.php
if ($type === COT_EXT_TYPE_PLUGIN && $code === 'myPaymentSystem') {
    $t->assign([
        'ADMIN_EXTENSIONS_JUMPTO_URL' => null,
    ]);
}
// File: plugins/myPaymentSystem/myPaymentSystem.admin.extensions.details.php
if ($type === COT_EXT_TYPE_PLUGIN && $code === 'myPaymentSystem') {
    $standalone = null;
    $t->assign([
        'ADMIN_EXTENSIONS_JUMPTO_URL' => $standalone,
    ]);
}

Displaying Setup Instructions in Admin Panel

If you need to show setup instructions (e.g., how to obtain API keys), use the admin.config.edit.tags hook:

// File: plugins/myPaymentSystem/myPaymentSystem.admin.config.edit.tags.php
if ($o === COT_EXT_TYPE_PLUGIN && $p === 'myPaymentSystem') {
    $adminHelp = 'To set up the payment system, log in to your account on myPaymentSystem.com, get your API key and secret key, then enter them in the plugin settings.';
}

Handling the Standalone Hook

The standalone hook handles two scenarios:

  1. Redirecting the user to the payment system website.
  2. Processing a webhook from the payment system.

Example code for the standalone hook:

// File: plugins/myPaymentSystem/myPaymentSystem.standalone.php
require_once cot_incfile('payments', 'module');

// Get the payment ID
$paymentId = cot_import('pid', 'G', 'INT');
if ($paymentId) {
    // Scenario 1: Redirect to payment
    $payment = PaymentRepository::getById($paymentId);
    if (!$payment) {
        cot_die_message(404);
    }

    // Check if the payment can be processed
    cot_block(in_array($payment['pay_status'], PaymentDictionary::ALLOW_PAYMENT_STATUSES, true));

    // Generate unique ID if required
    $psPaymentId = $paymentId . '-' . time();

    // Prepare API request
    $apiKey = Cot::$cfg['plugin']['myPaymentSystem']['apiKey'];
    $secret = Cot::$cfg['plugin']['myPaymentSystem']['secret'];
    $paymentData = [
        'amount' => $payment['pay_summ'],
        'description' => $payment['pay_desc'],
        'order_id' => $psPaymentId,
        'success_url' => PaymentService::getSuccessUrl($paymentId),
        'fail_url' => PaymentService::getFailUrl($paymentId),
    ];

    $response = sendApiRequest($apiKey, $secret, $paymentData);

    if ($response['status'] === 'success' && !empty($response['payment_url'])) {
        PaymentService::setStatus(
            $paymentId,
            PaymentDictionary::STATUS_PROCESS,
            'myPaymentSystem',
            PaymentDictionary::METHOD_CARD,
            $psPaymentId
        );
        cot_redirect($response['payment_url']);
    } else {
        cot_die_message(500, 'Payment initialization failed');
    }
}

// Scenario 2: Webhook processing
if (isset($_GET['a']) && $_GET['a'] === 'notify' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $webhookData = json_decode(file_get_contents('php://input'), true);
    $paymentId = $webhookData['payment_id'];
    $transactionId = $webhookData['transaction_id'];

    if (verifyWebhook($webhookData, Cot::$cfg['plugin']['myPaymentSystem']['secret'])) {
        PaymentService::setStatus(
            $paymentId,
            PaymentDictionary::STATUS_PAID,
            'myPaymentSystem',
            null,
            null,
            $transactionId
        );
    }
    exit;
}

Explanation

  • Scenario 1 (redirect): Validate payment, initiate payment request via API, redirect user.
  • Scenario 2 (webhook): Handle POST webhook, verify signature, update payment status. No output is allowed.
  • The sendApiRequest and verifyWebhook functions are examples—you must implement them based on your payment system’s documentation.

Tips & Best Practices

  1. Unique Payment ID: Generate unique IDs using payment ID and timestamp (e.g., $paymentId . '-' . time()) if required.
  2. Logging: Log incoming webhook data for debugging purposes.
  3. Webhook Testing: Use tools like Postman or ngrok to test webhooks on localhost.
  4. Read API Docs: Carefully follow your payment provider’s API documentation for proper implementation.
  5. Security: Always verify webhook signatures to prevent fake requests.

Conclusion

The Payments module in Cotonti provides a flexible and convenient way to integrate payment systems into your extensions. Creating payments via cot_payments_create_order() and handling the payments.payment.success hook allows you to automate service activation. To add support for a new payment system, build a plugin, register it in $cot_billings, handle webhooks, and test using Nullbilling. Following this guide will help you implement payment functionality quickly and reliably.

Module discussion, questions, and help are available in the forum section.

7 minutes read Sodium Carbonate

Comments (0)

No comments yet
Only registered users can post new comments

Content author

webitproff

Offline

Sodium Carbonate

Last logged: 2026-07-19 01:57

  • Page published: 2025-12-20 17:26
  • Last update: 2025-12-22 08:23
  • Language: