Skip to content

Commit

Permalink
Merge branch 'master' into feat/oneclick-fullTransaction
Browse files Browse the repository at this point in the history
  • Loading branch information
a1b4 authored Dec 26, 2019
2 parents 1afa138 + 1f29e93 commit 78dfb7a
Show file tree
Hide file tree
Showing 11 changed files with 351 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -337,3 +337,4 @@ coverage/
opencover.xml

.[Dd][Ss]_[Ss][Tt][Oo][Rr][Ee]
.leu
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ Todos los cambios notables a este proyecto serán documentados en este archivo.
El formato está basado en [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
y este proyecto adhiere a [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [2.3.0] - 2019-11-12

### Added

- Se agregan las clases necesarias para conectarse al Websocket de Onepay. Estas clases pueden ser utilizadas independientes, pero estan especialmente diseñadas para ser utilizadas por el SDK de POSIntegrado

- Se agregan templates para crear Issues en Github.

## [2.2.1] - 2019-05-20

### Fixed
Expand Down
22 changes: 22 additions & 0 deletions Transbank/Onepay/Exceptions/HttpHelperException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;


namespace Transbank.Onepay.Exceptions
{
public class HttpHelperException : TransbankException
{
public HttpHelperException() : base()
{
}

public HttpHelperException(string message)
: base(-1, message)
{
}

public HttpHelperException(string message, Exception innerException)
: base(-1, message, innerException)
{
}
}
}
22 changes: 22 additions & 0 deletions Transbank/Onepay/Exceptions/Sigv4UtilException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;


namespace Transbank.Onepay.Exceptions
{
public class Sigv4UtilException : TransbankException
{
public Sigv4UtilException() : base()
{
}

public Sigv4UtilException(string message)
: base(-1, message)
{
}

public Sigv4UtilException(string message, Exception innerException)
: base(-1, message, innerException)
{
}
}
}
25 changes: 25 additions & 0 deletions Transbank/Onepay/IOnepayPayment.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MQTTnet;
using MQTTnet.Client;


namespace Transbank.Onepay
{
public interface IOnepayPayment
{
int Ticket { get; }
int Total { get; }
string ExternalUniqueNumber { get; }
string Occ { get; }
string Ott { get; }

void Connected();
void NewMessage(string payload);
void Disconnected();
}

}
14 changes: 14 additions & 0 deletions Transbank/Onepay/Model/WebSocketMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Transbank.Onepay.Model
{
public class WebsocketMessage
{
public string status;
public string description;

public override string ToString()
{
return "Status: " + status + "\n" +
"Description: " + description;
}
}
}
20 changes: 20 additions & 0 deletions Transbank/Onepay/Model/WebsocketCredentials.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Transbank.Onepay.Model
{
public class WebsocketCredentials
{
public string iotEndpoint;
public string region;
public string accessKey;
public string secretKey;
public string sessionToken;

public override string ToString()
{
return "Endpoint: " + iotEndpoint + "\n" +
"Region: " + region + "\n" +
"Acces Key: " + accessKey + "\n" +
"SecretKey: " + secretKey + "\n" +
"SessionToken: " + sessionToken;
}
}
}
36 changes: 36 additions & 0 deletions Transbank/Onepay/Utils/HttpHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Text;
using Transbank.Onepay.Exceptions;

namespace Transbank.Onepay.Utils
{
public static class HttpHelper
{
// The Set of accepted and valid Url characters per RFC3986. Characters outside of this set will be encoded.
const string ValidUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

public static string UrlEncode(string data, bool isPath = false)
{

var encoded = new StringBuilder(data.Length * 2);

try
{
string unreservedChars = String.Concat(ValidUrlCharacters, (isPath ? "/:" : ""));

foreach (char symbol in Encoding.UTF8.GetBytes(data))
{
if (unreservedChars.IndexOf(symbol) != -1)
encoded.Append(symbol);
else
encoded.Append("%").Append(String.Format("{0:X2}", (int)symbol));
}
}
catch (Exception e)
{
throw new HttpHelperException("Unable to encode URL", e);
}
return encoded.ToString();
}
}
}
131 changes: 131 additions & 0 deletions Transbank/Onepay/Utils/Sigv4util.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using System;
using System.Security.Cryptography;
using System.Text;
using System.Globalization;
using Transbank.Onepay.Exceptions;
using Transbank.Onepay.Model;

namespace Transbank.Onepay.Utils
{
public static class Sigv4util
{
public const string ISO8601BasicFormat = "yyyyMMddTHHmmssZ";
public const string DateStringFormat = "yyyyMMdd";
public const string EmptyBodySha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
public static HashAlgorithm CanonicalRequestHashAlgorithm = HashAlgorithm.Create("SHA-256");
public const string HmacSha256 = "HMACSHA256";
public const string XAmzSignature = "X-Amz-Signature";

private static byte[] HmacSHA256(String data, byte[] key)
{
var algorithm = "HmacSHA256";
var keyHashAlgorithm = KeyedHashAlgorithm.Create(algorithm);
keyHashAlgorithm.Key = key;

return keyHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(data));
}

private static byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data)
{
var kha = KeyedHashAlgorithm.Create(algorithm);
kha.Key = key;
return kha.ComputeHash(data);
}

public static string ToHexString(byte[] data, bool lowerCase)
{
var stringBuilder = new StringBuilder();

try
{
for (var i = 0; i < data.Length; i++)
{
stringBuilder.Append(data[i].ToString(lowerCase ? "x2" : "X2"));
}

}
catch (Exception e)
{
throw new Sigv4UtilException("Unable to convert data to hex string", e);
}
return stringBuilder.ToString();
}

private static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName)
{
byte[] kSecret = Encoding.UTF8.GetBytes(("AWS4" + key).ToCharArray());
byte[] kDate = HmacSHA256(dateStamp, kSecret);
byte[] kRegion = HmacSHA256(regionName, kDate);
byte[] kService = HmacSHA256(serviceName, kRegion);
byte[] kSigning = HmacSHA256("aws4_request", kService);

return kSigning;
}

public static string getSignedurl(WebsocketCredentials credentials)
{
string requestUrl;
try
{

var requestDateTime = DateTime.UtcNow;
string datetime = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture);
var date = requestDateTime.ToString(DateStringFormat, CultureInfo.InvariantCulture);

string method = "GET";
string protocol = "wss";
string uri = "/mqtt";
string service = "iotdevicegateway";
string algorithm = "AWS4-HMAC-SHA256";

string credentialScope = date + "/" + credentials.region + "/" + service + "/" + "aws4_request";
string canonicalQuerystring = "X-Amz-Algorithm=" + algorithm;
canonicalQuerystring += "&X-Amz-Credential=" + HttpHelper.UrlEncode(credentials.accessKey + '/' + credentialScope);

canonicalQuerystring += "&X-Amz-Date=" + datetime;
canonicalQuerystring += "&X-Amz-Expires=86400";
canonicalQuerystring += "&X-Amz-SignedHeaders=host";

string canonicalHeaders = "host:" + credentials.iotEndpoint + "\n";

var canonicalRequest = method + "\n" + uri + "\n" + canonicalQuerystring + "\n" + canonicalHeaders + "\n" + "host" + "\n" + EmptyBodySha256;

byte[] hashValueCanonicalRequest = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest));

var builder = new StringBuilder();

for (int i = 0; i < hashValueCanonicalRequest.Length; i++)
{
builder.Append(hashValueCanonicalRequest[i].ToString("x2"));
}

string byteString = builder.ToString();

var stringToSign = algorithm + "\n" + datetime + "\n" + credentialScope + "\n" + byteString;

// compute the signing key
var keyedHashAlgorithm = KeyedHashAlgorithm.Create(HmacSha256);

keyedHashAlgorithm.Key = getSignatureKey(credentials.secretKey, date, credentials.region, service);

var signingKey = keyedHashAlgorithm.Key;

var signature = ComputeKeyedHash(HmacSha256, signingKey, Encoding.UTF8.GetBytes(stringToSign));
var signatureString = ToHexString(signature, true);

canonicalQuerystring += "&X-Amz-Signature=" + signatureString;
canonicalQuerystring += "&X-Amz-Security-Token=" + HttpHelper.UrlEncode(credentials.sessionToken);

requestUrl = protocol + "://" + credentials.iotEndpoint + uri + "?" + canonicalQuerystring;
}

catch (Exception e)
{
throw new Sigv4UtilException("Unable to get signed url", e);
}


return requestUrl;
}
}
}
68 changes: 68 additions & 0 deletions Transbank/Onepay/Websocket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Options;
using Newtonsoft.Json;
using Transbank.Onepay.Model;
using Transbank.Onepay.Utils;

namespace Transbank.Onepay
{
public class Websocket
{
private static readonly string OnepayIotEndpoint = "https://qml1wjqokl.execute-api.us-east-1.amazonaws.com/prod/onepayjs/auth/keys";
internal WebsocketCredentials Credentials;

public IMqttClient mqttClient;
public IMqttClientOptions mqttClientOptions;

public Websocket()
{
Credentials = FetchCredentials();

string requestUrl = Sigv4util.getSignedurl(Credentials);

var factory = new MqttFactory();
mqttClient = factory.CreateMqttClient();

mqttClientOptions = new MqttClientOptionsBuilder().
WithWebSocketServer(requestUrl).
WithKeepAlivePeriod(TimeSpan.FromMinutes(10)).Build();

}

public async Task ConnectAsync(IOnepayPayment obj)
{
mqttClient.UseConnectedHandler(e => {
obj.Connected();
});

mqttClient.UseApplicationMessageReceivedHandler(e => {
obj.NewMessage(Encoding.UTF8.GetString(e.ApplicationMessage.Payload));
});

mqttClient.UseDisconnectedHandler(e => {
obj.Disconnected();
});

_ = await mqttClient.ConnectAsync(mqttClientOptions, CancellationToken.None);
_ = await mqttClient.SubscribeAsync(new TopicFilterBuilder().WithTopic(obj.Ott).Build());
}

private WebsocketCredentials FetchCredentials()
{
var request = (HttpWebRequest)WebRequest.Create(OnepayIotEndpoint);

var responseStream = request.GetResponse().GetResponseStream();
var streamReader = new StreamReader(responseStream);
var credentialsAsJson = streamReader.ReadToEnd();
return JsonConvert.DeserializeObject<WebsocketCredentials>(credentialsAsJson);
}

}
}
7 changes: 4 additions & 3 deletions Transbank/Transbank.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
<RepositoryType>git</RepositoryType>
<NeutralLanguage>en-US</NeutralLanguage>
<PackageTags>payments, cl, chile, transbank</PackageTags>
<PackageIconUrl>http://www.transbankdevelopers.cl/library/img/favicon/mstile-310x310.png</PackageIconUrl>
<VersionPrefix>2.2.2</VersionPrefix>
<PackageIconUrl>https://www.transbankdevelopers.cl/favicon.ico</PackageIconUrl>
<VersionPrefix>2.3.1</VersionPrefix>
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
<Product>TransbankSDK</Product>
<Copyright>2018 - Transbank</Copyright>
Expand Down Expand Up @@ -109,7 +109,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.Web.Services3" Version="3.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="MQTTnet" Version="3.0.8" />
</ItemGroup>

<ItemGroup>
Expand Down

0 comments on commit 78dfb7a

Please sign in to comment.