#ruul_code

1 messages · Page 1 of 1 (latest)

native lionBOT
orchid anchorBOT
#

Below are links to other discussions we've had with you in the past week in case you want to review that information. If your question is related to one of these previous discussions, please provide a comprehensive summary of the current state and what you need help with now. We help many users simultaneously, so a summary allows us to resolve your issue as soon as possible.

native lionBOT
#

👋 Welcome to your new thread!

⏲️ We'll be here soon! We typically respond in a few minutes, but in some cases we might need a bit more time (e.g., server's busy, you've got a complex question, etc.).

⏱️ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can start a new thread if you have another question.

🔗 This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1257711401602912297

📝 Have more to share? Add details, code, screenshots, videos, etc. below.

reef cove
#
    const testClock = await createTestClock();

    const customer = await createTestCustomer(testClock);

    const paymentMethod = await paymentService.createPaymentMethod({
      number: "4242424242424242",
      exp_month: 12,
      exp_year: new Date().getFullYear() + 1,
      cvc: "314",
    });

    await paymentService.attachPaymentMethod(paymentMethod.id, customer.id);

    const productName = "test product";

    const product = await paymentService.createProduct(productName);

    const amount = 100;
    const currency = "usd";

    const price = await paymentService.createPrice(
      product.id,
      amount,
      currency,
      {
        interval: "month",
      }
    );

    const quantity = 5;

    const subscription = await paymentService.purchaseSubscriptionWithPaymentMethodPresent({
      customer: customer.id,
      price: price.id,
      paymentMethodId: paymentMethod.id,
      quantity
    });

    expect(subscription).toBeTruthy();
    expect(subscription.customer).toBe(customer.id);
    expect(subscription.items.data[0].price.id).toBe(price.id);
    expect(subscription.items.data[0].quantity).toBe(quantity);
    expect(subscription.status).toBe("active");

    const productName2 = "test product 2";

    const product2 = await paymentService.createProduct(productName2);

    const amount2 = 450;

    const price2 = await paymentService.createPrice(
      product2.id,
      amount2,
      currency,
      {
        interval: "month",
      }
    );

    const coupon = await paymentService.stripe.coupons.create({
      duration: 'once',
      percent_off: 50,
    });

    let downgradeSubscription = await paymentService.downgradeSubscription({
      subscription,
      newPrice: price2.id,
      quantity,
      coupon: coupon.id
    });

    expect(downgradeSubscription).toBeTruthy();
    expect(downgradeSubscription.customer).toBe(customer.id);
    expect(downgradeSubscription.items.data[0].price.id).toBe(price2.id);

    const frozenTime = getClockFrozenTime(32);

    let updatedClock = await paymentService.advanceTestClock(testClock.id, frozenTime);

    while (updatedClock.status === "advancing") {
      await new Promise((resolve) => setTimeout(resolve, 2500));
      updatedClock = await paymentService.retrieveTestClock(testClock.id);
    }

    expect(updatedClock.status).toBe("ready");

    downgradeSubscription = await paymentService.retrieveSubscription(subscription.id);

    expect(downgradeSubscription).toBeTruthy();
    expect(downgradeSubscription.customer).toBe(customer.id);
    expect(downgradeSubscription.items.data[0].price.id).toBe(price2.id);
    expect(downgradeSubscription.items.data[0].quantity).toBe(quantity);
    expect(downgradeSubscription.status).toBe("active");

    const latestInvoiceId = downgradeSubscription.latest_invoice as string;
    console.log(`Latest invoice ID: ${latestInvoiceId}`);

    const latestInvoice = await paymentService.stripe.invoices.retrieve(latestInvoiceId);

    console.log(`Latest invoice details:`, latestInvoice);

    expect(latestInvoice).toBeTruthy();
    // Check for discount details in the invoice
    expect(latestInvoice.discount).toBeTruthy();
    expect(latestInvoice.discount.coupon.id).toBe(coupon.id);

    const invoices = await paymentService.retrieveInvoices({
      customer: customer.id,
      limit: 10,
    });

    expect(invoices).toBeTruthy();
    expect(invoices.data).toHaveLength(2);

    // Ensure that the first invoice has the expected amount due
    const expectedAmountDue = (amount2 * quantity) / 2; // 50% off
    expect(invoices.data[0].amount_due).toBe(expectedAmountDue);

    expect(invoices.data.every((invoice) => invoice.subscription === subscription.id)).toBe(true);

    await paymentService.stripe.coupons.del(coupon.id);
  });```
#

I have something like this

#

but

#

latestInvoice doesn't have discount

#

on the other hand ``` test("upgradeSubscription() with coupon", async () => {
const testClock = await createTestClock();

const customer = await createTestCustomer(testClock);

const paymentMethod = await paymentService.createPaymentMethod({
  number: "4242424242424242",
  exp_month: 12,
  exp_year: new Date().getFullYear() + 1,
  cvc: "314",
});

await paymentService.attachPaymentMethod(paymentMethod.id, customer.id);

const productName = "test product";

const product = await paymentService.createProduct(productName);

const amount = 100;
const currency = "usd";

const price = await paymentService.createPrice(
  product.id,
  amount,
  currency,
  {
    interval: "month",
  }
);

const quantity = 5;

const subscription = await paymentService.purchaseSubscriptionWithPaymentMethodPresent({
  customer: customer.id,
  price: price.id,
  paymentMethodId: paymentMethod.id,
  quantity
});

expect(subscription).toBeTruthy();
expect(subscription.customer).toBe(customer.id);
expect(subscription.items.data[0].price.id).toBe(price.id);
expect(subscription.items.data[0].quantity).toBe(quantity);
expect(subscription.status).toBe("active");

const productName2 = "test product 2";

const product2 = await paymentService.createProduct(productName2);

const amount2 = 1500;

const price2 = await paymentService.createPrice(
  product2.id,
  amount2,
  currency,
  {
    interval: "month",
  }
);

const coupon = await paymentService.stripe.coupons.create({
  duration: 'once',
  percent_off: 50,
});

const upgradedSubscription = await paymentService.upgradeSubscription({
  subscription,
  newPrice: price2.id,
  quantity,
  coupon: coupon.id
});

expect(upgradedSubscription).toBeTruthy();
expect(upgradedSubscription.customer).toBe(customer.id);
expect(upgradedSubscription.items.data[0].price.id).toBe(price2.id);
expect(upgradedSubscription.items.data[0].quantity).toBe(quantity);
expect(upgradedSubscription.status).toBe("active");

const latestInvoice = await paymentService.stripe.invoices.retrieve(upgradedSubscription.latest_invoice as string);

expect(latestInvoice).toBeTruthy();
expect(latestInvoice.discount.coupon.id).toBe(coupon.id);

const invoices = await paymentService.retrieveInvoices({
  customer: customer.id,
  limit: 10,
});

expect(invoices).toBeTruthy();
expect(invoices.data).toHaveLength(2);
expect(invoices.data[1].subscription).toBe(subscription.id);
expect(invoices.data[1].amount_paid).toBe(amount * quantity);
expect(invoices.data[0].subscription).toBe(subscription.id);
expect(invoices.data[0].amount_paid).toBe((amount2 * quantity) / 2 - amount * quantity); // 50% off

});```the upgrade test checks out

#
   * Upgrade a subscription to a new product and price
   * @param subscription current subscription
   * @param newPrice new price id
   * @param quantity number of items
   * @returns updated subscription object
   */
  public async upgradeSubscription(
    input: {
      subscription: Stripe.Subscription,
      newPrice: string,
      quantity?: number,
      coupon?: string
    }
  ): Promise<Stripe.Subscription> {
    const { subscription, newPrice, quantity, coupon } = input;

    return this.stripe.subscriptions.update(subscription.id, {
      cancel_at_period_end: false,
      proration_behavior: "always_invoice",
      items: [
        {
          id: subscription.items.data[0].id,
          price: newPrice,
          ...(quantity && { quantity }),
        },
      ],
      ...(coupon && { coupon }),
    });
  }

  /**
   * Downgrade a subscription to a new product and price with proration
   * @param subscription current subscription
   * @param newPrice new price id
   * @param quantity number of items
   * @returns updated subscription object
   */
  public async downgradeSubscription(
    input: {
      subscription: Stripe.Subscription,
      newPrice: string,
      quantity?: number,
      coupon?: string
    }
  ): Promise<Stripe.Subscription> {
    const { subscription, newPrice, quantity, coupon } = input;

    return this.stripe.subscriptions.update(subscription.id, {
      cancel_at_period_end: false,
      proration_behavior: "create_prorations",
      items: [
        {
          id: subscription.items.data[0].id,
          price: newPrice,
          ...(quantity && { quantity }),
        },
      ],
      ...(coupon && { coupon }),
    });
  }
#

here are the two methods for comparison

#

maybe it's because of proration_behavior: "create_prorations", ?

orchid anchorBOT
opal scaffold
#

Hello

#

Taking a look

reef cove
#

Thank you really appreciate it

opal scaffold
#

Yep so the difference here is that you are applying a 50% once coupon, so it would only take affect if an Invoice was generated immediately upon that update request, which requires proration_behavior: 'always_invoice'

#

If you want that coupon to carry over to the next period, you need to set a recurring duration

reef cove
#

This is a problem if I set it to always invoice the downgrade is effective immediately right ?

opal scaffold
#

Yes that's correct

reef cove
#

Another question

#

Since I’ll pay more for the previous plan

#

What happens if downgraded plan + prorated sum is negative ?

opal scaffold
#

It would generate a credit on the Customer that would be applied to the next Invoice

#

I would recommend testing it out!

reef cove
#

nice, I'll have to remove the promo code / coupons on downgrades

#

I'll definitely give it a go if they request something like that