@startbutton/node-sdk
Version:
Official Node.js SDK for StartButton Payment API
3 lines (2 loc) • 9.34 kB
JavaScript
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("axios"),require("crypto")):"function"==typeof define&&define.amd?define(["exports","axios","crypto"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).StartButtonSDK={},t.axios,t.crypto)}(this,function(t,e,r){"use strict";function s(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(r){if("default"!==r){var s=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,s.get?s:{enumerable:!0,get:function(){return t[r]}})}}),e.default=t,Object.freeze(e)}var i=s(r);const n={test:{baseUrl:"https://api-dev.startbutton.tech",timeout:3e4},live:{baseUrl:"https://api.startbutton.tech",timeout:3e4}},a={VERSION:"1.0.0",USER_AGENT:"startbutton-node-sdk",SDK_SOURCE:"node-sdk",DEFAULT_TIMEOUT:3e4};class o{constructor(t,e={}){this.config=this.buildConfig(t,e)}buildConfig(t,e){const r=e.environment||"live",s=n[r];let i,o=t;if("string"==typeof t&&t.includes(":")){const[e,r]=t.split(":");e&&r&&(i=e,o=r)}return{secretKey:o,publicKey:i,baseUrl:e.baseUrl||s.baseUrl,timeout:e.timeout||s.timeout,version:a.VERSION,environment:r,userAgent:e.userAgent||a.USER_AGENT}}getConfig(){return{...this.config}}getSecretKey(){return this.config.secretKey}getPublicKey(){return this.config.publicKey}getBaseUrl(){return this.config.baseUrl}getTimeout(){return this.config.timeout}getEnvironment(){return this.config.environment}getUserAgent(){return this.config.userAgent||a.USER_AGENT}getVersion(){return this.config.version}isTestEnvironment(){return"test"===this.config.environment}isLiveEnvironment(){return"live"===this.config.environment}updateConfig(t){this.config=this.buildConfig(this.config.secretKey,{environment:this.config.environment,baseUrl:this.config.baseUrl,timeout:this.config.timeout,userAgent:this.config.userAgent,...t})}}class c extends Error{constructor(t,e,r,s){super(t),this.name="StartButtonError",this.status=e,this.code=r,this.errors=s}}class u extends c{constructor(t="Authentication failed"){super(t,401,"AUTHENTICATION_ERROR"),this.name="AuthenticationError"}}class h extends c{constructor(t,e){super(t,400,"VALIDATION_ERROR",e),this.name="ValidationError"}}class l extends c{constructor(t="Rate limit exceeded"){super(t,429,"RATE_LIMIT_ERROR"),this.name="RateLimitError"}}class g extends c{constructor(t="Internal server error"){super(t,500,"SERVER_ERROR"),this.name="ServerError"}}class d{constructor(t){this.retryAttempts=3,this.retryDelay=1e3,this.config=t,this.axiosInstance=this.createAxiosInstance(),this.setupInterceptors()}createAxiosInstance(){return e.create({baseURL:this.config.baseUrl,timeout:this.config.timeout,headers:this.getDefaultHeaders()})}getDefaultHeaders(){return{Authorization:`Bearer ${this.config.secretKey}`,"Content-Type":"application/json","User-Agent":`${this.config.userAgent}/${this.config.version}`,"X-SDK-Version":this.config.version,"X-SDK-Source":"node-sdk"}}setupInterceptors(){this.axiosInstance.interceptors.request.use(t=>(t.headers["X-Request-ID"]=this.generateRequestId(),t),t=>Promise.reject(t)),this.axiosInstance.interceptors.response.use(t=>t,t=>this.handleResponseError(t))}generateRequestId(){return`sb_${Date.now()}_${Math.random().toString(36).substr(2,9)}`}handleResponseError(t){if(!t.response)throw t.request?new c("Network error - no response received",0,"NETWORK_ERROR"):new c(t.message||"Unknown error occurred",0,"UNKNOWN_ERROR");{const{status:e,data:r}=t.response,s=r;switch(e){case 400:throw new h(s.message||"Validation failed",s.errors);case 401:throw new u(s.message);case 403:throw new c(s.message||"Access forbidden",e,"FORBIDDEN");case 404:throw new c(s.message||"Resource not found",e,"NOT_FOUND");case 429:throw new l(s.message);case 500:case 502:case 503:case 504:throw new g(s.message);default:throw new c(s.message||"Request failed",e,s.code)}}}async request(t){const e={method:t.method,url:t.url,data:t.data,params:t.params,headers:this.buildHeaders(t)};return this.executeWithRetry(e)}buildHeaders(t){const e=this.getDefaultHeaders(),r=!0===t.usePublicKey,s={...e,...t.headers||{}};if(r){const t=this.config.publicKey||this.config.secretKey;s.Authorization=`Bearer ${t}`}return s}async executeWithRetry(t,e=1){try{const e=await this.axiosInstance.request(t);return this.formatResponse(e)}catch(r){if(this.shouldRetry(r,e))return await this.delay(this.getRetryDelay(e)),this.executeWithRetry(t,e+1);throw r}}shouldRetry(t,e){return!(e>=this.retryAttempts)&&(t instanceof c&&((t.status||0)>=500||0===t.status))}getRetryDelay(t){return this.retryDelay*Math.pow(2,t-1)}delay(t){return new Promise(e=>setTimeout(e,t))}formatResponse(t){const{data:e}=t;if(!e||"object"!=typeof e)throw new c("Invalid response format",0,"INVALID_RESPONSE");return{status:e.status??!1,message:e.message??"",data:e.data,errors:e.errors,meta:e.meta}}setRetryConfig(t,e){this.retryAttempts=t,this.retryDelay=e}getConfig(){return{...this.config}}}class f{static validateEmail(t){if(!t||"string"!=typeof t)return!1;return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t.trim())}static validateAmount(t){return"number"==typeof t&&t>0&&Number.isInteger(t)}static validateReference(t){if(!t||"string"!=typeof t)return!1;const e=t.trim();return e.length>=10&&e.length<=50}static validateCurrency(t){return!(!t||"string"!=typeof t)}static validatePhoneNumber(t){if(!t||"string"!=typeof t)return!1;const e=t.replace(/\D/g,"");return e.length>=7&&e.length<=15}static validateAccountNumber(t){if(!t||"string"!=typeof t)return!1;const e=t.replace(/[\s-]/g,"");return/^\d{8,20}$/.test(e)}static validateBankCode(t){return!(!t||"string"!=typeof t)&&/^[A-Z0-9]{3,4}$/.test(t.toUpperCase())}static validateCallbackUrl(t){if(!t||"string"!=typeof t)return!1;try{const e=new URL(t);return["http:","https:"].includes(e.protocol)}catch{return!1}}static validateMetadata(t){if(!t||"object"!=typeof t)return!1;if(Array.isArray(t)||t.constructor!==Object)return!1;try{return JSON.stringify(t),!0}catch{return!1}}static generateReference(t="SB"){return`${t}_${Date.now().toString()}_${Math.random().toString(36).substring(2,8).toUpperCase()}`}static sanitizeAmount(t,e="NGN"){return"NGN"===e?Math.round(100*t):Math.round(t)}static validateAndThrow(t,e,r){if(!t)throw new h(e,r)}static validateInitializeTransactionData(t){const e=[];if(this.validateEmail(t.email)||e.push("Invalid email address"),this.validateAmount(t.amount)||e.push("Amount must be a positive integer"),t.reference&&!this.validateReference(t.reference)&&e.push("Reference must be between 10-50 characters"),t.currency&&!this.validateCurrency(t.currency)&&e.push("Invalid currency code"),t.callbackUrl&&!this.validateCallbackUrl(t.callbackUrl)&&e.push("Invalid callback URL"),t.metadata&&!this.validateMetadata(t.metadata)&&e.push("Invalid metadata object"),e.length>0)throw new h("Transaction initialization validation failed",e)}}class p{constructor(t){this.httpClient=t}async initialize(t){return f.validateInitializeTransactionData(t),this.httpClient.request({method:"POST",url:"/transaction/initialize",data:t,usePublicKey:!0})}async initializeS2S(t){return f.validateInitializeTransactionData(t),this.httpClient.request({method:"POST",url:"/transaction/initialize-s2s",data:t,usePublicKey:!0})}async initializeMobileMoney(t){return f.validateInitializeTransactionData(t),this.httpClient.request({method:"POST",url:"/transaction/initialize/s2s/mobile_money",data:t,usePublicKey:!0})}async verify(t){return this.httpClient.request({method:"GET",url:`/transaction/status/${t}`})}}class m{constructor(t){this.httpClient=t}async getWalletBalance(){return this.httpClient.request({method:"GET",url:"/wallet"})}}class y{constructor(t){this.httpClient=t}toStringifiedJson(t){return Buffer.isBuffer(t)?JSON.stringify(JSON.parse(t.toString("utf8"))):"string"==typeof t?JSON.stringify(JSON.parse(t)):JSON.stringify(t)}verifySignature(t,e,r){const s=this.toStringifiedJson(t);return i.createHmac("sha512",r).update(s).digest("hex")==e}transactionStatus(t){return this.httpClient.request({method:"GET",url:`/transaction/status/${t}`})}}class v{constructor(t,e={}){if(!t||"string"!=typeof t)throw new Error("Secret key is required and must be a string");this.configManager=new o(t,e),this.httpClient=new d(this.configManager.getConfig())}get transactions(){return this._transactions||(this._transactions=new p(this.httpClient)),this._transactions}get merchants(){return this._merchants||(this._merchants=new m(this.httpClient)),this._merchants}get webhooks(){return this._webhooks||(this._webhooks=new y(this.httpClient)),this._webhooks}getConfig(){return this.configManager.getConfig()}getHttpClient(){return this.httpClient}updateConfig(t){this.configManager.updateConfig(t),this.httpClient=new d(this.configManager.getConfig()),this._transactions=void 0,this._merchants=void 0,this._webhooks=void 0}isTestEnvironment(){return this.configManager.isTestEnvironment()}isLiveEnvironment(){return this.configManager.isLiveEnvironment()}getVersion(){return this.configManager.getVersion()}setRetryConfig(t,e){this.httpClient.setRetryConfig(t,e)}}t.AuthenticationError=u,t.ENVIRONMENTS=n,t.HttpClient=d,t.RateLimitError=l,t.SDKConfigManager=o,t.SDK_CONSTANTS=a,t.ServerError=g,t.StartButtonError=c,t.StartButtonSDK=v,t.ValidationError=h,t.default=v,Object.defineProperty(t,"__esModule",{value:!0})});
//# sourceMappingURL=index.umd.js.map