【EC-CUBE4】PAY.JPの決済プラグインを作る方法

EC-CUBE4でPAY.JPの決済プラグインを作る方法です。

PAY.JPはクレジットカード情報のトークン化が簡単にできますのでとても便利です。

クレジットカード情報のトークン化は以下のようなテンプレートを用意するだけです。

{#
    Shopping/index.twigに追記
    {{ include('@PayJP/credit.twig', ignore_missing=true) }}
#}
{% if Order.Payment.getMethodClass == 'Plugin\\PayJP\\Service\\Method\\CreditCard' %}
    <div class="ec-orderPayment">
        <div class="ec-rectHeading">
            <h2>クレジットカード</h2>
            <div class="ec-input">
                <script
                        type="text/javascript"
                        src="https://checkout.pay.jp/"
                        class="payjp-button"
                        data-key="{{ payjp_public_key }}"
                        data-submit-text="トークンを作成する"
                        data-partial="true"
                        data-token-name="{{ form.payjp_token.vars.full_name }}">
                </script>
            </div>
        </div>
    </div>
{% else %}
    {{ form_widget(form.payjp_token, {type: 'hidden'}) }}
{% endif %}

EC-CUBE4にはPaymentMethodInterfaceが用意されているのでシンプルな決済処理でしたら以下のように簡単に実装できます。

<?php


namespace Plugin\PayJP\Service\Method;


use Eccube\Common\EccubeConfig;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Entity\Order;
use Eccube\Repository\Master\OrderStatusRepository;
use Eccube\Service\Payment\PaymentMethod;
use Eccube\Service\Payment\PaymentMethodInterface;
use Eccube\Service\Payment\PaymentResult;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Service\PurchaseFlow\PurchaseFlow;
use Payjp\Charge;
use Payjp\Payjp;
use Plugin\PayJP\Entity\PaymentStatus;
use Plugin\PayJP\Repository\PaymentStatusRepository;
use Symfony\Component\Form\FormInterface;

class CreditCard implements PaymentMethodInterface
{
    /**
     * @var Order
     */
    protected $Order;

    /**
     * @var FormInterface
     */
    protected $form;

    /**
     * @var OrderStatusRepository
     */
    private $orderStatusRepository;

    /**
     * @var PaymentStatusRepository
     */
    private $paymentStatusRepository;

    /**
     * @var PurchaseFlow
     */
    private $purchaseFlow;

    /**
     * @var EccubeConfig
     */
    private $eccubeConfig;

    public function __construct(
        OrderStatusRepository $orderStatusRepository,
        PaymentStatusRepository $paymentStatusRepository,
        PurchaseFlow $purchaseFlow,
        EccubeConfig $eccubeConfig
    )
    {
        $this->orderStatusRepository = $orderStatusRepository;
        $this->paymentStatusRepository = $paymentStatusRepository;
        $this->purchaseFlow = $purchaseFlow;
        $this->eccubeConfig = $eccubeConfig;
    }

    /**
     * @inheritDoc
     */
    public function verify()
    {
        // TODO: Implement verify() method.
        $result = new PaymentResult();
        $result->setSuccess(true);

        return $result;
    }

    /**
     * @inheritDoc
     */
    public function checkout()
    {
        // TODO: Implement checkout() method.
        $token = $this->Order->getPayjpToken();

        Payjp::setApiKey($this->eccubeConfig['payjp_secret_key']);

        $charge = Charge::create([
            'card' => $token,
            'currency' => $this->eccubeConfig['currency'],
            'amount' => $this->Order->getPaymentTotal(),
        ]);

        if (!isset($charge["error"])) {
            // 受注ステータスを新規受付へ変更
            $OrderStatus = $this->orderStatusRepository->find(OrderStatus::NEW);
            $this->Order->setOrderStatus($OrderStatus);

            // 決済ステータスを実売上へ変更
            $PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::ACTUAL_SALES);
            $this->Order->setPayJpPaymentStatus($PaymentStatus);

            // PAY.JPの課金IDを保存
            $this->Order->setPayjpChargeId($charge['id']);

            // purchaseFlow::commitを呼び出し、購入処理をさせる
            $this->purchaseFlow->commit($this->Order, new PurchaseContext());

            $result = new PaymentResult();
            $result->setSuccess(true);
        }else{
            // 受注ステータスを購入処理中へ変更
            $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
            $this->Order->setOrderStatus($OrderStatus);

            // 決済ステータスを未決済へ変更
            $PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::OUTSTANDING);
            $this->Order->setPayJpPaymentStatus($PaymentStatus);

            $this->purchaseFlow->rollback($this->Order, new PurchaseContext());

            $result = new PaymentResult();
            $result->setSuccess(false);
            $result->setErrors([$charge['error']['message']]);
        }

        return $result;
    }

    /**
     * @inheritDoc
     */
    public function apply()
    {
        // TODO: Implement apply() method.
        // 受注ステーテスを決済処理中へ変更
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PENDING);
        $this->Order->setOrderStatus($OrderStatus);

        // 決済ステータスを未決済へ変更
        $PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::OUTSTANDING);
        $this->Order->setPayJpPaymentStatus($PaymentStatus);

        $this->purchaseFlow->prepare($this->Order, new PurchaseContext());
    }

    /**
     * @inheritDoc
     */
    public function setFormType(FormInterface $form)
    {
        // TODO: Implement setFormType() method.
        $this->form = $form;
    }

    /**
     * @inheritDoc
     */
    public function setOrder(Order $Order)
    {
        // TODO: Implement setOrder() method.
        $this->Order = $Order;
    }
}

詳しい内容を書くと長くなってしまうので、具体的な実装方法はGitHubに上げましたのでそちらをご確認下さい。

1件のコメント

  1. 通常の購入は出来ます。ありがとうございます。
    定期購入のプラン作成時に金額の箇所にて
    DateTime::__construct(): Failed to parse time string (1662819237) at position 8 (3): Unexpected character
    の表示が出ます。
    \payjp4\Resource\template\admin\Plan\create の処理でプラン登録が出来ません。

    お忙しいところ恐れ入りますがご確認いただけますと幸いです。

お気軽にコメントをどうぞ

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください