I am using klarna with merchello and when the time comes to create the order in merchello for some reason the status is remaining as unpaid. First I thought because I was just authorizing the payment (AuthorizePayment) and not authorizing and capturing the payment (AuthorizeCapturePayment) but I have fixed that and the behavior still remained the same. Here is my code:
private bool confirmOrder(IDictionary<string, string> processorArgs, ProcessorArgumentCollection processorArgs2, ICheckoutManagerBase checkoutManager, IEnumerable<string> customInvoiceNotes = null)
{
LogHelper.Info(typeof(KlarnaPaymentMethodUiController), "entered in confirm order method");
// Attempt to get a previously saved payment method if no explicit payment method key provided
var paymentMethod = getPaymentMethod(processorArgs, checkoutManager);
if (paymentMethod == null)
{
LogHelper.Error(typeof(KlarnaPaymentMethodUiController), "Payment failed. No payment method was found.", new NotImplementedException());
//_log.Error("Payment failed. No payment method was found.");
return false;
}
LogHelper.Info(typeof(KlarnaPaymentMethodUiController), "getPaymentMethod success");
var attempt = checkoutManager.Payment.AuthorizeCapturePayment(paymentMethod.Key, processorArgs2);
if (!attempt.Payment.Success)
{
//_log.Fatal("Payment failed.", attempt.Payment.Exception);
LogHelper.Error(typeof(KlarnaPaymentMethodUiController), "attempt payment success failed", attempt.Payment.Exception);
return false;
}
//_log.DebugFormat("Successful payment with key {0} and ref no {1}.", attempt.Payment.Result.Key, attempt.Payment.Result.ReferenceNumber);
LogHelper.Info(typeof(KlarnaPaymentMethodUiController), String.Format("Successful payment with key {0} and ref no {1}.", attempt.Payment.Result.Key, attempt.Payment.Result.ReferenceNumber));
// The orders collection on the invoice isn't automatically populated after order creation, so we need to do it manually
var invoiceOrders = MerchelloServices.OrderService.GetOrdersByInvoiceKey(attempt.Invoice.Key);
foreach (var io in invoiceOrders)
{
attempt.Invoice.Orders.Add(io);
}
foreach (var invoiceOrder in invoiceOrders)
{
MerchelloServices.OrderService.CreateOrder(invoiceOrder.OrderStatusKey, invoiceOrder.Key);
}
//_taskSequenceHandler.ExecuteTaskSequence(TaskSequenceTrigger.OnCheckoutComplete, attempt);
// store the invoice key in the CustomerContext for use on the receipt page.
CustomerContext.SetValue("invoiceKey", attempt.Invoice.Key.ToString());
checkoutManager.Reset();
return true;
}
Does anyone have a clue why and can point me to the right direction?
Yes I am, I have implemented a custom one to integrate with klarna. I am pretty sure that attempt.Payment.Success returns true, since I have personally debugged it. Do you have any clue?
I think you were referring to one of these methods:
/// <summary>
/// Does the actual work of creating and processing the payment
/// </summary>
/// <param name="invoice">The <see cref="IInvoice"/></param>
/// <param name="args">Any arguments required to process the payment.</param>
/// <returns>The <see cref="IPaymentResult"/></returns>
protected override IPaymentResult PerformAuthorizePayment(IInvoice invoice, ProcessorArgumentCollection args)
{
var payment = GatewayProviderService.CreatePayment(PaymentMethodType.PurchaseOrder, invoice.Total, PaymentMethod.Key);
payment.CustomerKey = invoice.CustomerKey;
payment.PaymentMethodName = PaymentMethod.Name;
payment.ReferenceNumber = PaymentMethod.PaymentCode + "-" + invoice.PrefixedInvoiceNumber();
payment.Collected = false;
payment.Authorized = true;
foreach (var arg in args)
{
payment.ExtendedData.SetValue(arg.Key, arg.Value);
}
GatewayProviderService.Save(payment);
// In this case, we want to do our own Apply Payment operation as the amount has not been collected -
// so we create an applied payment with a 0 amount. Once the payment has been "collected", another Applied Payment record will
// be created showing the full amount and the invoice status will be set to Paid.
var authorizedPaymentMsg = string.Format("Authorized payment of {0}. Klarna reservation number is {1}.", payment.Amount.ToString("C", new CultureInfo("sv-SE")), args["reservationNumber"]);
GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, authorizedPaymentMsg, 0);
return new PaymentResult(Attempt.Succeed(payment), invoice, true);
}
/// <summary>
/// Does the actual work of authorizing and capturing a payment
/// </summary>
/// <param name="invoice">The <see cref="IInvoice"/></param>
/// <param name="amount">The amount to capture</param>
/// <param name="args">Any arguments required to process the payment.</param>
/// <returns>The <see cref="IPaymentResult"/></returns>
protected override IPaymentResult PerformAuthorizeCapturePayment(IInvoice invoice, decimal amount, ProcessorArgumentCollection args)
{
throw new NotImplementedException();
}
/// <summary>
/// Does the actual work capturing a payment
/// </summary>
/// <param name="invoice">The <see cref="IInvoice"/></param>
/// <param name="payment">The previously Authorize payment to be captured</param>
/// <param name="amount">The amount to capture</param>
/// <param name="args">Any arguments required to process the payment.</param>
/// <returns>The <see cref="IPaymentResult"/></returns>
protected override IPaymentResult PerformCapturePayment(IInvoice invoice, IPayment payment, decimal amount, ProcessorArgumentCollection args)
{
var reservationNumber = payment.ExtendedData.GetValue("reservationNumber");
if (string.IsNullOrEmpty(reservationNumber))
{
return new PaymentResult(Attempt<IPayment>.Fail(payment, new Exception("Failed to capture payment because no Klarna reservation number was found.")), invoice, false);
}
var invoiceNumber = _paymentProcessor.ActivateOrder(reservationNumber);
if (invoiceNumber == null)
{
return new PaymentResult(Attempt<IPayment>.Fail(payment), invoice, false);
}
payment.ExtendedData.SetValue("invoiceNumber", invoiceNumber);
payment.Collected = true;
GatewayProviderService.Save(payment);
var capturedPaymentMsg = string.Format("Captured payment of {0}. Klarna invoice number is {1}.", payment.Amount.ToString("C", new CultureInfo("sv-SE")), invoiceNumber);
GatewayProviderService.ApplyPaymentToInvoice(payment.Key, invoice.Key, AppliedPaymentType.Debit, capturedPaymentMsg, amount);
return new PaymentResult(Attempt<IPayment>.Succeed(payment), invoice, false);
}
I think now understand that the issue is because the PerformAuthorizeCapturePayment is not actually implemented. What do you think?
Also how I can capture the payment only after authorizing it rather doing both at the same time? I couldn't find the right method in ICheckoutPaymentManager where there is the AuthorizePayment method.
I think your right - PerformAuthorizeCapturePayment would do it in a single shot - but I understand that is not what you're looking for.
You're also correct that the CheckoutManager does not have a CapturePayment method. This is by design since the checkout manager is intended to do the work of getting the basket through the checkout process. Once an invoice is saved (meaning some sort of sale has occurred - even if it's just authorized) the CheckoutManager's job is done.
So in your case, you will want to capture the payment directly from the invoice.
var attempt = CheckoutManager.AuthorizePayment(...);
// ... do whatever
var invoice = attempt.Invoice;
.... do whatever
// This will execute your CapturePayment method in your PaymentProvider
invoice.CapturePayment( .... );
This is basically the same way it is done for paying later (cash payments, PO Numbers ... etc).
I am sorry if I am hijacking this thread, but it seems like Fabian got it solved. But I have kind of the same problem. I made a custom QuickPay integration and creating the invoice before payment window, and after the payment is accepted I run the following code grabbing the invoice, but my order is not set to collected = 1 in the database, but it does get marked as Paid-CreditCard and is authorized = 1:
var invoice = MerchelloContext.Current.Services.InvoiceService.GetByKey(Guid.Parse(invoiceKey));
if (invoice == null)
{
throw new ApplicationException("Invoice null with order with ID \"" + secureOrderID + "\" ");
}
//invoice.InvoiceNumberPrefix = "FT";
//Mark order as paid in system
//Create payment
var paymentMethodGateway = MerchelloContext.Current.Gateways.Payment.GetPaymentGatewayMethodByKey(Guid.Parse("D9E42E14-0FDA-4B05-838E-632D91950771"));
Guid paymentMethodKey = Guid.Parse("D9E42E14-0FDA-4B05-838E-632D91950771");
var paymentMethodName = "QuickPay";
var payment = MerchelloContext.Current.Services.GatewayProviderService.CreatePayment(PaymentMethodType.CreditCard, 0, paymentMethodKey);
payment.CustomerKey = invoice.CustomerKey;
payment.PaymentMethodName = paymentMethodName;
payment.ReferenceNumber = "QuickPay-" + invoice.PrefixedInvoiceNumber();
payment.Collected = true;
payment.Authorized = true;
//Save invoice first
MerchelloContext.Current.Services.InvoiceService.Save(invoice);
//Save payment
MerchelloContext.Current.Services.GatewayProviderService.Save(payment);
var attempt = invoice.CapturePayment(payment, paymentMethodGateway, invoice.Total);
if (attempt.Payment.Success)
{
payment.Collected = true;
payment.Authorized = true;
Merchello.Core.Notification.Trigger("OrderConfirmation", attempt, new[] { invoice.BillToEmail });
UpdateInsertInDatabase("UPDATE customQuickPayOrderLog SET qpl_success = @qpl_success WHERE qpl_orderid = @QPOrderID",
new object[,] { { "@qpl_success", true }, { "@QPOrderID", secureOrderID } });
}
else
{
throw new ApplicationException(attempt.Payment.Exception.ToString());
}
You have any idea what changes or addition has to be made to get it correctly collected = 1 in the database? Because as it is now in some places in the merchello system this does not count as a paid order and does not create discount codes used in the system as well.
Created Order status remains unpaid
Hi Everyone,
I am using klarna with merchello and when the time comes to create the order in merchello for some reason the status is remaining as unpaid. First I thought because I was just authorizing the payment (AuthorizePayment) and not authorizing and capturing the payment (AuthorizeCapturePayment) but I have fixed that and the behavior still remained the same. Here is my code:
Does anyone have a clue why and can point me to the right direction?
Thanks
Hi Fabian,
Are you using a custom payment provider for this?
It should be happening in the underlying code here (if attempt.Payment.Success)
Hi Rusty,
Yes I am, I have implemented a custom one to integrate with klarna. I am pretty sure that attempt.Payment.Success returns true, since I have personally debugged it. Do you have any clue?
Thanks
In the AuthorizeCapture method of the payment provider, the invoice should be marked as paid. Can you post the code to your PaymentMethod?
Hi Rusty what do you mean by the AuthorizeCapture method of the payment provider? Isn't that a method of merchello?
When you are saying the PaymentMethod, which one you are to refering to exactly?
Thanks
Hi Rusty,
I think you were referring to one of these methods:
I think now understand that the issue is because the PerformAuthorizeCapturePayment is not actually implemented. What do you think?
Also how I can capture the payment only after authorizing it rather doing both at the same time? I couldn't find the right method in ICheckoutPaymentManager where there is the AuthorizePayment method.
Thanks a lot for your support rusty.
I think your right -
PerformAuthorizeCapturePayment
would do it in a single shot - but I understand that is not what you're looking for.You're also correct that the CheckoutManager does not have a
CapturePayment
method. This is by design since the checkout manager is intended to do the work of getting the basket through the checkout process. Once an invoice is saved (meaning some sort of sale has occurred - even if it's just authorized) the CheckoutManager's job is done.So in your case, you will want to capture the payment directly from the invoice.
This is basically the same way it is done for paying later (cash payments, PO Numbers ... etc).
Thanks a lot for your support Rusty, very helpful.
I am sorry if I am hijacking this thread, but it seems like Fabian got it solved. But I have kind of the same problem. I made a custom QuickPay integration and creating the invoice before payment window, and after the payment is accepted I run the following code grabbing the invoice, but my order is not set to collected = 1 in the database, but it does get marked as Paid-CreditCard and is authorized = 1:
You have any idea what changes or addition has to be made to get it correctly collected = 1 in the database? Because as it is now in some places in the merchello system this does not count as a paid order and does not create discount codes used in the system as well.
Best regards, Christian
is working on a reply...