Handling #Stripe #webhooks in asp.net for #SCA

service_512

Stripe Webhooks are going to become very important on September 14th 2019, because that is when new EU regulations come into effect for SCA (Strong Customer Authentication). This is where, if you are handling an off-session card transaction, then the you may need to engage the user to authorise the payment.

Webhooks are HTTP endpoints that are called on certain (or all) events triggered by Stripe, here is a simple example of a webhook handler written in C#, that will send an email with the data from Stripe to you. In the future, I will develop this further to actually handle the SCA transaction.

So, lets get started, with a simple ASHX file as follows;

<%@ WebHandler Language=C# Class=StripeWebhook.handler %>
using System;
using System.Web;
using System.IO;

namespace StripeWebhook
{
    public class handler : System.Web.IHttpHandler
    {
        public void ProcessRequest (HttpContext context)
        {
            context.Response.Write(OK);
            var poststring = new StreamReader(context.Request.InputStream).ReadToEnd(); 
            Email.Send(you@gmail.com,Received a Stripe Webhook call, poststring);
        }
    
        public bool IsReusable {
            get {
                return false;
            }
        }
    }
}

Now, the Email.Send is pretty boiler plate, and I’m leaving out my credentials with Amazon SES.

using System;
using System.Configuration;
using System.Net.Mail;
using System.Text;

namespace StripeWebhook
{

    public class Email
    {
        private static string FromAddress = “info@you.net”;
        private static string MailServer = “email-smtp.eu-west-1.amazonaws.com”;
        private static string Username = “*****”;
        private static string Password = “****/****”;

        /// <summary>
        /// Sends the specified recipient with reply to
        /// </summary>
        /// <param name=recipient>The recipient.</param>
        /// <param name=subject>The subject.</param>
        /// <param name=body>The body.</param>
        public static void Send(string recipient, string subject, string body)
        {
            var mail = new MailMessage(FromAddress, recipient);
            var client = new SmtpClient
            {
                Port = 25,
                EnableSsl = true,
                DeliveryMethod = SmtpDeliveryMethod.Network,
                UseDefaultCredentials = false,
                Host = MailServer,
                Credentials = new System.Net.NetworkCredential(
                    Username,
                    Password)
            };
            mail.Subject = subject;
            mail.Body = body;
            mail.IsBodyHtml = true;
            mail.BodyEncoding = Encoding.UTF8;
            client.Send(mail);
        }
    }
}

This, I then deploy to an IIS server, at say https://www.yourwebsite.com/stripe.ashx , and check it works by just opening the page in a browser.

I then go into Stripe, Switch to Test mode, then click Developers > Webhooks, then Add endpoint. Enter the url, of your handler, then click on “Receive all events”. then “Add Endpoint”

Make a test transaction against stripe with a test card like 4242 4242 4242 4242, and then you should receive an email like this;

{
  "id": "evt_1ElBYWGpNuQPqazsiV1UgOXs",
  "object": "event",
  "api_version": "2015-04-07",
  "created": 1560503923,
  "data": {
    "object": {
      "id": "ch_1ElBYUGpNuQPqazsGPLyUzjN",
      "object": "charge",
      "amount": 10000,
      "amount_refunded": 0,
      "application": null,
      "application_fee": null,
      "application_fee_amount": null,
      "balance_transaction": "txn_1ElBYVGpNuQPqazsEDEsOqBj",
      "billing_details": {
        "address": {
          "city": null,
          "country": null,
          "line1": null,
          "line2": null,
          "postal_code": null,
          "state": null
        },
        "email": null,
        "name": null,
        "phone": null
      },
      "captured": true,
      "created": 1560503922,
      "currency": "gbp",
      "customer": null,
      "description": "Charge for Paypal card",
      "destination": null,
      "dispute": null,
      "failure_code": null,
      "failure_message": null,
      "fraud_details": {
        
      },
      "invoice": null,
      "livemode": false,
      "metadata": {
        
      },
      "on_behalf_of": null,
      "order": null,
      "outcome": {
        "network_status": "approved_by_network",
        "reason": null,
        "risk_level": "normal",
        "risk_score": 9,
        "seller_message": "Payment complete.",
        "type": "authorized"
      },
      "paid": true,
      "payment_intent": null,
      "payment_method": "card_1ElBYUGpNuQPqazsgLWEJ5qr",
      "payment_method_details": {
        "card": {
          "brand": "visa",
          "checks": {
            "address_line1_check": null,
            "address_postal_code_check": null,
            "cvc_check": "pass"
          },
          "country": "US",
          "exp_month": 9,
          "exp_year": 2022,
          "fingerprint": "9nK3pNZASU9W8AOL",
          "funding": "credit",
          "last4": "4242",
          "three_d_secure": null,
          "wallet": null
        },
        "type": "card"
      },
      "receipt_email": null,
      "receipt_number": null,
      "receipt_url": "https:\/\/pay.stripe.com\/receipts\/acct_15s8pNGpNuQPqazs\/ch_1ElBYUGpNuQPqazsGPLyUzjN\/rcpt_FFgwbvHE1qdC84353g8eqXNRSouDIo5",
      "refunded": false,
      "refunds": {
        "object": "list",
        "data": [
          
        ],
        "has_more": false,
        "total_count": 0,
        "url": "\/v1\/charges\/ch_1ElBYUGpNuQPqazsGPLyUzjN\/refunds"
      },
      "review": null,
      "shipping": null,
      "source": {
        "id": "card_1ElBYUGpNuQPqazsgLWEJ5qr",
        "object": "card",
        "address_city": null,
        "address_country": null,
        "address_line1": null,
        "address_line1_check": null,
        "address_line2": null,
        "address_state": null,
        "address_zip": null,
        "address_zip_check": null,
        "brand": "Visa",
        "country": "US",
        "customer": null,
        "cvc_check": "pass",
        "dynamic_last4": null,
        "exp_month": 9,
        "exp_year": 2022,
        "fingerprint": "9nK3pNZASU9W8AOL",
        "funding": "credit",
        "last4": "4242",
        "metadata": {
          
        },
        "name": null,
        "tokenization_method": null
      },
      "source_transfer": null,
      "statement_descriptor": null,
      "status": "succeeded",
      "transfer_data": null,
      "transfer_group": null
    }
  },
  "livemode": false,
  "pending_webhooks": 2,
  "request": "req_HHqTmHqjKXT6iA",
  "type": "charge.succeeded"
}
Categories: Uncategorized

Free #website #Translation using a translator-proxy

logo

If you want to translate your website into languages other than English, then you might think you have your work cut out, but perhaps not.

WebsiteTranslation.net is a translator-proxy which takes HTML source from your home domain (in english), translates the content into another language, then spits it out onto another domain, which then appears in the new language.

To use it, you set up a subdomain on your website, and use a CNAME DNS setting to point it to “websiteTranslation.net”, on the first visit to the new website, the proxy will fetch content from your domain, translate it to the required language, and then serve the content back, ready-translated.

It supports dynamic content, deep urls, SSL (with let’s encrypt), and will proxy all other types of content, like images, CSS and scripts, without interfering with them.  The initial loading time is quite slow as the initial translation is performed, but afterwards, it speeds up 100-fold, thanks to a caching mechanism.

Although the demo is only for one webpage, if you want your whole website translated, then all you need to do is make a change to your website’s DNS settings to point a sub domain to our servers. No coding required! Your DNS settings are managed by the company who registered your website domain, and the process only takes a few minutes.

SEO Friendly

Unlike many website translation services, where the translated text is not visible to Google’s spider, this approach ensures that Google can read your translated website, and will index and rank it seperately to your main website. You can even get a country-specific ranking-boost by setting the geo-targeting option within Google Search Console

Dynamic websites supported

Every if your website is dynamic, and changes all the time, then the translated version will keep automatically up to date, by always using the latest version of your site. This means that you don’t have to manually update the translated versions of your site.

Fine tuning

Machine translation is far from perfect, and often it can make glaring mistakes that anyone can see. We offer an interface that allows you edit translated text on your website, to perfect the translated version

Getting started.

If you’d like to have your website translated, then you need to edit your DNS settings on your domain to point to our servers, and create a CNAME record to point to “WebsiteTranslation.net” for each subdomain / language you want to have available.

For example, if your website is “www.MyShoes.com”, and you wish to have this website available in Spanish and Italian, then you would set up the subdomains “it.MyShoes.com” and “es.MyShoes.com” and add corresponding CNAME records to “WebsiteTranslation.net”. Of Course, if you happen to own “www.MyShoes.es” and “www.MyShoes.it”, then you can point the CNAMES on these domains also – It’s even better for SEO.

We don’t yet support the facility for translating within the same domain – such as http://www.myshoes.com/it and http://www.myshoes.com/es – but talk to us, if this is what you need, and we can develop a solution for you.

After you’ve set up the CNAME records, you’ll need to tell us that you’re ready to go live. Use the contact us form below, and tell us your domains, and what language is needed for each. Also, tell us if you want automated or professional translation.

HTTPS supported

If you need to have HTTPS (Secure HTTP) on your website, then we offer free SSL certificates from Let’s Encrypt. These gives you an SEO boost due to Google’s Trustrank scoring, and also keeps your customer’s data more secure.

If you have your own HTTPS certificate issued by another authority, that you would prefer to use, then you need to ensure that it is valid for the subdomain being used – typically, this means a wildcard certificate. You will need to send us the PFX file of your certificate, and PFX password in order for us to use a third-party certificate.

Website Translation.net came as an idea, to cut the time needed to translate a website from one language to another, without duplicating the source code, so that changes in English could be reflected instantly in all other languages.

 

Categories: Uncategorized

#Forward phone calls from a phone number anywhere in the world to your mobile phone.

FORWARDING

This is a new feature of CloudAnsweringMachine.com – which now allows you to provision a phone number anywhere in the world, and then forward calls from that number to your mobile phone, or another landline.

This could be great if you don’t want to give away your personal phone number, but at the same time you want to receive calls on it. You can always cancel or redirect the phone number that is provisioned on CloudAnsweringMachine.

It’s also useful, if your friends / family / customers are located in another country and you want to give them a local number to call, which will be redirected to your phone number in your country.

At the moment, this comes at no addtional cost over the rental of the Cloud Answering Machine number, however, this may be reviewed, depending on usage levels.

To activate it, simply Login, then scroll to “Redirect”, then enter your forwarding number, and press OK. Any calls to your number will then be redirected to you, rather than going to an answering machine.  – Soon this will be available on the app too!

 

 

Categories: Uncategorized

Enhanced email validation using #DNS #MX

email

If you have a sign-up form, and you are collecting user’s email addresses, then you really want to cut down on the number of typos. I’ve seen as many as 5% of users mistyping “@gmail.com” as “@gmail.con, @gmail.co, @gmai.com, and @gmail.xom etc.”

Many of these email addresses pass basic regex checks, it’s “Gmail.co” is in the correct format for a domain name, but alas, your user will never be able to re-log in.

So, instead of only relying on regexes, you can also use a DNS MX lookup, that can check if there are mail exchanger(s) associated with the domain. This means that “@gmail.com” will work, but “@gmail.co” won’t

Firstly, we have to delve into how to perform a DNS MX lookup in C#, which is a UDP request sent over port 53 to a DNS server, in this case 8.8.8.8, which is Google’s public DNS resolver.

Here’s the class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

public static class Dns
{
public static IEnumerable<string> MxLookup(string domain)
{
const string strDns = “8.8.8.8”; // Google DNS
var udpClient = new UdpClient(strDns, 53);
// SEND REQUEST——————–
var list = new List<byte>();
list.AddRange(new byte[] { 88, 89, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 });
var tmp = domain.Split(‘.’);
foreach (string s in tmp)
{
list.Add(Convert.ToByte(s.Length));
var chars = s.ToCharArray();
list.AddRange(chars.Select(c => Convert.ToByte(Convert.ToInt32(c))));
}
list.AddRange(new byte[] { 0, 0, Convert.ToByte(15), 0, 1 });
var req = new byte[list.Count];
for (var i = 0; i < list.Count; i++) { req[i] = list[i]; }
udpClient.Send(req, req.Length);
// RECEIVE RESPONSE————–
IPEndPoint ep = null;
var receiveBuffer = udpClient.Receive(ref ep);
udpClient.Close();
var resp = new int[receiveBuffer.Length];
for (var i = 0; i < resp.Length; i++)
resp[i] = Convert.ToInt32(receiveBuffer[i]);
var status = resp[3];
if (status != 128) return null; // throw new Exception(string.Format(“{0}”, status));
var answers = resp[7];
if (answers == 0) return null; // throw new Exception(“No results”);
var pos = domain.Length + 18;
var lRecords = new List<string>();
while (answers > 0)
{
pos += 14; //offset
var str = GetMxRecord(resp, pos, out pos);
lRecords.Add(str);
answers–;
}
return lRecords;
}

private static string GetMxRecord(int[] resp, int start, out int pos)
{
StringBuilder sb = new StringBuilder();
int len = resp[start];
while (len > 0)
{
if (len != 192)
{
if (sb.Length > 0) sb.Append(“.”);
for (int i = start; i < start + len; i++)
sb.Append(Convert.ToChar(resp[i + 1]));
start += len + 1;
len = resp[start];
}
if (len != 192) continue;
var newPosition = resp[start + 1];
if (sb.Length > 0) sb.Append(“.”);
sb.Append(GetMxRecord(resp, newPosition, out newPosition));
start++;
break;
}
pos = start + 1;
return sb.ToString();
}
}

[Credit due to Christian Salway @ccsalway for this code]

This when called as Dns.MxLookup(“gmail.com”), would return a list of strings as follows;

"gmail-smtp-in.l.google.com",
"alt4.gmail-smtp-in.l.google.com",
"alt1.gmail-smtp-in.l.google.com",
"alt2.gmail-smtp-in.l.google.com",
"alt3.gmail-smtp-in.l.google.com"

These correspond to the mail exchange servers used by gmail, and indicate that the domain can receive email, otherwise, this function returns null.

Now, lets create an ASP.NET page that will act as a handler for an Ajax call to validate an email address as follows;

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

public partial class ajax_ValidateEmail : System.Web.UI.Page
{
private class ResponseClass
{
public bool success { get; set; }
public string error { get; set; }
public IEnumerable<string> information { get; set; }
}

protected void Page_Load(object sender, EventArgs e)
{
var response = new ResponseClass();
var email = Request.QueryString[“email”];
if (string.IsNullOrEmpty(email))
{
response.error = “Need on email on querystring”;
}
else
{
var idxAt = email.IndexOf(“@”, StringComparison.CurrentCulture);
if (idxAt == -1)
{
response.error = “Invalid email address”;
}
else
{
var domain = email.Substring(idxAt+1);
var mx = Dns.MxLookup(domain);
if (mx == null)
{
response.error = “Invalid domain”;
}
else
{
response.success = true;
response.information = mx;
}
}
}
var json = JsonConvert.SerializeObject(response, Formatting.Indented);
Response.ContentType = “application/json”;
Response.Write(json);
}
}

This will respond with the following Json in the case of success;

{
  "success": true,
  "error": null,
  "information": [
    "gmail-smtp-in.l.google.com",
    "alt4.gmail-smtp-in.l.google.com",
    "alt1.gmail-smtp-in.l.google.com",
    "alt2.gmail-smtp-in.l.google.com",
    "alt3.gmail-smtp-in.l.google.com"
  ]
}

And, in the case of failure;

{
  "success": false,
  "error": "Invalid domain",
  "information": null
}

This can then be called from Javascript (jquery) as follows;

var strEmail = $(“#tbEmail”).val();
$.get(“/ajax/ValidateEmail.aspx?email=” + strEmail,
function(response) {
if (!response.success) {
$(“#fgEmail”).addClass(“has-error”);
} else {
$(“#fgEmail”).removeClass(“has-error”);
}
});

You can go a step further and prevent the form submission, at the moment, I’m just relaying user feedback, and see what happens.

To see this live, see https://www.regcheck.org.uk

 

Categories: Uncategorized

Using #Electron to call a .NET #API

Screenshot 2019-06-02 at 15.04.30

Electron is a platform that allows you develop desktop applications which are cross-platform, and run on standard HTML , CSS , Javascript and Node. The mix of typically client-and-“server side” javascript is very unusual, but quite liberating.

Here is a simple example of using Electron with an API that typically would be called by server-side Node, in a desktop app here –

https://github.com/infiniteloopltd/ElectronRegCheck

It’s not really that much beyond the “hello world” example, but shows the basics of using a simple user interface, and calling an API.

The code in “renderer.js” is as follows;

var api = require(‘car-registration-api-uk’);
window.$ = window.jQuery = require(‘jquery’);

$(init);
function init()
{
$(“#btnSearch”).bind(“click”,btnSearch_click);
}

function btnSearch_click()
{
var reg = $(“#reg”).val();
api.CheckCarRegistrationUK(reg,”*** your username here***”,function(data){
$(“#output”).html(data.Description);
});
}

You’ll need an account on RegCheck.org.uk for this to work.

Categories: Uncategorized

Co2 emissions database available via #EEA

Categories: Uncategorized

Using JsonReaderWriterFactory. CreateJsonReader in C#

screen-shot-2017-04-17-at-114949-am

Whenever you want to read JSON in C#, I don’t normally look any further than Newtonsoft.Json , but if you are not allowed to use third-party libraries in your project, then there is a little known JSON parser built into .NET (or the System.Runtime.Serialization.Json library).

But, it’s not nice to use, and who wants to use XPath Queries with Json anyway, it’s awkward and unnatural!

So, I’ve written a class that does not use any third-party libraries, but uses a similar calling structure to Newtonsoft.JSON.

using System.Collections.Generic;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml.Linq;

namespace Synergy.Common
{
/// <summary>
/// A simplified version of JSON
/// </summary>
public class MyJson : Dictionary<string, MyJson>
{
/// <summary>
/// The value of the node
/// </summary>
public string Value;

// public Dictionary<string, MyJson> Children = new Dictionary<string, MyJson>();

/// <summary>
/// Parses the specified json.
/// </summary>
/// <param name=”json”>The json.</param>
/// <returns></returns>
public static MyJson Parse(string json)
{
var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(json), new System.Xml.XmlDictionaryReaderQuotas());
var xe = XElement.Load(jsonReader);
return FromElement(xe);
}

private static MyJson FromElement(XContainer xe)
{
var root = new MyJson { Value = “” };
foreach (var element in xe.Elements())
{
if (root.ContainsKey(element.Name.LocalName)) continue; // Don’t support duplicates so far
root.Add(element.Name.LocalName,
element.HasElements ? FromElement(element) : new MyJson {Value = element.Value});
}
return root;
}
}
}

You call MyJson.Parse() passing in some JSON, and what you get back, is a nested dictionary, where each dictionary entry can have sub-dictionaries. If it has an empty sub-dictionary, then it will have a Value property set.

The Value property is always a string, so there is no fancy date or number parsing yet, and it does not allow duplicate keys.

Feel free to comment with improvements.

Categories: Uncategorized

Get language codes from a country code in C#

Language-Families-World-Map

Given a country code – i.e. BE (Belgium), you might want to know the most commonly spoken languages in that country – i.e FR and NL (French and Dutch), here’s some code to do that in C#, without a database lookup.

private static IEnumerable<string> GetLanguagesSpokenInCountry(string country)
{
var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
var languagesSpoken = allCultures.Where(c =>
{
if (c.IsNeutralCulture) return false;
if (c.LCID == 0x7F) return false; // Ignore Invariant culture
var region = new RegionInfo(c.LCID);
return region.TwoLetterISORegionName == country;
}).Select(c => c.TwoLetterISOLanguageName.ToUpper()).ToList();
return languagesSpoken;
}

You might be surprised that India (IN) returns 19 languages! – on my PC.

Categories: Uncategorized

Cloud Answering Machine #app just re-launched for #android

Cloud Answering Machine is a service where you can get a second phone number hosted virtually online, that allows you to recieve voicemail and SMS messages, without requiring another physical phone, or physical phone line. What’s more, you can create a phone number anywhere in the world, so you can give the impression that your buisness is based in New York, when you really live in the UK.

The main use for a second phone line is to separate high importance calls from your work or family, from low importance calls from people who don’t need to be dealt with urgently. Perhaps separating calls from your sideline business and your day job, or even to create a phone number for purposes that you don’t want your wife or boss knowning about.

We’ve recently relaunched our app based on a purely Native platform, rather than the old Cordova based version at https://play.google.com/store/apps/details?id=com.record.recordingapp

Please feel free to download it – it’s free!, and please give us a good rating if you like it.

We’ve also open sourced it, so it’s free for you to build upon, or create your own companion apps for this. Check out the repo on Github: https://github.com/infiniteloopltd/CloudAnsweringMachineAndroid

Categories: Uncategorized

Translate #RESX files online for #free

resx

https://www.resxtranslate.com/  is a website that allows you upload .NET RESX files or Resource files, and have them translated into one of 25 languages automatically, and for free.

You upload the RESX file, enter in your email address, the language pair required, and the translation quality level. It then emails you back the translated RESX file in seconds (or minutes, depending on the size).

The automatic translation is powered by Yandex translate, which offers a translation API, which is called via a simple API as follows;

var strUrl = “https://translate.yandex.net/api/v1.5/tr.json/translate?&#8221;;
strUrl += “key=trnsl….”;
strUrl += “&text=” + text;
strUrl += “&lang=” + language;
var wc = new WebClient { Encoding = Encoding.UTF8 };
var strJson = wc.DownloadString(strUrl);
var jTranslate = JObject.Parse(strJson);
var strEnglish = jTranslate[“text”].First.ToString();
return strEnglish;

 

Categories: Uncategorized