Umbraco Commerce Custom Payment Provider

Hi

I’m trying to create a custom payment provider in Umbraco Commerce.

That because i need to convert the order for a ERP custom.

The payment provider is for a BankTransfert, so there is no external website or gateway i needed.

The implementation is this one (i didn’t found a full example on the payment provider documentation, so maybe there are some errors)

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Threading;
using System.Threading.Tasks;
using Umbraco.Commerce.Core;
using Umbraco.Commerce.Core.Api;
using Umbraco.Commerce.Core.Models;
using Umbraco.Commerce.Core.PaymentProviders;
using Umbraco.Extensions;

namespace qwerty.seneca.industrialsupplies.core.PaymentProviders.BankTransfer
{
[PaymentProvider(“bonifico”)]
public class BankTransferPaymentProvider : PaymentProviderBase<BankTransferPaymentProvider.BankTransferSettings>
{
private readonly ILogger _logger;
private readonly IConfiguration _configuration;

    public BankTransferPaymentProvider(UmbracoCommerceContext umbracoCommerce, ILogger<BankTransferPaymentProvider> logger, IConfiguration configuration)
        : base(umbracoCommerce)
    {
        _logger = logger;            
        _configuration = configuration;
    }

    public override bool FinalizeAtContinueUrl => true;

    public class BankTransferSettings
    {
        [PaymentProviderSetting(SortOrder = 100)]
        public string ContinueUrl { get; set; }

        [PaymentProviderSetting(SortOrder = 200)]
        public string CancelUrl { get; set; }

        [PaymentProviderSetting(SortOrder = 300)]
        public string ErrorUrl { get; set; }
    }

    public override string GetContinueUrl(PaymentProviderContext<BankTransferSettings> context)
    {
        /*
        var url = string.IsNullOrWhiteSpace(context.Settings.ContinueUrl) ? context.Urls.ContinueUrl : context.Settings.ContinueUrl;
        _logger.LogInformation("BankTransfer GetContinueUrl order {OrderId} -> {Url}", context.Order.Id, url);
        */

        // Recupera il base URL da appsettings.json
        var baseUrl = _configuration["UmbracoCommerce:WebSiteUrl"]?.TrimEnd('/');
        if (string.IsNullOrEmpty(baseUrl))
        {
            _logger.LogError("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
            throw new InvalidOperationException("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
        }


        var url = string.IsNullOrWhiteSpace(context.Settings.ContinueUrl) ? context.Urls.ContinueUrl : context.Settings.ContinueUrl;

        url = baseUrl.EnsureEndsWith("/") + url;

        return url;
    }

    public override string GetCancelUrl(PaymentProviderContext<BankTransferSettings> context)
    {
        var url = string.IsNullOrWhiteSpace(context.Settings.CancelUrl) ? context.Urls.CancelUrl : context.Settings.CancelUrl;

        // Recupera il base URL da appsettings.json
        var baseUrl = _configuration["UmbracoCommerce:WebSiteUrl"]?.TrimEnd('/');
        if (string.IsNullOrEmpty(baseUrl))
        {
            _logger.LogError("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
            throw new InvalidOperationException("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
        }

        url = baseUrl.EnsureEndsWith("/") + url;

        return url;
    }

    public override string GetErrorUrl(PaymentProviderContext<BankTransferSettings> context)
    {
        var url = string.IsNullOrWhiteSpace(context.Settings.ErrorUrl) ? (context.Urls.ErrorUrl ?? context.Urls.CancelUrl) : context.Settings.ErrorUrl;

        // Recupera il base URL da appsettings.json
        var baseUrl = _configuration["UmbracoCommerce:WebSiteUrl"]?.TrimEnd('/');
        if (string.IsNullOrEmpty(baseUrl))
        {
            _logger.LogError("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
            throw new InvalidOperationException("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
        }

        url = baseUrl.EnsureEndsWith("/") + url;

        return url;
    }

    public override async Task<PaymentFormResult> GenerateFormAsync(PaymentProviderContext<BankTransferSettings> context, CancellationToken cancellationToken = default)
    {
        // Offline: non redirigiamo a gateway, manteniamo Pending e mandiamo alla pagina istruzioni

        // Recupera il base URL da appsettings.json
        var baseUrl = _configuration["UmbracoCommerce:WebSiteUrl"]?.TrimEnd('/');
        if (string.IsNullOrEmpty(baseUrl))
        {
            _logger.LogError("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
            throw new InvalidOperationException("UmbracoCommerce:WebSiteUrl non configurato in appsettings.json");
        }

        var continueUrl = GetContinueUrl(context);

        _logger.LogInformation("BankTransfer GenerateForm order {OrderId} -> {Url}", context.Order.Id, continueUrl);

        return await Task.FromResult(new PaymentFormResult
        {
            Form = new PaymentForm(continueUrl, PaymentFormMethod.Get)
        });
    }

    public override async Task<CallbackResult> ProcessCallbackAsync(PaymentProviderContext<BankTransferSettings> context, CancellationToken cancellationToken = default)
    {
        return await Task.FromResult(new CallbackResult
        {
            TransactionInfo = new TransactionInfo
            {
                TransactionId = context.Order.Id.ToString(),
                AmountAuthorized = context.Order.TotalPrice.Value,
                PaymentStatus = PaymentStatus.PendingExternalSystem
            }
        });
    }
}

}’

The problem is that i’m NEVER reaching the ProcessCallbackAsync, even if i tried to set the FinalizeAtContinueUrl to true.

Thank you

I would question why you would need a custom payment provider as the inbuilt Invoicing payment provider is essentially a pass-through payment provider (ie, it expects you to capture the payment externally). You could then use an OrderFinalizingNotification event handler to listen for orders finalizing and then sync them to your ERP (See here for docs on events Events | Umbraco Commerce)