Accept Payment In Cryptocurrency 💰 To Your Shopping Cart 🛒

Introduction

 
The popularity of Blockchain is because of its features, such as security, transparency, faster transaction settlement etc. And that is the obvious reason behind the trend of receiving payments in cryptocurrencies growing continuously. Many big firms have already started accepting payment in Bitcoin, including Overstock, KFC Canada, Microsoft, Reddit, Subway, Shopify and the list goes on. If you want to start with the same, then this article is for you. You can enable cryptocurrency payments in your shopping cart too.
 
There are several Payment Gateways that provide the ability to receive payments with cryptocurrencies. CoinPaymets is one of those and we'll use the same to integrate into our ASP.NET Core website. If you are interested in alternatives, find a list at Payment Gateways To Accept Cryptocurrencies.
 
 I separated this article into different sections. And at the end, we'll combine it all together. 
  1. CoinPaymets IPN
  2. Prepare a Website
  3. Configure Merchant Account
  4. Configure Buyer Wallet
  5. Combine All Together
So, let's get started with the first section.
 

CoinPaymets IPN

 
CoinPaymets provide different options to create a transaction request, such as API, IPN, Invoice Builder, and others. As we're supposed to integrate it with a shopping cart project, IPN (Instant Payment Notification) is the best suitable option for us. IPN is a messaging service that notifies the merchants of the events related to CoinPaymets transactions. It is useful to automate the administrative process in the back-end. 
 
 IPN Diagram 
 
As you can see in the diagram, a buyer will initiate a payment request from the website, then it will be redirected to the CoinPaymets interface where a user can choose a currency for the payment. Further, a request goes to the server and it will create a deposit address for a buyer. Once the deposit address is created, the buyer has to send the amount to the deposit address. On the other hand, IPN continuously monitors the deposit address for specific time duration and gives event updates to the merchant. By using event notification, a merchant can automate the process, such as order update, inventory update, and email to buyers, etc.
 

Prepare Website

 
In order to send the request to the payment server, we need to prepare a website. Here, we're going to use the ASP.NET Core framework to create our website. Our goal is not to prepare a production-ready site, therefore we'll create a website with static data.
 
Step 1
 
Open Visual Studio and create a new project, choose ASP.NET Core Web Application. Provide a project name and hit the Ok button. Choose a model-view-controller template and go ahead. Visual Studio will create a website for you.
 
Step 2
 
Create a new file named CoinPaymentsController in the Controllers/ directory. Also, add one method named Index as below.
  1. public class CoinPaymentsController : Controller  
  2. {      
  3.     public IActionResult Index()  
  4.     {  
  5.         return View();  
  6.     }  
  7. }  
Step 3
 
Create a View for Index method named Index.cshtml, and add the following content to the View file.
  1. <form asp-controller="CoinPayments" asp-action="ProcessCheckout" method="get">  
  2.     <div class="container">  
  3.         <div class="row">  
  4.             <div class="col-xs-6">  
  5.                 <br/>  
  6.                 <img src="~/images/book.png" style="border: 1px solid"/>  
  7.                 <b>Price: $5000</b>  
  8.                 <input type="submit" value="Add To Cart" class="btn-primary">  
  9.             </div>   
  10.         </div>  
  11.     </div>  
  12. </form>  
Step 4
 
Add a book image named book.png to the wwwroot/images directory. Here, I've used one ebook image from C# Corner.
 
Step 5
 
Add reference of the newly added method to the _Layout.cshtml page in the Views/Shared/ directory.
  1. <ul class="nav navbar-nav">  
  2.     <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>  
  3.     <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>  
  4.     <li><a asp-area="" asp-controller="Home" asp-action="Contact">Contact</a></li>  
  5.     <li><a asp-area="" asp-controller="CoinPayments" asp-action="Index">Coin Payments</a></li>  
  6. </ul>  
Here, we should see something like the screen below.
 
 
Step 6
 
Further, to pass the data to the next request, we will need a model. So, add a new model OrderModel.cs in the Models/directory, and add the following content to the file.
  1. public class OrderModel  
  2. {  
  3.     public string OrderId { getset; }  
  4.   
  5.     [DisplayName("Order Total")]  
  6.     public decimal OrderTotal { getset; }  
  7.   
  8.     [DisplayName("Product Name")]  
  9.     public string ProductName { getset; }  
  10.   
  11.     [DisplayName("First Name")]  
  12.     public string FirstName { getset; }  
  13.   
  14.     [DisplayName("Last Name")]  
  15.     public string LastName { getset; }  
  16.   
  17.     [DisplayName("Email Address")]  
  18.     public string Email { getset; }  
  19. }    
You can get the order information from DB server in your site, but here for simplicity, we'll pass static information. 
 
Step 7
 
We will pass the order information to the checkout page. To do that, add one new action method named ProcessCheckout to the CoinPaymentsController.
  1. [HttpGet]  
  2. public IActionResult ProcessCheckout()  
  3. {  
  4.     var model = new OrderModel  
  5.     {  
  6.         OrderId = Guid.NewGuid().ToString(),  
  7.         OrderTotal = 5000,  
  8.         ProductName = "Programming C# for Beginners",  
  9.         FirstName = "John",  
  10.         LastName = "Smith",  
  11.         Email = "[email protected]"  
  12.     };  
  13.     return View(model);  
  14. }  
Step 8
 
Add a new View named ProcessCheckout.cshtml in the Views/CoinPayments/ and add the following code into it.
  1. <h2>Checkout</h2>  
  2.   
  3. <form asp-controller="CoinPayments" asp-action="ProcessCheckout" method="post">  
  4.     <div class="container">  
  5.         <div class="row">  
  6.             <div class="col-xs-6">  
  7.                 <div>  
  8.                     <hr />  
  9.                     <dl class="dl-horizontal">  
  10.   
  11.                         @Html.HiddenFor(model => model.OrderId)  
  12.   
  13.                         <dt>  
  14.                             @Html.DisplayNameFor(model => model.OrderTotal)  
  15.                         </dt>  
  16.                         <dd>  
  17.                             [email protected](model => model.OrderTotal)  
  18.                             @Html.HiddenFor(model => model.OrderTotal)  
  19.                         </dd>  
  20.                         <dt>  
  21.                             @Html.DisplayNameFor(model => model.ProductName)  
  22.                         </dt>  
  23.                         <dd>  
  24.                             @Html.DisplayFor(model => model.ProductName)  
  25.                             @Html.HiddenFor(model => model.ProductName)  
  26.                         </dd>  
  27.                     </dl>  
  28.                     <hr />  
  29.                     <div style="text-align:center; font-weight:600">Billing Information</div>  
  30.                     <dl class="dl-horizontal">  
  31.                         <dt>  
  32.                             @Html.DisplayNameFor(model => model.FirstName)  
  33.                         </dt>  
  34.                         <dd>  
  35.                             @Html.DisplayFor(model => model.FirstName)  
  36.                             @Html.HiddenFor(model => model.FirstName)  
  37.                         </dd>  
  38.                         <dt>  
  39.                             @Html.DisplayNameFor(model => model.LastName)  
  40.                             @Html.HiddenFor(model => model.LastName)  
  41.                         </dt>  
  42.                         <dd>  
  43.                             @Html.DisplayFor(model => model.Email)  
  44.                             @Html.HiddenFor(model => model.Email)  
  45.                         </dd>  
  46.                     </dl>  
  47.                 </div>  
  48.                 <input type="image" src="https://www.coinpayments.net/images/pub/checkout-blue.png" alt="Checkout">  
  49.             </div>  
  50.         </div>  
  51.     </div>  
  52. </form>  
The second page is almost ready.
 
 
 
Step 9
 
Now, we're ready to redirect a user to the CoinPayments website. So, we'll prepare a redirect URL. It requires a merchant id and the secret. So, let's add that to the appsettings.json file.
  1. {  
  2.   "Logging": {  
  3.     "IncludeScopes"false,  
  4.     "LogLevel": {  
  5.       "Default""Warning"  
  6.     }  
  7.   },  
  8.   "CoinPayments": {  
  9.     "BaseUrl""https://www.coinpayments.net/index.php",  
  10.     "MerchantId""",  
  11.     "IpnSecret"""  
  12.   }  
  13. }  
To use CoinPayment IPN, it requires two things - Merchant Id and IPN Secret. We'll add values of both later after we've configured the merchant account.
 
Step 10
 
To access the configuration in our controller, we have to use IConfiguration interface, and to access HttpContext, we'll add IHttpContextAccessor interface as below.
  1. public class CoinPaymentsController : Controller  
  2. {  
  3.     private readonly IConfiguration _configuration;  
  4.     private readonly IHttpContextAccessor _httpContextAccessor;          
  5.   
  6.     public CoinPaymentsController(IConfiguration configuration,  
  7.         IHttpContextAccessor httpContextAccessor)  
  8.     {  
  9.         _configuration = configuration;  
  10.         _httpContextAccessor = httpContextAccessor;              
  11.     }  
  12. ...  
  13. ..  
Step 11
 
Add a new action method named ProcessCheckout for Payment Gateway redirection. 
  1. [HttpPost]  
  2. public void ProcessCheckout(OrderModel orderModel)  
  3. {  
  4.     var queryParameters = CreateQueryParameters(orderModel);  
  5.   
  6.     var baseUrl = _configuration.GetSection("CoinPayments")["BaseUrl"];  
  7.     var redirectUrl = QueryHelpers.AddQueryString(baseUrl, queryParameters);  
  8.   
  9.     _httpContextAccessor.HttpContext.Response.Redirect(redirectUrl);  
  10. }  
Things to notice -
  • We have used BaseUrl from the configuration we set in the appsettings.json file before. 
  • CreateQueryParameters is a private method which is preparing URL with required parameters.
Step 12
 
Add a private method CreateQueryParameters  into CoinPaymentsController
  1. private IDictionary<stringstring> CreateQueryParameters(OrderModel orderModel)  
  2. {  
  3.     //get store location  
  4.     var storeLocation = $"{Request.Scheme}://{Request.Host}{Request.PathBase}";  
  5.   
  6.     var queryParameters = new Dictionary<stringstring>()  
  7.     {  
  8.         //IPN settings  
  9.         ["ipn_version"] = "1.0",  
  10.         ["cmd"] = "_pay_auto",  
  11.         ["ipn_type"] = "simple",  
  12.         ["ipn_mode"] = "hmac",  
  13.         ["merchant"] = _configuration.GetSection("CoinPayments")["MerchantId"],  
  14.         ["allow_extra"] = "0",  
  15.         ["currency"] = "USD",  
  16.         ["amountf"] = orderModel.OrderTotal.ToString("N2"),  
  17.         ["item_name"] = orderModel.ProductName,  
  18.   
  19.         //IPN, success and cancel URL  
  20.         ["success_url"] = $"{storeLocation}/CoinPayments/SuccessHandler?orderNumber={orderModel.OrderId}",  
  21.         ["ipn_url"] = $"{storeLocation}/CoinPayments/IPNHandler",  
  22.         ["cancel_url"] = $"{storeLocation}/CoinPayments/CancelOrder",  
  23.   
  24.         //order identifier                  
  25.         ["custom"] = orderModel.OrderId,  
  26.   
  27.         //billing info  
  28.         ["first_name"] = orderModel.FirstName,  
  29.         ["last_name"] = orderModel.LastName,  
  30.         ["email"] = orderModel.Email,  
  31.   
  32.     };  
  33.     return queryParameters;  
  34. }  
Things that are important:
  • success_url
     
    Once the buyer pays the order amount, we have to redirect them to our website, for that we have to set success_url.
     
  • cancel_url
     
    In case the order gets canceled user will redirect to the cancel_url.
     
  • ipn_url
     
    CoinPayments will send event notifications on ipn_url, we have to listen and change the state of order accordingly.
     
  • ipn_mode
     
    IPN server response will be validated using HMAC(Hash-based Message Authentication Code), CoinPayments only support hmac as of now.
Step 13
 
To handle the response of success_url and cancel_url, we'll add two methods to the controller.
  1. public IActionResult SuccessHandler(string orderNumber)  
  2. {  
  3.     ViewBag.OrderNumber = orderNumber;  
  4.   
  5.     return View("PaymentResponse");  
  6. }  
  7.   
  8. public IActionResult CancelOrder()  
  9. {  
  10.     return View("PaymentResponse");  
  11. }    
Basically, our aim is to display success or cancel message to the buyer, for that add a new view file named PaymentResponse.cshtml under Views/CoinPayments/directory; and add the following code.
  1. <style>  
  2.     .response-text {  
  3.         text-align: center  
  4.     }  
  5. </style>  
  6.   
  7. @if (!string.IsNullOrEmpty(ViewBag.OrderNumber))  
  8. {  
  9.     <div class="response-text">  
  10.         <hr />  
  11.         <h2>Thank you</h2>  
  12.         <hr />  
  13.         Your order has been successfully processed! <br /> <br />  
  14.         ORDER NUMBER: @ViewBag.OrderNumber  
  15.     </div>  
  16. }  
  17. else  
  18. {  
  19.     <div class="response-text">  
  20.         <hr />  
  21.         <h2>Sorry, your order has been cancelled</h2>   
  22.     </div>  
  23. }   
Step 14
 
To receive event notification from IPN, add another method, IPNHandler, to the controller. 
  1. [HttpPost]  
  2. public IActionResult IPNHandler()  
  3. {  
  4.     byte[] parameters;  
  5.     using (var stream = new MemoryStream())  
  6.     {  
  7.         Request.Body.CopyTo(stream);  
  8.         parameters = stream.ToArray();  
  9.     }  
  10.     var strRequest = Encoding.ASCII.GetString(parameters);  
  11.     var ipnSecret = _configuration.GetSection("CoinPayments")["IpnSecret"];  
  12.   
  13.     if (Helper.VerifyIpnResponse(strRequest, Request.Headers["hmac"], ipnSecret,  
  14.         out Dictionary<stringstring> values))  
  15.     {  
  16.         values.TryGetValue("first_name"out string firstName);  
  17.         values.TryGetValue("last_name"out string lastName);  
  18.         values.TryGetValue("email"out string email);  
  19.         values.TryGetValue("amount1"out string amount1);  
  20.         values.TryGetValue("subtotal"out string subtotal);  
  21.         values.TryGetValue("status"out string status);  
  22.         values.TryGetValue("status_text"out string statusText);  
  23.   
  24.         var newPaymentStatus = Helper.GetPaymentStatus(status, statusText);  
  25.   
  26.         switch (newPaymentStatus)  
  27.         {  
  28.             case PaymentStatus.Pending:  
  29.                 {  
  30.                     //TODO: update order status and use logging mechanism
  31.                     //order is pending                      
  32.                 }  
  33.                 break;  
  34.             case PaymentStatus.Authorized:  
  35.                 {  
  36.                     //order is authorized                      
  37.                 }  
  38.                 break;  
  39.             case PaymentStatus.Paid:  
  40.                 {  
  41.                     //order is paid                      
  42.                 }  
  43.                 break;  
  44.             default:  
  45.                 break;  
  46.         }  
  47.     }  
  48.     else  
  49.     {                                  
  50.         //Issue Occurred with CoinPayments IPN  
  51.     }  
  52.   
  53.     //nothing should be rendered to visitor  
  54.     return Content("");  
  55. }  
Here, we've used methods of the Helper class, but we haven't any helper class yet. So, let's add that.
 
Step 15
 
Add one new class named Helper.cs with three different methods: VerifyIpnResponse, HashHmac, and  GetPaymentStatus as follows,
  1. public static class Helper  
  2. {  
  3.     public static bool VerifyIpnResponse(string formString, string hmac, string ipnSecret, out Dictionary<stringstring> values)  
  4.     {  
  5.         values = new Dictionary<stringstring>(StringComparer.OrdinalIgnoreCase);  
  6.         foreach (var l in formString.Split('&'))  
  7.         {  
  8.             var line = l.Trim();  
  9.             var equalPox = line.IndexOf('=');  
  10.             if (equalPox >= 0)  
  11.                 values.Add(line.Substring(0, equalPox), line.Substring(equalPox + 1));  
  12.         }  
  13.   
  14.         values.TryGetValue("merchant"out string merchant);  
  15.   
  16.         //verify hmac with secret  
  17.         if (!string.IsNullOrEmpty(merchant) && !string.IsNullOrEmpty(hmac))  
  18.         {  
  19.             if (hmac.Trim() == HashHmac(formString, ipnSecret))  
  20.             {  
  21.                 return true;  
  22.             }  
  23.             return false;  
  24.         }  
  25.         return false;  
  26.     }  
  27.   
  28.     public static string HashHmac(string message, string secret)  
  29.     {  
  30.         Encoding encoding = Encoding.UTF8;  
  31.         using (HMACSHA512 hmac = new HMACSHA512(encoding.GetBytes(secret)))  
  32.         {  
  33.             var msg = encoding.GetBytes(message);  
  34.             var hash = hmac.ComputeHash(msg);  
  35.             return BitConverter.ToString(hash).ToLower().Replace("-"string.Empty);  
  36.         }  
  37.     }  
  38.   
  39.     public static PaymentStatus GetPaymentStatus(string paymentStatus, string pendingReason)  
  40.     {  
  41.         var result = PaymentStatus.Pending;  
  42.   
  43.         int status = Convert.ToInt32(paymentStatus);  
  44.   
  45.         switch (status)  
  46.         {  
  47.             case 0:  
  48.                 result = PaymentStatus.Pending;  
  49.                 break;  
  50.             case 1:  
  51.                 result = PaymentStatus.Authorized;  
  52.                 break;  
  53.             case -1:         
  54.                 result = PaymentStatus.Cancelled;  
  55.                 break;  
  56.             case 100:  
  57.                 result = PaymentStatus.Paid;  
  58.                 break;  
  59.             default:  
  60.                 break;  
  61.         }  
  62.         return result;  
  63.     }  
  64. }  
Also, add enum PaymentStatus to handle payment status,
  1. public enum PaymentStatus  
  2. {  
  3.     Pending = 10,          
  4.   
  5.     Authorized = 20,  
  6.       
  7.     Paid = 30,  
  8.       
  9.     Cancelled = 50,  
  10. }  
Here, when a response comes from the IPN server, we'll check if it is valid or not. To validate it, we'll use the header hmac parameter, and compare it to the hash code which will be generated using IPN secret. If both matches, we'll fetch the new status of payment and use it wherever needed.
 
You can skip all the above steps of creating a website, just download the demo project code from attachment.
 

Configure Merchant Account

 
Step 1
 
Goto the CoinPayments site and register your self.
 
Step 2
 
You'll get an email for verification. Once you get verified, log in to the account, you will see the screen as down below. Select the Merchant option from Account Wizard.
 
 Select Merchant Account
 
Step 3 
 
In the next step, it will ask you to enable coins, from which you'd expect to receive payments from buyers. Select the option "Only coins I select".
 
 Account Setup Wizard
 
Step 4
 
Here, you need to select coins. Currently, I'm selecting Bitcoin, Ether, NEO, Stratis, and Litecoin Testnet. As fiat currency payment gateways have a sandbox environment for the testing payment system, CoinPayments has one separate coin called Litecoin Testnet, with the use of that coin you can test your payment request. We'll use the same for testing payment later in this article.
 
 Update Preferences
 
Once you select coins, click on the Update coin preferences button, and it will display the message of the update.
 
Step 5
 
Your merchant account is configured! Further, go to the home tab from the upper menu.
 
 recent transaction
 
Step 6
 
In order to get the merchant id and secret, go to the account > account settings. Under the Basic settings tab, you will see merchant Id.
 
 Account Settings
 
Merchant Id
 
Step 7
 
Under the Merchant settings tab, you need to add IPN secret, that is used to verify that IPN sender. Also, update your mail preferences.
 
IPN Secret
 
We're done with merchant account confutation.
 

Configure Buyer Wallet

 
As we need a bank account to transfer money from one account to another account, in Blockchain you need to have a wallet. So, in our case, for testing, we need to configure a wallet for a buyer.
 
We'll configure it with testnet (test network), there are different kinds of networks available with almost all Blockchains, testnet is used for testing purpose. As we discussed before, Litecoin Testnet(LTCT) is our testing coin, hence we'll configure it with the wallet.
 
Step 1
 
Download Litecoin wallet from the website here: https://litecoin.org/ 
 
Step 2
 
Install and create a desktop shortcut, open properties, and add -testnet at the end of the target location. Target should look like:
"c:\Program Files (x86)\Litecoin\litecoin-qt.exe" -testnet OR
"c:\Program Files\Litecoin\litecoin-qt.exe" -testnet
 
 Litecoin testnet mode
 
Step 3
 
Open the wallet, you will see the screen as below. It will sync all the blocks from the start, hence it will take time to sync fully.
 
 Litecoin testnet syncing process
 
Step 4
 
Once it got synced, the wallet will appear as in the below screen. You can see your balance in recent transaction history under the overview tab. 
 
 
 
Step 5
 
As we don't have any coin amount, we need to load balance into our wallet first. CoinPayments providing 10 LTCT coins on one request. To receive coins open this link: https://www.coinpayments.net/login?return=acct_req_ltct 
 
After a while, check your coin balance into CoinPayments account.
 
 
 
Step 6
 
You received LTCT coins in your CoinPayments account, however, to pay coins to the gateway, we should transfer those coins to our Litecoin testnet wallet.
 
To transfer click on "view all balances", Goto LTCT, which is listed last in the list, where you can find an option for Send/Withdraw under "LTCT Option", provide the amount that you would like to transfer, and the wallet address in which you suppose to transfer coins. In the Litecoin wallet, there is an option of the payment request, which will give one address, and that address needs to pass on CoinPayments.
 
 
 
Step 7
 
Once the transaction is initiated, the gateway will send you a mail on your registered email address to verify that the event happened from you. After clicking on the acceptance link that is received in the mail, your withdrawal will be confirmed, and you will receive the coins in your wallet in a short period of time.
 
Step 8
 
Check the updated balance in your wallet.
 
 Amount received in wallet
 
And we've successfully configured the buyer wallet.
 

Combine All Together

 
Let's combine all together! First, copy the merchant id and the secret to the appsettings.json file. Keep your fingers crossed, we're going to run the flow from the first.
 
 
In this article, we have gone through the steps to integrate CoinPayments with ASP.NET Core website. First, we've prepared one website, then we have configured CoinPayments merchant account, and to send coins to the merchant, we've configured the buyer wallet.


Similar Articles