#karls_invoice-lines

1 messages ยท Page 1 of 1 (latest)

grand coveBOT
#

๐Ÿ‘‹ Welcome to your new thread!

โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.

โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always 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/1278378053273129063

๐Ÿ“ Have more to share? Add more details, code, screenshots, videos, etc. below.

glass ledge
#

A quick google search and I see a limit of 250 items, which is well below the 43 limit that we are experiencing.

crisp kayak
#

Can you send me the ID of an invoice that got truncated to 43 items? (in_1234)

grand coveBOT
crisp kayak
#

I am also seeing a 250 limit. The more surprising thing to me is that it sounds like this is failing silently for you. Our API should error if an item is added but we don't consider that addition valid

glass ledge
#

I agree. Let me find an example invoice.

#

409A0F1C-0008

#

This is the stripe payment form on my end that shows that the user had 108 items in their cart.

shrewd prism
#

We will need an Invoice ID, which looks like this: in_abc123

glass ledge
#

ok, 1 sec.

#

in_1PptzLG30Vl6ne6NH8mV3hZI

#

Here is the order form from my side.

shrewd prism
glass ledge
#

reviewing..

#

This customer has made 3 attemps now to register 108 athletes. First attempt was for 108 athletes that was truncated to 43 items. 2nd attempt was for 65 athletes and truncated to 43 items. The final attempt was the reamining 22 athletes. The is over a couple of days. Does this all jive?

shrewd prism
#

I'm a little confused I guess, because you're saying they attempt to register these numbers of athletes, but I'm not really sure what your integration it actually doing. It looks like you're creating a ton of Invoice Items for each Invoice, but then you add a Payment Method and explicitely call the Stripe API to pay the invoice explicitely.

Like, it looks like you're choosing to pay the Invoice before adding more Invoice Items

glass ledge
#

My Integration:

  1. User selects members to renew
  2. A payment intent is created (via javascript api call)
  3. Payment intent passed to my API
#
  1. An invoice is generated
  2. Line items added
  3. Invoice and payment intent committed to stripe.
#

4.a Payment intent added/attached to invoice.

#

Isn't this all correct?

shrewd prism
#

I don't think that's right. You create an Invoice, which then creates its own Payment Intent automatically. That's what happened with the example Invoice you sent

#

All the Invoice Items are being created on the Customer and aren't getting attached to a specific Invoice when they're created. They remain pending until they automatically get picked up by the next Invoice that gets created

glass ledge
#

Well then I'm confused. I thought I had to make a request to Stripe to get back a Payment Intent which I than later attach to a new Invoice.

shrewd prism
glass ledge
#

Yes, but I create the Invoice in my backend, in my API and is sort of a one-and-done operation. Maybe I'm mixing stirpe javascript API with Stripe .NET API logic??

#

What if I disable the option to automatically add the payment intent for the customer to the invoice and I continue to do it as I am doing?

#

I'm using a Stripe Payment form, so the Stripe Form API (not sure what it is called) and that requires that I create a payment intent object.

shrewd prism
#

I think you need to find a way to detect when the customer is done adding Invoice Items before creating the Invoice, since creating the Invoice and paying it in one step will automatically attach all existing pending Invoice Items on the Customer to the Invoice

shrewd prism
glass ledge
#

I'm not doing that.

#

.. and using the Card Api code from that to create my Stripe form.

shrewd prism
#

Sorry, I think we're talking past each other here. It seems like you may have a bit of a misunderstanding about how Invoices work in relation to your payment form. Once you use stripe.js to pay the Payment Intent on the Invoice, it finalizes the Invoice and picks up all the pending Invoice Items.

Are we aligned there?

glass ledge
#

Yes and no. (thinking)

#

Where do I generate the invoice: client-side with your javascript API code, or server-side in my code utilizing the .NET Stripe API code

shrewd prism
glass ledge
#

OK.. hold on a sec.

#

May I share some code with you?

shrewd prism
#

To what end? I'm happy to look at code, but I'm not sure what the question is at this point

glass ledge
#

To demonstrate the basic usage of the Stripe API in my backend as I understand it.

#

I need to understand where I'm wrong, as it seems I am having a fundamental breakdown of the Stripe flow.

shrewd prism
#

It might be more helpful to describe in your own words what you think is happening so that I can try to reframe first. Could you take a stab at giving me a step-by-step explanation of your understanding? Specifically calling out Stripe API calls and what they do

glass ledge
#

I understand. My code is fairly easy to read. I'm not going to dump 100's of lines of code here on you!

shrewd prism
#

Sorry, that's not really what I'm looking for. I'm looking for something like:

  1. Customer adds items to cart
  2. My back-end creates Invoice Items
  3. They navigate to the payment form where they're presented with the Payment Element
  4. I create the Invoice and use the Payment Intent from it in my Payment Element
glass ledge
#

ok, let me do that...

#
  1. Customer adds items to cart (not a stripe cart, but my virtual cart, a cart I hold in session for them)
  2. My front-end tallies up the total items and cost and utilizes the the Stripe Javascript API to show the customer a credit card form
  3. Client submits the stripe payment form, a payment intent is created and is passed to my back-end.
  4. My back-end API creates a new invoice and attaches the payment intent along with creating the invoice line items
  5. Finally the invoice is paid from my back-end by calling the Pay method of the Invoice class with my Invoice Request Object.
    DONE
#

back in 5 min.

shrewd prism
#

So the only thing I'll mention here is that your back-end isn't attaching a Payment Intent to the Invoice. The Stripe is automatically creating the Payment Intent when the Invoice gets paid. Up until that point, there is no Payment Intent.

You are using the Card Element to create a Payment Method, which then gets attached to the Customer object so that it can be used to pay the Invoice.

glass ledge
#

back..

shrewd prism
#

Additionally, the Invoice Items are being created before the Invoice itself. You are creating Pending Invoice Items on the Customer, which then get automatically picked up by the Invoice you create at the end of the workflow.

So your step-by-step actually looks like this:

  1. Customer adds items to cart (not a stripe cart, but my virtual cart, a cart I hold in session for them)
  2. My front-end tallies up the total items and cost and utilizes the the Stripe Javascript API to show the customer a credit card form
  3. Client submits the stripe Card Element form to create a Payment Method,
  4. My back-end attaches the Payment Method to the Customer so it can be used for Invoice payments
  5. My back-end creates a bunch of Invoice Items on the Customer
  6. My back-end API creates and pays a new Invoice which automatically creates a new Payment Intent along with picking up the Pending Invoice Items and adding them as the Invoice line items
    DONE
glass ledge
#

Is is correct to use the Card Element to create the Payment Method?

shrewd prism
glass ledge
#

Am I confusing Payment Method for Payment Intent?

shrewd prism
grand coveBOT
glass ledge
#

Item 5 above - where do you see that I am creating a "bunch" of invoices? I should be creating a single invoice.

shrewd prism
#

Step 5 is "create a bunch of Invoice Items" not Invoices

glass ledge
#

Sorry, you are correct.

sick ermine
#

karls_invoice-lines

glass ledge
#

OK, this all seems correct to me. Where am I going wrong?

sick ermine
#

@glass ledge I'm taking over. What exact issue are you having right now after all my colleague explanations?

glass ledge
#

He outlined the actual steps that my client is experiencing, front-end through back-end. So steps 1-6 above. I'm trying to understand where I am going wrong.

sick ermine
#

Sure but I assume you are the developer writing the end to end code. So what exactly is the problem you are experiencing right now as the developer reproducing the issue in Test mode?

glass ledge
#

This all started with -- my client generated an order with 108 items, but only 43 items were processed.

sick ermine
#

Yes I read the backlog. My colleague explicitly found calls where your code creates multiple separate Invoices

#

Also if I may: I don't think it's realistic to try and create an Invoice with 108 items in real time. this is going to take multiple minutes to sequentially create all the relevant lines

glass ledge
#

Yes, I am the developer. There is confusion where and when to add the Payment Intent object, and that it is being added automatically and I'm not suppose to attach it to the invoice (it is automatic) and so forth.

sick ermine
#

You would never create a PaymentIntent or "attach" it. You create an Invoice and that Invoice will create a PaymentIntent for you when you finalize it

glass ledge
#

..and then I asked if I was confusing Payment Method and Payment Intents..

#

So step 3 above - I get back a Payment Method and I pass that to my back-end and I attach it..

sick ermine
#

You can. I absolutely would not do that personally. My colleague was explaining what you seem to be doing. Not what you should be doing.

glass ledge
#

Yes, I add it to the Invoice Request object.

sick ermine
#

I'm sorry, it's been a really long conversation with a lot of confusion. It seems you might have gone down the wrong path from the start ๐Ÿ˜…

glass ledge
#

Maybe!

#

So, point of clarification

#

Do I, Don't I, use the stripe javascript API code to generate a Payment Method, pass that to my back-end and attach it to my Invoice Request object?

sick ermine
#

I would do it completely differently personally. So I would say "don't".

glass ledge
#

OK.

sick ermine
#

Sorry it's a bit of a vague answer but you're asking a bit of vague questions that are meta.

#

Are you asking:
#1: What is the right way to use the API to do my flow, and I am willing to rewrite my entire code from scratch
#2: How can I quickly patch my code/issue and not change anything else
Because the answers would be wildly different

glass ledge
#

Well, I'm hoping I'm not too far off! I'm really looking to understand where I am failing, where I have fundamentally gone wrong! I've been using this code for several years now and much of it was gotten to from here, in support. Not blaiming anyone, just trying to understand. Where am I fundamentally wrong in using the Card Element API and passing that payment method to my backend?

#

One other note: I rely heavily on the MetaData property and customization, I use them when I process the webhook to mark a registration as complete and paid on my end and to perform other tasks related to the order.

#

Hence the need to build up the Invoice and Line Items in my back-end.

sick ermine
#

Where am I fundamentally wrong in using the Card Element API and passing that payment method to my backend?
that's part of the problem. You're not "wrong" if you integrated years ago before any of the newer integration paths existed.

#

Sorry but I still don't really get what we're trying to solve.

My guess is: you have one small bug and you only want to fix this and nothing else. If that works with you, let's solely focus on that and nothing else: Why you have multiple separate Invoices when you should only have one

glass ledge
#

Yes, 108 items truncated (somewhere) to 43 items.

sick ermine
#

perfect, let's solely focus on this.

glass ledge
#

Cool. I included an invoice id above.

sick ermine
#

Your code is actively creating separate Invoices.

#

There's a really easy way to fix this I think

When you integrated a few years ago you always had to first create an InvoiceItem and then create an Invoice. If you tried to create an Invoice first you would get "nothing to invoice" as an error

We changed this a couple years ago. We now let you create an empty Invoice first. And then add InvoiceItems to it.

So my advice is to change your code to always create the Invoice first and then add each of the 108 lines to that Invoice by passing invoice: 'in_1234' when you create it

glass ledge
#

I don't think so. These invoices are the result of 3 seperate attempts by the customer to register 108 members. Those are 3 different attempts. Well, they all resulted in a successfull payment and processing on my end, but just truncated down somwhere to the 43 items.

sick ermine
#

I see, but if you make the change I mentioned it will solve your issue

glass ledge
#

Yes, I do recall the error you are mentioning. I can easily change this in my code.

#

So we have identified the fundamental issue. I will fix that right away.

#

Thanks for the help on this.

sick ermine
#

Can you share a bit of the code that creates the InvoiceItems and the Invoice?

glass ledge
#

Yes..

#

`
string newStripeID = String.Empty;
string newContactID = String.Empty;

var cust = ResolveStripeCustomer();
var paymentmethod = msoStripe.StripePaymentMethod.getStripePaymentMethod(getFormValue("PaymentMethod", true));

var invoicemeta = new msoStripe.Model.MetaData();
invoicemeta.Add(new MetaDataItem() { Name = "NGANumber", Value = Request.Form["NGANumber"] });
invoicemeta.Add(new MetaDataItem() { Name = "NGAType", Value = Request.Form["NGAType"] });

var stripe = new msoStripe.StripeInvoice(_apiKey);
var request = new msoStripe.Model.InvoiceRequest()
{
CustomerStripeID = cust.StripeID,
PaymentMethodID = paymentmethod.PaymentID,
MetaData = invoicemeta,
InvoiceMemo = "Club Registration Invoice"
};

var lineitems = new msoStripe.Model.InvoiceItems();
addSingleLineItem(lineitems, Request.Form["price-code"], Request.Form["NGANumber"], Request.Form["NGAType"], Request.Form["Season"]);

var response = stripe.Pay(request, lineitems);

WritePropertyValue("PayerKey", response.IdempotencyKey);
WritePropertyRaw("Response", response.SerializeJSON());
`

sick ermine
#

you either have your own abstraction layer on top of one of our SDKs or you don't use one of our SDKs at all so it's hard to say

glass ledge
#

Yes, hold on. The stripe.Pay call is the juice.

#

It's a little more code... I'm going to try and copy out the most relevant bits.

sick ermine
#

yeah just show me the bits that create the lines and the Invoice. You can wrap those between 3 backticks to format it.
Or use a gist or pastebin

glass ledge
#
var temp_invoiceid = default(string);
var client = new StripeClient(_key);

var paymentservice = new Service.StripePaymentMethod(_key);
paymentservice.Attach(Request.PaymentMethodID, Request.CustomerStripeID);

var service = new InvoiceService(client);

// create line items
var itemservice = new InvoiceItemService(client);
foreach (var item in Items)
{

    var itemoptions = new InvoiceItemCreateOptions()
    {
        Customer = Request.CustomerStripeID,
        Price = item.PriceCode
    };
    ...
    var invoiceitem = itemservice.Create(itemoptions);
}

// create the invoice
var invoptions = new InvoiceCreateOptions()
{
    Customer = Request.CustomerStripeID,
    PendingInvoiceItemsBehavior = "include"
};

// attach metadata to invoice
if (Request.MetaData != null && Request.MetaData.Count() > 0)
{
    invoptions.Metadata = new Dictionary<string, string>();
    foreach (var Meta in Request.MetaData)
    ...
}

// has a promotion code been provided?
...

var invoice = service.Create(invoptions);
temp_invoiceid = invoice.Id;

// pay the invoice
var invoiceresponse = default(Invoice);

var payoptions = new InvoicePayOptions()
{
    PaymentMethod = Request.PaymentMethodID
};

invoiceresponse = service.Pay(invoice.Id, payoptions);

return new Model.StripePayInvoiceResponse()
{
    IdempotencyKey = invoiceresponse.StripeResponse.IdempotencyKey,
    ID = invoiceresponse.Id,
    InvoiceUrl = invoiceresponse.HostedInvoiceUrl
};
sick ermine
#

okay so you have this:

var itemservice = new InvoiceItemService(client);
foreach (var item in Items)
{

    var itemoptions = new InvoiceItemCreateOptions()
    {
        Customer = Request.CustomerStripeID,
        Price = item.PriceCode
    };
    ...
    var invoiceitem = itemservice.Create(itemoptions);
}``` to create all your pending InvoiceItems which will then be lines on the Invoice.

and then you have ```
var invoptions = new InvoiceCreateOptions()
{
    Customer = Request.CustomerStripeID,
    PendingInvoiceItemsBehavior = "include"
};``` that creates the Invoice and pulls all the "pending InvoiceItems"
And I think the issue is you have so many we just "time out" and the Invoice is partially created with only a subset of lines (your bug).

So you swap the order. Create the Invoice first. And then create the lines by explicitly passing the extra `Invoice` parameter:

// create the empty invoice
var invoptions = new InvoiceCreateOptions()
{
Customer = Request.CustomerStripeID,
// Do not pull pending InvoiceItems anymore
//PendingInvoiceItemsBehavior = "include"
};
var invoice = service.Create(invoptions);

// create each line items attached to that Invoice
var itemservice = new InvoiceItemService(client);
foreach (var item in Items)
{
var itemoptions = new InvoiceItemCreateOptions()
{
Customer = Request.CustomerStripeID,
Price = item.PriceCode,
Invoice = invoice.Id
};
...
var invoiceitem = itemservice.Create(itemoptions);
}

glass ledge
#

1 sec..

#

Copy and pasting in Discord is either difficult or impossible..

#

Thank you for the detailed changes! So to clarify on the "extra Invoice paramters" This is:

Invoice = invoice.id

sick ermine
#

Discord is just raw text so whether you use a browser or the desktop app copy-pasting should work totally fine

#

and yes for the extra parameter

glass ledge
#

OK! I'm on it. Funny thing about the "bug" is that in 2 separate cases it exactly failed (maybe by design) at 43 items. Just an observation.

#

The copy/paste issue I'm having might be that I'm remoted into my home pc from my laptop.

sick ermine
#

might be that I'm remoted into my home pc from my laptop.
ah yeah that makes copy pasting painful

#

And yeah basically when we create the Invoice we have to pull in all those InvoiceItems and modify them and that's really slow and it can lead to timeouts.

glass ledge
#

Thanks! Bye for now.