I’m trying to capture payments through code and I can’t quite get it to work, and can’t figure out why. It runs fine, shows no indications of not working, but when i go to the backoffice the state hasn’t changed.
Here’s a snippet of how i’m currently trying to do it:
await umbracoCommerceApi.Uow.ExecuteAsync(async (uow, ct) =>
{
var order = umbracoCommerceApi.GetOrder(new Guid(orderStatus.UmbracoOrderId)).AsWritable(uow);
if(order is null)
{
Log.Warning($"Failed getting order {orderStatus.Id}");
throw new InvalidDataException($"Order not found {orderStatus.Id}");
}
if(orderStatus.StatusId == 4)
{
order.InitializeTransaction(orderNumberGenerator);
var paymentResult = await umbracoCommerceApi.CaptureOrderPaymentAsync(order, new CancellationToken());
var paymentStatus = await umbracoCommerceApi.FetchOrderPaymentStatusAsync(order, ct);
if(paymentResult.Success)
{
var orderStatuses = umbracoCommerceApi.GetOrderStatuses(order.StoreId);
order = order.Finalize(order.TransactionAmount, order.TransactionInfo.TransactionId, PaymentStatus.Captured);
order.SetOrderStatus(orderStatuses.First(x => x.Name == "Levererad").Id);
}
order.Recalculate(orderCalculator);
umbracoCommerceApi.SaveOrder(order);
}
uow.Complete();
});
What i find strange here is that paymentResult shows that it’s a success and payment has been captured. If i then fetch the payment status as can be seen in paymentStatus it’s showing it has not been captured.
In the project we are using Klarna through Umbraco.Commerce.PaymentProviders.Klarna 13.1.0.
I have tried setting up a simple invoice payment provider as well just for testing, but i get the same behaviour there as well.
Notice that nowhere in the code do i specify which payment provider I want to capture from, is this a problem or does Umbraco interpret and handle this on its own?
For info, the order status is properly updated, so that code works fine.
Please see the docs here on placing an order via code (these docs are for v15, but should be largely the same for v13 except for the fact that v15 is fully async, where v13 isn’t so it will have slightly different method names)
Thanks for reaching out. I looked at the documentation and was doing basically what was described there but in a slightly different order. I adjusted it and ran it in the same order, but still, the order changes status but the payment is not captured.
See the latest code here:
await umbracoCommerceApi.Uow.ExecuteAsync(async (uow, ct) =>
{
var order = umbracoCommerceApi.GetOrder(new Guid(orderStatus.UmbracoOrderId)).AsWritable(uow);
if(order is null)
{
Log.Warning($"Failed getting order {orderStatus.Id}");
throw new InvalidDataException($"Order not found {orderStatus.Id}");
}
if(orderStatus.StatusId == 4)
{
order.SetProperties(new Dictionary<string, string>
{
{ "marketingOptIn", "0" },
{ "billingAddressLine1", "10 Example Road" },
{ "billingCity", "Jönköping" },
{ "billingZipCode", "55322" },
{ "billingTelephone", "0046000000" },
{ "shippingSameAsBilling", "1" }
});
var paymentResult = await umbracoCommerceApi.CaptureOrderPaymentAsync(order, ct);
var orderStatuses = umbracoCommerceApi.GetOrderStatuses(order.StoreId);
order.SetOrderStatus(orderStatuses.First(x => x.Name == "Levererad").Id);
order.Recalculate(orderCalculator);
order.InitializeTransaction(orderNumberGenerator);
order.Finalize(order.TransactionAmount, Guid.NewGuid().ToString("N"), PaymentStatus.Captured);
umbracoCommerceApi.SaveOrder(order);
}
uow.Complete();
});
Any idea why it’s not working or any other input of someone with similar issues?
I’ll review this on Monday and see if it makes sense to add some extra docs around the processing of a payment through the payment providers (in the current example it just assumes the use of the Invoicing payment provider)
I gave this some though over the weekend and something doesn’t seem right.
Generally speaking with Payment Providers, the GenerateForm method is normally what sets up the payment at the payment gateway and then the customer is redirected to pay. It is also here that InitializeTransaction is called. We then get a callback (webhook response) that would capture the details of the payment and update the order calling FinalizerOrder. Now I get this isn’t what you want as you need to do everything all at once in code.
The problem I see is that you are calling CaptureOrderPaymentAsync to handle capturing the payment. But in a “normal” payment provider this would only be called for payments that have already been placed but are in an Authorized state, however here it seems like you are using the CaptureOrderPaymentAsync to perform the entire placement of the order and capture the payment all in one.
This could potentially cause problems if someone was to click the “Capture” button in the back office if you ever have the position where an order is captured but Authorized.
Ultimately, if you are aware of this stuff and you are just using CaptureOrderPaymentAsync as a means for placing and capturing an order in one, and this payment provider is used only for this purpose, then that’s probably OK. But from our perspective, that’s a non standard way of using the payment providers and so I don’t think there is anything we can do about updating the docs with additional code examples.