1+ import { Observable } from 'rxjs/Observable' ;
2+
3+ const REFERENCE_TYPE_STRING = 1 ;
4+ const REFERENCE_TYPE_HEX = 0 ;
5+ const HASH_METHOD = 'sha256' ;
6+
7+ export class DynamicUUID {
8+ /**
9+ * Generate a UUID
10+ *
11+ * @param serviceId The PAY. service-id from one of your sales locations
12+ * @param secret Your custom secret
13+ * @param reference Your reference to the transaction
14+ * @param padChar The reference will be padded with this character, default pad '0'
15+ * @param referenceType Define if you are using a string (8 chars) of hex (16 chars)
16+ */
17+ static encode ( serviceId : string , secret : string , reference : string , padChar = '0' , referenceType = REFERENCE_TYPE_STRING ) : Observable < string > {
18+ return Observable . create ( observable => {
19+ if ( referenceType == REFERENCE_TYPE_STRING ) {
20+ if ( ! this . isValidReferenceString ( reference ) ) {
21+ observable . error ( 'Secret invalid: ' + secret ) ;
22+ observable . complete ( ) ;
23+ return ;
24+ }
25+ reference = this . asciiToHex ( reference ) ;
26+ } else {
27+ if ( ! this . isValidHexString ( reference ) ) {
28+ observable . error ( 'Reference is not a valid hex: ' + reference ) ;
29+ observable . complete ( ) ;
30+ return ;
31+ }
32+ }
33+ reference = reference . toLowerCase ( ) ;
34+
35+ if ( ! this . isValidSecret ( secret ) ) {
36+ observable . error ( 'Secret invalid: ' + secret ) ;
37+ observable . complete ( ) ;
38+ return ;
39+ }
40+ if ( ! this . isValidServiceId ( serviceId ) ) {
41+ observable . error ( 'serviceId invalid: ' + serviceId ) ;
42+ observable . complete ( ) ;
43+ return ;
44+ }
45+ if ( ! this . isValidPadChar ( padChar ) ) {
46+ observable . error ( 'padChar invalid: ' + padChar ) ;
47+ observable . complete ( ) ;
48+ return ;
49+ }
50+
51+ var uuid = serviceId . replace ( / [ ^ 0 - 9 ] / g, '' ) + reference . padStart ( 16 , padChar ) ;
52+ var crypto = require ( 'crypto' ) ;
53+ var hash = crypto . createHmac ( HASH_METHOD , secret ) . update ( uuid ) . digest ( 'hex' ) ;
54+
55+ uuid = 'b' + hash . substring ( 0 , 7 ) + uuid ;
56+
57+ uuid =
58+ uuid . substring ( 0 , 8 ) + '-' +
59+ uuid . substring ( 8 , 12 ) + '-' +
60+ uuid . substring ( 12 , 16 ) + '-' +
61+ uuid . substring ( 16 , 20 ) + '-' +
62+ uuid . substring ( 20 , 32 ) ;
63+
64+ observable . next ( uuid ) ;
65+ observable . complete ( ) ;
66+ } ) ;
67+ }
68+
69+ /**
70+ * Decode a UUID
71+ *
72+ * @param uuid The UUID to decode
73+ * @param secret If supplied the uuid will be validated before decoding.
74+ * @param padChar The reference will be padded with this character, default '0'
75+ * @param referenceType
76+ */
77+ static decode ( uuid : string , secret = '' , padChar = '0' , referenceType = REFERENCE_TYPE_STRING ) : Observable < any > {
78+
79+ return Observable . create ( observable => {
80+
81+ if ( secret != '' ) {
82+ if ( ! this . isValidSecret ( secret ) ) {
83+ observable . error ( 'Secret is invalid: ' + secret ) ;
84+ observable . complete ( ) ;
85+ return
86+ }
87+ }
88+
89+ uuid = uuid . replace ( / [ ^ 0 - 9 a - z ] / ig, '' ) ;
90+ uuid = uuid . substr ( 8 , uuid . length ) ;
91+
92+ var serviceid = 'SL-' + uuid . substring ( 0 , 4 ) + '-' + uuid . substring ( 4 , 8 ) ;
93+ var referenceCode = uuid . substring ( 8 , uuid . length ) ;
94+
95+ referenceCode = this . ltrim ( referenceCode , padChar ) ;
96+
97+ var referenc_hex = referenceCode ;
98+ var reference_string = Buffer . from ( referenceCode , "hex" ) ;
99+
100+ var obj = {
101+ 'serviceId' : serviceid ,
102+ 'reference' : referenceType == REFERENCE_TYPE_HEX ? referenc_hex : reference_string
103+ } ;
104+
105+ observable . next ( obj ) ;
106+ observable . complete ( ) ;
107+ } ) ;
108+ }
109+
110+ /**
111+ * Validate UUID
112+ *
113+ * @param uuid Your UUID code
114+ * @param secret Your secret
115+ */
116+ static validate ( uuid : string , secret = '' ) : Observable < string > {
117+ return Observable . create ( observable => {
118+
119+ if ( secret != '' ) {
120+ if ( ! this . isValidSecret ( secret ) ) {
121+ observable . error ( 'Secret is invalid: ' + secret ) ;
122+ observable . complete ( ) ;
123+ return
124+ }
125+ }
126+
127+ var uuidData = uuid . replace ( / [ ^ 0 - 9 a - z ] / ig, '' ) ;
128+ uuidData = uuidData . substring ( 8 , uuidData . length ) ;
129+
130+ var crypto = require ( 'crypto' ) ;
131+ var hash = crypto . createHmac ( HASH_METHOD , secret ) . update ( uuidData ) . digest ( 'hex' ) ;
132+
133+ var checksum = 'b' + hash . substring ( 0 , 7 ) ;
134+
135+ observable . next ( checksum == uuid . substring ( 0 , 8 ) ) ;
136+ observable . complete ( ) ;
137+ } ) ;
138+ }
139+
140+ private static ltrim ( str , charlist ) {
141+ charlist = ! charlist ? ' \\s\u00A0' : ( charlist + '' )
142+ . replace ( / ( [ [ \] ( ) . ? / * { } + $ ^ : ] ) / g, '$1' )
143+ var re = new RegExp ( '^[' + charlist + ']+' , 'g' )
144+ return ( str + '' )
145+ . replace ( re , '' )
146+ }
147+
148+ private static asciiToHex ( str ) {
149+ var arr1 = [ ] ;
150+ for ( var n = 0 , l = str . length ; n < l ; n ++ ) {
151+ var hex = Number ( str . charCodeAt ( n ) ) . toString ( 16 ) . toUpperCase ( ) ;
152+ arr1 . push ( hex ) ;
153+ }
154+ return arr1 . join ( '' ) ;
155+ }
156+
157+ private static isValidReferenceString ( reference ) {
158+ return / ^ [ 0 - 9 a - z A - Z ] { 0 , 8 } $ / i. test ( reference )
159+ }
160+
161+ private static isValidHexString ( hexString ) {
162+ return / ^ [ 0 - 9 a - f ] { 0 , 16 } $ / i. test ( hexString )
163+ }
164+
165+ private static isValidSecret ( secret ) {
166+ return / ^ [ 0 - 9 a - f ] { 40 } $ / i. test ( secret ) ;
167+ }
168+
169+ private static isValidServiceId ( serviceId ) {
170+ return / ^ S L - [ 0 - 9 ] { 4 } - [ 0 - 9 ] { 4 } $ / . test ( serviceId ) ;
171+ }
172+
173+ private static isValidPadChar ( padChar ) {
174+ return / ^ [ a - z 0 - 9 ] { 1 } $ / i. test ( padChar ) ;
175+ }
176+
177+ }
0 commit comments