otobul / epaybg-bundle
Symfony 扩展包,用于商户使用 ePay.bg 通信包。
Requires
- php: ^7.2.5 || ^8.0
- symfony/form: ^4.4 || ^5.0 || ^6.0
- symfony/framework-bundle: ^4.4 || ^5.0 || ^6.0
- symfony/http-client: ^4.4 || ^5.0 || ^6.0
- symfony/string: ^5.0 || ^6.0
- symfony/validator: ^4.4 || ^5.0 || ^6.0
- twig/twig: ^2.13 || ^3.0
Requires (Dev)
- nyholm/symfony-bundle-test: ^1.6
- symfony/phpunit-bridge: ^5.0 || ^6.0
README
OtobulEpaybgBundle 是一个 symfony 扩展包,用于帮助商户处理 ePay.bg 通信包。
1. 安装
使用以下命令安装包
composer require otobul/epaybg-bundle
如果你没有使用 Symfony Flex,你还需要在 config/bundles.php 文件中启用 Otobul\EpaybgBundle\OtobulEpaybgBundle 并配置该扩展包。
2. 配置
2.1 配置扩展包
在 packages/otobul_epaybg.yaml 中配置扩展包
otobul_epaybg: # Identification number of the merchant min: '%env(OTOBUL_EPAYBG_MIN)%' # The secret word of the merchant secret: '%env(OTOBUL_EPAYBG_SECRET)%' # If true all requests will be sent to ePay.bg’s Demo System isDemo: '%env(bool:OTOBUL_EPAYBG_IS_DEMO)%'
使用以下命令列出默认值
php bin/console config:dump otobul_epaybg
为了正常工作,你还需要配置开发环境文件。如果你无法访问 ePay.bg 演示系统,你可以访问 https://demo.epay.bg/ 并注册以获取演示商户编号和密钥。
2.2 配置 webhook 通知
要使用 webhook 通知,你需要将 webhook 通知路由添加到你的配置中。在 config/routes/otobul_epaybg.yaml 中配置该路由
otobul_epaybg: resource: '@OtobulEpaybgBundle/Resources/config/routes.xml' prefix: /webhook/epaybg methods: ['POST']
新的通知 URL 将类似于:https://your-domain.com/webhook/epaybg/,在 ePay.bg 账户中添加通知 URL。
3. 使用
此扩展包提供以下功能
- 用于处理 ePay.bg webhook 通知的控制器;
- 可以在控制台命令或自定义控制器中使用的服务;
- 模板函数,可以直接在模板中生成“支付”按钮和 Easypay 支付代码。
3.1 模板
3.1.1 为 WEB_LOGIN 表单生成“支付”按钮
要在模板中生成简单的 web_login “支付”按钮,你可以使用 epayWebLoginForm twig 函数。示例
{% include '@OtobulEpaybg/Form/web_login.html.twig' with {
form: epayWebLoginForm({invoice: 1, amount: 100})
} only %}
必需参数
- invoice:您的唯一发票号码;
- amount:总金额(保加利亚列弗)。这是默认货币。
高级配置 web_login “支付”按钮允许您配置其他可选参数
{% include '@OtobulEpaybg/Form/web_login.html.twig' with {
form: epayWebLoginForm({
invoice: 1,
amount: 100,
returnUrl: url('your_payment_success_route'),
cancelUrl: url('your_payment_cancel_route'),
expDate: 'now'|date_modify('+7 day'),
currency: 'EUR',
description: 'Extra description max to 100 symbols',
encoding: 'utf-8',
}), button: 'Pay'
} only %}
可选参数
- returnUrl:您的成功路由。ePay.bg 在成功支付后会将用户重定向到此;
- cancelUrl:您的取消路由。ePay.bg 在取消支付后会将用户重定向到此;
- expDate:过期日期。变量需要是 \DateTime 类型。默认为 +7 天;
- currency:ISO 三字母货币代码。接受值为 BGN|EUR|USD;
- description:额外描述,最多 100 个符号;
- encoding:接受值为 utf-8 或 CP1251;
- button:支付按钮的标签。
3.1.2 为 CREDIT_CARD 表单生成“支付”按钮
要在模板中生成简单的 credit_card “支付”按钮,你可以使用 epayCreditCardForm twig 函数。示例
{% include '@OtobulEpaybg/Form/web_login.html.twig' with {
form: epayCreditCardForm({invoice: 1, amount: 100})
} only %}
对于高级配置,你可以使用上面相同的可选参数。
3.1.3 在模板中生成“Easypay 代码”
要在模板中生成 Easypay 代码,你可以使用 epayEasypayCode twig 函数。示例
{{ epayEasypayCode({invoice: 1, amount: 100}) }}
请注意,这将在 Easypay 服务器上执行 HTTP 请求以检索代码。请谨慎使用!推荐的方式是在控制器中生成代码并存储以供以后使用。
3.2 控制器
3.2.1 在控制器中生成“Easypay 代码”
以下是在控制器中使用 EpayManager 服务的示例用法,以检索 Easypay 支付代码。
// src/Controller/PaymentController.php namespace App\Controller; use Otobul\EpaybgBundle\Model\EpayPayloadData; use Otobul\EpaybgBundle\Service\EpayManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; class PaymentController extends AbstractController { public function generateEasypayCode(EpayManagerInterface $epayManager) { $invoice = 1; // Generate your unique invoice number $amount = 100; // Total sum for payment $easypayCode = $epayManager->getEasypayCode( new EpayPayloadData($invoice, $amount) ); return $this->render("payment/easypay_code.html.twig", [ 'invoice' => $invoice, 'easypayCode' => $easypayCode, ]); } }
3.2.2 在控制器中为 WEB_LOGIN 表单生成“支付”按钮
以下是在控制器中使用 EpayManager 服务的示例用法,以生成“支付”按钮表单。
// src/Controller/PaymentController.php namespace App\Controller; use Otobul\EpaybgBundle\Model\EpayPayloadData; use Otobul\EpaybgBundle\Service\EpayManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\Routing\RouterInterface; class PaymentController extends AbstractController { public function generateEasypayCode(EpayManagerInterface $epayManager, RouterInterface $router) { $payload = EpayPayloadData::createFromArray([ 'invoice' => 1, 'amount' => 100, ]); $returnUrl = $this->router->generate('your_payment_success_route'); $cancelUrl = $this->router->generate('your_payment_cancel_route'); $form = $epayManager->createWebLoginForm($payload, $returnUrl, $cancelUrl); return $this->render("payment/easypay_code.html.twig", [ 'form' => $form->createView(), ]); } }
要生成 CREDIT_CARD 表单,请使用 createCreditCardForm 函数。
3.3 事件
- NOTIFICATION_RECEIVED:在接收到webhook通知后直接调用。监听器有机会获取原始内容。
- NOTIFICATION_ERROR:如果webhook通知没有有效的校验和或数据,将调用此函数。监听器有机会获取错误信息。
- NOTIFICATION_RESPONSE:在发送webhook通知响应之前直接调用。监听器有机会获取原始响应内容。
- INVOICE_NOTIFICATION_RECEIVED:在接收到发票通知后直接调用。监听器有机会处理发票数据。
- EASYPAY_CODE_CREATED:在接收到Easypay代码后直接调用。监听器有机会获取代码和支付数据。
针对INVOICE_NOTIFICATION_RECEIVED此事件的示例事件订阅器:该事件在接收到发票通知后直接调用。监听器有机会处理发票数据。
// src/EventSubscriber/EpayInvoiceNotificationSubscriber.php namespace App\EventSubscriber; use Doctrine\ORM\EntityManagerInterface; use Otobul\EpaybgBundle\Event\OtobulEpaybgEvents; use Otobul\EpaybgBundle\Event\InvoiceNotificationReceivedEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class EpayInvoiceNotificationSubscriber implements EventSubscriberInterface { public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } public static function getSubscribedEvents() { return [ OtobulEpaybgEvents::INVOICE_NOTIFICATION_RECEIVED => 'epayInvoiceNotification', ]; } public function epayInvoiceNotification(InvoiceNotificationReceivedEvent $event) { // Example logic to find some entity related to this invoice. $order = $this->entityManager->getRepository(Order::class)->find($event->getInvoice()); if (!$order) { // If invoice cannot be found. Sending back NO, so ePay.bg system stops sending notification about the invoice. $event->setResponseStatusNo(); return; } // process order state below if($event->isPaid()) { // process PAID order here }else { // process DENIED or EXPIRED order here } // Sending back OK, so ePay.bg system stops sending notification about the invoice. $event->setResponseStatusOk(); } }
高级事件订阅器的示例。
// src/EventSubscriber/EpayInvoiceNotificationSubscriber.php namespace App\EventSubscriber; use Otobul\EpaybgBundle\Event\EasypayCodeCreatedEvent; use Otobul\EpaybgBundle\Event\NotificationErrorEvent; use Otobul\EpaybgBundle\Event\NotificationReceivedEvent; use Otobul\EpaybgBundle\Event\NotificationResponseEvent; use Otobul\EpaybgBundle\Event\OtobulEpaybgEvents; use Otobul\EpaybgBundle\Event\InvoiceNotificationReceivedEvent; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class EpayInvoiceNotificationSubscriber implements EventSubscriberInterface { private $logger; public function __construct(LoggerInterface $logger) { $this->logger = $logger; } public static function getSubscribedEvents() { return [ OtobulEpaybgEvents::NOTIFICATION_RECEIVED => 'epayNotificationReceived', OtobulEpaybgEvents::NOTIFICATION_ERROR => 'epayNotificationError', OtobulEpaybgEvents::NOTIFICATION_RESPONSE => 'epayNotificationResponse', OtobulEpaybgEvents::INVOICE_NOTIFICATION_RECEIVED => 'epayInvoiceNotification', OtobulEpaybgEvents::EASYPAY_CODE_CREATED => 'epayEasypayCodeCreated', ]; } public function epayNotificationReceived(NotificationReceivedEvent $event) { $this->logger->info('epayNotificationReceived: '. $event->getContent()); } public function epayNotificationError(NotificationErrorEvent $event) { $this->logger->info('epayNotificationError: '. $event->getMessage()); } public function epayNotificationResponse(NotificationResponseEvent $event) { $this->logger->info('epayNotificationResponse: '. $event->getContent()); } public function epayEasypayCodeCreated(EasypayCodeCreatedEvent $event) { $this->logger->info('epayEasypayCodeCreated: code'. $event->getCode()); $this->logger->info('epayEasypayCodeCreated: paymentData:'. print_r($event->getPaymentData()->toArray(), 1)); } public function epayInvoiceNotification(InvoiceNotificationReceivedEvent $event) { $this->logger->info('epayInvoiceNotification: '. $event->getInvoice() .' isPaid: '. $event->isPaid()); // Event object has access to invoice notification details /** * Invoice number * @var int $invoice */ $invoice = $event->getInvoice(); /** * Invoice status, can be PAID|DENIED|EXPIRED * @var string $status */ $status = $event->getStatus(); /** * Payment date * @var \DateTime $payDate */ $payDate = $event->getPayDate(); /** * Transaction number * @var int $stan */ $stan = $event->getStan(); /** * Authorization code * @var string $bcode */ $bcode = $event->getBcode(); /* // Your logic to find some entity related to this invoice. Example: $order = $this->orderRepository->find($invoice); if (!$order) { // If invoice cannot be found. Sending back NO, so ePay.bg system stops sending notification about the invoice. $event->setResponseStatusNo(); return; } process order state below */ if($event->isPaid()) { // process PAID order here }else { // process DENIED or EXPIRED order here } // Sending back OK, so ePay.bg system stops sending notification about the invoice. $event->setResponseStatusOk(); } }