4.3. OAuth

4.3.1. OAuth Overview

This page allows you to generate OAuth signatures using a known good OAuth 1.0a library, https://oauth.net/1/. This way you can quickly identify if 403 Forbidden error received from the PaynetEasy API server is due to a bad signature or another issue.

4.3.2. OAuth Implementation

Key:
HMAC key value is the concatenation of consumer secret (Merchant control key) + & + token secret (empty string).
For example, it will look as follows:
F9F65098-1111-1111-1111-621611111111&
Request body and data string to sign:
Parameters in request body must be arranged in lexicographical order.
Unique random nonce and current timestamp are generated by merchant.
OAuth parameters must be included in request body (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version).
Whole request is used as data string to calculate HMAC-SHA1 signature.
URL and parameters in data string must be percent-encoded and have the following structure:
POST&URL&parameters
Headers and signature:
Authorization header must start from Authorization: OAuth, have oauth_signature and the same OAuth parameters and values as were included in request body (oauth_consumer_key, oauth_nonce, oauth_signature_method, oauth_timestamp, oauth_version).
Merchant login is used as oauth_consumer_key in data string and authorization header, but it is not used as a part of the signing HMAC key.
HMAC-SHA1 signature in binary format must be Base64 and percent-encoded. The signature is sent in request header named ‘Authorization’.
For example, it will look as follows:
9bSeUoR5yJSSJ4KPhotT%2BofEHSQ%3D
Authorization header example:
OAuth realm="",
oauth_version="1.0",
oauth_signature_method="HMAC-SHA1",
oauth_consumer_key="merchantlogin",
oauth_timestamp="1513785920",
oauth_nonce="EqINVv5rkhx",
oauth_signature="9bSeUoR5yJSSJ4KPhotT%2BofEHSQ%3D"

Java implementation example

Here is an example of how to make a signed request in Java with help of Scribe library:
public String doPost(String url, Map<String, String>parameters) throws ConnectionIOException, ConnectionTimeoutException {
    OAuthConfig config = new OAuthConfig(apiToken, merchantControlKey, OAuthConstants.OUT_OF_BAND,
            SignatureType.Header, null,null);
    OAuthService service = new OAuth10aServiceImpl(new HmacSha1Mapi(), config);
    OAuthRequest request = new OAuthRequest(Verb.POST,url);
    for (Map.Entry < String,String >;entry :parameters.entrySet()){
        request.addBodyParameter(entry.getKey(), entry.getValue());
    } // empty token for 'two-legged'
    OAuth Token token = new Token("", "");
    service.signRequest(token, request);
    Response response = request.send();
    return response.getBody();
}

private static class Mapi extends DefaultApi {
    @Override
    public String getRequestTokenEndpoint() {
        return null; // not used
    } @Override public String getAccessTokenEndpoint() {
        return null; //not used
    }

    @Override
    public String getAuthorizationUrl(Token requestToken) {
        return null; // not used
    }
    @Override
    public SignatureService getSignatureService() {
        return new HMACSha1SignatureService();
    }
}

C# implementation example

This example shows the C# OAuth implementation for Payout integration:
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Net;

namespace ConsoleApplication1
{
    class Program

    {

        static void Main(string[] args)
        {
            string apiUrl = "https://sandbox.payneteasy.com/paynet/api/v2/payout/123";

            string consumerKey = "merchantlogin";
            string consumerSecret = "1EF4D28C-1111-2222-3333-444487505555";

            /* for oauth_timestamp */
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            string timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();

            /* for oauth_nonce */
            /* (obviously you wouldn't create a new Random class every time!) */
            string nonce = new Random().Next(123400, 9999999).ToString();


            //oauth parameters, need sort alpabetical, use SortedDictionary
            IDictionary<string,string> oauthParams = new SortedDictionary<string, string>();
            oauthParams.Add("oauth_consumer_key",consumerKey);
            oauthParams.Add("oauth_nonce",nonce);
            oauthParams.Add("oauth_timestamp",timestamp);
            oauthParams.Add("oauth_signature_method","HMAC-SHA1");
            oauthParams.Add("oauth_version","1.0");


            //Prepare POST request params , add oauth params
            IDictionary<string,string> requestParameters = new SortedDictionary<string, string>(oauthParams);

            requestParameters.Add("account_number", "1234567890");
            requestParameters.Add("amount", "100");
            requestParameters.Add("bank_branch", "test_branch");
            requestParameters.Add("bank_name", "test_bank");
            requestParameters.Add("client_orderid", "12345");
            requestParameters.Add("currency", "USD");

            StringBuilder builder = new StringBuilder();

            foreach (KeyValuePair<string,string> pair in requestParameters)
            {
                if (builder.Length > 0)
                {
                    builder.Append("&");
                }
                //ATTENTION, call  Uri.EscapeDataString
                builder.Append(Uri.EscapeDataString(pair.Key)).Append("=").Append(Uri.EscapeDataString(pair.Value));
            }
            string normalizedParameters = builder.ToString();



            //create signature base string
            builder = new StringBuilder();

            builder.Append("POST").Append("&");
            builder.Append(Uri.EscapeDataString(apiUrl)).Append("&");
            builder.Append(Uri.EscapeDataString(normalizedParameters));

            string signatureBaseString = builder.ToString();


            // calculate signature, always add & to end consumerSecret
            byte[] signatureKeyBytes = Encoding.UTF8.GetBytes(consumerSecret + "&");

            HMACSHA1 sha1 = new HMACSHA1(signatureKeyBytes);

            /* generate the signature and add it to our parameters */
            byte[] baseStringBytes = Encoding.UTF8.GetBytes(signatureBaseString);
            byte[] baseStringHash = sha1.ComputeHash(baseStringBytes);
            string base64StringHash = Convert.ToBase64String(baseStringHash);
            oauthParams.Add("oauth_signature", base64StringHash);


            //create oauth Header
            string authHeader = createOAuthHeader(oauthParams);

            /* we are ready to send the request! */

            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(apiUrl);
            request.Headers.Add("Authorization", authHeader);
            var data = Encoding.UTF8.GetBytes(normalizedParameters);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;
            request.Timeout = 30*1000;
            request.UserAgent = "DotNet";

            using (var stream = request.GetRequestStream())
            {
                stream.Write(data, 0, data.Length);
            }

            var response = (HttpWebResponse) request.GetResponse();
            var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            System.Console.WriteLine(responseString);
        }

        private static string createOAuthHeader(IDictionary<string, string> oauthParams)
        {
            StringBuilder builder = new StringBuilder("OAuth realm=\"\"");

            foreach (KeyValuePair<string, string> pair in oauthParams)
            {
                if (builder.Length > 0)
                {
                    builder.Append(",");
                }
                //ATTENTION, call  Uri.EscapeDataString
                builder.Append(string.Format("{0}=\"{1}\"", Uri.EscapeDataString(pair.Key), Uri.EscapeDataString(pair.Value)));
            }

            return builder.ToString();
        }
    }
}

4.3.3. Debug

To reproduce your API call, input all of the data from your original request, including the authentication tokens. Don’t forget to set the nonce and timestamp to the values you used. An OAuth signed URL should match regardless of the generating library. If the signatures differ, you know there is a bug in your OAuth signature code.

HTTP method
URL
parameters
version
consumer key
consumer secret
timestamp
nonce
signature method

normalized parameters
signature base string
signature
authorization header
Curl
              
            

4.3.4. Libraries

We use a simplified flow in which there’s no external authenticator, and there’s no access token and its secret; only the last step of the flow is used.
Merchant login is specified as client identifier (aka consumer key or API token).
A lot of OAuth 1.0a libraries for various programming languages can be found here (last checked 21.12.2017).
Some libraries which may be used: