The code below generates an invoice record every time payment fails (i.e. invalid CC#). How do I prevent this, and append failed payments to a single invoice? The line attempt = preparation.AuthorizePayment(paymentMethod.Key, args); is what generates the invoice, regardless whether one already exists for an order or not.
// payment attempt result information IPaymentResult attempt = null; // get payment method var paymentMethod = Payment.GetPaymentGatewayMethodByKey(model.PaymentMethodKey).PaymentMethod; // Save the payment method selection preparation.SavePaymentMethod(paymentMethod); // make sure there is a billing address - it can be empty - it just has to exist if(!preparation.IsReadyToInvoice())returnRedirect("/cart");
but the invoice has been created and saved to the database ...
var invoice = attempt.Invoice; // has not been paid.
var payment = attempt.Payment.Result;
You can get the invoice key and do something like
var key = attempt.Invoice.Key;
CustomerContext.SetValue("invoiceKey", attempt.Invoice.Key.ToString()); // save the invoice key in the Merchello cookie.
and then go back and ask for another form of payment.
When the customer submits a second payment (or different cc number)
var dataKey = CustomerContext.GetValue("invoiceKey");
var invoiceKey = new Guid(dataKey);
var invoice = MerchelloContext.Current.Services.InvoiceService.GetByKey(invoiceKey);
// use the extension directly off off the invoice
invoice.AuthorizeCapturePayment([payment method], ....)
@Rusty I'm not sure what CustomerContext is in your example above. The CustomerContext object doesn't expose a SetValue method that I can see. I think I understand what you're getting at, though - I need to save the failed invoice key, and if it exists, re-use that invoice and attempt another payment.
However, once the payment is successful (and the order is opened), I will need to make sure I clear out this value, as I assume the customer is still the same, and we don't want a 2nd order to attempt to re-use the invoice from the first order.
That being said, the issue would still exist that if a customer fails payment, the invoice would still appear in the dashboard, and barely looks different from an open order. I think my best option might be to actually delete the invoice using the InvoiceService upon payment failure - I think we really only want to see "Open" orders in the dashboard, however this would prevent a customer service rep from being able to administer a failed order. Hmm.
I will play around with these two options and see what works best.
As the one who created the Stripe provider, I have been using it for a project that only has anonymous customers and the checkout code is based on earlier examples then what have been published now. So it is quite possible that I missed a bug that is causing your problem. If that's the case, I want to fix it quickly. I'll try to find some time to look at this problem. I'll try to reproduce the problem with the example sites, but if I can't, I may need you to send me more info.
@Alex Most definitely. My initial question about the multiple invoices appears to be by design, and I have corrected that particular issue by storing the initial invoice id/key with the customer extended data, and re-using that invoice for future payment attempts.
Are you saying one or more of the 3 new items above might be the Stripe provider?
As a follow-up, I have determined that using the method that Rusty mentioned (to store the invoice key and attempt to re-use the same invoice record) causes issues when the user adjusts their cart after their first payment attempt. Apparently, the invoice record doesn't recalculate the amount based on the current state of the basket, so you will end up invoicing your customer a lesser or greater amount than the current basket's value. I ended up having to revert back to having the plethora of "junk" invoices in the dashboard :(
@Keith - can you elaborate on what you mean by the invoice record doesn't recalculate the amount based on the current state of the basket? Once an invoice is created, I would think the basket contents are irrelevant and you are now concerned with the actual sale and not altering what is being sold. Think I'm missing something here ...
@Rusty It's a catch-22 for us (again, this may be an issue with the Stripe Provider?).
When we call preparation.AuthorizeCapturePayment(), an invoice record is created whether the payment is successful or not. If payment fails (i.e. invalid card number), we show a validation message. At this point, if they somehow decide that they forgot something, they can add it to their cart, and go through the checkout process again. The workaround above (saving the original invoice key and calling existingInvoice.AuthorizeCapturePayment() on successive attempts) causes the original invoice to potentially have a different dollar amount than the current state of the basket.
The original issue was that we were seeing invoices in the dashboard for failed payments, one for each failure. A single customer failing 5 times generates 5 invoices, and I was hoping to avoid that by keeping a 1-to-1 invoice-to-basket relationship somehow. If there was a way to just hide unpaid invoices within the dashboard (and search them), I feel that would be sufficient for what I'm trying to accomplish.
I see. What is happening is a Merchello thing. The basket is coupled to the the SalesPreparation by a version key. The idea is if you start a checkout (not complete it) and then go back and futz around in the basket, the SalePreparation gets invalidated as weights, prices, quantities of items in the basket could have been changed - thus there could be changes in shipping and taxation, etc.
Merchello assumes that when the invoice is created, the the intent of the customer has been confirmed and whatever items it includes should not be modified. What's happening when you go back and add something to the basket, the version key changes and it invalidates the sales preparation and Merchello thinks it's dealing with a completely new sale - thus the new invoice.
You could either delete the failed invoice if the basket is modified OR empty the basket and attempt a repayment of the failed invoice. There is probably not too much code to do in order to get your workflow going, but you would have to circumvent the SalesPreparation object. The basket uses the BasketSalesPreparation object which in turn sub classes SalePreparationBase in the Core. Maybe make your own CustomSalePreparation object that sub classed the SalePreparationBase and created "new" version of the AuthorizeCapture ... you'd have to test it pretty well though.
Payments with Stripe provider
The code below generates an invoice record every time payment fails (i.e. invalid CC#). How do I prevent this, and append failed payments to a single invoice? The line attempt = preparation.AuthorizePayment(paymentMethod.Key, args); is what generates the invoice, regardless whether one already exists for an order or not.
One way to do it -
If the payment fails you will have
but the invoice has been created and saved to the database ...
You can get the invoice key and do something like
and then go back and ask for another form of payment.
When the customer submits a second payment (or different cc number)
@Rusty I'm not sure what CustomerContext is in your example above. The CustomerContext object doesn't expose a SetValue method that I can see. I think I understand what you're getting at, though - I need to save the failed invoice key, and if it exists, re-use that invoice and attempt another payment.
I believe I can use
And
However, once the payment is successful (and the order is opened), I will need to make sure I clear out this value, as I assume the customer is still the same, and we don't want a 2nd order to attempt to re-use the invoice from the first order.
That being said, the issue would still exist that if a customer fails payment, the invoice would still appear in the dashboard, and barely looks different from an open order. I think my best option might be to actually delete the invoice using the InvoiceService upon payment failure - I think we really only want to see "Open" orders in the dashboard, however this would prevent a customer service rep from being able to administer a failed order. Hmm.
I will play around with these two options and see what works best.
(Moved to separate topic)
Hi Keith,
As the one who created the Stripe provider, I have been using it for a project that only has anonymous customers and the checkout code is based on earlier examples then what have been published now. So it is quite possible that I missed a bug that is causing your problem. If that's the case, I want to fix it quickly. I'll try to find some time to look at this problem. I'll try to reproduce the problem with the example sites, but if I can't, I may need you to send me more info.
Thanks,
Alex
@Alex Most definitely. My initial question about the multiple invoices appears to be by design, and I have corrected that particular issue by storing the initial invoice id/key with the customer extended data, and re-using that invoice for future payment attempts.
Are you saying one or more of the 3 new items above might be the Stripe provider?
I think probably not, as the invoice creation happens in the Merchello core, but I couldn't understand how you got https://our.umbraco.org/projects/collaboration/merchello/merchello/61761-Invoice-dashboard-issues
I have been planning some updates to the Stripe provider, and wanted to make sure that if there are bugs, I address them.
As a follow-up, I have determined that using the method that Rusty mentioned (to store the invoice key and attempt to re-use the same invoice record) causes issues when the user adjusts their cart after their first payment attempt. Apparently, the invoice record doesn't recalculate the amount based on the current state of the basket, so you will end up invoicing your customer a lesser or greater amount than the current basket's value. I ended up having to revert back to having the plethora of "junk" invoices in the dashboard :(
@Keith - can you elaborate on what you mean by the invoice record doesn't recalculate the amount based on the current state of the basket? Once an invoice is created, I would think the basket contents are irrelevant and you are now concerned with the actual sale and not altering what is being sold. Think I'm missing something here ...
@Rusty It's a catch-22 for us (again, this may be an issue with the Stripe Provider?).
When we call preparation.AuthorizeCapturePayment(), an invoice record is created whether the payment is successful or not. If payment fails (i.e. invalid card number), we show a validation message. At this point, if they somehow decide that they forgot something, they can add it to their cart, and go through the checkout process again. The workaround above (saving the original invoice key and calling existingInvoice.AuthorizeCapturePayment() on successive attempts) causes the original invoice to potentially have a different dollar amount than the current state of the basket.
The original issue was that we were seeing invoices in the dashboard for failed payments, one for each failure. A single customer failing 5 times generates 5 invoices, and I was hoping to avoid that by keeping a 1-to-1 invoice-to-basket relationship somehow. If there was a way to just hide unpaid invoices within the dashboard (and search them), I feel that would be sufficient for what I'm trying to accomplish.
I see. What is happening is a Merchello thing. The basket is coupled to the the SalesPreparation by a version key. The idea is if you start a checkout (not complete it) and then go back and futz around in the basket, the SalePreparation gets invalidated as weights, prices, quantities of items in the basket could have been changed - thus there could be changes in shipping and taxation, etc.
Merchello assumes that when the invoice is created, the the intent of the customer has been confirmed and whatever items it includes should not be modified. What's happening when you go back and add something to the basket, the version key changes and it invalidates the sales preparation and Merchello thinks it's dealing with a completely new sale - thus the new invoice.
You could either delete the failed invoice if the basket is modified OR empty the basket and attempt a repayment of the failed invoice. There is probably not too much code to do in order to get your workflow going, but you would have to circumvent the SalesPreparation object. The basket uses the BasketSalesPreparation object which in turn sub classes SalePreparationBase in the Core. Maybe make your own CustomSalePreparation object that sub classed the SalePreparationBase and created "new" version of the AuthorizeCapture ... you'd have to test it pretty well though.
Is there a working version of stripe provider? I get error's when trying to build.
is working on a reply...