laravel / cashier-mollie

MIT License
375 stars 63 forks source link

Why is my preprocessor for variable prices not working correctly? #254

Open zqstack opened 4 years ago

zqstack commented 4 years ago

Dear Developer,

Do you know why my preprocessor for variable prices is not working correctly?

It seems to work for swapping plans, but for new subscriptions, it keeps asking for the original price. Am I missing something?

Here is my code:

cashier_plans.php:

'my_plan' => [
        'amount' => [
            'value' => '3.00',
            'currency' => 'EUR',
        ],

        'interval' => '1 month',
        'description' => 'My plan payment',

        'order_item_preprocessors' => [
            ProcessCoupons::class,

            MyPreprocessor::class,

            PersistOrderItems::class,
        ],
    ],

MyPreprocessor.php:

<?php

namespace App\PaymentPreprocessors;

use Laravel\Cashier\Order\BaseOrderItemPreprocessor;
use Laravel\Cashier\Order\OrderItem;
use Laravel\Cashier\Order\OrderItemCollection;

use Laravel\Cashier\Order\Contracts\PreprocessesOrderItems;

class MyPreprocessor extends BaseOrderItemPreprocessor implements PreprocessesOrderItems
{
    /**
     * @param \Laravel\Cashier\Order\OrderItemCollection $items
     * @return \Laravel\Cashier\Order\OrderItemCollection
     */
    public function handle( OrderItemCollection $items )
    {
        \Log::info( 'HANDLE' );

        $result = new OrderItemCollection;

        $items->each( function( OrderItem $item ) use ( &$result )
        {
            if( $item->orderableIsSet() )
            {
                //$coupons = $this->getActiveCoupons( $item->orderable_type, $item->orderable_id );
                //$result = $result->concat( $coupons->applyTo( $item ) );

                \Log::info( 'item' );
                \Log::info( $item );

                if( !is_null( $item->unit_price ) )
                {
                    \Log::info( 'item->unit_price' );
                    \Log::info( $item->unit_price );

                    $item->unit_price = 500;
                    $item->save();

                    \Log::info( 'NEW item->unit_price' );
                    \Log::info( $item->unit_price );
                }

                $result->push( $item );
            }
            else
            {
                $result->push( $item );
            }
        });

        return $result;
    }

    public static function preprocessOrderItem( OrderItem $item )
    {
        \Log::info( 'PREPROCESSORDERITEM' );

        \Log::info( 'item' );
        \Log::info( $item );

        if( !is_null( $item->unit_price ) )
        {
            \Log::info( 'item->unit_price' );
            \Log::info( $item->unit_price );

            $item->unit_price = 500;
            $item->save();

            \Log::info( 'NEW item->unit_price' );
            \Log::info( $item->unit_price );
        }

        $result->push( $item );
    }
}

Thank you for your help!

zqstack

lexdewilligen commented 4 years ago

First of all, it would help a lot if you remove all of your logs from the code. Second, I think you don't need to implement PreprocessesOrderItems. That's up to cashier. You just need to extend BaseOrderItemPreprocessor and write the logic for handle()to modify your OrderItems. And finally, to solve your isssue. Preprocessors are not invoked when creating a new subscription, as you can see in the create() method of a MandatedSubscriptionBuilder:

    /**
     * Create a new Cashier subscription.
     *
     * @return Subscription
     * \Laravel\Cashier\Exceptions\CouponException
     * @throws \Laravel\Cashier\Exceptions\InvalidMandateException
     */
    public function create()
    {
        $this->owner->guardMollieMandate();
        $now = now();

        return DB::transaction(function () use ($now) {
            $subscription = $this->makeSubscription($now);
            $subscription->save();

            if($this->coupon) {
                if($this->validateCoupon) {
                    $this->coupon->validateFor($subscription);

                    if($this->handleCoupon) {
                        $this->coupon->redeemFor($subscription);
                    }
                }
            }

            $subscription->scheduleNewOrderItemAt($this->nextPaymentAt);
            $subscription->save();

            $this->owner->cancelGenericTrial();

            return $subscription;
        });
    }

The preprocessors are only invoked when processing a subscription right before a mollie payment is created. You could therefore start a subscription with a price of your choice and always change the unit_price whenever such a subscription would be billed using the preprocessor you've created.

temo87 commented 4 years ago

I have a similar situation and thanks @lexdewilligen you for the tips, I have the PaymentPreprocessors working now.

One question remains. How can I change the price of the firstPayment? I have a custom plan in my cashier_plans.php but the price is dynamic and is per client different. So i would like to change the amount of the firstPayment per user.

I've found some more information in this thread https://github.com/laravel/cashier-mollie/issues/177 But i cannot find out how to use the FirstPaymentBuilder manually in a controller.