COGWare.OzowRefunds 1.0.2

dotnet add package COGWare.OzowRefunds --version 1.0.2                
NuGet\Install-Package COGWare.OzowRefunds -Version 1.0.2                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="COGWare.OzowRefunds" Version="1.0.2" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add COGWare.OzowRefunds --version 1.0.2                
#r "nuget: COGWare.OzowRefunds, 1.0.2"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install COGWare.OzowRefunds as a Cake Addin
#addin nuget:?package=COGWare.OzowRefunds&version=1.0.2

// Install COGWare.OzowRefunds as a Cake Tool
#tool nuget:?package=COGWare.OzowRefunds&version=1.0.2                

Ozow (instant EFT) Transaction Refunds

Process refunds for transactions submitted via the Ozow Instant EFT payment gateway

Ozow Refunds: Worth Noting

  • Before a merchant can process refunds, they must transfer funds into their Ozow refund float.
  • When a refund is created on the Ozow platform, it is not processed in real-time. Ozow batches refund requests and submits to the banks. The banks also take some time to process the refund into the customer's account.
  • Ozow does provide a means to query refunds, however, the NotifyUrl (included in the refund request object) is the best means of receiving the refund status notification.
  • For convenience, this package includes a helper method (RefundHelper.ParseRefundNotification) to read the request body of the Ozow refund notification and return a strongly-typed object (RefundNotification).

Getting Started

Install this NuGet package and the Microsoft.Extensions.Logging.Abstractions package into your .NET Core 6 or .NET Framework 4.8 application.

Package Manager:
Install-Package COGWare.OzowRefunds -Version <version> Install-Package Microsoft.Extensions.Logging.Abstractions

CLI:
dotnet add package --version <version> COGWare.OzowRefunds dotnet add package Microsoft.Extensions.Logging.Abstractions

Add Config

Add the OzowRefunds.IsProduction key to your settings file, eg:
<add key="OzowRefunds.IsProduction" value="false" />

Usage

Submit an Ozow Refund (With logging):

    // Create your logger from the logging framework's extensions package (in this case, NLog)
    var logger = LoggerFactory.Create(builder => builder.AddNLog()).CreateLogger<Program>();

    logger.LogInformation("starting");

    try {
        RefundResult? refundResult = await OzowRefunds.RefundHelper.ProcessRefund(
            // An instance of the RefundConfig object containing details of the
            // Ozow entity that the original transaction was processed against.
            // These details are available in the Ozow Merchant Console. 
            new RefundConfig() {
                // Your merchant API key.
                ApiKey = "123xyz",
                // The merchant site code.
                OzowSiteCode = "123-XYZ-456",
                // Your merchant API secret.
                PrivateKey = "xyz123"
            },
            // An instance of a Refund object representing the refund to be processed.
            new Refund() {
                // The amount to be refunded.
                Amount = 1,
                // Your refund identifier.
                Id = "1",
                // Ozow can post the status of the refund to this URL once the
                // refund has been processed by the bank.
                NotifyUrl = "https://yournotification.url",
                // This will appear as the reference on the customer's bank statement.
                RefundReason = "<Merchant> refund",
                // The original (Ozow) transactionId of the payment.
                TransactionId = "987qwe654rty321uio"
            });

        // Checks the status of the refund against the API response codes to 
        // determine if the refund can be considered successful.
        if(refundResult!.IsSuccessful) {
            // Update the status of the refund in your refund system.
            Console.WriteLine("Refund processed successfully! " +
                "Your refund identifier: '" + refundResult.Id + "', " +
                "Ozow refund reference: '" + refundResult.RefundId + "'");
        } else {
            // Update the status of the refund in your refund system, optionally including the
            // errors for analysis and manual resolution (if necessary).
            Console.WriteLine("Refund ('" + refundResult.Id + "') failed: " +
                String.Join(";", refundResult.Errors!));
        }
    } catch(Exception ex) {
        Console.WriteLine("ERROR: " + ex.GetBaseException().Message);
    }

    logger.LogInformation("complete");

Submit an Ozow Refund (without logging):

Note: Even though you have opted not log, your project will still require a reference to Microsoft.Extensions.Logging.Abstractions.

    try {
        RefundResult? refundResult = await OzowRefunds.RefundHelper.ProcessRefund(
            // An instance of the RefundConfig object containing details of the
            // Ozow entity that the original transaction was processed against.
            // These details are available in the Ozow Merchant Console. 
            new RefundConfig() {
                // Your merchant API key.
                ApiKey = "123xyz",
                // The merchant site code.
                OzowSiteCode = "123-XYZ-456",
                // Your merchant API secret.
                PrivateKey = "xyz123"
            },
            // An instance of a Refund object representing the refund to be processed.
            new Refund() {
                // The amount to be refunded.
                Amount = 1,
                // Your refund identifier.
                Id = "1",
                // Ozow can post the status of the refund to this URL once the
                // refund has been processed by the bank.
                NotifyUrl = "https://yournotification.url",
                // This will appear as the reference on the customer's bank statement.
                RefundReason = "<Merchant> refund",
                // The original (Ozow) transactionId of the payment.
                TransactionId = "987qwe654rty321uio"
            });

        // Checks the status of the refund against the API response codes to 
        // determine if the refund can be considered successful.
        if(refundResult!.IsSuccessful) {
            // Update the status of the refund in your refund system.
            Console.WriteLine("Refund processed successfully! " +
                "Your refund identifier: '" + refundResult.Id + "', " +
                "Ozow refund reference: '" + refundResult.RefundId + "'");
        } else {
            // Update the status of the refund in your refund system, optionally including the
            // errors for analysis and manual resolution (if necessary).
            Console.WriteLine("Refund ('" + refundResult.Id + "') failed: " +
                String.Join(";", refundResult.Errors!));
        }
    } catch(Exception ex) {
        Console.WriteLine(ex.GetBaseException().Message);
    }

Response object:

RefundResult

An object representing the response from the Ozow API.

  • Id: (String) - Your internal refund identifier.
  • RefundId: (String) - The Ozow refund transaction identifer for this specific refund (null if there were errors).
  • TransactionId: (String) - The original Ozow payment transaction identifier - a refund will be processed against this transaction.
  • Amount: (Double) - The amount to be refunded.
  • Errors: (List<String>) - An collection of errors returned when attempting to create the refund.
  • IsSuccessful: (Boolean) - A flag indicating if the refund was successfully added.

Ozow Refund Notification:

When Ozow receives a refund status update from the bank, their services will attempt to send a status update notification to the NotifyUrl specified in the refund request. They will attempt this a few times - in case of errors.

Deserializing the Refund Notification

Pass the HttpRequest object to the helper method and stand back in awe and wonder 😛 as a strongly-typed object is returned:

    [HttpPost]
    [Route("Notify")]
    public async Task<OzowRefunds.Models.RefundNotification> Notify() {
        return await OzowRefunds.RefundHelper.ParseRefundNotification(this.Request);
    }
RefundNotification
  • RefundId: (String) - The Ozow refund transaction identifer for this specific refund.
  • TransactionId: (String) - The original Ozow payment transaction identifier - a refund will be processed against this transaction.
  • CurrencyCode: (String) - The refund currency.
  • Amount: (Double) - The amount that was refunded.
  • Status: (Enum OzowRefundStatus) - An enum of statuses for the refund.
  • Pending
  • Complete
  • Submitted
  • Failed
  • Cancelled
  • Returned
  • StatusDescription: (String) - A description to help clarify the meaning of the Status enum.
  • StatusMessage: (String) - Message regarding the status of the refund. This field will not always have a value.
  • BankName: (String) - Name of the bank that processed the refund to the customer.
  • AccountNumber: (String) - Masked account number that the refund was paid into.
  • Hash: (String) - SHA512 hash used to ensure that certain fields in the message have not been altered after the hash was generated.

Alternatively, use something similar to this:

    NameValueCollection ozowInput = HttpUtility.ParseQueryString(<request body goes here>); 

    string accountNumber = ozowInput["AccountNumber"];
    double amount = Double.Parse(ozowInput["Amount"]);
    string bankName = ozowInput["BankName"];
    string currencyCode = ozowInput["CurrencyCode"];
    string hash = ozowInput["Hash"];
    string refundId = ozowInput["RefundId"];
    string status = ozowInput["Status"];
    string statusMessage = ozowInput["StatusMessage"];
    string transactionId = ozowInput["TransactionId"];
                           

Check Refunds

Ozow provides the ability to query refunds by date submitted and status.
This package wraps this functionality and provides a list of refund objects for a given date and status (again, with optional logging):

     List<RefundListResult> refundListResult = await OzowRefunds.RefundHelper.GetRefunds(
            // An instance of the RefundConfig object containing details of the
            // Ozow entity that you wish to query for submitted refunds.
            // These details are available in the Ozow Merchant Console. 
            new RefundConfig() {                    
                // Your merchant API key.
                ApiKey = "123xyz",
                // The merchant site code.
                OzowSiteCode = "123-XYZ-456",
                // Your merchant API secret.
                PrivateKey = "xyz123"
            }, 
            // Date the refund was submitted to Ozow.
            DateTime.Now.AddDays(-7), 
            // Refunds in this status will be returned.
            OzowRefunds.RefundHelper.OzowRefundStatus.Complete, 
            // (optional) logger
            logger);
RefundListResult
  • Id: (String) - The identifier for the refund.
  • CreatedDate: (DateTime) - The date the refund was created.
  • CreatedDateUtc: (DateTime) - The UTC date the refund was created.
  • MerchantCode: (String) - Corresponding merchant code of the refund.
  • SiteCode: (String) - Corresponding site code of the refund.
  • SiteName: (String) - Corresponding site name of the refund.
  • BankToName: (String) - Bank the refund was sent to.
  • CurrencyCode: (String) - Refund currency.
  • Amount: (Double) - Refund amount.
  • StatementReference: (String) - Bank reference of the refund for the payer.
  • Status: (String) - Refund status.
  • ToAccount: (String) - Masked account number the received the refund.
  • PaymentDate: (String) - Payment date of the refund.
  • TransactionReference: (String) - Reference of the transaction which was refunded.
  • Customer: (String) - Refunded transaction customer.
  • LastEvent: (String) - Most recent log activity of the refund.
  • CreatedBy: (String) - Name of the user who created the refund.

Logging:

Implements Microsoft.Extensions.Logging, so log away with any compatible logging framework,eg: NLog

Additional documentation

Feedback

I welcome comments, suggestions, feature requests and even honest criticism 😃

Want to show your appreciation?

That's mighty generous - thank you!
Buy me a coffee

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Framework net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.2 222 5/28/2023
1.0.1 162 5/19/2023
1.0.0 167 5/12/2023

Added exception handling for missing config, support for .NET 4.8, basic metrics to determine usage.