x-one/przelewy24-bundle

该软件包最新版本(v0.1.2)没有可用的许可信息。

将Przelewy24支付API与Symfony应用程序集成

安装: 176

依赖关系: 0

建议者: 0

安全: 0

类型:symfony-bundle

v0.1.2 2024-02-09 14:33 UTC

This package is not auto-updated.

Last update: 2024-09-18 17:27:36 UTC


README

Przelewy24Bundle将Symfony应用程序与在波兰广泛使用的Przelewy24支付API集成。

安装

查看英文教程,或阅读波兰语翻译

示例用法

安装文档中的Transaction实体对于实际应用来说过于简单。在典型用例中,我们希望创建一个Transaction与其相关联实体的关系。

假设我们想要为Order实体支付。我们必须首先为Transaction实体添加一个关系

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

    // ... Getters and setters
}

创建数据库迁移并运行它。现在可以处理订单的支付过程。我们需要创建一个Transaction实体

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;
    }
}

现在我们可以使用OrderTransactionFactory.createTransaction为给定的订单创建一个Transaction。我们仍然需要将其提交给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;
    }

侧记:使用锁是有益的。它保护与支付过程相关的业务逻辑免受竞争条件的影响。

$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();
}

现在我们可以使用OrderPaymentService.beginPayment为订单创建一个Transaction并将其提交给Przelewy24 API。结果存储在Transaction实体中,并持久化到数据库。

现在我们可以在控制器中使用生成的Transaction

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(),
    ]);
}

用户现在可以成功启动支付过程。成功后,他们将收到支付网关的URL,并可以为其订单支付。

一旦客户成功支付,Przelewy24 API将在我们的应用程序中调用一个webhook。这由软件包处理,并将交易标记为已支付。

让我们处理成功支付后的Order实体。我们必须创建一个消息处理器

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!');
        }
    }
}

现在支付过程已经完全处理。用户可以为其订单支付,成功的支付将被处理。

如果您向Transaction实体引入额外的关系,则在TransactionPaidHandler中可以以任何您希望的方式处理它们。例如,可以引入多个与不同实体关联的1-1关系,并使用单独的业务逻辑处理支付过程。

路线图

  • 内部重构,以改进代码的可读性和可测试性
  • 扩展Transaction实体以支持更多来自Przelewy24 API的字段,例如货币、语言等。
  • 编写单元和集成测试