Generic payment processing infrastructure for CiviCRM - provides foundational services for payment attempt tracking and webhook event management across multiple payment processors.
Payment Processing Core centralizes payment processing logic that was previously duplicated across payment processor extensions (Stripe, GoCardless, Deluxe, etc.).
What it provides:
- Payment attempt tracking entity
- Webhook event logging and deduplication
- Contribution completion/failure/cancellation services
- Queue-based webhook processing with retry logic
- Multi-processor support with auto-discovery
- PHP: 8.1+ (7.4+ minimum)
- CiviCRM: 6.4.1+
- Database: MySQL 5.7+ / MariaDB 10.3+
# Download and enable
cv ext:download io.compuco.paymentprocessingcore
cv ext:enable paymentprocessingcoreFor development:
git clone https://github.com/compucorp/io.compuco.paymentprocessingcore.git
cd io.compuco.paymentprocessingcore
composer install
cv en paymentprocessingcoreThe extension provides three entities:
PaymentAttempt- Track payment sessions and attemptsPaymentWebhook- Log and deduplicate webhook eventsPaymentProcessorCustomer- Store customer IDs across processors
use Civi\Api4\PaymentAttempt;
// Create a payment attempt
$attempt = PaymentAttempt::create(FALSE)
->addValue('contribution_id', $contributionId)
->addValue('contact_id', $contactId)
->addValue('processor_type', 'stripe')
->addValue('status', 'pending')
->execute()
->first();Complete pending contributions with idempotency:
$service = \Civi::service('paymentprocessingcore.contribution_completion');
$result = $service->complete(
$contributionId, // CiviCRM contribution ID
$transactionId, // Payment processor transaction ID
$feeAmount, // Optional: Fee amount
$sendReceipt // Optional: TRUE/FALSE/NULL (auto-detect)
);
if ($result['success']) {
// Contribution completed
}Features: Idempotent, handles accounting entries, auto-detects receipt settings, records fees.
Manage customer IDs across processors:
$customerService = \Civi::service('paymentprocessingcore.payment_processor_customer');
$customerId = $customerService->getOrCreateCustomerId(
$contactId,
$paymentProcessorId,
function() use ($stripeClient, $email) {
// Only runs if customer doesn't exist
$customer = $stripeClient->customers->create(['email' => $email]);
return $customer->id;
}
);Features: Prevents duplicates, reuses existing customers, works with any processor.
Queue-based webhook processing with automatic retry:
Webhook → Verify signature → Save to DB → Queue → Process → Complete/Retry
For Payment Processor Developers:
- Create webhook endpoint:
class CRM_YourProcessor_Page_Webhook extends CRM_Core_Page {
public function run(): void {
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_YOUR_PROCESSOR_SIGNATURE'] ?? '';
$receiver = \Civi::service('yourprocessor.webhook_receiver');
$receiver->handleRequest($payload, $signature);
CRM_Utils_System::civiExit();
}
}- Implement webhook handlers:
// Option 1 (Preferred): Implement interface
use Civi\Paymentprocessingcore\Webhook\WebhookHandlerInterface;
class PaymentSuccessHandler implements WebhookHandlerInterface {
public function handle(int $webhookId, array $params): string {
// Your business logic
return 'applied'; // or 'noop', 'ignored_out_of_order'
}
}
// Option 2 (Fallback): Duck typing - if autoload issues occur
// Registry wraps in Adapter automatically
class PaymentSuccessHandler {
public function handle(int $webhookId, array $params): string {
return 'applied';
}
}- Register handlers in ServiceContainer:
$registry = $this->container->getDefinition('paymentprocessingcore.webhook_handler_registry');
$registry->addMethodCall('registerHandler', [
'yourprocessor', // processor type
'payment.succeeded', // event type
'yourprocessor.handler.payment_success', // handler service ID
]);Features:
- Automatic retry with exponential backoff (5min → 15min → 45min)
- Stuck webhook recovery (resets after 30 minutes)
- Batch processing (250 events per processor per run)
- Multi-processor auto-discovery
This extension provides infrastructure for payment processors. After installation:
- Install payment processor extensions (Stripe, GoCardless, etc.)
- Configure payment processors as normal
- PaymentProcessingCore works automatically in the background
No additional configuration required.
# Setup Docker test environment
./scripts/run.sh setup
# Run tests
./scripts/run.sh tests
# Run linter
./scripts/lint.sh check
# Run static analysis
./scripts/run.sh phpstan-changed# Run all tests
./scripts/run.sh tests
# Run specific test
./scripts/run.sh test tests/phpunit/Civi/Api4/PaymentAttemptTest.php# Linting
./scripts/lint.sh check
./scripts/lint.sh fix
# Static analysis (PHPStan level 9)
./scripts/run.sh phpstan-changedSee CLAUDE.md for development guidelines including:
- PR and commit conventions
- Code quality standards
- CI requirements
- Architecture overview
- Issues: GitHub Issues
- Email: hello@compuco.io
Developed and maintained by Compuco.