npm package: @nait-aits/moneris
Description: ```console // with npm npm install @nait-aits/moneris ```
Author: NAIT AITS
Latest version: 0.0.2

## Installation

```console
// with npm
npm install @nait-aits/moneris
```

## What is this?

This package contains a control(MonerisChecoutPanel) that will help using moneris for payment. You can
include this component on your page and it will make it easier to integrate into your application.

There are a few things that make using moneris a bit difficult when using react/typescript, so hopefully
this simplifies things a bit.

## Usage

```typescript
<MonerisChecoutPanel<{data: number}>
  createTransactionData={{
    someData: {order: 123},
  }}
  createTransactionUrl={'https://urlToCreateTransaction'}
  processPaymentUrl={'https://urlToProcessAfterPayment'}
  cancelTransactionUrl={'https://urlForCancelledTransaction'}
  getAuthToken={promiseToGetToken}
  onComplete={paymentData => {
    //Handle what the app should do when everything is done
    //payment data is the type of the generic for the panel
    console.log('onComplete called', paymentData);
  }}
  onCancel={() => {
    //Handle what the app should do when user cancels
    console.log('onCancel called');
    
  }}
  onError={error => {
    //Handle what the app should do when moneris errors
    console.log('onError called');
  }}
  activated={trueIfYouWantToActivate}
/>
```

## Order things happen
1. User Clicks Pay Button
    * set activated parameter to true
2. createTransactionUrl is called, with the createTransactionData data posted
    * Server creates moneris ticket and returns moneris ticket to the client
3. Client pays via moneris interface
4. processPaymentUrl is called with the ticket as a parameter
    * server verifies ticket is paid and then processes
5. onComplete is called

## Server side notes

* There is code at the end for the MonerisRepository

### Sample C# code
```c#
[Authorize]
[HttpPost("CreateOrderTransaction")]
public async Task<object> CreateOrderTransaction([FromForm] List<string> slugs)
{
   ...

    var totals = GetPaymentValues(slugs);

    var transactionId = Guid.NewGuid();

    //create moneris ticket for order (Due to the Kount verification, orderID cannot exceed 32 digits so we need to remove the dashes from the guid)
    var ticket = await _monerisRepo.CreateOrder(totals.Total, transactionId.ToString().Replace("-",""), cartItems);

    ...

    //create some record somewhere to make a note of this transaction that is pending

    //You MUST return an obejct with the monerisTicket property
    return new
    {
        monerisTicket = ticket
    };
}

[Authorize]
[HttpPost("CancelOrderTransaction")]
public async Task<object> CancelOrderTransaction([FromForm] string monerisTicket)
{
    ... 

    //do any cleanup you need to do
    ...

    return new
    {
        ok = true
    };
}

[Authorize]
[HttpPost("ProcessPayment")]
public async Task<object> ProcessPayment([FromForm] string monerisTicket)
{
    var user = CurrentUser;

    //load the transaction

    //get the recept from moneris
    var receiptString = await _monerisRepo.GetPaymentReceipt(monerisTicket);
    transaction.ReceiptJsonData = receiptString;

    //Get the receipt
    var receipt = _monerisRepo.GetReceiptResponse(receiptString);

    if (receipt.success != "true" || receipt.receipt.result != "a")
    {
        if ( receipt.success != "true")
        {
           //The payment was NOT successful
        }

        if( receipt.receipt.result == "d")
        {
            //credit card was declined
        }
    }
    
    //make sure to validate that it is correct amount was paid vs what you were expecting
    //receipt.request.cc_total is the total from the payment

    //payment completed, so do what you need to do now

    //return the obejct that matches the generic type of the control.
    return new
    {
        orderId = newOrder.Id,
        transactionId = transaction.Id,
        subTotal = transaction.Subtotal,
        taxes = transaction.Taxes,
        total = transaction.Total,
        monerisTicket = monerisTicket
    };
}
```
## Pages/notes

## Server Side Code
You must use .NET Core as it requires IHttpClientFactory 
(this is injected in Startup.cs)
  * services.AddHttpClient();

### Moneris Documentation
https://developer.moneris.com:3000/livedemo/checkout/overview/guide/dotnet

### Moneris Test Card Numbers
https://developer.moneris.com/More/Testing/Testing%20a%20Solution

### Moneris development merchant setup
Login info is also displayed on this page

https://esqa.moneris.com/mpg/



### public/index.html
You need to make sure that you include the link to the Moneris library.

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    ...
    <script src="https://gatewayt.moneris.com/chkt/js/chkt_v1.00.js"></script>
    ...
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  </body>
</html>
```

## C# MonerisRepository Library
```c#

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading.Tasks;

namespace YourNameSpace.Repositories.Moneris
{
    public class MonerisRepository
    {
        private IHttpClientFactory _httpClientFactory;
        private string _baseUrl;
        private string _storeId;
        private string _token;
        private string _checkoutId;

        public MonerisRepository(string baseUrl, string storeId, string token, string checkoutId, IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
            _baseUrl = baseUrl;

            _storeId = storeId;
            _token = token;
            _checkoutId = checkoutId;
        }

        public async Task<string> CreateOrder(decimal amountDue, string orderId, List<MonerisPreloadRequest.CartItem> cartItems = null)
        {
            MonerisPreloadRequest orderData = new MonerisPreloadRequest()
            {
                store_id = _storeId,
                api_token = _token,
                checkout_id = _checkoutId,
                txn_total = String.Format("{0:0.00}", amountDue),
                environment = "qa",
                action = "preload",
                order_no = orderId
            };

            if (cartItems != null && cartItems.Count > 0)
            {
                orderData.cart.items = cartItems;
            }

            var httpClient = _httpClientFactory.CreateClient();

            HttpResponseMessage res = await httpClient.PostAsJsonAsync($"{_baseUrl}/chkt/request/request.php", orderData);

            if (res.IsSuccessStatusCode && res.StatusCode != System.Net.HttpStatusCode.NoContent)
            {
                var response = await res.Content.ReadFromJsonAsync<MonerisPreloadResponse>();

                if (response.response.success == "true")
                {
                    return response.response.ticket;
                }

                throw new Exception(response.response.error.ToString());
            }
            else
            {
                throw new HttpRequestException("Error Creating Ticket from Moneris", inner: null, statusCode: res.StatusCode);
            }
        }

        //we need this as a string as if the object returned isnt as expected we still need to save
        //it to our database or something
        public async Task<string> GetPaymentReceipt(string monerisTicket)
        {
            MonerisReceiptRequest requestData = new MonerisReceiptRequest()
            {
                store_id = _storeId,
                api_token = _token,
                checkout_id = _checkoutId,
                environment = "qa",
                action = "receipt",
                ticket = monerisTicket
            };

            var httpClient = _httpClientFactory.CreateClient();

            HttpResponseMessage res = await httpClient.PostAsJsonAsync($"{_baseUrl}/chkt/request/request.php", requestData);

            if (res.IsSuccessStatusCode && res.StatusCode != System.Net.HttpStatusCode.NoContent)
            {
                var resultText = await res.Content.ReadAsStringAsync();

                return resultText;
            }
            else
            {
                throw new HttpRequestException("Error Retrieving Receipt Request from Moneris", inner: null, statusCode: res.StatusCode);
            }
        }

        public ReceiptResponseItem GetReceiptResponse(string responseContent)
        {
            try
            {
                var response = JsonSerializer.Deserialize<MonerisReceiptResponse>(responseContent);

                if (responseContent == null)
                {
                    throw new Exception("Unable to parse MonerisReceiptResponse object from stream");
                }

                return response.response;

            }
            catch (Exception ex)
            {
                throw new Exception("Unable to parse response JSON");
            }
        }
    }

    public class MonerisReceiptRequest
    {
        public string store_id { get; set; }
        public string api_token { get; set; }
        public string checkout_id { get; set; }
        public string environment { get; set; }
        public string action { get; set; }
        public string ticket { get; set; }
    }

    public class MonerisPreloadRequest
    {
        public string store_id { get; set; }
        public string api_token { get; set; }
        public string checkout_id { get; set; }
        public string txn_total { get; set; }
        public string environment { get; set; }
        public string action { get; set; }
        public string order_no { get; set; }
        public Cart cart { get; set; } = new Cart();

        public class Cart
        {
            public List<CartItem> items { get; set; } = new List<CartItem>();
        }

        public class CartItem
        {
            public string url { get; set; }
            public string description { get; set; }
            public string product_code { get; set; }
            public decimal unit_cost { get; set; }
            public int quantity { get; set; }
        }
    }

    public class PreloadResponseItem
    {
        public string success { get; set; }
        public string ticket { get; set; }
        public object error { get; set; }
    }

    public class MonerisPreloadResponse
    {
        public PreloadResponseItem response { get; set; }
    }
}
```