Skip to content

Commit dc43c1f

Browse files
committed
first commit
0 parents  commit dc43c1f

4 files changed

Lines changed: 5615 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# settle-request-generator

index.html

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<html>
2+
<header>
3+
<link rel="stylesheet" href="https://unpkg.com/purecss@1.0.1/build/pure-min.css"
4+
integrity="sha384-oAOxQR6DkCoMliIh8yFnu25d7Eq/PHS21PClpwjOTeU2jRSq11vu66rf90/cZr47" crossorigin="anonymous">
5+
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
6+
<script src="jsencrypt.js"></script>
7+
8+
<style>
9+
body {
10+
padding: 40px;
11+
font-family: monospace;
12+
}
13+
14+
.wide {
15+
width: 530px;
16+
}
17+
18+
label {
19+
font-weight: bold;
20+
}
21+
22+
.change-animation {
23+
animation: pulse 0.5s 1;
24+
}
25+
26+
@keyframes pulse {
27+
0% {
28+
background-color: rgb(230, 255, 230);
29+
}
30+
31+
100% {
32+
background-color: #eee;
33+
}
34+
}
35+
</style>
36+
</header>
37+
38+
<body>
39+
<h1 id="test">Settle API PKCS#1 1.5 signature generator</h1>
40+
<div class="pure-form pure-form-aligned">
41+
42+
<div class="pure-control-group">
43+
<label for="merchant_id">Merchant ID</label>
44+
<input type="text" name="merchant_id" id="merchant_id"></input>
45+
</div>
46+
47+
<div class="pure-control-group">
48+
<label for="api_user">API User</label>
49+
<input type="text" name="api_user" id="api_user"></input>
50+
</div>
51+
52+
<div class="pure-control-group">
53+
<label for="private_key">Private key</label>
54+
<textarea name="private_key" id="private_key" cols="70" rows="15"></textarea>
55+
</div>
56+
57+
<div class="pure-control-group">
58+
<label for="method">Method</label>
59+
<input type="text" name="method" id="method"></input>
60+
</div>
61+
62+
<div class="pure-control-group">
63+
<label for="url">URL</label>
64+
<input class='wide' type="text" name="url" id="url" cols="70"></input>
65+
</div>
66+
67+
<div class="pure-control-group">
68+
<label for="body">Request body</label>
69+
<textarea name="body" id="body" cols="70" rows="10"></textarea>
70+
<span class="pure-form-message-inline">Pro tip:<br />Will autogenrate headers on focus lost</span>
71+
</div>
72+
73+
<div class="pure-control-group">
74+
<label for="bulk_headers">Bulk headers</label>
75+
<textarea class="change-animation" name="bulk_headers" id="bulk_headers" cols="70" rows="15" readonly></textarea>
76+
<span class="pure-form-message-inline">Pro tip:<br />Click text area to copy headers</span>
77+
</div>
78+
79+
<div class="pure-control-group">
80+
<label for="generate"></label>
81+
<button class="pure-button" id="generate">Generate headers!</button>
82+
</div>
83+
84+
</div>
85+
86+
<script src="index.js"></script>
87+
</body>
88+
89+
</html>

index.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// dec2hex :: Integer -> String
2+
// i.e. 0-255 -> '00'-'ff'
3+
function dec2hex(dec) {
4+
return ('0' + dec.toString(16)).substr(-2)
5+
}
6+
7+
// generateId :: Integer -> String
8+
function generateId(len) {
9+
var arr = new Uint8Array((len || 10) / 2)
10+
window.crypto.getRandomValues(arr)
11+
return `"${Array.from(arr, dec2hex).join('')}"`
12+
}
13+
14+
// getAukaTimestamp :: Void -> String
15+
function getAukaTimestamp() {
16+
const d = new Date();
17+
const fYear = d.getFullYear().toString()
18+
const month = (d.getMonth()+1).toString()
19+
const day = d.getDate().toString()
20+
const hours = d.getHours().toString()
21+
const minutes = (parseInt(d.getMinutes())).toString()
22+
const seconds = (parseInt(d.getSeconds())).toString()
23+
24+
const fMonth = month.length == 2 ? month : `0${month}`
25+
const fDay = day.length == 2 ? day : `0${day}`
26+
const fHours = hours.length == 2 ? hours: `0${hours}`
27+
const fMinutes = minutes.length == 2 ? minutes: `0${minutes}`
28+
const fSeconds = seconds.length == 2 ? seconds: `0${seconds}`
29+
30+
return `${fYear}-${fMonth}-${fDay} ${fHours}:${fMinutes}:${fSeconds}`;
31+
}
32+
33+
// generatePubKey :: String -> Void
34+
function generatePubKey(privateKey) {
35+
console.log('Generating new public key from private key')
36+
const crypt = new JSEncrypt();
37+
crypt.setKey(privateKey);
38+
document.getElementById('public_key').value = crypt.getPublicKey()
39+
}
40+
41+
// getHash :: String -> String
42+
function getHash(body) {
43+
return CryptoJS.SHA256(body).toString(CryptoJS.enc.Base64)
44+
}
45+
46+
// getSignedMessage :: String -> String
47+
function getSignedMessage(privateKey, message) {
48+
const crypt = new JSEncrypt();
49+
crypt.setKey(privateKey);
50+
return crypt.sign(message, CryptoJS.SHA256, "sha256");
51+
}
52+
53+
// triggerAnimation :: Object -> Void
54+
function triggerAnimation(element) {
55+
element.classList.remove("change-animation");
56+
void element.offsetWidth;
57+
element.classList.add("change-animation");
58+
}
59+
60+
// copyToClipboard :: Object -> Void
61+
function copyToClipboard(event) {
62+
const element = event.target
63+
void triggerAnimation(element)
64+
console.log(`Copying contents of ${element.name} to clipboard`)
65+
element.select()
66+
document.execCommand('copy')
67+
window.getSelection().removeAllRanges()
68+
}
69+
70+
const examplePrivateKey =
71+
`-----BEGIN RSA PRIVATE KEY-----
72+
MIICXQIBAAKBgQCotJXYCo9VPuS1qjBRPAP5jAN9Wj8qmYnKMy31w81jiL3QegVQ/w6pCoy3
73+
WdLdVRxBSV4bCcMEAbhHXKgzpX+vB2P6sbmFJkucu1RQMHs9B7JevixVKowdl+U4QooFbtr2
74+
JgBFeC4DMIADvBzzEbvKHRRU7hyhDVDjDmrQtC2nlwsfIDAiQABAocGAVCx+EyP5rgPKY2W9
75+
cHYjfqQekFFOlpbG2K5sagjPVW5Hu6qzbjyaCKlcvSwBxFKxM0mfD9PjYLFb1tUBqdlyBQFk
76+
v6jwIi7Xt0hErX6lcCuSFckM3/P3pC2w952v47HA35zdqC1aYW4bKqQ+cGfDqQI8eUFI4kUg
77+
Quw4cAkCQQD2BP6gxasdSEQhkcfaHmxuJGVH5DGHcZ7krG5zjd8CEAACShPS0cL71gkzVTX0
78+
3FYwLW8z6Z+tMGwZRxgDKKyTAkEAr4nlXVzDJJbfP0fJfOnXUyT4XcFu/iw0HRRknaqn79J3
79+
uWSCKqDGbS3cgXCYlrcGrIMWQjNDiiE1+1EWk0qw4wsd/bQJBANEbfbH43BnHiDxR+N3uGw+
80+
9XrJhvLPT3b6C/wLM3N1d/MTGgC3xFjuPKN5EWdk8zby+RQdMwnkGlj82IUvSBR5gwQcLbg6
81+
JkRyc02S0eZCvONK5VBAk6q5TRo6zJqaCnVunOwqoxdTrNOLRNvMiG9OLECQcC5/lBgLfwjS
82+
jFUCQQCEKcuxG/huGU5PuNRKA5TfpOE9l0cquiI9613YdbbUszc19PWXFywu5ttHzp29y3jE
83+
5YKEFh8qz9a4GK5ejcIr
84+
-----END RSA PRIVATE KEY-----`
85+
86+
const exampleBody =
87+
`{
88+
"payer": "abc123",
89+
"payee": "msisdn:47123456789",
90+
"idempotency_id": ${generateId()},
91+
"currency": "NOK",
92+
"amount": 1000,
93+
"require_identified": "True",
94+
"chat_text": "Some text",
95+
"expires_in": 120
96+
}`;
97+
98+
// setDefaultValues :: Void
99+
function setDefaultValues() {
100+
const method = 'POST'
101+
const url = 'https://api.sandbox.settle.eu/merchant/v1/payment_request/'
102+
const merchantID = 'abc123'
103+
const apiUser = 'xyz789'
104+
const privateKey = examplePrivateKey
105+
const body = exampleBody
106+
107+
document.getElementById('private_key').value = privateKey
108+
document.getElementById('method').value = method
109+
document.getElementById('url').value = url
110+
document.getElementById('body').value = body
111+
document.getElementById('merchant_id').value = merchantID
112+
document.getElementById('api_user').value = apiUser
113+
}
114+
115+
// generateHeaders :: Void
116+
function generateHeaders() {
117+
console.log('Generating hedears')
118+
const element = document.getElementById('bulk_headers');
119+
void triggerAnimation(element)
120+
121+
const privateKey = document.getElementById('private_key').value
122+
const method = document.getElementById('method').value
123+
const url = document.getElementById('url').value
124+
const merchantID = document.getElementById('merchant_id').value
125+
const timestamp = getAukaTimestamp()
126+
const apiUser = document.getElementById('api_user').value
127+
const body = document.getElementById('body').value
128+
const bodyHash = getHash(body)
129+
const contentDigest = `SHA256=${bodyHash}`
130+
const headers = `X-AUKA-CONTENT-DIGEST=${contentDigest}&X-AUKA-MERCHANT=${merchantID}&X-AUKA-TIMESTAMP=${timestamp}&X-AUKA-USER=${apiUser}`
131+
const message = `${method}|${url}|${headers}`
132+
const signature = getSignedMessage(privateKey, message)
133+
const authorization = 'RSA-SHA256 ' + signature
134+
const bulkHeaders =
135+
`Accept:application/vnd.mcash.api.merchant.v1+json
136+
Content-Type:application/json
137+
X-Auka-Merchant:${merchantID}
138+
X-Auka-User:${apiUser}
139+
X-Auka-Timestamp:${timestamp}
140+
X-Auka-Content-Digest:${contentDigest}
141+
Authorization:${authorization}`
142+
143+
element.value = bulkHeaders;
144+
}
145+
146+
// Main script
147+
document.getElementById('generate').onclick = () => generateHeaders()
148+
document.getElementById('method').onchange = () => generateHeaders()
149+
document.getElementById('url').onchange = () => generateHeaders()
150+
document.getElementById('body').onchange = () => generateHeaders()
151+
document.getElementById('bulk_headers').onclick = event => copyToClipboard(event)
152+
153+
154+
setDefaultValues()
155+
generateHeaders()

0 commit comments

Comments
 (0)