x-one / przelewy24-bundle
将Przelewy24支付API与Symfony应用程序集成
Requires
- php: >=8.2
- doctrine/persistence: ^3
- mnastalski/przelewy24-php: ^1.1
- symfony/framework-bundle: ^6.2
- symfony/routing: ^6.0
- symfony/serializer: ^6.0
- symfony/translation: ^6.0
- symfony/uid: ^6.2
- symfony/validator: ^6.2
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的字段,例如货币、语言等。
- 编写单元和集成测试