",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width
i?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.widget("ui.menu",{version:"1.12.1",defaultElement:"",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault()},"click .ui-menu-item":function(e){var i=t(e.target),s=t(t.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&s.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){if(!this.previousFilter){var i=t(e.target).closest(".ui-menu-item"),s=t(e.currentTarget);i[0]===s[0]&&(this._removeClass(s.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(e,s))}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){var i=!t.contains(this.element[0],t.ui.safeActiveElement(this.document[0]));i&&this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){var e=this.element.find(".ui-menu-item").removeAttr("role aria-disabled"),i=e.children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),i.children().each(function(){var e=t(this);e.data("ui-menu-submenu-caret")&&e.remove()})},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||"",o=!1,n=e.keyCode>=96&&105>=e.keyCode?""+(e.keyCode-96):String.fromCharCode(e.keyCode),clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i,s,n,o,a=this,r=this.options.icons.submenu,l=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),s=l.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),i=e.prev(),s=t("").data("ui-menu-submenu-caret",!0);a._addClass(s,"ui-menu-icon","ui-icon "+r),i.attr("aria-haspopup","true").prepend(s),e.attr("aria-labelledby",i.attr("id"))}),this._addClass(s,"ui-menu","ui-widget ui-widget-content ui-front"),e=l.add(this.element),i=e.find(this.options.items),i.not(".ui-menu-item").each(function(){var e=t(this);a._isDivider(e)&&a._addClass(e,"ui-menu-divider","ui-widget-content")}),n=i.not(".ui-menu-item, .ui-menu-divider"),o=n.children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(n,"ui-menu-item")._addClass(o,"ui-menu-item-wrapper"),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){if("icons"===t){var i=this.element.find(".ui-menu-icon");this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)}this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t+""),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i,s,n;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children(".ui-menu-item-wrapper"),this._addClass(s,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),n=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(n,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this._removeClass(s.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(e){return!t(e.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),o="textarea"===n,a="input"===n;this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=this.element[o||a?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,void 0;e=!1,s=!1,i=!1;var o=t.ui.keyCode;switch(n.keyCode){case o.PAGE_UP:e=!0,this._move("previousPage",n);break;case o.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case o.UP:e=!0,this._keyEvent("previous",n);break;case o.DOWN:e=!0,this._keyEvent("next",n);break;case o.ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case o.TAB:this.menu.active&&this.menu.select(n);break;case o.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),void 0):(this._searchTimeout(t),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(t),this._change(t),void 0)}}),this._initSource(),this.menu=t("").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._addClass(this.menu.element,"ui-autocomplete","ui-front"),this._on(this.menu.element,{mousedown:function(e){e.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,this.element[0]!==t.ui.safeActiveElement(this.document[0])&&this.element.trigger("focus")})},menufocus:function(e,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,e.originalEvent&&/^mouse/.test(e.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){t(e.target).trigger(e.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",e,{item:n})&&e.originalEvent&&/^key/.test(e.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&t.trim(s).length&&(this.liveRegion.children().hide(),t("").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,i){var s=i.item.data("ui-autocomplete-item"),n=this.previous;this.element[0]!==t.ui.safeActiveElement(this.document[0])&&(this.element.trigger("focus"),this.previous=n,this._delay(function(){this.previous=n,this.selectedItem=s})),!1!==this._trigger("select",e,{item:s})&&this._value(s.value),this.term=this._value(),this.close(e),this.selectedItem=s}}),this.liveRegion=t("
",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(t,e){this._super(t,e),"source"===t&&this._initSource(),"appendTo"===t&&this.menu.element.appendTo(this._appendTo()),"disabled"===t&&e&&this.xhr&&this.xhr.abort()},_isEventTargetInWidget:function(e){var i=this.menu.element[0];return e.target===this.element[0]||e.target===i||t.contains(i,e.target)},_closeOnClickOutside:function(t){this._isEventTargetInWidget(t)||this.close()},_appendTo:function(){var e=this.options.appendTo;return e&&(e=e.jquery||e.nodeType?t(e):this.document.find(e).eq(0)),e&&e[0]||(e=this.element.closest(".ui-front, dialog")),e.length||(e=this.document[0].body),e},_initSource:function(){var e,i,s=this;t.isArray(this.options.source)?(e=this.options.source,this.source=function(i,s){s(t.ui.autocomplete.filter(e,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(e,n){s.xhr&&s.xhr.abort(),s.xhr=t.ajax({url:i,data:e,dataType:"json",success:function(t){n(t)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(t){clearTimeout(this.searching),this.searching=this._delay(function(){var e=this.term===this._value(),i=this.menu.element.is(":visible"),s=t.altKey||t.ctrlKey||t.metaKey||t.shiftKey;(!e||e&&!i&&!s)&&(this.selectedItem=null,this.search(null,t))},this.options.delay)},search:function(t,e){return t=null!=t?t:this._value(),this.term=this._value(),t.length
").append(t("").text(i.label)).appendTo(e)},_move:function(t,e){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[t](e),void 0):(this.search(null,e),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(t,e),e.preventDefault())},_isContentEditable:function(t){if(!t.length)return!1;var e=t.prop("contentEditable");return"inherit"===e?this._isContentEditable(t.parent()):"true"===e}}),t.extend(t.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(e,i){var s=RegExp(t.ui.autocomplete.escapeRegex(i),"i");return t.grep(e,function(t){return s.test(t.label||t.value||t)})}}),t.widget("ui.autocomplete",t.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(t>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.children().hide(),t("
").text(i).appendTo(this.liveRegion))}}),t.ui.autocomplete});
\ No newline at end of file
diff --git a/utilities/ondc-crypto-utility-master/docs/script-dir/jquery-ui.structure.min.css b/utilities/ondc-crypto-utility-master/docs/script-dir/jquery-ui.structure.min.css
new file mode 100644
index 0000000..e880892
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/docs/script-dir/jquery-ui.structure.min.css
@@ -0,0 +1,5 @@
+/*! jQuery UI - v1.12.1 - 2018-12-06
+* http://jqueryui.com
+* Copyright jQuery Foundation and other contributors; Licensed MIT */
+
+.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}
\ No newline at end of file
diff --git a/utilities/ondc-crypto-utility-master/docs/script.js b/utilities/ondc-crypto-utility-master/docs/script.js
new file mode 100644
index 0000000..864989c
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/docs/script.js
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+var moduleSearchIndex;
+var packageSearchIndex;
+var typeSearchIndex;
+var memberSearchIndex;
+var tagSearchIndex;
+function loadScripts(doc, tag) {
+ createElem(doc, tag, 'search.js');
+
+ createElem(doc, tag, 'module-search-index.js');
+ createElem(doc, tag, 'package-search-index.js');
+ createElem(doc, tag, 'type-search-index.js');
+ createElem(doc, tag, 'member-search-index.js');
+ createElem(doc, tag, 'tag-search-index.js');
+}
+
+function createElem(doc, tag, path) {
+ var script = doc.createElement(tag);
+ var scriptElement = doc.getElementsByTagName(tag)[0];
+ script.src = pathtoroot + path;
+ scriptElement.parentNode.insertBefore(script, scriptElement);
+}
+
+function show(tableId, selected, columns) {
+ if (tableId !== selected) {
+ document.querySelectorAll('div.' + tableId + ':not(.' + selected + ')')
+ .forEach(function(elem) {
+ elem.style.display = 'none';
+ });
+ }
+ document.querySelectorAll('div.' + selected)
+ .forEach(function(elem, index) {
+ elem.style.display = '';
+ var isEvenRow = index % (columns * 2) < columns;
+ elem.classList.remove(isEvenRow ? oddRowColor : evenRowColor);
+ elem.classList.add(isEvenRow ? evenRowColor : oddRowColor);
+ });
+ updateTabs(tableId, selected);
+}
+
+function updateTabs(tableId, selected) {
+ document.querySelector('div#' + tableId +' .summary-table')
+ .setAttribute('aria-labelledby', selected);
+ document.querySelectorAll('button[id^="' + tableId + '"]')
+ .forEach(function(tab, index) {
+ if (selected === tab.id || (tableId === selected && index === 0)) {
+ tab.className = activeTableTab;
+ tab.setAttribute('aria-selected', true);
+ tab.setAttribute('tabindex',0);
+ } else {
+ tab.className = tableTab;
+ tab.setAttribute('aria-selected', false);
+ tab.setAttribute('tabindex',-1);
+ }
+ });
+}
+
+function switchTab(e) {
+ var selected = document.querySelector('[aria-selected=true]');
+ if (selected) {
+ if ((e.keyCode === 37 || e.keyCode === 38) && selected.previousSibling) {
+ // left or up arrow key pressed: move focus to previous tab
+ selected.previousSibling.click();
+ selected.previousSibling.focus();
+ e.preventDefault();
+ } else if ((e.keyCode === 39 || e.keyCode === 40) && selected.nextSibling) {
+ // right or down arrow key pressed: move focus to next tab
+ selected.nextSibling.click();
+ selected.nextSibling.focus();
+ e.preventDefault();
+ }
+ }
+}
+
+var updateSearchResults = function() {};
+
+function indexFilesLoaded() {
+ return moduleSearchIndex
+ && packageSearchIndex
+ && typeSearchIndex
+ && memberSearchIndex
+ && tagSearchIndex;
+}
+
+// Workaround for scroll position not being included in browser history (8249133)
+document.addEventListener("DOMContentLoaded", function(e) {
+ var contentDiv = document.querySelector("div.flex-content");
+ window.addEventListener("popstate", function(e) {
+ if (e.state !== null) {
+ contentDiv.scrollTop = e.state;
+ }
+ });
+ window.addEventListener("hashchange", function(e) {
+ history.replaceState(contentDiv.scrollTop, document.title);
+ });
+ contentDiv.addEventListener("scroll", function(e) {
+ var timeoutID;
+ if (!timeoutID) {
+ timeoutID = setTimeout(function() {
+ history.replaceState(contentDiv.scrollTop, document.title);
+ timeoutID = null;
+ }, 100);
+ }
+ });
+ if (!location.hash) {
+ history.replaceState(contentDiv.scrollTop, document.title);
+ }
+});
diff --git a/utilities/ondc-crypto-utility-master/docs/search.js b/utilities/ondc-crypto-utility-master/docs/search.js
new file mode 100644
index 0000000..2246cdd
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/docs/search.js
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+var noResult = {l: "No results found"};
+var loading = {l: "Loading search index..."};
+var catModules = "Modules";
+var catPackages = "Packages";
+var catTypes = "Types";
+var catMembers = "Members";
+var catSearchTags = "Search Tags";
+var highlight = "
$& ";
+var searchPattern = "";
+var fallbackPattern = "";
+var RANKING_THRESHOLD = 2;
+var NO_MATCH = 0xffff;
+var MIN_RESULTS = 3;
+var MAX_RESULTS = 500;
+var UNNAMED = "
";
+function escapeHtml(str) {
+ return str.replace(//g, ">");
+}
+function getHighlightedText(item, matcher, fallbackMatcher) {
+ var escapedItem = escapeHtml(item);
+ var highlighted = escapedItem.replace(matcher, highlight);
+ if (highlighted === escapedItem) {
+ highlighted = escapedItem.replace(fallbackMatcher, highlight)
+ }
+ return highlighted;
+}
+function getURLPrefix(ui) {
+ var urlPrefix="";
+ var slash = "/";
+ if (ui.item.category === catModules) {
+ return ui.item.l + slash;
+ } else if (ui.item.category === catPackages && ui.item.m) {
+ return ui.item.m + slash;
+ } else if (ui.item.category === catTypes || ui.item.category === catMembers) {
+ if (ui.item.m) {
+ urlPrefix = ui.item.m + slash;
+ } else {
+ $.each(packageSearchIndex, function(index, item) {
+ if (item.m && ui.item.p === item.l) {
+ urlPrefix = item.m + slash;
+ }
+ });
+ }
+ }
+ return urlPrefix;
+}
+function createSearchPattern(term) {
+ var pattern = "";
+ var isWordToken = false;
+ term.replace(/,\s*/g, ", ").trim().split(/\s+/).forEach(function(w, index) {
+ if (index > 0) {
+ // whitespace between identifiers is significant
+ pattern += (isWordToken && /^\w/.test(w)) ? "\\s+" : "\\s*";
+ }
+ var tokens = w.split(/(?=[A-Z,.()<>[\/])/);
+ for (var i = 0; i < tokens.length; i++) {
+ var s = tokens[i];
+ if (s === "") {
+ continue;
+ }
+ pattern += $.ui.autocomplete.escapeRegex(s);
+ isWordToken = /\w$/.test(s);
+ if (isWordToken) {
+ pattern += "([a-z0-9_$<>\\[\\]]*?)";
+ }
+ }
+ });
+ return pattern;
+}
+function createMatcher(pattern, flags) {
+ var isCamelCase = /[A-Z]/.test(pattern);
+ return new RegExp(pattern, flags + (isCamelCase ? "" : "i"));
+}
+var watermark = 'Search';
+$(function() {
+ var search = $("#search-input");
+ var reset = $("#reset-button");
+ search.val('');
+ search.prop("disabled", false);
+ reset.prop("disabled", false);
+ search.val(watermark).addClass('watermark');
+ search.blur(function() {
+ if ($(this).val().length === 0) {
+ $(this).val(watermark).addClass('watermark');
+ }
+ });
+ search.on('click keydown paste', function() {
+ if ($(this).val() === watermark) {
+ $(this).val('').removeClass('watermark');
+ }
+ });
+ reset.click(function() {
+ search.val('').focus();
+ });
+ search.focus()[0].setSelectionRange(0, 0);
+});
+$.widget("custom.catcomplete", $.ui.autocomplete, {
+ _create: function() {
+ this._super();
+ this.widget().menu("option", "items", "> :not(.ui-autocomplete-category)");
+ },
+ _renderMenu: function(ul, items) {
+ var rMenu = this;
+ var currentCategory = "";
+ rMenu.menu.bindings = $();
+ $.each(items, function(index, item) {
+ var li;
+ if (item.category && item.category !== currentCategory) {
+ ul.append("" + item.category + " ");
+ currentCategory = item.category;
+ }
+ li = rMenu._renderItemData(ul, item);
+ if (item.category) {
+ li.attr("aria-label", item.category + " : " + item.l);
+ li.attr("class", "result-item");
+ } else {
+ li.attr("aria-label", item.l);
+ li.attr("class", "result-item");
+ }
+ });
+ },
+ _renderItem: function(ul, item) {
+ var label = "";
+ var matcher = createMatcher(escapeHtml(searchPattern), "g");
+ var fallbackMatcher = new RegExp(fallbackPattern, "gi")
+ if (item.category === catModules) {
+ label = getHighlightedText(item.l, matcher, fallbackMatcher);
+ } else if (item.category === catPackages) {
+ label = getHighlightedText(item.l, matcher, fallbackMatcher);
+ } else if (item.category === catTypes) {
+ label = (item.p && item.p !== UNNAMED)
+ ? getHighlightedText(item.p + "." + item.l, matcher, fallbackMatcher)
+ : getHighlightedText(item.l, matcher, fallbackMatcher);
+ } else if (item.category === catMembers) {
+ label = (item.p && item.p !== UNNAMED)
+ ? getHighlightedText(item.p + "." + item.c + "." + item.l, matcher, fallbackMatcher)
+ : getHighlightedText(item.c + "." + item.l, matcher, fallbackMatcher);
+ } else if (item.category === catSearchTags) {
+ label = getHighlightedText(item.l, matcher, fallbackMatcher);
+ } else {
+ label = item.l;
+ }
+ var li = $(" ").appendTo(ul);
+ var div = $("
").appendTo(li);
+ if (item.category === catSearchTags && item.h) {
+ if (item.d) {
+ div.html(label + " (" + item.h + ") "
+ + item.d + " ");
+ } else {
+ div.html(label + " (" + item.h + ") ");
+ }
+ } else {
+ if (item.m) {
+ div.html(item.m + "/" + label);
+ } else {
+ div.html(label);
+ }
+ }
+ return li;
+ }
+});
+function rankMatch(match, category) {
+ if (!match) {
+ return NO_MATCH;
+ }
+ var index = match.index;
+ var input = match.input;
+ var leftBoundaryMatch = 2;
+ var periferalMatch = 0;
+ // make sure match is anchored on a left word boundary
+ if (index === 0 || /\W/.test(input[index - 1]) || "_" === input[index]) {
+ leftBoundaryMatch = 0;
+ } else if ("_" === input[index - 1] || (input[index] === input[index].toUpperCase() && !/^[A-Z0-9_$]+$/.test(input))) {
+ leftBoundaryMatch = 1;
+ }
+ var matchEnd = index + match[0].length;
+ var leftParen = input.indexOf("(");
+ var endOfName = leftParen > -1 ? leftParen : input.length;
+ // exclude peripheral matches
+ if (category !== catModules && category !== catSearchTags) {
+ var delim = category === catPackages ? "/" : ".";
+ if (leftParen > -1 && leftParen < index) {
+ periferalMatch += 2;
+ } else if (input.lastIndexOf(delim, endOfName) >= matchEnd) {
+ periferalMatch += 2;
+ }
+ }
+ var delta = match[0].length === endOfName ? 0 : 1; // rank full match higher than partial match
+ for (var i = 1; i < match.length; i++) {
+ // lower ranking if parts of the name are missing
+ if (match[i])
+ delta += match[i].length;
+ }
+ if (category === catTypes) {
+ // lower ranking if a type name contains unmatched camel-case parts
+ if (/[A-Z]/.test(input.substring(matchEnd)))
+ delta += 5;
+ if (/[A-Z]/.test(input.substring(0, index)))
+ delta += 5;
+ }
+ return leftBoundaryMatch + periferalMatch + (delta / 200);
+
+}
+function doSearch(request, response) {
+ var result = [];
+ searchPattern = createSearchPattern(request.term);
+ fallbackPattern = createSearchPattern(request.term.toLowerCase());
+ if (searchPattern === "") {
+ return this.close();
+ }
+ var camelCaseMatcher = createMatcher(searchPattern, "");
+ var fallbackMatcher = new RegExp(fallbackPattern, "i");
+
+ function searchIndexWithMatcher(indexArray, matcher, category, nameFunc) {
+ if (indexArray) {
+ var newResults = [];
+ $.each(indexArray, function (i, item) {
+ item.category = category;
+ var ranking = rankMatch(matcher.exec(nameFunc(item)), category);
+ if (ranking < RANKING_THRESHOLD) {
+ newResults.push({ranking: ranking, item: item});
+ }
+ return newResults.length <= MAX_RESULTS;
+ });
+ return newResults.sort(function(e1, e2) {
+ return e1.ranking - e2.ranking;
+ }).map(function(e) {
+ return e.item;
+ });
+ }
+ return [];
+ }
+ function searchIndex(indexArray, category, nameFunc) {
+ var primaryResults = searchIndexWithMatcher(indexArray, camelCaseMatcher, category, nameFunc);
+ result = result.concat(primaryResults);
+ if (primaryResults.length <= MIN_RESULTS && !camelCaseMatcher.ignoreCase) {
+ var secondaryResults = searchIndexWithMatcher(indexArray, fallbackMatcher, category, nameFunc);
+ result = result.concat(secondaryResults.filter(function (item) {
+ return primaryResults.indexOf(item) === -1;
+ }));
+ }
+ }
+
+ searchIndex(moduleSearchIndex, catModules, function(item) { return item.l; });
+ searchIndex(packageSearchIndex, catPackages, function(item) {
+ return (item.m && request.term.indexOf("/") > -1)
+ ? (item.m + "/" + item.l) : item.l;
+ });
+ searchIndex(typeSearchIndex, catTypes, function(item) {
+ return request.term.indexOf(".") > -1 ? item.p + "." + item.l : item.l;
+ });
+ searchIndex(memberSearchIndex, catMembers, function(item) {
+ return request.term.indexOf(".") > -1
+ ? item.p + "." + item.c + "." + item.l : item.l;
+ });
+ searchIndex(tagSearchIndex, catSearchTags, function(item) { return item.l; });
+
+ if (!indexFilesLoaded()) {
+ updateSearchResults = function() {
+ doSearch(request, response);
+ }
+ result.unshift(loading);
+ } else {
+ updateSearchResults = function() {};
+ }
+ response(result);
+}
+$(function() {
+ $("#search-input").catcomplete({
+ minLength: 1,
+ delay: 300,
+ source: doSearch,
+ response: function(event, ui) {
+ if (!ui.content.length) {
+ ui.content.push(noResult);
+ } else {
+ $("#search-input").empty();
+ }
+ },
+ autoFocus: true,
+ focus: function(event, ui) {
+ return false;
+ },
+ position: {
+ collision: "flip"
+ },
+ select: function(event, ui) {
+ if (ui.item.category) {
+ var url = getURLPrefix(ui);
+ if (ui.item.category === catModules) {
+ url += "module-summary.html";
+ } else if (ui.item.category === catPackages) {
+ if (ui.item.u) {
+ url = ui.item.u;
+ } else {
+ url += ui.item.l.replace(/\./g, '/') + "/package-summary.html";
+ }
+ } else if (ui.item.category === catTypes) {
+ if (ui.item.u) {
+ url = ui.item.u;
+ } else if (ui.item.p === UNNAMED) {
+ url += ui.item.l + ".html";
+ } else {
+ url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.l + ".html";
+ }
+ } else if (ui.item.category === catMembers) {
+ if (ui.item.p === UNNAMED) {
+ url += ui.item.c + ".html" + "#";
+ } else {
+ url += ui.item.p.replace(/\./g, '/') + "/" + ui.item.c + ".html" + "#";
+ }
+ if (ui.item.u) {
+ url += ui.item.u;
+ } else {
+ url += ui.item.l;
+ }
+ } else if (ui.item.category === catSearchTags) {
+ url += ui.item.u;
+ }
+ if (top !== window) {
+ parent.classFrame.location = pathtoroot + url;
+ } else {
+ window.location.href = pathtoroot + url;
+ }
+ $("#search-input").focus();
+ }
+ }
+ });
+});
diff --git a/utilities/ondc-crypto-utility-master/docs/stylesheet.css b/utilities/ondc-crypto-utility-master/docs/stylesheet.css
new file mode 100644
index 0000000..836c62d
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/docs/stylesheet.css
@@ -0,0 +1,865 @@
+/*
+ * Javadoc style sheet
+ */
+
+@import url('resources/fonts/dejavu.css');
+
+/*
+ * Styles for individual HTML elements.
+ *
+ * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular
+ * HTML element throughout the page.
+ */
+
+body {
+ background-color:#ffffff;
+ color:#353833;
+ font-family:'DejaVu Sans', Arial, Helvetica, sans-serif;
+ font-size:14px;
+ margin:0;
+ padding:0;
+ height:100%;
+ width:100%;
+}
+iframe {
+ margin:0;
+ padding:0;
+ height:100%;
+ width:100%;
+ overflow-y:scroll;
+ border:none;
+}
+a:link, a:visited {
+ text-decoration:none;
+ color:#4A6782;
+}
+a[href]:hover, a[href]:focus {
+ text-decoration:none;
+ color:#bb7a2a;
+}
+a[name] {
+ color:#353833;
+}
+pre {
+ font-family:'DejaVu Sans Mono', monospace;
+ font-size:14px;
+}
+h1 {
+ font-size:20px;
+}
+h2 {
+ font-size:18px;
+}
+h3 {
+ font-size:16px;
+}
+h4 {
+ font-size:15px;
+}
+h5 {
+ font-size:14px;
+}
+h6 {
+ font-size:13px;
+}
+ul {
+ list-style-type:disc;
+}
+code, tt {
+ font-family:'DejaVu Sans Mono', monospace;
+}
+:not(h1, h2, h3, h4, h5, h6) > code,
+:not(h1, h2, h3, h4, h5, h6) > tt {
+ font-size:14px;
+ padding-top:4px;
+ margin-top:8px;
+ line-height:1.4em;
+}
+dt code {
+ font-family:'DejaVu Sans Mono', monospace;
+ font-size:14px;
+ padding-top:4px;
+}
+.summary-table dt code {
+ font-family:'DejaVu Sans Mono', monospace;
+ font-size:14px;
+ vertical-align:top;
+ padding-top:4px;
+}
+sup {
+ font-size:8px;
+}
+button {
+ font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;
+ font-size: 14px;
+}
+/*
+ * Styles for HTML generated by javadoc.
+ *
+ * These are style classes that are used by the standard doclet to generate HTML documentation.
+ */
+
+/*
+ * Styles for document title and copyright.
+ */
+.clear {
+ clear:both;
+ height:0;
+ overflow:hidden;
+}
+.about-language {
+ float:right;
+ padding:0 21px 8px 8px;
+ font-size:11px;
+ margin-top:-9px;
+ height:2.9em;
+}
+.legal-copy {
+ margin-left:.5em;
+}
+.tab {
+ background-color:#0066FF;
+ color:#ffffff;
+ padding:8px;
+ width:5em;
+ font-weight:bold;
+}
+/*
+ * Styles for navigation bar.
+ */
+@media screen {
+ .flex-box {
+ position:fixed;
+ display:flex;
+ flex-direction:column;
+ height: 100%;
+ width: 100%;
+ }
+ .flex-header {
+ flex: 0 0 auto;
+ }
+ .flex-content {
+ flex: 1 1 auto;
+ overflow-y: auto;
+ }
+}
+.top-nav {
+ background-color:#4D7A97;
+ color:#FFFFFF;
+ float:left;
+ padding:0;
+ width:100%;
+ clear:right;
+ min-height:2.8em;
+ padding-top:10px;
+ overflow:hidden;
+ font-size:12px;
+}
+.sub-nav {
+ background-color:#dee3e9;
+ float:left;
+ width:100%;
+ overflow:hidden;
+ font-size:12px;
+}
+.sub-nav div {
+ clear:left;
+ float:left;
+ padding:0 0 5px 6px;
+ text-transform:uppercase;
+}
+.sub-nav .nav-list {
+ padding-top:5px;
+}
+ul.nav-list {
+ display:block;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.sub-nav-list {
+ float:left;
+ margin:0 25px 0 0;
+ padding:0;
+}
+ul.nav-list li {
+ list-style:none;
+ float:left;
+ padding: 5px 6px;
+ text-transform:uppercase;
+}
+.sub-nav .nav-list-search {
+ float:right;
+ margin:0 0 0 0;
+ padding:5px 6px;
+ clear:none;
+}
+.nav-list-search label {
+ position:relative;
+ right:-16px;
+}
+ul.sub-nav-list li {
+ list-style:none;
+ float:left;
+ padding-top:10px;
+}
+.top-nav a:link, .top-nav a:active, .top-nav a:visited {
+ color:#FFFFFF;
+ text-decoration:none;
+ text-transform:uppercase;
+}
+.top-nav a:hover {
+ text-decoration:none;
+ color:#bb7a2a;
+ text-transform:uppercase;
+}
+.nav-bar-cell1-rev {
+ background-color:#F8981D;
+ color:#253441;
+ margin: auto 5px;
+}
+.skip-nav {
+ position:absolute;
+ top:auto;
+ left:-9999px;
+ overflow:hidden;
+}
+/*
+ * Hide navigation links and search box in print layout
+ */
+@media print {
+ ul.nav-list, div.sub-nav {
+ display:none;
+ }
+}
+/*
+ * Styles for page header and footer.
+ */
+.title {
+ color:#2c4557;
+ margin:10px 0;
+}
+.sub-title {
+ margin:5px 0 0 0;
+}
+.header ul {
+ margin:0 0 15px 0;
+ padding:0;
+}
+.header ul li, .footer ul li {
+ list-style:none;
+ font-size:13px;
+}
+/*
+ * Styles for headings.
+ */
+body.class-declaration-page .summary h2,
+body.class-declaration-page .details h2,
+body.class-use-page h2,
+body.module-declaration-page .block-list h2 {
+ font-style: italic;
+ padding:0;
+ margin:15px 0;
+}
+body.class-declaration-page .summary h3,
+body.class-declaration-page .details h3,
+body.class-declaration-page .summary .inherited-list h2 {
+ background-color:#dee3e9;
+ border:1px solid #d0d9e0;
+ margin:0 0 6px -8px;
+ padding:7px 5px;
+}
+/*
+ * Styles for page layout containers.
+ */
+main {
+ clear:both;
+ padding:10px 20px;
+ position:relative;
+}
+dl.notes > dt {
+ font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif;
+ font-size:12px;
+ font-weight:bold;
+ margin:10px 0 0 0;
+ color:#4E4E4E;
+}
+dl.notes > dd {
+ margin:5px 10px 10px 0;
+ font-size:14px;
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+}
+dl.name-value > dt {
+ margin-left:1px;
+ font-size:1.1em;
+ display:inline;
+ font-weight:bold;
+}
+dl.name-value > dd {
+ margin:0 0 0 1px;
+ font-size:1.1em;
+ display:inline;
+}
+/*
+ * Styles for lists.
+ */
+li.circle {
+ list-style:circle;
+}
+ul.horizontal li {
+ display:inline;
+ font-size:0.9em;
+}
+div.inheritance {
+ margin:0;
+ padding:0;
+}
+div.inheritance div.inheritance {
+ margin-left:2em;
+}
+ul.block-list,
+ul.details-list,
+ul.member-list,
+ul.summary-list {
+ margin:10px 0 10px 0;
+ padding:0;
+}
+ul.block-list > li,
+ul.details-list > li,
+ul.member-list > li,
+ul.summary-list > li {
+ list-style:none;
+ margin-bottom:15px;
+ line-height:1.4;
+}
+.summary-table dl, .summary-table dl dt, .summary-table dl dd {
+ margin-top:0;
+ margin-bottom:1px;
+}
+ul.see-list, ul.see-list-long {
+ padding-left: 0;
+ list-style: none;
+}
+ul.see-list li {
+ display: inline;
+}
+ul.see-list li:not(:last-child):after,
+ul.see-list-long li:not(:last-child):after {
+ content: ", ";
+ white-space: pre-wrap;
+}
+/*
+ * Styles for tables.
+ */
+.summary-table, .details-table {
+ width:100%;
+ border-spacing:0;
+ border-left:1px solid #EEE;
+ border-right:1px solid #EEE;
+ border-bottom:1px solid #EEE;
+ padding:0;
+}
+.caption {
+ position:relative;
+ text-align:left;
+ background-repeat:no-repeat;
+ color:#253441;
+ font-weight:bold;
+ clear:none;
+ overflow:hidden;
+ padding:0;
+ padding-top:10px;
+ padding-left:1px;
+ margin:0;
+ white-space:pre;
+}
+.caption a:link, .caption a:visited {
+ color:#1f389c;
+}
+.caption a:hover,
+.caption a:active {
+ color:#FFFFFF;
+}
+.caption span {
+ white-space:nowrap;
+ padding-top:5px;
+ padding-left:12px;
+ padding-right:12px;
+ padding-bottom:7px;
+ display:inline-block;
+ float:left;
+ background-color:#F8981D;
+ border: none;
+ height:16px;
+}
+div.table-tabs {
+ padding:10px 0 0 1px;
+ margin:0;
+}
+div.table-tabs > button {
+ border: none;
+ cursor: pointer;
+ padding: 5px 12px 7px 12px;
+ font-weight: bold;
+ margin-right: 3px;
+}
+div.table-tabs > button.active-table-tab {
+ background: #F8981D;
+ color: #253441;
+}
+div.table-tabs > button.table-tab {
+ background: #4D7A97;
+ color: #FFFFFF;
+}
+.two-column-summary {
+ display: grid;
+ grid-template-columns: minmax(15%, max-content) minmax(15%, auto);
+}
+.three-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(15%, max-content) minmax(15%, auto);
+}
+.four-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(10%, max-content) minmax(10%, max-content) minmax(10%, auto);
+}
+@media screen and (max-width: 600px) {
+ .two-column-summary {
+ display: grid;
+ grid-template-columns: 1fr;
+ }
+}
+@media screen and (max-width: 800px) {
+ .three-column-summary {
+ display: grid;
+ grid-template-columns: minmax(10%, max-content) minmax(25%, auto);
+ }
+ .three-column-summary .col-last {
+ grid-column-end: span 2;
+ }
+}
+@media screen and (max-width: 1000px) {
+ .four-column-summary {
+ display: grid;
+ grid-template-columns: minmax(15%, max-content) minmax(15%, auto);
+ }
+}
+.summary-table > div, .details-table > div {
+ text-align:left;
+ padding: 8px 3px 3px 7px;
+}
+.col-first, .col-second, .col-last, .col-constructor-name, .col-summary-item-name {
+ vertical-align:top;
+ padding-right:0;
+ padding-top:8px;
+ padding-bottom:3px;
+}
+.table-header {
+ background:#dee3e9;
+ font-weight: bold;
+}
+.col-first, .col-first {
+ font-size:13px;
+}
+.col-second, .col-second, .col-last, .col-constructor-name, .col-summary-item-name, .col-last {
+ font-size:13px;
+}
+.col-first, .col-second, .col-constructor-name {
+ vertical-align:top;
+ overflow: auto;
+}
+.col-last {
+ white-space:normal;
+}
+.col-first a:link, .col-first a:visited,
+.col-second a:link, .col-second a:visited,
+.col-first a:link, .col-first a:visited,
+.col-second a:link, .col-second a:visited,
+.col-constructor-name a:link, .col-constructor-name a:visited,
+.col-summary-item-name a:link, .col-summary-item-name a:visited,
+.constant-values-container a:link, .constant-values-container a:visited,
+.all-classes-container a:link, .all-classes-container a:visited,
+.all-packages-container a:link, .all-packages-container a:visited {
+ font-weight:bold;
+}
+.table-sub-heading-color {
+ background-color:#EEEEFF;
+}
+.even-row-color, .even-row-color .table-header {
+ background-color:#FFFFFF;
+}
+.odd-row-color, .odd-row-color .table-header {
+ background-color:#EEEEEF;
+}
+/*
+ * Styles for contents.
+ */
+.deprecated-content {
+ margin:0;
+ padding:10px 0;
+}
+div.block {
+ font-size:14px;
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+}
+.col-last div {
+ padding-top:0;
+}
+.col-last a {
+ padding-bottom:3px;
+}
+.module-signature,
+.package-signature,
+.type-signature,
+.member-signature {
+ font-family:'DejaVu Sans Mono', monospace;
+ font-size:14px;
+ margin:14px 0;
+ white-space: pre-wrap;
+}
+.module-signature,
+.package-signature,
+.type-signature {
+ margin-top: 0;
+}
+.member-signature .type-parameters-long,
+.member-signature .parameters,
+.member-signature .exceptions {
+ display: inline-block;
+ vertical-align: top;
+ white-space: pre;
+}
+.member-signature .type-parameters {
+ white-space: normal;
+}
+/*
+ * Styles for formatting effect.
+ */
+.source-line-no {
+ color:green;
+ padding:0 30px 0 0;
+}
+h1.hidden {
+ visibility:hidden;
+ overflow:hidden;
+ font-size:10px;
+}
+.block {
+ display:block;
+ margin:0 10px 5px 0;
+ color:#474747;
+}
+.deprecated-label, .descfrm-type-label, .implementation-label, .member-name-label, .member-name-link,
+.module-label-in-package, .module-label-in-type, .override-specify-label, .package-label-in-type,
+.package-hierarchy-label, .type-name-label, .type-name-link, .search-tag-link, .preview-label {
+ font-weight:bold;
+}
+.deprecation-comment, .help-footnote, .preview-comment {
+ font-style:italic;
+}
+.deprecation-block {
+ font-size:14px;
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+ border-style:solid;
+ border-width:thin;
+ border-radius:10px;
+ padding:10px;
+ margin-bottom:10px;
+ margin-right:10px;
+ display:inline-block;
+}
+.preview-block {
+ font-size:14px;
+ font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif;
+ border-style:solid;
+ border-width:thin;
+ border-radius:10px;
+ padding:10px;
+ margin-bottom:10px;
+ margin-right:10px;
+ display:inline-block;
+}
+div.block div.deprecation-comment {
+ font-style:normal;
+}
+/*
+ * Styles specific to HTML5 elements.
+ */
+main, nav, header, footer, section {
+ display:block;
+}
+/*
+ * Styles for javadoc search.
+ */
+.ui-autocomplete-category {
+ font-weight:bold;
+ font-size:15px;
+ padding:7px 0 7px 3px;
+ background-color:#4D7A97;
+ color:#FFFFFF;
+}
+.result-item {
+ font-size:13px;
+}
+.ui-autocomplete {
+ max-height:85%;
+ max-width:65%;
+ overflow-y:scroll;
+ overflow-x:scroll;
+ white-space:nowrap;
+ box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
+}
+ul.ui-autocomplete {
+ position:fixed;
+ z-index:999999;
+}
+ul.ui-autocomplete li {
+ float:left;
+ clear:both;
+ width:100%;
+}
+.result-highlight {
+ font-weight:bold;
+}
+#search-input {
+ background-image:url('resources/glass.png');
+ background-size:13px;
+ background-repeat:no-repeat;
+ background-position:2px 3px;
+ padding-left:20px;
+ position:relative;
+ right:-18px;
+ width:400px;
+}
+#reset-button {
+ background-color: rgb(255,255,255);
+ background-image:url('resources/x.png');
+ background-position:center;
+ background-repeat:no-repeat;
+ background-size:12px;
+ border:0 none;
+ width:16px;
+ height:16px;
+ position:relative;
+ left:-4px;
+ top:-4px;
+ font-size:0px;
+}
+.watermark {
+ color:#545454;
+}
+.search-tag-desc-result {
+ font-style:italic;
+ font-size:11px;
+}
+.search-tag-holder-result {
+ font-style:italic;
+ font-size:12px;
+}
+.search-tag-result:target {
+ background-color:yellow;
+}
+.module-graph span {
+ display:none;
+ position:absolute;
+}
+.module-graph:hover span {
+ display:block;
+ margin: -100px 0 0 100px;
+ z-index: 1;
+}
+.inherited-list {
+ margin: 10px 0 10px 0;
+}
+section.class-description {
+ line-height: 1.4;
+}
+.summary section[class$="-summary"], .details section[class$="-details"],
+.class-uses .detail, .serialized-class-details {
+ padding: 0px 20px 5px 10px;
+ border: 1px solid #ededed;
+ background-color: #f8f8f8;
+}
+.inherited-list, section[class$="-details"] .detail {
+ padding:0 0 5px 8px;
+ background-color:#ffffff;
+ border:none;
+}
+.vertical-separator {
+ padding: 0 5px;
+}
+ul.help-section-list {
+ margin: 0;
+}
+ul.help-subtoc > li {
+ display: inline-block;
+ padding-right: 5px;
+ font-size: smaller;
+}
+ul.help-subtoc > li::before {
+ content: "\2022" ;
+ padding-right:2px;
+}
+span.help-note {
+ font-style: italic;
+}
+/*
+ * Indicator icon for external links.
+ */
+main a[href*="://"]::after {
+ content:"";
+ display:inline-block;
+ background-image:url('data:image/svg+xml; utf8, \
+ \
+ \
+ ');
+ background-size:100% 100%;
+ width:7px;
+ height:7px;
+ margin-left:2px;
+ margin-bottom:4px;
+}
+main a[href*="://"]:hover::after,
+main a[href*="://"]:focus::after {
+ background-image:url('data:image/svg+xml; utf8, \
+ \
+ \
+ ');
+}
+
+/*
+ * Styles for user-provided tables.
+ *
+ * borderless:
+ * No borders, vertical margins, styled caption.
+ * This style is provided for use with existing doc comments.
+ * In general, borderless tables should not be used for layout purposes.
+ *
+ * plain:
+ * Plain borders around table and cells, vertical margins, styled caption.
+ * Best for small tables or for complex tables for tables with cells that span
+ * rows and columns, when the "striped" style does not work well.
+ *
+ * striped:
+ * Borders around the table and vertical borders between cells, striped rows,
+ * vertical margins, styled caption.
+ * Best for tables that have a header row, and a body containing a series of simple rows.
+ */
+
+table.borderless,
+table.plain,
+table.striped {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+table.borderless > caption,
+table.plain > caption,
+table.striped > caption {
+ font-weight: bold;
+ font-size: smaller;
+}
+table.borderless th, table.borderless td,
+table.plain th, table.plain td,
+table.striped th, table.striped td {
+ padding: 2px 5px;
+}
+table.borderless,
+table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th,
+table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td {
+ border: none;
+}
+table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr {
+ background-color: transparent;
+}
+table.plain {
+ border-collapse: collapse;
+ border: 1px solid black;
+}
+table.plain > thead > tr, table.plain > tbody tr, table.plain > tr {
+ background-color: transparent;
+}
+table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th,
+table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td {
+ border: 1px solid black;
+}
+table.striped {
+ border-collapse: collapse;
+ border: 1px solid black;
+}
+table.striped > thead {
+ background-color: #E3E3E3;
+}
+table.striped > thead > tr > th, table.striped > thead > tr > td {
+ border: 1px solid black;
+}
+table.striped > tbody > tr:nth-child(even) {
+ background-color: #EEE
+}
+table.striped > tbody > tr:nth-child(odd) {
+ background-color: #FFF
+}
+table.striped > tbody > tr > th, table.striped > tbody > tr > td {
+ border-left: 1px solid black;
+ border-right: 1px solid black;
+}
+table.striped > tbody > tr > th {
+ font-weight: normal;
+}
+/**
+ * Tweak font sizes and paddings for small screens.
+ */
+@media screen and (max-width: 1050px) {
+ #search-input {
+ width: 300px;
+ }
+}
+@media screen and (max-width: 800px) {
+ #search-input {
+ width: 200px;
+ }
+ .top-nav,
+ .bottom-nav {
+ font-size: 11px;
+ padding-top: 6px;
+ }
+ .sub-nav {
+ font-size: 11px;
+ }
+ .about-language {
+ padding-right: 16px;
+ }
+ ul.nav-list li,
+ .sub-nav .nav-list-search {
+ padding: 6px;
+ }
+ ul.sub-nav-list li {
+ padding-top: 5px;
+ }
+ main {
+ padding: 10px;
+ }
+ .summary section[class$="-summary"], .details section[class$="-details"],
+ .class-uses .detail, .serialized-class-details {
+ padding: 0 8px 5px 8px;
+ }
+ body {
+ -webkit-text-size-adjust: none;
+ }
+}
+@media screen and (max-width: 500px) {
+ #search-input {
+ width: 150px;
+ }
+ .top-nav,
+ .bottom-nav {
+ font-size: 10px;
+ }
+ .sub-nav {
+ font-size: 10px;
+ }
+ .about-language {
+ font-size: 10px;
+ padding-right: 12px;
+ }
+}
diff --git a/utilities/ondc-crypto-utility-master/docs/tag-search-index.js b/utilities/ondc-crypto-utility-master/docs/tag-search-index.js
new file mode 100644
index 0000000..0367dae
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/docs/tag-search-index.js
@@ -0,0 +1 @@
+tagSearchIndex = [];updateSearchResults();
\ No newline at end of file
diff --git a/utilities/ondc-crypto-utility-master/docs/type-search-index.js b/utilities/ondc-crypto-utility-master/docs/type-search-index.js
new file mode 100644
index 0000000..d90ed03
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/docs/type-search-index.js
@@ -0,0 +1 @@
+typeSearchIndex = [{"l":"All Classes and Interfaces","u":"allclasses-index.html"},{"p":"org.ondc.crypto.util","l":"CryptoFunctions"},{"p":"org.ondc.crypto.util","l":"CryptoKeyPair"},{"p":"org.ondc.crypto.util","l":"CryptoTest"}];updateSearchResults();
\ No newline at end of file
diff --git a/utilities/ondc-crypto-utility-master/pom.xml b/utilities/ondc-crypto-utility-master/pom.xml
new file mode 100644
index 0000000..4934eb0
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/pom.xml
@@ -0,0 +1,171 @@
+
+
+
+ 4.0.0
+
+ org.ondc
+ ondc-crypto-util
+ 0.1-GA
+
+
+
+ ${project.groupId}:${project.artifactId}
+ This project has been created with an intension to help and assist ONDC Network Participants to build their own crypto libraries that are required for interaction in ONDC Network It covers key generation, signing, verification, encryption and decryption.
+ https://github.com/protean-ondc/ondc-crypto-utility
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.9.0
+ test
+
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.69
+
+
+
+
+
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+
+
+ maven-clean-plugin
+ 3.1.0
+
+
+
+ maven-resources-plugin
+ 3.0.2
+
+
+ maven-compiler-plugin
+ 3.8.0
+
+
+ maven-surefire-plugin
+ 2.22.1
+
+
+ maven-jar-plugin
+ 3.0.2
+
+
+ maven-install-plugin
+ 2.5.2
+
+
+ maven-deploy-plugin
+ 2.8.2
+
+
+
+ maven-site-plugin
+ 3.7.1
+
+
+ maven-project-info-reports-plugin
+ 3.0.0
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.7
+ true
+
+ ossrh
+ https://s01.oss.sonatype.org/
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+
+
+
+ CC BY-ND 2.0
+ https://creativecommons.org/licenses/by-nd/2.0/
+
+
+
+
+ Sujeet Suryawanshi
+ sujeets@proteantech.in
+ ONDC
+ http://ondc.org
+
+
+
+ scm:git:git://github.com/protean-ondc/ondc-crypto-utility.git
+ scm:git:ssh://github.com/protean-ondc/ondc-crypto-utility.git
+ https://github.com/protean-ondc/ondc-crypto-utility.git
+
+
+
diff --git a/utilities/ondc-crypto-utility-master/src/main/java/org/ondc/crypto/util/CryptoFunctions.java b/utilities/ondc-crypto-utility-master/src/main/java/org/ondc/crypto/util/CryptoFunctions.java
new file mode 100644
index 0000000..3f30f82
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/src/main/java/org/ondc/crypto/util/CryptoFunctions.java
@@ -0,0 +1,283 @@
+/*
+ *
+ */
+ package org.ondc.crypto.util;
+
+ import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+ import java.security.KeyFactory;
+ import java.security.KeyPair;
+ import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+ import java.security.NoSuchProviderException;
+ import java.security.PrivateKey;
+ import java.security.PublicKey;
+ import java.security.SecureRandom;
+ import java.security.Security;
+ import java.security.spec.InvalidKeySpecException;
+ import java.security.spec.PKCS8EncodedKeySpec;
+ import java.security.spec.X509EncodedKeySpec;
+import java.util.Base64;
+
+import javax.crypto.BadPaddingException;
+ import javax.crypto.Cipher;
+ import javax.crypto.IllegalBlockSizeException;
+ import javax.crypto.KeyAgreement;
+ import javax.crypto.NoSuchPaddingException;
+ import javax.crypto.SecretKey;
+ import javax.crypto.spec.SecretKeySpec;
+ import org.bouncycastle.jce.provider.BouncyCastleProvider;
+ import org.bouncycastle.math.ec.rfc8032.Ed25519;
+
+
+
+// TODO: Auto-generated Javadoc
+/**
+ * The Class CryptoFunctions provides generation of key pairs for signing and encryption along with signing, verification, encryption and decryption.
+ */
+public class CryptoFunctions {
+
+
+ /** The Constant X25519. */
+ private static final String X25519="X25519";
+
+ /** The Constant AES. */
+ private static final String AES="AES";
+
+ /** The Constant BLAKE2B_512. */
+ private static final String BLAKE2B_512="BLAKE2B-512";
+
+ /**
+ * This method generates ED25519 32 byte/256 bits key pair (Private and Public) for Signing
+ *
.
+ *
+ *
+ * System.out.println("Testing whether Signing Keys are generated::");
+ * CryptoKeyPair signingKeyPair=CryptoFunctions.generateSigningKeyPair();
+ *
+ * String message="message to be signed";
+ *
+ * byte[] signature= CryptoFunctions.sign(signingKeyPair.getPrivateKey(), message.getBytes());
+ *
+ * boolean verificationResult=CryptoFunctions.verify(signature, message.getBytes(), signingKeyPair.getPublickKey());
+ *
+
+ * @author SujeetS
+ * @return the crypto key pair
+ * @see CryptoFunctions#sign(byte[], byte[])
+ * @see CryptoFunctions#verify(byte[], byte[], byte[])
+ * @since 0.1
+ */
+
+
+ public static CryptoKeyPair generateSigningKeyPair() {
+
+ // generate ed25519 keys
+ SecureRandom RANDOM = new SecureRandom();
+
+ byte[] privateKey = new byte[Ed25519.SECRET_KEY_SIZE]; //32 Byte or 256 bits
+ byte[] publicKey = new byte[Ed25519.PUBLIC_KEY_SIZE]; //32 Byte or 256 bits
+
+ // generate private key using secure random
+ RANDOM.nextBytes(privateKey);
+
+ // generate public key
+ Ed25519.generatePublicKey(privateKey, 0, publicKey, 0);
+
+ // store generated key pair and return the same
+ CryptoKeyPair signingKeyPair=new CryptoKeyPair(publicKey,privateKey) ;
+ return signingKeyPair;
+ }
+
+
+ /**
+ * This method generates signature using given ED25519 32 byte/ 256 bits Private key
+ *
.
+ *
+ *
+ * System.out.println("Testing whether Signing Keys are generated::");
+ * CryptoKeyPair signingKeyPair=CryptoFunctions.generateSigningKeyPair();
+ *
+ *
+ * String message="message to be signed";
+ * byte[] signature= CryptoFunctions.sign(signingKeyPair.getPrivateKey(), message.getBytes());
+ * boolean verificationResult=CryptoFunctions.verify(signature, message.getBytes(), signingKeyPair.getPublickKey());
+ *
+
+ * @author SujeetS
+ * @param privateKey the private key that should be used to sign the message
+ * @param message the message that should be signed using given private key
+ * @return the byte[] signature of given message generated using given private key
+ * @see CryptoFunctions#generateSigningKeyPair()
+ * @see CryptoFunctions#verify(byte[], byte[], byte[])
+ * @since 0.1
+ */
+ public static byte[] sign(byte[] privateKey,byte[] message) {
+ // initialise signature variable
+ byte[] signature = new byte[Ed25519.SIGNATURE_SIZE];
+
+ // sign the received message with given private key
+ Ed25519.sign(privateKey, 0, message, 0, message.length, signature, 0);
+ return signature;
+ }
+
+ /**
+ * Verify given signature using ED25519 Public Key
+ *
+ *
+ * System.out.println("Testing whether Signing Keys are generated::");
+ * CryptoKeyPair signingKeyPair=CryptoFunctions.generateSigningKeyPair();
+ *
+ *
+ * String message="message to be signed";
+ * byte[] signature= CryptoFunctions.sign(signingKeyPair.getPrivateKey(), message.getBytes());
+ * boolean verificationResult=CryptoFunctions.verify(signature, message.getBytes(), signingKeyPair.getPublickKey());
+ *
+ * @author SujeetS
+ * @param signature the signature that needs to be verified
+ * @param message the message that needs to verified along with signature
+ * @param publicKey the public key to be used for verifying the signature
+ * @return true, if successful
+ * @see CryptoFunctions#generateSigningKeyPair()
+ * @see CryptoFunctions#sign(byte[], byte[]) *
+ */
+ public static boolean verify(byte[] signature,byte[] message, byte[] publicKey) {
+ //verify the given signature with
+ return Ed25519.verify(signature, 0, publicKey, 0, message, 0, message.length);
+ }
+
+
+ /**
+ * Generate encryption decryption key pair using x25519 key exchange algorithm.
+ *
+ * @author SujeetS
+ * @return the crypto key pair
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchPaddingException the no such padding exception
+ * @throws IllegalBlockSizeException the illegal block size exception
+ * @throws BadPaddingException the bad padding exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws NoSuchProviderException the no such provider exception
+ * @see CryptoFunctions#encryptDecrypt(int, byte[], byte[], byte[])
+ */
+
+ public static CryptoKeyPair generateEncDecKey() throws InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ KeyPairGenerator kpg= KeyPairGenerator.getInstance(X25519, BouncyCastleProvider.PROVIDER_NAME);
+ kpg.initialize(256); // 32 Byte or 256 Bits
+ KeyPair kp = kpg.generateKeyPair();
+ CryptoKeyPair encryptionDecryptionKeyPair=new CryptoKeyPair(kp.getPublic().getEncoded(),kp.getPrivate().getEncoded());
+ return encryptionDecryptionKeyPair;
+ }
+
+
+
+ /**
+ * This method can be used to do AES encryption and decryption using X25519 Key exchange algorithm
+ *
+ *
+ *
+ * CryptoKeyPair senderEncDecKeyPair=null;
+ * CryptoKeyPair receiverEncDecKeyPair=null;
+ *
+ * try {
+ * senderEncDecKeyPair= CryptoFunctions.generateEncDecKey();
+ * receiverEncDecKeyPair= CryptoFunctions.generateEncDecKey();
+ * } catch (Exception e) {
+ * // TODO Auto-generated catch block
+ * e.printStackTrace();
+ * }
+ * String message="message to be encrypted";
+ *
+ * byte[] encrypted= CryptoFunctions.encryptDecrypt(Cipher.ENCRYPT_MODE,message.getBytes(),senderEncDecKeyPair.getPrivateKey(),receiverEncDecKeyPair.getPublickKey());
+ *
+ * System.out.println("\n\n/* Sender Side /");
+ * System.out.println("{");
+ * System.out.println("\t\"plainChallengeString \":\""+message +"\",");
+ * System.out.println("\t\"EncryptedChallengeString \":\""+Base64.getEncoder().encodeToString(encrypted)+"\",");
+ * System.out.println("\t\"senderPrivateKey \":\""+Base64.getEncoder().encodeToString(senderEncDecKeyPair.getPrivateKey()) +"\",");
+ * System.out.println("\t\"receiverPublicKey \":\""+Base64.getEncoder().encodeToString(receiverEncDecKeyPair.getPublickKey()) +"\"");
+ * System.out.println("}\n\n");
+ * byte[] decrypted= CryptoFunctions.encryptDecrypt(Cipher.DECRYPT_MODE,encrypted,receiverEncDecKeyPair.getPrivateKey(),senderEncDecKeyPair.getPublickKey());
+ * String decryptedMessage=new String(decrypted);
+ * System.out.println("\n\n/** Receiver Side ");
+ * System.out.println("{");
+ * System.out.println("\t\"DecryptedChallengeString \":\""+decryptedMessage+"\",");
+ * System.out.println("\t\"receiverPrivateKey \":\""+Base64.getEncoder().encodeToString(receiverEncDecKeyPair.getPrivateKey()) +"\",");
+ * System.out.println("\t\"senderPublicKey \":\""+Base64.getEncoder().encodeToString(senderEncDecKeyPair.getPublickKey()) +"\"");
+ * System.out.println("}");
+ *
+ * @author SujeetS
+ * @param mode the mode to set either encrypt (Cipher.ENCRYPT_MODE ) or decrypt (Cipher.DECRYPT_MODE )
+ * @param challenge_string the challenge string that needs to be encrypted or decrypted depending on mode that is set. In case if mode is set to Cipher.ENCRYPT_MODE then this text shall be encrypted; whereas if mode is set to Cipher.DECRYPT_MODE, this text shall be decrypted
+ * @param privateKey the private key.
+ * In case of mode=Cipher.ENCRYPT_MODE, it should be private key of the sender
+ * In case of mode=Cipher.DECRYPT_MODE, it should be private key of the receiver
+ * @param publicKey the public key
+ * In case of mode=Cipher.ENCRYPT_MODE, it should be public key of the receiver
+ * In case of mode=Cipher.DECRYPT_MODE, it should be public key of the sender
+ * @return the byte[]
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws NoSuchProviderException the no such provider exception
+ * @throws InvalidKeySpecException the invalid key spec exception
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchPaddingException the no such padding exception
+ * @throws IllegalBlockSizeException the illegal block size exception
+ * @throws BadPaddingException the bad padding exception
+ * @see Cipher#ENCRYPT_MODE
+ * @see Cipher#DECRYPT_MODE
+ * @see CryptoFunctions#generateEncDecKey()
+ *
+ */
+ public static byte[] encryptDecrypt(int mode, byte[] challenge_string,byte[] privateKey, byte[] publicKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ KeyAgreement keyAgreement=KeyAgreement.getInstance(X25519, BouncyCastleProvider.PROVIDER_NAME);
+ X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey);
+ PublicKey publickey = KeyFactory.getInstance(X25519, BouncyCastleProvider.PROVIDER_NAME)
+ .generatePublic(x509EncodedKeySpec);
+ PrivateKey privatekey = KeyFactory.getInstance(X25519, BouncyCastleProvider.PROVIDER_NAME)
+ .generatePrivate(new PKCS8EncodedKeySpec(privateKey));
+ keyAgreement.init(privatekey);
+ keyAgreement.doPhase(publickey, true);
+ byte[] secret = keyAgreement.generateSecret();
+ SecretKey originalKey = new SecretKeySpec(secret , 0, secret.length, AES);
+ Cipher cipher = Cipher.getInstance(AES, BouncyCastleProvider.PROVIDER_NAME);
+ cipher.init(mode, originalKey);
+ byte[] encryptedDecrypted = cipher.doFinal(challenge_string);
+ return encryptedDecrypted;
+
+ }
+
+
+
+ /**
+ * Generate blake hash.
+ *
+ *
+ * String message = "message to hash";
+ * byte[] hash_1=CryptoFunctions.generateBlakeHash(message);
+ * String bs64_1 = Base64.getEncoder().encodeToString(hash_1);
+ * System.out.println(bs64_1);
+ *
+ * @param req the message for which digest(blake2b hash) needs to be generated
+ * @return the byte[] hash value
+ * @throws Exception the exception
+ */
+ public static byte[] generateBlakeHash(String req) throws Exception {
+ if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+ MessageDigest digest = MessageDigest.getInstance(BLAKE2B_512, BouncyCastleProvider.PROVIDER_NAME);
+ digest.reset();
+ digest.update(req.getBytes(StandardCharsets.UTF_8));
+ return digest.digest();
+ }
+
+ }
+
+
diff --git a/utilities/ondc-crypto-utility-master/src/main/java/org/ondc/crypto/util/CryptoKeyPair.java b/utilities/ondc-crypto-utility-master/src/main/java/org/ondc/crypto/util/CryptoKeyPair.java
new file mode 100644
index 0000000..0a3df09
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/src/main/java/org/ondc/crypto/util/CryptoKeyPair.java
@@ -0,0 +1,66 @@
+/*
+ *
+ */
+package org.ondc.crypto.util;
+
+
+// TODO: Auto-generated Javadoc
+/**
+ * The Class CryptoKeyPair is used to store keypair
+ */
+public class CryptoKeyPair {
+
+ /**
+ * Instantiates a new crypto key pair.
+ *
+ * @param publicKey the public key
+ * @param privateKey the private key
+ */
+ public CryptoKeyPair(byte[] publicKey,byte[] privateKey){
+ this.setPrivateKey(privateKey);
+ this.setPublicKey(publicKey);
+ }
+
+ /** The private key. */
+ private byte[] privateKey;
+
+ /**
+ * Gets the private key.
+ *
+ * @return the private key
+ */
+ public byte[] getPrivateKey() {
+ return privateKey;
+ }
+
+ /**
+ * Sets the private key.
+ *
+ * @param privateKey the new private key
+ */
+ public void setPrivateKey(byte[] privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ /**
+ * Gets the public key.
+ *
+ * @return the public key
+ */
+ public byte[] getPublickKey() {
+ return publicKey;
+ }
+
+ /**
+ * Sets the public key.
+ *
+ * @param publicKey the new public key
+ */
+ public void setPublicKey(byte[] publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ /** The public key. */
+ private byte[] publicKey;
+
+}
diff --git a/utilities/ondc-crypto-utility-master/src/test/java/org/ondc/crypto/util/CryptoTest.java b/utilities/ondc-crypto-utility-master/src/test/java/org/ondc/crypto/util/CryptoTest.java
new file mode 100644
index 0000000..72c9c7f
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/src/test/java/org/ondc/crypto/util/CryptoTest.java
@@ -0,0 +1,228 @@
+package org.ondc.crypto.util;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Base64;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+
+
+// TODO: Auto-generated Javadoc
+/**
+ * The Class CryptoTest is used to test ondc.crypto.util.CryptoFunctions
+ */
+public class CryptoTest {
+
+ /** The test case 1. */
+ private final String testCase1="Positive flow of signing and verification";
+
+ /**
+ * Test generation of signing key pair
+ */
+ @Test
+ @DisplayName(testCase1)
+ public void testGenerateSigningKeyPair_Normal() {
+ System.out.println("==========================================================================");
+ System.out.println(testCase1);
+ System.out.println("==========================================================================");
+
+ //System.out.println(testCase1+": Start");
+ System.out.println("\n");
+
+ CryptoKeyPair signingKeyPair=CryptoFunctions.generateSigningKeyPair();
+
+ System.out.println("Key Generation :: [OK]");
+
+ String message="message to be signed";
+
+ byte[] signature= CryptoFunctions.sign(signingKeyPair.getPrivateKey(), message.getBytes());
+
+ System.out.println("Signing :: [OK]");
+
+ System.out.println("\n\n/** Sender Side **/");
+ System.out.println("{");
+ System.out.println("\t\"message \":\""+message +"\",");
+ System.out.println("\t\"signature \":\""+Base64.getEncoder().encodeToString(signature)+"\",");
+ System.out.println("\t\"privateKey \":\""+Base64.getEncoder().encodeToString(signingKeyPair.getPrivateKey()) +"\",");
+ System.out.println("}\n\n");
+
+
+ boolean verificationResult=CryptoFunctions.verify(signature, message.getBytes(), signingKeyPair.getPublickKey());
+
+ System.out.println("\n\n/** Receiver Side **/");
+ System.out.println("{");
+ System.out.println("\t\"message \":\""+message +"\",");
+ System.out.println("\t\"signature \":\""+Base64.getEncoder().encodeToString(signature)+"\",");
+ System.out.println("\t\"publicKey \":\""+Base64.getEncoder().encodeToString(signingKeyPair.getPublickKey()) +"\",");
+ System.out.println("\t\"verified \":\""+verificationResult +"\",");
+ System.out.println("}\n\n");
+ if(verificationResult)
+ System.out.println("Verification :: [OK]");
+ else
+ System.out.println("Verification :: [[NOT OK]]");
+
+ assertEquals(true, verificationResult);
+
+
+ }
+
+ /**
+ * Test to verify tampered message and signature
+ */
+ @Test
+ @DisplayName("Negative Flow to check whether tampered message is verified unsuccessfully")
+ public void testGenerateSigningKeyPair_Tampered() {
+ System.out.println("==========================================================================");
+ System.out.println("Negative Flow to check whether tampered message is verified unsuccessfully");
+ System.out.println("==========================================================================");
+ System.out.println("\n");
+
+ CryptoKeyPair signingKeyPair=CryptoFunctions.generateSigningKeyPair();
+ System.out.println("Key Generation :: [OK]");
+
+
+ String message="message to be signed";
+
+ byte[] signature= CryptoFunctions.sign(signingKeyPair.getPrivateKey(), message.getBytes());
+ System.out.println("Signing :: [OK]");
+
+ System.out.println("\n\n/** Sender Side **/");
+ System.out.println("{");
+ System.out.println("\t\"message \":\""+message +"\",");
+ System.out.println("\t\"signature \":\""+Base64.getEncoder().encodeToString(signature)+"\",");
+ System.out.println("\t\"privateKey \":\""+Base64.getEncoder().encodeToString(signingKeyPair.getPrivateKey()) +"\",");
+ System.out.println("}\n\n");
+
+ message="tampered message to be verified";
+
+ boolean verificationResult=CryptoFunctions.verify(signature, message.getBytes(), signingKeyPair.getPublickKey());
+
+ System.out.println("\n\n/** Receiver Side **/");
+ System.out.println("{");
+ System.out.println("\t\"message \":\""+message +"\",");
+ System.out.println("\t\"signature \":\""+Base64.getEncoder().encodeToString(signature)+"\",");
+ System.out.println("\t\"publicKey \":\""+Base64.getEncoder().encodeToString(signingKeyPair.getPublickKey()) +"\",");
+ System.out.println("\t\"verified \":\""+verificationResult +"\",");
+ System.out.println("}\n\n");
+
+ if(!verificationResult)
+ System.out.println("Verification Failed as expected :: [OK]");
+ else
+ System.out.println("Verification Failed as expected :: [[NOT OK]]");
+
+
+ assertEquals(false, verificationResult);
+ }
+
+ /**
+ * Test generate encryption decryption key pair, encrypt and then decrypt.
+ *
+ * @throws InvalidKeyException the invalid key exception
+ * @throws NoSuchAlgorithmException the no such algorithm exception
+ * @throws NoSuchProviderException the no such provider exception
+ * @throws InvalidKeySpecException the invalid key spec exception
+ * @throws NoSuchPaddingException the no such padding exception
+ * @throws IllegalBlockSizeException the illegal block size exception
+ * @throws BadPaddingException the bad padding exception
+ */
+ @Test
+ @DisplayName("To check normal flow of Encryption and Decryption")
+ public void testGenerateEncryptionDecryptionKeyPair_Normal() throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
+ System.out.println("=================================================");
+ System.out.println("To check normal flow of Encryption and Decryption");
+ System.out.println("=================================================");
+ CryptoKeyPair senderEncDecKeyPair=null;
+ CryptoKeyPair receiverEncDecKeyPair=null;
+
+ try {
+ senderEncDecKeyPair= CryptoFunctions.generateEncDecKey();
+ receiverEncDecKeyPair= CryptoFunctions.generateEncDecKey();
+ System.out.println("Key Generation :: [OK]");
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ String message="message to be encrypted";
+
+ byte[] encrypted= CryptoFunctions.encryptDecrypt(Cipher.ENCRYPT_MODE,message.getBytes(),senderEncDecKeyPair.getPrivateKey(),receiverEncDecKeyPair.getPublickKey());
+
+ System.out.println("Encryption :: [OK]");
+
+ System.out.println("\n\n/** Sender Side **/");
+ System.out.println("{");
+ System.out.println("\t\"plainChallengeString \":\""+message +"\",");
+ System.out.println("\t\"EncryptedChallengeString \":\""+Base64.getEncoder().encodeToString(encrypted)+"\",");
+ System.out.println("\t\"senderPrivateKey \":\""+Base64.getEncoder().encodeToString(senderEncDecKeyPair.getPrivateKey()) +"\",");
+ System.out.println("\t\"receiverPublicKey \":\""+Base64.getEncoder().encodeToString(receiverEncDecKeyPair.getPublickKey()) +"\"");
+ System.out.println("}\n\n");
+
+ byte[] decrypted= CryptoFunctions.encryptDecrypt(Cipher.DECRYPT_MODE,encrypted,receiverEncDecKeyPair.getPrivateKey(),senderEncDecKeyPair.getPublickKey());
+ System.out.println("Decryption :: [OK]");
+
+ String decryptedMessage=new String(decrypted);
+
+
+ System.out.println("\n\n/** Receiver Side **/");
+ System.out.println("{");
+ System.out.println("\t\"decryptedChallengeString \":\""+decryptedMessage+"\",");
+ System.out.println("\t\"receiverPrivateKey \":\""+Base64.getEncoder().encodeToString(receiverEncDecKeyPair.getPrivateKey()) +"\",");
+ System.out.println("\t\"senderPublicKey \":\""+Base64.getEncoder().encodeToString(senderEncDecKeyPair.getPublickKey()) +"\",");
+ System.out.println("\t\"match\":\""+message.equals(decryptedMessage)+"\"");
+
+ System.out.println("}");
+
+ if(message.equals(decryptedMessage))
+ System.out.println("Verification :: [OK]");
+ else
+ System.out.println("Verification :: [[NOT OK]]");
+
+ assertEquals(message, decryptedMessage);
+ }
+
+@Test
+@DisplayName("To check whether hashing is working")
+public void testGenerateBlakeHash() throws Exception {
+ System.out.println("===================================");
+ System.out.println("To check whether hashing is working");
+ System.out.println("===================================");
+
+ String message = "message to hash";
+ byte[] hash_1=CryptoFunctions.generateBlakeHash(message);
+ System.out.println("Message Hashed :: [OK]");
+
+ String bs64_1 = Base64.getEncoder().encodeToString(hash_1);
+ System.out.println("{");
+ System.out.println("\t\"message to be hashed\":\""+message+"\",");
+ System.out.println("\t\"first_digest\":\""+bs64_1+"\",");
+
+ byte[] hash_2=CryptoFunctions.generateBlakeHash(message);
+ System.out.println("Message Hashed :: [OK]");
+
+ String bs64_2 = Base64.getEncoder().encodeToString(hash_2);
+ System.out.println("\t\"second_digest\":\""+bs64_2+"\",");
+ System.out.println("\t\"match\":\""+bs64_2.equals(bs64_1)+"\"");
+ System.out.println("}");
+ if(bs64_2.equals(bs64_1))
+ System.out.println("Hash Matching :: [OK]");
+ else
+ System.out.println("Hash Matching :: [[NOT OK]]");
+
+ assertEquals(bs64_1 , bs64_2);
+
+
+
+}
+
+}
diff --git a/utilities/ondc-crypto-utility-master/src/test/java/org/ondc/crypto/util/Test.java b/utilities/ondc-crypto-utility-master/src/test/java/org/ondc/crypto/util/Test.java
new file mode 100644
index 0000000..289fe59
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/src/test/java/org/ondc/crypto/util/Test.java
@@ -0,0 +1,42 @@
+package org.ondc.crypto.util;
+
+import org.bouncycastle.math.ec.rfc8032.Ed25519;
+
+import java.security.SecureRandom;
+import java.util.Base64;
+
+public class Test {
+
+
+ public static void main(String[] args) {
+ CryptoKeyPair signingKeyPair=CryptoFunctions.generateSigningKeyPair();
+ byte[] privateKey = signingKeyPair.getPrivateKey();
+ System.out.printf("Private Key: %s \n",Base64.getEncoder().encodeToString(privateKey));
+ System.out.printf("Public Key: %s \n",Base64.getEncoder().encodeToString(signingKeyPair.getPublickKey()));
+
+ byte[] signMessage = Test.sign(privateKey,"IND|ONDC:RET10|sellerApp|std:080|ref-app-seller-staging-v2.ondc.org".getBytes());
+ System.out.println(Base64.getEncoder().encodeToString(signMessage));
+ }
+
+// public static void vLookUp(HashMap payload){
+// String message = String.format("%s|%s|%s|%s",)
+// payload.get("domain")
+// }
+ public static CryptoKeyPair generateSigningKeyPair() {
+ SecureRandom RANDOM = new SecureRandom();
+ byte[] privateKey = new byte[Ed25519.SECRET_KEY_SIZE];
+ byte[] publicKey = new byte[Ed25519.PUBLIC_KEY_SIZE];
+ RANDOM.nextBytes(privateKey);
+ Ed25519.generatePublicKey(privateKey, 0, publicKey, 0);
+ return new CryptoKeyPair(publicKey,privateKey) ;
+ }
+
+ public static byte[] sign(byte[] privateKey,byte[] message) {
+ // initialise signature variable
+ byte[] signature = new byte[Ed25519.SIGNATURE_SIZE];
+
+ // sign the received message with given private key
+ Ed25519.sign(privateKey, 0, message, 0, message.length, signature, 0);
+ return signature;
+ }
+}
diff --git a/utilities/ondc-crypto-utility-master/target/ondc-crypto-util-0.1-GA.jar b/utilities/ondc-crypto-utility-master/target/ondc-crypto-util-0.1-GA.jar
new file mode 100644
index 0000000..7364b72
Binary files /dev/null and b/utilities/ondc-crypto-utility-master/target/ondc-crypto-util-0.1-GA.jar differ
diff --git a/utilities/ondc-crypto-utility-master/target/surefire-reports/TEST-org.ondc.crypto.util.CryptoTest.xml b/utilities/ondc-crypto-utility-master/target/surefire-reports/TEST-org.ondc.crypto.util.CryptoTest.xml
new file mode 100644
index 0000000..23e5cab
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/target/surefire-reports/TEST-org.ondc.crypto.util.CryptoTest.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/utilities/ondc-crypto-utility-master/target/surefire-reports/org.ondc.crypto.util.CryptoTest.txt b/utilities/ondc-crypto-utility-master/target/surefire-reports/org.ondc.crypto.util.CryptoTest.txt
new file mode 100644
index 0000000..f492067
--- /dev/null
+++ b/utilities/ondc-crypto-utility-master/target/surefire-reports/org.ondc.crypto.util.CryptoTest.txt
@@ -0,0 +1,4 @@
+-------------------------------------------------------------------------------
+Test set: org.ondc.crypto.util.CryptoTest
+-------------------------------------------------------------------------------
+Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.411 s - in org.ondc.crypto.util.CryptoTest
diff --git a/utilities/ondc-igm-sdk/.gitignore b/utilities/ondc-igm-sdk/.gitignore
new file mode 100644
index 0000000..a64ade4
--- /dev/null
+++ b/utilities/ondc-igm-sdk/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+/lib
+/example
\ No newline at end of file
diff --git a/utilities/ondc-igm-sdk/.prettierrc b/utilities/ondc-igm-sdk/.prettierrc
new file mode 100644
index 0000000..a0d1c9a
--- /dev/null
+++ b/utilities/ondc-igm-sdk/.prettierrc
@@ -0,0 +1,5 @@
+{
+ "printWidth": 120,
+ "trailingComma": "all",
+ "singleQuote": true
+}
diff --git a/utilities/ondc-igm-sdk/jestconfig.json b/utilities/ondc-igm-sdk/jestconfig.json
new file mode 100644
index 0000000..20c25c0
--- /dev/null
+++ b/utilities/ondc-igm-sdk/jestconfig.json
@@ -0,0 +1,7 @@
+{
+ "transform": {
+ "^.+\\.(t|j)sx?$": "ts-jest"
+ },
+ "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$",
+ "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"]
+}
diff --git a/utilities/ondc-igm-sdk/package.json b/utilities/ondc-igm-sdk/package.json
new file mode 100644
index 0000000..abff505
--- /dev/null
+++ b/utilities/ondc-igm-sdk/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "ondc-igm-sdk",
+ "version": "1.0.0",
+ "description": "ONDC official IGM Package",
+ "main": "lib/index.js",
+ "types": "lib/index.d.ts",
+ "homepage": "https://github.com/robin-chauhan1/ondc-igm-sdk",
+ "scripts": {
+ "test": "jest --config jestconfig.json",
+ "build": "tsc",
+ "format": "prettier --write \"src/**/*.ts\" \"src/**/*.js\"",
+ "lint": "tslint -p tsconfig.json",
+ "prepare": "npm run build",
+ "prepublishOnly": "npm run lint",
+ "preversion": "npm run lint",
+ "version": "npm run format && git add -A src",
+ "postversion": "git push && git push --tags"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/robin-chauhan1/ondc-igm-sdk"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@types/express": "^4.17.17",
+ "@types/jest": "^29.5.3",
+ "@types/uuid": "^9.0.2",
+ "jest": "^29.6.1",
+ "nodemon": "^3.0.1",
+ "prettier": "^3.0.0",
+ "ts-jest": "^29.1.1",
+ "ts-node": "^10.9.1",
+ "tslint": "^6.1.3",
+ "tslint-config-prettier": "^1.18.0",
+ "typescript": "^5.1.6"
+ },
+ "dependencies": {
+ "axios": "^1.4.0",
+ "express": "^4.18.2",
+ "firebase-admin": "^11.10.1",
+ "joi": "^17.9.2",
+ "uuid": "^9.0.0"
+ }
+}
diff --git a/utilities/ondc-igm-sdk/src/constants/endpoints.ts b/utilities/ondc-igm-sdk/src/constants/endpoints.ts
new file mode 100644
index 0000000..addb2e7
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/constants/endpoints.ts
@@ -0,0 +1,5 @@
+const ALL_ROUTES = {
+ ISSUE: '/issue',
+};
+
+export default ALL_ROUTES;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/.DS_Store b/utilities/ondc-igm-sdk/src/igmManager/.DS_Store
new file mode 100644
index 0000000..25443ad
Binary files /dev/null and b/utilities/ondc-igm-sdk/src/igmManager/.DS_Store differ
diff --git a/utilities/ondc-igm-sdk/src/igmManager/Issue/index.ts b/utilities/ondc-igm-sdk/src/igmManager/Issue/index.ts
new file mode 100644
index 0000000..a1d3e43
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/Issue/index.ts
@@ -0,0 +1,5 @@
+import Issue from './service';
+
+const issue = new Issue();
+
+export default issue;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/Issue/service.ts b/utilities/ondc-igm-sdk/src/igmManager/Issue/service.ts
new file mode 100644
index 0000000..98a4b98
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/Issue/service.ts
@@ -0,0 +1,116 @@
+import { IGM_ROUTES, EvaluateRoute, IssuesParamaters, ERROR_CODES } from '../interfaces/igm.types';
+import igmController from '../controller/igm.controller';
+import BuyerManager from '../manager/buyer';
+import { RouteSpecificManagerProps } from '../interfaces/manager.type';
+import SellerManager from '../manager/seller';
+import LogisticsServices from '../services/logisticsServices';
+
+const buyerManager = new BuyerManager();
+const sellerManager = new SellerManager();
+const logisticsService = new LogisticsServices();
+
+class Issues {
+ config: IssuesParamaters | undefined;
+
+ init(params: IssuesParamaters) {
+ if (!this.config) this.config = { ...params };
+ else throw new Error('Issues class has already been initialised');
+ }
+
+ /**
+ * Evaluates the provided route and invokes the corresponding controller function.
+ *
+ * @param req - The HTTP request object.
+ * @param res - The HTTP response object.
+ * @param route - The route to be evaluated.
+ * @returns An object with error information if an error occurs; otherwise, it returns undefined.
+ */
+ evaluateRoute({ req, res, route }: EvaluateRoute) {
+ try {
+ switch (route) {
+ case IGM_ROUTES.ISSUE:
+ // TO-DO: split validation logic in different function
+
+ if (!(this.config?.npType.includes('SELLER') || this.config?.npType.includes('LOGISTICS')))
+ throw new Error('issue endpoint cannot be hosted if NP is not seller');
+ igmController.issue({ req, res });
+
+ // TO-DO: split post-callback action in different function
+ if (this.config.onSuccess?.[IGM_ROUTES.ISSUE]) {
+ this.config.onSuccess?.[IGM_ROUTES.ISSUE]('');
+ }
+ break;
+ case IGM_ROUTES.ON_ISSUE:
+ // TO-DO: split validation logic in different function
+ if (!this.config?.npType.includes('BUYER'))
+ throw new Error('on_issue endpoint cannot be hosted if NP is not buyer');
+ igmController.on_issue(req, res);
+ // TO-DO: split post-callback action in different function
+ if (this.config.onSuccess?.[IGM_ROUTES.ON_ISSUE]) {
+ this.config.onSuccess?.[IGM_ROUTES.ON_ISSUE]('');
+ }
+ break;
+ case IGM_ROUTES.ISSUE_STATUS:
+ // TO-DO: split validation logic in different function
+ if (!(this.config?.npType.includes('SELLER') || this.config?.npType.includes('LOGISTICS')))
+ throw new Error('issue_status endpoint cannot be hosted if NP is not seller');
+ igmController.issue_status(req, res);
+ // TO-DO: split post-callback action in different function
+ if (this.config.onSuccess?.[IGM_ROUTES.ISSUE_STATUS]) {
+ this.config.onSuccess?.[IGM_ROUTES.ISSUE_STATUS]('');
+ }
+ break;
+ case IGM_ROUTES.ON_ISSUE_STATUS:
+ // TO-DO: split validation logic in different function
+ if (!this.config?.npType.includes('BUYER'))
+ throw new Error('on_issue_status endpoint cannot be hosted if NP is not buyer');
+ igmController.on_issue_status(req, res);
+ // TO-DO: split post-callback action in different function
+ if (this.config.onSuccess?.[IGM_ROUTES.ON_ISSUE_STATUS]) {
+ this.config.onSuccess?.[IGM_ROUTES.ON_ISSUE_STATUS]('');
+ }
+ break;
+ default:
+ throw new Error('Unknown route');
+ }
+ } catch (err: any) {
+ const errPayload = { message: err?.message, code: ERROR_CODES.ROUTE_NOT_VALID };
+ console.log('here', this.config?.onError);
+ if (this.config?.onError) this.config?.onError(errPayload);
+ return { ...errPayload, error: true };
+ }
+ return { success: true };
+ }
+
+ buyerIssue({ issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return buyerManager.issue({ issue, onError, onNack, onSuccess });
+ }
+
+ sellerOnIssue({ on_issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return sellerManager.on_issue({ on_issue, onError, onNack, onSuccess });
+ }
+
+ buyerIsseStatus({ issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return buyerManager.issue_status({ issue_status, onError, onNack, onSuccess });
+ }
+
+ sellerOnIssueStatus({ on_issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return sellerManager.on_issue_status({ on_issue_status, onError, onNack, onSuccess });
+ }
+
+ issueSellerToLogisitics({ issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return sellerManager.logistics_issue({ issue, onError, onNack, onSuccess });
+ }
+ issueStatusSellerToLogisitics({ issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return sellerManager.logistics_issue_status({ issue_status, onError, onNack, onSuccess });
+ }
+
+ onIssueFromLogisitics({ on_issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return logisticsService.on_issue({ on_issue, onError, onNack, onSuccess });
+ }
+ onIssueStatusFromLogistics({ on_issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ return logisticsService.on_issue_status({ on_issue_status, onError, onNack, onSuccess });
+ }
+}
+
+export default Issues;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/controller/igm.controller.ts b/utilities/ondc-igm-sdk/src/igmManager/controller/igm.controller.ts
new file mode 100644
index 0000000..6707a1c
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/controller/igm.controller.ts
@@ -0,0 +1,51 @@
+import { Request, Response } from 'express';
+import IgmServices from '../services/igm.services';
+
+const igmServices = new IgmServices();
+
+class IGMController {
+ constructor() {
+ this.issue = this.issue.bind(this);
+ }
+
+ /**
+ * Handles an issue-related HTTP request and invokes the corresponding service method.
+ *
+ * @param req - The HTTP request object.
+ * @param res - The HTTP response object.
+ */
+
+ async issue({ req, res }: { req: Request; res: Response }) {
+ try {
+ igmServices.issue(req, res);
+ } catch (err) {
+ console.log(err);
+ }
+ }
+
+ on_issue(req: Request, res: Response) {
+ try {
+ igmServices.on_issue(req, res);
+ } catch (err) {
+ console.log(err);
+ }
+ }
+
+ issue_status(req: Request, res: Response) {
+ try {
+ igmServices.issue_status(req, res);
+ } catch (err) {
+ console.log(err);
+ }
+ }
+
+ on_issue_status(req: Request, res: Response) {
+ try {
+ igmServices.on_issue_status(req, res);
+ } catch (err) {
+ console.log(err);
+ }
+ }
+}
+
+export default new IGMController();
diff --git a/utilities/ondc-igm-sdk/src/igmManager/interfaces/igm.types.ts b/utilities/ondc-igm-sdk/src/igmManager/interfaces/igm.types.ts
new file mode 100644
index 0000000..064e02c
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/interfaces/igm.types.ts
@@ -0,0 +1,48 @@
+import { Request, Response } from 'express';
+
+export enum IGM_ROUTES {
+ ISSUE = 'issue',
+ ON_ISSUE = 'on_issue',
+ ISSUE_STATUS = 'issue_status',
+ ON_ISSUE_STATUS = 'on_issue_status',
+}
+
+export enum ERROR_CODES {
+ ROUTE_NOT_VALID = 'ROUTE_NOT_VALID',
+}
+
+export type DOMAINS = 'RETAIL' | 'MOBILITY' | 'LOGISTICS';
+
+export type NP_TYPES = 'BUYER' | 'SELLER' | 'LOGISTICS';
+
+export type IgmRoutes = IGM_ROUTES.ISSUE | IGM_ROUTES.ON_ISSUE | IGM_ROUTES.ISSUE_STATUS | IGM_ROUTES.ON_ISSUE_STATUS;
+
+export interface EvaluateRoute {
+ req: Request;
+ res: Response;
+ route: IgmRoutes;
+}
+
+export interface IssueParameterContext {
+ subscriberType: NP_TYPES;
+ subscriberId: string;
+ subscriberURL: string;
+ subscriberDomain: string;
+ subscriberCountry: string;
+ subcriberState: string;
+ subscriberCity: string;
+ expected_response_time: string;
+ expected_resolution_time: string;
+ ttl: string;
+}
+
+export interface IssuesParamaters {
+ validateSchema: boolean;
+ domain: DOMAINS[];
+ npType: NP_TYPES[];
+ context: IssueParameterContext[];
+ onSuccess?: {
+ [k in IgmRoutes]?: (args: T) => any;
+ };
+ onError?: (args: K) => any;
+}
diff --git a/utilities/ondc-igm-sdk/src/igmManager/interfaces/issue.types.ts b/utilities/ondc-igm-sdk/src/igmManager/interfaces/issue.types.ts
new file mode 100644
index 0000000..174b35f
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/interfaces/issue.types.ts
@@ -0,0 +1,161 @@
+import {
+ ComplainantInfo,
+ Context,
+ IBaseIssue,
+ Issue,
+ IssueActions,
+ Item,
+ Message,
+ OrderDetails,
+ Person,
+} from './issueBase.types';
+
+export type ChangeFields = Omit & R;
+
+export type OmitKey = Pick>;
+
+// use this for /on_issue
+export type OnIssue = ChangeFields<
+ Omit,
+ {
+ context: Omit;
+ message: ChangeFields<
+ Message,
+ {
+ issue: ChangeFields<
+ Omit<
+ Issue,
+ | 'order_details'
+ | 'issue_type'
+ | 'category'
+ | 'complainant_info'
+ | 'description'
+ | 'expected_resolution_time'
+ | 'expected_response_time'
+ | 'source'
+ | 'status'
+ | 'sub_category'
+ | 'rating'
+ | 'resolution'
+ | 'resolution_provider'
+ >,
+ {
+ issue_actions: Omit;
+ }
+ >;
+ }
+ >;
+ }
+>;
+
+// issue for logistics payload
+export type IssueRequestLogistics = ChangeFields<
+ Omit,
+ {
+ message: ChangeFields<
+ Message,
+ {
+ issue: ChangeFields<
+ Omit,
+ {
+ complainant_info: ChangeFields<
+ ComplainantInfo,
+ {
+ person: Omit;
+ }
+ >;
+
+ order_details: ChangeFields<
+ Omit,
+ {
+ items: Omit- [];
+ }
+ >;
+ }
+ >;
+ }
+ >;
+ }
+>;
+
+export type IssueRequestLogisticsResolved = ChangeFields<
+ Omit
,
+ {
+ message: ChangeFields<
+ Message,
+ {
+ issue: ChangeFields<
+ Issue,
+ {
+ complainant_info: ChangeFields<
+ ComplainantInfo,
+ {
+ person: Omit;
+ }
+ >;
+ order_details: ChangeFields<
+ Omit,
+ {
+ items: Omit- [];
+ }
+ >;
+ }
+ >;
+ }
+ >;
+ }
+>;
+
+// issue_request contains complainent actions
+export type IssueRequest = ChangeFields<
+ IBaseIssue,
+ {
+ message: ChangeFields<
+ Message,
+ {
+ issue: Omit
;
+ }
+ >;
+ }
+>;
+
+
+// use this for /on_issue_status when Seller has RESOLVED the issue
+
+export type OnIssueStatusResoloved = ChangeFields<
+ Omit,
+ {
+ context: Omit;
+ message: ChangeFields<
+ Message,
+ {
+ issue: ChangeFields<
+ Omit<
+ Issue,
+ | 'order_details'
+ | 'issue_type'
+ | 'category'
+ | 'complainant_info'
+ | 'description'
+ | 'expected_resolution_time'
+ | 'expected_response_time'
+ | 'source'
+ | 'status'
+ | 'sub_category'
+ | 'rating'
+ >,
+ {
+ issue_actions: Omit;
+ }
+ >;
+ }
+ >;
+ }
+>;
+
+export type IssueStatusPayload = {
+ context: Context;
+ message: {
+ issue_id: string;
+ };
+};
diff --git a/utilities/ondc-igm-sdk/src/igmManager/interfaces/issueBase.types.ts b/utilities/ondc-igm-sdk/src/igmManager/interfaces/issueBase.types.ts
new file mode 100644
index 0000000..aeb502f
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/interfaces/issueBase.types.ts
@@ -0,0 +1,189 @@
+/// Base interface for all the All Responses
+export interface IBaseIssue {
+ context: Context;
+ message: Message;
+}
+export interface Context {
+ domain: string;
+ country: string;
+ city: string;
+ state?: string;
+ action: string;
+ core_version: string;
+ bap_id: string;
+ bap_uri: string;
+ bpp_id: string;
+ bpp_uri: string;
+ transaction_id: string;
+ message_id: string;
+ timestamp: string;
+ ttl: string;
+}
+export interface Message {
+ issue: Issue;
+}
+
+export enum Rating {
+ 'THUMBS-UP',
+ 'THUMBS-DOWN',
+}
+
+export interface Issue {
+ id: string;
+ category: string;
+ sub_category: string;
+ complainant_info: ComplainantInfo;
+ order_details: OrderDetails;
+ description: Description;
+ source: Source;
+ expected_response_time: ExpectedResTime;
+ expected_resolution_time: ExpectedResTime;
+ status: string;
+ issue_type: string;
+ issue_actions: IssueActions;
+ rating?: Rating;
+ resolution: Resolution | ResolutionWithoutRefund;
+ resolution_provider: ResolutionProvider;
+ created_at: string;
+ updated_at: string;
+}
+
+export interface ResolutionProvider {
+ respondent_info: RespondentInfo;
+}
+
+export interface Organization {
+ org: Org;
+ contact: Contact;
+ person: Org;
+}
+
+export interface Contact {
+ phone: string;
+ email: string;
+}
+
+export interface Org {
+ name: string;
+}
+
+export interface ResolutionSupport {
+ chat_link: string;
+ contact: Contact;
+ gros: Gro[];
+}
+
+export interface Gro {
+ person: Org;
+ contact: Contact;
+ gro_type: string;
+}
+
+export interface RespondentInfo {
+ type: string;
+ organization: Organization;
+ resolution_support: ResolutionSupport;
+}
+
+export interface Resolution {
+ short_desc: string;
+ long_desc: string;
+ action_triggered: 'REFUND';
+ refund_amount: string;
+}
+
+export interface ResolutionWithoutRefund {
+ short_desc: string;
+ long_desc: string;
+ action_triggered: 'RESOLVED' | 'REPLACE' | 'NO-ACTION' | 'CASCADED' | string;
+}
+
+export interface ComplainantInfo {
+ person: Person;
+ contact: ComplainantInfoContact;
+}
+export interface ComplainantInfoContact {
+ phone: string;
+ email: string;
+}
+export interface Person {
+ name: string;
+ email: string;
+}
+export interface Description {
+ short_desc: string;
+ long_desc: string;
+ additional_desc: AdditionalDesc;
+ images: string[];
+}
+export interface AdditionalDesc {
+ url: string;
+ content_type: string;
+}
+export interface ExpectedResTime {
+ duration: string;
+}
+export interface IssueActions {
+ complainant_actions: ComplainantAction[];
+ respondent_actions: RespondentAction[];
+}
+export interface ComplainantAction {
+ complainant_action: string;
+ short_desc: string;
+ updated_at: string;
+ updated_by: UpdatedBy;
+}
+export interface UpdatedBy {
+ org: Org;
+ contact: UpdatedByContact;
+ person: Org;
+}
+export interface RespondentAction {
+ respondent_action: string;
+ short_desc: string;
+ updated_at: string;
+ updated_by: UpdatedBy;
+ cascaded_level: number;
+}
+
+export interface UpdatedBy {
+ org: Org;
+ contact: Contact;
+ person: Org;
+}
+
+export interface Contact {
+ phone: string;
+ email: string;
+}
+
+export interface Org {
+ name: string;
+}
+export interface UpdatedByContact {
+ phone: string;
+ email: string;
+}
+export interface Org {
+ name: string;
+}
+export interface OrderDetails {
+ id: string;
+ state: string;
+ items: Item[];
+ fulfillments: Fulfillment[];
+ provider_id: string;
+ merchant_order_id?: string;
+}
+export interface Fulfillment {
+ id: string;
+ state: string;
+}
+export interface Item {
+ id: string;
+ quantity: number;
+}
+export interface Source {
+ network_participant_id: string;
+ type: string;
+}
diff --git a/utilities/ondc-igm-sdk/src/igmManager/interfaces/manager.type.ts b/utilities/ondc-igm-sdk/src/igmManager/interfaces/manager.type.ts
new file mode 100644
index 0000000..e807da1
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/interfaces/manager.type.ts
@@ -0,0 +1,65 @@
+import { ChangeFields, IssueRequest, IssueStatusPayload, OnIssue, OnIssueStatusResoloved } from './issue.types';
+
+export type IssuePayloadProps = ChangeFields<
+ IssueRequest,
+ {
+ context: Omit<
+ IssueRequest['context'],
+ 'bap_id' | 'bap_uri' | 'city' | 'state' | 'country' | 'timestamp' | 'transaction_id' | 'message_id'
+ >;
+ message: {
+ issue: Omit;
+ };
+ }
+>;
+
+export type OnIssuePayloadProps = ChangeFields<
+ IssueRequest,
+ {
+ context: Omit;
+ message: {
+ issue: Omit;
+ };
+ }
+>;
+
+export type IssueStatusPayloadProps = ChangeFields<
+ IssueStatusPayload,
+ {
+ context: Omit<
+ IssueStatusPayload['context'],
+ 'bap_id' | 'bap_uri' | 'city' | 'state' | 'country' | 'domain' | 'core_version'
+ >;
+ }
+>;
+
+export type OnIssueStatusPayloadProps = ChangeFields<
+ OnIssueStatusResoloved,
+ {
+ context: Omit<
+ IssueStatusPayload['context'],
+ 'bpp_id' | 'bpp_uri' | 'city' | 'state' | 'country' | 'domain' | 'core_version'
+ >;
+ }
+>;
+
+interface Callbacks {
+ onSuccess: (successResponse: any) => void;
+ onError: (errorResponse: any) => void;
+ onNack: (nackResponse: any) => void;
+}
+
+export type ManagerProps = {
+ issue?: U;
+ on_issue?: V;
+ issue_status?: W;
+ on_issue_status?: K;
+} & Callbacks;
+
+export type RouteSpecificManagerProps = ManagerProps<
+ 'issue' | 'on_issue' | 'issue_status' | 'on_issue_status',
+ IssuePayloadProps,
+ OnIssuePayloadProps,
+ IssueStatusPayloadProps,
+ OnIssueStatusPayloadProps
+>;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/manager/buyer/index.ts b/utilities/ondc-igm-sdk/src/igmManager/manager/buyer/index.ts
new file mode 100644
index 0000000..14e1d10
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/manager/buyer/index.ts
@@ -0,0 +1,54 @@
+import { RouteSpecificManagerProps } from '../../interfaces/manager.type';
+import BuyerServices from '../../services/buyerServices';
+
+const buyerServices = new BuyerServices();
+
+class BuyerManager {
+ constructor() {
+ this.issue = this.issue.bind(this);
+ }
+
+ async issue({ issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await buyerServices.issue(issue!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+
+ async issue_status({ issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await buyerServices.issue_status(issue_status!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+}
+
+export default BuyerManager;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/manager/logistics/index.ts b/utilities/ondc-igm-sdk/src/igmManager/manager/logistics/index.ts
new file mode 100644
index 0000000..48bdbde
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/manager/logistics/index.ts
@@ -0,0 +1,50 @@
+import { RouteSpecificManagerProps } from '../../interfaces/manager.type';
+import LogisticsServices from '../../services/logisticsServices';
+
+const logisticsService = new LogisticsServices();
+
+class LogisticsManager {
+ async on_issue({ on_issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await logisticsService.on_issue(on_issue!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+
+ async on_issue_status({ on_issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await logisticsService.on_issue_status(on_issue_status!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+}
+
+export default LogisticsManager;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/manager/seller/index.ts b/utilities/ondc-igm-sdk/src/igmManager/manager/seller/index.ts
new file mode 100644
index 0000000..5df1af6
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/manager/seller/index.ts
@@ -0,0 +1,91 @@
+import { RouteSpecificManagerProps } from '../../interfaces/manager.type';
+import SellerService from '../../services/sellerServices';
+
+const sellerService = new SellerService();
+
+class SellerManager {
+ async logistics_issue({ issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await sellerService.issueToLogistics(issue!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+
+ async logistics_issue_status({ issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await sellerService.issue_statusToLogistics(issue_status!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+
+ async on_issue({ on_issue, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await sellerService.on_issue_post(on_issue!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+ async on_issue_status({ on_issue_status, onError, onNack, onSuccess }: RouteSpecificManagerProps) {
+ try {
+ const response: any = await sellerService.on_issue_status_post(on_issue_status!);
+
+ if (response.status === 200) {
+ if (response.data.message.ack.status === 'ACK') {
+ return onSuccess(response.data);
+ } else {
+ if (onNack) {
+ return onNack(response.data);
+ }
+ }
+ }
+ return onError(response.data);
+ } catch (e) {
+ if (onError) {
+ onError(e);
+ }
+ }
+ }
+}
+
+export default SellerManager;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/middleware/requestValidator.ts b/utilities/ondc-igm-sdk/src/igmManager/middleware/requestValidator.ts
new file mode 100644
index 0000000..53cce29
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/middleware/requestValidator.ts
@@ -0,0 +1,46 @@
+import { Request, NextFunction, Response } from 'express';
+import IssueInstance from '../Issue';
+import ALL_ROUTES from '../../constants/endpoints';
+import { IssueSchema } from '../../utils/schema';
+
+/**
+ * Middleware for validating the request body against the given Joi schema.
+ *
+ * @param schema - The Joi schema used for request body validation.
+ * @returns A middleware function that validates the request body, returning a 400 response with error details if invalid.
+ */
+
+function validateSchema({ requestedBody, url }: { url: string; requestedBody: T }) {
+ switch (url) {
+ case ALL_ROUTES.ISSUE:
+ const { error } = IssueSchema.validate(requestedBody);
+ return error;
+
+ default:
+ return { message: 'Somthing went wrong in validation ' };
+ }
+}
+
+function validateRequest(req: Request, res: Response, next: NextFunction) {
+ // Check if the request URL contains 'issue' or 'issue_status' (with enabled schema validation)
+
+ let validationError;
+
+ if (IssueInstance.config?.validateSchema) {
+ // Validate the request body against the provided schema
+
+ // (req.originalUrl.includes('issue') || (req.originalUrl.includes('issue_status') && ))
+
+ validationError = validateSchema({ url: req.originalUrl, requestedBody: req.body });
+
+ if (validationError) {
+ console.log('error :', validationError.message);
+ return res.status(400).json({ Response: [], message: 'Invalid payload data', error: validationError.message });
+ }
+ }
+ // If the validation passes, continue to the next middleware or route handler
+
+ return next();
+}
+
+export { validateRequest };
diff --git a/utilities/ondc-igm-sdk/src/igmManager/routes/igm.routes.ts b/utilities/ondc-igm-sdk/src/igmManager/routes/igm.routes.ts
new file mode 100644
index 0000000..03eeb1d
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/routes/igm.routes.ts
@@ -0,0 +1,56 @@
+import express, { Response } from 'express';
+import issue from '../Issue';
+import { ERROR_CODES, IGM_ROUTES, IgmRoutes } from '../interfaces/igm.types';
+// import { validateRequest } from '../middleware/requestValidator';
+// import issueSchema from '../../utils/schema/issue.schema';
+
+const router = express.Router();
+
+/**
+ * Evaluates an error code and sends an appropriate HTTP response based on the error code.
+ *
+ * @param error - The error object containing the error code and other details.
+ * @param res - The HTTP response object used to send the response.
+ */
+
+const evaluateErrorCode = (error: { [key: string]: any; code: ERROR_CODES }, res: Response) => {
+ switch (error.code) {
+ case ERROR_CODES.ROUTE_NOT_VALID:
+ return res.sendStatus(404);
+ default:
+ return res.json(500).json({ message: 'Something went wrong' });
+ }
+};
+
+/**
+ * Route handler for handling issue-related POST requests with different routes.
+ *
+ * @param route - The route extracted from the request parameters.
+ * @param validateRequest - Middleware function for validating the request body against the issue schema.
+ * @param req - The HTTP request object.
+ * @param res - The HTTP response object.
+ */
+
+// validateRequest(issueSchema),
+router.post('/:route(issue|on_issue|issue_status|on_issue_status)', (req, res) => {
+ // Extract the 'route' from the request parameters and cast it to the 'IgmRoutes' type.
+
+ const route: IgmRoutes = req.params.route;
+
+ // If 'issue.config' is not available, throw an error indicating that IGM has not been initialized.
+ if (!issue.config) throw new Error('IGM has not been initialised');
+
+ // Evaluate the route using the 'evaluateRoute' method from the 'issue' object.
+ const response: any = issue.evaluateRoute({ req, res, route });
+
+ // If the response contains an error, handle the error using 'evaluateErrorCode' and send the appropriate HTTP response.
+ if (response?.error) {
+ return evaluateErrorCode(response, res);
+ }
+
+ // If no error occurs, send a 200 response with the JSON containing the 'route' name.
+ // return res.status(200).json({ name: route });
+ return;
+});
+
+export default router;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/services/buyerServices/index.ts b/utilities/ondc-igm-sdk/src/igmManager/services/buyerServices/index.ts
new file mode 100644
index 0000000..2603312
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/services/buyerServices/index.ts
@@ -0,0 +1,277 @@
+import { Response, Request } from 'express';
+import { AxiosResponse } from 'axios';
+import { v4 as uuid } from 'uuid';
+import { PROTOCOL_CONTEXT } from '../../../shared/contents';
+import { IssueStatusPayload, IssueRequest, OnIssue, OnIssueStatusResoloved } from '../../interfaces/issue.types';
+import IssueInstance from '../../Issue';
+import postApi from '../../../utils/posApi';
+import { IssuePayloadProps, IssueStatusPayloadProps } from '../../interfaces/manager.type';
+import { SchemaValidator } from '../../../utils/validator.schema';
+import { OnIssueSchema, OnIssueStatusScehma, OnIssueStatusResolovedSchema } from '../../../utils/schema';
+import { hasResolvedAction } from '../../../utils/commonFunction';
+
+class BuyerServices {
+ constructor() {
+ this.issue = this.issue.bind(this);
+ ``;
+ }
+
+ /**
+ * Creates and submits a new issue based on the provided request.
+ *
+ * @param req - The incoming HTTP request containing the issue data in the request body.
+ * @returns A promise that resolves to the response containing the status of the submitted issue.
+ * @throws Will throw an error if an issue schema validation fails or an API call encounters an error.
+ */
+ async issue(issue: IssuePayloadProps) {
+ try {
+ const issueRequest: IssueRequest = {
+ context: {
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: PROTOCOL_CONTEXT.ISSUE,
+ core_version: '1.0.0',
+ bap_id: IssueInstance.config?.context[0].subscriberId!,
+ bap_uri: IssueInstance.config?.context[0].subscriberURL!,
+ bpp_id: issue.context.bpp_id,
+ bpp_uri: issue.context.bpp_uri,
+ transaction_id: uuid(),
+ message_id: uuid(),
+ timestamp: new Date().toISOString(),
+ ttl: issue.context.ttl,
+ },
+ message: {
+ issue: {
+ id: uuid(),
+ category: issue.message.issue.category,
+ sub_category: issue.message.issue.sub_category,
+ complainant_info: issue.message.issue.complainant_info,
+ status: issue.message.issue.status || 'OPEN',
+ issue_type: PROTOCOL_CONTEXT?.ISSUE.toUpperCase(),
+ issue_actions: issue.message.issue.issue_actions,
+ order_details: {
+ id: issue.message.issue.order_details?.id,
+ state: issue.message.issue.order_details?.state,
+ items: issue.message.issue.order_details.items,
+ fulfillments: issue.message.issue.order_details.fulfillments,
+ provider_id: issue.message.issue.order_details?.provider_id,
+ },
+ description: {
+ short_desc: issue.message.issue.description?.short_desc,
+ long_desc: issue.message.issue.description?.long_desc,
+ additional_desc: issue.message.issue.description?.additional_desc,
+ images: issue.message.issue.description?.images,
+ },
+ source: {
+ network_participant_id: IssueInstance.config?.context[0].subscriberId!,
+ type: 'CONSUMER',
+ },
+ expected_response_time: {
+ duration: IssueInstance.config?.context[0].expected_resolution_time!,
+ },
+ expected_resolution_time: {
+ duration: IssueInstance.config?.context[0].expected_resolution_time!,
+ },
+ created_at: issue.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+
+ const response: AxiosResponse = await postApi({
+ baseUrl: issueRequest.context.bpp_uri,
+ data: issueRequest,
+ endpoint: PROTOCOL_CONTEXT.ISSUE,
+ method: 'POST',
+ });
+ if (response.status === 200) {
+ return response;
+ }
+ return { payload: issueRequest, status: response.status, message: 'Something went wrong' };
+ } catch (err) {
+ throw err;
+ }
+ }
+
+ async on_issue(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: OnIssue = req.body;
+
+ const isOnIssueSchemaValid = SchemaValidator({ schema: OnIssueSchema, data: issueRequestpayload });
+
+ if (isOnIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isOnIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isOnIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.on_issue) {
+ IssueInstance.config?.onSuccess.on_issue({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ /**
+ * Retrieves the status of a specific issue based on the provided request.
+ *
+ * @param req - The incoming HTTP request containing the issue ID in the request body.
+ * @param res - The HTTP response object to send the issue status response.
+ * @returns A promise that resolves to the response containing the status of the requested issue.
+ * @throws Will throw an error if an API call encounters an error.
+ */
+ async issue_status(payload: IssueStatusPayloadProps) {
+ try {
+ const issueStatusPayload: IssueStatusPayload = {
+ context: {
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: 'issue_status',
+ core_version: '1.0.0',
+ bap_id: IssueInstance.config?.context[0].subscriberId!,
+ bap_uri: IssueInstance.config?.context[0].subscriberURL!,
+ bpp_id: payload.context.bpp_id,
+ bpp_uri: payload.context.bpp_uri,
+ transaction_id: payload.context.transaction_id,
+ message_id: uuid(),
+ timestamp: payload.context.timestamp,
+ ttl: payload.context.ttl,
+ },
+ message: {
+ issue_id: payload.message.issue_id,
+ },
+ };
+
+ const response: AxiosResponse = await postApi({
+ baseUrl: payload.context.bpp_uri,
+ data: issueStatusPayload,
+ endpoint: '/issue_status',
+ method: 'POST',
+ });
+ return { payload: issueStatusPayload, data: response.data, status: response.status };
+ } catch (err) {
+ throw err;
+ }
+ }
+
+ async on_issue_status(req: Request, res: Response) {
+ let isOnIssueSchemaValid;
+
+ try {
+ const issueRequestpayload: OnIssueStatusResoloved = req.body;
+
+ const respondent_actions = issueRequestpayload.message.issue.issue_actions.respondent_actions;
+
+ if (hasResolvedAction(respondent_actions)) {
+ isOnIssueSchemaValid = SchemaValidator({ schema: OnIssueStatusResolovedSchema, data: issueRequestpayload });
+ } else {
+ isOnIssueSchemaValid = SchemaValidator({ schema: OnIssueStatusScehma, data: issueRequestpayload });
+ }
+
+ if (isOnIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isOnIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isOnIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.on_issue_status) {
+ IssueInstance.config?.onSuccess.on_issue_status({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+}
+
+export default BuyerServices;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/services/igm.services.ts b/utilities/ondc-igm-sdk/src/igmManager/services/igm.services.ts
new file mode 100644
index 0000000..8af77b0
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/services/igm.services.ts
@@ -0,0 +1,44 @@
+import { Request, Response } from 'express';
+import BuyerServices from './buyerServices';
+import SellerService from './sellerServices';
+import IssueInstance from '../Issue/index';
+import LogisticsServices from './logisticsServices';
+
+const sellerService = new SellerService();
+const buyerServices = new BuyerServices();
+const logisticsServices = new LogisticsServices();
+class IgmServices {
+ constructor() {
+ this.issue = this.issue.bind(this);
+ }
+
+ issue(req: Request, res: Response) {
+ if (IssueInstance.config?.npType[0] === 'SELLER') {
+ return sellerService.issue(req, res);
+ }
+ return logisticsServices.issue(req, res);
+ }
+
+ on_issue(req: Request, res: Response) {
+ if (IssueInstance.config?.npType[0] === 'BUYER') {
+ return buyerServices.on_issue(req, res);
+ }
+ return sellerService.on_issue(req, res);
+ }
+
+ issue_status(req: Request, res: Response) {
+ if (IssueInstance.config?.npType[0] === 'SELLER') {
+ return sellerService.issue_status(req, res);
+ }
+ return logisticsServices.issue_status(req, res);
+ }
+
+ on_issue_status(req: Request, res: Response) {
+ if (IssueInstance.config?.npType[0] === 'BUYER') {
+ return buyerServices.on_issue_status(req, res);
+ }
+ return sellerService.on_issue_status(req, res);
+ }
+}
+
+export default IgmServices;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/services/logisticsServices/index.ts b/utilities/ondc-igm-sdk/src/igmManager/services/logisticsServices/index.ts
new file mode 100644
index 0000000..043d898
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/services/logisticsServices/index.ts
@@ -0,0 +1,418 @@
+import { Request, Response } from 'express';
+import IssueInstance from '../../Issue/';
+import { SchemaValidator } from '../../../utils/validator.schema';
+import { IssueStatusSchema, LogisticsIssueSchema } from '../../../utils/schema';
+import { IssueStatusPayload, OnIssue } from '../../interfaces/issue.types';
+import { AxiosResponse } from 'axios';
+import postApi from '../../../utils/posApi';
+import { hasRefundKey, hasResolvedAction } from '../../../utils/commonFunction';
+
+class LogisticsServices {
+ async issue(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: OnIssue = req.body;
+
+ const isIssueSchemaValid = SchemaValidator({ schema: LogisticsIssueSchema, data: issueRequestpayload });
+
+ if (isIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.issue) {
+ IssueInstance.config?.onSuccess.issue({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ async issue_status(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: IssueStatusPayload = req.body;
+
+ const isIssueSchemaValid = SchemaValidator({ schema: IssueStatusSchema, data: issueRequestpayload });
+
+ if (isIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.issue_status) {
+ IssueInstance.config?.onSuccess.issue_status({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ async on_issue(payload: any) {
+ let on_issue_payload;
+ if (!hasResolvedAction(payload.message.issue.issue_actions.respondent_actions)) {
+ on_issue_payload = {
+ context: {
+ ttl: payload.context.ttl,
+ domain: payload.context.domain,
+ country: payload.context.country,
+ city: payload.context.city,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ complainant_actions: payload.message.issue.issue_actions.complainant_actions,
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+ } else if (hasRefundKey(payload)) {
+ on_issue_payload = {
+ context: {
+ ttl: payload.context.ttl,
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ complainant_actions: payload.message.issue.issue_actions.complainant_actions,
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ resolution_provider: {
+ respondent_info: {
+ organization: {
+ contact: {
+ email: payload.message.issue.resolution_provider.respondent_info.organization.contact.email,
+ phone: payload.message.issue.resolution_provider.respondent_info.organization.contact.phone,
+ },
+ org: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.org.name,
+ },
+ person: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.person.name,
+ },
+ },
+ type: payload.message.issue.resolution_provider.respondent_info.type,
+ resolution_support: {
+ chat_link: payload.message.issue.resolution_provider.respondent_info.resolution_support.chat_link,
+ contact: {
+ phone: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.phone,
+ email: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.email,
+ },
+ gros: payload.message.issue.resolution_provider.respondent_info.resolution_support.gros,
+ },
+ },
+ },
+ resolution: {
+ short_desc: payload.message.issue.resolution.short_desc,
+ long_desc: payload.message.issue.resolution.long_desc,
+ action_triggered: 'REFUND',
+ refund_amount: payload.message.issue.resolution.refund_amount,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+ } else {
+ on_issue_payload = {
+ context: {
+ ttl: payload.context.ttl,
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ complainant_actions: payload.message.issue.issue_actions.complainant_actions,
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ resolution_provider: {
+ respondent_info: {
+ organization: {
+ contact: {
+ email: payload.message.issue.resolution_provider.respondent_info.organization.contact.email,
+ phone: payload.message.issue.resolution_provider.respondent_info.organization.contact.phone,
+ },
+ org: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.org.name,
+ },
+ person: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.person.name,
+ },
+ },
+ type: payload.message.issue.resolution_provider.respondent_info.type,
+ resolution_support: {
+ chat_link: payload.message.issue.resolution_provider.respondent_info.resolution_support.chat_link,
+ contact: {
+ phone: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.phone,
+ email: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.email,
+ },
+ gros: payload.message.issue.resolution_provider.respondent_info.resolution_support.gros,
+ },
+ },
+ },
+ resolution: {
+ short_desc: payload.message.issue.resolution.short_desc,
+ long_desc: payload.message.issue.resolution.long_desc,
+ action_triggered: payload.message.issue.resolution.action_triggered,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+ }
+
+ try {
+ const response: AxiosResponse = await postApi({
+ baseUrl: payload.context.bap_uri,
+ data: on_issue_payload,
+ endpoint: '/on_issue_status',
+ method: 'POST',
+ });
+
+ if (response.status === 200) {
+ return response;
+ }
+
+ return { payload: on_issue_payload, status: response.status, message: 'Something went wrong' };
+ } catch (e) {
+ return e;
+ }
+ }
+
+ async on_issue_status(payload: any) {
+ console.log('🚀 ~ file: index.ts:310 ~ LogisticsServices ~ on_issue_status ~ payload:', payload);
+ let on_issue_payload;
+
+ if (hasResolvedAction(payload.message.issue.issue_actions.respondent_actions)) {
+ on_issue_payload = {
+ context: {
+ ttl: payload.context.ttl,
+ domain: payload.context.domain,
+ country: payload.context.country,
+ city: payload.context.city,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ complainant_actions: payload.message.issue.issue_actions.complainant_actions,
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ resolution_provider: {
+ respondent_info: {
+ organization: {
+ contact: {
+ email: payload.message.issue.resolution_provider.respondent_info.organization.contact.email,
+ phone: payload.message.issue.resolution_provider.respondent_info.organization.contact.phone,
+ },
+ org: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.org.name,
+ },
+ person: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.person.name,
+ },
+ },
+ type: payload.message.issue.resolution_provider.respondent_info.type,
+ resolution_support: {
+ chat_link: payload.message.issue.resolution_provider.respondent_info.resolution_support.chat_link,
+ contact: {
+ phone: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.phone,
+ email: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.email,
+ },
+ gros: payload.message.issue.resolution_provider.respondent_info.resolution_support.gros,
+ },
+ },
+ },
+ resolution: payload.message.issue.resolution,
+ created_at: payload.message.issue.created_at,
+ updated_at: payload.message.issue.updated_at,
+ },
+ },
+ };
+ } else {
+ on_issue_payload = {
+ context: {
+ ttl: payload.context.ttl,
+ domain: payload.context.domain,
+ country: payload.context.country,
+ city: payload.context.city,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ complainant_actions: payload.message.issue.issue_actions.complainant_actions,
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: payload.message.issue.updated_at,
+ },
+ },
+ };
+ }
+
+ try {
+ const response: AxiosResponse = await postApi({
+ baseUrl: payload.context.bap_uri,
+ data: on_issue_payload,
+ endpoint: '/on_issue_status',
+ method: 'POST',
+ });
+
+ if (response.status === 200) {
+ return response;
+ }
+
+ return { payload: on_issue_payload, status: response.status, message: 'Something went wrong' };
+ } catch (e) {
+ return e;
+ }
+ }
+}
+
+export default LogisticsServices;
diff --git a/utilities/ondc-igm-sdk/src/igmManager/services/sellerServices/index.ts b/utilities/ondc-igm-sdk/src/igmManager/services/sellerServices/index.ts
new file mode 100644
index 0000000..4bbe282
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/igmManager/services/sellerServices/index.ts
@@ -0,0 +1,544 @@
+import { Request, Response } from 'express';
+import { v4 as uuid } from 'uuid';
+import IssueInstance from '../../Issue/';
+import { IssueRequest, IssueStatusPayload, OnIssue, OnIssueStatusResoloved } from '../../interfaces/issue.types';
+import { AxiosResponse } from 'axios';
+import postApi from '../../../utils/posApi';
+import { SchemaValidator } from '../../../utils/validator.schema';
+import {
+ IssueSchema,
+ IssueStatusSchema,
+ LogisticOnIssueSchema,
+ OnIssueStatusResolovedSchema,
+ OnIssueStatusScehma,
+} from '../../../utils/schema';
+import { OnIssuePayloadProps } from '../../interfaces/manager.type';
+import { PROTOCOL_CONTEXT } from '../../../shared/contents';
+import { hasRefundKey, hasResolvedAction } from '../../../utils/commonFunction';
+
+class SellerService {
+ async issue(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: IssueRequest = req.body;
+
+ const isIssueSchemaValid = SchemaValidator({ schema: IssueSchema, data: issueRequestpayload });
+
+ if (isIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.issue) {
+ IssueInstance.config?.onSuccess.issue({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ async issueToLogistics(issue: any) {
+ try {
+ const issueRequest: IssueRequest = {
+ context: {
+ domain: issue.context.domain,
+ country: issue.context.country,
+ city: issue.context.city,
+ action: PROTOCOL_CONTEXT.ISSUE,
+ core_version: '1.0.0',
+ bap_id: IssueInstance.config?.context[0].subscriberId!,
+ bap_uri: IssueInstance.config?.context[0].subscriberURL!,
+ bpp_id: issue.context.bpp_id,
+ bpp_uri: issue.context.bpp_uri,
+ transaction_id: uuid(),
+ message_id: uuid(),
+ timestamp: new Date().toISOString(),
+ ttl: issue.context.ttl,
+ },
+ message: {
+ issue: {
+ id: issue.message.issue.id,
+ category: issue.message.issue.category,
+ sub_category: issue.message.issue.sub_category,
+ complainant_info: issue.message.issue.complainant_info,
+ status: issue.message.issue.status || 'OPEN',
+ issue_type: PROTOCOL_CONTEXT?.ISSUE.toUpperCase(),
+ issue_actions: issue.message.issue.issue_actions,
+ order_details: {
+ id: issue.message.issue.order_details?.id,
+ state: issue.message.issue.order_details?.state,
+ items: issue.message.issue.order_details.items,
+ fulfillments: issue.message.issue.order_details.fulfillments,
+ provider_id: issue.message.issue.order_details?.provider_id,
+ },
+ description: {
+ short_desc: issue.message.issue.description?.short_desc,
+ long_desc: issue.message.issue.description?.long_desc,
+ additional_desc: issue.message.issue.description?.additional_desc,
+ images: issue.message.issue.description?.images,
+ },
+ source: {
+ network_participant_id: issue.message.issue.source.network_participant_id,
+ type: issue.message.issue.source.type,
+ },
+ expected_response_time: {
+ duration: issue.message.issue.expected_response_time.duration,
+ },
+ expected_resolution_time: {
+ duration: issue.message.issue.expected_response_time.duration,
+ },
+ created_at: issue.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+
+ const response: AxiosResponse = await postApi({
+ baseUrl: issueRequest.context.bpp_uri,
+ data: issueRequest,
+ endpoint: PROTOCOL_CONTEXT.ISSUE,
+ method: 'POST',
+ });
+ if (response.status === 200) {
+ return response;
+ }
+ return { payload: issueRequest, status: response.status, message: 'Something went wrong' };
+ } catch (err) {
+ throw err;
+ }
+ }
+
+ async issue_statusToLogistics(payload: any) {
+ try {
+ const issueStatusPayload: IssueStatusPayload = {
+ context: {
+ domain: payload.context.domain,
+ country: payload.context.country,
+ city: payload.context.city,
+ action: PROTOCOL_CONTEXT.ISSUE,
+ core_version: '1.0.0',
+ bap_id: IssueInstance.config?.context[0].subscriberId!,
+ bap_uri: IssueInstance.config?.context[0].subscriberURL!,
+ bpp_id: payload.context.bpp_id,
+ bpp_uri: payload.context.bpp_uri,
+ transaction_id: uuid(),
+ message_id: uuid(),
+ timestamp: new Date().toISOString(),
+ ttl: payload.context.ttl,
+ },
+ message: {
+ issue_id: payload.message.issue_id,
+ },
+ };
+
+ const response: AxiosResponse = await postApi({
+ baseUrl: payload.context.bpp_uri,
+ data: issueStatusPayload,
+ endpoint: '/issue_status',
+ method: 'POST',
+ });
+ return { payload: issueStatusPayload, data: response.data, status: response.status };
+ } catch (err) {
+ throw err;
+ }
+ }
+
+ async on_issue(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: OnIssue = req.body;
+
+ const isOnIssueSchemaValid = SchemaValidator({ schema: LogisticOnIssueSchema, data: issueRequestpayload });
+
+ if (isOnIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isOnIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isOnIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.on_issue) {
+ IssueInstance.config?.onSuccess.on_issue({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ async on_issue_post(payload: OnIssuePayloadProps) {
+ const on_issue_payload: OnIssue = {
+ context: {
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: 'on_issue',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+
+ try {
+ const response: AxiosResponse = await postApi({
+ baseUrl: payload.context.bap_uri,
+ data: on_issue_payload,
+ endpoint: '/on_issue',
+ method: 'POST',
+ });
+
+ if (response.status === 200) {
+ return response;
+ }
+
+ return { payload: on_issue_payload, status: response.status, message: 'Something went wrong' };
+ } catch (e) {
+ return e;
+ }
+ }
+
+ async issue_status(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: IssueStatusPayload = req.body;
+
+ const isIssueSchemaValid = SchemaValidator({ schema: IssueStatusSchema, data: issueRequestpayload });
+
+ if (isIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.issue_status) {
+ IssueInstance.config?.onSuccess.issue_status({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ async on_issue_status(req: Request, res: Response) {
+ try {
+ const issueRequestpayload: OnIssue | OnIssueStatusResoloved = req.body;
+
+ let isIssueSchemaValid;
+
+ if (!hasResolvedAction(issueRequestpayload.message.issue.issue_actions.respondent_actions)) {
+ isIssueSchemaValid = SchemaValidator({ schema: OnIssueStatusScehma, data: issueRequestpayload });
+ } else {
+ isIssueSchemaValid = SchemaValidator({ schema: OnIssueStatusResolovedSchema, data: issueRequestpayload });
+ }
+
+ if (isIssueSchemaValid) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ error: isIssueSchemaValid.message,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+
+ if (IssueInstance.config?.onSuccess?.on_issue_status) {
+ IssueInstance.config?.onSuccess.on_issue_status({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ }
+
+ return res.status(200).send({
+ payload: issueRequestpayload,
+ message: {
+ ack: {
+ status: 'ACK',
+ },
+ },
+ });
+ } catch (err) {
+ if (IssueInstance.config?.onError) {
+ IssueInstance.config?.onError({
+ error: err,
+ message: {
+ ack: {
+ status: 'NACK',
+ },
+ },
+ });
+ }
+ throw err;
+ }
+ }
+
+ async on_issue_status_post(payload: any) {
+ let on_issue_payload: OnIssue | OnIssueStatusResoloved;
+ const respondent_action = payload.message.issue.issue_actions.respondent_actions;
+
+ if (respondent_action.some((item: any) => item.respondent_action === 'RESOLVED') && hasRefundKey(payload)) {
+ on_issue_payload = {
+ context: {
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ resolution_provider: {
+ respondent_info: {
+ organization: {
+ contact: {
+ email: payload.message.issue.resolution_provider.respondent_info.organization.contact.email,
+ phone: payload.message.issue.resolution_provider.respondent_info.organization.contact.phone,
+ },
+ org: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.org.name,
+ },
+ person: {
+ name: payload.message.issue.resolution_provider.respondent_info.organization.person.name,
+ },
+ },
+ type: payload.message.issue.resolution_provider.respondent_info.type,
+ resolution_support: {
+ chat_link: payload.message.issue.resolution_provider.respondent_info.resolution_support.chat_link,
+ contact: {
+ phone: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.phone,
+ email: payload.message.issue.resolution_provider.respondent_info.resolution_support.contact.email,
+ },
+ gros: payload.message.issue.resolution_provider.respondent_info.resolution_support.gros,
+ },
+ },
+ },
+ resolution: {
+ short_desc: payload.message.issue.resolution.short_desc,
+ long_desc: payload.message.issue.resolution.long_desc,
+ action_triggered: 'REFUND',
+ refund_amount: payload.message.issue.resolution.refund_amount,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+ } else {
+ on_issue_payload = {
+ context: {
+ domain: IssueInstance.config?.context[0].subscriberDomain!,
+ country: IssueInstance.config?.context[0].subscriberCountry!,
+ city: IssueInstance.config?.context[0].subscriberCity!,
+ action: 'on_issue_status',
+ core_version: '1.0.0',
+ bap_id: payload.context.bap_id,
+ bap_uri: payload.context.bap_uri,
+ bpp_id: IssueInstance.config?.context[0].subscriberId!,
+ bpp_uri: IssueInstance.config?.context[0].subscriberURL!,
+ transaction_id: payload.context.transaction_id,
+ message_id: payload.context.message_id,
+ timestamp: payload.context.timestamp,
+ },
+ message: {
+ issue: {
+ id: payload.message.issue.id,
+ issue_actions: {
+ respondent_actions: payload.message.issue.issue_actions.respondent_actions,
+ },
+ created_at: payload.message.issue.created_at,
+ updated_at: new Date().toISOString(),
+ },
+ },
+ };
+ }
+
+ try {
+ const response: AxiosResponse = await postApi({
+ baseUrl: payload.context.bap_uri,
+ data: on_issue_payload,
+ endpoint: '/on_issue_status',
+ method: 'POST',
+ });
+
+ if (response.status === 200) {
+ return response;
+ }
+
+ return { payload: on_issue_payload, status: response.status, message: 'Something went wrong' };
+ } catch (e) {
+ return e;
+ }
+ }
+}
+
+export default SellerService;
diff --git a/utilities/ondc-igm-sdk/src/index.ts b/utilities/ondc-igm-sdk/src/index.ts
new file mode 100644
index 0000000..4c29b1b
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/index.ts
@@ -0,0 +1,3 @@
+import router from './igmManager/routes/igm.routes';
+
+export { router as issueRoutes };
diff --git a/utilities/ondc-igm-sdk/src/shared/cityCode.ts b/utilities/ondc-igm-sdk/src/shared/cityCode.ts
new file mode 100644
index 0000000..0cc96f0
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/shared/cityCode.ts
@@ -0,0 +1,778 @@
+export const CITY_CODE = [
+ {
+ State: 'Andhra Pradesh',
+ City: 'Chittoor',
+ Code: 'std:08572',
+ },
+ {
+ State: 'Andhra Pradesh',
+ City: 'Vuyyuru',
+ Code: 'std:08676',
+ },
+ {
+ State: 'Andhra Pradesh',
+ City: 'Vizayanagaram',
+ Code: 'std:08922',
+ },
+ {
+ State: 'Bihar',
+ City: 'Patna',
+ Code: 'std:0612',
+ },
+ {
+ State: 'Bihar',
+ City: 'Samastipur',
+ Code: 'std:06274',
+ },
+ {
+ State: 'Bihar',
+ City: 'Khagaria',
+ Code: 'std:06244',
+ },
+ {
+ State: 'Bihar',
+ City: 'Muzaffarpur',
+ Code: 'std:0621',
+ },
+ {
+ State: 'Bihar',
+ City: 'Katihar',
+ Code: 'std:06452',
+ },
+ {
+ State: 'Bihar',
+ City: 'Raxaul',
+ Code: 'std:06255',
+ },
+ {
+ State: 'Bihar',
+ City: 'Kishanganj',
+ Code: 'std:06466',
+ },
+ {
+ State: 'Bihar',
+ City: 'Darbhanga',
+ Code: 'std:06272',
+ },
+ {
+ State: 'Bihar',
+ City: 'Jamui',
+ Code: 'std:06345',
+ },
+ {
+ State: 'Bihar',
+ City: 'Madhubani',
+ Code: 'std:06276',
+ },
+ {
+ State: 'Bihar',
+ City: 'Nawada',
+ Code: 'std:06324',
+ },
+ {
+ State: 'Bihar',
+ City: 'Munger',
+ },
+ {
+ State: 'Bihar',
+ City: 'Bhagalpur',
+ Code: 'std:0641',
+ },
+ {
+ State: 'Bihar',
+ City: 'Purnia',
+ },
+ {
+ State: 'Bihar',
+ City: 'Hajipur',
+ Code: 'std:06224',
+ },
+ {
+ State: 'Bihar',
+ City: 'Begusarai',
+ Code: 'std:06243',
+ },
+ {
+ State: 'Delhi',
+ City: 'Delhi',
+ Code: 'std:011',
+ },
+ {
+ State: 'Goa',
+ City: 'Panaji',
+ Code: 'std:0832',
+ },
+ {
+ State: 'Goa',
+ City: 'Porvorim',
+ Code: 'std:0832217',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Rajkot',
+ Code: 'std:0281',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Patan',
+ Code: 'std:02766',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Anand',
+ Code: 'std:02692',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Mahesana',
+ Code: 'std:02762',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Mehsana',
+ Code: 'std:02762',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Bhavnagar',
+ Code: 'std:0278',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Gandhidham',
+ Code: 'std:02836',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Amreli',
+ Code: 'std:02792',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Vapi',
+ Code: 'std:0260',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Mundra',
+ Code: 'std:02838',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Ahmedabad',
+ Code: 'std:079',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Navsari',
+ Code: 'std:02637',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Surendranagar',
+ Code: 'std:02752',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Jamnagar',
+ Code: 'std:0288',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Vadodara',
+ Code: 'std:0265',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Unjha',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Petlad',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Valsad',
+ Code: 'std:02632',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Vallabh Vidyanagar',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Surat',
+ Code: 'std:0261',
+ },
+ {
+ State: 'Gujarat',
+ City: 'Nadiad',
+ Code: 'std:0268',
+ },
+ {
+ State: 'Haryana',
+ City: 'Gurgaon',
+ Code: 'std:0124',
+ },
+ {
+ State: 'Haryana',
+ City: 'Faridabad',
+ Code: 'std:0129',
+ },
+ {
+ State: 'Jammu & Kashmir',
+ City: 'Jammu',
+ Code: 'std:0191',
+ },
+ {
+ State: 'Jammu & Kashmir',
+ City: 'Srinagar',
+ Code: 'std:0194',
+ Column4: 'Direct code for Ooty not available, 3 sub-divisions have been mentioned with codes',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Bengaluru',
+ Code: 'std:080',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Udupi',
+ Code: 'std:0820',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Mysuru',
+ Code: 'std:0821',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Mandya',
+ Code: 'std:08232',
+ Column4: "Mentioned as 'Theni' in STD codes",
+ },
+ {
+ State: 'Karnataka',
+ City: 'Mangaluru',
+ Code: 'std:0824',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Achladi',
+ Code: 'std:08252',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Karkala',
+ Code: 'std:08258',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Chikmagalur',
+ Code: 'std:08262',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Bailhongal',
+ Code: 'std:08288',
+ },
+ {
+ State: 'Karnataka',
+ City: 'Ranebennur',
+ Code: 'std:08373',
+ },
+ {
+ State: 'Kerala',
+ City: 'Thiruvananthapuram',
+ Code: 'std:0471',
+ },
+ {
+ State: 'Kerala',
+ City: 'Quilon',
+ Code: 'std:0474',
+ },
+ {
+ State: 'Kerala',
+ City: 'Alleppy',
+ Code: 'std:0477',
+ },
+ {
+ State: 'Kerala',
+ City: 'Mavelikkara',
+ Code: 'std:0479',
+ },
+ {
+ State: 'Kerala',
+ City: 'Ernakulam',
+ Code: 'std:0484',
+ },
+ {
+ State: 'Kerala',
+ City: 'Trichur',
+ Code: 'std:0487',
+ },
+ {
+ State: 'Kerala',
+ City: 'Kannur',
+ Code: 'std:0497',
+ },
+ {
+ State: 'Madhya Pradesh',
+ City: 'Chhindwara',
+ Code: 'std:07162',
+ },
+ {
+ State: 'Madhya Pradesh',
+ City: 'Indore',
+ Code: 'std:0731',
+ },
+ {
+ State: 'Madhya Pradesh',
+ City: 'Bareli',
+ Code: 'std:07486',
+ },
+ {
+ State: 'Madhya Pradesh',
+ City: 'Bhopal',
+ Code: 'std:0755',
+ },
+ {
+ State: 'Maharashtra',
+ City: 'Pune',
+ Code: 'std:020',
+ },
+ {
+ State: 'Maharashtra',
+ City: 'Mumbai',
+ Code: 'std:022',
+ },
+ {
+ State: 'Meghalaya',
+ City: 'Shillong',
+ Code: 'std:0364',
+ },
+ {
+ State: 'Orissa',
+ City: 'Cuttack',
+ Code: 'std:0671',
+ },
+ {
+ State: 'Punjab',
+ City: 'Chandigarh',
+ Code: 'std:0172',
+ },
+ {
+ State: 'Rajasthan',
+ City: 'Jaipur',
+ Code: 'std:0141',
+ },
+ {
+ State: 'Rajasthan',
+ City: 'Kotputli',
+ Code: 'std:01421',
+ Column4: 'Direct code for Ghaziabad not available; sub-divisions mentioned with codes',
+ },
+ {
+ State: 'Rajasthan',
+ City: 'Bansur',
+ Code: 'std:01461',
+ },
+ {
+ State: 'Rajasthan',
+ City: 'Sriganganagar',
+ Code: 'std:0154',
+ },
+ {
+ State: 'Sikkim',
+ City: 'Gangtok',
+ Code: 'std:03592',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Thanjavur',
+ Code: 'std:04362',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Nagercoil',
+ Code: 'std:04652',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Villupuram',
+ Code: 'std:04146',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Coimbatore',
+ Code: 'std:0422',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Erode',
+ Code: 'std:0424',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Madurai',
+ Code: 'std:0452',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Vellore',
+ Code: 'std:0416',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Chennai',
+ Code: 'std:044',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Tiruppur',
+ Code: 'std:0421',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Salem',
+ Code: 'std:0427',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Ooty',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Kumbakonam',
+ Code: 'std:0435',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Dindigul',
+ Code: 'std:0451',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Namakkal',
+ Code: 'std:04286',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Theni Allinagaram',
+ Code: 'std:04546',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Cuddalore',
+ Code: 'std:04142',
+ Column4: 'Direct code for Lakhimpur not available; sub-divisions mentioned with codes',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Karur',
+ Code: 'std:04324',
+ Column4: 'Direct code for Bahraich not available; sub-divisions mentioned with codes',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Rajapalayam',
+ Code: 'std:04563',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'KK Nagar',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Sivakasi',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Pollachi',
+ Code: 'std:04259',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Tiruvannamalai',
+ Code: 'std:04175',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Sivaganga',
+ Code: 'std:04575',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Kotagiri',
+ Code: 'std:04266',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Trichy',
+ Code: 'std:0431',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Ambur',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Vaniyambadi',
+ Code: 'std:04174',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Tirupur',
+ Code: 'std:0421',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Gandhipuram',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Coonoor',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Tirunelveli',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Kovilpatti',
+ Code: 'std:04632',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Ramanathapuram',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Melvisharam',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Hosur',
+ Code: 'std:04344',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Thoothukudi',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Iyyengarkulam',
+ Code: 'std:04112',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Manavalanagar',
+ Code: 'std:04116',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Tindivanam',
+ Code: 'std:04147',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Mettupalayam',
+ Code: 'std:04254',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Mayiladuthura',
+ Code: 'std:04364',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Ramanathpuram',
+ Code: 'std:04567',
+ },
+ {
+ State: 'Tamil Nadu',
+ City: 'Tirunelvelli',
+ Code: 'std:0462',
+ },
+ {
+ State: 'Telangana',
+ City: 'Hyderabad',
+ Code: 'std:040',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Varanasi',
+ Code: 'std:0542',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Prayagraj',
+ Code: 'std:0532',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Ghaziabad',
+ Code: 'std:0120',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Agra',
+ Code: 'std:0562',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Bareilly',
+ Code: 'std:0581',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Gorakhpur',
+ Code: 'std:0551',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Basti',
+ Code: 'std:05542',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Kanpur',
+ Code: 'std:0512',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Balrampur',
+ Code: 'std:05263',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Khalilabad',
+ Code: 'std:05547',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Noida',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Meerut',
+ Code: 'std:0121',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Hardoi',
+ Code: 'std:05852',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Shahjahanpur',
+ Code: 'std:05842',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Sitapur',
+ Code: 'std:05862',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Aligarh',
+ Code: 'std:0571',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Barabanki',
+ Code: 'std:05248',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'lakhimpur kheri',
+ Code: 'std:05872',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Farrukhabad',
+ Code: 'std:05692',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Lucknow',
+ Code: 'std:0522',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Sultanpur',
+ Code: 'std:05362',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Lakhimpur',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Bahraich',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Greater Noida',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Akbarpur',
+ Code: 'std:05271',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Saharanpur',
+ Code: 'std:0132',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Muzaffarnagar',
+ Code: 'std:0131',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Bijnore',
+ Code: 'std:01342',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Kotdwara',
+ Code: 'std:01382',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Hamirpur',
+ Code: 'std:05282',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Moradabad',
+ Code: 'std:0591',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Bilari',
+ Code: 'std:05921',
+ },
+ {
+ State: 'Uttar Pradesh',
+ City: 'Chhapra',
+ Code: 'std:06152',
+ },
+ {
+ State: 'Uttarakhand',
+ City: 'Haridwar',
+ Code: 'std:01334',
+ },
+ {
+ State: 'Uttarakhand',
+ City: 'Dehradun',
+ Code: 'std:0135',
+ },
+ {
+ State: 'West Bengal',
+ City: 'Kolkata',
+ Code: 'std:033',
+ },
+];
diff --git a/utilities/ondc-igm-sdk/src/shared/contents.ts b/utilities/ondc-igm-sdk/src/shared/contents.ts
new file mode 100644
index 0000000..eac37b4
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/shared/contents.ts
@@ -0,0 +1,58 @@
+export const SYSTEM_ROLES = {
+ SUPER_ADMIN: 'SUPER_ADMIN',
+};
+
+export const RESOURCE_POSSESSION = {
+ OWN: 'OWN',
+ ANY: 'ANY',
+ SUB: 'SUB',
+};
+
+export const HEADERS = {
+ ACCESS_TOKEN: 'access-token',
+ AUTH_TOKEN: 'Authorization',
+};
+
+export const PAYMENT_TYPES = {
+ 'ON-ORDER': 'ON-ORDER',
+ 'PRE-FULFILLMENT': 'PRE-FULFILLMENT',
+ 'ON-FULFILLMENT': 'ON-FULFILLMENT',
+ 'POST-FULFILLMENT': 'POST-FULFILLMENT',
+};
+
+export const PROTOCOL_CONTEXT = {
+ ISSUE: 'issue',
+ ON_ISSUE: 'on_issue',
+ ISSUE_STATUS: 'issue_status',
+ ON_ISSUE_STATUS: 'on_issue_status',
+};
+
+export const PROTOCOL_PAYMENT = {
+ PAID: 'PAID',
+ 'NOT-PAID': 'NOT-PAID',
+};
+
+export const PROTOCOL_VERSION = {
+ v_0_9_1: '0.9.1',
+ v_0_9_3: '0.9.3',
+ v_1_0_0: '1.0.0',
+};
+
+export const SUBSCRIBER_TYPE = {
+ BAP: 'BAP',
+ BPP: 'BPP',
+ BG: 'BG',
+ LREG: 'LREG',
+ CREG: 'CREG',
+ RREG: 'RREG',
+};
+
+export const ORDER_STATUS = {
+ COMPLETED: 'completed',
+ 'IN-PROGRESS': 'in-progress',
+};
+
+export const PAYMENT_COLLECTED_BY = {
+ BAP: 'BAP',
+ BPP: 'BPP',
+};
diff --git a/utilities/ondc-igm-sdk/src/utils/commonFunction.ts b/utilities/ondc-igm-sdk/src/utils/commonFunction.ts
new file mode 100644
index 0000000..92bf3e3
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/commonFunction.ts
@@ -0,0 +1,7 @@
+export function hasResolvedAction(respondentActions: any[]): boolean {
+ return respondentActions.some((action) => action.respondent_action === 'RESOLVED');
+}
+
+export function hasRefundKey(payload: any) {
+ return Object.keys(payload.message.issue.resolution).includes('refund_amount');
+}
diff --git a/utilities/ondc-igm-sdk/src/utils/httpRequest.ts b/utilities/ondc-igm-sdk/src/utils/httpRequest.ts
new file mode 100644
index 0000000..6fb792a
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/httpRequest.ts
@@ -0,0 +1,92 @@
+import axios from 'axios';
+
+/**
+ * Used to communicate with server
+ */
+
+class HttpRequest {
+ baseUrl: string | any;
+ url: string;
+ method: string;
+ data: any;
+ headers: any;
+ options: any;
+ /**
+ * @param {*} baseUrl Base URL(domain url)
+ * @param {*} url Resource URL
+ * @param {*} method HTTP method(GET | POST | PUT | PATCH | DELETE)
+ * @param {*} headers HTTP request headers (If applicable)
+ * @param {*} data HTTP request data (If applicable)
+ * @param {*} options other params
+ */
+ constructor(
+ baseUrl: string | any,
+ url: string,
+ method: string = 'get',
+ data: any = {},
+ headers?: any,
+ options?: any,
+ ) {
+ this.baseUrl = baseUrl;
+ this.url = url;
+ this.method = method;
+ this.data = data;
+ this.headers = headers;
+ this.options = options;
+ this.send = this.send.bind(this);
+ }
+
+ /**
+ * Send http request to server to write data to / read data from server
+ * axios library provides promise implementation to send request to server
+ * Here we are using axios library for requesting a resource
+ */
+ async send() {
+ try {
+ let headers = {
+ ...this.headers,
+ ...(this.method.toLowerCase() != 'get' && {
+ 'Content-Type': 'application/json',
+ }),
+ };
+
+ let result;
+
+ if (this.method.toLowerCase() == 'get') {
+ result = await axios({
+ baseURL: this.baseUrl,
+ url: this.url,
+ method: this.method,
+ headers: headers,
+ timeout: 180000, // If the request takes longer than `timeout`, the request will be aborted.
+ });
+ } else {
+ // Make server request using axios
+ result = await axios({
+ baseURL: this.baseUrl,
+ url: this.url,
+ method: this.method,
+ headers: headers,
+ timeout: 180000, // If the request takes longer than `timeout`, the request will be aborted.
+ data: JSON.stringify(this.data),
+ });
+ }
+ return result;
+ } catch (err: any) {
+ if (err.response) {
+ // The client was given an error response (5xx, 4xx)
+ console.info('Error response', err, '\n', err.response);
+ } else if (err.request) {
+ // The client never received a response, and the request was never left
+ console.info('Error request', err, '\n', err.request);
+ } else {
+ // Anything else
+ console.info('Error message', err, '\n', err.message);
+ }
+
+ throw err;
+ }
+ }
+}
+
+export default HttpRequest;
diff --git a/utilities/ondc-igm-sdk/src/utils/posApi.ts b/utilities/ondc-igm-sdk/src/utils/posApi.ts
new file mode 100644
index 0000000..77e7a50
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/posApi.ts
@@ -0,0 +1,32 @@
+import HttpRequest from './httpRequest';
+
+/**
+ * Performs an HTTP POST or GET request to the specified endpoint with the provided data.
+ *
+ * @param data - The data to be sent in the request body for POST requests.
+ * @param endpoint - The endpoint URL where the request will be sent.
+ * @param method - The HTTP method to be used for the request (either 'POST' or 'GET').
+ * @returns A promise that resolves to the response data from the API call.
+ * @throws Will throw an error if the API call encounters an error.
+ */
+
+const postApi = async ({
+ baseUrl,
+ data,
+ endpoint,
+ method,
+}: {
+ baseUrl: string;
+ endpoint: string;
+ data: T;
+ method: 'POST' | 'GET';
+}) => {
+ const apiCall = new HttpRequest(baseUrl, endpoint, method, {
+ ...data,
+ });
+
+ const response = apiCall.send();
+ return response;
+};
+
+export default postApi;
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/OnIssueStatus.schema.ts b/utilities/ondc-igm-sdk/src/utils/schema/OnIssueStatus.schema.ts
new file mode 100644
index 0000000..3cf0a07
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/OnIssueStatus.schema.ts
@@ -0,0 +1,170 @@
+import Joi from 'joi';
+
+const customMessages = {
+ 'string.base': '{#label} should be a type of text',
+ 'string.empty': '{#label} cannot be empty',
+ 'any.required': '{#label} is required',
+ 'string.email': '{#label} must be a valid email',
+ 'string.phone': '{#label} must be a valid phone number',
+ 'string.uri': '{#label} must be a valid URI',
+ 'string.isoDate': '{#label} must be a valid ISO date',
+ 'number.base': '{#label} should be a type of number',
+ 'number.integer': '{#label} must be an integer',
+};
+
+export const OnIssueStatusScehma = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required().messages(customMessages),
+ country: Joi.string().required().messages(customMessages),
+ city: Joi.string().required().messages(customMessages),
+ action: Joi.string().required().messages(customMessages),
+ core_version: Joi.string().required().messages(customMessages),
+ bap_id: Joi.string().required().messages(customMessages),
+ bap_uri: Joi.string().required().messages(customMessages),
+ bpp_id: Joi.string().required().messages(customMessages),
+ bpp_uri: Joi.string().required().messages(customMessages),
+ transaction_id: Joi.string().required().messages(customMessages),
+ message_id: Joi.string().required().messages(customMessages),
+ timestamp: Joi.string().isoDate().required().messages(customMessages),
+ }).required(),
+
+ message: Joi.object({
+ issue: Joi.object({
+ id: Joi.string().required().messages(customMessages),
+ issue_actions: Joi.object({
+ respondent_actions: Joi.array()
+ .items(
+ Joi.object({
+ respondent_action: Joi.string().required().messages(customMessages),
+ short_desc: Joi.string().required().messages(customMessages),
+ updated_at: Joi.string().isoDate().required().messages(customMessages),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string()
+ .pattern(/^[0-9]{10}$/)
+ .required()
+ .messages(customMessages),
+ email: Joi.string().email().required().messages(customMessages),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ }).required(),
+ cascaded_level: Joi.number().integer().required().messages(customMessages),
+ }),
+ )
+ .required(),
+ }).required(),
+ created_at: Joi.string().isoDate().required().messages(customMessages),
+ updated_at: Joi.string().isoDate().required().messages(customMessages),
+ }).required(),
+ }).required(),
+});
+
+export const OnIssueStatusResolovedSchema = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required().messages(customMessages),
+ country: Joi.string().required().messages(customMessages),
+ city: Joi.string().required().messages(customMessages),
+ action: Joi.string().required().messages(customMessages),
+ core_version: Joi.string().required().messages(customMessages),
+ bap_id: Joi.string().required().messages(customMessages),
+ bap_uri: Joi.string().required().messages(customMessages),
+ bpp_id: Joi.string().required().messages(customMessages),
+ bpp_uri: Joi.string().required().messages(customMessages),
+ transaction_id: Joi.string().required().messages(customMessages),
+ message_id: Joi.string().required().messages(customMessages),
+ timestamp: Joi.string().isoDate().required().messages(customMessages),
+ }).required(),
+
+ message: Joi.object({
+ issue: Joi.object({
+ id: Joi.string().required().messages(customMessages),
+ issue_actions: Joi.object({
+ respondent_actions: Joi.array()
+ .items(
+ Joi.object({
+ respondent_action: Joi.string().required().messages(customMessages),
+ short_desc: Joi.string().required().messages(customMessages),
+ updated_at: Joi.string().isoDate().required().messages(customMessages),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string()
+ .pattern(/^[0-9]{10}$/)
+ .required()
+ .messages(customMessages),
+ email: Joi.string().email().required().messages(customMessages),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ }).required(),
+ cascaded_level: Joi.number().integer().required().messages(customMessages),
+ }),
+ )
+ .required(),
+ }).required(),
+ created_at: Joi.string().isoDate().required().messages(customMessages),
+ updated_at: Joi.string().isoDate().required().messages(customMessages),
+ resolution_provider: Joi.object({
+ respondent_info: Joi.object({
+ type: Joi.string().required().messages(customMessages),
+ organization: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string()
+ .pattern(/^[0-9]{10}$/)
+ .required()
+ .messages(customMessages),
+ email: Joi.string().email().required().messages(customMessages),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ }).required(),
+ resolution_support: Joi.object({
+ chat_link: Joi.string().required().messages(customMessages),
+ contact: Joi.object({
+ phone: Joi.string()
+ .pattern(/^[0-9]{10}$/)
+ .required()
+ .messages(customMessages),
+ email: Joi.string().email().required().messages(customMessages),
+ }).required(),
+ gros: Joi.array()
+ .items(
+ Joi.object({
+ person: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string()
+ .pattern(/^[0-9]{10}$/)
+ .required()
+ .messages(customMessages),
+ email: Joi.string().email().required().messages(customMessages),
+ }).required(),
+ gro_type: Joi.string().required().messages(customMessages),
+ }),
+ )
+ .required(),
+ }).required(),
+ }).required(),
+ }).required(),
+ resolution: Joi.object({
+ short_desc: Joi.string().required().messages(customMessages),
+ long_desc: Joi.string().required().messages(customMessages),
+ action_triggered: Joi.string().required().messages(customMessages),
+ refund_amount: Joi.number().required().messages(customMessages),
+ }).required(),
+ }).required(),
+ }).required(),
+});
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/index.ts b/utilities/ondc-igm-sdk/src/utils/schema/index.ts
new file mode 100644
index 0000000..852465e
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/index.ts
@@ -0,0 +1,16 @@
+import IssueSchema from './issue.schema';
+import OnIssueSchema from './on_issue.schema';
+import { OnIssueStatusScehma, OnIssueStatusResolovedSchema } from './OnIssueStatus.schema';
+import IssueStatusSchema from './issueStatus.scehma';
+import LogisticOnIssueSchema from './logistics.onIssue.schema';
+import LogisticsIssueSchema from './logistics.issue.schema'
+
+export {
+ IssueSchema,
+ OnIssueSchema,
+ OnIssueStatusScehma,
+ OnIssueStatusResolovedSchema,
+ IssueStatusSchema,
+ LogisticOnIssueSchema,
+ LogisticsIssueSchema
+};
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/issue.schema.ts b/utilities/ondc-igm-sdk/src/utils/schema/issue.schema.ts
new file mode 100644
index 0000000..47f23d1
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/issue.schema.ts
@@ -0,0 +1,263 @@
+import Joi from 'joi';
+
+const issueSchema = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required().messages({
+ 'string.base': 'Context: Domain must be a string.',
+ 'any.required': 'Context: Domain is required.',
+ }),
+ country: Joi.string().required().messages({
+ 'string.base': 'Context: Country must be a string.',
+ 'any.required': 'Context: Country is required.',
+ }),
+ city: Joi.string().required().messages({
+ 'string.base': 'Context: City must be a string.',
+ 'any.required': 'Context: City is required.',
+ }),
+ action: Joi.string().required().messages({
+ 'string.base': 'Context: Action must be a string.',
+ 'any.required': 'Context: Action is required.',
+ }),
+ core_version: Joi.string().required().messages({
+ 'string.base': 'Context: Core version must be a string.',
+ 'any.required': 'Context: Core version is required.',
+ }),
+ bap_id: Joi.string().required().messages({
+ 'string.base': 'Context: Bap ID must be a string.',
+ 'any.required': 'Context: Bap ID is required.',
+ }),
+ bap_uri: Joi.string().required().messages({
+ 'string.base': 'Context: Bap URI must be a string.',
+ 'string.uri': 'Context: Bap URI must be a valid URI.',
+ 'any.required': 'Context: Bap URI is required.',
+ }),
+ bpp_id: Joi.string().required().messages({
+ 'string.base': 'Context: Bpp ID must be a string.',
+ 'any.required': 'Context: Bpp ID is required.',
+ }),
+ bpp_uri: Joi.string().required().messages({
+ 'string.base': 'Context: Bpp URI must be a string.',
+ 'string.uri': 'Context: Bpp URI must be a valid URI.',
+ 'any.required': 'Context: Bpp URI is required.',
+ }),
+ transaction_id: Joi.string().required().messages({
+ 'string.base': 'Context: Transaction ID must be a string.',
+ 'any.required': 'Context: Transaction ID is required.',
+ }),
+ message_id: Joi.string().required().messages({
+ 'string.base': 'Context: Message ID must be a string.',
+ 'any.required': 'Context: Message ID is required.',
+ }),
+ timestamp: Joi.string().isoDate().required().messages({
+ 'string.base': 'Context: Timestamp must be a valid ISO date string.',
+ 'string.isoDate': 'Context: Timestamp must be a valid ISO date string.',
+ 'any.required': 'Context: Timestamp is required.',
+ }),
+ ttl: Joi.string().required().messages({
+ 'string.base': 'Context: TTL must be a string.',
+ 'any.required': 'Context: TTL is required.',
+ }),
+ })
+ .required()
+ .messages({
+ 'any.required': 'Context is required.',
+ }),
+
+ message: Joi.object({
+ issue: Joi.object({
+ id: Joi.string().required().messages({
+ 'string.base': 'Issue: ID must be a string.',
+ 'any.required': 'Issue: ID is required.',
+ }),
+ category: Joi.string().required().messages({
+ 'string.base': 'Issue: Category must be a string.',
+ 'any.required': 'Issue: Category is required.',
+ }),
+ sub_category: Joi.string().required().messages({
+ 'string.base': 'Issue: Sub-category must be a string.',
+ 'any.required': 'Issue: Sub-category is required.',
+ }),
+ complainant_info: Joi.object({
+ person: Joi.object({
+ name: Joi.string().required().messages({
+ 'string.base': 'Complainant: Name must be a string.',
+ 'any.required': 'Complainant: Name is required.',
+ }),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required().messages({
+ 'string.base': 'Complainant: Phone must be a string.',
+ 'any.required': 'Complainant: Phone is required.',
+ }),
+ email: Joi.string().email().required().messages({
+ 'string.base': 'Complainant: Email must be a string.',
+ 'string.email': 'Complainant: Email must be a valid email address.',
+ 'any.required': 'Complainant: Email is required.',
+ }),
+ }).required(),
+ }).required(),
+ order_details: Joi.object({
+ id: Joi.string().uuid().required().messages({
+ 'string.base': 'Order Details: ID must be a string.',
+ 'string.uuid': 'Order Details: ID must be a valid UUID.',
+ 'any.required': 'Order Details: ID is required.',
+ }),
+ state: Joi.string().required().messages({
+ 'string.base': 'Order Details: State must be a string.',
+ 'any.required': 'Order Details: State is required.',
+ }),
+ items: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.string().required().messages({
+ 'string.base': 'Order Item: ID must be a string.',
+ 'any.required': 'Order Item: ID is required.',
+ }),
+ quantity: Joi.number().integer().required().messages({
+ 'number.base': 'Order Item: Quantity must be a number.',
+ 'number.integer': 'Order Item: Quantity must be an integer.',
+ 'any.required': 'Order Item: Quantity is required.',
+ }),
+ }),
+ )
+ .required(),
+ fulfillments: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.string().required().messages({
+ 'string.base': 'Fulfillment: ID must be a string.',
+ 'any.required': 'Fulfillment: ID is required.',
+ }),
+ state: Joi.string().required().messages({
+ 'string.base': 'Fulfillment: State must be a string.',
+ 'any.required': 'Fulfillment: State is required.',
+ }),
+ }),
+ )
+ .required(),
+ provider_id: Joi.string().required().messages({
+ 'string.base': 'Order Details: Provider ID must be a string.',
+ 'any.required': 'Order Details: Provider ID is required.',
+ }),
+ }).required(),
+ description: Joi.object({
+ short_desc: Joi.string().required().messages({
+ 'string.base': 'Description: Short description must be a string.',
+ 'any.required': 'Description: Short description is required.',
+ }),
+ long_desc: Joi.string().required().messages({
+ 'string.base': 'Description: Long description must be a string.',
+ 'any.required': 'Description: Long description is required.',
+ }),
+ additional_desc: Joi.object({
+ url: Joi.string().required().messages({
+ 'string.base': 'Additional Description: URL must be a string.',
+ 'string.uri': 'Additional Description: URL must be a valid URI.',
+ 'any.required': 'Additional Description: URL is required.',
+ }),
+ content_type: Joi.string().required().messages({
+ 'string.base': 'Additional Description: Content type must be a string.',
+ 'any.required': 'Additional Description: Content type is required.',
+ }),
+ }).required(),
+ images: Joi.array().items(Joi.string()).required().messages({
+ 'array.base': 'Images must be an array.',
+ 'any.required': 'Images is required.',
+ 'string.uri': 'Each image must be a valid URI.',
+ }),
+ }).required(),
+ source: Joi.object({
+ network_participant_id: Joi.string().required().messages({
+ 'string.base': 'Source: Network participant ID must be a string.',
+ 'any.required': 'Source: Network participant ID is required.',
+ }),
+ type: Joi.string().required().messages({
+ 'string.base': 'Source: Type must be a string.',
+ 'any.required': 'Source: Type is required.',
+ }),
+ }).required(),
+ expected_response_time: Joi.object({
+ duration: Joi.string().required().messages({
+ 'string.base': 'Expected Response Time: Duration must be a string.',
+ 'any.required': 'Expected Response Time: Duration is required.',
+ }),
+ }).required(),
+ expected_resolution_time: Joi.object({
+ duration: Joi.string().required().messages({
+ 'string.base': 'Expected Resolution Time: Duration must be a string.',
+ 'any.required': 'Expected Resolution Time: Duration is required.',
+ }),
+ }).required(),
+ status: Joi.string().required().messages({
+ 'string.base': 'Status must be a string.',
+ 'any.required': 'Status is required.',
+ }),
+ issue_type: Joi.string().required().messages({
+ 'string.base': 'Issue Type must be a string.',
+ 'any.required': 'Issue Type is required.',
+ }),
+ issue_actions: Joi.object({
+ complainant_actions: Joi.array()
+ .items(
+ Joi.object({
+ complainant_action: Joi.string().required().messages({
+ 'string.base': 'Complainant Action must be a string.',
+ 'any.required': 'Complainant Action is required.',
+ }),
+ short_desc: Joi.string().required().messages({
+ 'string.base': 'Complainant Action: Short description must be a string.',
+ 'any.required': 'Complainant Action: Short description is required.',
+ }),
+ updated_at: Joi.string().isoDate().required().messages({
+ 'string.base': 'Complainant Action: Updated at must be a valid ISO date string.',
+ 'string.isoDate': 'Complainant Action: Updated at must be a valid ISO date string.',
+ 'any.required': 'Complainant Action: Updated at is required.',
+ }),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required().messages({
+ 'string.base': 'Updated By: Organization name must be a string.',
+ 'any.required': 'Updated By: Organization name is required.',
+ }),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required().messages({
+ 'string.base': 'Updated By: Contact phone must be a string.',
+ 'any.required': 'Updated By: Contact phone is required.',
+ }),
+ email: Joi.string().email().required().messages({
+ 'string.base': 'Updated By: Contact email must be a string.',
+ 'string.email': 'Updated By: Contact email must be a valid email address.',
+ 'any.required': 'Updated By: Contact email is required.',
+ }),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required().messages({
+ 'string.base': 'Updated By: Person name must be a string.',
+ 'any.required': 'Updated By: Person name is required.',
+ }),
+ }).required(),
+ }).required(),
+ }),
+ )
+ .required(),
+ }).required(),
+ created_at: Joi.string().isoDate().required().messages({
+ 'string.base': 'Created at must be a valid ISO date string.',
+ 'string.isoDate': 'Created at must be a valid ISO date string.',
+ 'any.required': 'Created at is required.',
+ }),
+ updated_at: Joi.string().isoDate().required().messages({
+ 'string.base': 'Updated at must be a valid ISO date string.',
+ 'string.isoDate': 'Updated at must be a valid ISO date string.',
+ 'any.required': 'Updated at is required.',
+ }),
+ }).required(),
+ })
+ .required()
+ .messages({
+ 'any.required': 'Message is required.',
+ }),
+});
+
+export default issueSchema;
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/issueStatus.scehma.ts b/utilities/ondc-igm-sdk/src/utils/schema/issueStatus.scehma.ts
new file mode 100644
index 0000000..26e29d4
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/issueStatus.scehma.ts
@@ -0,0 +1,63 @@
+import Joi from 'joi';
+
+const IssueStatusSchema = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required().messages({
+ 'any.required': 'Domain is required',
+ }),
+ country: Joi.string().required().messages({
+ 'any.required': 'Country is required',
+ }),
+ city: Joi.string().required().messages({
+ 'any.required': 'City is required',
+ }),
+ action: Joi.string().required().messages({
+ 'any.required': 'Action is required',
+ }),
+ core_version: Joi.string().required().messages({
+ 'any.required': 'Core version is required',
+ }),
+ bap_id: Joi.string().required().messages({
+ 'any.required': 'BAP ID is required',
+ }),
+ bap_uri: Joi.string().required().messages({
+ 'any.required': 'BAP URI is required',
+ 'string.uri': 'BAP URI must be a valid URI',
+ }),
+ bpp_id: Joi.string().required().messages({
+ 'any.required': 'BPP ID is required',
+ }),
+ bpp_uri: Joi.string().required().messages({
+ 'any.required': 'BPP URI is required',
+ 'string.uri': 'BPP URI must be a valid URI',
+ }),
+ transaction_id: Joi.string().required().messages({
+ 'any.required': 'Transaction ID is required',
+ }),
+ message_id: Joi.string().required().messages({
+ 'any.required': 'Message ID is required',
+ }),
+ timestamp: Joi.string().isoDate().required().messages({
+ 'any.required': 'Timestamp is required',
+ 'string.isoDate': 'Timestamp must be a valid ISO date',
+ }),
+ ttl: Joi.string().required().messages({
+ 'any.required': 'TTL is required',
+ }),
+ })
+ .required()
+ .messages({
+ 'any.required': 'Context is required',
+ }),
+ message: Joi.object({
+ issue_id: Joi.string().required().messages({
+ 'any.required': 'Issue ID is required',
+ }),
+ })
+ .required()
+ .messages({
+ 'any.required': 'Message is required',
+ }),
+});
+
+export default IssueStatusSchema;
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/logistics.issue.schema.ts b/utilities/ondc-igm-sdk/src/utils/schema/logistics.issue.schema.ts
new file mode 100644
index 0000000..a819070
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/logistics.issue.schema.ts
@@ -0,0 +1,137 @@
+import Joi from 'joi';
+
+const LogisticsIssueSchema = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required(),
+ country: Joi.string().required(),
+ city: Joi.string().required(),
+ action: Joi.string().required(),
+ core_version: Joi.string().required(),
+ bap_id: Joi.string().required(),
+ bap_uri: Joi.string().required(),
+ bpp_id: Joi.string().required(),
+ bpp_uri: Joi.string().required(),
+ transaction_id: Joi.string().required(),
+ message_id: Joi.string().required(),
+ timestamp: Joi.string().isoDate().required(),
+ ttl: Joi.string().required(),
+ }).required(),
+
+ message: Joi.object({
+ issue: Joi.object({
+ id: Joi.string().required(),
+ category: Joi.string().required(),
+ sub_category: Joi.string().required(),
+ complainant_info: Joi.object({
+ person: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required(),
+ email: Joi.string().email().required(),
+ }).required(),
+ }).required(),
+
+ order_details: Joi.object({
+ id: Joi.string().uuid().required(),
+ state: Joi.string().required(),
+ items: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.string().required(),
+ quantity: Joi.number().required(),
+ }),
+ )
+ .required(),
+ fulfillments: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.string().required(),
+ state: Joi.string().required(),
+ }),
+ )
+ .required(),
+ provider_id: Joi.string().required(),
+ merchant_order_id: Joi.string(),
+ }).required(),
+
+ description: Joi.object({
+ short_desc: Joi.string().required(),
+ long_desc: Joi.string().required(),
+ additional_desc: Joi.object({
+ url: Joi.string().required(),
+ content_type: Joi.string().required(),
+ }).required(),
+ images: Joi.array().items(Joi.string()).required(),
+ }).required(),
+
+ source: Joi.object({
+ network_participant_id: Joi.string().required(),
+ type: Joi.string().required(),
+ }).required(),
+
+ expected_response_time: Joi.object({
+ duration: Joi.string().required(),
+ }).required(),
+
+ expected_resolution_time: Joi.object({
+ duration: Joi.string().required(),
+ }).required(),
+
+ status: Joi.string().required(),
+ issue_type: Joi.string().required(),
+
+ issue_actions: Joi.object({
+ complainant_actions: Joi.array()
+ .items(
+ Joi.object({
+ complainant_action: Joi.string().required(),
+ short_desc: Joi.string().required(),
+ updated_at: Joi.string().isoDate().required(),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required(),
+ email: Joi.string().email().required(),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ }).required(),
+ }),
+ )
+ .required(),
+
+ respondent_actions: Joi.array()
+ .items(
+ Joi.object({
+ respondent_action: Joi.string().required(),
+ short_desc: Joi.string().required(),
+ updated_at: Joi.string().isoDate().required(),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required(),
+ email: Joi.string().email().required(),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ }).required(),
+ cascaded_level: Joi.number().required(),
+ }),
+ )
+ .required(),
+ }).required(),
+
+ created_at: Joi.string().isoDate().required(),
+ updated_at: Joi.string().isoDate().required(),
+ }).required(),
+ }).required(),
+});
+
+export default LogisticsIssueSchema;
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/logistics.onIssue.schema.ts b/utilities/ondc-igm-sdk/src/utils/schema/logistics.onIssue.schema.ts
new file mode 100644
index 0000000..f422c53
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/logistics.onIssue.schema.ts
@@ -0,0 +1,137 @@
+import Joi from 'joi';
+
+const LogisticOnIssueSchema = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required(),
+ country: Joi.string().required(),
+ city: Joi.string().required(),
+ action: Joi.string().required(),
+ core_version: Joi.string().required(),
+ bap_id: Joi.string().required(),
+ bap_uri: Joi.string().required(),
+ bpp_id: Joi.string().required(),
+ bpp_uri: Joi.string().required(),
+ transaction_id: Joi.string().required(),
+ message_id: Joi.string().required(),
+ timestamp: Joi.string().isoDate().required(),
+ }).required(),
+
+ message: Joi.object({
+ issue: Joi.object({
+ id: Joi.string().required(),
+ category: Joi.string().required(),
+ sub_category: Joi.string().required(),
+ complainant_info: Joi.object({
+ person: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required(),
+ email: Joi.string().email().required(),
+ }).required(),
+ }).required(),
+ order_details: Joi.object({
+ id: Joi.string().uuid().required(),
+ state: Joi.string().required(),
+ items: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.string().required(),
+ quantity: Joi.number().integer().required(),
+ }),
+ )
+ .required(),
+ fulfillments: Joi.array()
+ .items(
+ Joi.object({
+ id: Joi.string().required(),
+ state: Joi.string().required(),
+ }),
+ )
+ .required(),
+ provider_id: Joi.string().required(),
+ merchant_order_id: Joi.string(),
+ }).required(),
+ description: Joi.object({
+ short_desc: Joi.string().required(),
+ long_desc: Joi.string().required(),
+ additional_desc: Joi.object({
+ url: Joi.string().uri().required(),
+ content_type: Joi.string().required(),
+ }).required(),
+ images: Joi.array().items(Joi.string().uri()).required(),
+ }).required(),
+ source: Joi.object({
+ network_participant_id: Joi.string().required(),
+ type: Joi.string().required(),
+ }).required(),
+ expected_response_time: Joi.object({
+ duration: Joi.string()
+ .regex(/^PT\d+[HS]$/)
+ .required()
+ .messages({
+ 'string.pattern.base': 'Expected response time must be in ISO 8601 duration format (e.g., PT2H)',
+ }),
+ }).required(),
+ expected_resolution_time: Joi.object({
+ duration: Joi.string()
+ .regex(/^P\d+D$/)
+ .required()
+ .messages({
+ 'string.pattern.base': 'Expected resolution time must be in ISO 8601 duration format (e.g., P1D)',
+ }),
+ }).required(),
+ status: Joi.string().required(),
+ issue_type: Joi.string().required(),
+ issue_actions: Joi.object({
+ complainant_actions: Joi.array()
+ .items(
+ Joi.object({
+ complainant_action: Joi.string().required(),
+ short_desc: Joi.string().required(),
+ updated_at: Joi.string().isoDate().required(),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required(),
+ email: Joi.string().email().required(),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ }).required(),
+ }),
+ )
+ .required(),
+ respondent_actions: Joi.array()
+ .items(
+ Joi.object({
+ respondent_action: Joi.string().required(),
+ short_desc: Joi.string().required(),
+ updated_at: Joi.string().isoDate().required(),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ contact: Joi.object({
+ phone: Joi.string().required(),
+ email: Joi.string().email().required(),
+ }).required(),
+ person: Joi.object({
+ name: Joi.string().required(),
+ }).required(),
+ }).required(),
+ cascaded_level: Joi.number().integer().required(),
+ }),
+ )
+ .required(),
+ }).required(),
+ created_at: Joi.string().isoDate().required(),
+ updated_at: Joi.string().isoDate().required(),
+ }).required(),
+ }).required(),
+});
+
+export default LogisticOnIssueSchema;
diff --git a/utilities/ondc-igm-sdk/src/utils/schema/on_issue.schema.ts b/utilities/ondc-igm-sdk/src/utils/schema/on_issue.schema.ts
new file mode 100644
index 0000000..84885c2
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/schema/on_issue.schema.ts
@@ -0,0 +1,72 @@
+import Joi from 'joi';
+
+const customMessages = {
+ 'string.base': '{{#label}} must be a string',
+ 'string.uri': '{{#label}} must be a valid URI',
+ 'string.isoDate': '{{#label}} must be a valid ISO date',
+ 'string.email': '{{#label}} must be a valid email address',
+ 'number.base': '{{#label}} must be a number',
+ 'array.base': '{{#label}} must be an array',
+};
+
+const OnIssueSchema = Joi.object({
+ context: Joi.object({
+ domain: Joi.string().required().messages(customMessages),
+ country: Joi.string().required().messages(customMessages),
+ city: Joi.string().required().messages(customMessages),
+ action: Joi.string().required().messages(customMessages),
+ core_version: Joi.string().required().messages(customMessages),
+ bap_id: Joi.string().required().messages(customMessages),
+ bap_uri: Joi.string().required().messages(customMessages),
+ bpp_id: Joi.string().required().messages(customMessages),
+ bpp_uri: Joi.string().required().messages(customMessages),
+ transaction_id: Joi.string().required().messages(customMessages),
+ message_id: Joi.string().required().messages(customMessages),
+ timestamp: Joi.string().isoDate().required().messages(customMessages),
+ }).required(),
+ message: Joi.object({
+ issue: Joi.object({
+ id: Joi.string().required().messages(customMessages),
+ issue_actions: Joi.object({
+ respondent_actions: Joi.array().items(
+ Joi.object({
+ respondent_action: Joi.string().required().messages(customMessages),
+ short_desc: Joi.string().required().messages(customMessages),
+ updated_at: Joi.string().isoDate().required().messages(customMessages),
+ updated_by: Joi.object({
+ org: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ })
+ .required()
+ .messages(customMessages),
+ contact: Joi.object({
+ phone: Joi.string().required().messages(customMessages),
+ email: Joi.string().email().required().messages(customMessages),
+ })
+ .required()
+ .messages(customMessages),
+ person: Joi.object({
+ name: Joi.string().required().messages(customMessages),
+ })
+ .required()
+ .messages(customMessages),
+ })
+ .required()
+ .messages(customMessages),
+ cascaded_level: Joi.number().required().messages(customMessages),
+ }),
+ ),
+ })
+ .required()
+ .messages(customMessages),
+ created_at: Joi.string().isoDate().required().messages(customMessages),
+ updated_at: Joi.string().isoDate().required().messages(customMessages),
+ })
+ .required()
+ .messages(customMessages),
+ })
+ .required()
+ .messages(customMessages),
+});
+
+export default OnIssueSchema;
diff --git a/utilities/ondc-igm-sdk/src/utils/validator.schema.ts b/utilities/ondc-igm-sdk/src/utils/validator.schema.ts
new file mode 100644
index 0000000..006eb75
--- /dev/null
+++ b/utilities/ondc-igm-sdk/src/utils/validator.schema.ts
@@ -0,0 +1,16 @@
+import { Schema, ValidationError } from 'joi';
+
+/**
+ * Performs schema validation on the provided data using the given schema definition.
+ *
+ * @param schema - The schema definition used for validation.
+ * @param data - The data to be validated against the schema.
+ * @returns A `ValidationError` object if the data fails validation, or `undefined` if the data is valid.
+ * @template T - The type of data to be validated.
+ */
+
+export function SchemaValidator({ schema, data }: { schema: Schema; data: T }): ValidationError | undefined {
+ const { error } = schema.validate(data, { abortEarly: false });
+
+ return error;
+}
diff --git a/utilities/ondc-igm-sdk/tsconfig.json b/utilities/ondc-igm-sdk/tsconfig.json
new file mode 100644
index 0000000..1e8cfdd
--- /dev/null
+++ b/utilities/ondc-igm-sdk/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "module": "commonjs",
+ "declaration": true,
+ "outDir": "./lib",
+ "strict": true,
+ "esModuleInterop": true
+ },
+ "include": ["src"],
+ "exclude": ["node_modules", "**/__tests__/*"]
+}
diff --git a/utilities/ondc-igm-sdk/tslint.json b/utilities/ondc-igm-sdk/tslint.json
new file mode 100644
index 0000000..267f369
--- /dev/null
+++ b/utilities/ondc-igm-sdk/tslint.json
@@ -0,0 +1,3 @@
+{
+ "extends": ["tslint:recommended", "tslint-config-prettier"]
+}
diff --git a/utilities/signing_and_verification/golang/crypto.go b/utilities/signing_and_verification/golang/crypto.go
new file mode 100644
index 0000000..3d8dcdd
--- /dev/null
+++ b/utilities/signing_and_verification/golang/crypto.go
@@ -0,0 +1,432 @@
+package main
+
+import (
+ "bytes"
+ "crypto/aes"
+ "crypto/ed25519"
+ "crypto/rand"
+ "crypto/x509/pkix"
+ "encoding/asn1"
+ b64 "encoding/base64"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "regexp"
+ "time"
+
+ "golang.org/x/crypto/blake2b"
+ "golang.org/x/crypto/curve25519"
+ "maze.io/x/crypto/x25519"
+)
+
+type pkcs8 struct {
+ Version int
+ Algo pkix.AlgorithmIdentifier
+ PrivateKey []byte
+ // optional attributes omitted.
+}
+
+type publicKeyInfo struct {
+ Raw asn1.RawContent
+ Algorithm pkix.AlgorithmIdentifier
+ PublicKey asn1.BitString
+}
+
+type pkixPublicKey struct {
+ Algo pkix.AlgorithmIdentifier
+ BitString asn1.BitString
+}
+
+func base64Decode(payload string) ([]byte, error) {
+ return b64.StdEncoding.DecodeString(payload)
+}
+
+func base64Encode(payload []byte) string {
+ return b64.StdEncoding.EncodeToString(payload)
+}
+
+func generateEncryptionKeys() (string, string, error) {
+ //publicKey, privateKey, err := ed25519.GenerateKey(nil)
+ privateKey, err := x25519.GenerateKey(rand.Reader)
+ if err != nil {
+ fmt.Println("Error generating x25519 keys for encryption")
+ return "", "", err
+ }
+
+ marshaledPrivateKey, err := marshalX25519PrivateKey(privateKey.Bytes())
+ if err != nil {
+ fmt.Println("Error marshaling enc private key to x509.pkcs format", err)
+ return "", "", err
+ }
+
+ marshaledPublicKey, err := marshalX25519PublicKey(privateKey.PublicKey.Bytes())
+ if err != nil {
+ fmt.Println("Error marshaling enc public key to x509 format", err)
+ return "", "", err
+ }
+
+ return base64Encode(marshaledPublicKey), base64Encode(marshaledPrivateKey), nil
+}
+
+func marshalX25519PrivateKey(key []byte) ([]byte, error) {
+ var privateKey []byte
+ curveKey, err := asn1.Marshal(key[:32])
+ if err != nil {
+ fmt.Println("Error asn1 marshaling private key")
+ return privateKey, err
+ }
+ pkcsKey := pkcs8{
+ Version: 1,
+ Algo: pkix.AlgorithmIdentifier{
+ Algorithm: asn1.ObjectIdentifier{1, 3, 101, 110},
+ },
+ PrivateKey: curveKey,
+ }
+ privateKey, err = asn1.Marshal(pkcsKey)
+ if err != nil {
+ fmt.Println("Error asn1 marshaling pkcs8 key", err)
+ return privateKey, err
+ }
+ return privateKey, nil
+}
+
+func marshalX25519PublicKey(key []byte) ([]byte, error) {
+ x509Key := pkixPublicKey{
+ Algo: pkix.AlgorithmIdentifier{
+ Algorithm: asn1.ObjectIdentifier{1, 3, 101, 110},
+ },
+ BitString: asn1.BitString{
+ Bytes: key,
+ BitLength: 8 * len(key),
+ },
+ }
+ publicKey, err := asn1.Marshal(x509Key)
+ if err != nil {
+ fmt.Println("Error asn1 marshaling public key", err)
+ return publicKey, err
+ }
+ return publicKey, nil
+}
+
+func parseX25519PrivateKey(key string) ([]byte, error) {
+ var parsedKey []byte
+ decoded, err := base64Decode(key)
+ if err != nil {
+ fmt.Println("Error base64 decoding x25519 private key", err)
+ return parsedKey, err
+ }
+
+ var pkcsKey pkcs8
+ _, err = asn1.Unmarshal(decoded, &pkcsKey)
+ if err != nil {
+ fmt.Println("Error asn1 unmarshaling x25519 private key", err)
+ return parsedKey, err
+ }
+
+ _, err = asn1.Unmarshal(pkcsKey.PrivateKey, &parsedKey)
+ if err != nil {
+ fmt.Println("Error asn1 unmashaling pkcs privat key", err)
+ return parsedKey, err
+ }
+ return parsedKey, nil
+}
+
+func parseX25519PublicKey(key string) ([]byte, error) {
+ var parsedKey []byte
+
+ decoded, err := base64Decode(key)
+ if err != nil {
+ fmt.Println("Error base64 decoding x25519 public key", err)
+ return parsedKey, err
+ }
+
+ var x509Key publicKeyInfo
+ _, err = asn1.Unmarshal(decoded, &x509Key)
+ if err != nil {
+ fmt.Println("Error asn1 unmarshaling x25519 public key", err)
+ return parsedKey, err
+ }
+
+ return x509Key.PublicKey.RightAlign(), nil
+}
+
+func aesEncrypt(payload []byte, key []byte) ([]byte, error) {
+ cipher, err := aes.NewCipher(key)
+ blockSize := cipher.BlockSize()
+ if err != nil {
+ fmt.Println("Error creating AES cipher", err)
+ return nil, err
+ }
+ size := len(payload)
+ if size%blockSize != 0 {
+ remainder := blockSize - (size % blockSize)
+ pad := bytes.Repeat([]byte(" "), remainder)
+ payload = append(payload, pad...)
+ size = len(payload)
+ }
+
+ buf := make([]byte, blockSize)
+ var encrypted []byte
+ for i := 0; i < size; i += blockSize {
+ cipher.Encrypt(buf, payload[i:i+blockSize])
+ encrypted = append(encrypted, buf...)
+ }
+ return encrypted, nil
+}
+
+func aesDecrypt(cipherText []byte, key []byte) ([]byte, error) {
+ cipher, err := aes.NewCipher(key)
+ blockSize := cipher.BlockSize()
+ if err != nil {
+ fmt.Println("Error creating AES cipher", err)
+ return nil, err
+ }
+ size := len(cipherText)
+ buf := make([]byte, blockSize)
+ var plainText []byte
+ for i := 0; i < size; i += blockSize {
+ cipher.Decrypt(buf, cipherText[i:i+blockSize])
+ plainText = append(plainText, buf...)
+ }
+ return plainText, nil
+}
+
+func generateSigningKeys() (string, string, error) {
+ publicKey, privateKey, err := ed25519.GenerateKey(nil)
+ if err != nil {
+ fmt.Println("Error generating signing keys", err)
+ return "", "", err
+ }
+
+ return base64Encode(publicKey), base64Encode(privateKey), nil
+}
+
+func signRequest(privateKey string, payload []byte, currentTime int, ttl int) (string, error) {
+
+ //compute blake 512 hash over the payload
+ hash := blake2b.Sum512(payload)
+ digest := base64Encode(hash[:])
+
+ //create a signature and then sign with ed25519 private key
+ signatureBody := fmt.Sprintf("(created): %d\n(expires): %d\ndigest: BLAKE-512=%s", currentTime, (currentTime + ttl), digest)
+ decodedKey, err := base64Decode(privateKey)
+ if err != nil {
+ fmt.Println("Error decoding signing private key", err)
+ return "", err
+ }
+ signature := ed25519.Sign(decodedKey, []byte(signatureBody))
+ return base64Encode(signature), nil
+}
+
+func getRequestBody() ([]byte, error) {
+ var payload []byte
+ file, err := os.Open("request_body_raw_text.txt")
+ if err != nil {
+ fmt.Println("Error opening request body text file", err)
+ return payload, err
+ }
+ defer file.Close()
+
+ payload, err = io.ReadAll(file)
+ if err != nil {
+ fmt.Println("Error reading request body text file", err)
+ return payload, err
+ }
+
+ return payload, nil
+}
+
+func getAuthHeader() (string, error) {
+ var authHeader string
+
+ payload, err := getRequestBody()
+ if err != nil {
+ return authHeader, err
+ }
+
+ privateKey := os.Getenv("PRIVATE_KEY")
+ currentTime := int(time.Now().Unix())
+
+ //ttl we are using is 30 seconds
+ ttl := 30
+
+ signature, err := signRequest(privateKey, payload, currentTime, ttl)
+ if err != nil {
+ fmt.Println("Could not compute signature", err)
+ return authHeader, err
+ }
+
+ subscriberID := os.Getenv("SUBSCRIBER_ID")
+ if subscriberID == "" {
+ subscriberID = "buyer-app.ondc.org"
+ }
+
+ uniqueKeyID := os.Getenv("UNIQUE_KEY_ID")
+ if uniqueKeyID == "" {
+ uniqueKeyID = "207"
+ }
+ authHeader = fmt.Sprintf(`Signature keyId="%s|%s|ed25519",algorithm="ed25519",created="%d",expires="%d",headers="(created) (expires) digest",signature="%s"`, subscriberID, uniqueKeyID, currentTime, currentTime+ttl, signature)
+
+ return authHeader, nil
+}
+
+func verifyRequest(authHeader string) bool {
+
+ payload, err := getRequestBody()
+ if err != nil {
+ return false
+ }
+
+ publicKey := os.Getenv("PUBLIC_KEY")
+
+ _, created, expires, signature, err := parseAuthHeader(authHeader)
+ if err != nil {
+ return false
+ }
+
+ //compute blake 512 hash over the payload
+ hash := blake2b.Sum512(payload)
+ digest := base64Encode(hash[:])
+
+ //create a signature and then sign with ed25519 private key
+ computedMessage := fmt.Sprintf("(created): %s\n(expires): %s\ndigest: BLAKE-512=%s", created, expires, digest)
+ publicKeyBytes, err := base64Decode(publicKey)
+ if err != nil {
+ fmt.Println("Error decoding public key", err)
+ return false
+ }
+ receivedSignature, err := base64Decode(signature)
+ if err != nil {
+ fmt.Println("Unable to base64 decode received signature", err)
+ return false
+ }
+ return ed25519.Verify(publicKeyBytes, []byte(computedMessage), receivedSignature)
+}
+
+func parseAuthHeader(authHeader string) (string, string, string, string, error) {
+ signatureRegex := regexp.MustCompile(`keyId=\"(.+?)\".+?created=\"(.+?)\".+?expires=\"(.+?)\".+?signature=\"(.+?)\"`)
+ groups := signatureRegex.FindAllStringSubmatch(authHeader, -1)
+ if len(groups) > 0 && len(groups[0]) > 4 {
+ return groups[0][1], groups[0][2], groups[0][3], groups[0][4], nil
+ }
+ fmt.Println("Error parsing auth header. Please make sure that the auh headers passed as command line argument is valid")
+ return "", "", "", "", errors.New("error parsing auth header")
+}
+
+func encrypt(privateKey string, publicKey string) (string, error) {
+ var encryptedText string
+ parsedPrivateKey, err := parseX25519PrivateKey(privateKey)
+ if err != nil {
+ fmt.Println("Error parsing private key.", err)
+ return encryptedText, err
+ }
+
+ parsedPublicKey, err := parseX25519PublicKey(publicKey)
+ if err != nil {
+ fmt.Println("Error parsing public key.", err)
+ return encryptedText, err
+ }
+
+ secretKey, err := curve25519.X25519(parsedPrivateKey, parsedPublicKey)
+ if err != nil {
+ fmt.Println("Error constructing secret key", err)
+ return encryptedText, nil
+ }
+
+ plainText := "ONDC is a Great Initiative!"
+ cipherBytes, err := aesEncrypt([]byte(plainText), secretKey)
+ if err != nil {
+ fmt.Println("Error encrypting with AES", err)
+ return encryptedText, err
+ }
+
+ encryptedText = base64Encode(cipherBytes)
+ return encryptedText, nil
+}
+
+func decrypt(privateKey string, publicKey string, cipherText string) (string, error) {
+ var decryptedText string
+ parsedPrivateKey, err := parseX25519PrivateKey(privateKey)
+ if err != nil {
+ fmt.Println("Error parsing private key.", err)
+ return decryptedText, err
+ }
+
+ parsedPublicKey, err := parseX25519PublicKey(publicKey)
+ if err != nil {
+ fmt.Println("Error parsing public key.", err)
+ return decryptedText, err
+ }
+
+ secretKey, err := curve25519.X25519(parsedPrivateKey, parsedPublicKey)
+ if err != nil {
+ fmt.Println("Error constructing secret key", err)
+ return decryptedText, nil
+ }
+
+ cipherBytes, err := base64Decode(cipherText)
+ if err != nil {
+ fmt.Println("Error base64 decoding cipher text", err)
+ return decryptedText, err
+ }
+ plainBytes, err := aesDecrypt(cipherBytes, secretKey)
+ if err != nil {
+ fmt.Println("Error decrypting with AES", err)
+ return decryptedText, err
+ }
+
+ decryptedText = string(plainBytes)
+ return decryptedText, nil
+}
+
+func main() {
+
+ args := os.Args[1:]
+ if len(args) == 0 {
+ fmt.Println("Missing paramsters. Try ./crypto generate_key_pairs")
+ return
+ }
+
+ switch args[0] {
+ case "generate_key_pairs":
+ signingPublicKey, signingPrivateKey, err := generateSigningKeys()
+ if err != nil {
+ fmt.Println("Could not generate signing keys")
+ return
+ }
+ encPublicKey, encPrivateKey, err := generateEncryptionKeys()
+ if err != nil {
+ fmt.Println("Could not generate encryption keys")
+ return
+ }
+ fmt.Println("Signing_private_key:", signingPrivateKey)
+ fmt.Println("Signing_public_key:", signingPublicKey)
+ fmt.Println("Crypto_Privatekey:", encPrivateKey)
+ fmt.Println("Crypto_Publickey:", encPublicKey)
+ case "create_authorisation_header":
+ authHeader, err := getAuthHeader()
+ if err == nil {
+ fmt.Println(authHeader)
+ }
+ case "verify_authorisation_header":
+ authHeader := args[1]
+ fmt.Println(verifyRequest(authHeader))
+ case "encrypt":
+ privateKey := args[1]
+ publicKey := args[2]
+ cipherText, err := encrypt(privateKey, publicKey)
+ if err == nil {
+ fmt.Println(cipherText)
+ }
+ case "decrypt":
+ privateKey := args[1]
+ publicKey := args[2]
+ cipherText := args[3]
+ plainText, err := decrypt(privateKey, publicKey, cipherText)
+ if err == nil {
+ fmt.Println(plainText)
+ }
+
+ }
+}
diff --git a/utilities/signing_and_verification/golang/go.mod b/utilities/signing_and_verification/golang/go.mod
new file mode 100644
index 0000000..5f8bcf2
--- /dev/null
+++ b/utilities/signing_and_verification/golang/go.mod
@@ -0,0 +1,10 @@
+module shopalyst.com/opensource/ondccryptoutil
+
+go 1.20
+
+require (
+ golang.org/x/crypto v0.7.0
+ maze.io/x/crypto v0.0.0-20190131090603-9b94c9afe066
+)
+
+require golang.org/x/sys v0.6.0 // indirect
diff --git a/utilities/signing_and_verification/golang/go.sum b/utilities/signing_and_verification/golang/go.sum
new file mode 100644
index 0000000..c7bf69f
--- /dev/null
+++ b/utilities/signing_and_verification/golang/go.sum
@@ -0,0 +1,6 @@
+golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
+golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+maze.io/x/crypto v0.0.0-20190131090603-9b94c9afe066 h1:UrD21H1Ue5Nl8f2x/NQJBRdc49YGmla3mRStinH8CCE=
+maze.io/x/crypto v0.0.0-20190131090603-9b94c9afe066/go.mod h1:DEvumi+swYmlKxSlnsvPwS15tRjoypCCeJFXswU5FfQ=
diff --git a/utilities/signing_and_verification/golang/request_body_raw_text.txt b/utilities/signing_and_verification/golang/request_body_raw_text.txt
new file mode 100644
index 0000000..8ad501a
--- /dev/null
+++ b/utilities/signing_and_verification/golang/request_body_raw_text.txt
@@ -0,0 +1,31 @@
+{
+ "context": {
+ "domain": "nic2004:60212",
+ "country": "IND",
+ "city": "Kochi",
+ "action": "search",
+ "core_version": "0.9.1",
+ "bap_id": "bap.stayhalo.in",
+ "bap_uri": "https://8f9f-49-207-209-131.ngrok.io/protocol/",
+ "transaction_id": "e6d9f908-1d26-4ff3-a6d1-3af3d3721054",
+ "message_id": "a2fe6d52-9fe4-4d1a-9d0b-dccb8b48522d",
+ "timestamp": "2022-01-04T09:17:55.971Z",
+ "ttl": "P1M"
+ },
+ "message": {
+ "intent": {
+ "fulfillment": {
+ "start": {
+ "location": {
+ "gps": "10.108768, 76.347517"
+ }
+ },
+ "end": {
+ "location": {
+ "gps": "10.102997, 76.353480"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/utilities/signing_and_verification/node/index.js b/utilities/signing_and_verification/node/index.js
new file mode 100644
index 0000000..808d1d3
--- /dev/null
+++ b/utilities/signing_and_verification/node/index.js
@@ -0,0 +1,37 @@
+const nacl = require("tweetnacl");
+const crypto = require("crypto");
+
+function generateKeyPairs() {
+ const signingKeyPair = nacl.sign.keyPair();
+ const { privateKey, publicKey } = crypto.generateKeyPairSync('x25519', {
+ modulusLength: 2048,
+ publicKeyEncoding: {
+ type: 'spki',
+ format: 'pem',
+ },
+ privateKeyEncoding: {
+ type: 'pkcs8',
+ format: 'pem',
+ },
+ });
+
+ return {
+ Signing_private_key: Buffer.from(signingKeyPair.secretKey).toString(
+ "base64"
+ ),
+ Signing_public_key: Buffer.from(signingKeyPair.publicKey).toString(
+ "base64"
+ ),
+ Encryption_Privatekey: privateKey.toString('utf-8')
+ .replace(/-----BEGIN PRIVATE KEY-----/, '')
+ .replace(/-----END PRIVATE KEY-----/, '')
+ .replace(/\s/g, ''),
+ Encryption_Publickey: publicKey.toString('utf-8')
+ .replace(/-----BEGIN PUBLIC KEY-----/, '')
+ .replace(/-----END PUBLIC KEY-----/, '')
+ .replace(/\s/g, ''),
+ };
+}
+
+const keyPairs = generateKeyPairs();
+console.log(keyPairs);
diff --git a/utilities/signing_and_verification/node/package.json b/utilities/signing_and_verification/node/package.json
new file mode 100644
index 0000000..537764b
--- /dev/null
+++ b/utilities/signing_and_verification/node/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "generate-key-pairs",
+ "version": "1.0.0",
+ "description": "This Node.js script generates key pairs for signing and encryption using the `tweetnacl` and `crypto` library.",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "crypto": "^1.0.1",
+ "tweetnacl": "^1.0.3"
+ }
+}
diff --git a/utilities/signing_and_verification/node/readme.md b/utilities/signing_and_verification/node/readme.md
new file mode 100644
index 0000000..aa7e28e
--- /dev/null
+++ b/utilities/signing_and_verification/node/readme.md
@@ -0,0 +1,50 @@
+# Signing Key pair generation using Node.js
+
+This Node.js script generates key pairs for signing and encryption using the `tweetnacl` and `crypto` library.
+
+## Prerequisites
+
+Make sure you have Node.js installed on your system. If not, you can download it from [Node.js website](https://nodejs.org/).
+
+## Installation
+
+```bash
+npm i
+```
+
+## Usage
+
+- Clone the repository or copy the script into your project.
+- Run the script using the following command:
+
+```bash
+node index.js
+```
+
+## Output
+
+The script will output key pairs in base64-encoded format for signing and encryption.
+
+```bash
+{
+ "Signing_private_key": "BASE64_ENCODED_PRIVATE_KEY",
+ "Signing_public_key": "BASE64_ENCODED_PUBLIC_KEY",
+ "Encryption_Privatekey": "BASE64_ENCODED_PRIVATE_KEY",
+ "Encryption_Publickey": "BASE64_ENCODED_PUBLIC_KEY"
+}
+
+```
+
+## Example
+
+```javascript
+const keyPairs = generateKeyPairs();
+console.log(keyPairs);
+```
+
+Feel free to integrate this script into your project or use it as a reference for key pair generation in Node.js.
+
+```vbnet
+Copy and paste this markdown content into a `readme.md` file in your project repository. Modify it as needed based on your project structure and additional information you want to provide.
+
+```
diff --git a/utilities/signing_and_verification/php/.env.example b/utilities/signing_and_verification/php/.env.example
new file mode 100644
index 0000000..11a8856
--- /dev/null
+++ b/utilities/signing_and_verification/php/.env.example
@@ -0,0 +1,9 @@
+SIGNING_PRIV_KEY="your signing private key"
+SIGNING_PUB_KEY="your signing public key"
+ENC_PUB_KEY="your encryption/crypto public key"
+ENC_PRIV_KEY="your encryption/crypto private key"
+COUNTERPARTY_PUB_KEY="the other party's signing public key"
+SUBSCRIBER_ID="your subscriber id"
+UNIQUE_KEY_ID="your ukid"
+AUTH_HEADER="the auth header that is to be verified"
+REQUEST_BODY={"context":{"domain":"nic2004:60212","country":"IND","city":"Kochi","action":"search","core_version":"0.9.1","bap_id":"bap.stayhalo.in","bap_uri":"https://8f9f-49-207-209-131.ngrok.io/protocol/","transaction_id":"e6d9f908-1d26-4ff3-a6d1-3af3d3721054","message_id":"a2fe6d52-9fe4-4d1a-9d0b-dccb8b48522d","timestamp":"2022-01-04T09:17:55.971Z","ttl":"P1M"},"message":{"intent":{"fulfillment":{"start":{"location":{"gps":"10.108768, 76.347517"}},"end":{"location":{"gps":"10.102997, 76.353480"}}}}}}
\ No newline at end of file
diff --git a/utilities/signing_and_verification/php/.gitignore b/utilities/signing_and_verification/php/.gitignore
new file mode 100644
index 0000000..27fad11
--- /dev/null
+++ b/utilities/signing_and_verification/php/.gitignore
@@ -0,0 +1,475 @@
+##### Windows
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+
+# Dump file
+*.stackdump
+
+# Folder config file
+[Dd]esktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+##### Linux
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+##### MacOS
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+##### Backup
+*.bak
+*.gho
+*.ori
+*.orig
+*.tmp
+
+##### GPG
+secring.*
+
+##### Dropbox
+# Dropbox settings and caches
+.dropbox
+.dropbox.attr
+.dropbox.cache
+
+##### SynopsysVCS
+# Waveform formats
+*.vcd
+*.vpd
+*.evcd
+*.fsdb
+
+# Default name of the simulation executable. A different name can be
+# specified with this switch (the associated daidir database name is
+# also taken from here): -o /
+simv
+
+# Generated for Verilog and VHDL top configs
+simv.daidir/
+simv.db.dir/
+
+# Infrastructure necessary to co-simulate SystemC models with
+# Verilog/VHDL models. An alternate directory may be specified with this
+# switch: -Mdir=
+csrc/
+
+# Log file - the following switch allows to specify the file that will be
+# used to write all messages from simulation: -l
+*.log
+
+# Coverage results (generated with urg) and database location. The
+# following switch can also be used: urg -dir .vdb
+simv.vdb/
+urgReport/
+
+# DVE and UCLI related files.
+DVEfiles/
+ucli.key
+
+# When the design is elaborated for DirectC, the following file is created
+# with declarations for C/C++ functions.
+vc_hdrs.h
+
+##### SVN
+.svn/
+
+##### Mercurial
+.hg/
+.hgignore
+.hgsigs
+.hgsub
+.hgsubstate
+.hgtags
+
+##### Bazaar
+.bzr/
+.bzrignore
+
+##### CVS
+/CVS/*
+**/CVS/*
+.cvsignore
+*/.cvsignore
+
+##### TortoiseGit
+# Project-level settings
+/.tgitconfig
+
+##### PuTTY
+# Private key
+*.ppk
+
+##### Vim
+# Swap
+[._]*.s[a-v][a-z]
+!*.svg # comment out if you don't need vector files
+[._]*.sw[a-p]
+[._]s[a-rt-v][a-z]
+[._]ss[a-gi-z]
+[._]sw[a-p]
+
+# Session
+Session.vim
+Sessionx.vim
+
+# Temporary
+.netrwhist
+*~
+# Auto-generated tag files
+tags
+# Persistent undo
+[._]*.un~
+
+##### Emacs
+# -*- mode: gitignore; -*-
+*~
+\#*\#
+/.emacs.desktop
+/.emacs.desktop.lock
+*.elc
+auto-save-list
+tramp
+.\#*
+
+# Org-mode
+.org-id-locations
+*_archive
+
+# flymake-mode
+*_flymake.*
+
+# eshell files
+/eshell/history
+/eshell/lastdir
+
+# elpa packages
+/elpa/
+
+# reftex files
+*.rel
+
+# AUCTeX auto folder
+/auto/
+
+# cask packages
+.cask/
+dist/
+
+# Flycheck
+flycheck_*.el
+
+# server auth directory
+/server/
+
+# projectiles files
+.projectile
+
+# directory configuration
+.dir-locals.el
+
+# network security
+/network-security.data
+
+##### SublimeText
+# Cache files for Sublime Text
+*.tmlanguage.cache
+*.tmPreferences.cache
+*.stTheme.cache
+
+# Workspace files are user-specific
+*.sublime-workspace
+
+# Project files should be checked into the repository, unless a significant
+# proportion of contributors will probably not be using Sublime Text
+# *.sublime-project
+
+# SFTP configuration file
+sftp-config.json
+sftp-config-alt*.json
+
+# Package control specific files
+Package Control.last-run
+Package Control.ca-list
+Package Control.ca-bundle
+Package Control.system-ca-bundle
+Package Control.cache/
+Package Control.ca-certs/
+Package Control.merged-ca-bundle
+Package Control.user-ca-bundle
+oscrypto-ca-bundle.crt
+bh_unicode_properties.cache
+
+# Sublime-github package stores a github token in this file
+# https://packagecontrol.io/packages/sublime-github
+GitHub.sublime-settings
+
+##### Notepad++
+# Notepad++ backups #
+*.bak
+
+##### TextMate
+*.tmproj
+*.tmproject
+tmtags
+
+##### VisualStudioCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+*.code-workspace
+
+# Local History for Visual Studio Code
+.history/
+
+##### NetBeans
+**/nbproject/private/
+**/nbproject/Makefile-*.mk
+**/nbproject/Package-*.bash
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
+
+##### JetBrains
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+##### Eclipse
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+.recommenders
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# PyDev specific (Python IDE for Eclipse)
+*.pydevproject
+
+# CDT-specific (C/C++ Development Tooling)
+.cproject
+
+# CDT- autotools
+.autotools
+
+# Java annotation processor (APT)
+.factorypath
+
+# PDT-specific (PHP Development Tools)
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# Tern plugin
+.tern-project
+
+# TeXlipse plugin
+.texlipse
+
+# STS (Spring Tool Suite)
+.springBeans
+
+# Code Recommenders
+.recommenders/
+
+# Annotation Processing
+.apt_generated/
+.apt_generated_test/
+
+# Scala IDE specific (Scala & Java development for Eclipse)
+.cache-main
+.scala_dependencies
+.worksheet
+
+# Uncomment this line if you wish to ignore the project description file.
+# Typically, this file would be tracked if it contains build/dependency configurations:
+#.project
+
+##### Dreamweaver
+# DW Dreamweaver added files
+_notes
+_compareTemp
+configs/
+dwsync.xml
+dw_php_codehinting.config
+*.mno
+
+##### CodeKit
+# General CodeKit files to ignore
+config.codekit
+config.codekit3
+/min
+
+##### Gradle
+.gradle
+**/build/
+!src/**/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Cache of project
+.gradletasknamecache
+
+# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
+# gradle/wrapper/gradle-wrapper.properties
+
+##### Composer
+composer.phar
+/vendor/
+
+# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
+# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+composer.lock
+
+##### PHP CodeSniffer
+# gitignore for the PHP Codesniffer framework
+# website: https://github.com/squizlabs/PHP_CodeSniffer
+#
+# Recommended template: PHP.gitignore
+
+/wpcs/*
+
+##### SASS
+.sass-cache/
+*.css.map
+*.sass.map
+
+.env
\ No newline at end of file
diff --git a/utilities/signing_and_verification/php/README.md b/utilities/signing_and_verification/php/README.md
new file mode 100644
index 0000000..7d7f03b
--- /dev/null
+++ b/utilities/signing_and_verification/php/README.md
@@ -0,0 +1,83 @@
+# Signing and Verification
+Steps for signing and verification
+
+1. Install php and dependencies
+
+```sh
+# linux setup
+# download php
+sudo apt install php php-curl
+# Download Composer
+curl -sS https://getcomposer.org/installer -o composer-setup.php
+# install composer
+sudo php composer-setup.php --install-dir=/usr/local/bin --filename=composer
+# change to cloned directory
+cd /reference-implementations/utilities/signing_and_verification/php
+composer install
+
+# brew setup
+# install php
+brew install php # latest version is 8.0.0, this will work for versions >= 7.4
+# install composer
+brew install composer
+# change to cloned directory
+cd /reference-implementations/utilities/signing_and_verification/php
+composer install
+```
+2. ENV variables
+```sh
+SIGNING_PRIV_KEY="your signing private key"
+SIGNING_PUB_KEY="your signing public key"
+ENC_PUB_KEY="your encryption/crypto public key"
+ENC_PRIV_KEY="your encryption/crypto private key"
+COUNTERPARTY_PUB_KEY="the other party's signing public key"
+SUBSCRIBER_ID="your subscriber id"
+UNIQUE_KEY_ID="your ukid"
+AUTH_HEADER="the auth header that is to be verified"
+REQUEST_BODY="json stringified payload"
+```
+3. Generate keys
+```sh
+composer run start -- -g
+
+# OUTPUT
+Signing priv key: VfwASYHVjMAC63LClJKVTvjHcvuUZ4oQKhXmpY6+pwsu7b5xNQzhD/drIPer5m3kasjjicaj/+lblZsNnlQMCw==
+Signing pub key: Lu2+cTUM4Q/3ayD3q+Zt5GrI44nGo//pW5WbDZ5UDAs=
+Encryption priv key: MC4CAQEwBQYDK2VuBCIEINaTmxwcMRLGuxX1lrwo0Lxd2FHqn84YqQoDzVQXe46+
+Encryption pub key: MCowBQYDK2VuAyEAFT6F4dxn1waTvLUbY5tdKh/IezuOp+tlHkAwQw82qXU=
+```
+4. Create authorisation header
+```sh
+# REQUEST_BODY, SIGNING_PRIV_KEY, SUBSCRIBER_ID, UNIQUE_KEY_ID should be set
+composer run start -- -s
+
+# OUTPUT
+Signature keyId="buyer-app.ondc.org|207|ed25519",algorithm="ed25519",created="1641287875",expires="1641291475",headers="(created) (expires) digest",signature="fKQWvXhln4UdyZdL87ViXQObdBme0dHnsclD2LvvnHoNxIgcvAwUZOmwAnH5QKi9Upg5tRaxpoGhCFGHD+d+Bw=="
+```
+
+5. Verify authorisation header
+```sh
+# AUTH_HEADER, REQUEST_BODY, COUNTERPARTY_SIGNING_PUB_KEY should be set
+composer run start -- -v
+
+#OUTPUT
+0 | 1 # depending upon truth value (true = 1)
+```
+
+6. Encrypt Payload
+```sh
+# ENC_PRIV_KEY, COUNTERPARTY_PUB_KEY should be set
+composer run start -- -e 'message to encrypt'
+
+#OUTPUT
+dq6j2KZp61G6PMM9IhHW2fbOnquy7gkwJN/tVXkKAI4=
+```
+
+8. Decrypt Payload
+```sh
+# ENC_PRIV_KEY, ENC_PUB_KEY should be set
+composer run start -d 'dq6j2KZp61G6PMM9IhHW2fbOnquy7gkwJN/tVXkKAI4='
+
+#OUTPUT
+message to encrypt
+```
diff --git a/utilities/signing_and_verification/php/composer.json b/utilities/signing_and_verification/php/composer.json
new file mode 100644
index 0000000..041881f
--- /dev/null
+++ b/utilities/signing_and_verification/php/composer.json
@@ -0,0 +1,27 @@
+{
+ "name": "charnpreet/subscription",
+ "type": "project",
+ "autoload": {
+ "psr-4": {
+ "Charnpreet\\Subscription\\": "src/",
+ "Custom\\AlgorithmIdentifier\\Asymmetric\\": "src/utils"
+ }
+ },
+ "authors": [
+ {
+ "name": "b"
+ }
+ ],
+ "require": {
+ "sop/crypto-types": "^0.3.0",
+ "phpseclib/phpseclib": "^3.0",
+ "vlucas/phpdotenv": "^5.6",
+ "ramsey/uuid": "^4.7",
+ "sop/asn1": "^4.1",
+ "sop/crypto-encoding": "^0.3.0",
+ "sop/x501": "^0.6.1"
+ },
+ "scripts": {
+ "start": "php -f src/index.php --"
+ }
+ }
diff --git a/utilities/signing_and_verification/php/src/index.php b/utilities/signing_and_verification/php/src/index.php
new file mode 100644
index 0000000..4dce0df
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/index.php
@@ -0,0 +1,193 @@
+safeload();
+
+function hash_msg(string $msg): string
+{
+ return base64_encode(sodium_crypto_generichash($msg, "", 64));
+}
+
+function create_signing_string(string $digest_base64, string $created = null, string $expires = null): string
+{
+ $now = new DateTime();
+ $one_hour = new DateInterval("PT1H");
+
+ if ($created == null) {
+ $created = $now->getTimestamp();
+ }
+
+ if ($expires == null) {
+ $expires = $now->add($one_hour)->getTimestamp();
+ }
+
+ $signing_string = "(created): $created\n(expires): $expires\ndigest: BLAKE-512=$digest_base64";
+ return $signing_string;
+}
+
+function sign_response(string $signing_key, string $private_key): string
+{
+ return base64_encode(sodium_crypto_sign_detached($signing_key, base64_decode($private_key)));
+}
+
+function verify_response(string $signature, string $signing_key, string $public_key): bool
+{
+ $decoded_public_key = base64_decode($public_key);
+
+ if ($decoded_public_key === false) {
+ throw new Exception('Failed to decode public key from base64.');
+ }
+
+ // Check if the public key has the correct length
+ if (strlen($decoded_public_key) !== SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES) {
+ throw new Exception('The public key is not the correct length.');
+ }
+
+ // Verify the signature
+ $verification_result = sodium_crypto_sign_verify_detached(base64_decode($signature), $signing_key, $decoded_public_key);
+ echo "Verification Result: ", $verification_result ? 'True' : 'False', PHP_EOL;
+
+ return $verification_result;
+}
+
+
+function create_authorisation_header(string $request_body, string $created = null, string $expires = null)
+{
+ $now = new DateTime();
+ $one_hour = new DateInterval("PT1H");
+
+ if ($created == null) {
+ $created = $now->getTimestamp();
+ }
+
+ if ($expires == null) {
+ $expires = $now->add($one_hour)->getTimeStamp();
+ }
+
+ $signing_key = create_signing_string(hash_msg($request_body), $created, $expires);
+ $signature = sign_response($signing_key, $_ENV['SIGNING_PRIV_KEY']);
+
+ $subscriber_id = $_ENV['SUBSCRIBER_ID'];
+ $unique_key_id = $_ENV['UNIQUE_KEY_ID'];
+
+ $header = "Signature keyId=\"$subscriber_id|$unique_key_id|ed25519\",algorithm=\"ed25519\",created=\"$created\",expires=\"$expires\",headers=\"(created) (expires) digest\",signature=\"$signature\"";
+ return $header;
+}
+
+// util
+function get_filter_dict(string $filter_string)
+{
+ $filter_string_list = preg_split("/,/", $filter_string);
+ $filter_string_list = array_map(function ($item) {return trim($item, " \n\r\t\v\0\"");}, $filter_string_list);
+ $filter_string_dict = [];
+ foreach ($filter_string_list as $item) {
+ $split_item = preg_split("/=/", $item, 2);
+ $filter_string_dict[$split_item[0]] = trim($split_item[1], " \n\r\t\v\0\"");
+ }
+ return $filter_string_dict;
+}
+
+function verify_authorisation_header(string $auth_header, string $request_body_str, string $public_key): bool
+{
+ $auth_header = str_replace("Signature ", "", $_ENV['AUTH_HEADER']);
+ $header_parts = get_filter_dict($auth_header);
+ $created = (int) $header_parts["created"];
+ $expires = (int) $header_parts["expires"];
+
+ $now = new DateTime();
+ if ($created <= $now->getTimestamp() && $expires >= $now->getTimestamp()) {
+ $signing_key = create_signing_string(hash_msg($_ENV['REQUEST_BODY']), $created, $expires);
+ return verify_response($header_parts['signature'], $signing_key, $public_key);
+ }else{
+ throw new Exception('The signature has expired.');
+ }
+ return false;
+}
+
+function gen_keys()
+{
+ $signkeypair = sodium_crypto_sign_keypair();
+ $signprivkey = sodium_crypto_sign_secretkey($signkeypair);
+ $signpubkey = sodium_crypto_sign_publickey($signkeypair);
+
+ // using libsodium
+ $enckeypair = sodium_crypto_box_keypair();
+ $encprivkey = sodium_crypto_box_secretkey($enckeypair);
+ $encpubkey = sodium_crypto_box_publickey($enckeypair);
+
+ // $openssl = openssl_pkey_get_private($encprivkey);
+
+ // echo "Encryption priv key: ", base64_encode($encprivkey), "\n";
+ $encprivkey = new OneAsymmetricKey(new X25519AlgorithmIdentifier(), "\x04\x20" . $encprivkey);
+ $encpubkey = new PublicKeyInfo(new X25519AlgorithmIdentifier(), new BitString($encpubkey));
+
+ echo "Signing priv key: ", base64_encode($signprivkey), "\n";
+ echo "Signing pub key: ", base64_encode($signpubkey), "\n";
+ echo "Encryption priv key: ", base64_encode($encprivkey->toDER()), "\n";
+ echo "Encryption pub key: ", base64_encode($encpubkey->toDER()), "\n";
+}
+
+function encrypt(string $crypto_private_key, string $crypto_public_key, ?string $message = null): string
+{
+ $pkey = OneAsymmetricKey::fromDER(base64_decode($crypto_private_key));
+ $pubkey = PublicKeyInfo::fromDER(base64_decode($crypto_public_key));
+ $pkey = hex2bin(str_replace("0420", "", bin2hex($pkey->privateKeyData())));
+ $shkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($pkey, $pubkey->publicKeyData());
+ $shpkey = sodium_crypto_box_secretkey($shkey);
+
+ $cipher = new AES('ecb');
+ $cipher->setKey($shpkey);
+ return base64_encode($cipher->encrypt($message == null ? 'ONDC is a great initiative!' : $message));
+}
+
+function decrypt(string $crypto_private_key, string $crypto_public_key, string $cipher_text): string
+{
+ $pkey = OneAsymmetricKey::fromDER(base64_decode($crypto_private_key));
+ $pubkey = PublicKeyInfo::fromDER(base64_decode($crypto_public_key));
+ $pkey = hex2bin(str_replace("0420", "", bin2hex($pkey->privateKeyData())));
+ $shkey = sodium_crypto_box_keypair_from_secretkey_and_publickey($pkey, $pubkey->publicKeyData());
+ $shpkey = sodium_crypto_box_secretkey($shkey);
+
+ $cipher = new AES('ecb');
+ $cipher->setKey($shpkey);
+ return ($cipher->decrypt(base64_decode($cipher_text)));
+}
+
+function main(array $args)
+{
+ switch ($args[1]) {
+ case "-g":
+ gen_keys();
+ break;
+ case "-e":
+ echo encrypt($_ENV['ENC_PRIV_KEY'], $_ENV['ENC_PUB_KEY'], $args[2]);
+ break;
+ case "-d":
+ echo decrypt($_ENV['ENC_PRIV_KEY'], $_ENV['COUNTERPARTY_PUB_KEY'], $args[2]);
+ break;
+ case "-s":
+ echo create_authorisation_header($_ENV['REQUEST_BODY']);
+ break;
+ case "-v":
+ echo verify_authorisation_header($_ENV['AUTH_HEADER'] || '', $_ENV['REQUEST_BODY'] || '', $_ENV["SIGNING_PUB_KEY"]);
+ break;
+ default:
+ echo "$args[1] is not a valid argument: please check" . "\n";
+ echo "signing and verification helper: " . "\n";
+ echo "-g: generate signing and enc key pairs" . "\n";
+ echo "-e: encrypt with enc keys" . "\n";
+ echo "-d: decrypt with enc keys" . "\n";
+ echo "-s: create signed header" . "\n";
+ echo "-v: verify signed header" . "\n";
+ }
+}
+
+main($argv);
diff --git a/utilities/signing_and_verification/php/src/utils/ECPublicKeyAlgorithmIdentifier.php b/utilities/signing_and_verification/php/src/utils/ECPublicKeyAlgorithmIdentifier.php
new file mode 100644
index 0000000..367af2d
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/utils/ECPublicKeyAlgorithmIdentifier.php
@@ -0,0 +1,303 @@
+ 192,
+ self::CURVE_PRIME192V2 => 192,
+ self::CURVE_PRIME192V3 => 192,
+ self::CURVE_PRIME239V1 => 239,
+ self::CURVE_PRIME239V2 => 239,
+ self::CURVE_PRIME239V3 => 239,
+ self::CURVE_PRIME256V1 => 256,
+ self::CURVE_SECP112R1 => 112,
+ self::CURVE_SECP112R2 => 112,
+ self::CURVE_SECP128R1 => 128,
+ self::CURVE_SECP128R2 => 128,
+ self::CURVE_SECP160K1 => 160,
+ self::CURVE_SECP160R1 => 160,
+ self::CURVE_SECP160R2 => 160,
+ self::CURVE_SECP192K1 => 192,
+ self::CURVE_SECP224K1 => 224,
+ self::CURVE_SECP224R1 => 224,
+ self::CURVE_SECP256K1 => 256,
+ self::CURVE_SECP384R1 => 384,
+ self::CURVE_SECP521R1 => 521,
+ ];
+
+ /**
+ * OID of the named curve.
+ *
+ * @var string
+ */
+ protected $_namedCurve;
+
+ /**
+ * Constructor.
+ *
+ * @param string $named_curve Curve identifier
+ */
+ public function __construct(string $named_curve)
+ {
+ $this->_oid = self::OID_EC_PUBLIC_KEY;
+ $this->_namedCurve = $named_curve;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function name(): string
+ {
+ return 'ecPublicKey';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return self
+ */
+ public static function fromASN1Params(
+ ?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
+ {
+ if (!isset($params)) {
+ throw new \UnexpectedValueException('No parameters.');
+ }
+ $named_curve = $params->asObjectIdentifier()->oid();
+ return new self($named_curve);
+ }
+
+ /**
+ * Get OID of the named curve.
+ */
+ public function namedCurve(): string
+ {
+ return $this->_namedCurve;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return ObjectIdentifier
+ */
+ protected function _paramsASN1(): ?Element
+ {
+ return new ObjectIdentifier($this->_namedCurve);
+ }
+}
diff --git a/utilities/signing_and_verification/php/src/utils/Ed25519AlgorithmIdentifier.php b/utilities/signing_and_verification/php/src/utils/Ed25519AlgorithmIdentifier.php
new file mode 100644
index 0000000..f7ccb13
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/utils/Ed25519AlgorithmIdentifier.php
@@ -0,0 +1,44 @@
+_oid = self::OID_ED25519;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function name(): string
+ {
+ return 'id-Ed25519';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supportsKeyAlgorithm(AlgorithmIdentifier $algo): bool
+ {
+ return self::OID_ED25519 === $algo->oid();
+ }
+}
diff --git a/utilities/signing_and_verification/php/src/utils/Ed448AlgorithmIdentifier.php b/utilities/signing_and_verification/php/src/utils/Ed448AlgorithmIdentifier.php
new file mode 100644
index 0000000..888ac6a
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/utils/Ed448AlgorithmIdentifier.php
@@ -0,0 +1,44 @@
+_oid = self::OID_ED448;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function name(): string
+ {
+ return 'id-Ed448';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function supportsKeyAlgorithm(AlgorithmIdentifier $algo): bool
+ {
+ return self::OID_ED448 === $algo->oid();
+ }
+}
diff --git a/utilities/signing_and_verification/php/src/utils/RFC8410EdAlgorithmIdentifier.php b/utilities/signing_and_verification/php/src/utils/RFC8410EdAlgorithmIdentifier.php
new file mode 100644
index 0000000..a86bacc
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/utils/RFC8410EdAlgorithmIdentifier.php
@@ -0,0 +1,59 @@
+_oid = self::OID_RSA_ENCRYPTION;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function name(): string
+ {
+ return 'rsaEncryption';
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return self
+ */
+ public static function fromASN1Params(
+ ?UnspecifiedType $params = null): SpecificAlgorithmIdentifier
+ {
+ if (!isset($params)) {
+ throw new \UnexpectedValueException('No parameters.');
+ }
+ $params->asNull();
+ return new self();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @return NullType
+ */
+ protected function _paramsASN1(): ?Element
+ {
+ return new NullType();
+ }
+}
diff --git a/utilities/signing_and_verification/php/src/utils/X25519AlgorithmIdentifier.php b/utilities/signing_and_verification/php/src/utils/X25519AlgorithmIdentifier.php
new file mode 100644
index 0000000..213e64c
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/utils/X25519AlgorithmIdentifier.php
@@ -0,0 +1,31 @@
+_oid = self::OID_X25519;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function name(): string
+ {
+ return 'id-X25519';
+ }
+}
diff --git a/utilities/signing_and_verification/php/src/utils/X448AlgorithmIdentifier.php b/utilities/signing_and_verification/php/src/utils/X448AlgorithmIdentifier.php
new file mode 100644
index 0000000..638b244
--- /dev/null
+++ b/utilities/signing_and_verification/php/src/utils/X448AlgorithmIdentifier.php
@@ -0,0 +1,29 @@
+_oid = self::OID_X448;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function name(): string
+ {
+ return 'id-X448';
+ }
+}
diff --git a/utilities/signing_and_verification/python/README.md b/utilities/signing_and_verification/python/README.md
new file mode 100644
index 0000000..1893bf8
--- /dev/null
+++ b/utilities/signing_and_verification/python/README.md
@@ -0,0 +1,78 @@
+# Signing and Verification
+Steps for signing and verification
+
+1. Install pip3 and dependencies
+```
+sudo apt update
+sudo apt install python3-pip
+pip3 install -r requirements.txt
+```
+
+2. Export request body json path:
+this file should be a valid json file
+```
+export REQUEST_BODY_PATH=
+```
+ eg if you rename the file ie request_body_raw_text.txt -> request_body.json , export REQUEST_BODY_PATH=request_body.json
+
+3. Generate key-pairs
+```
+python cryptic_utils.py generate_key_pairs
+```
+ output will be like
+ ```
+ Signing_private_key: G0Pme72u8Y1MwxHqvY4iBW+7VPtJ7dsX7SGs6zZ5yvVIzdRAyHR6YkwHG2ufOE+12lsbJRwBF4Hqd7dUEOZZkg==
+ Signing_public_key: SM3UQMh0emJMBxtrnzhPtdpbGyUcAReB6ne3VBDmWZI=
+ Encryption_Privatekey: MC4CAQAwBQYDK2VuBCIEIKi7NbXeN8QzXjN48XkjOiS/UaR6rumXep8VslMy4fRU
+ Encryption_Publickey: MCowBQYDK2VuAyEA9CEWxnLJkmwW/67QR739BEam7bbd3NsffjDa5HANf0Q=
+ ```
+
+4. Export private and public keys
+```
+export PRIVATE_KEY=
+export PUBLIC_KEY=
+export ENCRYPTION_PRIVATE_KEY=
+export ENCRYPTION_PUBLIC_KEY=
+```
+
+5. Create authorisation header
+```
+python cryptic_utils.py create_authorisation_header
+```
+output will be 'auth_header' like
+```shell
+Signature keyId="buyer-app.ondc.org|207|ed25519",algorithm="ed25519",created="1641287875",expires="1641291475",headers="(created) (expires) digest",signature="fKQWvXhln4UdyZdL87ViXQObdBme0dHnsclD2LvvnHoNxIgcvAwUZOmwAnH5QKi9Upg5tRaxpoGhCFGHD+d+Bw=="
+```
+
+6. Verify authorisation header
+```
+python cryptic_utils.py verify_authorisation_header ''
+```
+eg usage from output of point 5
+```
+python cryptic_utils.py verify_authorisation_header 'Signature keyId="buyer-app.ondc.org|207|ed25519",algorithm="ed25519",created="1641287875",expires="1641291475",headers="(created) (expires) digest",signature="fKQWvXhln4UdyZdL87ViXQObdBme0dHnsclD2LvvnHoNxIgcvAwUZOmwAnH5QKi9Upg5tRaxpoGhCFGHD+d+Bw=="'
+```
+output will be true
+
+7. Encrypt Payload
+```
+python cryptic_utils.py encrypt "PrivateKey" "PublicKey"
+```
+
+eg usage
+```
+python cryptic_utils.py encrypt "MC4CAQAwBQYDK2VuBCIEIOgl3rf3arbk1PvIe0C9TZp7ImR71NSQdvuSu+zzY6xo" "MCowBQYDK2VuAyEAi801MjVpgFOXHjliyT6Nb14HkS5dj1p41qbeyU6/SC8="
+```
+
+Output will be base64 encoded string like "CrwN248HS4CIYsUvxtrK0pWCBaoyZh4LnWtGqeH7Mpc="
+
+8. Decrypt Payload
+```
+python cryptic_utils.py decrypt "PrivateKey" "PublicKey" "Encrypted String"
+```
+eg usage
+```
+python cryptic_utils.py decrypt "MC4CAQAwBQYDK2VuBCIEIOgl3rf3arbk1PvIe0C9TZp7ImR71NSQdvuSu+zzY6xo" "MCowBQYDK2VuAyEAi801MjVpgFOXHjliyT6Nb14HkS5dj1p41qbeyU6/SC8=" "CrwN248HS4CIYsUvxtrK0pWCBaoyZh4LnWtGqeH7Mpc="
+```
+
+Output will be a Plain Text decoded string "ONDC is a Great Initiative!"
diff --git a/utilities/signing_and_verification/python/auth_header_signing_and_verification.md b/utilities/signing_and_verification/python/auth_header_signing_and_verification.md
new file mode 100644
index 0000000..7708545
--- /dev/null
+++ b/utilities/signing_and_verification/python/auth_header_signing_and_verification.md
@@ -0,0 +1,28 @@
+**Pre-requisites**
+
+* Key pairs, for signing & encryption, can be generated using using [libsodium](https://libsodium.gitbook.io/doc/bindings_for_other_languages).
+
+**Creating Key Pairs**
+
+* Create key pairs, for signing (ed25519) & encryption (X25519);
+* Update base64 encoded public keys in registry;
+
+* Utility to generate signing key pairs and test signing & verification is [here](https://github.com/ONDC-Official/reference-implementations/tree/main/utilities/signing_and_verification)
+
+**Auth Header Signing**
+
+* Generate UTF-8 byte array from json payload;
+* Generate Blake2b hash from UTF-8 byte array;
+
+* Create base64 encoding of Blake2b hash. This becomes the digest for signing;
+* Sign the request, using your private signing key, and add the signature to the request authorization header, following steps documented [here](https://docs.google.com/document/d/1Iw_x-6mtfoMh0KJwL4sqQYM0kD17MLxiMCUOZDBerBo/edit#heading=h.zs1tt1ewtdt)
+
+**Auth Header Verification**
+
+* Extract the digest from the encoded signature in the request;
+* Get the signing_public_key from registry using lookup (by using the ukId in the authorization header);
+
+* Create (UTF-8) byte array from the raw payload and generate Blake2b hash;
+* Compare generated Blake2b hash with the decoded digest from the signature in the request;
+
+* In case of failure to verify, HTTP error 401 should be thrown;
diff --git a/utilities/signing_and_verification/python/cryptic_utils.py b/utilities/signing_and_verification/python/cryptic_utils.py
new file mode 100644
index 0000000..4a4d6eb
--- /dev/null
+++ b/utilities/signing_and_verification/python/cryptic_utils.py
@@ -0,0 +1,155 @@
+import base64
+import datetime
+import os
+import re
+import json
+import fire as fire
+import nacl.encoding
+import nacl.hash
+from nacl.bindings import crypto_sign_ed25519_sk_to_seed
+from nacl.signing import SigningKey, VerifyKey
+from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey,X25519PublicKey
+from cryptography.hazmat.primitives import serialization
+from Cryptodome.Cipher import AES
+from Cryptodome.Util.Padding import pad,unpad
+
+f = open(os.getenv("REQUEST_BODY_PATH", "request_body_raw_text.txt"), "r")
+request_body_raw_text = f.read()
+request_body_raw_text = json.loads(request_body_raw_text) # parse the string into a python dictionary object
+request_body_raw_text = json.dumps(request_body_raw_text, separators=(',', ':')) #minify the payload to remove any extra spaces and line breaks
+
+def hash_message(msg: str):
+ HASHER = nacl.hash.blake2b
+ digest = HASHER(bytes(msg, 'utf-8'), digest_size=64, encoder=nacl.encoding.Base64Encoder)
+ digest_str = digest.decode("utf-8")
+ return digest_str
+
+
+def create_signing_string(digest_base64, created=None, expires=None):
+ if created is None:
+ created = int(datetime.datetime.now().timestamp())
+ if expires is None:
+ expires = int((datetime.datetime.now() + datetime.timedelta(hours=1)).timestamp())
+ signing_string = f"""(created): {created}
+(expires): {expires}
+digest: BLAKE-512={digest_base64}"""
+ return signing_string
+
+
+def sign_response(signing_key, private_key):
+ private_key64 = base64.b64decode(private_key)
+ seed = crypto_sign_ed25519_sk_to_seed(private_key64)
+ signer = SigningKey(seed)
+ signed = signer.sign(bytes(signing_key, encoding='utf8'))
+ signature = base64.b64encode(signed.signature).decode()
+ return signature
+
+def verify_response(signature, signing_key, public_key):
+ try:
+ public_key64 = base64.b64decode(public_key)
+ VerifyKey(public_key64).verify(bytes(signing_key, 'utf8'), base64.b64decode(signature))
+ return True
+ except Exception:
+ return False
+
+
+def get_filter_dictionary_or_operation(filter_string):
+ filter_string_list = re.split(',', filter_string)
+ filter_string_list = [x.strip(' ') for x in filter_string_list] # to remove white spaces from list
+ filter_dictionary_or_operation = dict()
+ for fs in filter_string_list:
+ splits = fs.split('=', maxsplit=1)
+ key = splits[0].strip()
+ value = splits[1].strip()
+ filter_dictionary_or_operation[key] = value.replace("\"", "")
+ return filter_dictionary_or_operation
+
+
+def create_authorisation_header(request_body=request_body_raw_text, created=None, expires=None):
+ created = int(datetime.datetime.now().timestamp()) if created is None else created
+ expires = int((datetime.datetime.now() + datetime.timedelta(hours=1)).timestamp()) if expires is None else expires
+ signing_key = create_signing_string(hash_message(request_body),
+ created=created, expires=expires)
+ signature = sign_response(signing_key, private_key=os.getenv("PRIVATE_KEY"))
+
+ subscriber_id = os.getenv("SUBSCRIBER_ID", "buyer-app.ondc.org")
+ unique_key_id = os.getenv("UNIQUE_KEY_ID", "207")
+ header = f'"Signature keyId="{subscriber_id}|{unique_key_id}|ed25519",algorithm="ed25519",created=' \
+ f'"{created}",expires="{expires}",headers="(created) (expires) digest",signature="{signature}""'
+ return header
+
+
+def verify_authorisation_header(auth_header, request_body_str=request_body_raw_text,
+ public_key=os.getenv("PUBLIC_KEY")):
+ # `request_body_str` should request.data i.e. raw data string
+
+ # `public_key` is sender's public key
+ # i.e. if Seller is verifying Buyer's request, then seller will first do lookup for buyer-app
+ # and will verify the request using buyer-app's public-key
+ header_parts = get_filter_dictionary_or_operation(auth_header.replace("Signature ", ""))
+ created = int(header_parts['created'])
+ expires = int(header_parts['expires'])
+ current_timestamp = int(datetime.datetime.now().timestamp())
+ if created <= current_timestamp <= expires:
+ signing_key = create_signing_string(hash_message(request_body_str), created=created, expires=expires)
+ return verify_response(header_parts['signature'], signing_key, public_key=public_key)
+ else:
+ return False
+
+
+def generate_key_pairs():
+ signing_key = SigningKey.generate()
+ private_key = base64.b64encode(signing_key._signing_key).decode()
+ #print(private_key)
+ public_key = base64.b64encode(bytes(signing_key.verify_key)).decode()
+ inst_private_key = X25519PrivateKey.generate()
+ #print(base64.b64encode(bytes(tencryption_private_key.).decode()))
+ inst_public_key = inst_private_key.public_key()
+ bytes_private_key = inst_private_key.private_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PrivateFormat.PKCS8,
+ encryption_algorithm=serialization.NoEncryption()
+ )
+ bytes_public_key = inst_public_key.public_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
+ )
+ encryption_private_key = base64.b64encode(bytes_private_key).decode('utf-8')
+ encryption_public_key = base64.b64encode(bytes_public_key).decode('utf-8')
+ return {"Signing_private_key": private_key,
+ "Signing_public_key": public_key,
+ "Encryption_Privatekey": encryption_private_key,
+ "Encryption_Publickey": encryption_public_key}
+
+
+def encrypt(encryption_private_key, encryption_public_key, null):
+ private_key = serialization.load_der_private_key(
+ base64.b64decode(encryption_private_key),
+ password=None
+ )
+ public_key = serialization.load_der_public_key(
+ base64.b64decode(encryption_public_key)
+ )
+ shared_key = private_key.exchange(public_key)
+ cipher = AES.new(shared_key, AES.MODE_ECB)
+ text = b'ONDC is a Great Initiative!!'
+ return base64.b64encode(cipher.encrypt(pad(text,AES.block_size))).decode('utf-8')
+
+
+def decrypt(encryption_private_key, encryption_public_key, cipherstring):
+ private_key = serialization.load_der_private_key(
+ base64.b64decode(encryption_private_key),
+ password=None
+ )
+ public_key = serialization.load_der_public_key(
+ base64.b64decode(encryption_public_key)
+ )
+ shared_key = private_key.exchange(public_key)
+ cipher = AES.new(shared_key, AES.MODE_ECB)
+ ciphertxt = base64.b64decode(cipherstring)
+ # print(AES.block_size, len(ciphertxt))
+ return unpad(cipher.decrypt(ciphertxt), AES.block_size).decode('utf-8')
+
+
+if __name__ == '__main__':
+ fire.Fire()
diff --git a/utilities/signing_and_verification/python/cryptkey.py b/utilities/signing_and_verification/python/cryptkey.py
new file mode 100644
index 0000000..25b5529
--- /dev/null
+++ b/utilities/signing_and_verification/python/cryptkey.py
@@ -0,0 +1,49 @@
+
+from cryptography.hazmat.primitives import serialization
+import base64
+from Cryptodome.Cipher import AES
+from Cryptodome.Util.Padding import pad,unpad
+
+pkey = serialization.load_der_private_key(
+ base64.b64decode("MC4CAQAwBQYDK2VuBCIEIKh1Dq7Fu82lqQdBQJTHTvBTxtD6hLconopqvVLVy81s"),
+ password=None
+)
+
+ukey=pkey.public_key()
+encstr = base64.b64decode("TaaRFx6fxSbFJO2Lp9Kbap1rZTjAAveAeASr19G7iXI8Meaz6Ok6B4C709pC3GpR".encode('utf-8'))
+
+uenkey = ukey.public_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
+)
+
+penkey = pkey.private_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PrivateFormat.PKCS8,
+ encryption_algorithm=serialization.NoEncryption()
+)
+
+print("Local Private Key: ", base64.b64encode(penkey).decode('utf-8'))
+print("Local Public Key: ", base64.b64encode(uenkey).decode('utf-8'))
+print("ONDC Public Key: ", "MCowBQYDK2VuAyEAa9Wbpvd9SsrpOZFcynyt/TO3x0Yrqyys4NUGIvyxX2Q=")
+
+myukey = base64.b64encode(uenkey).decode('utf-8')
+
+ondcpub = (base64.b64decode(
+ "MCowBQYDK2VuAyEAa9Wbpvd9SsrpOZFcynyt/TO3x0Yrqyys4NUGIvyxX2Q="))
+oenkey = serialization.load_der_public_key(ondcpub)
+
+shared_key = pkey.exchange(oenkey)
+print("Shared Secret: ", base64.b64encode(shared_key).decode('utf-8'))
+
+# print("Derived Secret: ", base64.b64encode(key).decode('utf-8'))
+
+
+cipher = AES.new(shared_key,AES.MODE_ECB)
+dec = cipher.decrypt(encstr)
+enc = cipher.encrypt(pad(b'ABCDEFGH', AES.block_size))
+print(dec.decode('utf-8'))
+print(base64.b64encode(enc).decode('utf-8'))
+estr=cipher.encrypt(pad(b'ABCDEFGH', AES.block_size))
+dstr=cipher.decrypt(estr)
+print(unpad(dstr, AES.block_size).decode('utf-8'))
\ No newline at end of file
diff --git a/utilities/signing_and_verification/python/request_body_raw_text.txt b/utilities/signing_and_verification/python/request_body_raw_text.txt
new file mode 100644
index 0000000..8ad501a
--- /dev/null
+++ b/utilities/signing_and_verification/python/request_body_raw_text.txt
@@ -0,0 +1,31 @@
+{
+ "context": {
+ "domain": "nic2004:60212",
+ "country": "IND",
+ "city": "Kochi",
+ "action": "search",
+ "core_version": "0.9.1",
+ "bap_id": "bap.stayhalo.in",
+ "bap_uri": "https://8f9f-49-207-209-131.ngrok.io/protocol/",
+ "transaction_id": "e6d9f908-1d26-4ff3-a6d1-3af3d3721054",
+ "message_id": "a2fe6d52-9fe4-4d1a-9d0b-dccb8b48522d",
+ "timestamp": "2022-01-04T09:17:55.971Z",
+ "ttl": "P1M"
+ },
+ "message": {
+ "intent": {
+ "fulfillment": {
+ "start": {
+ "location": {
+ "gps": "10.108768, 76.347517"
+ }
+ },
+ "end": {
+ "location": {
+ "gps": "10.102997, 76.353480"
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/utilities/signing_and_verification/python/requirements.txt b/utilities/signing_and_verification/python/requirements.txt
new file mode 100644
index 0000000..ad9bfd1
--- /dev/null
+++ b/utilities/signing_and_verification/python/requirements.txt
@@ -0,0 +1,4 @@
+fire==0.5.0
+PyNaCl==1.5.0
+cryptography==39.0.1
+pycryptodomex==3.17
\ No newline at end of file
diff --git a/utilities/signing_and_verification/python/scratch.py b/utilities/signing_and_verification/python/scratch.py
new file mode 100644
index 0000000..a3a19d4
--- /dev/null
+++ b/utilities/signing_and_verification/python/scratch.py
@@ -0,0 +1,75 @@
+
+import json
+from cryptography.hazmat.primitives import serialization
+import base64
+from Cryptodome.Cipher import AES
+from Cryptodome import Random
+
+rand_gen = Random.new()
+
+pkey = serialization.load_der_private_key(
+ base64.b64decode("MC4CAQAwBQYDK2VuBCIEIFl3LqvTWbbT9ws+ZMseya8qdR4H7E4YmBLapKPiRGEc"),
+ password=None
+)
+
+ukey=pkey.public_key()
+
+uenkey = ukey.public_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PublicFormat.SubjectPublicKeyInfo
+)
+
+penkey = pkey.private_bytes(
+ encoding=serialization.Encoding.DER,
+ format=serialization.PrivateFormat.PKCS8,
+ encryption_algorithm=serialization.NoEncryption()
+)
+
+print("Local Private Key: ", base64.b64encode(penkey).decode('utf-8'))
+print("Local Public Key: ", base64.b64encode(uenkey).decode('utf-8'))
+print("ONDC Public Key: ", "MCowBQYDK2VuAyEAa9Wbpvd9SsrpOZFcynyt/TO3x0Yrqyys4NUGIvyxX2Q=")
+
+myukey = base64.b64encode(uenkey).decode('utf-8')
+#"MCowBQYDK2VuAyEAa9Wbpvd9SsrpOZFcynyt/TO3x0Yrqyys4NUGIvyxX2Q=")
+ondcpub = (base64.b64decode(
+ "MCowBQYDK2VuAyEAF2efvGvniY1X7mVwjK+9z17pcrM+hnEYNUKiiUVSuyY="
+ )
+)
+
+oenkey = serialization.load_der_public_key(ondcpub)
+
+shared_key = pkey.exchange(oenkey)
+print("Shared Secret: ", base64.b64encode(shared_key).decode('utf-8'))
+
+cipher = AES.new(shared_key,AES.MODE_GCM)
+
+nonce = cipher.nonce
+
+text = b'aef26da5-12f3-4e95-8bfb-e3398489c4c4'
+
+cstr,authTag = cipher.encrypt_and_digest(text)
+
+
+ciphdict = {
+ "cipher": base64.b64encode(cstr).decode('utf-8'),
+ "hmac": base64.b64encode(authTag).decode('utf-8'),
+ "nonce": base64.b64encode(nonce).decode('utf-8')
+}
+
+ciphjson = json.dumps(ciphdict, sort_keys=True, indent=2)
+print(ciphjson.encode('utf-8').decode('utf-8'))
+
+
+encstr = base64.b64encode(ciphjson.encode('utf-8')).decode('utf-8')
+print(encstr)
+
+decstr = json.loads(base64.b64decode(encstr))
+
+cipher1 = AES.new(shared_key,AES.MODE_GCM,nonce=base64.b64decode(decstr["nonce"]))
+
+dec = cipher1.decrypt_and_verify(base64.b64decode(decstr["cipher"]),base64.b64decode(decstr["hmac"]))
+
+print("Decrypted Text:", dec.decode('utf-8'))
+
+
+
diff --git a/utilities/vlookup/node.js/.gitignore b/utilities/vlookup/node.js/.gitignore
new file mode 100644
index 0000000..49e0fc6
--- /dev/null
+++ b/utilities/vlookup/node.js/.gitignore
@@ -0,0 +1,2 @@
+/node_modules
+/package-lock.json
\ No newline at end of file
diff --git a/utilities/vlookup/node.js/README.md b/utilities/vlookup/node.js/README.md
new file mode 100644
index 0000000..9e13889
--- /dev/null
+++ b/utilities/vlookup/node.js/README.md
@@ -0,0 +1,65 @@
+# ONDC VLookup Service
+
+This repository contains a simple Express.js server that facilitates the ONDC (Open Network for Digital Commerce) VLookup service. The VLookup service allows you to perform lookups in the ONDC registry and obtain relevant information based on specified parameters.
+
+## Getting Started
+
+### Prerequisites
+
+- Node.js installed on your machine
+
+### Installation
+
+1. Clone this repository to your local machine.
+2. Navigate to the project directory in the terminal.
+3. Run `npm install` to install the required dependencies.
+
+### Usage
+
+1. Start the server by running `npm start` in the terminal.
+2. The server will run on http://localhost:9900.
+
+## Routes
+
+### VLookup
+
+Perform a VLookup operation by sending a POST request to `http://localhost:9900/vlookup`. The payload should be in the following format:
+
+```json
+{
+ "senderSubscriberId": "your_subscriber_id",
+ "privateKey": "your_private_key",
+
+ //search parameters of the NP whose details need to be fetched from registry
+
+ "domain": "ONDC:RET10",
+ "subscriberId": "subscriber_id", // subscriber_id you want to lookup
+ "country": "IND", // country
+ "type": "buyerApp", //buyerApp, sellerApp, gateway
+ "city": "std:022", // city code
+ "env": "staging" //staging,preprod,prod
+}
+```
+
+## Signature
+
+Sign your data by sending a POST request to [http://localhost:9900/vlookup/sign](http://localhost:9900/vlookup/sign). The payload should be the same as for the VLookup route. The response will include a signature.
+
+## Important Note
+
+Make sure to replace the sample private key with your actual private key.
+
+## Dependencies
+
+- Express.js
+- sodium-native
+- crypto
+- axios
+
+## Contributing
+
+Feel free to contribute to the development of this project by submitting pull requests.
+
+## License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
diff --git a/utilities/vlookup/node.js/index.js b/utilities/vlookup/node.js/index.js
new file mode 100644
index 0000000..9b831cc
--- /dev/null
+++ b/utilities/vlookup/node.js/index.js
@@ -0,0 +1,147 @@
+const express = require("express");
+const sodium = require("sodium-native");
+const crypto = require("crypto");
+const axios = require("axios");
+const app = express();
+const port = 9900;
+
+const createSigningString = (data) => {
+ const { country, domain, type, city, subscriberId } = data;
+ return [country, domain, type, city, subscriberId].join("|");
+};
+
+const getEnvDetails = (env) => {
+ let envLink = "";
+ if (env === "preprod") {
+ envLink = "https://preprod.registry.ondc.org/ondc/vlookup";
+ } else if (env === "prod") {
+ envLink = "https://prod.registry.ondc.org/vlookup";
+ } else if (env === "staging") {
+ envLink = "https://staging.registry.ondc.org/vlookup";
+ } else {
+ throw new Error("Unsupported environment");
+ }
+ return envLink;
+};
+
+const fetchRegistryResponse = async (
+ requestId,
+ timestamp,
+ signature,
+ searchParameters,
+ senderSubscriberId,
+ envLink
+) => {
+ try {
+ const response = await axios.post(envLink, {
+ sender_subscriber_id: senderSubscriberId,
+ request_id: requestId,
+ timestamp,
+ search_parameters: searchParameters,
+ signature,
+ });
+ return response;
+ } catch (error) {
+ return error;
+ }
+};
+
+const getVLookUpData = async (signature, data) => {
+ try {
+ const requestId = crypto.randomUUID();
+ const timestamp = new Date().toISOString();
+ const { country, domain, type, city, subscriberId } = data;
+ const searchParameters = {
+ country,
+ domain,
+ type,
+ city,
+ subscriber_id: subscriberId,
+ };
+ const senderSubscriberId = data.senderSubscriberId;
+ const envLink = getEnvDetails(data.env);
+
+ const res = await fetchRegistryResponse(
+ requestId,
+ timestamp,
+ signature,
+ searchParameters,
+ senderSubscriberId,
+ envLink
+ );
+ return res;
+ } catch (error) {
+ logError("getVLookUpData", error);
+ throw error;
+ }
+};
+
+const signData = (message, privateKey) => {
+ const signature = new Uint8Array(sodium.crypto_sign_BYTES);
+ sodium.crypto_sign_detached(signature, message, privateKey);
+ return signature;
+};
+
+const sign = (signingString, privateKey) => {
+ let privateKeyBytes = Buffer.from(privateKey, "base64");
+ const message = Buffer.from(signingString);
+
+ if (privateKeyBytes.length !== sodium.crypto_sign_SECRETKEYBYTES) {
+ return "Invalid private key length";
+ }
+ privateKey = new Uint8Array(privateKeyBytes);
+
+ const signature = signData(message, privateKey);
+
+ signatureBase64 = Buffer.from(signature).toString("base64");
+
+ return signatureBase64;
+};
+
+const vLookUp = async (data) => {
+ try {
+ const signingString = createSigningString(data);
+ const signature = sign(signingString, data.privateKey);
+ let res = await getVLookUpData(signature, data);
+ res = res.data;
+ return res;
+ } catch (error) {
+ logError("vLookUp", error);
+ throw error;
+ }
+};
+
+const logError = (location, error) => {
+ console.error(`Error in ${location}:`, error);
+};
+
+app.use(express.json());
+
+// Route for signing your data
+app.post("/vlookup/sign", (req, res) => {
+ try {
+ const data = req.body;
+ const signingString = createSigningString(data);
+ const signature = sign(signingString, data.privateKey);
+ res.status(200).send({ success: true, signature });
+ } catch (error) {
+ logError("/vlookup/sign", error);
+ res.status(400).send({ success: false, response: error.message });
+ }
+});
+
+// Route for vlookup
+app.post("/vlookup", async (req, res) => {
+ try {
+ const response = await vLookUp(req.body);
+ res.status(200).send({ success: true, response });
+ } catch (error) {
+ logError("/vlookup", error);
+ res.status(400).send({ success: false, response: error.message });
+ }
+});
+
+// Start the server
+app.listen(port, () => {
+ console.log(`Server is running on http://localhost:${port}`);
+});
diff --git a/utilities/vlookup/node.js/package.json b/utilities/vlookup/node.js/package.json
new file mode 100644
index 0000000..8a69f84
--- /dev/null
+++ b/utilities/vlookup/node.js/package.json
@@ -0,0 +1,18 @@
+{
+ "name": "vlookup-service",
+ "version": "1.0.0",
+ "description": "This repository contains a simple Express.js server that facilitates the ONDC (Open Network for Digital Commerce) VLookup service. The VLookup service allows you to perform lookups in the ONDC registry and obtain relevant information based on specified parameters.",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1",
+ "start": "node index.js"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "axios": "^1.6.2",
+ "crypto": "^1.0.1",
+ "express": "^4.18.2",
+ "sodium-native": "^4.0.4"
+ }
+}
diff --git a/utilities/vlookup/python/README.md b/utilities/vlookup/python/README.md
new file mode 100644
index 0000000..1f3e8f2
--- /dev/null
+++ b/utilities/vlookup/python/README.md
@@ -0,0 +1,65 @@
+# ONDC VLookup Service
+
+This repository contains a simple FASTAPI server that facilitates the ONDC (Open Network for Digital Commerce) VLookup service. The VLookup service allows you to perform lookups in the ONDC registry and obtain relevant information based on specified parameters.
+
+## Getting Started
+
+### Prerequisites
+
+- Python installed on your machine
+
+### Installation
+
+1. Clone this repository to your local machine.
+2. Navigate to the project directory in the terminal.
+3. Run `pip install -r requirements.txt` to install the required dependencies.
+
+### Usage
+
+1. Start the server by running `python -m uvicorn main:app --reload` in the terminal.
+2. The server will run on http://localhost:8000.
+
+## Routes
+
+### VLookup
+
+Perform a VLookup operation by sending a POST request to `http://localhost:8000/vlookup`. The payload should be in the following format:
+
+```json
+{
+ "senderSubscriberId": "your_subscriber_id",
+ "privateKey": "your_private_key",
+
+ //search parameters of the NP whose details need to be fetched from registry
+
+ "domain": "ONDC:RET10",
+ "subscriberId": "subscriber_id", // subscriber_id you want to lookup
+ "country": "IND", // country
+ "type": "buyerApp", //buyerApp, sellerApp, gateway
+ "city": "std:022", // city code
+ "env": "staging" //staging,preprod,prod
+}
+```
+
+## Important Note
+
+Make sure to replace the sample private key with your actual private key.
+
+## Dependencies
+
+- pybase64
+- datetime
+- uuid
+- requests
+- PyNaCl
+- fastapi
+- uvicorn
+- pydantic
+
+## Contributing
+
+Feel free to contribute to the development of this project by submitting pull requests.
+
+## License
+
+This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
\ No newline at end of file
diff --git a/utilities/vlookup/python/main.py b/utilities/vlookup/python/main.py
new file mode 100644
index 0000000..2c2dba1
--- /dev/null
+++ b/utilities/vlookup/python/main.py
@@ -0,0 +1,31 @@
+from fastapi import FastAPI
+import uvicorn
+from pydantic import BaseModel
+import json
+from utils import vlookup
+
+app = FastAPI()
+
+class VLookupRequest(BaseModel):
+ senderSubscriberId: str
+ privateKey: str
+ domain : str
+ subscriberId: str
+ country: str
+ type_: str
+ city: str
+ env: str
+
+@app.post("/vlookup")
+def vlookup_func(request_data: VLookupRequest):
+ request_dict = request_data.model_dump()
+
+ try:
+ result = vlookup(request_dict)
+ result = json.loads(result)
+ return result
+ except Exception as e:
+ raise e
+
+if __name__ == "__main__":
+ uvicorn.run(app, host="127.0.0.1", port=8000)
diff --git a/utilities/vlookup/python/requirements.txt b/utilities/vlookup/python/requirements.txt
new file mode 100644
index 0000000..1f4606d
--- /dev/null
+++ b/utilities/vlookup/python/requirements.txt
@@ -0,0 +1,8 @@
+pybase64
+datetime
+uuid
+requests
+PyNaCl
+fastapi
+uvicorn
+pydantic
\ No newline at end of file
diff --git a/utilities/vlookup/python/utils.py b/utilities/vlookup/python/utils.py
new file mode 100644
index 0000000..a6e4397
--- /dev/null
+++ b/utilities/vlookup/python/utils.py
@@ -0,0 +1,108 @@
+import base64
+from datetime import datetime, timezone
+import uuid
+import requests
+import nacl.encoding;
+import nacl.hash
+from nacl.bindings import crypto_sign_ed25519_sk_to_seed
+from nacl.signing import SigningKey
+import json
+
+def create_signing_string(data):
+ country = data.get('country')
+ domain = data.get('domain')
+ type_ = data.get('type_') # 'type' is a reserved keyword in Python, using 'type_' instead
+ city = data.get('city')
+ subscriber_id = data.get('subscriberId')
+ return "|".join([country, domain, type_, city, subscriber_id])
+
+
+def get_env_details(env):
+ env_link = ""
+ if env == "preprod":
+ env_link = "https://preprod.registry.ondc.org/ondc/vlookup"
+ elif env == "prod":
+ env_link = "https://prod.registry.ondc.org/vlookup"
+ elif env == "staging":
+ env_link = "https://staging.registry.ondc.org/vlookup"
+ else:
+ raise ValueError("Unsupported environment")
+ return env_link
+
+def fetchRegistryResponse(request_id,timestamp,signature,search_parameters,sender_subscriber_id,envLink):
+ try:
+ payload = {
+ 'sender_subscriber_id': sender_subscriber_id,
+ 'request_id': str(request_id),
+ 'timestamp': timestamp,
+ 'search_parameters': search_parameters,
+ 'signature': signature
+ }
+
+ # Make the HTTP POST request
+ json_data = json.loads(json.dumps(payload))
+ response = requests.post(envLink,json=json_data)
+ return response
+
+ except Exception as e:
+ raise e
+
+def getVLookUpData(signature,data):
+ requestId = uuid.uuid4()
+ timestamp = datetime.now(timezone.utc)
+ formatted_timestamp = timestamp.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'
+ country = data.get('country')
+ domain = data.get('domain')
+ type_ = data.get('type_') # 'type' is a reserved keyword in Python, so using 'type_' instead
+ city = data.get('city')
+ subscriber_id = data.get('subscriberId')
+
+ search_parameters = {
+ 'country': country,
+ 'domain': domain,
+ 'type': type_,
+ 'city': city,
+ 'subscriber_id': subscriber_id,
+ }
+
+ sender_subscriber_id = data['senderSubscriberId']
+ env_link = get_env_details(data['env'])
+
+ try:
+ res = fetchRegistryResponse(requestId,
+ formatted_timestamp,
+ signature,
+ search_parameters,
+ sender_subscriber_id,
+ env_link
+ )
+ return res
+ except Exception as e:
+ raise e
+
+
+
+def sign_response(signing_key, private_key):
+ private_key64 = base64.b64decode(private_key)
+ seed = crypto_sign_ed25519_sk_to_seed(private_key64)
+ signer = SigningKey(seed)
+ signed = signer.sign(bytes(signing_key, encoding='utf8'))
+ signature = base64.b64encode(signed.signature).decode()
+ return signature
+
+
+def vlookup(data):
+ try:
+ private_key = data.get('privateKey')
+ signingString = create_signing_string(data)
+ signature = sign_response(signingString,private_key)
+ result = getVLookUpData(signature, data)
+ result = result.content
+ return result
+
+ except Exception as e:
+ raise(e)
+
+
+
+