πŸ’³ Automating Invoice Payment in Dataverse with Azure Durable Functions

 

In enterprise solutions, payment workflows often involve complex steps, third-party interactions, and the need for robust orchestration. In a recent article, Temmy Raharjo illustrates how Azure Durable Functions can streamline a Dataverse-integrated payment process, using a well-structured invoice payment scenario.

🎯 Problem Statement

When dealing with external systems (like payment gateways), we often rely on plugins or Power Automate flows within Dynamics 365 or Dataverse. However, these come with limitations:

  • Plugin timeouts (maximum 2 minutes)

  • Complex retry and exception handling

  • Limited orchestration capabilities

✅ The Solution: Azure Durable Functions

Durable Functions are an extension of Azure Functions that enable stateful orchestrations. Temmy breaks down how this model can be used to coordinate a multistep invoice payment process triggered from Dataverse.

πŸ‘‡ Key Steps Involved:

  1. User clicks "Send Payment" in Dynamics (Dataverse model-driven app)

  2. Custom API is triggered to send a message to Azure Service Bus Queue

  3. A listener picks up the message and triggers the Durable Orchestration

  4. The orchestration performs:

    • Payment Initiation via HTTP call

    • Waits for external callback (WaitForExternalEvent)

    • Updates payment status in Dataverse

🧩 Technical Flow Summary:

  • Custom API (Dataverse) sends invoiceid and sessionid to Service Bus

  • Azure Function (Queue Trigger) listens for the Service Bus message

  • Orchestrator Function is invoked, which:

    • Calls an activity to initiate payment

    • Uses context.WaitForExternalEvent("PaymentCallback")

    • Proceeds after receiving a webhook callback with the session ID

    • Updates the payment status accordingly

🧠 Why Session ID Matters
The Session ID is a unique identifier passed through the process to correlate the initial orchestration with the external callback. It ensures the correct Durable Function instance is resumed when the external payment gateway calls back with the result.


πŸ“Œ Key Benefits of This Approach:

  • Handles long-running external interactions safely

  • Eliminates plugin timeout issues

  • Enables better exception handling and retries

  • Decouples systems using messaging patterns (Service Bus)

πŸ“· Visual Diagram

The solution includes a helpful flowchart showing the process from user action to final payment update (attached above ☝️).




πŸ›‘ Retry Mechanism with Durable Functions

Real-world integrations, especially with external systems like payment gateways or APIs, are prone to temporary failures—timeouts, transient network issues, or throttling. Azure Durable Functions make it easy to introduce robust retry logic without complicating your orchestration code.

πŸ” How Retry Works in Durable Functions

Durable Functions allow you to wrap your Activity Functions with a built-in retry policy using the RetryOptions class. This means if an external API call or payment request fails, the orchestration can automatically retry the operation a specified number of times with a defined delay.

✅ Example: Retrying Payment Initiation

Let’s say you're calling an external payment API from an Activity Function:

csharp

var retryOptions = new RetryOptions( firstRetryInterval: TimeSpan.FromSeconds(10), maxNumberOfAttempts: 3) { Handle = ex => ex is TimeoutException || ex is HttpRequestException }; var result = await context.CallActivityWithRetryAsync<string>( "InitiatePaymentActivity", retryOptions, inputPayload);
  • firstRetryInterval – wait 10 seconds before the first retry

  • maxNumberOfAttempts – retry up to 3 times

  • Handle – conditionally retry only for specific exception types

This ensures resilience without writing complex retry loops or state-tracking code.

✅ Where to Apply Retry in Our Scenario

In both of the blogs discussed:

  1. Invoice Payment (Blog 1)

    • Use retry on: HTTP call to the external payment API

    • Benefit: Ensures the payment process isn't marked failed due to temporary connectivity issues

  2. Service Bus Integration (Blog 2)

    • Use retry on: Activity that updates Dataverse or calls external systems

    • Benefit: Reliable end-to-end message processing even if Dataverse API fails momentarily

⏱ Best Practices

  • Don’t retry on known business logic errors (e.g., payment declined)

  • Log all retry attempts for audit and diagnostics

  • Use exponential backoff for high-traffic systems

Comments

Popular posts from this blog

πŸ€– Copilot vs Microsoft Copilot vs Copilot Studio: What’s the Difference?

Automating Unique Number Generation in Dynamics 365 Using Plugins

In-Process vs Isolated Process Azure Functions: What’s the Difference?