1
votes

I used this code to cancel stripe subscription with prorate

    $user = User::find(Auth::id());
    $subscription = \Stripe\Subscription::retrieve(
        $user->subscription('main')->stripe_id
    );

    $subscription->delete([
        'prorate' => true
    ]);

in the stripe dashboard subscription cancelled and it is created upcoming invoice with prorate amount. I want to refund prorate amount to customer's card. I tried to get prorate amount using below code.

    $subscription = \Stripe\Subscription::retrieve($user->subscription('main')->stripe_id);
    $subscription_item = array(
        "id" => $subscription->items->data[0]->id,
        "plan" => $subscription->items->data[0]->plan->id,
        "quantity" => 0,
    );
    $proration_date = time();
    $upcoming_prorated_invoice = \Stripe\Invoice::upcoming([
        "customer" => $subscription->customer,
        "subscription" => $subscription->id,
        "subscription_items" => array($subscription_item),
        "subscription_proration_date" => $proration_date, // optional
    ]);

    dd($upcoming_prorated_invoice);

I got below error message

Stripe\Exception\InvalidRequestException

You cannot preview the upcoming invoice for a canceled subscription.

UPDATE

    $subscription->delete([
        'prorate' => true,
        'invoice_now' => true
    ]);

    //$latest_invoice = \Stripe\Invoice::retrieve($subscription->latest_invoice);
    //$latest_charge_id = $latest_invoice->charge;

    $invoice = \Stripe\Invoice::retrieve([ 'id' => $subscription->latest_invoice, 'expand' => ['payment_intent.charges'], ]);
    //$charge = $invoice->payment_intent->charges->data[0];
    dd($invoice);

payment_intent return as null

1
The approach here is not to use the upcoming invoice items API. There's a few ways to approach this. I would explore prorate: true and invoice_now: true. Then you could finalize the Invoice and your customer's balance will be credited. Then you can look at the proration line items on the invoice and try to Refund this amount by linking it to the original charge on the invoice (you will need to expand the charge). Hope that helps! - v3nkman
@v3nkman I tried what you said. I could not get charge id from last invoice. it is return as null. - Shankar S Bavan
If the last Invoice was successfully paid, then it should have a PaymentIntent [1]. You can expand the PaymentIntent and Charge [2] with the following code with the Invoice ID. ` $invoice = \Stripe\Invoice::retrieve([ 'id' => 'in_xxx', 'expand' => ['payment_intent.charges'], ]); $charge = $invoice->payment_intent->charges->data[0]; ` Hope that helps! [1] stripe.com/docs/api/invoices/… [2] stripe.com/docs/api/payment_intents/… Can you compare that with your implementation? - v3nkman
payment_intent: null - Shankar S Bavan
If the amount due on the last Invoice was 0 then there will be no PaymentIntent to drive payment as no charge will be made. I believe that is a different use case to your original question. If you need to refund money based on a proration (unused time on a Subscription) then the last Invoice should have a PaymentIntent. - v3nkman

1 Answers

0
votes

Finally I got solution. ref

    $subscription = \Stripe\Subscription::retrieve($user->subscription('main')->stripe_id);

    $proration_date = time();

    $refund = $this->prorate($subscription, $proration_date);

    if ($refund) {
        $latest_invoice = \Stripe\Invoice::retrieve($subscription->latest_invoice);

        if ($latest_invoice) {
            \Stripe\Refund::create([
                'charge' => $latest_invoice->charge,
                'amount' => $refund,
            ]);
        }
    }



public function prorate(\Stripe\Subscription $subscription, $proration_time)
{
    $period_start = $subscription->current_period_start;
    $period_end = $subscription->current_period_end;
    $amount = $subscription->plan->amount;

    $period_length = $period_end - $period_start;

    $elapsed_since_start = $proration_time - $period_start;

    $refund = $amount - floor(($elapsed_since_start / $period_length) * $amount);

    return $refund > 0 ? (int) $refund : 0;
}