mollie / laravel-cashier-mollie

Official Mollie integration for Laravel Cashier
https://www.cashiermollie.com/
MIT License
135 stars 44 forks source link

One time charges #257

Open evertjanMlbrgn opened 1 month ago

evertjanMlbrgn commented 1 month ago

I saw: https://github.com/mollie/laravel-cashier-mollie/issues/122

Thank you very much.

Naoray commented 1 month ago

Hi @evertjanMlbrgn,

It would be nice to have an option to say that tax is included in the one time charge.

That is similar to the feature requested in #214. I will note it down. However, atm it's not possible.

It would be nice to be able to get a list of all one time charges (processed, and unprocessed)

I'll note that down on the feature wishlist. As a workaround you can query all OrderItems that have no orderable relationship OrderItem::whereNull(['orderable_type', 'orderable_id'])->get().

Would it be possible to add a one time charge to the next subscription invoice instead of creating a new invoice?

If you want the user to pay at the moment the one time charge is due, then you can't delay the invoice creation. Therefore it's not possible to just add the one time charge to another invoice.

Depending on your use case you could achieve such a functionality by not using the Charge functionality of this package, but rather store the charge that will be added to the order of the Subscription separately. Then you could add a Preprocessor which checks if there are any pending charges and if so adds them to the order.

// create ChargeItem and mark it as process_at the end of the current subscription period
$item = new ChargeItem([
    'description' => 'Monthly subscription',
    'quantity' => 1,
    'unit_price' => new Money(1000, new Currency('USD')),
    'process_at' => $user->subscription('default')->ends_at,
]);

$item->ownable()->associate($user);
$item->save();

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

class AddPendingChargesToOrder extends BaseOrderItemPreprocessor
{
    /**
     * @param  \Laravel\Cashier\Order\OrderItemCollection  $items
     * @return \Laravel\Cashier\Order\OrderItemCollection
     */
    public function handle(OrderItemCollection $items)
    {
        // You can add, remove or modify order items here.
        // Each order item relates to 1 line on the invoice.

        $owner = $items->owners()->first();

        if ($owner->hasPendingCharges()) {
            $chargeOrderItems = $owner
                ->pendingCharges
                ->map->toOrderItem();

            $items = $items->merge($chargeOrderItems);
        }

        return $items;
    }
}

class ChargeItem extends Model
{
    public function ownable()
    {
        return $this->morphTo();
    }

    public function toOrderItem(array $overrides = []): OrderItem
    {
        return Cashier::$orderItemModel::make(array_merge([
            'owner_type' => $this->ownable_type,
            'owner_id' => $this->ownable_id,
            'description' => $this->description,
            'quantity' => $this->quantity,
            'currency' => $this->unitPrice->getCurrency()->getCode(),
            'unit_price' => $this->unitPrice->getAmount(),
            'tax_percentage' => $this->taxPercentage,
            'process_at' => $this->process_at,
        ], $overrides));
    }
}

class User extends Model
{
    public function pendingCharges(): HasMany
    {
        return $this->morphMany(ChargeItem::class, 'ownable')
            ->where('process_at', '>', Carbon::now());
    }
}

If you have further questions, please let me know!

evertjanMlbrgn commented 1 month ago

@Naoray Thank you very much i'm able to get the one time charges and i will try to implement the code you provided :-)

Naoray commented 1 month ago

You are welcome @evertjanMlbrgn!

Don't forget to add your custom preprocessor class to the cashier_plans.defaults.order_item_preprocessors and make sure it's before the PersistOrderItems.