Archive

Archive for November, 2017

#Verify a bank account number in C# using #Stripe

fitness-bank

Say you collect payout data from your affiliates or partners, and you would like to make sure that the bank details are correct, so that you do not have the embarrassment and cost of a bounced payment.

Here is a trick on how to do this, using Stripe – This is not actually using Stripe to make the payout, but it’s using Stripe Connect to verify the bank account details.

First set up a Stripe account, then create a sub account via Stripe connect. You will need this account id, and your private key. Do not use your main account id, since your bank account details will be overwritten.

Here’s the main code;

public static string Verify(string BankCountry, string BankSortCode, string BankAccountNumber)
{
var wc = new WebClient();
string strResult;
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls; // comparable to modern browsers
var myCreds = new NetworkCredential(PrivateKey, “”);
wc.Credentials = myCreds;
strUrl = string.Format(strUrl, stripe_user_id);
var strPostdata = “”;
// to-do: Possible-bug: Does this add new bank accounts every time you try to update the account?
if (BankAccountNumber != null && !BankAccountNumber.StartsWith(“*”))
{
strPostdata += “&external_account[object]=bank_account”;
strPostdata += “&external_account[country]=” + BankCountry;
strPostdata += “&external_account[currency]=” + CurrencyFromBankCountry(BankCountry);
strPostdata += “&external_account[routing_number]=” + BankSortCode.Replace(“-“, “”).Replace(” “, “”);
strPostdata += “&external_account[account_number]=” + BankAccountNumber;
}
wc.Headers[“Content-Type”] = “application/x-www-form-urlencoded”;
strResult = wc.UploadString(strUrl, strPostdata);
}
catch (WebException exception)
{
strResult = GetExceptionDetails(exception);
var jError = JObject.Parse(strResult);
thrownew Exception(jError[“error”][“message”].ToString());
}
var userJson = GetAccountDetails();
var jUser = JObject.Parse(userJson);
foreach (var bankAccont in jUser[“bank_accounts”][“data”])
{
var strStripeLast4 = bankAccont[“last4”].ToString();
var strPassedLast4 = BankAccountNumber.Substring(BankAccountNumber.Length – 4);
if (strPassedLast4 == strStripeLast4)
{
return bankAccont.ToString();
}
}
return”Unknown failure”;
}
You’ll also need this to pull back the bank details (i.e. bank name) from Stripe:
private static string GetAccountDetails()
{
/*
-u sk_test_jCTjZ7050Z9zbRyj3me2JFEW:
*/
var wc = new WebClient();
string strResult;
try
{
var myCreds = new NetworkCredential(PrivateKey, “”);
wc.Credentials = myCreds;
strUrl += stripe_user_id;
strResult = wc.DownloadString(strUrl);
}
catch (WebException exception)
{
strResult = GetExceptionDetails(exception);
}
return strResult;
}
And two helper methods;
– GetExceptionDetails helps you get a descriptive error message rather than just a HTTP status code when something goes wrong.
private static string GetExceptionDetails(WebException exception)
{
string strResult = “”;
if (exception.Response != null)
{
var responseStream = exception.Response.GetResponseStream();
if (responseStream != null)
{
using (var reader = new StreamReader(responseStream))
{
strResult = reader.ReadToEnd();
}
}
}
return strResult;
}
And this, – which could be made a little neater using RegionInfo in System.Globalisation, but served for my purposes;
privatestaticstring CurrencyFromBankCountry(string BankCountry)
{
switch (BankCountry)
{
case”US”: return”USD”;
case”IE”: return”EUR”;
case”GB”: return”GBP”;
case”AU”: return”AUD”;
case”CA”: return”CAD”;
case”DK”: return”DKK”;
case”FI”: return”EUR”;
case”FR”: return”EUR”;
case”JP”: return”JPY”;
case”NO”: return”NOK”;
case”SG”: return”SGD”;
case”ES”: return”EUR”;
case”SE”: return”SEK”;
case”AT”: return”EUR”;
case”BE”: return”EUR”;
case”DE”: return”EUR”;
case”HK”: return”HKD”;
case”IT”: return”EUR”;
case”LU”: return”EUR”;
case”NL”: return”EUR”;
case”PT”: return”EUR”;
}
throw new Exception(“Unrecognised bank country”);
}
Categories: Uncategorized

Send #email via #JQuery

download

This was bit of an aborted project, since I see that the JQuery Plugin registry is now closed for new submissions. But, I created a little JQuery Plugin as a Wrapper for the SMTPJS library with this;

(function ( $ ) {
$.getScript( “https://smtpjs.com/v2/smtp.js”, function( data, textStatus, jqxhr ) {
$.fn.Email = Email;
});
$.fn.Email = function() {
return “Not Loaded yet”;
};
}( jQuery ));

Which can be called as follows;

$.fn.Email.send(“from@you.com”,
“to@them.com”,
“Subject line”,
“Body in HTML”,
{
token: “GUID from SMTPJS.com”,
callback: function done(message) { alert(message) }
});

The GIT repo is hereĀ https://github.com/infiniteloopltd/SmtpJQuery/

Categories: Uncategorized

#Transactional Email in #Javascript

elastic-email

Transactional email differs from promotional / marketing email, in the sense that it is typically not sent it batches, but is sent in response to a user signing up to your website, or an event such as a low credit warning, or notification.

Normally, people think of email as a server side exercise, but you can also use Javascript to send email too – with the help of SMTPJS.com

Here’s a sample script using Elastic Email’s SMTP with SMTPJS –

Email.send(
“me@gmail.com”,
“you@gmail.com”,
“This is a subject”,
“this is the body”,
“smtp25.elasticemail.com”,
“username@gmail.com”,
“989accc0-cba1-4fbf-abf2-7bad4fc80526”,
function(data){ alert(data) }

);

It almost needs no explanation, it’s so simple!

Need an SMTP server:
If you don’t have access to an SMTP server, you can create one by opening an account at Elastic Email.com, then pressing Settings > SMTP/API, and using the SMTP configuration shown.Ā Get SMTP Credentials

 

Categories: Uncategorized

Determine what version of #TLS your code is using.

 

1_ibqd7KX_cmoaUmlidifNbA

If your code connects to a HTTPS web service, it’s possible that you are still not being fully secure. The thing is – HTTPS comes in a few different “Flavours”, or specifically TLS versions.

If you connect to a HTTPS service, but use an old TLS version, then you’re not being as secure as you could be.

However, if the HTTPS endpoint doesn’t complain about your TLS version, it’s often pretty hard to see exactly what TLS version you are using, and the last thing you want is to break open WireShark and try and pick apart the packets to see.

That’s where this handy API from Jeff Hodges comes in, atĀ https://www.howsmyssl.com/a/check

If you make a call from C#, you may see a result like this

{
“given_cipher_suites”: [
“TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA”,
“TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA”,
“TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA”,
“TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA”,
“TLS_RSA_WITH_AES_256_CBC_SHA”,
“TLS_RSA_WITH_AES_128_CBC_SHA”,
“TLS_RSA_WITH_3DES_EDE_CBC_SHA”,
“TLS_RSA_WITH_RC4_128_SHA”,
“TLS_RSA_WITH_RC4_128_MD5”
],
“ephemeral_keys_supported”: true,
“session_ticket_supported”: true,
“tls_compression_supported”: false,
“unknown_cipher_suite_supported”: false,
“beast_vuln”: false,
“able_to_detect_n_minus_one_splitting”: true,
“insecure_cipher_suites”: {
“TLS_RSA_WITH_3DES_EDE_CBC_SHA”: [
“uses 3DES which is vulnerable to the Sweet32 attack but was not configured as a fallback in the ciphersuite order”
],
“TLS_RSA_WITH_RC4_128_MD5”: [
“uses RC4 which has insecure biases in its output”
],
“TLS_RSA_WITH_RC4_128_SHA”: [
“uses RC4 which has insecure biases in its output”
]
},
“tls_version”: “TLS 1.0”,
“rating”: “Bad”
}

You can see, I get a “Bad” rating here, because I’m using TLS 1.0, and some obsolete cyphers.

I put in the following lineĀ to use TLS 1.2:

ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;

And received;

{
“given_cipher_suites”: [
“TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384”,
“TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256”,
“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”,
“TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256”,
“TLS_DHE_RSA_WITH_AES_256_GCM_SHA384”,
“TLS_DHE_RSA_WITH_AES_128_GCM_SHA256”,
“TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384”,
“TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256”,
“TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384”,
“TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256”,
“TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA”,
“TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA”,
“TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA”,
“TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA”,
“TLS_RSA_WITH_AES_256_GCM_SHA384”,
“TLS_RSA_WITH_AES_128_GCM_SHA256”,
“TLS_RSA_WITH_AES_256_CBC_SHA256”,
“TLS_RSA_WITH_AES_128_CBC_SHA256”,
“TLS_RSA_WITH_AES_256_CBC_SHA”,
“TLS_RSA_WITH_AES_128_CBC_SHA”,
“TLS_RSA_WITH_3DES_EDE_CBC_SHA”,
“TLS_RSA_WITH_RC4_128_SHA”,
“TLS_RSA_WITH_RC4_128_MD5”
],
“ephemeral_keys_supported”: true,
“session_ticket_supported”: true,
“tls_compression_supported”: false,
“unknown_cipher_suite_supported”: false,
“beast_vuln”: false,
“able_to_detect_n_minus_one_splitting”: false,
“insecure_cipher_suites”: {
“TLS_RSA_WITH_3DES_EDE_CBC_SHA”: [
“uses 3DES which is vulnerable to the Sweet32 attack but was not configured as a fallback in the ciphersuite order”
],
“TLS_RSA_WITH_RC4_128_MD5”: [
“uses RC4 which has insecure biases in its output”
],
“TLS_RSA_WITH_RC4_128_SHA”: [
“uses RC4 which has insecure biases in its output”
]
},
“tls_version”: “TLS 1.2”,
“rating”: “Bad”
}

Still using some old Cyphers, but at least using TLS 1.2 now.

If you see the same API when called from Chome, you see:

{Ā Ā 
“given_cipher_suites”:[Ā Ā 
“TLS_GREASE_IS_THE_WORD_AA”,
“TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256”,
“TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256”,
“TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384”,
“TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384”,
“TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256”,
“TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256”,
“TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA”,
“TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA”,
“TLS_RSA_WITH_AES_128_GCM_SHA256”,
“TLS_RSA_WITH_AES_256_GCM_SHA384”,
“TLS_RSA_WITH_AES_128_CBC_SHA”,
“TLS_RSA_WITH_AES_256_CBC_SHA”,
“TLS_RSA_WITH_3DES_EDE_CBC_SHA”
],
“ephemeral_keys_supported”:true,
“session_ticket_supported”:true,
“tls_compression_supported”:false,
“unknown_cipher_suite_supported”:false,
“beast_vuln”:false,
“able_to_detect_n_minus_one_splitting”:false,
“insecure_cipher_suites”:{Ā Ā 

},
“tls_version”:“TLSĀ 1.2”,
“rating”:“ProbablyĀ Okay”
}

Interestingly, you can also use this to see issues with Search Engine Spider bots, – look at the report from Yandex (Russian Search Engine)

Given Cipher Suites

The cipher suites your client said it supports, in the order it sent them, are:

  • TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_DH_DSS_WITH_AES_256_GCM_SHA384
  • TLS_DHE_DSS_WITH_AES_256_GCM_SHA384
  • TLS_DH_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_RSA_WITH_AES_256_GCM_SHA384
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA256
  • TLS_DH_RSA_WITH_AES_256_CBC_SHA256
  • TLS_DH_DSS_WITH_AES_256_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_256_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_256_CBC_SHA
  • TLS_DH_RSA_WITH_AES_256_CBC_SHA
  • TLS_DH_DSS_WITH_AES_256_CBC_SHA
  • TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA
  • TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA
  • TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384
  • TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384
  • TLS_ECDH_RSA_WITH_AES_256_CBC_SHA
  • TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_AES_256_GCM_SHA384
  • TLS_RSA_WITH_AES_256_CBC_SHA256
  • TLS_RSA_WITH_AES_256_CBC_SHA
  • TLS_RSA_WITH_CAMELLIA_256_CBC_SHA
  • TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA
  • TLS_DH_DSS_WITH_AES_128_GCM_SHA256
  • TLS_DHE_DSS_WITH_AES_128_GCM_SHA256
  • TLS_DH_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
  • TLS_DHE_DSS_WITH_AES_128_CBC_SHA256
  • TLS_DH_RSA_WITH_AES_128_CBC_SHA256
  • TLS_DH_DSS_WITH_AES_128_CBC_SHA256
  • TLS_DHE_RSA_WITH_AES_128_CBC_SHA
  • TLS_DHE_DSS_WITH_AES_128_CBC_SHA
  • TLS_DH_RSA_WITH_AES_128_CBC_SHA
  • TLS_DH_DSS_WITH_AES_128_CBC_SHA
  • TLS_DHE_RSA_WITH_SEED_CBC_SHA
  • TLS_DHE_DSS_WITH_SEED_CBC_SHA
  • TLS_DH_RSA_WITH_SEED_CBC_SHA
  • TLS_DH_DSS_WITH_SEED_CBC_SHA
  • TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA
  • TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA
  • TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256
  • TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256
  • TLS_ECDH_RSA_WITH_AES_128_CBC_SHA
  • TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA
  • TLS_RSA_WITH_AES_128_GCM_SHA256
  • TLS_RSA_WITH_AES_128_CBC_SHA256
  • TLS_RSA_WITH_AES_128_CBC_SHA
  • TLS_RSA_WITH_SEED_CBC_SHA
  • TLS_RSA_WITH_CAMELLIA_128_CBC_SHA
  • TLS_RSA_WITH_IDEA_CBC_SHA
  • TLS_ECDHE_RSA_WITH_RC4_128_SHA
  • TLS_ECDHE_ECDSA_WITH_RC4_128_SHA
  • TLS_ECDH_RSA_WITH_RC4_128_SHA
  • TLS_ECDH_ECDSA_WITH_RC4_128_SHA
  • TLS_RSA_WITH_RC4_128_SHA
  • TLS_RSA_WITH_RC4_128_MD5
  • TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA
  • TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
  • TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA
  • TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA
  • TLS_RSA_WITH_3DES_EDE_CBC_SHA
  • TLS_EMPTY_RENEGOTIATION_INFO_SCSV
Categories: Uncategorized

Ļ€ = 6.2831… ? Have we got it wrong?

cooltext266929018210520

Recently reading a book by Steve mould “The Element In the Room”, he proposes that our definition of pi was a mistake, and instead it should have been double it’s value.

How can this be?, surely someone would have noticed. Well, it all comes down to definition. Pi is defined as the ratio between the diameter of a circle to the circumference. However, it could have just as easily be defined as the ratio of the radius of a circle divided by the diameter, and when that happens, many mathematical formulas become simplified.

Here’s a great video of Steve Mould arguing his case for Tau, or the double pi constant.

 

Categories: Uncategorized

Implementing a #HTTPS #Tunnel using c#

rZl08

You very rarely have to work at TCP/IP level whenever writing network code in C#, and if you find yourself doing it, you’ve probably taken a very wrong turn. However, since information is sparse about the format of low level TCP requests, I’m writing this article.

This particular code snippet shows how to make a HTTPS tunnel via a proxy server in C#. It involves these steps:

  1. Send a CONNECT command instructing the proxy to connect to a remote server on a given port, i.e. 443 for SSL, and forward all subsequent traffic to this host and port.
  2. Read the response from the proxy, it should be a line saying “200 Tunnel Established”
  3. Pass the stream to a SSL stream object, and callĀ AuthenticateAsClient
  4. Send HTTP data as normal.

Here’s some code to connect to ICanHazIP via SSL.

private static string TestIp(WebProxy proxy)
{
var tcp = new TcpClient(proxy.Address.Host, proxy.Address.Port);
var stream = tcp.GetStream();
var connect = Encoding.ASCII.GetBytes(“CONNECT http://www.icanhazip.com:443 HTTP/1.0\n\n”);
stream.Write(connect, 0, connect.Length);
var rawStream = new StreamReader(stream);
var strConnect = rawStream.ReadLine();
var ssl = new SslStream(stream);
ssl.AuthenticateAsClient(“www.icanhazip.com”);
var send = Encoding.ASCII.GetBytes(“GET / HTTP/1.0\r\n” +
“Host:www.icanhazip.com\r\n” +
“\r\n”);
ssl.Write(send, 0, send.Length);
var sr = new StreamReader(ssl);
var str = sr.ReadToEnd();
tcp.Close();
ssl.Close();
return str;
}

If you’re interested in low level socket programming in C#, check out by book:

Categories: Uncategorized

Access UK TV #EPG via #JSON

iplayerfreesat02

If you are developing an app or website that allows people check television guides in the UK, then this API may help you out.

First, a list of 576 TV channels from the UK can be listed via this Rest API call;

http://tv.apixml.net/api.aspx?action=channels

Then, to get details of programs to be shown on this channel, then you can use the url;

http://tv.apixml.net/api.aspx?action=program&channelid=03c9f88a54910f471bb80c039e2a8fa1

Where channelId is obtained from the previous call. The schedule is always for the next 24 hours.

This API will be built into out TV UK app for iOS:Ā https://itunes.apple.com/us/app/tv-uk/id464570844?mt=8

 

 

 

Categories: Uncategorized

Sending a base64 encoded file using #SMTP.js

smtpjs

SMTPjs.com is a javascript library that allows you send email through Javascript. – You still need access to a outgoing mail (SMTP) server, but this makes life a little easier for your JS apps.

Today, we released v2.0.1 of the library, which has a few important fixes

  • The message body is no longer limited in size
  • There is now an optional callback parameter, that allows your code continue after the email is sent.
  • Attachments can be sent in base 64 format (dataURI) to the library.

Previous versions of the library will continue to be functional, but we recommend upgrading to the latest version.

Callback feature:

Depending on whether you use a stored token, or send your SMTP credentials with every request, then you use one of the two following blocks of code:

Email.send(“from@you.com”,
“to@them.com”,
“Subject”,
“Body”,
“smtp.server.com”,
“username”,
“password”,
function done(message) { alert(“sent”) }
);

or, when using stored credentials;

Email.send(“from@you.com”,
“to@them.com”,
“Subject”,
“Body”,
{
token: “your-token”,
callback:function done(message) { alert(“sent”) }
});

DataUri Attachments

Previously, in order to send an attachment with this script, then the file you needed to send needed to be already hosted online. This, may not be ideal, if you wanted your user to select a file from their device, or otherwise dynamically generate the file to be sent.

So, let’s imagine we want the user to select a file to be sent from their device, we add a form element as follows;

<input type=”file” id=”fileupload” onchange=”uploadFileToServer()” />

Then define the function uploadFileToServer as follows:

function uploadFileToServer() {
var file = event.srcElement.files[0];
console.log(file);
var reader = new FileReader();
reader.readAsBinaryString(file);
reader.onload = function () {
var datauri = “data:” + file.type + “;base64,” + btoa(reader.result);
Email.sendWithAttachment(“from@you.com”,
“to@them.com”,
“Subject”,
“Body”,
“smtp.server.com”,
“username”,
“password”,,
datauri,
function done(message) { alert(“Message sent OK”) }
);
};
reader.onerror = function() {
console.log(‘there are some problems’);
};
}

Categories: Uncategorized

#Translate any country name into any language in C#

WordPress-Theme-Translation

If you have a multi-lingual app or website that needs to show a drop down list of country names, then it suddenly adds 195 words to your translation file, which is going to make your translation word count shoot up suddenly.

Surely, someone else has done this already?, yes, they have. The Unicode consortium have a public file that you can download that contains a list of countries, and lots of other information translated into many different languages; here;Ā http://unicode.org/Public/cldr/1.7.0/core.zip

I extracted the two letter files ({language}.xml) from core/common/main, removed the dtd definition, and put it in to a folder named /data/localisation off the root of my website.

public static string Country(string code, string culture)
{
// Source: http://unicode.org/Public/cldr/1.7.0/core.zip
var locale = new CultureInfo(culture).TwoLetterISOLanguageName.ToLower();
var strLocalXmlFile = HttpContext.Current.Server.MapPath(“/data/Localisation/” + locale + “.xml”);
var xdoc = new XmlDocument();
xdoc.Load(strLocalXmlFile);
var xn = xdoc.SelectSingleNode(“//territory[@type='” + code + “‘]”);
return xn.InnerText;
}

Then you can call Country(“GB”,”sv-SE”) to getĀ Storbritannien

Of course, there is many more things that you may want to translate, and nothing is better than a human translator, which I’d recommend OutsourceTranslation.com to find one.

Categories: Uncategorized

Protect yourself from #IAP #Fraud com.zeptolab.ctrbonus.superpower1

maxresdefault

IAP or In app Purchases is now the most popular way App developers get paid for their apps. Most IAPs don’t actually incur added cost the developer when used, such as unlocking extra levels, or power ups, but some activate real-world services, and can incur added server costs to the developer – such as extra file storage, or paid-for API calls.

This is why you really need to ensure that you validate your IAP receipt from apple, to ensure that it has indeed come from apple, and not a replay attack from someone else’s IAP.

I received this IAP receipt that failed validation this morning:

ewoJInNpZ25hdHVyZSIgPSAiQXBkeEpkdE53UFUyckE1L2NuM2tJTzFPVGsyNWZlREthMGFhZ3l5UnZlV2xjRmxnbHY2UkY2em5raUJTM3VtOVVjN3BWb2IrUHFaUjJUOHd5VnJITnBsb2YzRFgzSXFET2xXcSs5MGE3WWwrcXJSN0E3ald3dml3NzA4UFMrNjdQeUhSbmhPL0c3YlZxZ1JwRXI2RXVGeWJpVTFGWEFpWEpjNmxzMVlBc3NReEFBQURWekNDQTFNd2dnSTdvQU1DQVFJQ0NHVVVrVTNaV0FTMU1BMEdDU3FHU0liM0RRRUJCUVVBTUg4eEN6QUpCZ05WQkFZVEFsVlRNUk13RVFZRFZRUUtEQXBCY0hCc1pTQkpibU11TVNZd0pBWURWUVFMREIxQmNIQnNaU0JEWlhKMGFXWnBZMkYwYVc5dUlFRjFkR2h2Y21sMGVURXpNREVHQTFVRUF3d3FRWEJ3YkdVZ2FWUjFibVZ6SUZOMGIzSmxJRU5sY25ScFptbGpZWFJwYjI0Z1FYVjBhRzl5YVhSNU1CNFhEVEE1TURZeE5USXlNRFUxTmxvWERURTBNRFl4TkRJeU1EVTFObG93WkRFak1DRUdBMVVFQXd3YVVIVnlZMmhoYzJWU1pXTmxhWEIwUTJWeWRHbG1hV05oZEdVeEd6QVpCZ05WQkFzTUVrRndjR3hsSUdsVWRXNWxjeUJUZEc5eVpURVRNQkVHQTFVRUNnd0tRWEJ3YkdVZ1NXNWpMakVMTUFrR0ExVUVCaE1DVlZNd2daOHdEUVlKS29aSWh2Y05BUUVCQlFBRGdZMEFNSUdKQW9HQkFNclJqRjJjdDRJclNkaVRDaGFJMGc4cHd2L2NtSHM4cC9Sd1YvcnQvOTFYS1ZoTmw0WElCaW1LalFRTmZnSHNEczZ5anUrK0RyS0pFN3VLc3BoTWRkS1lmRkU1ckdYc0FkQkVqQndSSXhleFRldngzSExFRkdBdDFtb0t4NTA5ZGh4dGlJZERnSnYyWWFWczQ5QjB1SnZOZHk2U01xTk5MSHNETHpEUzlvWkhBZ01CQUFHamNqQndNQXdHQTFVZEV3RUIvd1FDTUFBd0h3WURWUjBqQkJnd0ZvQVVOaDNvNHAyQzBnRVl0VEpyRHRkREM1RllRem93RGdZRFZSMFBBUUgvQkFRREFnZUFNQjBHQTFVZERnUVdCQlNwZzRQeUdVakZQaEpYQ0JUTXphTittVjhrOVRBUUJnb3Foa2lHOTJOa0JnVUJCQUlGQURBTkJna3Foa2lHOXcwQkFRVUZBQU9DQVFFQUVhU2JQanRtTjRDL0lCM1FFcEszMlJ4YWNDRFhkVlhBZVZSZVM1RmFaeGMrdDg4cFFQOTNCaUF4dmRXLzNlVFNNR1k1RmJlQVlMM2V0cVA1Z204d3JGb2pYMGlreVZSU3RRKy9BUTBLRWp0cUIwN2tMczlRVWU4Y3pSOFVHZmRNMUV1bVYvVWd2RGQ0TndOWXhMUU1nNFdUUWZna1FRVnk4R1had1ZIZ2JFL1VDNlk3MDUzcEdYQms1MU5QTTN3b3hoZDNnU1JMdlhqK2xvSHNTdGNURXFlOXBCRHBtRzUrc2s0dHcrR0szR01lRU41LytlMVFUOW5wL0tsMW5qK2FCdzdDMHhzeTBiRm5hQWQxY1NTNnhkb3J5L0NVdk02Z3RLc21uT09kcVRlc2JwMGJzOHNuNldxczBDOWRnY3hSSHVPTVoydG04bnBMVW03YXJnT1N6UT09IjsKCSJwdXJjaGFzZS1pbmZvIiA9ICJld29KSW05eWFXZHBibUZzTFhCMWNtTm9ZWE5sTFdSaGRHVXRjSE4wSWlBOUlDSXlNREV5TFRBM0xURXlJREExT2pVME9qTTFJRUZ0WlhKcFkyRXZURzl6WDBGdVoyVnNaWE1pT3dvSkluQjFjbU5vWVhObExXUmhkR1V0YlhNaUlEMGdJakV6TkRJd09UYzJOelU0T0RJaU93b0pJbTl5YVdkcGJtRnNMWFJ5WVc1ellXTjBhVzl1TFdsa0lpQTlJQ0l4TnpBd01EQXdNamswTkRrME1qQWlPd29KSW1KMmNuTWlJRDBnSWpFdU5DSTdDZ2tpWVhCd0xXbDBaVzB0YVdRaUlEMGdJalExTURVME1qSXpNeUk3Q2draWRISmhibk5oWTNScGIyNHRhV1FpSUQwZ0lqRTNNREF3TURBeU9UUTBPVFF5TUNJN0Nna2ljWFZoYm5ScGRIa2lJRDBnSWpFaU93b0pJbTl5YVdkcGJtRnNMWEIxY21Ob1lYTmxMV1JoZEdVdGJYTWlJRDBnSWpFek5ESXdPVGMyTnpVNE9ESWlPd29KSW1sMFpXMHRhV1FpSUQwZ0lqVXpOREU0TlRBME1pSTdDZ2tpZG1WeWMybHZiaTFsZUhSbGNtNWhiQzFwWkdWdWRHbG1hV1Z5SWlBOUlDSTVNRFV4TWpNMklqc0tDU0p3Y205a2RXTjBMV2xrSWlBOUlDSmpiMjB1ZW1Wd2RHOXNZV0l1WTNSeVltOXVkWE11YzNWd1pYSndiM2RsY2pFaU93b0pJbkIxY21Ob1lYTmxMV1JoZEdVaUlEMGdJakl3TVRJdE1EY3RNVElnTVRJNk5UUTZNelVnUlhSakwwZE5WQ0k3Q2draWIzSnBaMmx1WVd3dGNIVnlZMmhoYzJVdFpHRjBaU0lnUFNBaU1qQXhNaTB3TnkweE1pQXhNam8xTkRvek5TQkZkR012UjAxVUlqc0tDU0ppYVdRaUlEMGdJbU52YlM1NlpYQjBiMnhoWWk1amRISmxlSEJsY21sdFpXNTBjeUk3Q2draWNIVnlZMmhoYzJVdFpHRjBaUzF3YzNRaUlEMGdJakl3TVRJdE1EY3RNVElnTURVNk5UUTZNelVnUVcxbGNtbGpZUzlNYjNOZlFXNW5aV3hsY3lJN0NuMD0iOwoJInBvZCIgPSAiMTciOwoJInNpZ25pbmctc3RhdHVzIiA9ICIwIjsKfQ

Running it through my IAP receipt validator I got this;

{ “receipt”: { “original_purchase_date_pst”: “2012-07-12 05:54:35 America/Los_Angeles”, “purchase_date_ms”: “1342097675882”, “original_transaction_id”: “170000029449420”, “original_purchase_date_ms”: “1342097675882”, “app_item_id”: “450542233”, “transaction_id”: “170000029449420”, “quantity”: “1”, “bvrs”: “1.4”, “version_external_identifier”: “9051236”, “bid”: “com.zeptolab.ctrexperiments”, “product_id”: “com.zeptolab.ctrbonus.superpower1“, “purchase_date”: “2012-07-12 12:54:35 Etc/GMT”, “purchase_date_pst”: “2012-07-12 05:54:35 America/Los_Angeles”, “original_purchase_date”: “2012-07-12 12:54:35 Etc/GMT”, “item_id”: “534185042” }, “status”: 0 }

Where I’ve highlightedĀ com.zeptolab.ctrbonus.superpower1Ā – Which looks like ZeptoLab’s Cut the Rope Super Power 1 from 2012!,Ā  Not the IAP I was expecting at all.

Thankfully my validator code spotted this.

Here’s the code for decoding an Apple receipt in c#

private const string urlProduction = “https://buy.itunes.apple.com/verifyReceipt&#8221;;
private const string urlSandbox = “https://sandbox.itunes.apple.com/verifyReceipt&#8221;;

private string Verify(string receipt, string serviceUrl, string sharedSecret)
{
var json = string.Format(“{{\”receipt-data\”:\”{0}\”,\”password\”:\”{1}\”}}”, receipt, sharedSecret);
var wr = WebRequest.Create(serviceUrl);
wr.ContentType = “text/plain”;
wr.Method = “POST”;
var sw = new System.IO.StreamWriter(wr.GetRequestStream());
sw.Write(json);
sw.Flush();
sw.Close();
var wresp = wr.GetResponse();
if (wresp != null)
{
var sr = new System.IO.StreamReader(wresp.GetResponseStream());
var response = sr.ReadToEnd();
sr.Close();
return response;
}
return “no-data”;
}

 

 

Categories: Uncategorized