I added a new processor allowing to calculate the amount of the gift card.
services:
app.order_processing.gift_card_processor:
class: App\OrderProcessing\GiftCardProcessor
arguments:
- '@sylius.factory.adjustment'
- '@translator'
tags:
- { name: sylius.order_processor, priority: 5 }
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
<?php
declare(strict_types=1);
namespace App\OrderProcessing;
use App\Entity\Order\Adjustment;
use App\Entity\Order\Order;
use Sylius\Component\Order\Model\OrderInterface as BaseOrderInterface;
use Sylius\Component\Order\Processor\OrderProcessorInterface;
use Sylius\Component\Resource\Factory\FactoryInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Webmozart\Assert\Assert;
final class GiftCardProcessor implements OrderProcessorInterface
{
private FactoryInterface $adjustmentFactory;
private TranslatorInterface $translator;
public function __construct(
FactoryInterface $adjustmentFactory,
TranslatorInterface $translator
) {
$this->adjustmentFactory = $adjustmentFactory;
$this->translator = $translator;
}
public function process(BaseOrderInterface $order): void
{
/** @var Order $order */
Assert::isInstanceOf($order, Order::class);
// Remove all gift card adjustments, we recalculate everything from scratch.
$order->removeAdjustments(Adjustment::ORDER_GIFT_CARD_ADJUSTMENT);
foreach ($order->getGiftCardOrders() as $giftCardOrder) {
$giftCard = $giftCardOrder->getGiftCard();
$giftCardRemainingAmount = (int) $giftCard->getRemainingAmount() * 100;
$amount = $giftCardRemainingAmount > $order->getTotal() ? $order->getTotal() : $giftCardRemainingAmount;
/** @var Adjustment $adjustment */
$adjustment = $this->adjustmentFactory->createNew();
$adjustment->setType(Adjustment::ORDER_GIFT_CARD_ADJUSTMENT);
$adjustment->setAmount(-$amount);
$adjustment->setLabel($this->translator->trans('gift_card.ui.gift_card'));
$giftCard->addAdjustment($adjustment);
$giftCardOrder->setAmount($amount / 100);
$order->addAdjustment($adjustment);
}
}
}
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
The total order amount displayed in the cart and inserted in the database is correct (screenshot 1 and 2).
The bug occurs during payment on stripe, the amount displayed corresponds to the initial amount which does not support the reduction of the gift card (screenshot 3)
CodePudding user response:
If you are using this Sylius plugin : flux-se/sylius-payum-stripe-plugin
you have to create individual coupons representing your gift cards decorating : https://github.com/FLUX-SE/SyliusPayumStripePlugin/blob/master/src/Provider/DetailsProvider.php the new array member to create is the discounts
like the Stripe doc is defining it : https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-discounts
The plugin is only taking care of default Sylius adjustments linked to an OrderItem
or an OrderItemUnit
. If the adjustment is linked on the Order
then it won't be taken into account because Stripe is only making a sum of all line_item
as total. Stripe is not allowing negative amount for a line item, that's why coupons are the only way to reduce the total amount of the payment.
Here is the required payum extension handling the creation of coupons if you label the coupon ids with this format sprintf('GIFT_CARD_%s', $giftCard->getCode())
:
<?php
declare(strict_types=1);
namespace App\GiftCard\Payum\Extension;
use FluxSE\PayumStripe\Request\Api\Resource\CreateCoupon;
use FluxSE\PayumStripe\Request\Api\Resource\RetrieveCoupon;
use FluxSE\SyliusPayumStripePlugin\Action\ConvertPaymentAction;
use Payum\Core\Extension\Context;
use Payum\Core\Extension\ExtensionInterface;
use Payum\Core\Request\Convert;
use Stripe\Exception\ApiErrorException;
use Sylius\Component\Core\Model\PaymentInterface;
final class CheckCouponsExtension implements ExtensionInterface
{
public function onPreExecute(Context $context)
{
}
public function onExecute(Context $context)
{
}
public function onPostExecute(Context $context)
{
if ($context->getException()) {
return;
}
if (false === $context->getAction() instanceof ConvertPaymentAction) {
return;
}
/** @var mixed|Convert $request */
$request = $context->getRequest();
if (false === $request instanceof Convert) {
return;
}
/** @var mixed|PaymentInterface $payment */
$payment = $request->getSource();
if (false === $payment instanceof PaymentInterface) {
return;
}
$order = $payment->getOrder();
if (null === $order) {
return;
}
$gateway = $context->getGateway();
foreach ($order->getGiftCardOrders() as $giftCardOrder) {
$giftCard = $giftCardOrder->getGiftCard();
$couponId = sprintf('GIFT_CARD_%s', $giftCard->getCode());
$retrieveCouponRequest = new RetrieveCoupon($couponId);
try {
$gateway->execute($retrieveCouponRequest);
} catch (ApiErrorException $e) {
$createCouponRequest = new CreateCoupon([
'id' => $couponId,
"amount_off" => $giftCard->getAmount()/100,
"currency" => $order->getCurrencyCode(),
"metadata" => [
'SYLIUS_GIFTCARD_ID' => $giftCard->getId(),
'SYLIUS_GIFTCARD_CODE' => $giftCard->getCode(),
],
"name" => sprintf("Gift card #%d", $giftCard->getId()),
]);
$gateway->execute($createCouponRequest);
}
}
}
}
And here is the service declaration :
services:
App\GiftCard\Payum\Extension\CheckCouponsExtension:
public: true
tags:
- name: payum.extension
alias: app.extension.check_coupons
factory: stripe_checkout_session