Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

One time charges #257

Open
evertjanMlbrgn opened this issue Jul 25, 2024 · 3 comments
Open

One time charges #257

evertjanMlbrgn opened this issue Jul 25, 2024 · 3 comments

Comments

@evertjanMlbrgn
Copy link

I saw: #122

  • It would be nice to have an option to say that tax is included in the one time charge.
  • It would be nice to be able to get a list of all one time charges (processed, and unprocessed)
  • Would it be possible to add a one time charge to the next subscription invoice instead of creating a new invoice?

Thank you very much.

@Naoray
Copy link
Collaborator

Naoray commented Aug 5, 2024

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
Copy link
Author

@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
Copy link
Collaborator

Naoray commented Aug 5, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants