## Getting Started
### Minimal Setup
1. **Install the Bundle**
```bash
composer require ekyna/payum-payzen-bundle
Ensure Ekyna\PayumPayzenBundle\EkynaPayumPayzenBundle is registered in config/bundles.php.
Configure Payum
Add the PayZen gateway to your config/packages/payum.yaml:
payum:
gateways:
payzen:
factory: 'payum.core.gateway_factory'
class: 'Ekyna\PayumPayzen\PayzenGateway'
config:
api_key: '%env(PAYZEN_API_KEY)%'
secret_key: '%env(PAYZEN_SECRET_KEY)%'
sandbox: '%env(bool:PAYZEN_SANDBOX)%'
currency: 'EUR'
payment_method: 'CB' # or 'IDEAL', 'SEPA', etc.
First Use Case: Payment Initiation
Inject the Payum service and trigger a payment:
use Payum\Core\Payum;
use Payum\Core\Request\Capture;
public function __construct(private Payum $payum) {}
public function processPayment(float $amount, string $orderId): void
{
$storage = $this->payum->getStorage('Ekyna\PayumPayzenBundle\Model\PayzenPayment');
$payment = $storage->create();
$payment->setNumber($orderId);
$payment->setTotalAmount($amount);
$payment->setCurrencyCode('EUR');
$captureRequest = new Capture();
$captureRequest->setToken($payment->getToken());
$captureRequest->setModel($payment);
$this->payum->getGateway('payzen')->execute($captureRequest);
// Redirect to PayZen's payment page (URL stored in $payment->getAfterUrl())
}
Initiate Payment
Use Capture request to redirect users to PayZen’s payment page. Store the token and afterUrl for later use.
$this->payum->getGateway('payzen')->execute($captureRequest);
return new RedirectResponse($payment->getAfterUrl());
Handle Callback
PayZen posts a callback to your afterUrl. Use Notify request to validate the payment:
public function handlePayzenCallback(Request $request)
{
$notifyRequest = new Notify();
$notifyRequest->setModel($this->payum->getStorage()->find($request->get('PAYMENT_ID')));
$this->payum->getGateway('payzen')->execute($notifyRequest);
if ($notifyRequest->isSuccessful()) {
// Payment successful: update order status, send confirmation, etc.
}
}
Refunds & Cancellations
Use Refund or Cancel requests for post-payment actions:
$refundRequest = new Refund();
$refundRequest->setModel($payment);
$refundRequest->setAmount(10.00);
$this->payum->getGateway('payzen')->execute($refundRequest);
Symfony Forms Integration
Use Payum\Core\Bridge\Symfony\Forms\Type\PaymentType to bind payments to forms:
# config/packages/payum.yaml
payum:
gateways:
payzen:
# ... config ...
payment_methods:
- 'CB'
- 'IDEAL'
$builder->add('payment', PaymentType::class, [
'action' => $this->generateUrl('payum_payzen_notify'),
'gateway_name' => 'payzen',
]);
Storage Customization
Extend Ekyna\PayumPayzenBundle\Model\PayzenPayment to add custom fields (e.g., customerEmail):
class CustomPayzenPayment extends PayzenPayment
{
protected $customerEmail;
public function getCustomerEmail(): ?string { return $this->customerEmail; }
public function setCustomerEmail(string $email): self { $this->customerEmail = $email; return $this; }
}
Update config/packages/payum.yaml to use your class:
payum:
storage:
Ekyna\PayumPayzenBundle\Model\PayzenPayment: doctrine://default
Webhook Validation
Validate PayZen callbacks using the secret_key:
use Ekyna\PayumPayzen\PayzenGateway;
public function handleCallback(Request $request)
{
$gateway = $this->payum->getGateway('payzen');
if (!$gateway->validateCallback($request->getContent(), $request->get('PAYMENT_ID'))) {
throw new \RuntimeException('Invalid callback signature');
}
}
Sandbox vs. Production
sandbox: true) before going live.4929010000000007 for CB). Verify PayZen’s test docs.Callback Handling
afterUrl. Ensure this endpoint:
PAYMENT_ID and signature (use validateCallback()).// ❌ Avoid: Assuming $request->get('PAYMENT_ID') is trusted without validation.
$payment = $this->payum->getStorage()->find($request->get('PAYMENT_ID'));
Currency & Amount Precision
10.00 EUR → 1000 cents).number_format($amount * 100, 0, '', '') to avoid floating-point errors.Payment Method Restrictions
SEPA) require additional validation (IBAN, BIC). Validate these before initiating the payment.Enable Payum Logging
Add to config/packages/monolog.yaml:
handlers:
payum:
type: stream
path: "%kernel.logs_dir%/payum.log"
level: debug
channels: ["payum"]
Then configure Payum to use the channel:
# config/packages/payum.yaml
payum:
logging: true
Gateway Configuration Errors
api_key and secret_key are correct (no trailing whitespace).payment_method is supported by PayZen (e.g., CB, IDEAL).currency matches PayZen’s supported currencies.Doctrine Storage Issues
PayzenPayment entity is properly mapped:
/** @ORM\Entity(repositoryClass="Ekyna\PayumPayzenBundle\Repository\PayzenPaymentRepository") */
class PayzenPayment extends AbstractPayzenPayment {}
php bin/console doctrine:schema:validate to catch mapping errors.Custom Payment Actions Extend the gateway to add custom logic (e.g., pre-authorization):
use Ekyna\PayumPayzen\PayzenGateway as BaseGateway;
class CustomPayzenGateway extends BaseGateway
{
public function preAuthorize(array $details)
{
$details['action'] = 'PREAUTHORIZATION';
return parent::execute($details);
}
}
Register it in config/packages/payum.yaml:
payum:
gateways:
custom_payzen:
factory: 'payum.core.gateway_factory'
class: 'App\Payum\CustomPayzenGateway'
config: { ... }
Override Callback Validation
Extend Ekyna\PayumPayzen\PayzenGateway to add custom validation rules:
public function validateCallback(string $content, string $paymentId): bool
{
if (!$this->validateSignature($content)) {
return false;
}
// Add custom logic (e.g., check IP, user agent)
return true;
}
Webhook Retry Logic Implement a retry mechanism for failed callbacks using Symfony Messenger:
use Payum\Core\Request\Notify;
public function handleCallback(Request $request)
{
try {
$this->
How can I help you explore Laravel packages today?