Files
opensourcepos/app/Models/PaymentTransaction.php
Ollama 4d8e2442b5 feat: Add payment provider plugin system for external gateway integrations
This commit introduces a comprehensive payment provider architecture to enable
seamless integration with external payment gateways like SumUp and PayPal/Zettle.

Architecture:
- PaymentProviderInterface: Contract for all payment providers
- PaymentProviderBase: Abstract base class with common functionality
- PaymentProviderRegistry: Singleton registry for provider management
- PaymentTransaction model: Transaction tracking and status management

Infrastructure:
- Webhook controller: Endpoint for external payment callbacks
- Payment events: payment_initiated, payment_completed, sale_completed
- payment_helper.php: Helper functions for payment provider content
- Migration for ospos_payment_transactions table

Core changes:
- Add Events::trigger('payment_options') in locale_helper.php
- Add Events::trigger('sale_completed') in Sales controller
- Add Events::trigger('payment_initiated') in postAddPayment()
- Add webhook routes for /payments/webhook/{provider}

Provider stubs:
- SumUpProvider: Card reader terminal integration
- PayPalProvider: Card reader and QR code payment integration

Related issues: #4346, #4322, #3232, #3789, #3790, #2275
2026-04-01 21:29:45 +00:00

82 lines
2.3 KiB
PHP

<?php
namespace App\Models;
use CodeIgniter\Model;
class PaymentTransaction extends Model
{
protected $table = 'payment_transactions';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $useSoftDeletes = false;
protected $allowedFields = [
'provider_id',
'sale_id',
'transaction_id',
'amount',
'currency',
'status',
'metadata',
'created_at',
'updated_at'
];
protected $useTimestamps = true;
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
public const STATUS_PENDING = 'pending';
public const STATUS_AUTHORIZED = 'authorized';
public const STATUS_COMPLETED = 'completed';
public const STATUS_FAILED = 'failed';
public const STATUS_REFUNDED = 'refunded';
public const STATUS_CANCELLED = 'cancelled';
public function getTransaction(string $transactionId, ?string $providerId = null): ?array
{
$builder = $this->builder();
$builder->where('transaction_id', $transactionId);
if ($providerId !== null) {
$builder->where('provider_id', $providerId);
}
$result = $builder->get()->getRowArray();
return $result;
}
public function getTransactionsBySale(int $saleId): array
{
return $this->where('sale_id', $saleId)
->orderBy('created_at', 'DESC')
->findAll();
}
public function getPendingTransactions(?string $providerId = null): array
{
$builder = $this->builder();
$builder->where('status', self::STATUS_PENDING);
if ($providerId !== null) {
$builder->where('provider_id', $providerId);
}
return $builder->get()->getResultArray();
}
public function updateStatus(int $id, string $status, array $additionalData = []): bool
{
$data = ['status' => $status];
if (!empty($additionalData['metadata'])) {
$existing = $this->find($id);
if ($existing) {
$existingMetadata = json_decode($existing['metadata'] ?? '{}', true);
$data['metadata'] = json_encode(array_merge($existingMetadata, $additionalData['metadata']));
}
}
return $this->update($id, $data);
}
}