x-one/przelewy24-bundle 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

x-one/przelewy24-bundle

最新稳定版本:v0.1.2

Composer 安装命令:

composer require x-one/przelewy24-bundle

包简介

Integrates Przelewy24 payment API with Symfony applications

README 文档

README

Przelewy24Bundle integrates Symfony applications with Przelewy24 payment API, widely used in Poland.

Installation

See the English tutorial, or read the Polish translation.

Example usage

The Transaction entity from Installation docs is too simple for real world use. In a typical use-case, we will want to create a relation between a Transaction and what it's associated with.

Let's assume that we want to pay for Order entities. We must start with adding a relation to the Transaction entity:

#[ORM\Entity]
class Transaction extends Przelewy24Transaction
{
    #[ORM\OneToOne(inversedBy: 'transaction')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Order $order = null;

    // ... Getters and setters
}

Create a database migration and run it. Now it's possible to handle the payment process for an Order. We need to create a Transaction entity:

class OrderTransactionFactory
{
    public function __construct(
        // You can either set the redirect URL in services.yaml,
        //  when your frontend is decoupled (SPA) from the backend

        // Or you can instead inject UrlGenerator
        //  and point to an internal route
        private string $afterPaymentRedirectUrl,
    ) {}

    /** Creates a transaction for an order. */
    public function createTransaction(Order $order): Transaction
    {
        // Let's assume Order stores prices in PLN in a decimal format: 12.34 PLN
        // We need to convert PLN to 1/100 of PLN (grosz),
        //  as it's the lowest common denominator
        $transactionAmount = (int) bcmul($order->getTotalGrossPrice(), '100', 2);

        $transaction = new Transaction();

        $transaction
            ->setOrder($order)
            ->setEmail($order->getEmail())
            ->setAmount($transactionAmount)
            ->setUrlReturn($this->afterPaymentRedirectUrl)
            ->setDescription('Order ' . $order->getId())
        ;

        return $transaction;
    }
}

We can now use OrderTransactionFactory.createTransaction to create a Transaction for a given Order. We still need to submit it to the Przelewy24 API:

use XOne\Bundle\Przelewy24Bundle\Service\Przelewy24Client;
use Doctrine\ORM\EntityManagerInterface;

class OrderPaymentService
{
    public function __construct(
        private EntityManagerInterface $entityManager,
        private OrderTransactionFactory $transactionFactory,
        private Przelewy24Client $przelewy24Client,
    ) {
    }

    public function beginPayment(Order $order): Transaction
    {
        // <Business logic - validate the entity, ...>

        // Create a Transaction
        $transaction = $this->transactionFactory->createTransaction($divorce);

        // Save Transaction in the database prior to calling Przelewy24 API
        // This ensures that no race conditions can occur
        $this->entityManager->persist($transaction);
        $this->entityManager->flush();

        // Call Przelewy24 API - generates a payment link for the user
        $this->przelewy24Client->submitTransaction($transaction);
        $this->entityManager->flush();

        return $transaction;
    }

Side-note: It's beneficial to use locks. It protects business logic related to the payment process from race conditions.

$store = new FlockStore();
$factory = new LockFactory($store);
$lock = $factory->createLock('order-payment-'.$order->getId(), ttl: 5);

if (!$lock->acquire()) {
    throw new \Error('The Order payment process is locked!');
}

try {
    return $this->orderPaymentService->beginPayment($order);
} finally {
    $lock->release();
}

We can now use OrderPaymentService.beginPayment to create an Transaction for an Order and submit it to Przelewy24 API. The results are stored in the Transaction entity, which is persisted in the database.

We can now use the resulting Transaction in a controller:

public function __invoke(Order $order): JsonResponse
{
    $transaction = $this->orderPaymentService->beginPayment($order);

    // Process $transaction->status to ensure it's successful

    return new JsonResponse([
        'gatewayUrl' => $transaction->getUrlGateway(),
    ]);
}

The user now can successfully start the payment process. On success, they receive a payment gateway URL, and they can pay for their Order.

Once a customer successfully pays, the Przelewy24 API calls a webhook in our application. This is handled by the bundle and the transaction is marked as paid.

Let's process the Order entity on successful payment. We must create a Message Handler:

use App\Entity\Transaction;
use XOne\Bundle\Przelewy24Bundle\Messenger\Przelewy24TransactionPaid;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;

#[AsMessageHandler]
class TransactionPaidHandler
{
    public function __construct(
        private EntityManagerInterface $entityManager,
    ) {
    }

    public function __invoke(Przelewy24TransactionPaid $message): void
    {
        // Read the Transaction entity
        $repository = $this->entityManager->getRepository($message->transactionFQCN);
        /** @var Transaction $transaction */
        $transaction = $repository->find($message->transactionId);

        $order = $transaction->getOrder();
        if (!is_null($order)) {
            // Do something with the Order
            // Send emails, mark it for realization, etc...
        } else {
            throw new \Error('Payment for a Transaction was processed, but there were no relations!');
        }
    }
}

Now the payment process is fully handled. An user can pay for their Order, and a successful payment will be processed.

If you introduce additional relations to the Transaction entity, it is be possible to handle them in any way you want in the TransactionPaidHandler. For example, it's possible to introduce multiple 1-1 relations with different Entities, and handle the payment process with separate business logic.

Roadmap

  • Internal refactor to improve code readability and testability
  • Extend the Transaction entity to support more fields from Przelewy24 API, such as currency, language, etc.
  • Write unit and integration tests

统计信息

  • 总下载量: 223
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: Unknown
  • 更新时间: 2023-10-31