c975l/payment-bundle

定义了使用 Stripe 进行支付的表单、交易、数据库存储等。


README

PaymentBundle 执行以下操作

  • 定义请求支付的表单,
  • 将交易存储在具有唯一订单 ID 的数据库表中,
  • 允许添加按钮/链接以进行预定义支付,
  • 允许定义支付的自由金额表单(捐赠、咨询等),
  • 通过 c975LEmailBundle 向用户发送交易电子邮件,作为 c975LEmailBundle 提供将电子邮件保存到数据库的可能性,此 Bundle 有一个选项可以通过此 Bundle 不这样做,
  • 向网站发送包含上述相同信息以及费用和预计收入的电子邮件,
  • 创建提示以通知用户,
  • 在交易后显示有关支付的详细信息。

此 Bundle 依赖于使用 Stripe 和其 PHP 库因此您必须有一个 Stripe 账户。 建议使用 SSL 证书来保证用户的信心。

PaymentBundle 专用网页.

PaymentBundle API 文档.

包安装

步骤 1:下载包

3.x 版本与 Symfony 4.x 兼容。使用 v2.x 用于 Symfony 3.x 使用 Composer 安装库

    composer require c975L/payment-bundle

步骤 2:配置包

检查依赖配置

c975LPaymentBundle 使用 c975L/ConfigBundle 来管理配置参数。使用路由 "/payment/config" 并具有适当的用户角色来修改它们。

步骤 3:启用路由

然后,通过将它们添加到项目的 /config/routes.yaml 文件中来启用路由

c975_l_payment:
    resource: "@c975LPaymentBundle/Controller/"
    type: annotation
    prefix: /
    #Multilingual website use the following
    #prefix: /{_locale}
    #defaults:   { _locale: '%locale%' }
    #requirements:
    #    _locale: en|fr|es

步骤 4:创建 MySql 表

您可以使用 php bin/console make:migration 创建迁移文件,如 Symfony 的 Doctrine 文档 中所述,或者使用 /Resources/sql/payment.sql 创建表 stripe_paymentDROP TABLE 被注释掉,以避免错误删除。

步骤 5:将图像复制到 web 文件夹

运行以下命令来安装图像

php bin/console assets:install --symlink

它将复制 Resources/public/images/ 文件夹的内容到您的 web 文件夹。它们用于在支付表单上显示。

您还可以查看 Stripe 的官方徽章

如何使用

过程如下

  • 用户选择一个产品,
  • 用户点击付款,
  • 在数据库中创建支付,
  • 用户被重定向到付款表单,
  • 用户付款,
  • 用户被重定向到返回路由,
  • 执行操作以交付产品(如果支付成功或发送错误),
  • 用户被重定向到最终确认或交付产品页面。

为了实现这一点,您需要定义2个控制器路由和2个服务方法(+接口)(虽然您可以在控制器中完成所有这些,但最佳实践建议保持控制器方法简洁)。

以下是一些示例文件

//YourProductServiceInterface file
namespace App\Service;

use c975L\PaymentBundle\Entity\Payment;

interface YourProductServiceInterface
{
    public function validate(Payment $payment);
}
//Your ProductService file
namespace App\Service;

use Doctrine\ORM\EntityManagerInterface;
use App\Service\YourProductServiceInterface;

class YourProductService implements YourProductServiceInterface
{
    private $em;

    public function __construct(EntityManagerInterface $em)
    {
        $this->em = $em;
    }

    public function validate(Payment $payment)
    {
        /**
         * For example if `$payment->getAction()` contains the result of "json_encode(array('addCredits' => 10));"
         */
        $action = (array) json_decode($payment->getAction());
        if (array_key_exists('addCredits', $action)) {
            //Gets the user
            $user = $this->em->getRepository('c975LUserBundle:User')
                ->findOneById($payment->getUserId());

            //Adds credits to user
            $user->setCredits($user->getCredits() + $action['addCredits']);
            $this->em->persist($user);

            //Set payment as finished
            $payment->setFinished(true);
            $this->em->persist($payment);
            $this->em->flush();

            return true;
        }

        return false;
    }
}
//YourPaymentServiceInterface file
namespace App\Service;

interface YourPaymentServiceInterface
{
    public function payment($yourNeededData);
}
//Your PaymentService file
namespace App\Service;

use App\Service\YourPaymentServiceInterface;
use c975L\PaymentBundle\Service\PaymentServiceInterface;

class YourPaymentService implements YourPaymentServiceInterface
{
    public function payment(PaymentServiceInterface $paymentService, $yourNeededData)
    {
        /**
         * Except amount and currency all the fields are nullable
         * You may use the data define in `$yourNeededData`
         */
        $paymentData = array(
            'amount' => YOUR_AMOUNT, //Must be an integer in cents
            'currency' => YOUR_CURRENCY, //Coded on 3 letters or use "$paymentService->getParameter('c975LPayment.defaultCurrency')" to get your default currency
            'action' => YOUR_ACTION, //Store the action to achieve after the payment. Mainly used by `returnRoute`. As a string, you can store plain text, json, etc.
            'description' => YOUR_DESCRIPTION,
            'userId' => USER_ID,
            'userIp' => $request->getClientIp(),
            'live' => false|true, //If your product is live or not, different from live config value
            'returnRoute' => 'THE_NAME_OF_YOUR_RETURN_ROUTE', //This Route is defined in your Controller
            'vat' => 'YOUR_VAT_RATE', //Rate value without % i.e. 5.5 for 5.5%, or 20 for 20%
            );
        $paymentService->create($paymentData);
    }
}
//Your Controller file
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use c975L\PaymentBundle\Entity\Payment;
use c975L\PaymentBundle\Service\PaymentServiceInterface;
use App\Service\YourPaymentServiceInterface;

class PaymentController extends AbstractController
{
    /**
     * Route used to proceed to payment
     * @return Response
     *
     * @Route("proceed-to-payment",
     *     name="proceed_to_payment")
     */
    public function proceedToPayment(YourPaymentServiceInterface $yourPaymentService)
    {
        //Creates the Payment
        $yourPaymentService->payment();

        //Redirects to the payment form
        return $this->redirectToRoute('payment_form');
    }

    /**
     * Return Route used after payment
     * @return Redirect
     * @throws NotFoundHttpException
     *
     * @Route("/payment-done/{orderId}",
     *    name="payment_done",
     *    methods={"HEAD", "GET"})
     */
    public function paymentDone(YourProductServiceInterface $yourProductService, PaymentServiceInterface $paymentService, Payment $payment)
    {
        //Validates the Payment
        $validation = $yourProductService->validate($payment);

        //Redirects or renders
        if ($validation) {
            return $this->redirectToRoute('YOUR_ROUTE');
        }

         //Payment has been done but product was not validated
        $paymentService->error($payment);

        return $this->redirectToRoute('payment_display', array('orderId' => $payment->getOrderId()));
    }
}

在投入生产之前,请使用测试卡进行测试。

商家数据

您需要在/templates/bundles/c975LPaymentBundle/fragments/merchantData.html.twig中覆盖模板fragments/merchantData.html.twig,并在此处列出所有您的官方数据,例如地址、增值税号等。

此模板将在用户支付后发送给用户的电子邮件中包含。

提及支付系统

您可以通过简单地包含以下代码的html片段来提及所使用的支付系统(例如在页脚中):{% include '@c975LPayment/fragments/paymentSystem.html.twig' %}。这将包括Stripe标志和接受的卡。

使用支付按钮/链接

您可以通过使用以下代码的Twig扩展在任何您想要的地方添加任何支付按钮/链接

{{ payment_button('YOUR_TEXT_TO_DISPLAY', AMOUNT, 'CURRENCY', 'YOUR_OPTIONAL_STYLES') }}
{{ payment_link('YOUR_TEXT_TO_DISPLAY', AMOUNT, 'CURRENCY') }}

AMOUNT是实际金额(即12.92),不是以分为单位的金额。

或者您可以使用它为空,这将引导用户填写表单以进行支付

{{ payment_button() }}
{{ payment_link() }}

如果这个项目帮助您减少了开发时间,您可以通过顶部的“赞助”按钮赞助我:)